Skip to content
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
1 change: 1 addition & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1582,6 +1582,7 @@ The following type trait primitives are supported by Clang. Those traits marked
* ``__is_integral`` (C++, Embarcadero)
* ``__is_interface_class`` (Microsoft):
Returns ``false``, even for types defined with ``__interface``.
* ``__is_layout_compatible`` (C++, GNU, Microsoft)
* ``__is_literal`` (Clang):
Synonym for ``__is_literal_type``.
* ``__is_literal_type`` (C++, GNU, Microsoft):
Expand Down
6 changes: 6 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ C++20 Feature Support
behavior can use the flag '-Xclang -fno-skip-odr-check-in-gmf'.
(`#79240 <https://github.com/llvm/llvm-project/issues/79240>`_).

- Implemented the `__is_layout_compatible` intrinsic to support
`P0466R5: Layout-compatibility and Pointer-interconvertibility Traits <https://wg21.link/P0466R5>`_.
Note: `CWG1719: Layout compatibility and cv-qualification revisited <https://cplusplus.github.io/CWG/issues/1719.html>`_
and `CWG2759: [[no_unique_address] and common initial sequence <https://cplusplus.github.io/CWG/issues/2759.html>`_
are not yet implemented.

C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^

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 @@ -520,6 +520,7 @@ TYPE_TRAIT_1(__is_trivially_copyable, IsTriviallyCopyable, KEYCXX)
TYPE_TRAIT_1(__is_union, IsUnion, KEYCXX)
TYPE_TRAIT_1(__has_unique_object_representations,
HasUniqueObjectRepresentations, KEYCXX)
TYPE_TRAIT_2(__is_layout_compatible, IsLayoutCompatible, 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 @@ -14040,6 +14040,8 @@ class Sema final {
bool SemaValueIsRunOfOnes(CallExpr *TheCall, unsigned ArgNum);

public:
bool IsLayoutCompatible(QualType T1, QualType T2) const;

// Used by C++ template instantiation.
ExprResult SemaBuiltinShuffleVector(CallExpr *TheCall);
ExprResult SemaConvertVectorExpr(Expr *E, TypeSourceInfo *TInfo,
Expand Down
1 change: 1 addition & 0 deletions clang/lib/Parse/ParseExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1114,6 +1114,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
REVERTIBLE_TYPE_TRAIT(__is_fundamental);
REVERTIBLE_TYPE_TRAIT(__is_integral);
REVERTIBLE_TYPE_TRAIT(__is_interface_class);
REVERTIBLE_TYPE_TRAIT(__is_layout_compatible);
REVERTIBLE_TYPE_TRAIT(__is_literal);
REVERTIBLE_TYPE_TRAIT(__is_lvalue_expr);
REVERTIBLE_TYPE_TRAIT(__is_lvalue_reference);
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19150,6 +19150,10 @@ static bool isLayoutCompatible(ASTContext &C, QualType T1, QualType T2) {
return false;
}

bool Sema::IsLayoutCompatible(QualType T1, QualType T2) const {
return isLayoutCompatible(getASTContext(), T1, T2);
}

//===--- CHECK: pointer_with_type_tag attribute: datatypes should match ----//

/// Given a type tag expression find the type tag itself.
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Sema/SemaExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5922,6 +5922,9 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, QualType LhsT,

llvm_unreachable("unhandled type trait");
return false;
}
case BTT_IsLayoutCompatible: {
return Self.IsLayoutCompatible(LhsT, RhsT);
}
default: llvm_unreachable("not a BTT");
}
Expand Down
2 changes: 2 additions & 0 deletions clang/test/CXX/drs/dr13xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,8 @@ namespace dr1330 { // dr1330: 4 c++11
E e; // #dr1330-e
}

// dr1334: sup 1719

namespace dr1341 { // dr1341: sup P0683R1
#if __cplusplus >= 202002L
int a;
Expand Down
28 changes: 28 additions & 0 deletions clang/test/CXX/drs/dr17xx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,34 @@ namespace dr1715 { // dr1715: 3.9
#endif
}

namespace dr1719 { // dr1719: no
#if __cplusplus >= 201103L
struct CStruct {
int one;
int two;
};

struct CStruct2 {
int one;
int two;
};

struct CStructWithQualifiers {
const int one;
volatile int two;
};

static_assert(__is_layout_compatible(CStruct, const CStruct2), "");
static_assert(__is_layout_compatible(CStruct, volatile CStruct2), "");
static_assert(__is_layout_compatible(const CStruct, volatile CStruct2), "");
// FIXME: all of the following pairs of types are layout-compatible
static_assert(!__is_layout_compatible(int, const int), "");
static_assert(!__is_layout_compatible(int, volatile int), "");
static_assert(!__is_layout_compatible(const int, volatile int), "");
static_assert(!__is_layout_compatible(CStruct, CStructWithQualifiers), "");
#endif
} // namespace dr1719

namespace dr1722 { // dr1722: 9
#if __cplusplus >= 201103L
void f() {
Expand Down
232 changes: 232 additions & 0 deletions clang/test/SemaCXX/type-traits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1558,6 +1558,238 @@ void is_standard_layout()
int t71[F(__is_standard_layout(HasEmptyIndirectBaseAsSecondUnionMember))];
}

struct CStruct2 {
int one;
int two;
};

struct CEmptyStruct2 {};

struct CppEmptyStruct2 : CStruct2 {};
struct CppStructStandard2 : CEmptyStruct2 {
int three;
int four;
};
struct CppStructNonStandardByBase2 : CStruct2 {
int three;
int four;
};
struct CppStructNonStandardByVirt2 : CStruct2 {
virtual void method() {}
};
struct CppStructNonStandardByMemb2 : CStruct2 {
CppStructNonStandardByVirt member;
};
struct CppStructNonStandardByProt2 : CStruct2 {
int five;
protected:
int six;
};
struct CppStructNonStandardByVirtBase2 : virtual CStruct2 {
};
struct CppStructNonStandardBySameBase2 : CEmptyStruct2 {
CEmptyStruct member;
};
struct CppStructNonStandardBy2ndVirtBase2 : CEmptyStruct2 {
CEmptyStruct member;
};

struct CStructWithQualifiers {
const int one;
volatile int two;
};

struct CStructNoUniqueAddress {
int one;
[[no_unique_address]] int two;
};

struct CStructNoUniqueAddress2 {
int one;
[[no_unique_address]] int two;
};

struct CStructAlignment {
int one;
alignas(16) int two;
};

enum EnumLayout : int {};
enum class EnumClassLayout {};
enum EnumForward : int;
enum class EnumClassForward;

struct CStructIncomplete;

struct CStructNested {
int a;
CStruct s;
int b;
};

struct CStructNested2 {
int a2;
CStruct s2;
int b2;
};

struct CStructWithBitfelds {
int a : 5;
int : 0;
};

struct CStructWithBitfelds2 {
int a : 5;
int : 0;
};

struct CStructWithBitfelds3 {
int : 0;
int b : 5;
};

struct CStructWithBitfelds4 {
EnumLayout a : 5;
int : 0;
};

union UnionLayout {
int a;
double b;
CStruct c;
[[no_unique_address]] CEmptyStruct d;
[[no_unique_address]] CEmptyStruct2 e;
};

union UnionLayout2 {
CStruct c;
int a;
CEmptyStruct2 e;
double b;
[[no_unique_address]] CEmptyStruct d;
};

union UnionLayout3 {
CStruct c;
int a;
double b;
[[no_unique_address]] CEmptyStruct d;
};

struct StructWithAnonUnion {
union {
int a;
double b;
CStruct c;
[[no_unique_address]] CEmptyStruct d;
[[no_unique_address]] CEmptyStruct2 e;
};
};

struct StructWithAnonUnion2 {
union {
CStruct c;
int a;
CEmptyStruct2 e;
double b;
[[no_unique_address]] CEmptyStruct d;
};
};

struct StructWithAnonUnion3 {
union {
CStruct c;
int a;
CEmptyStruct2 e;
double b;
[[no_unique_address]] CEmptyStruct d;
} u;
};


void is_layout_compatible(int n)
{
static_assert(__is_layout_compatible(void, void), "");
static_assert(!__is_layout_compatible(void, int), "");
static_assert(!__is_layout_compatible(void, const void), ""); // FIXME: this is CWG1719
static_assert(!__is_layout_compatible(void, volatile void), ""); // FIXME: this is CWG1719
static_assert(!__is_layout_compatible(const int, volatile int), ""); // FIXME: this is CWG1719
static_assert(__is_layout_compatible(int, int), "");
static_assert(!__is_layout_compatible(int, const int), ""); // FIXME: this is CWG1719
static_assert(!__is_layout_compatible(int, volatile int), ""); // FIXME: this is CWG1719
static_assert(!__is_layout_compatible(const int, volatile int), ""); // FIXME: this is CWG1719
static_assert(!__is_layout_compatible(int, unsigned int), "");
static_assert(!__is_layout_compatible(char, unsigned char), "");
static_assert(!__is_layout_compatible(char, signed char), "");
static_assert(!__is_layout_compatible(unsigned char, signed char), "");
static_assert(__is_layout_compatible(int[], int[]), "");
static_assert(__is_layout_compatible(int[2], int[2]), "");
static_assert(!__is_layout_compatible(int[n], int[2]), ""); // FIXME: VLAs should be rejected
static_assert(!__is_layout_compatible(int[n], int[n]), ""); // FIXME: VLAs should be rejected
static_assert(__is_layout_compatible(int&, int&), "");
static_assert(!__is_layout_compatible(int&, char&), "");
static_assert(__is_layout_compatible(void(int), void(int)), "");
static_assert(!__is_layout_compatible(void(int), void(char)), "");
static_assert(__is_layout_compatible(void(&)(int), void(&)(int)), "");
static_assert(!__is_layout_compatible(void(&)(int), void(&)(char)), "");
static_assert(__is_layout_compatible(void(*)(int), void(*)(int)), "");
static_assert(!__is_layout_compatible(void(*)(int), void(*)(char)), "");
using function_type = void();
using function_type2 = void(char);
static_assert(__is_layout_compatible(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_layout_compatible(function_type, const function_type), "");
// expected-warning@-1 {{'const' qualifier on function type 'function_type' (aka 'void ()') has no effect}}
static_assert(!__is_layout_compatible(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_layout_compatible(CStruct, CStruct2), "");
static_assert(__is_layout_compatible(CStruct, const CStruct2), "");
static_assert(__is_layout_compatible(CStruct, volatile CStruct2), "");
static_assert(__is_layout_compatible(const CStruct, volatile CStruct2), "");
static_assert(__is_layout_compatible(CEmptyStruct, CEmptyStruct2), "");
static_assert(__is_layout_compatible(CppEmptyStruct, CppEmptyStruct2), "");
static_assert(__is_layout_compatible(CppStructStandard, CppStructStandard2), "");
static_assert(!__is_layout_compatible(CppStructNonStandardByBase, CppStructNonStandardByBase2), "");
static_assert(!__is_layout_compatible(CppStructNonStandardByVirt, CppStructNonStandardByVirt2), "");
static_assert(!__is_layout_compatible(CppStructNonStandardByMemb, CppStructNonStandardByMemb2), "");
static_assert(!__is_layout_compatible(CppStructNonStandardByProt, CppStructNonStandardByProt2), "");
static_assert(!__is_layout_compatible(CppStructNonStandardByVirtBase, CppStructNonStandardByVirtBase2), "");
static_assert(!__is_layout_compatible(CppStructNonStandardBySameBase, CppStructNonStandardBySameBase2), "");
static_assert(!__is_layout_compatible(CppStructNonStandardBy2ndVirtBase, CppStructNonStandardBy2ndVirtBase2), "");
static_assert(!__is_layout_compatible(CStruct, CStructWithQualifiers), ""); // FIXME: this is CWG1719
static_assert(__is_layout_compatible(CStruct, CStructNoUniqueAddress) == bool(__has_cpp_attribute(no_unique_address)), ""); // FIXME: this is CWG2759
static_assert(__is_layout_compatible(CStructNoUniqueAddress, CStructNoUniqueAddress2) == bool(__has_cpp_attribute(no_unique_address)), ""); // FIXME: this is CWG2759
static_assert(__is_layout_compatible(CStruct, CStructAlignment), "");
static_assert(__is_layout_compatible(CStructWithBitfelds, CStructWithBitfelds), "");
static_assert(__is_layout_compatible(CStructWithBitfelds, CStructWithBitfelds2), "");
static_assert(!__is_layout_compatible(CStructWithBitfelds, CStructWithBitfelds3), "");
static_assert(!__is_layout_compatible(CStructWithBitfelds, CStructWithBitfelds4), "");
static_assert(__is_layout_compatible(int CStruct2::*, int CStruct2::*), "");
static_assert(!__is_layout_compatible(int CStruct2::*, char CStruct2::*), "");
static_assert(__is_layout_compatible(void(CStruct2::*)(int), void(CStruct2::*)(int)), "");
static_assert(!__is_layout_compatible(void(CStruct2::*)(int), void(CStruct2::*)(char)), "");
static_assert(__is_layout_compatible(CStructNested, CStructNested2), "");
static_assert(__is_layout_compatible(UnionLayout, UnionLayout), "");
static_assert(__is_layout_compatible(UnionLayout, UnionLayout2), "");
static_assert(!__is_layout_compatible(UnionLayout, UnionLayout3), "");
static_assert(__is_layout_compatible(StructWithAnonUnion, StructWithAnonUnion2), "");
static_assert(__is_layout_compatible(StructWithAnonUnion, StructWithAnonUnion3), "");
static_assert(__is_layout_compatible(EnumLayout, EnumClassLayout), "");
static_assert(__is_layout_compatible(EnumForward, EnumForward), "");
static_assert(__is_layout_compatible(EnumForward, EnumClassForward), "");
// Layout compatibility for enums might be relaxed in the future. See https://github.com/cplusplus/CWG/issues/39#issuecomment-1184791364
static_assert(!__is_layout_compatible(EnumLayout, int), "");
static_assert(!__is_layout_compatible(EnumClassLayout, int), "");
static_assert(!__is_layout_compatible(EnumForward, int), "");
static_assert(!__is_layout_compatible(EnumClassForward, int), "");
// FIXME: the following should be rejected (array of unknown bound and void are the only allowed incomplete types)
static_assert(__is_layout_compatible(CStructIncomplete, CStructIncomplete), "");
static_assert(!__is_layout_compatible(CStruct, CStructIncomplete), "");
static_assert(__is_layout_compatible(CStructIncomplete[2], CStructIncomplete[2]), "");
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some additional test cases to consider:

  • A type is layout compatible with its qualified version. e.g., SomeStruct and const SomeStruct
  • Layout compatibility of function types (not function pointer types), e.g., void(int) and void(int)
  • Diagnostics when given an incomplete type for either operand
  • That int and unsigned int are not layout compatible
  • That char and signed char/unsigned char are not layout compatible
  • That an enumeration type and its underlying type are not layout compatible

If this catches bugs in the implementation of isLayoutCompatible(), that' fine, we can document the current behavior in the test with a FIXME and come back to addressing the issues in a follow-up.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • A type is layout compatible with its qualified version. e.g., SomeStruct and const SomeStruct

We do not yet implement https://cplusplus.github.io/CWG/issues/1719.html, so some tests are better deferred to the implementation of that DR

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should add those tests somewhere (either here or in dr1xxx.cpp) so 1) we know we don't crash on them, 2) we're alerted to behavioral changes in this area, and 3) better documentation of existing behavior.

Copy link
Contributor Author

@Endilll Endilll Feb 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A type is layout compatible with its qualified version. e.g., SomeStruct and const SomeStruct

Done. It even works correctly for class types.

Layout compatibility of function types (not function pointer types), e.g., void(int) and void(int)

Done. I added tests for function types, reference to functions, pointers to function, pointers to data members, pointers to member functions.

Diagnostics when given an incomplete type for either operand

We don't issue any, as far as I can see. And I don't think we can when the types are the same, ignoring cv qualifiers:

Two types cv1 T1 and cv2 T2 are layout-compatible types if T1 and T2 are the same type, layout-compatible enumerations, or layout-compatible standard-layout class types.

That int and unsigned int are not layout compatible
That char and signed char/unsigned char are not layout compatible
That an enumeration type and its underlying type are not layout compatible

Done.

I think we should add those tests somewhere (either here or in dr1xxx.cpp) so 1) we know we don't crash on them, 2) we're alerted to behavioral changes in this area, and 3) better documentation of existing behavior.

Agreed. I marked 1334 as superseded by 1719, and added a test for 1719.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't issue any, as far as I can see. And I don't think we can when the types are the same, ignoring cv qualifiers:

That's a bug, see https://eel.is/c++draft/tab:meta.rel#row-6-column-3-sentence-1: "T and U shall be complete types, cv void, or arrays of unknown bound."

but also: https://eel.is/c++draft/tab:meta.rel#row-6-column-3-sentence-1and http://eel.is/c++draft/class.mem#general-23 -- you can't test for layout compatibility if the class type is not complete because you don't know the members. So we should accept void but not struct NeverDefined.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • That an enumeration type and its underlying type are not layout compatible

This is possibly a defect (cplusplus/CWG#39 (comment), cplusplus/CWG#95 (comment)), but no CWG issue is filed yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for mentioning that! I left a comment around enum tests.


void is_signed()
{
//int t01[T(__is_signed(char))];
Expand Down