[clang] f5be5cd - [Clang] Add __builtin_common_type (#99473)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Sep 22 00:25:56 PDT 2024
Author: Nikolas Klauser
Date: 2024-09-22T09:25:52+02:00
New Revision: f5be5cdaad7edf52e39ad439cf5d608c930efca2
URL: https://github.com/llvm/llvm-project/commit/f5be5cdaad7edf52e39ad439cf5d608c930efca2
DIFF: https://github.com/llvm/llvm-project/commit/f5be5cdaad7edf52e39ad439cf5d608c930efca2.diff
LOG: [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.
Added:
clang/test/SemaCXX/type-trait-common-type.cpp
Modified:
clang/docs/LanguageExtensions.rst
clang/docs/ReleaseNotes.rst
clang/include/clang/AST/ASTContext.h
clang/include/clang/AST/DeclID.h
clang/include/clang/Basic/Builtins.h
clang/lib/AST/ASTContext.cpp
clang/lib/AST/ASTImporter.cpp
clang/lib/AST/DeclTemplate.cpp
clang/lib/Lex/PPMacroExpansion.cpp
clang/lib/Sema/SemaLookup.cpp
clang/lib/Sema/SemaTemplate.cpp
clang/lib/Serialization/ASTReader.cpp
clang/lib/Serialization/ASTWriter.cpp
libcxx/include/__type_traits/common_type.h
libcxx/include/module.modulemap
Removed:
################################################################################
diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index f62f90fb9650a9..0c6b9b1b8f9ce4 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1516,6 +1516,46 @@ Attributes (N2335) C2
``#embed`` (N3017) C23 C89, C++
============================================ ================================ ============= =============
+Builtin type aliases
+====================
+
+Clang provides a few builtin aliases to improve the throughput of certain metaprogramming facilities.
+
+__builtin_common_type
+---------------------
+
+.. code-block:: c++
+
+ template <template <class... Args> class BaseTemplate,
+ template <class TypeMember> class HasTypeMember,
+ class HasNoTypeMember,
+ class... Ts>
+ using __builtin_common_type = ...;
+
+This alias is used for implementing ``std::common_type``. If ``std::common_type`` should contain a ``type`` member,
+it is an alias to ``HasTypeMember<TheCommonType>``. Otherwise it is an alias to ``HasNoTypeMember``. The
+``BaseTemplate`` is usually ``std::common_type``. ``Ts`` are the arguments to ``std::common_type``.
+
+__type_pack_element
+-------------------
+
+.. code-block:: c++
+
+ template <std::size_t Index, class... Ts>
+ using __type_pack_element = ...;
+
+This alias returns the type at ``Index`` in the parameter pack ``Ts``.
+
+__make_integer_seq
+------------------
+
+.. code-block:: c++
+
+ template <template <class IntSeqT, IntSeqT... Ints> class IntSeq, class T, T N>
+ using __make_integer_seq = ...;
+
+This alias returns ``IntSeq`` instantiated with ``IntSeqT = T``and ``Ints`` being the pack ``0, ..., N - 1``.
+
Type Trait Primitives
=====================
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 00d254b70277d4..da5205087fd821 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -123,6 +123,9 @@ C++ Language Changes
- Add ``__builtin_elementwise_popcount`` builtin for integer types only.
+- The builtin type alias ``__builtin_common_type`` has been added to improve the
+ performance of ``std::common_type``.
+
C++2c Feature Support
^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index b65a1f7dff5bc1..1984310df0442e 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -403,6 +403,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// The identifier '__type_pack_element'.
mutable IdentifierInfo *TypePackElementName = nullptr;
+ /// The identifier '__builtin_common_type'.
+ mutable IdentifierInfo *BuiltinCommonTypeName = nullptr;
+
QualType ObjCConstantStringType;
mutable RecordDecl *CFConstantStringTagDecl = nullptr;
mutable TypedefDecl *CFConstantStringTypeDecl = nullptr;
@@ -610,6 +613,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
mutable ExternCContextDecl *ExternCContext = nullptr;
mutable BuiltinTemplateDecl *MakeIntegerSeqDecl = nullptr;
mutable BuiltinTemplateDecl *TypePackElementDecl = nullptr;
+ mutable BuiltinTemplateDecl *BuiltinCommonTypeDecl = nullptr;
/// The associated SourceManager object.
SourceManager &SourceMgr;
@@ -1134,6 +1138,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
ExternCContextDecl *getExternCContextDecl() const;
BuiltinTemplateDecl *getMakeIntegerSeqDecl() const;
BuiltinTemplateDecl *getTypePackElementDecl() const;
+ BuiltinTemplateDecl *getBuiltinCommonTypeDecl() const;
// Builtin Types.
CanQualType VoidTy;
@@ -2025,6 +2030,12 @@ class ASTContext : public RefCountedBase<ASTContext> {
return TypePackElementName;
}
+ IdentifierInfo *getBuiltinCommonTypeName() const {
+ if (!BuiltinCommonTypeName)
+ BuiltinCommonTypeName = &Idents.get("__builtin_common_type");
+ return BuiltinCommonTypeName;
+ }
+
/// 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 81454a247229f5..f4607e42c4be38 100644
--- a/clang/include/clang/AST/DeclID.h
+++ b/clang/include/clang/AST/DeclID.h
@@ -83,6 +83,9 @@ enum PredefinedDeclIDs {
/// The internal '__type_pack_element' template.
PREDEF_DECL_TYPE_PACK_ELEMENT_ID,
+ /// The internal '__builtin_common_type' template.
+ PREDEF_DECL_COMMON_TYPE_ID,
+
/// The number of declaration IDs that are predefined.
NUM_PREDEF_DECL_IDS
};
diff --git a/clang/include/clang/Basic/Builtins.h b/clang/include/clang/Basic/Builtins.h
index e85ec5b2dca14e..89f65682ae5b41 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 __builtin_common_type BuiltinTemplateDecl.
+ BTK__builtin_common_type,
};
} // end namespace clang
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 85b3984940ffc2..8bd5abf2bf9643 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 c2fb7dddcfc637..bba97e289da2e1 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 976b3a3e1ecedb..6fe817c5ef1c6b 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 1d671ab72b0c03..2b62f573857ee8 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 76bfbdebdbbfff..ed5d44aa898f4f 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 b052afede2cd67..92274cda15e0b7 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 47a286be2303fd..ede3070787722d 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 008bf571f847dc..4ee14b1e260159 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();
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 00000000000000..7190dcad76f1a3
--- /dev/null
+++ b/clang/test/SemaCXX/type-trait-common-type.cpp
@@ -0,0 +1,210 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -verify -std=c++17 -Wno-vla-cxx-extension %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -verify -std=c++20 -Wno-vla-cxx-extension %s
+
+#if !__has_builtin(__builtin_common_type)
+# error
+#endif
+
+// expected-note@*:* {{template declaration from hidden source: template <template <class ...> class, template <class> class, class, class ...>}}
+
+void test() {
+ __builtin_common_type<> a; // expected-error {{too few template arguments for template '__builtin_common_type'}}
+ __builtin_common_type<1> b; // expected-error {{template argument for template template parameter must be a class template or type alias template}}
+ __builtin_common_type<int, 1> c; // expected-error {{template argument for template template parameter must be a class template or type alias template}}
+}
+
+struct empty_type {};
+
+template <class T>
+struct type_identity {
+ using type = T;
+};
+
+template <class...>
+struct common_type;
+
+template <class... Args>
+using common_type_t = typename common_type<Args...>::type;
+
+void test_vla() {
+ int i = 4;
+ int VLA[i];
+ __builtin_common_type<common_type_t, type_identity, empty_type, decltype(VLA)> d; // expected-error {{variably modified type 'decltype(VLA)' (aka 'int[i]') cannot be used as a template argument}}
+}
+
+template <class... Args>
+using common_type_base = __builtin_common_type<common_type_t, type_identity, empty_type, Args...>;
+
+template <class... Args>
+struct common_type : common_type_base<Args...> {};
+
+struct Incomplete;
+
+template<>
+struct common_type<Incomplete, Incomplete>;
+
+static_assert(__is_same(common_type_base<>, empty_type));
+static_assert(__is_same(common_type_base<Incomplete>, empty_type));
+static_assert(__is_same(common_type_base<char>, type_identity<char>));
+static_assert(__is_same(common_type_base<int>, type_identity<int>));
+static_assert(__is_same(common_type_base<const int>, type_identity<int>));
+static_assert(__is_same(common_type_base<volatile int>, type_identity<int>));
+static_assert(__is_same(common_type_base<const volatile int>, type_identity<int>));
+static_assert(__is_same(common_type_base<int[]>, type_identity<int*>));
+static_assert(__is_same(common_type_base<const int[]>, type_identity<const int*>));
+static_assert(__is_same(common_type_base<void(&)()>, type_identity<void(*)()>));
+static_assert(__is_same(common_type_base<int[], int[]>, type_identity<int*>));
+
+static_assert(__is_same(common_type_base<int, int>, type_identity<int>));
+static_assert(__is_same(common_type_base<int, long>, type_identity<long>));
+static_assert(__is_same(common_type_base<long, int>, type_identity<long>));
+static_assert(__is_same(common_type_base<long, long>, type_identity<long>));
+
+static_assert(__is_same(common_type_base<const int, long>, type_identity<long>));
+static_assert(__is_same(common_type_base<const volatile int, long>, type_identity<long>));
+static_assert(__is_same(common_type_base<int, const long>, type_identity<long>));
+static_assert(__is_same(common_type_base<int, const volatile long>, type_identity<long>));
+
+static_assert(__is_same(common_type_base<int*, long*>, empty_type));
+
+static_assert(__is_same(common_type_base<int, long, float>, type_identity<float>));
+static_assert(__is_same(common_type_base<unsigned, char, long>, type_identity<long>));
+static_assert(__is_same(common_type_base<long long, long long, long>, type_identity<long long>));
+
+static_assert(__is_same(common_type_base<int [[clang::address_space(1)]]>, type_identity<int [[clang::address_space(1)]]>));
+static_assert(__is_same(common_type_base<int [[clang::address_space(1)]], int>, type_identity<int>));
+static_assert(__is_same(common_type_base<long [[clang::address_space(1)]], int>, type_identity<long>));
+static_assert(__is_same(common_type_base<long [[clang::address_space(1)]], int [[clang::address_space(1)]]>, type_identity<long>));
+static_assert(__is_same(common_type_base<long [[clang::address_space(1)]], long [[clang::address_space(1)]]>, type_identity<long [[clang::address_space(1)]]>));
+static_assert(__is_same(common_type_base<long [[clang::address_space(1)]], long [[clang::address_space(2)]]>, type_identity<long>));
+
+struct S {};
+struct T : S {};
+
+static_assert(__is_same(common_type_base<int S::*, int S::*>, type_identity<int S::*>));
+static_assert(__is_same(common_type_base<int S::*, int T::*>, type_identity<int T::*>));
+static_assert(__is_same(common_type_base<int S::*, long S::*>, empty_type));
+
+static_assert(__is_same(common_type_base<int (S::*)(), int (S::*)()>, type_identity<int (S::*)()>));
+static_assert(__is_same(common_type_base<int (S::*)(), int (T::*)()>, type_identity<int (T::*)()>));
+static_assert(__is_same(common_type_base<int (S::*)(), long (S::*)()>, empty_type));
+
+struct NoCommonType {};
+
+template <>
+struct common_type<NoCommonType, NoCommonType> {};
+
+struct CommonTypeInt {};
+
+template <>
+struct common_type<CommonTypeInt, CommonTypeInt> {
+ using type = int;
+};
+
+template <>
+struct common_type<CommonTypeInt, int> {
+ using type = int;
+};
+
+template <>
+struct common_type<int, CommonTypeInt> {
+ using type = int;
+};
+
+static_assert(__is_same(common_type_base<NoCommonType>, empty_type));
+static_assert(__is_same(common_type_base<CommonTypeInt>, type_identity<int>));
+static_assert(__is_same(common_type_base<NoCommonType, NoCommonType, NoCommonType>, empty_type));
+static_assert(__is_same(common_type_base<CommonTypeInt, CommonTypeInt, CommonTypeInt>, type_identity<int>));
+static_assert(__is_same(common_type_base<CommonTypeInt&, CommonTypeInt&&>, type_identity<int>));
+
+static_assert(__is_same(common_type_base<void, int>, empty_type));
+static_assert(__is_same(common_type_base<void, void>, type_identity<void>));
+static_assert(__is_same(common_type_base<const void, void>, type_identity<void>));
+static_assert(__is_same(common_type_base<void, const void>, type_identity<void>));
+
+template <class T>
+struct ConvertibleTo {
+ operator T();
+};
+
+static_assert(__is_same(common_type_base<ConvertibleTo<int>>, type_identity<ConvertibleTo<int>>));
+static_assert(__is_same(common_type_base<ConvertibleTo<int>, int>, type_identity<int>));
+static_assert(__is_same(common_type_base<ConvertibleTo<int&>, ConvertibleTo<long&>>, type_identity<long>));
+
+struct ConvertibleToB;
+
+struct ConvertibleToA {
+ operator ConvertibleToB();
+};
+
+struct ConvertibleToB {
+ operator ConvertibleToA();
+};
+
+static_assert(__is_same(common_type_base<ConvertibleToA, ConvertibleToB>, empty_type));
+
+struct const_ref_convertible {
+ operator int&() const &;
+ operator int&() && = delete;
+};
+
+#if __cplusplus >= 202002L
+static_assert(__is_same(common_type_base<const_ref_convertible, int &>, type_identity<int>));
+#else
+static_assert(__is_same(common_type_base<const_ref_convertible, int &>, empty_type));
+#endif
+
+struct WeirdConvertible_1p2_p3 {};
+
+struct WeirdConvertible3 {
+ operator WeirdConvertible_1p2_p3();
+};
+
+struct WeirdConvertible1p2 {
+ operator WeirdConvertible_1p2_p3();
+};
+
+template <>
+struct common_type<WeirdConvertible3, WeirdConvertible1p2> {
+ using type = WeirdConvertible_1p2_p3;
+};
+
+template <>
+struct common_type<WeirdConvertible1p2, WeirdConvertible3> {
+ using type = WeirdConvertible_1p2_p3;
+};
+
+struct WeirdConvertible1 {
+ operator WeirdConvertible1p2();
+};
+
+struct WeirdConvertible2 {
+ operator WeirdConvertible1p2();
+};
+
+template <>
+struct common_type<WeirdConvertible1, WeirdConvertible2> {
+ using type = WeirdConvertible1p2;
+};
+
+template <>
+struct common_type<WeirdConvertible2, WeirdConvertible1> {
+ using type = WeirdConvertible1p2;
+};
+
+static_assert(__is_same(common_type_base<WeirdConvertible1, WeirdConvertible2, WeirdConvertible3>,
+ type_identity<WeirdConvertible_1p2_p3>));
+
+struct PrivateTypeMember
+{
+ operator int();
+};
+
+template<>
+struct common_type<PrivateTypeMember, PrivateTypeMember>
+{
+private:
+ using type = int;
+};
+
+static_assert(__is_same(common_type_base<PrivateTypeMember, PrivateTypeMember, PrivateTypeMember>, empty_type));
diff --git a/libcxx/include/__type_traits/common_type.h b/libcxx/include/__type_traits/common_type.h
index f6bd9ed71b7a47..ef542f8bccfe02 100644
--- a/libcxx/include/__type_traits/common_type.h
+++ b/libcxx/include/__type_traits/common_type.h
@@ -14,8 +14,10 @@
#include <__type_traits/decay.h>
#include <__type_traits/is_same.h>
#include <__type_traits/remove_cvref.h>
+#include <__type_traits/type_identity.h>
#include <__type_traits/void_t.h>
#include <__utility/declval.h>
+#include <__utility/empty.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
@@ -23,7 +25,19 @@
_LIBCPP_BEGIN_NAMESPACE_STD
-#if _LIBCPP_STD_VER >= 20
+#if __has_builtin(__builtin_common_type)
+
+template <class... _Args>
+struct common_type;
+
+template <class... _Args>
+using __common_type_t = typename common_type<_Args...>::type;
+
+template <class... _Args>
+struct common_type : __builtin_common_type<__common_type_t, __type_identity, __empty, _Args...> {};
+
+#else
+# if _LIBCPP_STD_VER >= 20
// Let COND_RES(X, Y) be:
template <class _Tp, class _Up>
using __cond_type = decltype(false ? std::declval<_Tp>() : std::declval<_Up>());
@@ -39,10 +53,10 @@ struct __common_type3<_Tp, _Up, void_t<__cond_type<const _Tp&, const _Up&>>> {
template <class _Tp, class _Up, class = void>
struct __common_type2_imp : __common_type3<_Tp, _Up> {};
-#else
+# else
template <class _Tp, class _Up, class = void>
struct __common_type2_imp {};
-#endif
+# endif
// sub-bullet 3 - "if decay_t<decltype(false ? declval<D1>() : declval<D2>())> ..."
template <class _Tp, class _Up>
@@ -92,6 +106,8 @@ template <class _Tp, class _Up, class _Vp, class... _Rest>
struct _LIBCPP_TEMPLATE_VIS common_type<_Tp, _Up, _Vp, _Rest...>
: __common_type_impl<__common_types<_Tp, _Up, _Vp, _Rest...> > {};
+#endif
+
#if _LIBCPP_STD_VER >= 14
template <class... _Tp>
using common_type_t = typename common_type<_Tp...>::type;
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index c1181a3622513f..ef4a242cf8bf7f 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1858,7 +1858,9 @@ module std_private_type_traits_common_reference [system
}
module std_private_type_traits_common_type [system] {
header "__type_traits/common_type.h"
+ export std_private_type_traits_type_identity
export std_private_utility_declval
+ export std_private_utility_empty
}
module std_private_type_traits_conditional [system] { header "__type_traits/conditional.h" }
module std_private_type_traits_conjunction [system] { header "__type_traits/conjunction.h" }
More information about the cfe-commits
mailing list