Skip to content

Conversation

@philnik777
Copy link
Contributor

No description provided.

@github-actions
Copy link

github-actions bot commented Dec 27, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@philnik777 philnik777 force-pushed the builtin_common_reference branch from e0db731 to 55f9d1e Compare March 6, 2025 11:07
@philnik777 philnik777 force-pushed the builtin_common_reference branch 2 times, most recently from 3010ba7 to 19a7693 Compare March 29, 2025 13:39
@philnik777 philnik777 marked this pull request as ready for review March 31, 2025 13:35
@philnik777 philnik777 requested a review from a team as a code owner March 31, 2025 13:35
@llvmbot llvmbot added clang Clang issues not falling into any other category libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Mar 31, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 31, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-libcxx

Author: Nikolas Klauser (philnik777)

Changes

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 --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]

@philnik777 philnik777 force-pushed the builtin_common_reference branch from 19a7693 to 97d6d69 Compare April 11, 2025 20:16
@philnik777 philnik777 force-pushed the builtin_common_reference branch from 97d6d69 to 273d0e6 Compare April 11, 2025 20:17
@huixie90
Copy link
Member

FWIW, I created patch #141408 to implement the libc++ counter part of the missing bit.
So can i request to merge this patch after landing of my patch? the benifits are :

  • Avoid behaviour differences between the two branches
  • having more test coverage for the new behaviour that the paper introduces

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems reasonable to me? I'm not a huge fan of how much is being put into SemaTemplate.cpp to support all the builtins though. I'm wondering if we need to split that up in a follow-up and have a SemaTemplateBuiltins for all of these to live.

Please let Aaron/others take a poke at this, and make sure there are a few more Libc++ reviewers to make sure you're all in agreement with how this works (I see the convo that is happening).

Also, should we loop in libstdc++ folks here?

@philnik777
Copy link
Contributor Author

This seems reasonable to me? I'm not a huge fan of how much is being put into SemaTemplate.cpp to support all the builtins though. I'm wondering if we need to split that up in a follow-up and have a SemaTemplateBuiltins for all of these to live.

I can take a look at splitting things up in a follow-up. Shouldn't be too complicated. I do wonder whether it's worth the split, since it's a relatively small amount of code. OTOH it's a very clean split...

Please let Aaron/others take a poke at this, and make sure there are a few more Libc++ reviewers to make sure you're all in agreement with how this works (I see the convo that is happening).

Also, should we loop in libstdc++ folks here?

IDK whether they have any thought on this, but certainly doesn't hurt. CC @jwakely

@philnik777
Copy link
Contributor Author

@huixie90 Are you happy with landing this now?

