Skip to content

Commit d5922cf

Browse files
authored
[clang] Implement __is_layout_compatible (#81506)
This patch implements `__is_layout_compatible` intrinsic, which supports `std::is_layout_compatible` type trait introduced in C++20 ([P0466R5](https://wg21.link/p0466r5) "Layout-compatibility and Pointer-interconvertibility Traits"). Name matches GCC and MSVC intrinsic. Basically, this patch exposes our existing machinery for checking for layout compatibility and figuring out common initial sequences. Said machinery is a bit outdated, as it doesn't implement [CWG1719](https://cplusplus.github.io/CWG/issues/1719.html) "Layout compatibility and cv-qualification revisited" and [CWG2759](https://cplusplus.github.io/CWG/issues/2759.html) "`[[no_unique_address]` and common initial sequence". Those defect reports are considered out of scope of of this PR, but will be implemented in subsequent PRs. Partially addresses #48204
1 parent f1efc64 commit d5922cf

File tree

10 files changed

+280
-0
lines changed

10 files changed

+280
-0
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1582,6 +1582,7 @@ The following type trait primitives are supported by Clang. Those traits marked
15821582
* ``__is_integral`` (C++, Embarcadero)
15831583
* ``__is_interface_class`` (Microsoft):
15841584
Returns ``false``, even for types defined with ``__interface``.
1585+
* ``__is_layout_compatible`` (C++, GNU, Microsoft)
15851586
* ``__is_literal`` (Clang):
15861587
Synonym for ``__is_literal_type``.
15871588
* ``__is_literal_type`` (C++, GNU, Microsoft):

clang/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,12 @@ C++20 Feature Support
9696
behavior can use the flag '-Xclang -fno-skip-odr-check-in-gmf'.
9797
(`#79240 <https://github.com/llvm/llvm-project/issues/79240>`_).
9898

99+
- Implemented the `__is_layout_compatible` intrinsic to support
100+
`P0466R5: Layout-compatibility and Pointer-interconvertibility Traits <https://wg21.link/P0466R5>`_.
101+
Note: `CWG1719: Layout compatibility and cv-qualification revisited <https://cplusplus.github.io/CWG/issues/1719.html>`_
102+
and `CWG2759: [[no_unique_address] and common initial sequence <https://cplusplus.github.io/CWG/issues/2759.html>`_
103+
are not yet implemented.
104+
99105
C++23 Feature Support
100106
^^^^^^^^^^^^^^^^^^^^^
101107

clang/include/clang/Basic/TokenKinds.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,7 @@ TYPE_TRAIT_1(__is_trivially_copyable, IsTriviallyCopyable, KEYCXX)
520520
TYPE_TRAIT_1(__is_union, IsUnion, KEYCXX)
521521
TYPE_TRAIT_1(__has_unique_object_representations,
522522
HasUniqueObjectRepresentations, KEYCXX)
523+
TYPE_TRAIT_2(__is_layout_compatible, IsLayoutCompatible, KEYCXX)
523524

524525
#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) KEYWORD(__##Trait, KEYCXX)
525526
#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
@@ -14078,6 +14078,8 @@ class Sema final {
1407814078
bool SemaValueIsRunOfOnes(CallExpr *TheCall, unsigned ArgNum);
1407914079

1408014080
public:
14081+
bool IsLayoutCompatible(QualType T1, QualType T2) const;
14082+
1408114083
// Used by C++ template instantiation.
1408214084
ExprResult SemaBuiltinShuffleVector(CallExpr *TheCall);
1408314085
ExprResult SemaConvertVectorExpr(Expr *E, TypeSourceInfo *TInfo,

clang/lib/Parse/ParseExpr.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1114,6 +1114,7 @@ ExprResult Parser::ParseCastExpression(CastParseKind ParseKind,
11141114
REVERTIBLE_TYPE_TRAIT(__is_fundamental);
11151115
REVERTIBLE_TYPE_TRAIT(__is_integral);
11161116
REVERTIBLE_TYPE_TRAIT(__is_interface_class);
1117+
REVERTIBLE_TYPE_TRAIT(__is_layout_compatible);
11171118
REVERTIBLE_TYPE_TRAIT(__is_literal);
11181119
REVERTIBLE_TYPE_TRAIT(__is_lvalue_expr);
11191120
REVERTIBLE_TYPE_TRAIT(__is_lvalue_reference);

clang/lib/Sema/SemaChecking.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19155,6 +19155,10 @@ static bool isLayoutCompatible(ASTContext &C, QualType T1, QualType T2) {
1915519155
return false;
1915619156
}
1915719157

19158+
bool Sema::IsLayoutCompatible(QualType T1, QualType T2) const {
19159+
return isLayoutCompatible(getASTContext(), T1, T2);
19160+
}
19161+
1915819162
//===--- CHECK: pointer_with_type_tag attribute: datatypes should match ----//
1915919163

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

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6010,6 +6010,9 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, QualType LhsT,
60106010

60116011
llvm_unreachable("unhandled type trait");
60126012
return false;
6013+
}
6014+
case BTT_IsLayoutCompatible: {
6015+
return Self.IsLayoutCompatible(LhsT, RhsT);
60136016
}
60146017
default: llvm_unreachable("not a BTT");
60156018
}

clang/test/CXX/drs/dr13xx.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,8 @@ namespace dr1330 { // dr1330: 4 c++11
307307
E e; // #dr1330-e
308308
}
309309

310+
// dr1334: sup 1719
311+
310312
namespace dr1341 { // dr1341: sup P0683R1
311313
#if __cplusplus >= 202002L
312314
int a;

clang/test/CXX/drs/dr17xx.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,34 @@ namespace dr1715 { // dr1715: 3.9
4646
#endif
4747
}
4848

49+
namespace dr1719 { // dr1719: no
50+
#if __cplusplus >= 201103L
51+
struct CStruct {
52+
int one;
53+
int two;
54+
};
55+
56+
struct CStruct2 {
57+
int one;
58+
int two;
59+
};
60+
61+
struct CStructWithQualifiers {
62+
const int one;
63+
volatile int two;
64+
};
65+
66+
static_assert(__is_layout_compatible(CStruct, const CStruct2), "");
67+
static_assert(__is_layout_compatible(CStruct, volatile CStruct2), "");
68+
static_assert(__is_layout_compatible(const CStruct, volatile CStruct2), "");
69+
// FIXME: all of the following pairs of types are layout-compatible
70+
static_assert(!__is_layout_compatible(int, const int), "");
71+
static_assert(!__is_layout_compatible(int, volatile int), "");
72+
static_assert(!__is_layout_compatible(const int, volatile int), "");
73+
static_assert(!__is_layout_compatible(CStruct, CStructWithQualifiers), "");
74+
#endif
75+
} // namespace dr1719
76+
4977
namespace dr1722 { // dr1722: 9
5078
#if __cplusplus >= 201103L
5179
void f() {

clang/test/SemaCXX/type-traits.cpp

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,6 +1558,238 @@ void is_standard_layout()
15581558
int t71[F(__is_standard_layout(HasEmptyIndirectBaseAsSecondUnionMember))];
15591559
}
15601560

1561+
struct CStruct2 {
1562+
int one;
1563+
int two;
1564+
};
1565+
1566+
struct CEmptyStruct2 {};
1567+
1568+
struct CppEmptyStruct2 : CStruct2 {};
1569+
struct CppStructStandard2 : CEmptyStruct2 {
1570+
int three;
1571+
int four;
1572+
};
1573+
struct CppStructNonStandardByBase2 : CStruct2 {
1574+
int three;
1575+
int four;
1576+
};
1577+
struct CppStructNonStandardByVirt2 : CStruct2 {
1578+
virtual void method() {}
1579+
};
1580+
struct CppStructNonStandardByMemb2 : CStruct2 {
1581+
CppStructNonStandardByVirt member;
1582+
};
1583+
struct CppStructNonStandardByProt2 : CStruct2 {
1584+
int five;
1585+
protected:
1586+
int six;
1587+
};
1588+
struct CppStructNonStandardByVirtBase2 : virtual CStruct2 {
1589+
};
1590+
struct CppStructNonStandardBySameBase2 : CEmptyStruct2 {
1591+
CEmptyStruct member;
1592+
};
1593+
struct CppStructNonStandardBy2ndVirtBase2 : CEmptyStruct2 {
1594+
CEmptyStruct member;
1595+
};
1596+
1597+
struct CStructWithQualifiers {
1598+
const int one;
1599+
volatile int two;
1600+
};
1601+
1602+
struct CStructNoUniqueAddress {
1603+
int one;
1604+
[[no_unique_address]] int two;
1605+
};
1606+
1607+
struct CStructNoUniqueAddress2 {
1608+
int one;
1609+
[[no_unique_address]] int two;
1610+
};
1611+
1612+
struct CStructAlignment {
1613+
int one;
1614+
alignas(16) int two;
1615+
};
1616+
1617+
enum EnumLayout : int {};
1618+
enum class EnumClassLayout {};
1619+
enum EnumForward : int;
1620+
enum class EnumClassForward;
1621+
1622+
struct CStructIncomplete;
1623+
1624+
struct CStructNested {
1625+
int a;
1626+
CStruct s;
1627+
int b;
1628+
};
1629+
1630+
struct CStructNested2 {
1631+
int a2;
1632+
CStruct s2;
1633+
int b2;
1634+
};
1635+
1636+
struct CStructWithBitfelds {
1637+
int a : 5;
1638+
int : 0;
1639+
};
1640+
1641+
struct CStructWithBitfelds2 {
1642+
int a : 5;
1643+
int : 0;
1644+
};
1645+
1646+
struct CStructWithBitfelds3 {
1647+
int : 0;
1648+
int b : 5;
1649+
};
1650+
1651+
struct CStructWithBitfelds4 {
1652+
EnumLayout a : 5;
1653+
int : 0;
1654+
};
1655+
1656+
union UnionLayout {
1657+
int a;
1658+
double b;
1659+
CStruct c;
1660+
[[no_unique_address]] CEmptyStruct d;
1661+
[[no_unique_address]] CEmptyStruct2 e;
1662+
};
1663+
1664+
union UnionLayout2 {
1665+
CStruct c;
1666+
int a;
1667+
CEmptyStruct2 e;
1668+
double b;
1669+
[[no_unique_address]] CEmptyStruct d;
1670+
};
1671+
1672+
union UnionLayout3 {
1673+
CStruct c;
1674+
int a;
1675+
double b;
1676+
[[no_unique_address]] CEmptyStruct d;
1677+
};
1678+
1679+
struct StructWithAnonUnion {
1680+
union {
1681+
int a;
1682+
double b;
1683+
CStruct c;
1684+
[[no_unique_address]] CEmptyStruct d;
1685+
[[no_unique_address]] CEmptyStruct2 e;
1686+
};
1687+
};
1688+
1689+
struct StructWithAnonUnion2 {
1690+
union {
1691+
CStruct c;
1692+
int a;
1693+
CEmptyStruct2 e;
1694+
double b;
1695+
[[no_unique_address]] CEmptyStruct d;
1696+
};
1697+
};
1698+
1699+
struct StructWithAnonUnion3 {
1700+
union {
1701+
CStruct c;
1702+
int a;
1703+
CEmptyStruct2 e;
1704+
double b;
1705+
[[no_unique_address]] CEmptyStruct d;
1706+
} u;
1707+
};
1708+
1709+
1710+
void is_layout_compatible(int n)
1711+
{
1712+
static_assert(__is_layout_compatible(void, void), "");
1713+
static_assert(!__is_layout_compatible(void, int), "");
1714+
static_assert(!__is_layout_compatible(void, const void), ""); // FIXME: this is CWG1719
1715+
static_assert(!__is_layout_compatible(void, volatile void), ""); // FIXME: this is CWG1719
1716+
static_assert(!__is_layout_compatible(const int, volatile int), ""); // FIXME: this is CWG1719
1717+
static_assert(__is_layout_compatible(int, int), "");
1718+
static_assert(!__is_layout_compatible(int, const int), ""); // FIXME: this is CWG1719
1719+
static_assert(!__is_layout_compatible(int, volatile int), ""); // FIXME: this is CWG1719
1720+
static_assert(!__is_layout_compatible(const int, volatile int), ""); // FIXME: this is CWG1719
1721+
static_assert(!__is_layout_compatible(int, unsigned int), "");
1722+
static_assert(!__is_layout_compatible(char, unsigned char), "");
1723+
static_assert(!__is_layout_compatible(char, signed char), "");
1724+
static_assert(!__is_layout_compatible(unsigned char, signed char), "");
1725+
static_assert(__is_layout_compatible(int[], int[]), "");
1726+
static_assert(__is_layout_compatible(int[2], int[2]), "");
1727+
static_assert(!__is_layout_compatible(int[n], int[2]), ""); // FIXME: VLAs should be rejected
1728+
static_assert(!__is_layout_compatible(int[n], int[n]), ""); // FIXME: VLAs should be rejected
1729+
static_assert(__is_layout_compatible(int&, int&), "");
1730+
static_assert(!__is_layout_compatible(int&, char&), "");
1731+
static_assert(__is_layout_compatible(void(int), void(int)), "");
1732+
static_assert(!__is_layout_compatible(void(int), void(char)), "");
1733+
static_assert(__is_layout_compatible(void(&)(int), void(&)(int)), "");
1734+
static_assert(!__is_layout_compatible(void(&)(int), void(&)(char)), "");
1735+
static_assert(__is_layout_compatible(void(*)(int), void(*)(int)), "");
1736+
static_assert(!__is_layout_compatible(void(*)(int), void(*)(char)), "");
1737+
using function_type = void();
1738+
using function_type2 = void(char);
1739+
static_assert(__is_layout_compatible(const function_type, const function_type), "");
1740+
// expected-warning@-1 {{'const' qualifier on function type 'function_type' (aka 'void ()') has no effect}}
1741+
// expected-warning@-2 {{'const' qualifier on function type 'function_type' (aka 'void ()') has no effect}}
1742+
static_assert(__is_layout_compatible(function_type, const function_type), "");
1743+
// expected-warning@-1 {{'const' qualifier on function type 'function_type' (aka 'void ()') has no effect}}
1744+
static_assert(!__is_layout_compatible(const function_type, const function_type2), "");
1745+
// expected-warning@-1 {{'const' qualifier on function type 'function_type' (aka 'void ()') has no effect}}
1746+
// expected-warning@-2 {{'const' qualifier on function type 'function_type2' (aka 'void (char)') has no effect}}
1747+
static_assert(__is_layout_compatible(CStruct, CStruct2), "");
1748+
static_assert(__is_layout_compatible(CStruct, const CStruct2), "");
1749+
static_assert(__is_layout_compatible(CStruct, volatile CStruct2), "");
1750+
static_assert(__is_layout_compatible(const CStruct, volatile CStruct2), "");
1751+
static_assert(__is_layout_compatible(CEmptyStruct, CEmptyStruct2), "");
1752+
static_assert(__is_layout_compatible(CppEmptyStruct, CppEmptyStruct2), "");
1753+
static_assert(__is_layout_compatible(CppStructStandard, CppStructStandard2), "");
1754+
static_assert(!__is_layout_compatible(CppStructNonStandardByBase, CppStructNonStandardByBase2), "");
1755+
static_assert(!__is_layout_compatible(CppStructNonStandardByVirt, CppStructNonStandardByVirt2), "");
1756+
static_assert(!__is_layout_compatible(CppStructNonStandardByMemb, CppStructNonStandardByMemb2), "");
1757+
static_assert(!__is_layout_compatible(CppStructNonStandardByProt, CppStructNonStandardByProt2), "");
1758+
static_assert(!__is_layout_compatible(CppStructNonStandardByVirtBase, CppStructNonStandardByVirtBase2), "");
1759+
static_assert(!__is_layout_compatible(CppStructNonStandardBySameBase, CppStructNonStandardBySameBase2), "");
1760+
static_assert(!__is_layout_compatible(CppStructNonStandardBy2ndVirtBase, CppStructNonStandardBy2ndVirtBase2), "");
1761+
static_assert(!__is_layout_compatible(CStruct, CStructWithQualifiers), ""); // FIXME: this is CWG1719
1762+
static_assert(__is_layout_compatible(CStruct, CStructNoUniqueAddress) == bool(__has_cpp_attribute(no_unique_address)), ""); // FIXME: this is CWG2759
1763+
static_assert(__is_layout_compatible(CStructNoUniqueAddress, CStructNoUniqueAddress2) == bool(__has_cpp_attribute(no_unique_address)), ""); // FIXME: this is CWG2759
1764+
static_assert(__is_layout_compatible(CStruct, CStructAlignment), "");
1765+
static_assert(__is_layout_compatible(CStructWithBitfelds, CStructWithBitfelds), "");
1766+
static_assert(__is_layout_compatible(CStructWithBitfelds, CStructWithBitfelds2), "");
1767+
static_assert(!__is_layout_compatible(CStructWithBitfelds, CStructWithBitfelds3), "");
1768+
static_assert(!__is_layout_compatible(CStructWithBitfelds, CStructWithBitfelds4), "");
1769+
static_assert(__is_layout_compatible(int CStruct2::*, int CStruct2::*), "");
1770+
static_assert(!__is_layout_compatible(int CStruct2::*, char CStruct2::*), "");
1771+
static_assert(__is_layout_compatible(void(CStruct2::*)(int), void(CStruct2::*)(int)), "");
1772+
static_assert(!__is_layout_compatible(void(CStruct2::*)(int), void(CStruct2::*)(char)), "");
1773+
static_assert(__is_layout_compatible(CStructNested, CStructNested2), "");
1774+
static_assert(__is_layout_compatible(UnionLayout, UnionLayout), "");
1775+
static_assert(__is_layout_compatible(UnionLayout, UnionLayout2), "");
1776+
static_assert(!__is_layout_compatible(UnionLayout, UnionLayout3), "");
1777+
static_assert(__is_layout_compatible(StructWithAnonUnion, StructWithAnonUnion2), "");
1778+
static_assert(__is_layout_compatible(StructWithAnonUnion, StructWithAnonUnion3), "");
1779+
static_assert(__is_layout_compatible(EnumLayout, EnumClassLayout), "");
1780+
static_assert(__is_layout_compatible(EnumForward, EnumForward), "");
1781+
static_assert(__is_layout_compatible(EnumForward, EnumClassForward), "");
1782+
// Layout compatibility for enums might be relaxed in the future. See https://github.com/cplusplus/CWG/issues/39#issuecomment-1184791364
1783+
static_assert(!__is_layout_compatible(EnumLayout, int), "");
1784+
static_assert(!__is_layout_compatible(EnumClassLayout, int), "");
1785+
static_assert(!__is_layout_compatible(EnumForward, int), "");
1786+
static_assert(!__is_layout_compatible(EnumClassForward, int), "");
1787+
// FIXME: the following should be rejected (array of unknown bound and void are the only allowed incomplete types)
1788+
static_assert(__is_layout_compatible(CStructIncomplete, CStructIncomplete), "");
1789+
static_assert(!__is_layout_compatible(CStruct, CStructIncomplete), "");
1790+
static_assert(__is_layout_compatible(CStructIncomplete[2], CStructIncomplete[2]), "");
1791+
}
1792+
15611793
void is_signed()
15621794
{
15631795
//int t01[T(__is_signed(char))];

0 commit comments

Comments
 (0)