aboutsummaryrefslogtreecommitdiff
path: root/clang/lib
diff options
context:
space:
mode:
authorNikolas Klauser <nikolasklauser@berlin.de>2024-09-22 09:25:52 +0200
committerGitHub <noreply@github.com>2024-09-22 09:25:52 +0200
commitf5be5cdaad7edf52e39ad439cf5d608c930efca2 (patch)
tree9eac42ba689d68ba63b5e0a2adc8fba8fbd15e85 /clang/lib
parent15e6b5d643618d0c477d31188f6894a31bad98d8 (diff)
downloadllvm-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.cpp7
-rw-r--r--clang/lib/AST/ASTImporter.cpp3
-rw-r--r--clang/lib/AST/DeclTemplate.cpp56
-rw-r--r--clang/lib/Lex/PPMacroExpansion.cpp1
-rw-r--r--clang/lib/Sema/SemaLookup.cpp7
-rw-r--r--clang/lib/Sema/SemaTemplate.cpp159
-rw-r--r--clang/lib/Serialization/ASTReader.cpp7
-rw-r--r--clang/lib/Serialization/ASTWriter.cpp1
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();