@philnik777 philnik777 requested a review from huixie90 November 13, 2025 13:36
Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Precommit CI found relevant build failures:

  FAILED: tools/clang/lib/Sema/CMakeFiles/obj.clangSema.dir/SemaTemplate.cpp.o 
  sccache /opt/llvm/bin/clang++ -DCLANG_EXPORTS -DGTEST_HAS_RTTI=0 -D_DEBUG -D_GLIBCXX_ASSERTIONS -D_GLIBCXX_USE_CXX11_ABI=1 -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/lib/Sema -I/home/gha/actions-runner/_work/llvm-project/llvm-project/clang/lib/Sema -I/home/gha/actions-runner/_work/llvm-project/llvm-project/clang/include -I/home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/clang/include -I/home/gha/actions-runner/_work/llvm-project/llvm-project/build/include -I/home/gha/actions-runner/_work/llvm-project/llvm-project/llvm/include -gmlt -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror -Werror=date-time -Werror=unguarded-availability-new -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -pedantic -Wno-long-long -Wc++98-compat-extra-semi -Wimplicit-fallthrough -Wcovered-switch-default -Wno-noexcept-type -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wsuggest-override -Wstring-conversion -Wno-pass-failed -Wmisleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -fno-common -Woverloaded-virtual -Wno-nested-anon-types -O3 -DNDEBUG -std=c++17  -fno-exceptions -funwind-tables -fno-rtti -UNDEBUG -MD -MT tools/clang/lib/Sema/CMakeFiles/obj.clangSema.dir/SemaTemplate.cpp.o -MF tools/clang/lib/Sema/CMakeFiles/obj.clangSema.dir/SemaTemplate.cpp.o.d -o tools/clang/lib/Sema/CMakeFiles/obj.clangSema.dir/SemaTemplate.cpp.o -c /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/lib/Sema/SemaTemplate.cpp
  /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/lib/Sema/SemaTemplate.cpp:3542:18: error: no matching function for call to 'InstantiateTemplate'
   3542 |       auto BCR = InstantiateTemplate(S, CommonReference,
        |                  ^~~~~~~~~~~~~~~~~~~
  /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/lib/Sema/SemaTemplate.cpp:3215:17: note: candidate function not viable: requires 5 arguments, but 4 were provided
   3215 | static QualType InstantiateTemplate(Sema &S, ElaboratedTypeKeyword Keyword, TemplateName Template,
        |                 ^                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   3216 |                                     ArrayRef<TemplateArgument> Args,
        |                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   3217 |                                     SourceLocation Loc) {
        |                                     ~~~~~~~~~~~~~~~~~~
  /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/lib/Sema/SemaTemplate.cpp:3559:19: error: no matching function for call to 'InstantiateTemplate'
   3559 |     if (auto CT = InstantiateTemplate(S, CommonType, {T1, T2}, TemplateLoc);
        |                   ^~~~~~~~~~~~~~~~~~~
  /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/lib/Sema/SemaTemplate.cpp:3215:17: note: candidate function not viable: requires 5 arguments, but 4 were provided
   3215 | static QualType InstantiateTemplate(Sema &S, ElaboratedTypeKeyword Keyword, TemplateName Template,
        |                 ^                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   3216 |                                     ArrayRef<TemplateArgument> Args,
        |                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   3217 |                                     SourceLocation Loc) {
        |                                     ~~~~~~~~~~~~~~~~~~
  /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/lib/Sema/SemaTemplate.cpp:3762:73: error: too few arguments to function call, expected 6, have 3
   3762 |       return SemaRef.CheckTemplateIdType(HasTypeMember, TemplateLoc, TAs);
        |              ~~~~~~~~~~~~~~~~~~~~~~~~~~~                                ^
  /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/include/clang/Sema/Sema.h:11648:12: note: 'CheckTemplateIdType' declared here
   11648 |   QualType CheckTemplateIdType(ElaboratedTypeKeyword Keyword,
         |            ^                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   11649 |                                TemplateName Template,
         |                                ~~~~~~~~~~~~~~~~~~~~~~
   11650 |                                SourceLocation TemplateLoc,
         |                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~
   11651 |                                TemplateArgumentListInfo &TemplateArgs,
         |                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   11652 |                                Scope *Scope, bool ForNestedNameSpecifier);
         |                                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  3 errors generated.

Comment on lines +13 to +17
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> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really want these to have default arguments instead of passing "" in explicitly? I'm worried this makes it easier to add new subclasses that accidentally forget to set the name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently the names are purely cosmetic in nature except when you have NTTPs which rely on a previous type, so I'm not too worried if someone forgets. I wanted to auto-generate docs with this at some point, but I didn't get to it yet.

return To;
}

// COND-RES(X, Y) be decltype(false ? declval<X(&)()>()() : declval<Y(&)()>()())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is very opaque, please expand on it.

After looking at the body of the function, it makes more sense (only just) but that is not how this should work. The comment on top of the function should be sufficient for the reader to understand what they expect to find in the body. I should not have to look at the body of the function to get the topline comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What exactly do you want? I'm quoting the standard here, just like everywhere else.

Copy link
Member

@huixie90 huixie90 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

libc++ changes LGTM. Leave the final approval to the clang folks

@philnik777
Copy link
Contributor Author

Landing for now, since I don't think there are big concerns anymore and I'd like to avoid merge conflicts. @shafik @AaronBallman if you have any more comments I'm happy to address them post-commit.

@philnik777 philnik777 merged commit 3b9e203 into llvm:main Dec 1, 2025
81 checks passed
@philnik777 philnik777 deleted the builtin_common_reference branch December 1, 2025 14:42
@llvm-ci
Copy link
Collaborator

llvm-ci commented Dec 1, 2025

LLVM Buildbot has detected a new failure on builder clang-m68k-linux-cross running on suse-gary-m68k-cross while building clang,libcxx at step 5 "ninja check 1".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/27/builds/19672

Here is the relevant piece of the build log for the reference
Step 5 (ninja check 1) failure: stage 1 checked (failure)
...
[51/406] Building CXX object tools/clang/tools/extra/unittests/clang-tidy/CMakeFiles/ClangTidyTests.dir/ClangTidyDiagnosticConsumerTest.cpp.o
[52/406] Building CXX object tools/clang/tools/extra/unittests/clang-change-namespace/CMakeFiles/ClangChangeNamespaceTests.dir/ChangeNamespaceTests.cpp.o
[53/406] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/DataflowAnalysisContextTest.cpp.o
[54/406] Building CXX object tools/clang/tools/extra/unittests/clang-tidy/CMakeFiles/ClangTidyTests.dir/TransformerClangTidyCheckTest.cpp.o
[55/406] Building CXX object tools/clang/tools/extra/include-cleaner/unittests/CMakeFiles/ClangIncludeCleanerTests.dir/FindHeadersTest.cpp.o
[56/406] Building CXX object tools/clang/tools/extra/unittests/clang-tidy/CMakeFiles/ClangTidyTests.dir/IncludeInserterTest.cpp.o
[57/406] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/IntervalPartitionTest.cpp.o
[58/406] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/CFGTest.cpp.o
[59/406] Building CXX object tools/clang/tools/extra/unittests/clang-tidy/CMakeFiles/ClangTidyTests.dir/AddConstTest.cpp.o
[60/406] Building CXX object tools/clang/tools/extra/unittests/clang-tidy/CMakeFiles/ClangTidyTests.dir/OverlappingReplacementsTest.cpp.o
FAILED: tools/clang/tools/extra/unittests/clang-tidy/CMakeFiles/ClangTidyTests.dir/OverlappingReplacementsTest.cpp.o 
/usr/bin/c++ -DGTEST_HAS_RTTI=0 -DLLVM_BUILD_STATIC -D_DEBUG -D_GLIBCXX_ASSERTIONS -D_GLIBCXX_USE_CXX11_ABI=1 -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/stage1/tools/clang/tools/extra/unittests/clang-tidy -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang-tools-extra/unittests/clang-tidy -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang-tools-extra/unittests/clang-tidy/../../include-cleaner/include -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/include -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/stage1/tools/clang/include -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/stage1/include -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/llvm/include -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang-tools-extra/clang-tidy -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/third-party/unittest/googletest/include -I/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/third-party/unittest/googlemock/include -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wno-missing-field-initializers -pedantic -Wno-long-long -Wimplicit-fallthrough -Wno-uninitialized -Wno-nonnull -Wno-class-memaccess -Wno-dangling-reference -Wno-redundant-move -Wno-pessimizing-move -Wno-array-bounds -Wno-stringop-overread -Wno-noexcept-type -Wdelete-non-virtual-dtor -Wsuggest-override -Wno-comment -Wno-misleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -fno-common -Woverloaded-virtual -O3 -DNDEBUG -std=c++17  -Wno-variadic-macros -fno-exceptions -funwind-tables -fno-rtti -UNDEBUG -Wno-suggest-override -MD -MT tools/clang/tools/extra/unittests/clang-tidy/CMakeFiles/ClangTidyTests.dir/OverlappingReplacementsTest.cpp.o -MF tools/clang/tools/extra/unittests/clang-tidy/CMakeFiles/ClangTidyTests.dir/OverlappingReplacementsTest.cpp.o.d -o tools/clang/tools/extra/unittests/clang-tidy/CMakeFiles/ClangTidyTests.dir/OverlappingReplacementsTest.cpp.o -c /var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang-tools-extra/unittests/clang-tidy/OverlappingReplacementsTest.cpp
c++: fatal error: Killed signal terminated program cc1plus
compilation terminated.
[61/406] Building CXX object tools/clang/tools/extra/clangd/unittests/CMakeFiles/ClangdTests.dir/ASTTests.cpp.o
[62/406] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/CachedConstAccessorsLatticeTest.cpp.o
[63/406] Building CXX object tools/clang/tools/extra/clangd/unittests/CMakeFiles/ClangdTests.dir/FormatTests.cpp.o
[64/406] Building CXX object tools/clang/tools/extra/clangd/unittests/CMakeFiles/ClangdTests.dir/FeatureModulesTests.cpp.o
[65/406] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/MapLatticeTest.cpp.o
[66/406] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/ChromiumCheckModelTest.cpp.o
[67/406] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/DeterminismTest.cpp.o
[68/406] Building CXX object tools/clang/tools/extra/clangd/unittests/CMakeFiles/ClangdTests.dir/CodeCompletionStringsTests.cpp.o
[69/406] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/DebugSupportTest.cpp.o
[70/406] Building CXX object tools/clang/tools/extra/clangd/unittests/CMakeFiles/ClangdTests.dir/DumpASTTests.cpp.o
[71/406] Building CXX object tools/clang/tools/extra/clangd/unittests/CMakeFiles/ClangdTests.dir/CollectMacrosTests.cpp.o
[72/406] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/MatchSwitchTest.cpp.o
[73/406] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/CFGMatchSwitchTest.cpp.o
[74/406] Building CXX object tools/clang/tools/extra/clangd/unittests/CMakeFiles/ClangdTests.dir/CallHierarchyTests.cpp.o
[75/406] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/ASTOpsTest.cpp.o
[76/406] Building CXX object tools/clang/tools/extra/clangd/unittests/CMakeFiles/ClangdTests.dir/FeatureModulesRegistryTests.cpp.o
[77/406] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/LifetimeSafetyTest.cpp.o
/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/unittests/Analysis/LifetimeSafetyTest.cpp: In instantiation of ‘bool clang::lifetimes::internal::{anonymous}::AreLiveAtImplMatcherP2<Annotation_type, ConfFilter_type>::gmock_Impl<arg_type>::MatchAndExplain(const arg_type&, testing::MatchResultListener*) const [with arg_type = const clang::lifetimes::internal::{anonymous}::OriginsInfo&; Annotation_type = const char*; ConfFilter_type = clang::lifetimes::internal::{anonymous}::LivenessKindFilter]’:
/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/unittests/Analysis/LifetimeSafetyTest.cpp:303:1:   required from here
  303 | MATCHER_P2(AreLiveAtImpl, Annotation, ConfFilter, "") {
/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/unittests/Analysis/LifetimeSafetyTest.cpp:313:19: warning: loop variable ‘<structured bindings>’ creates a copy from type ‘const std::pair<clang::lifetimes::internal::utils::ID<clang::lifetimes::internal::OriginTag>, clang::lifetimes::internal::LivenessKind>’ [-Wrange-loop-construct]
  313 |   for (const auto [OID, ActualConfidence] : ActualLiveSetOpt.value()) {
      |                   ^~~~~~~~~~~~~~~~~~~~~~~
/var/lib/buildbot/workers/suse-gary-m68k-cross/clang-m68k-linux-cross/llvm/clang/unittests/Analysis/LifetimeSafetyTest.cpp:313:19: note: use reference type to prevent copying
  313 |   for (const auto [OID, ActualConfidence] : ActualLiveSetOpt.value()) {
      |                   ^~~~~~~~~~~~~~~~~~~~~~~
      |                   &
[78/406] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/LoggerTest.cpp.o
[79/406] Building CXX object tools/clang/tools/extra/clangd/unittests/CMakeFiles/ClangdTests.dir/ASTSignalsTests.cpp.o
[80/406] Building CXX object tools/clang/tools/extra/clangd/unittests/CMakeFiles/ClangdTests.dir/ClangdLSPServerTests.cpp.o
[81/406] Building CXX object tools/clang/tools/extra/clangd/unittests/CMakeFiles/ClangdTests.dir/ExpectedTypeTest.cpp.o
[82/406] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp.o
[83/406] Building CXX object tools/clang/unittests/CMakeFiles/AllClangUnitTests.dir/Analysis/ExprMutationAnalyzerTest.cpp.o
[84/406] Building CXX object tools/clang/tools/extra/clangd/unittests/CMakeFiles/ClangdTests.dir/FindTargetTests.cpp.o
[85/406] Building CXX object tools/clang/tools/extra/clangd/unittests/CMakeFiles/ClangdTests.dir/BackgroundIndexTests.cpp.o

@nico
Copy link
Contributor

nico commented Dec 2, 2025

This breaks building the libc++ module in the chromium build for me:

../../../../llvm-project/out/gn/bin/clang++ -MD -MF clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/std/module.pcm.d -D__ARM_NEON__=1 -DCR_XCODE_BUILD=17B100 -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE -D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS -D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS -D_LIBCPP_INSTRUMENTED_WITH_ASAN=0 -DCR_LIBCXX_REVISION=91c45fc28459e814889633be985b698c5e8050b4 -DNDEBUG -DNVALGRIND -DDYNAMIC_ANNOTATIONS_ENABLED=0 -D_LIBCPP_BUILDING_LIBRARY -I../.. -Iclang_arm64_for_rust_host_build_tools/gen -fno-delete-null-pointer-checks -fno-strict-overflow -fno-ident -fno-math-errno -fno-strict-aliasing -fstack-protector -fcolor-diagnostics -fmerge-all-constants -fno-sized-deallocation -fcrash-diagnostics-dir=../clang-crashreports -mllvm -instcombine-lower-dbg-declare=0 -mllvm -split-threshold-for-reg-with-hint=0 -ffp-contract=off -fcomplete-member-pointers --target=arm64-apple-macos -mno-outline -Wno-builtin-macro-redefined -D__DATE__= -D__TIME__= -D__TIMESTAMP__= -ffile-compilation-dir=. -no-canonical-prefixes -Xclang -fmodule-file-home-is-cwd -Xclang -fmodules-cache-path=/not_exist_dummy_dir --warning-suppression-mappings=../../build/config/warning_suppression.txt -ftrivial-auto-var-init=pattern -O2 -fno-omit-frame-pointer -g0 -isysroot ../../../../../../../Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.1.sdk -mmacos-version-min=12.0 -fsanitize=array-bounds -fsanitize-trap=array-bounds -fvisibility=hidden -Wheader-hygiene -Wstring-conversion -Wtautological-overlap-compare -fstrict-aliasing -Wundef -fPIC -Wall -Wno-unused-variable -Wno-c++11-narrowing -Wno-unused-but-set-variable -Wunguarded-availability -Wno-missing-field-initializers -Wno-unused-parameter -Wno-psabi -Wloop-analysis -Wno-unneeded-internal-declaration -Wno-cast-function-type -Wno-thread-safety-reference-return -Wno-nontrivial-memcall -Wno-uninitialized-const-pointer -Wexit-time-destructors -Werror -Wno-exit-time-destructors -std=c++20 -Wno-trigraphs -fno-exceptions -fno-rtti -nostdinc++ -isystemgen/third_party/libc++/src/include -isystem../../third_party/libc++abi/src/include -fvisibility-inlines-hidden -Wno-invalid-offsetof -Wenum-compare-conditional -Wno-nullability-completeness -fbuiltin-module-map -fmodule-map-file=gen/third_party/libc++/src/include/module.modulemap -fmodule-map-file=../../../../../../../Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.1.sdk/usr/include/DarwinBasic.modulemap -fmodule-map-file=../../../../../../../Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.1.sdk/usr/include/DarwinFoundation1.modulemap -fmodule-map-file=../../../../../../../Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.1.sdk/usr/include/DarwinFoundation2.modulemap -fmodule-map-file=../../../../../../../Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.1.sdk/usr/include/DarwinFoundation3.modulemap -fmodule-file=_AvailabilityInternal=clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/_AvailabilityInternal/DarwinFoundation1.pcm -fmodule-file=_Builtin_float=clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/_Builtin_float/module.pcm -fmodule-file=_Builtin_inttypes=clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/_Builtin_inttypes/module.pcm -fmodule-file=_Builtin_limits=clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/_Builtin_limits/module.pcm -fmodule-file=_Builtin_stdalign=clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/_Builtin_stdalign/module.pcm -fmodule-file=_Builtin_stdarg=clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/_Builtin_stdarg/module.pcm -fmodule-file=_Builtin_stddef=clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/_Builtin_stddef/module.pcm -fmodule-file=_Builtin_stdint=clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/_Builtin_stdint/module.pcm -fmodule-file=_DarwinFoundation1=clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/_DarwinFoundation1/DarwinFoundation1.pcm -fmodule-file=_DarwinFoundation2=clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/_DarwinFoundation2/DarwinFoundation2.pcm -fmodule-file=_DarwinFoundation3=clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/_DarwinFoundation3/DarwinFoundation3.pcm -fmodule-file=std_core=clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/std_core/module.pcm -fmodule-file=std_ctype_h=clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/std_ctype_h/module.pcm -fmodule-file=std_errno_h=clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/std_errno_h/module.pcm -fmodule-file=std_fenv_h=clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/std_fenv_h/module.pcm -fmodule-file=std_float_h=clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/std_float_h/module.pcm -fmodule-file=std_inttypes_h=clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/std_inttypes_h/module.pcm -fmodule-file=std_math_h=clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/std_math_h/module.pcm -fmodule-file=std_private_mbstate_t=clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/std_private_mbstate_t/module.pcm -fmodule-file=std_string_h=clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/std_string_h/module.pcm -fmodule-file=std_uchar_h=clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/std_uchar_h/module.pcm -fmodule-file=std_wctype_h=clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/std_wctype_h/module.pcm -fmodule-file=xlocale=clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/xlocale/DarwinFoundation3.pcm -fmodule-name=std -c -x c++ -Xclang -emit-module gen/third_party/libc++/src/include/module.modulemap -o clang_arm64_for_rust_host_build_tools/obj/buildtools/third_party/libc++/std/module.pcm
In module 'std_core' imported from gen/third_party/libc++/src/include/__algorithm/comp.h:13:
gen/third_party/libc++/src/include/__type_traits/is_convertible.h:22:101: error: definition of '__nat' must be imported from module 'std_core.type_traits.nat' before it is required
   22 | struct _LIBCPP_NO_SPECIALIZATIONS is_convertible : integral_constant<bool, __is_convertible(_T1, _T2)> {};
      |                                                                                                     ^
gen/third_party/libc++/src/include/__filesystem/path.h:150:37: note: in instantiation of template class 'std::is_convertible<std::__nat, std::input_iterator_tag>' requested here
  150 | template <class _Iter, bool _IsIt = __has_input_iterator_category<_Iter>::value, class = void>
      |                                     ^
gen/third_party/libc++/src/include/__filesystem/path.h:171:48: note: in instantiation of default argument for '__is_pathable_iter<string_type>' required here
  171 |           bool _IsIterT     = !_IsCharIterT && __is_pathable_iter<_Tp>::value>
      |                                                ^~~~~~~~~~~~~~~~~~~~~~~
gen/third_party/libc++/src/include/__filesystem/path.h:384:59: note: in instantiation of default argument for '__is_pathable<string_type, true, false>' required here
  384 |   using _EnableIfPathable _LIBCPP_NODEBUG = __enable_if_t<__is_pathable<_SourceOrIter>::value, _Tp>;
      |                                                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
gen/third_party/libc++/src/include/__filesystem/path.h:412:36: note: in instantiation of template type alias '_EnableIfPathable' requested here
  412 |   template <class _Source, class = _EnableIfPathable<_Source, void> >
      |                                    ^
gen/third_party/libc++/src/include/__filesystem/path.h:413:25: note: in instantiation of default argument for 'path<string_type>' required here
  413 |   _LIBCPP_HIDE_FROM_ABI path(const _Source& __src, format = format::auto_format) {
      |                         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  414 |     _SourceCVT<_Source>::__append_source(__pn_, __src);
      |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  415 |   }
      |   ~
gen/third_party/libc++/src/include/__filesystem/path.h:799:57: note: while substituting deduced template arguments into function template 'path' [with _Source = string_type, $1 = (no value)]
  799 |   _LIBCPP_HIDE_FROM_ABI path root_name() const { return string_type(__root_name()); }
      |                                                         ^
gen/third_party/libc++/src/include/__type_traits/nat.h:20:8: note: definition here is not reachable
   20 | struct __nat {
      |        ^
1 error generated.

The diag is a bit poor in that it leaves out the

template <class _Tp, class _Up>
using __has_iterator_category_convertible_to _LIBCPP_NODEBUG =
    is_convertible<__detected_or_t<__nat, __iterator_category, iterator_traits<_Tp> >, _Up>;

bit which links the path.h note to __nat.

path.h does include __iterator/iterator_traits.h which does include __type_traits/nat.h. So this might be a bug in the impl?

Please take a look and revert for now if it takes a while to fix.

@nico
Copy link
Contributor

nico commented Dec 2, 2025

Here's another snippet that builds fine before this patch but not after it:

#include <type_traits>

template <typename T>
class Foo;

struct Baz {
  Baz() = default;

#if 0
  Baz(Foo<int> foo) {}
#else
  template <typename T>
    requires(std::is_convertible_v<T, Foo<int>>)
  explicit Baz(T foo) {}
#endif
};

struct Quux {
  mutable Baz baz;
};
# libc++ built as described in libcxx/docs/VendorDocumentation.rst,
# but with -DCMAKE_INSTALL_PREFIX=$PWD/install-libcxx added to cmake invocation
# ninja -C build cxx cxxabi unwind
# ninja -C build install-cxx install-cxxabi install-unwind

out/gn/bin/clang -c repro.cc -std=c++20 -nostdinc++ -isystem install-libcxx/include/c++/v1 

Before, this built fine. After, it yields

% out/gn/bin/clang -c repro.cc -std=c++20 -nostdinc++ -isystem install-libcxx/include/c++/v1 
In file included from repro.cc:1:
In file included from install-libcxx/include/c++/v1/type_traits:491:
install-libcxx/include/c++/v1/__type_traits/is_convertible.h:26:96: error: implicit instantiation of undefined template 'Foo<int>'
   26 | _LIBCPP_NO_SPECIALIZATIONS inline constexpr bool is_convertible_v = __is_convertible(_From, _To);
      |                                                                                                ^
repro.cc:13:19: note: in instantiation of variable template specialization 'std::is_convertible_v<Baz, Foo<int>>' requested here
   13 |     requires(std::is_convertible_v<T, Foo<int>>)
      |                   ^
repro.cc:13:14: note: while substituting template arguments into constraint expression here
   13 |     requires(std::is_convertible_v<T, Foo<int>>)
      |              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
repro.cc:6:34: note: while checking constraint satisfaction for template 'Baz<Baz>' required here
    6 | struct __attribute__((lockable)) Baz {
      |                                  ^~~
repro.cc:6:34: note: while substituting deduced template arguments into function template 'Baz' [with T = Baz]
repro.cc:18:8: note: while declaring the implicit copy constructor for 'Quux'
   18 | struct Quux {
      |        ^
repro.cc:4:7: note: template is declared here
    4 | class Foo;
      |       ^
1 error generated.

The snippet is a bit weird! The motivation is that it wants to say Baz(Foo<int> foo) {}, but Foo<T> is forward-declared, so it uses that template hack, and everyone who wants to call that ctor has to make sure to include a definition of Foo<>.

This worked before this change, but not after. Was that behavior change intentional? (The repro only works if the mutable is present somehow, which suggests that this isn't intentional.)

Maybe this is also what's causing the libc++-built-as-module diag mentioned above?

nico added a commit that referenced this pull request Dec 3, 2025
This reverts commit 3b9e203.
Causes not-yet-understood semantic differences, see commits
on #121199.
@nico
Copy link
Contributor

nico commented Dec 3, 2025

I reverted this in e05fffb for now, per Discord.

augusto2112 pushed a commit to augusto2112/llvm-project that referenced this pull request Dec 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants