[clang] [libcxx] [Clang] Add __common_type builtin (PR #99473)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Jul 19 06:03:14 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-modules
Author: Nikolas Klauser (philnik777)
<details>
<summary>Changes</summary>
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.
---
Patch is 26.14 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/99473.diff
16 Files Affected:
- (modified) clang/include/clang/AST/ASTContext.h (+11)
- (modified) clang/include/clang/AST/DeclID.h (+4-1)
- (modified) clang/include/clang/Basic/Builtins.h (+4-1)
- (modified) clang/include/clang/Sema/Sema.h (+4)
- (modified) clang/lib/AST/ASTContext.cpp (+7)
- (modified) clang/lib/AST/ASTImporter.cpp (+3)
- (modified) clang/lib/AST/DeclTemplate.cpp (+53)
- (modified) clang/lib/Lex/PPMacroExpansion.cpp (+1)
- (modified) clang/lib/Sema/SemaChecking.cpp (+8)
- (modified) clang/lib/Sema/SemaLookup.cpp (+4)
- (modified) clang/lib/Sema/SemaTemplate.cpp (+159-1)
- (modified) clang/lib/Serialization/ASTReader.cpp (+3)
- (modified) clang/lib/Serialization/ASTWriter.cpp (+2)
- (added) clang/test/SemaCXX/type-trait-common-type.cpp (+126)
- (modified) libcxx/include/__type_traits/common_type.h (+13-3)
- (modified) libcxx/include/module.modulemap (+2)
``````````diff
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 608bd90fcc3ff..d02e742297898 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -399,6 +399,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// The identifier '__type_pack_element'.
mutable IdentifierInfo *TypePackElementName = nullptr;
+ /// The identifier '__common_type'.
+ mutable IdentifierInfo *CommonTypeName = nullptr;
+
QualType ObjCConstantStringType;
mutable RecordDecl *CFConstantStringTagDecl = nullptr;
mutable TypedefDecl *CFConstantStringTypeDecl = nullptr;
@@ -606,6 +609,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
mutable ExternCContextDecl *ExternCContext = nullptr;
mutable BuiltinTemplateDecl *MakeIntegerSeqDecl = nullptr;
mutable BuiltinTemplateDecl *TypePackElementDecl = nullptr;
+ mutable BuiltinTemplateDecl *CommonTypeDecl = nullptr;
/// The associated SourceManager object.
SourceManager &SourceMgr;
@@ -1107,6 +1111,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
ExternCContextDecl *getExternCContextDecl() const;
BuiltinTemplateDecl *getMakeIntegerSeqDecl() const;
BuiltinTemplateDecl *getTypePackElementDecl() const;
+ BuiltinTemplateDecl *getCommonTypeDecl() const;
// Builtin Types.
CanQualType VoidTy;
@@ -1984,6 +1989,12 @@ class ASTContext : public RefCountedBase<ASTContext> {
return TypePackElementName;
}
+ IdentifierInfo *getCommonTypeName() const {
+ if (!CommonTypeName)
+ CommonTypeName = &Idents.get("__common_type");
+ return CommonTypeName;
+ }
+
/// Retrieve the Objective-C "instancetype" type, if already known;
/// otherwise, returns a NULL type;
QualType getObjCInstanceType() {
diff --git a/clang/include/clang/AST/DeclID.h b/clang/include/clang/AST/DeclID.h
index e5e27389fac60..875e9a72b3951 100644
--- a/clang/include/clang/AST/DeclID.h
+++ b/clang/include/clang/AST/DeclID.h
@@ -84,13 +84,16 @@ enum PredefinedDeclIDs {
/// The internal '__type_pack_element' template.
PREDEF_DECL_TYPE_PACK_ELEMENT_ID = 17,
+
+ /// The internal '__common_type' template.
+ PREDEF_DECL_COMMON_TYPE_ID = 18,
};
/// The number of declaration IDs that are predefined.
///
/// For more information about predefined declarations, see the
/// \c PredefinedDeclIDs type and the PREDEF_DECL_*_ID constants.
-const unsigned int NUM_PREDEF_DECL_IDS = 18;
+const unsigned int NUM_PREDEF_DECL_IDS = 19;
/// GlobalDeclID means DeclID in the current ASTContext and LocalDeclID means
/// DeclID specific to a certain ModuleFile. Specially, in ASTWriter, the
diff --git a/clang/include/clang/Basic/Builtins.h b/clang/include/clang/Basic/Builtins.h
index e85ec5b2dca14..4353b72f71383 100644
--- a/clang/include/clang/Basic/Builtins.h
+++ b/clang/include/clang/Basic/Builtins.h
@@ -309,7 +309,10 @@ enum BuiltinTemplateKind : int {
BTK__make_integer_seq,
/// This names the __type_pack_element BuiltinTemplateDecl.
- BTK__type_pack_element
+ BTK__type_pack_element,
+
+ /// This names the __common_type BuiltinTemplateDecl.
+ BTK__common_type,
};
} // end namespace clang
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 3cb1aa935fe46..5c7945c4c5c58 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2282,6 +2282,10 @@ class Sema final : public SemaBase {
/// Check to see if a given expression could have '.c_str()' called on it.
bool hasCStrMethod(const Expr *E);
+ // Check whether a type member 'Type::Name' exists, and if yes, return the
+ // type. If there is no type, the QualType is null
+ QualType getTypeMember(StringRef Name, QualType Type);
+
/// Diagnose pointers that are always non-null.
/// \param E the expression containing the pointer
/// \param NullKind NPCK_NotNull if E is a cast to bool, otherwise, E is
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index a8e599f7ebe04..b8b4f426ff96c 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -1170,6 +1170,13 @@ ASTContext::getTypePackElementDecl() const {
return TypePackElementDecl;
}
+BuiltinTemplateDecl *ASTContext::getCommonTypeDecl() const {
+ if (!CommonTypeDecl)
+ CommonTypeDecl =
+ buildBuiltinTemplateDecl(BTK__common_type, getCommonTypeName());
+ return CommonTypeDecl;
+}
+
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 0c27f6f5df2da..e4515e19a49a3 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -5408,6 +5408,9 @@ ExpectedDecl ASTNodeImporter::VisitBuiltinTemplateDecl(BuiltinTemplateDecl *D) {
case BuiltinTemplateKind::BTK__type_pack_element:
ToD = Importer.getToContext().getTypePackElementDecl();
break;
+ case BuiltinTemplateKind::BTK__common_type:
+ ToD = Importer.getToContext().getCommonTypeDecl();
+ 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 722c7fcf0b0df..d290c91fb8290 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1599,6 +1599,57 @@ createTypePackElementParameterList(const ASTContext &C, DeclContext *DC) {
nullptr);
}
+static TemplateParameterList *createCommonTypeList(const ASTContext &C,
+ DeclContext *DC) {
+ // class... Args
+ auto *Args = TemplateTypeParmDecl::Create(
+ C, DC, {}, {}, /*Depth=*/1, /*Position=*/0, /*Id=*/nullptr,
+ /*Typename=*/false, /*ParameterPack=*/true);
+ Args->setImplicit();
+
+ // <class... Args>
+ auto *BaseTemplateList =
+ TemplateParameterList::Create(C, {}, {}, Args, {}, nullptr);
+
+ // template <class... Args> class BaseTemplate
+ auto *BaseTemplate = TemplateTemplateParmDecl::Create(
+ C, DC, {}, /*Depth=*/0, /*Position=*/0, /*ParameterPack=*/false, {},
+ /*Typename=*/false, BaseTemplateList);
+
+ // class TypeMember
+ auto *TypeMember = TemplateTypeParmDecl::Create(
+ C, DC, {}, {}, /*Depth=*/1, /*Position=*/0, /*Id=*/nullptr,
+ /*Typename=*/false, /*ParameterPack=*/false);
+
+ // <class TypeMember>
+ auto *HasTypeMemberList =
+ TemplateParameterList::Create(C, {}, {}, TypeMember, {}, nullptr);
+
+ // template <class TypeMember> class HasTypeMember
+ auto *HasTypeMember =
+ TemplateTemplateParmDecl::Create(C, DC, {}, /*Depth=*/0, /*Position=*/1,
+ /*ParameterPack=*/false, {},
+ /*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, {}, {}, /*Depth=*/0, /*Position=*/3,
+ /*Id=*/nullptr, /*Typename=*/false, /*ParameterPack=*/true);
+ Ts->setImplicit();
+
+ // template <template <class... Args> class BaseTemplate,
+ // template <class TypeMember> class HasTypeMember, class HasNoTypeMember,
+ // class... Ts>
+ return TemplateParameterList::Create(
+ C, {}, {}, {BaseTemplate, HasTypeMember, HasNoTypeMember, Ts}, {},
+ nullptr);
+}
+
static TemplateParameterList *createBuiltinTemplateParameterList(
const ASTContext &C, DeclContext *DC, BuiltinTemplateKind BTK) {
switch (BTK) {
@@ -1606,6 +1657,8 @@ static TemplateParameterList *createBuiltinTemplateParameterList(
return createMakeIntegerSeqParameterList(C, DC);
case BTK__type_pack_element:
return createTypePackElementParameterList(C, DC);
+ case BTK__common_type:
+ return createCommonTypeList(C, DC);
}
llvm_unreachable("unhandled BuiltinTemplateKind!");
diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp
index 3913ff08c2eb5..4a6dd13229fe0 100644
--- a/clang/lib/Lex/PPMacroExpansion.cpp
+++ b/clang/lib/Lex/PPMacroExpansion.cpp
@@ -1822,6 +1822,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("__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/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 45b9bbb23dbf7..10c821fd367e7 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -6844,6 +6844,14 @@ CXXRecordMembersNamed(StringRef Name, Sema &S, QualType Ty) {
return Results;
}
+QualType Sema::getTypeMember(StringRef Name, QualType Type) {
+ auto Results = CXXRecordMembersNamed<TypeDecl>(Name, *this, Type);
+ assert(Results.size() <= 1);
+ if (Results.empty())
+ return {};
+ return Context.getTypeDeclType(*Results.begin());
+}
+
/// Check if we could call '.c_str()' on an object.
///
/// FIXME: This returns the wrong results in some cases (if cv-qualifiers don't
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index 7a6a64529f52e..96551c5106b1b 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -932,6 +932,10 @@ bool Sema::LookupBuiltin(LookupResult &R) {
R.addDecl(getASTContext().getTypePackElementDecl());
return true;
}
+ if (II == getASTContext().getCommonTypeName()) {
+ R.addDecl(getASTContext().getCommonTypeDecl());
+ 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 9d96201625389..bf3d35f0c1abb 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -3058,6 +3058,141 @@ void Sema::NoteAllFoundTemplates(TemplateName Name) {
}
}
+static std::optional<QualType> commonTypeImpl(Sema &S,
+ TemplateName BaseTemplate,
+ SourceLocation TemplateLoc,
+ ArrayRef<TemplateArgument> Ts) {
+ auto lookUpCommonType = [&](TemplateArgument T1,
+ TemplateArgument T2) -> std::optional<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 commonTypeImpl(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())));
+ QualType BaseTemplateInst =
+ S.CheckTemplateIdType(BaseTemplate, TemplateLoc, Args);
+ if (S.RequireCompleteType(TemplateLoc, BaseTemplateInst,
+ diag::err_incomplete_type))
+ return std::nullopt;
+ if (QualType Type = S.getTypeMember("type", BaseTemplateInst);
+ !Type.isNull()) {
+ return Type;
+ }
+ return std::nullopt;
+ };
+
+ // 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 std::nullopt;
+
+ // 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) -> std::optional<QualType> {
+ EnterExpressionEvaluationContext UnevaluatedContext(
+ S, Sema::ExpressionEvaluationContext::Unevaluated);
+ Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/true);
+ Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl());
+
+ // false
+ OpaqueValueExpr CondExpr({}, S.Context.BoolTy,
+ ExprValueKind::VK_PRValue);
+ ExprResult Cond = &CondExpr;
+
+ auto EVK =
+ ConstRefQual ? ExprValueKind::VK_LValue : ExprValueKind::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 std::nullopt;
+
+ // decay_t<decltype(false ? declval<D1>() : declval<D2>())>
+ return S.BuiltinDecay(Result, TemplateLoc);
+ };
+
+ if (auto Res = CheckConditionalOperands(false))
+ 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 std::nullopt;
+ 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: {
+ std::optional<QualType> Result = Ts[Ts.size() - 1].getAsType();
+ for (size_t i = Ts.size() - 1; i != 0; --i) {
+ Result = lookUpCommonType(Ts[i - 1].getAsType(), *Result);
+ if (!Result)
+ return std::nullopt;
+ }
+ return Result;
+ }
+ }
+}
+
static QualType
checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD,
ArrayRef<TemplateArgument> Converted,
@@ -3114,7 +3249,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.
@@ -3140,6 +3275,29 @@ checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD,
int64_t N = Index.getExtValue();
return Ts.getPackAsArray()[N].getAsType();
}
+
+ case BTK__common_type: {
+ assert(Converted.size() == 4);
+ if (Converted[0].isDependent() || Converted[1].isDependent() ||
+ Converted[2].isDependent() || Converted[3].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 = commonTypeImpl(SemaRef, BaseTemplate, TemplateLoc, Ts)) {
+ 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 afdeccaf93a9d..40ebc2c5d020f 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -7789,6 +7789,9 @@ static Decl *getPredefinedDecl(ASTContext &Context, PredefinedDeclIDs ID) {
case PREDEF_DECL_TYPE_PACK_ELEMENT_ID:
return Context.getTypePackElementDecl();
+
+ case PREDEF_DECL_COMMON_TYPE_ID:
+ return Context.getCommonTypeDecl();
}
llvm_unreachable("PredefinedDeclIDs unknown enum value");
}
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index c78d8943d6d92..b17eeed357aac 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -5036,6 +5036,8 @@ void ASTWriter::PrepareWritingSpecialDecls(Sema &SemaRef) {
PREDEF_DECL_CF_CONSTANT_STRING_TAG_ID);
RegisterPredefDecl(Context.TypePackElementDecl,
PREDEF_DECL_TYPE_PACK_ELEMENT_ID);
+ RegisterPredefDecl(Context.CommonTypeDecl,
+ PREDEF_DECL_COMMON_TYPE_ID);
const TranslationUnitDecl *TU = Context.getTranslationUnitDecl();
diff --git a/clang/test/SemaCXX/type-trait-common-type.cpp b/clang/test/SemaCXX/type-trait-common-type.cpp
new file mode 100644
index 0000000000000..44207f066a333
--- /dev/null
+++ b/clang/test/SemaCXX/type-trait-common-type.cpp
@@ -0,0 +1,126 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -verify -std=c++17 %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -verify -std=c++20 %s
+
+#if !__has_builtin(__common_type)
+# error
+#endif
+
+// expected-note@*:* {{template declaration from hidden source: template <class, template <class> class, class>}}
+
+void test() {
+ __common_type<> a; // expected-error {{too few template arguments for template '__common_type'}}
+}
+
+struct empty_type {};
+
+template <class T>
+struct type_identity {
+ using type = T;
+};
+
+template <class...>
+struct common_type;
+
+template <class... Args>
+using common_type_base = __common_type<common_type, type_identity, empty_type, Args...>; // expected-error {{incomplete type 'common_type<Incomplete, Incomplete>' where a complete type is required}}
+
+template <class... Args>
+struct common_type : common_type_base<Args...> {};
+
+struct Incomplete;
+
+template<>
+struct common_type<Incomplete, Incomplete>; // expected-note {{forward declaration}}
+
+static_assert(__is_same(common_type_base<>, empty_type));
+static_assert(__is_same(common_type_base<Incomplete>, empty_type)); // expected-note {{requested here}}
+static_assert(__is_same(common_type_base<char>, type_identity<char>));
+static_asse...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/99473
More information about the cfe-commits
mailing list