Skip to content

[clang] Implement __is_pointer_interconvertible_base_of() #88473

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://wg21.link/P0466R5>`_.

- Clang now implements [module.import]p7 fully. Clang now will import module
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -1975,6 +1975,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);
Expand Down
21 changes: 21 additions & 0 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
16 changes: 16 additions & 0 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6082,6 +6082,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");
}
Expand Down
91 changes: 90 additions & 1 deletion clang/test/SemaCXX/type-traits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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));
Expand Down