https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/121199
>From 3010ba7ab5392394b37261807fc7cf4cbb205e0c Mon Sep 17 00:00:00 2001 From: Nikolas Klauser <nikolasklau...@berlin.de> Date: Fri, 27 Sep 2024 22:11:14 +0200 Subject: [PATCH] [Clang] Add __builtin_common_reference --- clang/docs/LanguageExtensions.rst | 17 + clang/include/clang/Basic/BuiltinTemplates.td | 30 +- clang/include/clang/Sema/Sema.h | 19 + clang/lib/Sema/SemaExprCXX.cpp | 92 +---- clang/lib/Sema/SemaTemplate.cpp | 355 +++++++++++++++++- clang/lib/Sema/SemaType.cpp | 75 ++++ .../SemaCXX/type-trait-common-reference.cpp | 123 ++++++ .../include/__type_traits/common_reference.h | 36 +- 8 files changed, 629 insertions(+), 118 deletions(-) create mode 100644 clang/test/SemaCXX/type-trait-common-reference.cpp diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 3b8a9cac6587a..6fc86507ca423 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -1677,6 +1677,23 @@ Builtin type aliases Clang provides a few builtin aliases to improve the throughput of certain metaprogramming facilities. +__builtin_common_reference +-------------------------- + +.. code-block:: c++ + + template <template <class, class, template <class> class, template <class> class> class BasicCommonReferenceT, + template <class... Args> CommonTypeT, + template <class> HasTypeMember, + class HasNoTypeMember, + class... Ts> + using __builtin_common_reference = ...; + +This alias is used for implementing ``std::common_refernce``. If ``std::common_reference`` should contain a ``type`` +member, it is an alias to ``HasTypeMember<TheCommonReference>``. Otherwse it is an alias to ``HasNoTypeMember``. The +``CommonTypeT`` is usually ``std::common_type_t``. ``BasicCommonReferenceT`` is usually an alias template to +``basic_common_reference<T, U, TX, UX>::type``. + __builtin_common_type --------------------- diff --git a/clang/include/clang/Basic/BuiltinTemplates.td b/clang/include/clang/Basic/BuiltinTemplates.td index d46ce063d2f7e..5c79e89800829 100644 --- a/clang/include/clang/Basic/BuiltinTemplates.td +++ b/clang/include/clang/Basic/BuiltinTemplates.td @@ -10,11 +10,11 @@ class TemplateArg<string name> { string Name = name; } -class Template<list<TemplateArg> args, string name> : TemplateArg<name> { +class Template<list<TemplateArg> args, string name = ""> : TemplateArg<name> { list<TemplateArg> Args = args; } -class Class<string name, bit is_variadic = 0> : TemplateArg<name> { +class Class<string name = "", bit is_variadic = 0> : TemplateArg<name> { bit IsVariadic = is_variadic; } @@ -50,3 +50,29 @@ def __builtin_common_type : BuiltinTemplate< Template<[Class<"TypeMember">], "HasTypeMember">, Class<"HasNoTypeMember">, Class<"Ts", /*is_variadic=*/1>]>; + +// template <template <class," +// class," +// template <class> class," +// template <class> class> class BasicCommonReferenceT," +// template <class... Args> class CommonTypeT," +// template <class> class HasTypeMember," +// class HasNoTypeMember," +// class... Ts>" +def __builtin_common_reference : BuiltinTemplate< + [Template<[Class<>, + Class<>, + Template<[Class<>]>, + Template<[Class<>]>], "BasicCommonReferenceT">, + Template<[Class<"Args", /*is_variadic=*/1>], "CommonTypeT">, + Template<[Class<>], "HasTypeMember">, + Class<"HasNoTypeMember">, + Class<"Ts", /*is_variadic=*/1>]>; + +foreach Ref = ["", "lvalue", "rvalue"] in { + foreach Const = ["", "const"] in { + foreach Volatile = ["", "volatile"] in { + def __clang_internal_xref_#Ref#Const#Volatile : BuiltinTemplate<[Class<>]>; + } + } +} diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 066bce61c74c1..762cb851ee04f 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -15076,15 +15076,34 @@ class Sema final : public SemaBase { QualType BuiltinDecay(QualType BaseType, SourceLocation Loc); QualType BuiltinAddReference(QualType BaseType, UTTKind UKind, SourceLocation Loc); + + QualType BuiltinAddRValueReference(QualType BaseType, SourceLocation Loc) { + return BuiltinAddReference(BaseType, UnaryTransformType::AddRvalueReference, + Loc); + } + + QualType BuiltinAddLValueReference(QualType BaseType, SourceLocation Loc) { + return BuiltinAddReference(BaseType, UnaryTransformType::AddLvalueReference, + Loc); + } + QualType BuiltinRemoveExtent(QualType BaseType, UTTKind UKind, SourceLocation Loc); QualType BuiltinRemoveReference(QualType BaseType, UTTKind UKind, SourceLocation Loc); + + QualType BuiltinRemoveCVRef(QualType BaseType, SourceLocation Loc) { + return BuiltinRemoveReference(BaseType, UTTKind::RemoveCVRef, Loc); + } + QualType BuiltinChangeCVRQualifiers(QualType BaseType, UTTKind UKind, SourceLocation Loc); QualType BuiltinChangeSignedness(QualType BaseType, UTTKind UKind, SourceLocation Loc); + bool BuiltinIsConvertible(QualType From, QualType To, SourceLocation Loc, + bool CheckNothrow = false); + /// Ensure that the type T is a literal type. /// /// This routine checks whether the type @p T is a literal type. If @p T is an diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp index 46895db4a0756..5ca5a8a57fa0f 100644 --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -5739,76 +5739,6 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT, static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs, const TypeSourceInfo *Rhs, SourceLocation KeyLoc); -static ExprResult CheckConvertibilityForTypeTraits( - Sema &Self, const TypeSourceInfo *Lhs, const TypeSourceInfo *Rhs, - SourceLocation KeyLoc, llvm::BumpPtrAllocator &OpaqueExprAllocator) { - - QualType LhsT = Lhs->getType(); - QualType RhsT = Rhs->getType(); - - // C++0x [meta.rel]p4: - // Given the following function prototype: - // - // template <class T> - // typename add_rvalue_reference<T>::type create(); - // - // the predicate condition for a template specialization - // is_convertible<From, To> shall be satisfied if and only if - // the return expression in the following code would be - // well-formed, including any implicit conversions to the return - // type of the function: - // - // To test() { - // return create<From>(); - // } - // - // Access checking is performed as if in a context unrelated to To and - // From. Only the validity of the immediate context of the expression - // of the return-statement (including conversions to the return type) - // is considered. - // - // We model the initialization as a copy-initialization of a temporary - // of the appropriate type, which for this expression is identical to the - // return statement (since NRVO doesn't apply). - - // Functions aren't allowed to return function or array types. - if (RhsT->isFunctionType() || RhsT->isArrayType()) - return ExprError(); - - // A function definition requires a complete, non-abstract return type. - if (!Self.isCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT) || - Self.isAbstractType(Rhs->getTypeLoc().getBeginLoc(), RhsT)) - return ExprError(); - - // Compute the result of add_rvalue_reference. - if (LhsT->isObjectType() || LhsT->isFunctionType()) - LhsT = Self.Context.getRValueReferenceType(LhsT); - - // Build a fake source and destination for initialization. - InitializedEntity To(InitializedEntity::InitializeTemporary(RhsT)); - Expr *From = new (OpaqueExprAllocator.Allocate<OpaqueValueExpr>()) - OpaqueValueExpr(KeyLoc, LhsT.getNonLValueExprType(Self.Context), - Expr::getValueKindForType(LhsT)); - InitializationKind Kind = - InitializationKind::CreateCopy(KeyLoc, SourceLocation()); - - // Perform the initialization in an unevaluated context within a SFINAE - // trap at translation unit scope. - EnterExpressionEvaluationContext Unevaluated( - Self, Sema::ExpressionEvaluationContext::Unevaluated); - Sema::SFINAETrap SFINAE(Self, /*AccessCheckingSFINAE=*/true); - Sema::ContextRAII TUContext(Self, Self.Context.getTranslationUnitDecl()); - InitializationSequence Init(Self, To, Kind, From); - if (Init.Failed()) - return ExprError(); - - ExprResult Result = Init.Perform(Self, To, Kind, From); - if (Result.isInvalid() || SFINAE.hasErrorOccurred()) - return ExprError(); - - return Result; -} - static APValue EvaluateSizeTTypeTrait(Sema &S, TypeTrait Kind, SourceLocation KWLoc, ArrayRef<TypeSourceInfo *> Args, @@ -5958,9 +5888,8 @@ static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind, S.Context.getPointerType(T.getNonReferenceType())); TypeSourceInfo *UPtr = S.Context.CreateTypeSourceInfo( S.Context.getPointerType(U.getNonReferenceType())); - return !CheckConvertibilityForTypeTraits(S, UPtr, TPtr, RParenLoc, - OpaqueExprAllocator) - .isInvalid(); + return S.BuiltinIsConvertible(UPtr->getType(), TPtr->getType(), + RParenLoc); } if (Kind == clang::TT_IsNothrowConstructible) @@ -6200,20 +6129,9 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceI } case BTT_IsConvertible: case BTT_IsConvertibleTo: - case BTT_IsNothrowConvertible: { - if (RhsT->isVoidType()) - return LhsT->isVoidType(); - llvm::BumpPtrAllocator OpaqueExprAllocator; - ExprResult Result = CheckConvertibilityForTypeTraits(Self, Lhs, Rhs, KeyLoc, - OpaqueExprAllocator); - if (Result.isInvalid()) - return false; - - if (BTT != BTT_IsNothrowConvertible) - return true; - - return Self.canThrow(Result.get()) == CT_Cannot; - } + case BTT_IsNothrowConvertible: + return Self.BuiltinIsConvertible(LhsT, RhsT, KeyLoc, + BTT == BTT_IsNothrowConvertible); case BTT_IsAssignable: case BTT_IsNothrowAssignable: diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index be81b6a46b2c0..9ef45871401dc 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -3097,6 +3097,33 @@ void Sema::NoteAllFoundTemplates(TemplateName Name) { } } +static QualType InstantiateTemplate(Sema &S, TemplateName Template, + ArrayRef<TemplateArgument> Args, + SourceLocation Loc) { + TemplateArgumentListInfo ArgList; + for (auto Arg : Args) { + if (Arg.getKind() == TemplateArgument::Type) { + ArgList.addArgument(TemplateArgumentLoc( + Arg, S.Context.getTrivialTypeSourceInfo(Arg.getAsType()))); + } else { + ArgList.addArgument( + S.getTrivialTemplateArgumentLoc(Arg, QualType(), Loc)); + } + } + + EnterExpressionEvaluationContext UnevaluatedContext( + S, Sema::ExpressionEvaluationContext::Unevaluated); + Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/true); + Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl()); + + QualType Instantiation = S.CheckTemplateIdType(Template, Loc, ArgList); + + if (SFINAE.hasErrorOccurred()) + return QualType(); + + return Instantiation; +} + static QualType builtinCommonTypeImpl(Sema &S, TemplateName BaseTemplate, SourceLocation TemplateLoc, ArrayRef<TemplateArgument> Ts) { @@ -3107,24 +3134,7 @@ static QualType builtinCommonTypeImpl(Sema &S, TemplateName BaseTemplate, 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; + return InstantiateTemplate(S, BaseTemplate, {T1, T2}, TemplateLoc); }; // Note A: For the common_type trait applied to a template parameter pack T of @@ -3231,6 +3241,230 @@ static QualType builtinCommonTypeImpl(Sema &S, TemplateName BaseTemplate, } } +static QualType CopyCV(QualType From, QualType To) { + if (From.isConstQualified()) + To.addConst(); + if (From.isVolatileQualified()) + To.addVolatile(); + return To; +} + +// COND-RES(X, Y) be decltype(false ? declval<X(&)()>()() : declval<Y(&)()>()()) +static QualType CondRes(Sema &S, QualType X, QualType Y, SourceLocation Loc) { + 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; + + // declval<X(&)()>()() + OpaqueValueExpr LHSExpr(Loc, X.getNonLValueExprType(S.Context), + Expr::getValueKindForType(X)); + ExprResult LHS = &LHSExpr; + + // declval<Y(&)()>()() + OpaqueValueExpr RHSExpr(Loc, Y.getNonLValueExprType(S.Context), + Expr::getValueKindForType(Y)); + ExprResult RHS = &RHSExpr; + + ExprValueKind VK = VK_PRValue; + ExprObjectKind OK = OK_Ordinary; + + // decltype(false ? declval<X(&)()>()() : declval<Y(&)()>()()) + QualType Result = + S.CheckConditionalOperands(Cond, LHS, RHS, VK, OK, Loc); + + if (SFINAE.hasErrorOccurred()) + return QualType(); + if (VK == VK_LValue) + return S.BuiltinAddLValueReference(Result, Loc); + return Result; +} + +static QualType CommonRef(Sema &S, QualType A, QualType B, + SourceLocation Loc) { + // Given types A and B, let X be remove_reference_t<A>, let Y be + // remove_reference_t<B>, and let COMMON-REF(A, B) be: + assert(A->isReferenceType() && B->isReferenceType() && + "A and B have to be ref qualified for a COMMON-REF"); + auto X = A.getNonReferenceType(); + auto Y = B.getNonReferenceType(); + + // If A and B are both lvalue reference types, COMMON-REF(A, B) is + // COND-RES(COPYCV(X, Y) &, COPYCV(Y, X) &) if that type exists and is a + // reference type. + if (A->isLValueReferenceType() && B->isLValueReferenceType()) { + auto CR = CondRes(S, S.BuiltinAddLValueReference(CopyCV(X, Y), Loc), + S.BuiltinAddLValueReference(CopyCV(Y, X), Loc), Loc); + if (CR.isNull() || !CR->isReferenceType()) + return QualType(); + return CR; + } + + // Otherwise, let C be remove_reference_t<COMMON-REF(X&, Y&)>&&. If A and B + // are both rvalue reference types, C is well-formed, and + // is_convertible_v<A, C> && is_convertible_v<B, C> is true, then + // COMMON-REF(A, B) is C. + if (A->isRValueReferenceType() && B->isRValueReferenceType()) { + auto C = CommonRef(S, S.BuiltinAddLValueReference(X, Loc), + S.BuiltinAddLValueReference(Y, Loc), Loc); + if (C.isNull()) + return QualType(); + + C = C.getNonReferenceType(); + + if (S.BuiltinIsConvertible(A, C, Loc) && S.BuiltinIsConvertible(B, C, Loc)) + return S.BuiltinAddRValueReference(C, Loc); + return QualType(); + } + + // Otherwise, if A is an lvalue reference and B is an rvalue reference, then + // COMMON-REF(A, B) is COMMON-REF(B, A). + if (A->isLValueReferenceType() && B->isRValueReferenceType()) + std::swap(A, B); + + // Otherwise, let D be COMMON-REF(const X&, Y&). If A is an rvalue reference + // and B is an lvalue reference and D is well-formed and + // is_convertible_v<A, D> is true, then COMMON-REF(A, B) is D. + if (A->isRValueReferenceType() && B->isLValueReferenceType()) { + auto X2 = X; + X2.addConst(); + auto D = CommonRef(S, S.BuiltinAddLValueReference(X2, Loc), + S.BuiltinAddLValueReference(Y, Loc), Loc); + if (!D.isNull() && S.BuiltinIsConvertible(A, D, Loc)) + return D; + return QualType(); + } + + // Otherwise, COMMON-REF(A, B) is ill-formed. + // This is implemented by returning from the individual branches above. + + llvm_unreachable("The above cases should be exhaustive"); +} + +static QualType builtinCommonReferenceImpl(Sema &S, + TemplateName CommonReference, + TemplateName CommonType, + SourceLocation TemplateLoc, + ArrayRef<TemplateArgument> Ts) { + switch (Ts.size()) { + // If sizeof...(T) is zero, there shall be no member type. + case 0: + return QualType(); + + // Otherwise, if sizeof...(T) is one, let T0 denote the sole type in the + // pack T. The member typedef type shall denote the same type as T0. + case 1: + return Ts[0].getAsType(); + + // Otherwise, if sizeof...(T) is two, let T1 and T2 denote the two types in + // the pack T. Then + case 2: { + auto T1 = Ts[0].getAsType(); + auto T2 = Ts[1].getAsType(); + + // Let R be COMMON-REF(T1, T2). If T1 and T2 are reference types, R is + // well-formed, and is_convertible_v<add_pointer_t<T1>, add_pointer_t<R>> && + // is_convertible_v<add_pointer_t<T2>, add_pointer_t<R>> is true, then the + // member typedef type denotes R. + if (T1->isReferenceType() && T2->isReferenceType()) { + QualType R = CommonRef(S, T1, T2, TemplateLoc); + if (!R.isNull()) { + if (S.BuiltinIsConvertible(S.BuiltinAddPointer(T1, TemplateLoc), + S.BuiltinAddPointer(R, TemplateLoc), + TemplateLoc) && + S.BuiltinIsConvertible(S.BuiltinAddPointer(T2, TemplateLoc), + S.BuiltinAddPointer(R, TemplateLoc), + TemplateLoc)) { + return R; + } + } + } + + // Otherwise, if basic_common_reference<remove_cvref_t<T1>, + // remove_cvref_t<T2>, XREF(T1), XREF(T2)>::type is well-formed, + // then the member typedef type denotes that type. + { + auto getXRef = [&](QualType T) { + static BuiltinTemplateDecl* Quals[12] = { + S.Context.get__clang_internal_xref_Decl(), + S.Context.get__clang_internal_xref_constDecl(), + S.Context.get__clang_internal_xref_volatileDecl(), + S.Context.get__clang_internal_xref_constvolatileDecl(), + S.Context.get__clang_internal_xref_lvalueDecl(), + S.Context.get__clang_internal_xref_lvalueconstDecl(), + S.Context.get__clang_internal_xref_lvaluevolatileDecl(), + S.Context.get__clang_internal_xref_lvalueconstvolatileDecl(), + S.Context.get__clang_internal_xref_rvalueDecl(), + S.Context.get__clang_internal_xref_rvalueconstDecl(), + S.Context.get__clang_internal_xref_rvaluevolatileDecl(), + S.Context.get__clang_internal_xref_rvalueconstvolatileDecl(), + }; + size_t Index = 0; + if (T->isLValueReferenceType()) { + T = T.getNonReferenceType(); + Index += 4; + } else if (T->isRValueReferenceType()) { + T = T.getNonReferenceType(); + Index += 8; + } + if (T.isConstQualified()) + Index += 1; + + if (T.isVolatileQualified()) + Index += 2; + + return Quals[Index]; + }; + + auto BCR = InstantiateTemplate( + S, CommonReference, + {S.BuiltinRemoveCVRef(T1, TemplateLoc), + S.BuiltinRemoveCVRef(T2, TemplateLoc), + TemplateName{getXRef(T1)}, + TemplateName{getXRef(T2)}}, + TemplateLoc); + if (!BCR.isNull()) + return BCR; + } + + // Otherwise, if COND-RES(T1, T2) is well-formed, then the member typedef + // type denotes that type. + if (auto CR = CondRes(S, T1, T2, TemplateLoc); !CR.isNull()) + return CR; + + // Otherwise, if common_type_t<T1, T2> is well-formed, then the member + // typedef type denotes that type. + if (auto CT = InstantiateTemplate(S, CommonType, {T1, T2}, TemplateLoc); + !CT.isNull()) + return CT; + + // Otherwise, there shall be no member type. + return QualType(); + } + + // Otherwise, if sizeof...(T) is greater than two, let T1, T2, and Rest, + // respectively, denote the first, second, and (pack of) remaining types + // comprising T. Let C be the type common_reference_t<T1, T2>. Then: + default: { + auto T1 = Ts[0]; + auto T2 = Ts[1]; + auto Rest = Ts.drop_front(2); + auto C = builtinCommonReferenceImpl(S, CommonReference, CommonType, TemplateLoc, {T1, T2}); + if (C.isNull()) + return QualType(); + llvm::SmallVector<TemplateArgument, 4> Args; + Args.emplace_back(C); + Args.append(Rest.begin(), Rest.end()); + return builtinCommonReferenceImpl(S, CommonReference, CommonType, TemplateLoc, Args); + } + } +} + static QualType checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD, ArrayRef<TemplateArgument> Converted, @@ -3335,6 +3569,91 @@ checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD, } return HasNoTypeMember; } + + case BTK__builtin_common_reference: { + assert(Converted.size() == 5); + if (llvm::any_of(Converted, [](auto &C) { return C.isDependent(); })) + return Context.getCanonicalTemplateSpecializationType(TemplateName(BTD), + Converted); + + TemplateName BasicCommonReference = Converted[0].getAsTemplate(); + TemplateName CommonType = Converted[1].getAsTemplate(); + TemplateName HasTypeMember = Converted[2].getAsTemplate(); + QualType HasNoTypeMember = Converted[3].getAsType(); + ArrayRef<TemplateArgument> Ts = Converted[4].getPackAsArray(); + if (auto CR = builtinCommonReferenceImpl(SemaRef, BasicCommonReference, + CommonType, TemplateLoc, Ts); + !CR.isNull()) { + TemplateArgumentListInfo TAs; + TAs.addArgument(TemplateArgumentLoc( + TemplateArgument(CR), SemaRef.Context.getTrivialTypeSourceInfo( + CR, TemplateArgs[1].getLocation()))); + return SemaRef.CheckTemplateIdType(HasTypeMember, TemplateLoc, TAs); + } + return HasNoTypeMember; + } + + case BTK__clang_internal_xref_: + case BTK__clang_internal_xref_const: + case BTK__clang_internal_xref_volatile: + case BTK__clang_internal_xref_constvolatile: + case BTK__clang_internal_xref_lvalue: + case BTK__clang_internal_xref_lvalueconst: + case BTK__clang_internal_xref_lvaluevolatile: + case BTK__clang_internal_xref_lvalueconstvolatile: + case BTK__clang_internal_xref_rvalue: + case BTK__clang_internal_xref_rvalueconst: + case BTK__clang_internal_xref_rvaluevolatile: + case BTK__clang_internal_xref_rvalueconstvolatile: { + if (llvm::any_of(Converted, [](auto &C) { return C.isDependent(); })) + return Context.getCanonicalTemplateSpecializationType(TemplateName(BTD), + Converted); + + auto BTK = BTD->getBuiltinTemplateKind(); + auto anyOf = [&](auto... Vals) { + return ((BTK == Vals) || ...); + }; + + bool AddCV = anyOf(BTK__clang_internal_xref_constvolatile, + BTK__clang_internal_xref_lvalueconstvolatile, + BTK__clang_internal_xref_rvalueconstvolatile); + + bool AddConst = AddCV || anyOf(BTK__clang_internal_xref_const, + BTK__clang_internal_xref_lvalueconst, + BTK__clang_internal_xref_rvalueconst); + + bool AddVolatile = AddCV || anyOf(BTK__clang_internal_xref_volatile, + BTK__clang_internal_xref_lvaluevolatile, + BTK__clang_internal_xref_rvaluevolatile); + + bool AddLValue = anyOf(BTK__clang_internal_xref_lvalue, + BTK__clang_internal_xref_lvalueconst, + BTK__clang_internal_xref_lvaluevolatile, + BTK__clang_internal_xref_lvalueconstvolatile); + + bool AddRValue = anyOf(BTK__clang_internal_xref_rvalue, + BTK__clang_internal_xref_rvalueconst, + BTK__clang_internal_xref_rvaluevolatile, + BTK__clang_internal_xref_rvalueconstvolatile); + + assert(Converted.size() == 1); + + QualType T = Converted[0].getAsType(); + + if (AddConst) + T.addConst(); + + if (AddVolatile) + T.addVolatile(); + + if (AddLValue) + T = SemaRef.BuiltinAddLValueReference(T, TemplateLoc); + + if (AddRValue) + T = SemaRef.BuiltinAddRValueReference(T, TemplateLoc); + + return T; + } } llvm_unreachable("unexpected BuiltinTemplateDecl!"); } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 776d6e55acc18..81e9c6c72761e 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -32,6 +32,8 @@ #include "clang/Lex/Preprocessor.h" #include "clang/Sema/DeclSpec.h" #include "clang/Sema/DelayedDiagnostic.h" +#include "clang/Sema/EnterExpressionEvaluationContext.h" +#include "clang/Sema/Initialization.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/ParsedAttr.h" #include "clang/Sema/ParsedTemplate.h" @@ -9904,6 +9906,79 @@ QualType Sema::BuiltinChangeSignedness(QualType BaseType, UTTKind UKind, return Context.getQualifiedType(Underlying, BaseType.getQualifiers()); } +bool Sema::BuiltinIsConvertible(QualType From, QualType To, SourceLocation Loc, + bool CheckNothrow) { + if (To->isVoidType()) + return From->isVoidType(); + + if ((!From->isIncompleteArrayType() && !From->isVoidType() && + RequireCompleteType( + Loc, From, diag::err_incomplete_type_used_in_type_trait_expr)) || + (!To->isIncompleteArrayType() && !To->isVoidType() && + RequireCompleteType(Loc, To, + diag::err_incomplete_type_used_in_type_trait_expr))) + return false; + + // C++11 [meta.rel]p4: + // Given the following function prototype: + // + // template <class T> + // typename add_rvalue_reference<T>::type create(); + // + // the predicate condition for a template specialization + // is_convertible<From, To> shall be satisfied if and only if + // the return expression in the following code would be + // well-formed, including any implicit conversions to the return + // type of the function: + // + // To test() { + // return create<From>(); + // } + // + // Access checking is performed as if in a context unrelated to To and + // From. Only the validity of the immediate context of the expression + // of the return-statement (including conversions to the return type) + // is considered. + // + // We model the initialization as a copy-initialization of a temporary + // of the appropriate type, which for this expression is identical to the + // return statement (since NRVO doesn't apply). + + // Functions aren't allowed to return function or array types. + if (To->isFunctionType() || To->isArrayType()) + return false; + + // A function definition requires a non-abstract return type. + if (isAbstractType(Loc, To)) + return false; + + From = BuiltinAddRValueReference(From, Loc); + + // Build a fake source and destination for initialization. + InitializedEntity ToEntity(InitializedEntity::InitializeTemporary(To)); + OpaqueValueExpr FromExpr(Loc, From.getNonLValueExprType(Context), + Expr::getValueKindForType(From)); + InitializationKind Kind = + InitializationKind::CreateCopy(Loc, SourceLocation()); + + // Perform the initialization in an unevaluated context within a SFINAE + // trap at translation unit scope. + EnterExpressionEvaluationContext Unevaluated( + *this, Sema::ExpressionEvaluationContext::Unevaluated); + Sema::SFINAETrap SFINAE(*this, /*AccessCheckingSFINAE=*/true); + Sema::ContextRAII TUContext(*this, Context.getTranslationUnitDecl()); + Expr *FromExprPtr = &FromExpr; + InitializationSequence Init(*this, ToEntity, Kind, FromExprPtr); + if (Init.Failed()) + return false; + + ExprResult Result = Init.Perform(*this, ToEntity, Kind, FromExprPtr); + if (Result.isInvalid() || SFINAE.hasErrorOccurred()) + return false; + + return !CheckNothrow || canThrow(Result.get()) == CT_Cannot; +} + QualType Sema::BuildUnaryTransformType(QualType BaseType, UTTKind UKind, SourceLocation Loc) { if (BaseType->isDependentType()) diff --git a/clang/test/SemaCXX/type-trait-common-reference.cpp b/clang/test/SemaCXX/type-trait-common-reference.cpp new file mode 100644 index 0000000000000..80518857652a9 --- /dev/null +++ b/clang/test/SemaCXX/type-trait-common-reference.cpp @@ -0,0 +1,123 @@ +// 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_reference) +# error +#endif + +// expected-note@*:* {{template <template <class, class, template <class> class, template <class> class> class, template <class ...> class, template <class> class, class, class ...>}} + +void test() { + __builtin_common_reference<> a; // expected-error {{too few template arguments for template '__builtin_common_reference'}} + __builtin_common_reference<1> b; // expected-error {{template argument for template template parameter must be a class template or type alias template}} + __builtin_common_reference<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; + +template <class, class, template <class> class, template <class> class> +struct basic_common_reference {}; + +template <class T, class U, template <class> class TX, template <class> class UX> +using basic_common_reference_t = typename basic_common_reference<T, U, TX, UX>::type; + +void test_vla() { + int i = 4; + int VLA[i]; + __builtin_common_reference<basic_common_reference_t, 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_reference_base = __builtin_common_reference<basic_common_reference_t, common_type_t, type_identity, empty_type, Args...>; + +template <class... Args> +struct common_reference : common_reference_base<Args...> {}; + +template <class... Args> +using common_reference_t = typename __builtin_common_reference<basic_common_reference_t, common_type_t, type_identity, empty_type, Args...>::type; + +struct Incomplete; + +template<> +struct common_type<Incomplete, Incomplete>; + +static_assert(__is_same(common_reference_base<>, empty_type)); + +static_assert(__is_same(common_reference_base<Incomplete>, type_identity<Incomplete>)); +static_assert(__is_same(common_reference_base<char>, type_identity<char>)); +static_assert(__is_same(common_reference_base<int>, type_identity<int>)); +static_assert(__is_same(common_reference_base<const int>, type_identity<const int>)); +static_assert(__is_same(common_reference_base<volatile int>, type_identity<volatile int>)); +static_assert(__is_same(common_reference_base<const volatile int>, type_identity<const volatile int>)); +static_assert(__is_same(common_reference_base<int[]>, type_identity<int[]>)); +static_assert(__is_same(common_reference_base<const int[]>, type_identity<const int[]>)); +static_assert(__is_same(common_reference_base<void(&)()>, type_identity<void(&)()>)); + +static_assert(__is_same(common_reference_base<int[], int[]>, type_identity<int*>)); +static_assert(__is_same(common_reference_base<int, int>, type_identity<int>)); +static_assert(__is_same(common_reference_base<int, long>, type_identity<long>)); +static_assert(__is_same(common_reference_base<long, int>, type_identity<long>)); +static_assert(__is_same(common_reference_base<long, long>, type_identity<long>)); + +static_assert(__is_same(common_reference_base<const int, long>, type_identity<long>)); +static_assert(__is_same(common_reference_base<const volatile int, long>, type_identity<long>)); +static_assert(__is_same(common_reference_base<int, const long>, type_identity<long>)); +static_assert(__is_same(common_reference_base<int, const volatile long>, type_identity<long>)); + +static_assert(__is_same(common_reference_base<int*, long*>, empty_type)); +static_assert(__is_same(common_reference_base<const unsigned int *const &, const unsigned int *const &>, type_identity<const unsigned int *const &>)); + +static_assert(__is_same(common_reference_base<int, long, float>, type_identity<float>)); +static_assert(__is_same(common_reference_base<unsigned, char, long>, type_identity<long>)); +static_assert(__is_same(common_reference_base<long long, long long, long>, type_identity<long long>)); + +static_assert(__is_same(common_reference_base<int [[clang::address_space(1)]]>, type_identity<int [[clang::address_space(1)]]>)); +static_assert(__is_same(common_reference_base<int [[clang::address_space(1)]], int>, type_identity<int>)); +static_assert(__is_same(common_reference_base<long [[clang::address_space(1)]], int>, type_identity<long>)); +static_assert(__is_same(common_reference_base<long [[clang::address_space(1)]], int [[clang::address_space(1)]]>, type_identity<long>)); +static_assert(__is_same(common_reference_base<long [[clang::address_space(1)]], long [[clang::address_space(1)]]>, type_identity<long>)); +static_assert(__is_same(common_reference_base<long [[clang::address_space(1)]], long [[clang::address_space(2)]]>, type_identity<long>)); + +struct S {}; +struct T : S {}; +struct U {}; + +static_assert(__is_same(common_reference_base<S&&, T&&>, type_identity<S&&>)); + +static_assert(__is_same(common_reference_base<int S::*, int S::*>, type_identity<int S::*>)); +static_assert(__is_same(common_reference_base<int S::*, int T::*>, type_identity<int T::*>)); +static_assert(__is_same(common_reference_base<int S::*, long S::*>, empty_type)); + +static_assert(__is_same(common_reference_base<int (S::*)(), int (S::*)()>, type_identity<int (S::*)()>)); +static_assert(__is_same(common_reference_base<int (S::*)(), int (T::*)()>, type_identity<int (T::*)()>)); +static_assert(__is_same(common_reference_base<int (S::*)(), long (S::*)()>, empty_type)); + +static_assert(__is_same(common_reference_base<int&, int&>, type_identity<int&>)); +static_assert(__is_same(common_reference_base<int&, const int&>, type_identity<const int&>)); +static_assert(__is_same(common_reference_base<volatile int&, const int&>, type_identity<const volatile int&>)); + +template <class T, class U> +struct my_pair; + +template <class T1, class U1, class T2, class U2, template <class> class TX, template <class> class UX> +struct basic_common_reference<my_pair<T1, U1>, my_pair<T2, U2>, TX, UX> { + using type = my_pair<common_reference_t<TX<T1>, UX<T2>>, common_reference_t<TX<U1>, UX<U2>>>; +}; + +static_assert(__is_same(common_reference_base<my_pair<const int&, int&>, my_pair<int&, volatile int&>>, type_identity<my_pair<const int&, volatile int&>>)); +static_assert(__is_same(common_reference_base<const my_pair<int, int>&, my_pair<int&, volatile int&>>, type_identity<my_pair<const int&, const volatile int&>>)); +static_assert(__is_same(common_reference_base<const int&, const volatile int&>, type_identity<const volatile int&>)); +static_assert(__is_same(common_reference_base<int&&, const volatile int&>, type_identity<int>)); +static_assert(__is_same(common_reference_base<my_pair<int, int>&&, my_pair<int&, volatile int&>>, type_identity<my_pair<const int&, int>>)); +static_assert(__is_same(common_reference_base<my_pair<int, int>&&, my_pair<int&, int>&&>, type_identity<my_pair<const int&, int&&>>)); diff --git a/libcxx/include/__type_traits/common_reference.h b/libcxx/include/__type_traits/common_reference.h index c27da5251b9b7..cd29d37bf2f22 100644 --- a/libcxx/include/__type_traits/common_reference.h +++ b/libcxx/include/__type_traits/common_reference.h @@ -17,16 +17,36 @@ #include <__type_traits/is_reference.h> #include <__type_traits/remove_cvref.h> #include <__type_traits/remove_reference.h> +#include <__type_traits/type_identity.h> #include <__utility/declval.h> #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) # pragma GCC system_header #endif +#if _LIBCPP_STD_VER >= 20 + _LIBCPP_BEGIN_NAMESPACE_STD -// common_reference -#if _LIBCPP_STD_VER >= 20 +template <class...> +struct common_reference; + +template <class... _Types> +using common_reference_t = typename common_reference<_Types...>::type; + +template <class, class, template <class> class, template <class> class> +struct basic_common_reference {}; + +#if __has_builtin(__builtin_common_reference) + +template <class _Tp, class _Up, template <class> class _Tx, template <class> class _Ux> +using __basic_common_reference_t = basic_common_reference<_Tp, _Up, _Tx, _Ux>::type; + +template <class... _Args> +struct common_reference : __builtin_common_reference<__basic_common_reference_t, common_type_t, type_identity, __empty, _Args...> {}; + +#else + // Let COND_RES(X, Y) be: template <class _Xp, class _Yp> using __cond_res _LIBCPP_NODEBUG = decltype(false ? std::declval<_Xp (&)()>()() : std::declval<_Yp (&)()>()()); @@ -108,12 +128,6 @@ struct __common_ref {}; // Note C: For the common_reference trait applied to a parameter pack [...] -template <class...> -struct common_reference; - -template <class... _Types> -using common_reference_t = typename common_reference<_Types...>::type; - // bullet 1 - sizeof...(T) == 0 template <> struct common_reference<> {}; @@ -145,8 +159,6 @@ struct __common_reference_sub_bullet1<_Tp, _Up> { // sub-bullet 2 - Otherwise, if basic_common_reference<remove_cvref_t<T1>, remove_cvref_t<T2>, XREF(T1), XREF(T2)>::type // is well-formed, then the member typedef `type` denotes that type. -template <class, class, template <class> class, template <class> class> -struct basic_common_reference {}; template <class _Tp, class _Up> using __basic_common_reference_t _LIBCPP_NODEBUG = @@ -185,8 +197,10 @@ struct common_reference<_Tp, _Up, _Vp, _Rest...> : common_reference<common_refer template <class...> struct common_reference {}; -#endif // _LIBCPP_STD_VER >= 20 +#endif // __has_builtin(__builtin_common_reference) _LIBCPP_END_NAMESPACE_STD +#endif // _LIBCPP_STD_VER >= 20 + #endif // _LIBCPP___TYPE_TRAITS_COMMON_REFERENCE_H _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits