Skip to content

Commit 178e462

Browse files
committed
model the builtin as a type trait
1 parent d08ccf6 commit 178e462

File tree

20 files changed

+216
-173
lines changed

20 files changed

+216
-173
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -434,36 +434,6 @@ __datasizeof
434434
``__datasizeof`` behaves like ``sizeof``, except that it returns the size of the
435435
type ignoring tail padding.
436436

437-
.. _builtin_structured_binding_size-doc:
438-
439-
__builtin_structured_binding_size (C++)
440-
---------------------------------------
441-
``__builtin_structured_binding_size`` returns the *structured binding size*
442-
([dcl.struct.bind]) of the type ``T`` (or unevaluated expression ``arg``)
443-
passed as argument.
444-
445-
This is equivalent to the size of the pack ``p`` in ``auto&& [...p] = arg;``.
446-
If the argument is not destructurable (ie not a builtin array, builtin SIMD vector,
447-
builtin complex, *tuple-like* type or destructurable class type),
448-
``__builtin_structured_binding_size(T)`` is not a valid expression
449-
(``__builtin_structured_binding_size`` is SFINEA-friendly).
450-
451-
A type is considered a valid *tuple-like* if ``std::tuple_size_v<T>`` is a valid expression,
452-
even if there is no valid ``std::tuple_element`` specialization or suitable
453-
``get`` function for that type.
454-
455-
.. code-block:: c++
456-
457-
template<std::size_t Idx, typename T>
458-
requires (Idx < __builtin_structured_binding_size(T))
459-
decltype(auto) constexpr get_binding(T&& obj) {
460-
auto && [...p] = std::forward<T>(obj);
461-
return p...[Idx];
462-
}
463-
struct S { int a = 0, b = 42; };
464-
static_assert(__builtin_structured_binding_size(S) == 2);
465-
static_assert(get_binding<1>(S{}) == 42);
466-
467437

468438
_BitInt, _ExtInt
469439
----------------
@@ -1942,6 +1912,38 @@ A simplistic usage example as might be seen in standard C++ headers follows:
19421912
// Emulate type trait for compatibility with other compilers.
19431913
#endif
19441914

1915+
1916+
.. _builtin_structured_binding_size-doc:
1917+
1918+
__builtin_structured_binding_size (C++)
1919+
---------------------------------------
1920+
1921+
The ``__builtin_structured_binding_size(T)`` type trait returns
1922+
the *structured binding size* ([dcl.struct.bind]) of type ``T``
1923+
1924+
This is equivalent to the size of the pack ``p`` in ``auto&& [...p] = declval<T&>();``.
1925+
If the argument is not destructurable (ie not a builtin array, builtin SIMD vector,
1926+
builtin complex, *tuple-like* type or destructurable class type),
1927+
``__builtin_structured_binding_size(T)`` is not a valid expression
1928+
(``__builtin_structured_binding_size`` is SFINAE-friendly).
1929+
1930+
A type is considered a valid *tuple-like* if ``std::tuple_size_v<T>`` is a valid expression,
1931+
even if there is no valid ``std::tuple_element`` specialization or suitable
1932+
``get`` function for that type.
1933+
1934+
.. code-block:: c++
1935+
1936+
template<std::size_t Idx, typename T>
1937+
requires (Idx < __builtin_structured_binding_size(T))
1938+
decltype(auto) constexpr get_binding(T&& obj) {
1939+
auto && [...p] = std::forward<T>(obj);
1940+
return p...[Idx];
1941+
}
1942+
struct S { int a = 0, b = 42; };
1943+
static_assert(__builtin_structured_binding_size(S) == 2);
1944+
static_assert(get_binding<1>(S{}) == 42);
1945+
1946+
19451947
Blocks
19461948
======
19471949

clang/include/clang/AST/ExprCXX.h

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2765,7 +2765,7 @@ class CXXPseudoDestructorExpr : public Expr {
27652765
/// \endcode
27662766
class TypeTraitExpr final
27672767
: public Expr,
2768-
private llvm::TrailingObjects<TypeTraitExpr, TypeSourceInfo *> {
2768+
private llvm::TrailingObjects<TypeTraitExpr, APValue, TypeSourceInfo *> {
27692769
/// The location of the type trait keyword.
27702770
SourceLocation Loc;
27712771

@@ -2780,6 +2780,10 @@ class TypeTraitExpr final
27802780
SourceLocation RParenLoc,
27812781
bool Value);
27822782

2783+
TypeTraitExpr(QualType T, SourceLocation Loc, TypeTrait Kind,
2784+
ArrayRef<TypeSourceInfo *> Args, SourceLocation RParenLoc,
2785+
APValue Value);
2786+
27832787
TypeTraitExpr(EmptyShell Empty) : Expr(TypeTraitExprClass, Empty) {}
27842788

27852789
size_t numTrailingObjects(OverloadToken<TypeSourceInfo *>) const {
@@ -2798,19 +2802,34 @@ class TypeTraitExpr final
27982802
SourceLocation RParenLoc,
27992803
bool Value);
28002804

2805+
static TypeTraitExpr *Create(const ASTContext &C, QualType T,
2806+
SourceLocation Loc, TypeTrait Kind,
2807+
ArrayRef<TypeSourceInfo *> Args,
2808+
SourceLocation RParenLoc, APValue Value);
2809+
28012810
static TypeTraitExpr *CreateDeserialized(const ASTContext &C,
2811+
bool IsStoredAsBool,
28022812
unsigned NumArgs);
28032813

28042814
/// Determine which type trait this expression uses.
28052815
TypeTrait getTrait() const {
28062816
return static_cast<TypeTrait>(TypeTraitExprBits.Kind);
28072817
}
28082818

2809-
bool getValue() const {
2810-
assert(!isValueDependent());
2819+
bool isStoredAsBoolean() const {
2820+
return TypeTraitExprBits.IsBooleanTypeTrait;
2821+
}
2822+
2823+
bool getBoolValue() const {
2824+
assert(!isValueDependent() && TypeTraitExprBits.IsBooleanTypeTrait);
28112825
return TypeTraitExprBits.Value;
28122826
}
28132827

2828+
const APValue &getAPValue() const {
2829+
assert(!isValueDependent() && !TypeTraitExprBits.IsBooleanTypeTrait);
2830+
return *getTrailingObjects<APValue>();
2831+
}
2832+
28142833
/// Determine the number of arguments to this type trait.
28152834
unsigned getNumArgs() const { return TypeTraitExprBits.NumArgs; }
28162835

@@ -2840,6 +2859,10 @@ class TypeTraitExpr final
28402859
const_child_range children() const {
28412860
return const_child_range(const_child_iterator(), const_child_iterator());
28422861
}
2862+
2863+
unsigned numTrailingObjects(OverloadToken<APValue>) const {
2864+
return TypeTraitExprBits.IsBooleanTypeTrait ? 0 : 1;
2865+
}
28432866
};
28442867

28452868
/// An Embarcadero array type trait, as used in the implementation of

clang/include/clang/AST/Stmt.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ class alignas(void *) Stmt {
531531
unsigned : NumExprBits;
532532

533533
LLVM_PREFERRED_TYPE(UnaryExprOrTypeTrait)
534-
unsigned Kind : 4;
534+
unsigned Kind : 3;
535535
LLVM_PREFERRED_TYPE(bool)
536536
unsigned IsType : 1; // true if operand is a type, false if an expression.
537537
};
@@ -954,11 +954,13 @@ class alignas(void *) Stmt {
954954
LLVM_PREFERRED_TYPE(TypeTrait)
955955
unsigned Kind : 8;
956956

957-
/// If this expression is not value-dependent, this indicates whether
958-
/// the trait evaluated true or false.
959957
LLVM_PREFERRED_TYPE(bool)
960-
unsigned Value : 1;
958+
unsigned IsBooleanTypeTrait : 1;
961959

960+
/// If this expression is a non value-dependent boolean trait,
961+
/// this indicates whether the trait evaluated true or false.
962+
LLVM_PREFERRED_TYPE(bool)
963+
unsigned Value : 1;
962964
/// The number of arguments to this type trait. According to [implimits]
963965
/// 8 bits would be enough, but we require (and test for) at least 16 bits
964966
/// to mirror FunctionType.

clang/include/clang/Basic/TokenKinds.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,7 @@ TYPE_TRAIT_2(__reference_converts_from_temporary, ReferenceConvertsFromTemporary
554554
// is not exposed to users.
555555
TYPE_TRAIT_2(/*EmptySpellingName*/, IsDeducible, KEYCXX)
556556
TYPE_TRAIT_1(__is_bitwise_cloneable, IsBitwiseCloneable, KEYALL)
557-
UNARY_EXPR_OR_TYPE_TRAIT(__builtin_structured_binding_size, StructuredBindingSize, KEYCXX)
557+
TYPE_TRAIT_1(__builtin_structured_binding_size, StructuredBindingSize, KEYCXX)
558558

559559
// Embarcadero Expression Traits
560560
EXPRESSION_TRAIT(__is_lvalue_expr, IsLValueExpr, KEYCXX)

clang/lib/AST/ASTImporter.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8957,11 +8957,14 @@ ExpectedStmt ASTNodeImporter::VisitTypeTraitExpr(TypeTraitExpr *E) {
89578957

89588958
// According to Sema::BuildTypeTrait(), if E is value-dependent,
89598959
// Value is always false.
8960-
bool ToValue = (E->isValueDependent() ? false : E->getValue());
8961-
8962-
return TypeTraitExpr::Create(
8963-
Importer.getToContext(), ToType, ToBeginLoc, E->getTrait(), ToArgs,
8964-
ToEndLoc, ToValue);
8960+
if (E->isValueDependent() || E->isStoredAsBoolean()) {
8961+
bool ToValue = (E->isValueDependent() ? false : E->getBoolValue());
8962+
return TypeTraitExpr::Create(Importer.getToContext(), ToType, ToBeginLoc,
8963+
E->getTrait(), ToArgs, ToEndLoc, ToValue);
8964+
}
8965+
return TypeTraitExpr::Create(Importer.getToContext(), ToType, ToBeginLoc,
8966+
E->getTrait(), ToArgs, ToEndLoc,
8967+
E->getAPValue());
89658968
}
89668969

89678970
ExpectedStmt ASTNodeImporter::VisitCXXTypeidExpr(CXXTypeidExpr *E) {

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2154,8 +2154,6 @@ bool Compiler<Emitter>::VisitUnaryExprOrTypeTraitExpr(
21542154
E->getArgumentType()),
21552155
E);
21562156
}
2157-
assert(Kind != UETT_StructuredBindingSize &&
2158-
"should have been evaluated in Sema");
21592157

21602158
return false;
21612159
}
@@ -2846,9 +2844,13 @@ template <class Emitter>
28462844
bool Compiler<Emitter>::VisitTypeTraitExpr(const TypeTraitExpr *E) {
28472845
if (DiscardResult)
28482846
return true;
2849-
if (E->getType()->isBooleanType())
2850-
return this->emitConstBool(E->getValue(), E);
2851-
return this->emitConst(E->getValue(), E);
2847+
if (E->isStoredAsBoolean()) {
2848+
if (E->getType()->isBooleanType())
2849+
return this->emitConstBool(E->getBoolValue(), E);
2850+
return this->emitConst(E->getBoolValue(), E);
2851+
}
2852+
PrimType T = classifyPrim(E->getType());
2853+
return this->visitAPValue(E->getAPValue(), T, E);
28522854
}
28532855

28542856
template <class Emitter>

clang/lib/AST/ExprCXX.cpp

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1861,6 +1861,7 @@ TypeTraitExpr::TypeTraitExpr(QualType T, SourceLocation Loc, TypeTrait Kind,
18611861
TypeTraitExprBits.Kind = Kind;
18621862
assert(static_cast<unsigned>(Kind) == TypeTraitExprBits.Kind &&
18631863
"TypeTraitExprBits.Kind overflow!");
1864+
TypeTraitExprBits.IsBooleanTypeTrait = true;
18641865
TypeTraitExprBits.Value = Value;
18651866
TypeTraitExprBits.NumArgs = Args.size();
18661867
assert(Args.size() == TypeTraitExprBits.NumArgs &&
@@ -1873,19 +1874,54 @@ TypeTraitExpr::TypeTraitExpr(QualType T, SourceLocation Loc, TypeTrait Kind,
18731874
setDependence(computeDependence(this));
18741875
}
18751876

1877+
TypeTraitExpr::TypeTraitExpr(QualType T, SourceLocation Loc, TypeTrait Kind,
1878+
ArrayRef<TypeSourceInfo *> Args,
1879+
SourceLocation RParenLoc, APValue Value)
1880+
: Expr(TypeTraitExprClass, T, VK_PRValue, OK_Ordinary), Loc(Loc),
1881+
RParenLoc(RParenLoc) {
1882+
assert(Kind <= TT_Last && "invalid enum value!");
1883+
TypeTraitExprBits.Kind = Kind;
1884+
assert(static_cast<unsigned>(Kind) == TypeTraitExprBits.Kind &&
1885+
"TypeTraitExprBits.Kind overflow!");
1886+
TypeTraitExprBits.IsBooleanTypeTrait = false;
1887+
TypeTraitExprBits.NumArgs = Args.size();
1888+
assert(Args.size() == TypeTraitExprBits.NumArgs &&
1889+
"TypeTraitExprBits.NumArgs overflow!");
1890+
1891+
*getTrailingObjects<APValue>() = Value;
1892+
1893+
auto **ToArgs = getTrailingObjects<TypeSourceInfo *>();
1894+
for (unsigned I = 0, N = Args.size(); I != N; ++I)
1895+
ToArgs[I] = Args[I];
1896+
1897+
setDependence(computeDependence(this));
1898+
}
1899+
18761900
TypeTraitExpr *TypeTraitExpr::Create(const ASTContext &C, QualType T,
18771901
SourceLocation Loc,
18781902
TypeTrait Kind,
18791903
ArrayRef<TypeSourceInfo *> Args,
18801904
SourceLocation RParenLoc,
18811905
bool Value) {
1882-
void *Mem = C.Allocate(totalSizeToAlloc<TypeSourceInfo *>(Args.size()));
1906+
void *Mem =
1907+
C.Allocate(totalSizeToAlloc<APValue, TypeSourceInfo *>(0, Args.size()));
1908+
return new (Mem) TypeTraitExpr(T, Loc, Kind, Args, RParenLoc, Value);
1909+
}
1910+
1911+
TypeTraitExpr *TypeTraitExpr::Create(const ASTContext &C, QualType T,
1912+
SourceLocation Loc, TypeTrait Kind,
1913+
ArrayRef<TypeSourceInfo *> Args,
1914+
SourceLocation RParenLoc, APValue Value) {
1915+
void *Mem =
1916+
C.Allocate(totalSizeToAlloc<APValue, TypeSourceInfo *>(1, Args.size()));
18831917
return new (Mem) TypeTraitExpr(T, Loc, Kind, Args, RParenLoc, Value);
18841918
}
18851919

18861920
TypeTraitExpr *TypeTraitExpr::CreateDeserialized(const ASTContext &C,
1921+
bool IsStoredAsBool,
18871922
unsigned NumArgs) {
1888-
void *Mem = C.Allocate(totalSizeToAlloc<TypeSourceInfo *>(NumArgs));
1923+
void *Mem = C.Allocate(totalSizeToAlloc<APValue, TypeSourceInfo *>(
1924+
IsStoredAsBool ? 0 : 1, NumArgs));
18891925
return new (Mem) TypeTraitExpr(EmptyShell());
18901926
}
18911927

clang/lib/AST/ExprConstant.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12102,7 +12102,12 @@ class IntExprEvaluator
1210212102
}
1210312103

1210412104
bool VisitTypeTraitExpr(const TypeTraitExpr *E) {
12105-
return Success(E->getValue(), E);
12105+
if (E->isStoredAsBoolean())
12106+
return Success(E->getBoolValue(), E);
12107+
if (E->getAPValue().isAbsent())
12108+
return false;
12109+
assert(E->getAPValue().isInt() && "APValue type not supported");
12110+
return Success(E->getAPValue().getInt(), E);
1210612111
}
1210712112

1210812113
bool VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *E) {
@@ -14878,11 +14883,6 @@ bool IntExprEvaluator::VisitUnaryExprOrTypeTraitExpr(
1487814883
}
1487914884
return Success(Sizeof, E);
1488014885
}
14881-
case UETT_StructuredBindingSize:
14882-
// This can only be computed from Sema and has been cached.
14883-
// We can still get there from code that strips the outer ConstantExpr.
14884-
return false;
14885-
1488614886
case UETT_OpenMPRequiredSimdAlign:
1488714887
assert(E->isArgumentType());
1488814888
return Success(

clang/lib/AST/ItaniumMangle.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5358,10 +5358,6 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity,
53585358
MangleAlignofSizeofArg();
53595359
break;
53605360

5361-
case UETT_StructuredBindingSize:
5362-
MangleExtensionBuiltin(SAE);
5363-
break;
5364-
53655361
case UETT_VectorElements:
53665362
case UETT_OpenMPRequiredSimdAlign:
53675363
case UETT_VecStep:

clang/lib/CodeGen/CGExprScalar.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -724,7 +724,12 @@ class ScalarExprEmitter
724724
}
725725

726726
Value *VisitTypeTraitExpr(const TypeTraitExpr *E) {
727-
return llvm::ConstantInt::get(ConvertType(E->getType()), E->getValue());
727+
if (E->isStoredAsBoolean())
728+
return llvm::ConstantInt::get(ConvertType(E->getType()),
729+
E->getBoolValue());
730+
assert(E->getAPValue().isInt() && "APValue type not supported");
731+
return llvm::ConstantInt::get(ConvertType(E->getType()),
732+
E->getAPValue().getInt());
728733
}
729734

730735
Value *VisitConceptSpecializationExpr(const ConceptSpecializationExpr *E) {

0 commit comments

Comments
 (0)