diff options
author | Nikolas Klauser <nikolasklauser@berlin.de> | 2024-09-22 09:25:52 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-22 09:25:52 +0200 |
commit | f5be5cdaad7edf52e39ad439cf5d608c930efca2 (patch) | |
tree | 9eac42ba689d68ba63b5e0a2adc8fba8fbd15e85 /clang/lib | |
parent | 15e6b5d643618d0c477d31188f6894a31bad98d8 (diff) | |
download | llvm-f5be5cdaad7edf52e39ad439cf5d608c930efca2.zip llvm-f5be5cdaad7edf52e39ad439cf5d608c930efca2.tar.gz llvm-f5be5cdaad7edf52e39ad439cf5d608c930efca2.tar.bz2 |
[Clang] Add __builtin_common_type (#99473)
This implements the logic of the `common_type` base template as a
builtin alias. If there should be no `type` member, an empty class is
returned. Otherwise a specialization of a `type_identity`-like class is
returned. The base template (i.e. `std::common_type`) as well as the
empty class and `type_identity`-like struct are given as arguments to
the builtin.
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/AST/ASTContext.cpp | 7 | ||||
-rw-r--r-- | clang/lib/AST/ASTImporter.cpp | 3 | ||||
-rw-r--r-- | clang/lib/AST/DeclTemplate.cpp | 56 | ||||
-rw-r--r-- | clang/lib/Lex/PPMacroExpansion.cpp | 1 | ||||
-rw-r--r-- | clang/lib/Sema/SemaLookup.cpp | 7 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplate.cpp | 159 | ||||
-rw-r--r-- | clang/lib/Serialization/ASTReader.cpp | 7 | ||||
-rw-r--r-- | clang/lib/Serialization/ASTWriter.cpp | 1 |
8 files changed, 239 insertions, 2 deletions
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 85b3984..8bd5abf 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1184,6 +1184,13 @@ ASTContext::getTypePackElementDecl() const { return TypePackElementDecl; } +BuiltinTemplateDecl *ASTContext::getBuiltinCommonTypeDecl() const { + if (!BuiltinCommonTypeDecl) + BuiltinCommonTypeDecl = buildBuiltinTemplateDecl( + BTK__builtin_common_type, getBuiltinCommonTypeName()); + return BuiltinCommonTypeDecl; +} + RecordDecl *ASTContext::buildImplicitRecord(StringRef Name, RecordDecl::TagKind TK) const { SourceLocation Loc; diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp index c2fb7dd..bba97e2 100644 --- a/clang/lib/AST/ASTImporter.cpp +++ b/clang/lib/AST/ASTImporter.cpp @@ -5467,6 +5467,9 @@ ExpectedDecl ASTNodeImporter::VisitBuiltinTemplateDecl(BuiltinTemplateDecl *D) { case BuiltinTemplateKind::BTK__type_pack_element: ToD = Importer.getToContext().getTypePackElementDecl(); break; + case BuiltinTemplateKind::BTK__builtin_common_type: + ToD = Importer.getToContext().getBuiltinCommonTypeDecl(); + break; } assert(ToD && "BuiltinTemplateDecl of unsupported kind!"); Importer.MapImported(D, ToD); diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 976b3a3..6fe817c 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -1608,6 +1608,60 @@ createTypePackElementParameterList(const ASTContext &C, DeclContext *DC) { nullptr); } +static TemplateParameterList *createBuiltinCommonTypeList(const ASTContext &C, + DeclContext *DC) { + // class... Args + auto *Args = + TemplateTypeParmDecl::Create(C, DC, SourceLocation(), SourceLocation(), + /*Depth=*/1, /*Position=*/0, /*Id=*/nullptr, + /*Typename=*/false, /*ParameterPack=*/true); + + // <class... Args> + auto *BaseTemplateList = TemplateParameterList::Create( + C, SourceLocation(), SourceLocation(), Args, SourceLocation(), nullptr); + + // template <class... Args> class BaseTemplate + auto *BaseTemplate = TemplateTemplateParmDecl::Create( + C, DC, SourceLocation(), /*Depth=*/0, /*Position=*/0, + /*ParameterPack=*/false, /*Id=*/nullptr, + /*Typename=*/false, BaseTemplateList); + + // class TypeMember + auto *TypeMember = + TemplateTypeParmDecl::Create(C, DC, SourceLocation(), SourceLocation(), + /*Depth=*/1, /*Position=*/0, /*Id=*/nullptr, + /*Typename=*/false, /*ParameterPack=*/false); + + // <class TypeMember> + auto *HasTypeMemberList = + TemplateParameterList::Create(C, SourceLocation(), SourceLocation(), + TypeMember, SourceLocation(), nullptr); + + // template <class TypeMember> class HasTypeMember + auto *HasTypeMember = TemplateTemplateParmDecl::Create( + C, DC, SourceLocation(), /*Depth=*/0, /*Position=*/1, + /*ParameterPack=*/false, /*Id=*/nullptr, + /*Typename=*/false, HasTypeMemberList); + + // class HasNoTypeMember + auto *HasNoTypeMember = TemplateTypeParmDecl::Create( + C, DC, {}, {}, /*Depth=*/0, /*Position=*/2, /*Id=*/nullptr, + /*Typename=*/false, /*ParameterPack=*/false); + + // class... Ts + auto *Ts = TemplateTypeParmDecl::Create( + C, DC, SourceLocation(), SourceLocation(), /*Depth=*/0, /*Position=*/3, + /*Id=*/nullptr, /*Typename=*/false, /*ParameterPack=*/true); + + // template <template <class... Args> class BaseTemplate, + // template <class TypeMember> class HasTypeMember, class HasNoTypeMember, + // class... Ts> + return TemplateParameterList::Create( + C, SourceLocation(), SourceLocation(), + {BaseTemplate, HasTypeMember, HasNoTypeMember, Ts}, SourceLocation(), + nullptr); +} + static TemplateParameterList *createBuiltinTemplateParameterList( const ASTContext &C, DeclContext *DC, BuiltinTemplateKind BTK) { switch (BTK) { @@ -1615,6 +1669,8 @@ static TemplateParameterList *createBuiltinTemplateParameterList( return createMakeIntegerSeqParameterList(C, DC); case BTK__type_pack_element: return createTypePackElementParameterList(C, DC); + case BTK__builtin_common_type: + return createBuiltinCommonTypeList(C, DC); } llvm_unreachable("unhandled BuiltinTemplateKind!"); diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index 1d671ab..2b62f57 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -1836,6 +1836,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { // Report builtin templates as being builtins. .Case("__make_integer_seq", getLangOpts().CPlusPlus) .Case("__type_pack_element", getLangOpts().CPlusPlus) + .Case("__builtin_common_type", getLangOpts().CPlusPlus) // Likewise for some builtin preprocessor macros. // FIXME: This is inconsistent; we usually suggest detecting // builtin macros via #ifdef. Don't add more cases here. diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp index 76bfbde..ed5d44a 100644 --- a/clang/lib/Sema/SemaLookup.cpp +++ b/clang/lib/Sema/SemaLookup.cpp @@ -928,10 +928,15 @@ bool Sema::LookupBuiltin(LookupResult &R) { if (II == getASTContext().getMakeIntegerSeqName()) { R.addDecl(getASTContext().getMakeIntegerSeqDecl()); return true; - } else if (II == getASTContext().getTypePackElementName()) { + } + if (II == getASTContext().getTypePackElementName()) { R.addDecl(getASTContext().getTypePackElementDecl()); return true; } + if (II == getASTContext().getBuiltinCommonTypeName()) { + R.addDecl(getASTContext().getBuiltinCommonTypeDecl()); + return true; + } } // Check if this is an OpenCL Builtin, and if so, insert its overloads. diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index b052afe..92274cd 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -3076,6 +3076,140 @@ void Sema::NoteAllFoundTemplates(TemplateName Name) { } } +static QualType builtinCommonTypeImpl(Sema &S, TemplateName BaseTemplate, + SourceLocation TemplateLoc, + ArrayRef<TemplateArgument> Ts) { + auto lookUpCommonType = [&](TemplateArgument T1, + TemplateArgument T2) -> QualType { + // Don't bother looking for other specializations if both types are + // builtins - users aren't allowed to specialize for them + if (T1.getAsType()->isBuiltinType() && T2.getAsType()->isBuiltinType()) + return builtinCommonTypeImpl(S, BaseTemplate, TemplateLoc, {T1, T2}); + + TemplateArgumentListInfo Args; + Args.addArgument(TemplateArgumentLoc( + T1, S.Context.getTrivialTypeSourceInfo(T1.getAsType()))); + Args.addArgument(TemplateArgumentLoc( + T2, S.Context.getTrivialTypeSourceInfo(T2.getAsType()))); + + EnterExpressionEvaluationContext UnevaluatedContext( + S, Sema::ExpressionEvaluationContext::Unevaluated); + Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/true); + Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl()); + + QualType BaseTemplateInst = + S.CheckTemplateIdType(BaseTemplate, TemplateLoc, Args); + + if (SFINAE.hasErrorOccurred()) + return QualType(); + + return BaseTemplateInst; + }; + + // Note A: For the common_type trait applied to a template parameter pack T of + // types, the member type shall be either defined or not present as follows: + switch (Ts.size()) { + + // If sizeof...(T) is zero, there shall be no member type. + case 0: + return QualType(); + + // If sizeof...(T) is one, let T0 denote the sole type constituting the + // pack T. The member typedef-name type shall denote the same type, if any, as + // common_type_t<T0, T0>; otherwise there shall be no member type. + case 1: + return lookUpCommonType(Ts[0], Ts[0]); + + // If sizeof...(T) is two, let the first and second types constituting T be + // denoted by T1 and T2, respectively, and let D1 and D2 denote the same types + // as decay_t<T1> and decay_t<T2>, respectively. + case 2: { + QualType T1 = Ts[0].getAsType(); + QualType T2 = Ts[1].getAsType(); + QualType D1 = S.BuiltinDecay(T1, {}); + QualType D2 = S.BuiltinDecay(T2, {}); + + // If is_same_v<T1, D1> is false or is_same_v<T2, D2> is false, let C denote + // the same type, if any, as common_type_t<D1, D2>. + if (!S.Context.hasSameType(T1, D1) || !S.Context.hasSameType(T2, D2)) + return lookUpCommonType(D1, D2); + + // Otherwise, if decay_t<decltype(false ? declval<D1>() : declval<D2>())> + // denotes a valid type, let C denote that type. + { + auto CheckConditionalOperands = [&](bool ConstRefQual) -> QualType { + EnterExpressionEvaluationContext UnevaluatedContext( + S, Sema::ExpressionEvaluationContext::Unevaluated); + Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/true); + Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl()); + + // false + OpaqueValueExpr CondExpr(SourceLocation(), S.Context.BoolTy, + VK_PRValue); + ExprResult Cond = &CondExpr; + + auto EVK = ConstRefQual ? VK_LValue : VK_PRValue; + if (ConstRefQual) { + D1.addConst(); + D2.addConst(); + } + + // declval<D1>() + OpaqueValueExpr LHSExpr(TemplateLoc, D1, EVK); + ExprResult LHS = &LHSExpr; + + // declval<D2>() + OpaqueValueExpr RHSExpr(TemplateLoc, D2, EVK); + ExprResult RHS = &RHSExpr; + + ExprValueKind VK = VK_PRValue; + ExprObjectKind OK = OK_Ordinary; + + // decltype(false ? declval<D1>() : declval<D2>()) + QualType Result = + S.CheckConditionalOperands(Cond, LHS, RHS, VK, OK, TemplateLoc); + + if (Result.isNull() || SFINAE.hasErrorOccurred()) + return QualType(); + + // decay_t<decltype(false ? declval<D1>() : declval<D2>())> + return S.BuiltinDecay(Result, TemplateLoc); + }; + + if (auto Res = CheckConditionalOperands(false); !Res.isNull()) + return Res; + + // Let: + // CREF(A) be add_lvalue_reference_t<const remove_reference_t<A>>, + // COND-RES(X, Y) be + // decltype(false ? declval<X(&)()>()() : declval<Y(&)()>()()). + + // C++20 only + // Otherwise, if COND-RES(CREF(D1), CREF(D2)) denotes a type, let C denote + // the type decay_t<COND-RES(CREF(D1), CREF(D2))>. + if (!S.Context.getLangOpts().CPlusPlus20) + return QualType(); + return CheckConditionalOperands(true); + } + } + + // If sizeof...(T) is greater than two, let T1, T2, and R, respectively, + // denote the first, second, and (pack of) remaining types constituting T. Let + // C denote the same type, if any, as common_type_t<T1, T2>. If there is such + // a type C, the member typedef-name type shall denote the same type, if any, + // as common_type_t<C, R...>. Otherwise, there shall be no member type. + default: { + QualType Result = Ts.front().getAsType(); + for (auto T : llvm::drop_begin(Ts)) { + Result = lookUpCommonType(Result, T.getAsType()); + if (Result.isNull()) + return QualType(); + } + return Result; + } + } +} + static QualType checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD, ArrayRef<TemplateArgument> Converted, @@ -3132,7 +3266,7 @@ checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD, TemplateLoc, SyntheticTemplateArgs); } - case BTK__type_pack_element: + case BTK__type_pack_element: { // Specializations of // __type_pack_element<Index, T_1, ..., T_N> // are treated like T_Index. @@ -3158,6 +3292,29 @@ checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD, int64_t N = Index.getExtValue(); return Ts.getPackAsArray()[N].getAsType(); } + + case BTK__builtin_common_type: { + assert(Converted.size() == 4); + if (llvm::any_of(Converted, [](auto &C) { return C.isDependent(); })) + return Context.getCanonicalTemplateSpecializationType(TemplateName(BTD), + Converted); + + TemplateName BaseTemplate = Converted[0].getAsTemplate(); + TemplateName HasTypeMember = Converted[1].getAsTemplate(); + QualType HasNoTypeMember = Converted[2].getAsType(); + ArrayRef<TemplateArgument> Ts = Converted[3].getPackAsArray(); + if (auto CT = builtinCommonTypeImpl(SemaRef, BaseTemplate, TemplateLoc, Ts); + !CT.isNull()) { + TemplateArgumentListInfo TAs; + TAs.addArgument(TemplateArgumentLoc( + TemplateArgument(CT), SemaRef.Context.getTrivialTypeSourceInfo( + CT, TemplateArgs[1].getLocation()))); + + return SemaRef.CheckTemplateIdType(HasTypeMember, TemplateLoc, TAs); + } + return HasNoTypeMember; + } + } llvm_unreachable("unexpected BuiltinTemplateDecl!"); } diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 47a286b..ede3070 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -7892,6 +7892,13 @@ Decl *ASTReader::getPredefinedDecl(PredefinedDeclIDs ID) { return Context.TypePackElementDecl; NewLoaded = Context.getTypePackElementDecl(); break; + + case PREDEF_DECL_COMMON_TYPE_ID: + if (Context.BuiltinCommonTypeDecl) + return Context.BuiltinCommonTypeDecl; + NewLoaded = Context.getBuiltinCommonTypeDecl(); + break; + case NUM_PREDEF_DECL_IDS: llvm_unreachable("Invalid decl ID"); break; diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 008bf57..4ee14b1 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -5051,6 +5051,7 @@ void ASTWriter::PrepareWritingSpecialDecls(Sema &SemaRef) { PREDEF_DECL_CF_CONSTANT_STRING_TAG_ID); RegisterPredefDecl(Context.TypePackElementDecl, PREDEF_DECL_TYPE_PACK_ELEMENT_ID); + RegisterPredefDecl(Context.BuiltinCommonTypeDecl, PREDEF_DECL_COMMON_TYPE_ID); const TranslationUnitDecl *TU = Context.getTranslationUnitDecl(); |