Skip to content

Commit 6dcb604

Browse files
authored
[clang] Implement __is_pointer_interconvertible_base_of() (#88473)
This patch implements intrinsic that supports `std::is_pointer_interconvertible_base_of` type trait from [P0466R5](https://wg21.link/p0466r5) "Layout-compatibility and Pointer-interconvertibility Traits". Normative wording: > Comment: If `Base` and Derived are non-union class types and are not (possibly _cv_-qualified) versions of the same type, `Derived` is a complete type. > Condition: `Derived` is unambiguously derived from `Base` without regard to _cv_-qualifiers, and each object of type `Derived` is pointer-interconvertible (6.7.2 [basic.compound]) with its `Base` subobject, or `Base` and `Derived` are not unions and name the same class type without regard to _cv_-qualifiers. The paper also express the following intent: > Note that `is_pointer_interconvertible_base_of_v<T,T>` is always true under this wording, even though `T` is not derived from itself. I find the treatment of unions in the wording contradictory to this intent, and I'm not able to find anything relevant in minutes or on the reflector. That said, this patch implements what the wording says, since it's very explicit about unions.
1 parent 0a6f6df commit 6dcb604

File tree

6 files changed

+132
-2
lines changed

6 files changed

+132
-2
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ C++20 Feature Support
9898
behavior can use the flag '-Xclang -fno-skip-odr-check-in-gmf'.
9999
(#GH79240).
100100

101-
- Implemented the `__is_layout_compatible` intrinsic to support
101+
- Implemented the `__is_layout_compatible` and `__is_pointer_interconvertible_base_of`
102+
intrinsics to support
102103
`P0466R5: Layout-compatibility and Pointer-interconvertibility Traits <https://wg21.link/P0466R5>`_.
103104

104105
- Clang now implements [module.import]p7 fully. Clang now will import module

clang/include/clang/Basic/TokenKinds.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,7 @@ TYPE_TRAIT_1(__is_union, IsUnion, KEYCXX)
521521
TYPE_TRAIT_1(__has_unique_object_representations,
522522
HasUniqueObjectRepresentations, KEYCXX)
523523
TYPE_TRAIT_2(__is_layout_compatible, IsLayoutCompatible, KEYCXX)
524+
TYPE_TRAIT_2(__is_pointer_interconvertible_base_of, IsPointerInterconvertibleBaseOf, KEYCXX)
524525

525526
#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) KEYWORD(__##Trait, KEYCXX)
526527
#include "clang/Basic/TransformTypeTraits.def"

clang/include/clang/Sema/Sema.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1999,6 +1999,8 @@ class Sema final : public SemaBase {
19991999
};
20002000

20012001
bool IsLayoutCompatible(QualType T1, QualType T2) const;
2002+
bool IsPointerInterconvertibleBaseOf(const TypeSourceInfo *Base,
2003+
const TypeSourceInfo *Derived);
20022004

20032005
bool CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall,
20042006
const FunctionProtoType *Proto);

clang/lib/Sema/SemaChecking.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19710,6 +19710,27 @@ bool Sema::IsLayoutCompatible(QualType T1, QualType T2) const {
1971019710
return isLayoutCompatible(getASTContext(), T1, T2);
1971119711
}
1971219712

19713+
//===-------------- Pointer interconvertibility ----------------------------//
19714+
19715+
bool Sema::IsPointerInterconvertibleBaseOf(const TypeSourceInfo *Base,
19716+
const TypeSourceInfo *Derived) {
19717+
QualType BaseT = Base->getType()->getCanonicalTypeUnqualified();
19718+
QualType DerivedT = Derived->getType()->getCanonicalTypeUnqualified();
19719+
19720+
if (BaseT->isStructureOrClassType() && DerivedT->isStructureOrClassType() &&
19721+
getASTContext().hasSameType(BaseT, DerivedT))
19722+
return true;
19723+
19724+
if (!IsDerivedFrom(Derived->getTypeLoc().getBeginLoc(), DerivedT, BaseT))
19725+
return false;
19726+
19727+
// Per [basic.compound]/4.3, containing object has to be standard-layout.
19728+
if (DerivedT->getAsCXXRecordDecl()->isStandardLayout())
19729+
return true;
19730+
19731+
return false;
19732+
}
19733+
1971319734
//===--- CHECK: pointer_with_type_tag attribute: datatypes should match ----//
1971419735

1971519736
/// Given a type tag expression find the type tag itself.

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6100,6 +6100,22 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceI
61006100
Self.Diag(Rhs->getTypeLoc().getBeginLoc(), diag::err_vla_unsupported)
61016101
<< 1 << tok::kw___is_layout_compatible;
61026102
return Self.IsLayoutCompatible(LhsT, RhsT);
6103+
}
6104+
case BTT_IsPointerInterconvertibleBaseOf: {
6105+
if (LhsT->isStructureOrClassType() && RhsT->isStructureOrClassType() &&
6106+
!Self.getASTContext().hasSameUnqualifiedType(LhsT, RhsT)) {
6107+
Self.RequireCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT,
6108+
diag::err_incomplete_type);
6109+
}
6110+
6111+
if (LhsT->isVariableArrayType())
6112+
Self.Diag(Lhs->getTypeLoc().getBeginLoc(), diag::err_vla_unsupported)
6113+
<< 1 << tok::kw___is_pointer_interconvertible_base_of;
6114+
if (RhsT->isVariableArrayType())
6115+
Self.Diag(Rhs->getTypeLoc().getBeginLoc(), diag::err_vla_unsupported)
6116+
<< 1 << tok::kw___is_pointer_interconvertible_base_of;
6117+
6118+
return Self.IsPointerInterconvertibleBaseOf(Lhs, Rhs);
61036119
}
61046120
default: llvm_unreachable("not a BTT");
61056121
}

clang/test/SemaCXX/type-traits.cpp

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1740,7 +1740,7 @@ void is_layout_compatible(int n)
17401740
static_assert(!__is_layout_compatible(void, int));
17411741
static_assert(__is_layout_compatible(void, const void));
17421742
static_assert(__is_layout_compatible(void, volatile void));
1743-
static_assert(__is_layout_compatible(const int, volatile int));
1743+
static_assert(__is_layout_compatible(const void, volatile void));
17441744
static_assert(__is_layout_compatible(int, int));
17451745
static_assert(__is_layout_compatible(int, const int));
17461746
static_assert(__is_layout_compatible(int, volatile int));
@@ -1839,6 +1839,95 @@ void is_layout_compatible(int n)
18391839
static_assert(!__is_layout_compatible(EnumClassForward, int));
18401840
}
18411841

1842+
namespace IPIBO {
1843+
struct Base {};
1844+
struct Base2 {};
1845+
struct Base3 : Base {};
1846+
struct Base3Virtual : virtual Base {};
1847+
struct Derived : Base {};
1848+
struct DerivedIndirect : Base3 {};
1849+
struct DerivedMultiple : Base, Base2 {};
1850+
struct DerivedAmbiguous : Base, Base3 {};
1851+
/* expected-warning@-1 {{direct base 'Base' is inaccessible due to ambiguity:
1852+
struct IPIBO::DerivedAmbiguous -> Base
1853+
struct IPIBO::DerivedAmbiguous -> Base3 -> Base}} */
1854+
struct DerivedPrivate : private Base {};
1855+
struct DerivedVirtual : virtual Base {};
1856+
1857+
union Union {};
1858+
union UnionIncomplete;
1859+
struct StructIncomplete; // #StructIncomplete
1860+
1861+
void is_pointer_interconvertible_base_of(int n)
1862+
{
1863+
static_assert(__is_pointer_interconvertible_base_of(Base, Derived));
1864+
static_assert(!__is_pointer_interconvertible_base_of(Base2, Derived));
1865+
static_assert(__is_pointer_interconvertible_base_of(Base, DerivedIndirect));
1866+
static_assert(__is_pointer_interconvertible_base_of(Base, DerivedMultiple));
1867+
static_assert(!__is_pointer_interconvertible_base_of(Base3, DerivedMultiple));
1868+
static_assert(!__is_pointer_interconvertible_base_of(Base, DerivedAmbiguous));
1869+
static_assert(__is_pointer_interconvertible_base_of(Base, DerivedPrivate));
1870+
static_assert(!__is_pointer_interconvertible_base_of(Base, DerivedVirtual));
1871+
static_assert(!__is_pointer_interconvertible_base_of(Union, Union));
1872+
static_assert(!__is_pointer_interconvertible_base_of(UnionIncomplete, UnionIncomplete));
1873+
static_assert(__is_pointer_interconvertible_base_of(StructIncomplete, StructIncomplete));
1874+
static_assert(__is_pointer_interconvertible_base_of(StructIncomplete, const StructIncomplete));
1875+
static_assert(__is_pointer_interconvertible_base_of(StructIncomplete, volatile StructIncomplete));
1876+
static_assert(__is_pointer_interconvertible_base_of(const StructIncomplete, volatile StructIncomplete));
1877+
static_assert(!__is_pointer_interconvertible_base_of(StructIncomplete, Derived));
1878+
static_assert(!__is_pointer_interconvertible_base_of(Base, StructIncomplete));
1879+
// expected-error@-1 {{incomplete type 'StructIncomplete' where a complete type is required}}
1880+
// expected-note@#StructIncomplete {{forward declaration of 'IPIBO::StructIncomplete'}}
1881+
static_assert(!__is_pointer_interconvertible_base_of(CStruct2, CppStructNonStandardByBase2));
1882+
static_assert(!__is_pointer_interconvertible_base_of(void, void));
1883+
static_assert(!__is_pointer_interconvertible_base_of(void, int));
1884+
static_assert(!__is_pointer_interconvertible_base_of(void, const void));
1885+
static_assert(!__is_pointer_interconvertible_base_of(void, volatile void));
1886+
static_assert(!__is_pointer_interconvertible_base_of(const void, volatile void));
1887+
static_assert(!__is_pointer_interconvertible_base_of(int, int));
1888+
static_assert(!__is_pointer_interconvertible_base_of(int, const int));
1889+
static_assert(!__is_pointer_interconvertible_base_of(int, volatile int));
1890+
static_assert(!__is_pointer_interconvertible_base_of(const int, volatile int));
1891+
static_assert(!__is_pointer_interconvertible_base_of(int *, int * __restrict));
1892+
static_assert(!__is_pointer_interconvertible_base_of(int, _Atomic int));
1893+
static_assert(!__is_pointer_interconvertible_base_of(_Atomic(int), _Atomic int));
1894+
static_assert(!__is_pointer_interconvertible_base_of(int, unsigned int));
1895+
static_assert(!__is_pointer_interconvertible_base_of(char, unsigned char));
1896+
static_assert(!__is_pointer_interconvertible_base_of(char, signed char));
1897+
static_assert(!__is_pointer_interconvertible_base_of(unsigned char, signed char));
1898+
using function_type = void();
1899+
using function_type2 = void(char);
1900+
static_assert(!__is_pointer_interconvertible_base_of(const function_type, const function_type));
1901+
// expected-warning@-1 {{'const' qualifier on function type 'function_type' (aka 'void ()') has no effect}}
1902+
// expected-warning@-2 {{'const' qualifier on function type 'function_type' (aka 'void ()') has no effect}}
1903+
static_assert(!__is_pointer_interconvertible_base_of(function_type, const function_type));
1904+
// expected-warning@-1 {{'const' qualifier on function type 'function_type' (aka 'void ()') has no effect}}
1905+
static_assert(!__is_pointer_interconvertible_base_of(const function_type, const function_type2));
1906+
// expected-warning@-1 {{'const' qualifier on function type 'function_type' (aka 'void ()') has no effect}}
1907+
// expected-warning@-2 {{'const' qualifier on function type 'function_type2' (aka 'void (char)') has no effect}}
1908+
static_assert(!__is_pointer_interconvertible_base_of(int CStruct2::*, int CStruct2::*));
1909+
static_assert(!__is_pointer_interconvertible_base_of(int CStruct2::*, char CStruct2::*));
1910+
static_assert(!__is_pointer_interconvertible_base_of(void(CStruct2::*)(int), void(CStruct2::*)(int)));
1911+
static_assert(!__is_pointer_interconvertible_base_of(void(CStruct2::*)(int), void(CStruct2::*)(char)));
1912+
static_assert(!__is_pointer_interconvertible_base_of(int[], int[]));
1913+
static_assert(!__is_pointer_interconvertible_base_of(int[], double[]));
1914+
static_assert(!__is_pointer_interconvertible_base_of(int[2], int[2]));
1915+
static_assert(!__is_pointer_interconvertible_base_of(int[n], int[2]));
1916+
// expected-error@-1 {{variable length arrays are not supported in '__is_pointer_interconvertible_base_of'}}
1917+
static_assert(!__is_pointer_interconvertible_base_of(int[n], int[n]));
1918+
// expected-error@-1 {{variable length arrays are not supported in '__is_pointer_interconvertible_base_of'}}
1919+
// expected-error@-2 {{variable length arrays are not supported in '__is_pointer_interconvertible_base_of'}}
1920+
static_assert(!__is_pointer_interconvertible_base_of(int&, int&));
1921+
static_assert(!__is_pointer_interconvertible_base_of(int&, char&));
1922+
static_assert(!__is_pointer_interconvertible_base_of(void(int), void(int)));
1923+
static_assert(!__is_pointer_interconvertible_base_of(void(int), void(char)));
1924+
static_assert(!__is_pointer_interconvertible_base_of(void(&)(int), void(&)(int)));
1925+
static_assert(!__is_pointer_interconvertible_base_of(void(&)(int), void(&)(char)));
1926+
static_assert(!__is_pointer_interconvertible_base_of(void(*)(int), void(*)(int)));
1927+
static_assert(!__is_pointer_interconvertible_base_of(void(*)(int), void(*)(char)));
1928+
}
1929+
}
1930+
18421931
void is_signed()
18431932
{
18441933
//static_assert(__is_signed(char));

0 commit comments

Comments
 (0)