This is an archive of the discontinued LLVM Phabricator instance.

[ADT] Adopt the new casting infrastructure for PointerUnion
ClosedPublic

Authored by 0x59616e on May 14 2022, 9:03 AM.

Diff Detail

Event Timeline

0x59616e created this revision.May 14 2022, 9:03 AM
Herald added a project: Restricted Project. · View Herald TranscriptMay 14 2022, 9:03 AM
0x59616e requested review of this revision.May 14 2022, 9:03 AM
Herald added a project: Restricted Project. · View Herald TranscriptMay 14 2022, 9:03 AM
0x59616e retitled this revision from Adopt the new casting infrastructure for PointerUnion to [ADT] Adopt the new casting infrastructure for PointerUnion.May 14 2022, 9:03 AM
bzcheeseman added inline comments.
llvm/include/llvm/ADT/PointerUnion.h
213

CastIsPossible inheritance is not necessary since you define isPossible below.

216

Out of curiosity, does it make sense to implement PointerUnion::is<T> in terms of isa<T> rather than the other way around? And same for doCast below?

224

You can inherit from the NullableValueCastFailed cast trait to get this

226

You can inherit from the DefaultDoCastIfPossible cast trait to get this

0x59616e updated this revision to Diff 429503.EditedMay 14 2022, 7:25 PM
0x59616e marked 4 inline comments as done.

Given that I'm still young to C++ and llvm, every suggestion, even the
one you feel it's trivial, are very welcomed ;)

This diff comes with the following updates:

  • Define PointerUnion::is and PointerUnion::get in terms of isa<T> and cast<T>.
  • Add FIXME comment for PointerUnion::is(), PointerUnion::get() and PointerUnion::dyn_cast().
0x59616e added inline comments.
llvm/include/llvm/ADT/PointerUnion.h
216

I'm feeling the same, too.

0x59616e added a comment.EditedMay 14 2022, 7:28 PM

Given that I'm still young to C++ and llvm, every suggestion, even the
one you feel it's trivial, are very welcomed ;)

This diff comes with the following updates:

  • Define PointerUnion::is and ::get in terms of isa<T> and cast<T>.
  • Add FIXME comment for is(), get() and dyn_cast().

Also, CastInfoPointerUnionImpl is defined and declared as a friend of PointerUnion to support isa<T> and cast<T> directly rather than calling into PointerUnion::is and PointerUnion::get.

0x59616e updated this revision to Diff 429505.May 14 2022, 7:52 PM

Didn't notice the build failure. Remove the CastInfoPointerUnionImpl and revert
back to the original implementation.

Is there any way for CastInfo to directly access PointerUnion::Val, which is
a protected member ?

0x59616e added a comment.EditedMay 14 2022, 8:22 PM

I can only come up with this :

Define CastInfoPointerUnionImpl as below :

template <typename... PTs> struct CastInfoPointerUnionImpl {
  using From = PointerUnion<PTs...>;

  template <typename To> static inline bool isPossible(From &F) {
   return  F.Val.getInt() == FirstIndexOfType<To, PTs...>::value;
  }

  template <typename To> static To doCast(From &F) {
    assert(isPossible<To>(F) && "cast to an incompatible type !");
    return PointerLikeTypeTraits<To>::getFromVoidPointer(F.Val.getPointer());
  }
};

and declared it as a friend of PointerUnion.

And then CastInfo:

template <typename To, typename... PTs>
struct CastInfo<To, PointerUnion<PTs...>>
    : NullableValueCastFailed<To>,
      DefaultDoCastIfPossible<To, PointerUnion<PTs...>,
                              CastInfo<To, PointerUnion<PTs...>>> {
  using From = PointerUnion<PTs...>;
  using Impl = CastInfoPointerUnionImpl<PTs...>;

  static inline bool isPossible(From &f) {
    return Impl::template isPossible<To>(f);
  }

  static To doCast(From &f) {
    return Impl::template doCast<To>(f);
  }
};

By doing this we can implement PointerUnion::is and PointerUnion::get in this way:

/// Test if the Union currently holds the type matching T.
 template <typename T> bool is() const { return isa<T>(*this); }

 /// Returns the value of the specified pointer type.
 ///
 /// If the specified pointer type is incorrect, assert.
 template <typename T> T get() const {
   assert(isa<T>(*this) && "Invalid accessor called");
   return cast<T>(*this);
 }

Does this make sense ?

Define CastInfoPointerUnionImpl as below :

template <typename... PTs> struct CastInfoPointerUnionImpl {
  using From = PointerUnion<PTs...>;

  template <typename To> static inline bool isPossible(From &F) {
   return  F.Val.getInt() == FirstIndexOfType<To, PTs...>::value;
  }

  template <typename To> static To doCast(From &F) {
    assert(isPossible<To>(F) && "cast to an incompatible type !");
    return PointerLikeTypeTraits<To>::getFromVoidPointer(F.Val.getPointer());
  }
};

and declared it as a friend of PointerUnion.

Could you declare the CastInfo struct itself as a friend? something like template<typename To, typename...PTs> friend struct CastInfo<To, PointerUnion<PTs...>>? That way you don't need the impl struct so it cuts down on overall code. Truly asking here - I think it's doable but I may be wrong.

And then CastInfo:

template <typename To, typename... PTs>
struct CastInfo<To, PointerUnion<PTs...>>
    : NullableValueCastFailed<To>,
      DefaultDoCastIfPossible<To, PointerUnion<PTs...>,
                              CastInfo<To, PointerUnion<PTs...>>> {
  using From = PointerUnion<PTs...>;
  using Impl = CastInfoPointerUnionImpl<PTs...>;

  static inline bool isPossible(From &f) {
    return Impl::template isPossible<To>(f);
  }

  static To doCast(From &f) {
    return Impl::template doCast<To>(f);
  }
};

This looks really nice - I think it still will even with the Impl::* methods inlined :)

By doing this we can implement PointerUnion::is and PointerUnion::get in this way:

/// Test if the Union currently holds the type matching T.
 template <typename T> bool is() const { return isa<T>(*this); }

 /// Returns the value of the specified pointer type.
 ///
 /// If the specified pointer type is incorrect, assert.
 template <typename T> T get() const {
   assert(isa<T>(*this) && "Invalid accessor called");
   return cast<T>(*this);
 }

Does this make sense ?

Comments inline :) Overall makes good sense to me though!

bzcheeseman added inline comments.May 14 2022, 8:39 PM
llvm/include/llvm/ADT/PointerUnion.h
213

I think you'll need public inheritance here and below? Otherwise I'd expect that you get compiler errors saying this partial specialization doesn't define the functions you need.

0x59616e updated this revision to Diff 429508.May 14 2022, 9:11 PM
0x59616e marked an inline comment as done.

Implement CastInfoPointerUnionImpl

0x59616e added a comment.EditedMay 14 2022, 9:12 PM

Thanks for feedback ;)

Define CastInfoPointerUnionImpl as below :

template <typename... PTs> struct CastInfoPointerUnionImpl {
  using From = PointerUnion<PTs...>;

  template <typename To> static inline bool isPossible(From &F) {
   return  F.Val.getInt() == FirstIndexOfType<To, PTs...>::value;
  }

  template <typename To> static To doCast(From &F) {
    assert(isPossible<To>(F) && "cast to an incompatible type !");
    return PointerLikeTypeTraits<To>::getFromVoidPointer(F.Val.getPointer());
  }
};

and declared it as a friend of PointerUnion.

Could you declare the CastInfo struct itself as a friend? something like template<typename To, typename...PTs> friend struct CastInfo<To, PointerUnion<PTs...>>? That way you don't need the impl struct so it cuts down on overall code. Truly asking here - I think it's doable but I may be wrong.

It seems that partial specialization struct can't be declared as a friend ?

0x59616e updated this revision to Diff 429510.May 14 2022, 9:16 PM

I used arc incorrectly. Re-update the diff.

Thanks for feedback ;)
<snip>
It seems that partial specialization struct can't be declared as a friend.

:( I know you can have a templated friend declaration but I guess this doesn't work.

In that case this approach seems fine, thanks for working on it! Just a few more cleanups but overall I think it's looking really solid :)

llvm/include/llvm/ADT/PointerUnion.h
91

nit: Full sentences, please.

128

A quick comment to explain what's going on would be great, maybe /// This is needed to give the CastInfo implementation below access to protected members. or something like that? Just a quick one-liner so it's not confusing in the future.

210

Please explain why this struct is needed with a /// comment.

243

Public inheritance here too. Do you have checks that const is handled correctly?

0x59616e updated this revision to Diff 429512.EditedMay 14 2022, 9:49 PM
0x59616e marked 4 inline comments as done.

Thanks for feedback !

  • Add test case for const
  • Add some comments.
0x59616e added inline comments.May 14 2022, 9:50 PM
llvm/include/llvm/ADT/PointerUnion.h
243

I've added some test for const case, but I'm not sure whether it's comprehensive enough or not.

0x59616e updated this revision to Diff 429513.May 14 2022, 9:51 PM
  • public inheritance for ConstStrippingForwardingCast
bzcheeseman added inline comments.May 14 2022, 10:18 PM
llvm/include/llvm/ADT/PointerUnion.h
243

If you see the tests for the cast infra there are some spots where we have auto result = cast<T>(something); followed by a static_assert that decltype(result) must be a specific type. That was the kind of thing I meant here - i.e. if I have a const pointer union and I do cast<float *> (for example), should it return const float *? Or not? I guess what I was going for was to explicitly test "what happens when I have a const input to a cast? How explicit do I have to be?

At the bottom there you're really close - it looks like you're on the right track but it would be great to get a static_assert for what the output type of a cast on PU4 is, just to make it explicit.

This comment was removed by bzcheeseman.
0x59616e added inline comments.May 14 2022, 10:43 PM
llvm/include/llvm/ADT/PointerUnion.h
243

What's your opinion about whether it should return a const or non-const pointer when casting from a const PointerUnion ?

It seems that people are not always using a plain pointer in PointerUnion. Sometimes they seem to use a custom smart pointer ?

0x59616e updated this revision to Diff 429515.May 14 2022, 11:03 PM

update diff

  • define our own castFailed in the hope of fixing the build failure of clang.
  • add static_assert for type check
0x59616e updated this revision to Diff 429523.May 15 2022, 2:03 AM

update diff trying to fix buildbot failure

0x59616e updated this revision to Diff 429526.May 15 2022, 4:52 AM

apply clang-format

bzcheeseman accepted this revision.May 15 2022, 9:58 AM

Thank you for working on this!

llvm/include/llvm/ADT/PointerUnion.h
243

Honestly I'm not sure! I think it comes down to the semantics of PointerUnion, sort of like const void const* where each const means slightly different things. I think the way that seems simplest is to say "yes, const comes out of a const union". The way that I think matches C++'s const rules is "no, const applies to the union, not the things inside".

I think in this patch you're using the second way, which is totally fair (and you added tests to make it explicit, so thank you!)

llvm/unittests/ADT/PointerUnionTest.cpp
283 ↗(On Diff #429526)

misspelling here, "misamatch" -> "mismatch"

This revision is now accepted and ready to land.May 15 2022, 9:58 AM
lattner accepted this revision.May 15 2022, 11:10 AM

I'm excited to see this, thank you or working on it. +1 with the changes aman suggested.

0x59616e added a comment.EditedMay 15 2022, 11:33 PM

Thanks @lattner @bzcheeseman ! I'll land this patch as soon as possible after making sure everything runs smoothly.

0x59616e marked an inline comment as done.May 15 2022, 11:33 PM