diff options
author | Christopher Di Bella <cjdb@google.com> | 2022-08-22 00:27:10 +0000 |
---|---|---|
committer | Christopher Di Bella <cjdb@google.com> | 2022-08-22 03:03:32 +0000 |
commit | e9ef45635b77598fc9ce0cd38d7d3f8c9d88a49d (patch) | |
tree | 8efe48cde4ea5365183924ac3cd27c962e29dd3f /clang/lib/Sema | |
parent | d2d77e050b32ce3f917688aeeb9e6f8f3c209560 (diff) | |
download | llvm-e9ef45635b77598fc9ce0cd38d7d3f8c9d88a49d.zip llvm-e9ef45635b77598fc9ce0cd38d7d3f8c9d88a49d.tar.gz llvm-e9ef45635b77598fc9ce0cd38d7d3f8c9d88a49d.tar.bz2 |
[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
Diffstat (limited to 'clang/lib/Sema')
-rw-r--r-- | clang/lib/Sema/DeclSpec.cpp | 9 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 6 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplateVariadic.cpp | 3 | ||||
-rw-r--r-- | clang/lib/Sema/SemaType.cpp | 305 |
4 files changed, 283 insertions, 40 deletions
diff --git a/clang/lib/Sema/DeclSpec.cpp b/clang/lib/Sema/DeclSpec.cpp index d4dc790..af31105 100644 --- a/clang/lib/Sema/DeclSpec.cpp +++ b/clang/lib/Sema/DeclSpec.cpp @@ -18,6 +18,7 @@ #include "clang/AST/TypeLoc.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceManager.h" +#include "clang/Basic/Specifiers.h" #include "clang/Basic/TargetInfo.h" #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/Sema.h" @@ -389,7 +390,8 @@ bool Declarator::isDeclarationOfFunction() const { return E->getType()->isFunctionType(); return false; - case TST_underlyingType: +#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case TST_##Trait: +#include "clang/Basic/TransformTypeTraits.def" case TST_typename: case TST_typeofType: { QualType QT = DS.getRepAsType().get(); @@ -576,7 +578,10 @@ const char *DeclSpec::getSpecifierName(DeclSpec::TST T, case DeclSpec::TST_auto_type: return "__auto_type"; case DeclSpec::TST_decltype: return "(decltype)"; case DeclSpec::TST_decltype_auto: return "decltype(auto)"; - case DeclSpec::TST_underlyingType: return "__underlying_type"; +#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) \ + case DeclSpec::TST_##Trait: \ + return "__" #Trait; +#include "clang/Basic/TransformTypeTraits.def" case DeclSpec::TST_unknown_anytype: return "__unknown_anytype"; case DeclSpec::TST_atomic: return "_Atomic"; case DeclSpec::TST_BFloat16: return "__bf16"; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 004c1ff..463551c 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -145,7 +145,8 @@ bool Sema::isSimpleTypeSpecifier(tok::TokenKind Kind) const { case tok::kw___ibm128: case tok::kw_wchar_t: case tok::kw_bool: - case tok::kw___underlying_type: +#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case tok::kw___##Trait: +#include "clang/Basic/TransformTypeTraits.def" case tok::kw___auto_type: return true; @@ -5923,7 +5924,8 @@ static bool RebuildDeclaratorInCurrentInstantiation(Sema &S, Declarator &D, switch (DS.getTypeSpecType()) { case DeclSpec::TST_typename: case DeclSpec::TST_typeofType: - case DeclSpec::TST_underlyingType: +#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case DeclSpec::TST_##Trait: +#include "clang/Basic/TransformTypeTraits.def" case DeclSpec::TST_atomic: { // Grab the type from the parser. TypeSourceInfo *TSI = nullptr; diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp index 790792f..285bf67 100644 --- a/clang/lib/Sema/SemaTemplateVariadic.cpp +++ b/clang/lib/Sema/SemaTemplateVariadic.cpp @@ -858,7 +858,8 @@ bool Sema::containsUnexpandedParameterPacks(Declarator &D) { switch (DS.getTypeSpecType()) { case TST_typename: case TST_typeofType: - case TST_underlyingType: +#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case TST_##Trait: +#include "clang/Basic/TransformTypeTraits.def" case TST_atomic: { QualType T = DS.getRepAsType().get(); if (!T.isNull() && T->containsUnexpandedParameterPack()) diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 14b2d2e..9ea9d80 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -19,9 +19,11 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" +#include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" #include "clang/AST/TypeLocVisitor.h" #include "clang/Basic/PartialDiagnostic.h" +#include "clang/Basic/SourceLocation.h" #include "clang/Basic/Specifiers.h" #include "clang/Basic/TargetInfo.h" #include "clang/Lex/Preprocessor.h" @@ -33,6 +35,7 @@ #include "clang/Sema/SemaInternal.h" #include "clang/Sema/Template.h" #include "clang/Sema/TemplateInstCallback.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" @@ -1247,6 +1250,18 @@ getImageAccess(const ParsedAttributesView &Attrs) { return OpenCLAccessAttr::Keyword_read_only; } +static UnaryTransformType::UTTKind +TSTToUnaryTransformType(DeclSpec::TST SwitchTST) { + switch (SwitchTST) { +#define TRANSFORM_TYPE_TRAIT_DEF(Enum, Trait) \ + case TST_##Trait: \ + return UnaryTransformType::Enum; +#include "clang/Basic/TransformTypeTraits.def" + default: + llvm_unreachable("attempted to parse a non-unary transform builtin"); + } +} + /// Convert the specified declspec to the appropriate type /// object. /// \param state Specifies the declarator containing the declaration specifier @@ -1628,12 +1643,13 @@ static QualType ConvertDeclSpecToType(TypeProcessingState &state) { } break; } - case DeclSpec::TST_underlyingType: +#define TRANSFORM_TYPE_TRAIT_DEF(_, Trait) case DeclSpec::TST_##Trait: +#include "clang/Basic/TransformTypeTraits.def" Result = S.GetTypeFromParser(DS.getRepAsType()); - assert(!Result.isNull() && "Didn't get a type for __underlying_type?"); - Result = S.BuildUnaryTransformType(Result, - UnaryTransformType::EnumUnderlyingType, - DS.getTypeSpecTypeLoc()); + assert(!Result.isNull() && "Didn't get a type for the transformation?"); + Result = S.BuildUnaryTransformType( + Result, TSTToUnaryTransformType(DS.getTypeSpecType()), + DS.getTypeSpecTypeLoc()); if (Result.isNull()) { Result = Context.IntTy; declarator.setInvalidType(true); @@ -6067,8 +6083,7 @@ namespace { TL.setRParenLoc(DS.getTypeofParensRange().getEnd()); } void VisitUnaryTransformTypeLoc(UnaryTransformTypeLoc TL) { - // FIXME: This holds only because we only have one unary transform. - assert(DS.getTypeSpecType() == DeclSpec::TST_underlyingType); + assert(DS.isTransformTypeTrait(DS.getTypeSpecType())); TL.setKWLoc(DS.getTypeSpecTypeLoc()); TL.setParensRange(DS.getTypeofParensRange()); assert(DS.getRepAsType()); @@ -9205,39 +9220,259 @@ QualType Sema::BuildDecltypeType(Expr *E, bool AsUnevaluated) { return Context.getDecltypeType(E, getDecltypeForExpr(E)); } -QualType Sema::BuildUnaryTransformType(QualType BaseType, - UnaryTransformType::UTTKind UKind, - SourceLocation Loc) { - switch (UKind) { - case UnaryTransformType::EnumUnderlyingType: - if (!BaseType->isDependentType() && !BaseType->isEnumeralType()) { - Diag(Loc, diag::err_only_enums_have_underlying_types); - return QualType(); - } else { - QualType Underlying = BaseType; - if (!BaseType->isDependentType()) { - // The enum could be incomplete if we're parsing its definition or - // recovering from an error. - NamedDecl *FwdDecl = nullptr; - if (BaseType->isIncompleteType(&FwdDecl)) { - Diag(Loc, diag::err_underlying_type_of_incomplete_enum) << BaseType; - Diag(FwdDecl->getLocation(), diag::note_forward_declaration) << FwdDecl; - return QualType(); - } +static QualType GetEnumUnderlyingType(Sema &S, QualType BaseType, + SourceLocation Loc) { + assert(BaseType->isEnumeralType()); + EnumDecl *ED = BaseType->castAs<EnumType>()->getDecl(); + assert(ED && "EnumType has no EnumDecl"); - EnumDecl *ED = BaseType->castAs<EnumType>()->getDecl(); - assert(ED && "EnumType has no EnumDecl"); + S.DiagnoseUseOfDecl(ED, Loc); - DiagnoseUseOfDecl(ED, Loc); + QualType Underlying = ED->getIntegerType(); + assert(!Underlying.isNull()); - Underlying = ED->getIntegerType(); - assert(!Underlying.isNull()); - } - return Context.getUnaryTransformType(BaseType, Underlying, - UnaryTransformType::EnumUnderlyingType); + return Underlying; +} + +QualType Sema::BuiltinEnumUnderlyingType(QualType BaseType, + SourceLocation Loc) { + if (!BaseType->isEnumeralType()) { + Diag(Loc, diag::err_only_enums_have_underlying_types); + return QualType(); + } + + // The enum could be incomplete if we're parsing its definition or + // recovering from an error. + NamedDecl *FwdDecl = nullptr; + if (BaseType->isIncompleteType(&FwdDecl)) { + Diag(Loc, diag::err_underlying_type_of_incomplete_enum) << BaseType; + Diag(FwdDecl->getLocation(), diag::note_forward_declaration) << FwdDecl; + return QualType(); + } + + return GetEnumUnderlyingType(*this, BaseType, Loc); +} + +QualType Sema::BuiltinAddPointer(QualType BaseType, SourceLocation Loc) { + QualType Pointer = BaseType.isReferenceable() || BaseType->isVoidType() + ? BuildPointerType(BaseType.getNonReferenceType(), Loc, + DeclarationName()) + : BaseType; + + return Pointer.isNull() ? QualType() : Pointer; +} + +QualType Sema::BuiltinRemovePointer(QualType BaseType, SourceLocation Loc) { + // We don't want block pointers or ObjectiveC's id type. + if (!BaseType->isAnyPointerType() || BaseType->isObjCIdType()) + return BaseType; + + return BaseType->getPointeeType(); +} + +QualType Sema::BuiltinDecay(QualType BaseType, SourceLocation Loc) { + QualType Underlying = BaseType.getNonReferenceType(); + if (Underlying->isArrayType()) + return Context.getDecayedType(Underlying); + + if (Underlying->isFunctionType()) + return BuiltinAddPointer(BaseType, Loc); + + SplitQualType Split = Underlying.getSplitUnqualifiedType(); + // std::decay is supposed to produce 'std::remove_cv', but since 'restrict' is + // in the same group of qualifiers as 'const' and 'volatile', we're extending + // '__decay(T)' so that it removes all qualifiers. + Split.Quals.removeCVRQualifiers(); + return Context.getQualifiedType(Split); +} + +QualType Sema::BuiltinAddReference(QualType BaseType, UTTKind UKind, + SourceLocation Loc) { + assert(LangOpts.CPlusPlus); + QualType Reference = + BaseType.isReferenceable() + ? BuildReferenceType(BaseType, + UKind == UnaryTransformType::AddLvalueReference, + Loc, DeclarationName()) + : BaseType; + return Reference.isNull() ? QualType() : Reference; +} + +QualType Sema::BuiltinRemoveExtent(QualType BaseType, UTTKind UKind, + SourceLocation Loc) { + if (UKind == UnaryTransformType::RemoveAllExtents) + return Context.getBaseElementType(BaseType); + + if (const auto *AT = Context.getAsArrayType(BaseType)) + return AT->getElementType(); + + return BaseType; +} + +QualType Sema::BuiltinRemoveReference(QualType BaseType, UTTKind UKind, + SourceLocation Loc) { + assert(LangOpts.CPlusPlus); + QualType T = BaseType.getNonReferenceType(); + if (UKind == UTTKind::RemoveCVRef && + (T.isConstQualified() || T.isVolatileQualified())) { + Qualifiers Quals; + QualType Unqual = Context.getUnqualifiedArrayType(T, Quals); + Quals.removeConst(); + Quals.removeVolatile(); + T = Context.getQualifiedType(Unqual, Quals); + } + return T; +} + +QualType Sema::BuiltinChangeCVRQualifiers(QualType BaseType, UTTKind UKind, + SourceLocation Loc) { + if ((BaseType->isReferenceType() && UKind != UTTKind::RemoveRestrict) || + BaseType->isFunctionType()) + return BaseType; + + Qualifiers Quals; + QualType Unqual = Context.getUnqualifiedArrayType(BaseType, Quals); + + if (UKind == UTTKind::RemoveConst || UKind == UTTKind::RemoveCV) + Quals.removeConst(); + if (UKind == UTTKind::RemoveVolatile || UKind == UTTKind::RemoveCV) + Quals.removeVolatile(); + if (UKind == UTTKind::RemoveRestrict) + Quals.removeRestrict(); + + return Context.getQualifiedType(Unqual, Quals); +} + +static QualType ChangeIntegralSignedness(Sema &S, QualType BaseType, + bool IsMakeSigned, + SourceLocation Loc) { + if (BaseType->isEnumeralType()) { + QualType Underlying = GetEnumUnderlyingType(S, BaseType, Loc); + if (auto *BitInt = dyn_cast<BitIntType>(Underlying)) { + unsigned int Bits = BitInt->getNumBits(); + if (Bits > 1) + return S.Context.getBitIntType(!IsMakeSigned, Bits); + + S.Diag(Loc, diag::err_make_signed_integral_only) + << IsMakeSigned << /*_BitInt(1)*/ true << BaseType << 1 << Underlying; + return QualType(); } + if (Underlying->isBooleanType()) { + S.Diag(Loc, diag::err_make_signed_integral_only) + << IsMakeSigned << /*_BitInt(1)*/ false << BaseType << 1 + << Underlying; + return QualType(); + } + } + + bool Int128Unsupported = !S.Context.getTargetInfo().hasInt128Type(); + std::array<CanQualType *, 6> AllSignedIntegers = { + &S.Context.SignedCharTy, &S.Context.ShortTy, &S.Context.IntTy, + &S.Context.LongTy, &S.Context.LongLongTy, &S.Context.Int128Ty}; + ArrayRef<CanQualType *> AvailableSignedIntegers( + AllSignedIntegers.data(), AllSignedIntegers.size() - Int128Unsupported); + std::array<CanQualType *, 6> AllUnsignedIntegers = { + &S.Context.UnsignedCharTy, &S.Context.UnsignedShortTy, + &S.Context.UnsignedIntTy, &S.Context.UnsignedLongTy, + &S.Context.UnsignedLongLongTy, &S.Context.UnsignedInt128Ty}; + ArrayRef<CanQualType *> AvailableUnsignedIntegers(AllUnsignedIntegers.data(), + AllUnsignedIntegers.size() - + Int128Unsupported); + ArrayRef<CanQualType *> *Consider = + IsMakeSigned ? &AvailableSignedIntegers : &AvailableUnsignedIntegers; + + uint64_t BaseSize = S.Context.getTypeSize(BaseType); + auto *Result = + llvm::find_if(*Consider, [&S, BaseSize](const CanQual<Type> *T) { + return BaseSize == S.Context.getTypeSize(T->getTypePtr()); + }); + + assert(Result != Consider->end()); + return QualType((*Result)->getTypePtr(), 0); +} + +QualType Sema::BuiltinChangeSignedness(QualType BaseType, UTTKind UKind, + SourceLocation Loc) { + bool IsMakeSigned = UKind == UnaryTransformType::MakeSigned; + if ((!BaseType->isIntegerType() && !BaseType->isEnumeralType()) || + BaseType->isBooleanType() || + (BaseType->isBitIntType() && + BaseType->getAs<BitIntType>()->getNumBits() < 2)) { + Diag(Loc, diag::err_make_signed_integral_only) + << IsMakeSigned << BaseType->isBitIntType() << BaseType << 0; + return QualType(); + } + + bool IsNonIntIntegral = + BaseType->isChar16Type() || BaseType->isChar32Type() || + BaseType->isWideCharType() || BaseType->isEnumeralType(); + + QualType Underlying = + IsNonIntIntegral + ? ChangeIntegralSignedness(*this, BaseType, IsMakeSigned, Loc) + : IsMakeSigned ? Context.getCorrespondingSignedType(BaseType) + : Context.getCorrespondingUnsignedType(BaseType); + if (Underlying.isNull()) + return Underlying; + return Context.getQualifiedType(Underlying, BaseType.getQualifiers()); +} + +QualType Sema::BuildUnaryTransformType(QualType BaseType, UTTKind UKind, + SourceLocation Loc) { + if (BaseType->isDependentType()) + return Context.getUnaryTransformType(BaseType, BaseType, UKind); + QualType Result; + switch (UKind) { + case UnaryTransformType::EnumUnderlyingType: { + Result = BuiltinEnumUnderlyingType(BaseType, Loc); + break; + } + case UnaryTransformType::AddPointer: { + Result = BuiltinAddPointer(BaseType, Loc); + break; + } + case UnaryTransformType::RemovePointer: { + Result = BuiltinRemovePointer(BaseType, Loc); + break; + } + case UnaryTransformType::Decay: { + Result = BuiltinDecay(BaseType, Loc); + break; + } + case UnaryTransformType::AddLvalueReference: + case UnaryTransformType::AddRvalueReference: { + Result = BuiltinAddReference(BaseType, UKind, Loc); + break; } - llvm_unreachable("unknown unary transform type"); + case UnaryTransformType::RemoveAllExtents: + case UnaryTransformType::RemoveExtent: { + Result = BuiltinRemoveExtent(BaseType, UKind, Loc); + break; + } + case UnaryTransformType::RemoveCVRef: + case UnaryTransformType::RemoveReference: { + Result = BuiltinRemoveReference(BaseType, UKind, Loc); + break; + } + case UnaryTransformType::RemoveConst: + case UnaryTransformType::RemoveCV: + case UnaryTransformType::RemoveRestrict: + case UnaryTransformType::RemoveVolatile: { + Result = BuiltinChangeCVRQualifiers(BaseType, UKind, Loc); + break; + } + case UnaryTransformType::MakeSigned: + case UnaryTransformType::MakeUnsigned: { + Result = BuiltinChangeSignedness(BaseType, UKind, Loc); + break; + } + default: + llvm_unreachable("unknown unary transform type"); + } + + return !Result.isNull() + ? Context.getUnaryTransformType(BaseType, Result, UKind) + : Result; } QualType Sema::BuildAtomicType(QualType T, SourceLocation Loc) { |