[clang] [libcxx] [Clang] Add __builtin_common_reference (PR #121199)

via cfe-commits cfe-commits at lists.llvm.org
Mon Mar 31 11:20:13 PDT 2025


================
@@ -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),
----------------
huixie90 wrote:

perhaps update the status of P2655 is partially implemented?

https://github.com/llvm/llvm-project/pull/121199


More information about the cfe-commits mailing list