Skip to content

Commit e9ef456

Browse files
committed
[clang] adds unary type transformations as compiler built-ins
Adds * `__add_lvalue_reference` * `__add_pointer` * `__add_rvalue_reference` * `__decay` * `__make_signed` * `__make_unsigned` * `__remove_all_extents` * `__remove_extent` * `__remove_const` * `__remove_volatile` * `__remove_cv` * `__remove_pointer` * `__remove_reference` * `__remove_cvref` These are all compiler built-in equivalents of the unary type traits found in [[meta.trans]][1]. The compiler already has all of the information it needs to answer these transformations, so we can skip needing to make partial specialisations in standard library implementations (we already do this for a lot of the query traits). This will hopefully improve compile times, as we won't need use as much memory in such a base part of the standard library. [1]: http://wg21.link/meta.trans Co-authored-by: zoecarver Reviewed By: aaron.ballman, rsmith Differential Revision: https://reviews.llvm.org/D116203
1 parent d2d77e0 commit e9ef456

33 files changed

+1489
-198
lines changed

clang/include/clang/AST/Type.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,9 @@ class QualType {
797797
return Value.getPointer().isNull();
798798
}
799799

800+
// Determines if a type can form `T&`.
801+
bool isReferenceable() const;
802+
800803
/// Determine whether this particular QualType instance has the
801804
/// "const" qualifier set, without looking through typedefs that may have
802805
/// added "const" at a different level.
@@ -4637,7 +4640,8 @@ class DependentDecltypeType : public DecltypeType, public llvm::FoldingSetNode {
46374640
class UnaryTransformType : public Type {
46384641
public:
46394642
enum UTTKind {
4640-
EnumUnderlyingType
4643+
#define TRANSFORM_TYPE_TRAIT_DEF(Enum, _) Enum,
4644+
#include "clang/Basic/TransformTypeTraits.def"
46414645
};
46424646

46434647
private:
@@ -6584,6 +6588,19 @@ inline const Type *QualType::getTypePtrOrNull() const {
65846588
return (isNull() ? nullptr : getCommonPtr()->BaseType);
65856589
}
65866590

6591+
inline bool QualType::isReferenceable() const {
6592+
// C++ [defns.referenceable]
6593+
// type that is either an object type, a function type that does not have
6594+
// cv-qualifiers or a ref-qualifier, or a reference type.
6595+
const Type &Self = **this;
6596+
if (Self.isObjectType() || Self.isReferenceType())
6597+
return true;
6598+
if (const auto *F = Self.getAs<FunctionProtoType>())
6599+
return F->getMethodQuals().empty() && F->getRefQualifier() == RQ_None;
6600+
6601+
return false;
6602+
}
6603+
65876604
inline SplitQualType QualType::split() const {
65886605
if (!hasLocalNonFastQualifiers())
65896606
return SplitQualType(getTypePtrUnsafe(),

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8674,6 +8674,10 @@ def err_typecheck_expect_flt_or_vector : Error<
86748674
"a vector of such types is required">;
86758675
def err_cast_selector_expr : Error<
86768676
"cannot type cast @selector expression">;
8677+
def err_make_signed_integral_only : Error<
8678+
"'%select{make_unsigned|make_signed}0' is only compatible with "
8679+
"non-%select{bool|_BitInt(1)}1 integers and enum types, but was given "
8680+
"%2%select{| whose underlying type is %4}3">;
86778681
def ext_typecheck_cond_incompatible_pointers : ExtWarn<
86788682
"pointer type mismatch%diff{ ($ and $)|}0,1">,
86798683
InGroup<DiagGroup<"pointer-type-mismatch">>;

clang/include/clang/Basic/Specifiers.h

Lines changed: 25 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -53,41 +53,42 @@ namespace clang {
5353
TST_unspecified,
5454
TST_void,
5555
TST_char,
56-
TST_wchar, // C++ wchar_t
57-
TST_char8, // C++20 char8_t (proposed)
58-
TST_char16, // C++11 char16_t
59-
TST_char32, // C++11 char32_t
56+
TST_wchar, // C++ wchar_t
57+
TST_char8, // C++20 char8_t (proposed)
58+
TST_char16, // C++11 char16_t
59+
TST_char32, // C++11 char32_t
6060
TST_int,
6161
TST_int128,
62-
TST_bitint, // Bit-precise integer types.
63-
TST_half, // OpenCL half, ARM NEON __fp16
64-
TST_Float16, // C11 extension ISO/IEC TS 18661-3
65-
TST_Accum, // ISO/IEC JTC1 SC22 WG14 N1169 Extension
62+
TST_bitint, // Bit-precise integer types.
63+
TST_half, // OpenCL half, ARM NEON __fp16
64+
TST_Float16, // C11 extension ISO/IEC TS 18661-3
65+
TST_Accum, // ISO/IEC JTC1 SC22 WG14 N1169 Extension
6666
TST_Fract,
6767
TST_BFloat16,
6868
TST_float,
6969
TST_double,
7070
TST_float128,
7171
TST_ibm128,
72-
TST_bool, // _Bool
73-
TST_decimal32, // _Decimal32
74-
TST_decimal64, // _Decimal64
75-
TST_decimal128, // _Decimal128
72+
TST_bool, // _Bool
73+
TST_decimal32, // _Decimal32
74+
TST_decimal64, // _Decimal64
75+
TST_decimal128, // _Decimal128
7676
TST_enum,
7777
TST_union,
7878
TST_struct,
79-
TST_class, // C++ class type
80-
TST_interface, // C++ (Microsoft-specific) __interface type
81-
TST_typename, // Typedef, C++ class-name or enum name, etc.
79+
TST_class, // C++ class type
80+
TST_interface, // C++ (Microsoft-specific) __interface type
81+
TST_typename, // Typedef, C++ class-name or enum name, etc.
8282
TST_typeofType,
8383
TST_typeofExpr,
84-
TST_decltype, // C++11 decltype
85-
TST_underlyingType, // __underlying_type for C++11
86-
TST_auto, // C++11 auto
87-
TST_decltype_auto, // C++1y decltype(auto)
88-
TST_auto_type, // __auto_type extension
89-
TST_unknown_anytype, // __unknown_anytype extension
90-
TST_atomic, // C11 _Atomic
84+
TST_decltype, // C++11 decltype
85+
#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) TST_##Trait,
86+
#include "clang/Basic/TransformTypeTraits.def"
87+
TST_auto, // C++11 auto
88+
TST_decltype_auto, // C++1y decltype(auto)
89+
TST_auto_type, // __auto_type extension
90+
TST_unknown_anytype, // __unknown_anytype extension
91+
TST_atomic, // C11 _Atomic
9192
#define GENERIC_IMAGE_TYPE(ImgType, Id) TST_##ImgType##_t, // OpenCL image types
9293
#include "clang/Basic/OpenCLImageTypes.def"
9394
TST_error // erroneous type
@@ -96,8 +97,8 @@ namespace clang {
9697
/// Structure that packs information about the type specifiers that
9798
/// were written in a particular type specifier sequence.
9899
struct WrittenBuiltinSpecs {
99-
static_assert(TST_error < 1 << 6, "Type bitfield not wide enough for TST");
100-
/*DeclSpec::TST*/ unsigned Type : 6;
100+
static_assert(TST_error < 1 << 7, "Type bitfield not wide enough for TST");
101+
/*DeclSpec::TST*/ unsigned Type : 7;
101102
/*DeclSpec::TSS*/ unsigned Sign : 2;
102103
/*TypeSpecifierWidth*/ unsigned Width : 2;
103104
unsigned ModeAttr : 1;

clang/include/clang/Basic/TokenKinds.def

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,9 @@ TYPE_TRAIT_1(__is_trivially_copyable, IsTriviallyCopyable, KEYCXX)
501501
TYPE_TRAIT_1(__is_union, IsUnion, KEYCXX)
502502
TYPE_TRAIT_1(__has_unique_object_representations,
503503
HasUniqueObjectRepresentations, KEYCXX)
504-
KEYWORD(__underlying_type , KEYCXX)
504+
505+
#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) KEYWORD(__##Trait, KEYCXX)
506+
#include "clang/Basic/TransformTypeTraits.def"
505507

506508
// Clang-only C++ Type Traits
507509
TYPE_TRAIT_1(__is_trivially_relocatable, IsTriviallyRelocatable, KEYCXX)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//==--- TransformTypeTraits.def - type trait transformations --------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file defines transform type traits' names.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
TRANSFORM_TYPE_TRAIT_DEF(AddLvalueReference, add_lvalue_reference)
14+
TRANSFORM_TYPE_TRAIT_DEF(AddPointer, add_pointer)
15+
TRANSFORM_TYPE_TRAIT_DEF(AddRvalueReference, add_rvalue_reference)
16+
TRANSFORM_TYPE_TRAIT_DEF(Decay, decay)
17+
TRANSFORM_TYPE_TRAIT_DEF(MakeSigned, make_signed)
18+
TRANSFORM_TYPE_TRAIT_DEF(MakeUnsigned, make_unsigned)
19+
TRANSFORM_TYPE_TRAIT_DEF(RemoveAllExtents, remove_all_extents)
20+
TRANSFORM_TYPE_TRAIT_DEF(RemoveConst, remove_const)
21+
TRANSFORM_TYPE_TRAIT_DEF(RemoveCV, remove_cv)
22+
TRANSFORM_TYPE_TRAIT_DEF(RemoveCVRef, remove_cvref)
23+
TRANSFORM_TYPE_TRAIT_DEF(RemoveExtent, remove_extent)
24+
TRANSFORM_TYPE_TRAIT_DEF(RemovePointer, remove_pointer)
25+
TRANSFORM_TYPE_TRAIT_DEF(RemoveReference, remove_reference)
26+
TRANSFORM_TYPE_TRAIT_DEF(RemoveRestrict, remove_restrict)
27+
TRANSFORM_TYPE_TRAIT_DEF(RemoveVolatile, remove_volatile)
28+
TRANSFORM_TYPE_TRAIT_DEF(EnumUnderlyingType, underlying_type)
29+
#undef TRANSFORM_TYPE_TRAIT_DEF

clang/include/clang/Parse/Parser.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2906,7 +2906,6 @@ class Parser : public CodeCompletionHandler {
29062906
void AnnotateExistingDecltypeSpecifier(const DeclSpec &DS,
29072907
SourceLocation StartLoc,
29082908
SourceLocation EndLoc);
2909-
void ParseUnderlyingTypeSpecifier(DeclSpec &DS);
29102909
void ParseAtomicSpecifier(DeclSpec &DS);
29112910

29122911
ExprResult ParseAlignArgument(SourceLocation Start,
@@ -3004,6 +3003,8 @@ class Parser : public CodeCompletionHandler {
30043003
SourceLocation &EllipsisLoc);
30053004
void ParseBracketDeclarator(Declarator &D);
30063005
void ParseMisplacedBracketDeclarator(Declarator &D);
3006+
bool MaybeParseTypeTransformTypeSpecifier(DeclSpec &DS);
3007+
DeclSpec::TST TypeTransformTokToDeclSpec();
30073008

30083009
//===--------------------------------------------------------------------===//
30093010
// C++ 7: Declarations [dcl.dcl]

clang/include/clang/Sema/DeclSpec.h

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "clang/Lex/Token.h"
3333
#include "clang/Sema/Ownership.h"
3434
#include "clang/Sema/ParsedAttr.h"
35+
#include "llvm/ADT/STLExtras.h"
3536
#include "llvm/ADT/SmallVector.h"
3637
#include "llvm/Support/Compiler.h"
3738
#include "llvm/Support/ErrorHandling.h"
@@ -290,7 +291,9 @@ class DeclSpec {
290291
static const TST TST_typeofExpr = clang::TST_typeofExpr;
291292
static const TST TST_decltype = clang::TST_decltype;
292293
static const TST TST_decltype_auto = clang::TST_decltype_auto;
293-
static const TST TST_underlyingType = clang::TST_underlyingType;
294+
#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) \
295+
static const TST TST_##Trait = clang::TST_##Trait;
296+
#include "clang/Basic/TransformTypeTraits.def"
294297
static const TST TST_auto = clang::TST_auto;
295298
static const TST TST_auto_type = clang::TST_auto_type;
296299
static const TST TST_unknown_anytype = clang::TST_unknown_anytype;
@@ -333,7 +336,7 @@ class DeclSpec {
333336
/*TypeSpecifierWidth*/ unsigned TypeSpecWidth : 2;
334337
/*TSC*/unsigned TypeSpecComplex : 2;
335338
/*TSS*/unsigned TypeSpecSign : 2;
336-
/*TST*/unsigned TypeSpecType : 6;
339+
/*TST*/unsigned TypeSpecType : 7;
337340
unsigned TypeAltiVecVector : 1;
338341
unsigned TypeAltiVecPixel : 1;
339342
unsigned TypeAltiVecBool : 1;
@@ -400,8 +403,8 @@ class DeclSpec {
400403
ObjCDeclSpec *ObjCQualifiers;
401404

402405
static bool isTypeRep(TST T) {
403-
return (T == TST_typename || T == TST_typeofType ||
404-
T == TST_underlyingType || T == TST_atomic);
406+
return T == TST_atomic || T == TST_typename || T == TST_typeofType ||
407+
isTransformTypeTrait(T);
405408
}
406409
static bool isExprRep(TST T) {
407410
return (T == TST_typeofExpr || T == TST_decltype || T == TST_bitint);
@@ -418,6 +421,14 @@ class DeclSpec {
418421
T == TST_interface || T == TST_union ||
419422
T == TST_class);
420423
}
424+
static bool isTransformTypeTrait(TST T) {
425+
constexpr std::array<TST, 16> Traits = {
426+
#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) TST_##Trait,
427+
#include "clang/Basic/TransformTypeTraits.def"
428+
};
429+
430+
return T >= Traits.front() && T <= Traits.back();
431+
}
421432

422433
DeclSpec(AttributeFactory &attrFactory)
423434
: StorageClassSpec(SCS_unspecified),
@@ -522,7 +533,7 @@ class DeclSpec {
522533
}
523534

524535
SourceRange getTypeofParensRange() const { return TypeofParensRange; }
525-
void setTypeofParensRange(SourceRange range) { TypeofParensRange = range; }
536+
void setTypeArgumentRange(SourceRange range) { TypeofParensRange = range; }
526537

527538
bool hasAutoTypeSpec() const {
528539
return (TypeSpecType == TST_auto || TypeSpecType == TST_auto_type ||

clang/include/clang/Sema/Sema.h

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2511,8 +2511,23 @@ class Sema final {
25112511
/// If AsUnevaluated is false, E is treated as though it were an evaluated
25122512
/// context, such as when building a type for decltype(auto).
25132513
QualType BuildDecltypeType(Expr *E, bool AsUnevaluated = true);
2514-
QualType BuildUnaryTransformType(QualType BaseType,
2515-
UnaryTransformType::UTTKind UKind,
2514+
2515+
using UTTKind = UnaryTransformType::UTTKind;
2516+
QualType BuildUnaryTransformType(QualType BaseType, UTTKind UKind,
2517+
SourceLocation Loc);
2518+
QualType BuiltinEnumUnderlyingType(QualType BaseType, SourceLocation Loc);
2519+
QualType BuiltinAddPointer(QualType BaseType, SourceLocation Loc);
2520+
QualType BuiltinRemovePointer(QualType BaseType, SourceLocation Loc);
2521+
QualType BuiltinDecay(QualType BaseType, SourceLocation Loc);
2522+
QualType BuiltinAddReference(QualType BaseType, UTTKind UKind,
2523+
SourceLocation Loc);
2524+
QualType BuiltinRemoveExtent(QualType BaseType, UTTKind UKind,
2525+
SourceLocation Loc);
2526+
QualType BuiltinRemoveReference(QualType BaseType, UTTKind UKind,
2527+
SourceLocation Loc);
2528+
QualType BuiltinChangeCVRQualifiers(QualType BaseType, UTTKind UKind,
2529+
SourceLocation Loc);
2530+
QualType BuiltinChangeSignedness(QualType BaseType, UTTKind UKind,
25162531
SourceLocation Loc);
25172532

25182533
//===--------------------------------------------------------------------===//

clang/include/clang/module.modulemap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ module Clang_Basic {
7171
textual header "Basic/RISCVVTypes.def"
7272
textual header "Basic/Sanitizers.def"
7373
textual header "Basic/TargetCXXABI.def"
74+
textual header "Basic/TransformTypeTraits.def"
7475

7576
module * { export * }
7677
}

clang/lib/AST/ASTContext.cpp

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10781,7 +10781,8 @@ unsigned ASTContext::getIntWidth(QualType T) const {
1078110781
}
1078210782

1078310783
QualType ASTContext::getCorrespondingUnsignedType(QualType T) const {
10784-
assert((T->hasSignedIntegerRepresentation() || T->isSignedFixedPointType()) &&
10784+
assert((T->hasIntegerRepresentation() || T->isEnumeralType() ||
10785+
T->isFixedPointType()) &&
1078510786
"Unexpected type");
1078610787

1078710788
// Turn <4 x signed int> -> <4 x unsigned int>
@@ -10799,8 +10800,11 @@ QualType ASTContext::getCorrespondingUnsignedType(QualType T) const {
1079910800
T = ETy->getDecl()->getIntegerType();
1080010801

1080110802
switch (T->castAs<BuiltinType>()->getKind()) {
10803+
case BuiltinType::Char_U:
10804+
// Plain `char` is mapped to `unsigned char` even if it's already unsigned
1080210805
case BuiltinType::Char_S:
1080310806
case BuiltinType::SChar:
10807+
case BuiltinType::Char8:
1080410808
return UnsignedCharTy;
1080510809
case BuiltinType::Short:
1080610810
return UnsignedShortTy;
@@ -10814,7 +10818,7 @@ QualType ASTContext::getCorrespondingUnsignedType(QualType T) const {
1081410818
return UnsignedInt128Ty;
1081510819
// wchar_t is special. It is either signed or not, but when it's signed,
1081610820
// there's no matching "unsigned wchar_t". Therefore we return the unsigned
10817-
// version of it's underlying type instead.
10821+
// version of its underlying type instead.
1081810822
case BuiltinType::WChar_S:
1081910823
return getUnsignedWCharType();
1082010824

@@ -10843,13 +10847,16 @@ QualType ASTContext::getCorrespondingUnsignedType(QualType T) const {
1084310847
case BuiltinType::SatLongFract:
1084410848
return SatUnsignedLongFractTy;
1084510849
default:
10846-
llvm_unreachable("Unexpected signed integer or fixed point type");
10850+
assert((T->hasUnsignedIntegerRepresentation() ||
10851+
T->isUnsignedFixedPointType()) &&
10852+
"Unexpected signed integer or fixed point type");
10853+
return T;
1084710854
}
1084810855
}
1084910856

1085010857
QualType ASTContext::getCorrespondingSignedType(QualType T) const {
10851-
assert((T->hasUnsignedIntegerRepresentation() ||
10852-
T->isUnsignedFixedPointType()) &&
10858+
assert((T->hasIntegerRepresentation() || T->isEnumeralType() ||
10859+
T->isFixedPointType()) &&
1085310860
"Unexpected type");
1085410861

1085510862
// Turn <4 x unsigned int> -> <4 x signed int>
@@ -10867,8 +10874,11 @@ QualType ASTContext::getCorrespondingSignedType(QualType T) const {
1086710874
T = ETy->getDecl()->getIntegerType();
1086810875

1086910876
switch (T->castAs<BuiltinType>()->getKind()) {
10877+
case BuiltinType::Char_S:
10878+
// Plain `char` is mapped to `signed char` even if it's already signed
1087010879
case BuiltinType::Char_U:
1087110880
case BuiltinType::UChar:
10881+
case BuiltinType::Char8:
1087210882
return SignedCharTy;
1087310883
case BuiltinType::UShort:
1087410884
return ShortTy;
@@ -10882,7 +10892,7 @@ QualType ASTContext::getCorrespondingSignedType(QualType T) const {
1088210892
return Int128Ty;
1088310893
// wchar_t is special. It is either unsigned or not, but when it's unsigned,
1088410894
// there's no matching "signed wchar_t". Therefore we return the signed
10885-
// version of it's underlying type instead.
10895+
// version of its underlying type instead.
1088610896
case BuiltinType::WChar_U:
1088710897
return getSignedWCharType();
1088810898

@@ -10911,7 +10921,10 @@ QualType ASTContext::getCorrespondingSignedType(QualType T) const {
1091110921
case BuiltinType::SatULongFract:
1091210922
return SatLongFractTy;
1091310923
default:
10914-
llvm_unreachable("Unexpected unsigned integer or fixed point type");
10924+
assert(
10925+
(T->hasSignedIntegerRepresentation() || T->isSignedFixedPointType()) &&
10926+
"Unexpected signed integer or fixed point type");
10927+
return T;
1091510928
}
1091610929
}
1091710930

clang/lib/AST/ItaniumMangle.cpp

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3980,16 +3980,22 @@ void CXXNameMangler::mangleType(const UnaryTransformType *T) {
39803980
// If this is dependent, we need to record that. If not, we simply
39813981
// mangle it as the underlying type since they are equivalent.
39823982
if (T->isDependentType()) {
3983-
Out << 'U';
3983+
Out << "u";
39843984

3985+
StringRef BuiltinName;
39853986
switch (T->getUTTKind()) {
3986-
case UnaryTransformType::EnumUnderlyingType:
3987-
Out << "3eut";
3988-
break;
3987+
#define TRANSFORM_TYPE_TRAIT_DEF(Enum, Trait) \
3988+
case UnaryTransformType::Enum: \
3989+
BuiltinName = "__" #Trait; \
3990+
break;
3991+
#include "clang/Basic/TransformTypeTraits.def"
39893992
}
3993+
Out << BuiltinName.size() << BuiltinName;
39903994
}
39913995

3996+
Out << "I";
39923997
mangleType(T->getBaseType());
3998+
Out << "E";
39933999
}
39944000

39954001
void CXXNameMangler::mangleType(const AutoType *T) {

0 commit comments

Comments
 (0)