[clang] [libcxx] [Clang] Add __builtin_common_reference (PR #121199)
Nikolas Klauser via cfe-commits
cfe-commits at lists.llvm.org
Thu Mar 6 03:07:49 PST 2025
https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/121199
>From 55f9d1e5abf5f217e58988742bd7f375d744beb8 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at 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 578ee02f09b9b..2cdf78cb0f108 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1657,6 +1657,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 80177996b48b0..241e6a678b79f 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -15026,15 +15026,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 34219e0235a74..62c60d6d7cdd2 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -5743,76 +5743,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 bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
SourceLocation KWLoc,
ArrayRef<TypeSourceInfo *> Args,
@@ -5934,9 +5864,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)
@@ -6167,20 +6096,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 fcbbf5dbffa53..1c4d8ce95bee5 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -3082,6 +3082,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) {
@@ -3092,24 +3119,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
@@ -3216,6 +3226,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,
@@ -3320,6 +3554,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 11943c0b53591..56961ca3498ba 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"
@@ -9988,6 +9990,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 0d35570da2622..9b83dde0dc594 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
More information about the cfe-commits
mailing list