[clang] [libcxx] [Clang] Add __builtin_common_reference (PR #121199)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 31 06:35:50 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
@llvm/pr-subscribers-libcxx
Author: Nikolas Klauser (philnik777)
<details>
<summary>Changes</summary>
---
Patch is 40.04 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/121199.diff
9 Files Affected:
- (modified) clang/docs/LanguageExtensions.rst (+17)
- (modified) clang/include/clang/Basic/BuiltinTemplates.td (+28-2)
- (modified) clang/include/clang/Sema/Sema.h (+19)
- (modified) clang/lib/Sema/SemaExprCXX.cpp (+5-87)
- (modified) clang/lib/Sema/SemaTemplate.cpp (+335-18)
- (modified) clang/lib/Sema/SemaType.cpp (+75)
- (added) clang/test/SemaCXX/type-trait-common-reference.cpp (+141)
- (modified) libcxx/include/__type_traits/common_reference.h (+26-11)
- (modified) libcxx/include/module.modulemap (+1)
``````````diff
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..47601c1840b92 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);
+ if (VK == VK_XValue)
+ return S.BuiltinAddRValueReference(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...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/121199
More information about the cfe-commits
mailing list