diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 45a9a79739a4e..399d5ba796d15 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -98,7 +98,8 @@ C++20 Feature Support behavior can use the flag '-Xclang -fno-skip-odr-check-in-gmf'. (#GH79240). -- Implemented the `__is_layout_compatible` intrinsic to support +- Implemented the `__is_layout_compatible` and `__is_pointer_interconvertible_base_of` + intrinsics to support `P0466R5: Layout-compatibility and Pointer-interconvertibility Traits `_. - Clang now implements [module.import]p7 fully. Clang now will import module diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 800af0e6d0448..a27fbed358a60 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -521,6 +521,7 @@ TYPE_TRAIT_1(__is_union, IsUnion, KEYCXX) TYPE_TRAIT_1(__has_unique_object_representations, HasUniqueObjectRepresentations, KEYCXX) TYPE_TRAIT_2(__is_layout_compatible, IsLayoutCompatible, KEYCXX) +TYPE_TRAIT_2(__is_pointer_interconvertible_base_of, IsPointerInterconvertibleBaseOf, KEYCXX) #define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) KEYWORD(__##Trait, KEYCXX) #include "clang/Basic/TransformTypeTraits.def" diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 6b9789334811e..3b252f94dcbee 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1999,6 +1999,8 @@ class Sema final : public SemaBase { }; bool IsLayoutCompatible(QualType T1, QualType T2) const; + bool IsPointerInterconvertibleBaseOf(const TypeSourceInfo *Base, + const TypeSourceInfo *Derived); bool CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall, const FunctionProtoType *Proto); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index abfd9a3031577..8e21811b67d90 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -19710,6 +19710,27 @@ bool Sema::IsLayoutCompatible(QualType T1, QualType T2) const { return isLayoutCompatible(getASTContext(), T1, T2); } +//===-------------- Pointer interconvertibility ----------------------------// + +bool Sema::IsPointerInterconvertibleBaseOf(const TypeSourceInfo *Base, + const TypeSourceInfo *Derived) { + QualType BaseT = Base->getType()->getCanonicalTypeUnqualified(); + QualType DerivedT = Derived->getType()->getCanonicalTypeUnqualified(); + + if (BaseT->isStructureOrClassType() && DerivedT->isStructureOrClassType() && + getASTContext().hasSameType(BaseT, DerivedT)) + return true; + + if (!IsDerivedFrom(Derived->getTypeLoc().getBeginLoc(), DerivedT, BaseT)) + return false; + + // Per [basic.compound]/4.3, containing object has to be standard-layout. + if (DerivedT->getAsCXXRecordDecl()->isStandardLayout()) + return true; + + return false; +} + //===--- CHECK: pointer_with_type_tag attribute: datatypes should match ----// /// Given a type tag expression find the type tag itself. diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 8911257a6f614..1cfd3e56a583e 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -6100,6 +6100,22 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceI Self.Diag(Rhs->getTypeLoc().getBeginLoc(), diag::err_vla_unsupported) << 1 << tok::kw___is_layout_compatible; return Self.IsLayoutCompatible(LhsT, RhsT); + } + case BTT_IsPointerInterconvertibleBaseOf: { + if (LhsT->isStructureOrClassType() && RhsT->isStructureOrClassType() && + !Self.getASTContext().hasSameUnqualifiedType(LhsT, RhsT)) { + Self.RequireCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT, + diag::err_incomplete_type); + } + + if (LhsT->isVariableArrayType()) + Self.Diag(Lhs->getTypeLoc().getBeginLoc(), diag::err_vla_unsupported) + << 1 << tok::kw___is_pointer_interconvertible_base_of; + if (RhsT->isVariableArrayType()) + Self.Diag(Rhs->getTypeLoc().getBeginLoc(), diag::err_vla_unsupported) + << 1 << tok::kw___is_pointer_interconvertible_base_of; + + return Self.IsPointerInterconvertibleBaseOf(Lhs, Rhs); } default: llvm_unreachable("not a BTT"); } diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp index 421d3007d27ff..d43701c3d976e 100644 --- a/clang/test/SemaCXX/type-traits.cpp +++ b/clang/test/SemaCXX/type-traits.cpp @@ -1740,7 +1740,7 @@ void is_layout_compatible(int n) static_assert(!__is_layout_compatible(void, int)); static_assert(__is_layout_compatible(void, const void)); static_assert(__is_layout_compatible(void, volatile void)); - static_assert(__is_layout_compatible(const int, volatile int)); + static_assert(__is_layout_compatible(const void, volatile void)); static_assert(__is_layout_compatible(int, int)); static_assert(__is_layout_compatible(int, const int)); static_assert(__is_layout_compatible(int, volatile int)); @@ -1839,6 +1839,95 @@ void is_layout_compatible(int n) static_assert(!__is_layout_compatible(EnumClassForward, int)); } +namespace IPIBO { +struct Base {}; +struct Base2 {}; +struct Base3 : Base {}; +struct Base3Virtual : virtual Base {}; +struct Derived : Base {}; +struct DerivedIndirect : Base3 {}; +struct DerivedMultiple : Base, Base2 {}; +struct DerivedAmbiguous : Base, Base3 {}; +/* expected-warning@-1 {{direct base 'Base' is inaccessible due to ambiguity: + struct IPIBO::DerivedAmbiguous -> Base + struct IPIBO::DerivedAmbiguous -> Base3 -> Base}} */ +struct DerivedPrivate : private Base {}; +struct DerivedVirtual : virtual Base {}; + +union Union {}; +union UnionIncomplete; +struct StructIncomplete; // #StructIncomplete + +void is_pointer_interconvertible_base_of(int n) +{ + static_assert(__is_pointer_interconvertible_base_of(Base, Derived)); + static_assert(!__is_pointer_interconvertible_base_of(Base2, Derived)); + static_assert(__is_pointer_interconvertible_base_of(Base, DerivedIndirect)); + static_assert(__is_pointer_interconvertible_base_of(Base, DerivedMultiple)); + static_assert(!__is_pointer_interconvertible_base_of(Base3, DerivedMultiple)); + static_assert(!__is_pointer_interconvertible_base_of(Base, DerivedAmbiguous)); + static_assert(__is_pointer_interconvertible_base_of(Base, DerivedPrivate)); + static_assert(!__is_pointer_interconvertible_base_of(Base, DerivedVirtual)); + static_assert(!__is_pointer_interconvertible_base_of(Union, Union)); + static_assert(!__is_pointer_interconvertible_base_of(UnionIncomplete, UnionIncomplete)); + static_assert(__is_pointer_interconvertible_base_of(StructIncomplete, StructIncomplete)); + static_assert(__is_pointer_interconvertible_base_of(StructIncomplete, const StructIncomplete)); + static_assert(__is_pointer_interconvertible_base_of(StructIncomplete, volatile StructIncomplete)); + static_assert(__is_pointer_interconvertible_base_of(const StructIncomplete, volatile StructIncomplete)); + static_assert(!__is_pointer_interconvertible_base_of(StructIncomplete, Derived)); + static_assert(!__is_pointer_interconvertible_base_of(Base, StructIncomplete)); + // expected-error@-1 {{incomplete type 'StructIncomplete' where a complete type is required}} + // expected-note@#StructIncomplete {{forward declaration of 'IPIBO::StructIncomplete'}} + static_assert(!__is_pointer_interconvertible_base_of(CStruct2, CppStructNonStandardByBase2)); + static_assert(!__is_pointer_interconvertible_base_of(void, void)); + static_assert(!__is_pointer_interconvertible_base_of(void, int)); + static_assert(!__is_pointer_interconvertible_base_of(void, const void)); + static_assert(!__is_pointer_interconvertible_base_of(void, volatile void)); + static_assert(!__is_pointer_interconvertible_base_of(const void, volatile void)); + static_assert(!__is_pointer_interconvertible_base_of(int, int)); + static_assert(!__is_pointer_interconvertible_base_of(int, const int)); + static_assert(!__is_pointer_interconvertible_base_of(int, volatile int)); + static_assert(!__is_pointer_interconvertible_base_of(const int, volatile int)); + static_assert(!__is_pointer_interconvertible_base_of(int *, int * __restrict)); + static_assert(!__is_pointer_interconvertible_base_of(int, _Atomic int)); + static_assert(!__is_pointer_interconvertible_base_of(_Atomic(int), _Atomic int)); + static_assert(!__is_pointer_interconvertible_base_of(int, unsigned int)); + static_assert(!__is_pointer_interconvertible_base_of(char, unsigned char)); + static_assert(!__is_pointer_interconvertible_base_of(char, signed char)); + static_assert(!__is_pointer_interconvertible_base_of(unsigned char, signed char)); + using function_type = void(); + using function_type2 = void(char); + static_assert(!__is_pointer_interconvertible_base_of(const function_type, const function_type)); + // expected-warning@-1 {{'const' qualifier on function type 'function_type' (aka 'void ()') has no effect}} + // expected-warning@-2 {{'const' qualifier on function type 'function_type' (aka 'void ()') has no effect}} + static_assert(!__is_pointer_interconvertible_base_of(function_type, const function_type)); + // expected-warning@-1 {{'const' qualifier on function type 'function_type' (aka 'void ()') has no effect}} + static_assert(!__is_pointer_interconvertible_base_of(const function_type, const function_type2)); + // expected-warning@-1 {{'const' qualifier on function type 'function_type' (aka 'void ()') has no effect}} + // expected-warning@-2 {{'const' qualifier on function type 'function_type2' (aka 'void (char)') has no effect}} + static_assert(!__is_pointer_interconvertible_base_of(int CStruct2::*, int CStruct2::*)); + static_assert(!__is_pointer_interconvertible_base_of(int CStruct2::*, char CStruct2::*)); + static_assert(!__is_pointer_interconvertible_base_of(void(CStruct2::*)(int), void(CStruct2::*)(int))); + static_assert(!__is_pointer_interconvertible_base_of(void(CStruct2::*)(int), void(CStruct2::*)(char))); + static_assert(!__is_pointer_interconvertible_base_of(int[], int[])); + static_assert(!__is_pointer_interconvertible_base_of(int[], double[])); + static_assert(!__is_pointer_interconvertible_base_of(int[2], int[2])); + static_assert(!__is_pointer_interconvertible_base_of(int[n], int[2])); + // expected-error@-1 {{variable length arrays are not supported in '__is_pointer_interconvertible_base_of'}} + static_assert(!__is_pointer_interconvertible_base_of(int[n], int[n])); + // expected-error@-1 {{variable length arrays are not supported in '__is_pointer_interconvertible_base_of'}} + // expected-error@-2 {{variable length arrays are not supported in '__is_pointer_interconvertible_base_of'}} + static_assert(!__is_pointer_interconvertible_base_of(int&, int&)); + static_assert(!__is_pointer_interconvertible_base_of(int&, char&)); + static_assert(!__is_pointer_interconvertible_base_of(void(int), void(int))); + static_assert(!__is_pointer_interconvertible_base_of(void(int), void(char))); + static_assert(!__is_pointer_interconvertible_base_of(void(&)(int), void(&)(int))); + static_assert(!__is_pointer_interconvertible_base_of(void(&)(int), void(&)(char))); + static_assert(!__is_pointer_interconvertible_base_of(void(*)(int), void(*)(int))); + static_assert(!__is_pointer_interconvertible_base_of(void(*)(int), void(*)(char))); +} +} + void is_signed() { //static_assert(__is_signed(char));