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" } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits