[libcxx-commits] [clang] [libcxx] [Clang] Add __common_type builtin (PR #99473)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Sat Sep 7 01:28:06 PDT 2024


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/99473

>From d6903daf0da6979822b8981ea3641455ff6d06f8 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Tue, 16 Jul 2024 14:48:10 +0200
Subject: [PATCH 1/7] [Clang] Add __common_type builtin

---
 clang/include/clang/AST/ASTContext.h          |  11 ++
 clang/include/clang/AST/DeclID.h              |   5 +-
 clang/include/clang/Basic/Builtins.h          |   5 +-
 clang/include/clang/Sema/Sema.h               |   4 +
 clang/lib/AST/ASTContext.cpp                  |   7 +
 clang/lib/AST/ASTImporter.cpp                 |   3 +
 clang/lib/AST/DeclTemplate.cpp                |  53 ++++++
 clang/lib/Lex/PPMacroExpansion.cpp            |   1 +
 clang/lib/Sema/SemaChecking.cpp               |   8 +
 clang/lib/Sema/SemaLookup.cpp                 |   4 +
 clang/lib/Sema/SemaTemplate.cpp               | 160 +++++++++++++++++-
 clang/lib/Serialization/ASTReader.cpp         |   3 +
 clang/lib/Serialization/ASTWriter.cpp         |   2 +
 clang/test/SemaCXX/type-trait-common-type.cpp | 126 ++++++++++++++
 libcxx/include/__type_traits/common_type.h    |  16 +-
 libcxx/include/module.modulemap               |   2 +
 16 files changed, 404 insertions(+), 6 deletions(-)
 create mode 100644 clang/test/SemaCXX/type-trait-common-type.cpp

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 608bd90fcc3ff9..d02e742297898c 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -399,6 +399,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// The identifier '__type_pack_element'.
   mutable IdentifierInfo *TypePackElementName = nullptr;
 
+  /// The identifier '__common_type'.
+  mutable IdentifierInfo *CommonTypeName = nullptr;
+
   QualType ObjCConstantStringType;
   mutable RecordDecl *CFConstantStringTagDecl = nullptr;
   mutable TypedefDecl *CFConstantStringTypeDecl = nullptr;
@@ -606,6 +609,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
   mutable ExternCContextDecl *ExternCContext = nullptr;
   mutable BuiltinTemplateDecl *MakeIntegerSeqDecl = nullptr;
   mutable BuiltinTemplateDecl *TypePackElementDecl = nullptr;
+  mutable BuiltinTemplateDecl *CommonTypeDecl = nullptr;
 
   /// The associated SourceManager object.
   SourceManager &SourceMgr;
@@ -1107,6 +1111,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
   ExternCContextDecl *getExternCContextDecl() const;
   BuiltinTemplateDecl *getMakeIntegerSeqDecl() const;
   BuiltinTemplateDecl *getTypePackElementDecl() const;
+  BuiltinTemplateDecl *getCommonTypeDecl() const;
 
   // Builtin Types.
   CanQualType VoidTy;
@@ -1984,6 +1989,12 @@ class ASTContext : public RefCountedBase<ASTContext> {
     return TypePackElementName;
   }
 
+  IdentifierInfo *getCommonTypeName() const {
+    if (!CommonTypeName)
+      CommonTypeName = &Idents.get("__common_type");
+    return CommonTypeName;
+  }
+
   /// Retrieve the Objective-C "instancetype" type, if already known;
   /// otherwise, returns a NULL type;
   QualType getObjCInstanceType() {
diff --git a/clang/include/clang/AST/DeclID.h b/clang/include/clang/AST/DeclID.h
index e5e27389fac60d..875e9a72b39512 100644
--- a/clang/include/clang/AST/DeclID.h
+++ b/clang/include/clang/AST/DeclID.h
@@ -84,13 +84,16 @@ enum PredefinedDeclIDs {
 
   /// The internal '__type_pack_element' template.
   PREDEF_DECL_TYPE_PACK_ELEMENT_ID = 17,
+
+  /// The internal '__common_type' template.
+  PREDEF_DECL_COMMON_TYPE_ID = 18,
 };
 
 /// The number of declaration IDs that are predefined.
 ///
 /// For more information about predefined declarations, see the
 /// \c PredefinedDeclIDs type and the PREDEF_DECL_*_ID constants.
-const unsigned int NUM_PREDEF_DECL_IDS = 18;
+const unsigned int NUM_PREDEF_DECL_IDS = 19;
 
 /// GlobalDeclID means DeclID in the current ASTContext and LocalDeclID means
 /// DeclID specific to a certain ModuleFile. Specially, in ASTWriter, the
diff --git a/clang/include/clang/Basic/Builtins.h b/clang/include/clang/Basic/Builtins.h
index e85ec5b2dca14e..4353b72f713838 100644
--- a/clang/include/clang/Basic/Builtins.h
+++ b/clang/include/clang/Basic/Builtins.h
@@ -309,7 +309,10 @@ enum BuiltinTemplateKind : int {
   BTK__make_integer_seq,
 
   /// This names the __type_pack_element BuiltinTemplateDecl.
-  BTK__type_pack_element
+  BTK__type_pack_element,
+
+  /// This names the __common_type BuiltinTemplateDecl.
+  BTK__common_type,
 };
 
 } // end namespace clang
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 3cb1aa935fe461..5c7945c4c5c583 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2282,6 +2282,10 @@ class Sema final : public SemaBase {
   /// Check to see if a given expression could have '.c_str()' called on it.
   bool hasCStrMethod(const Expr *E);
 
+  // Check whether a type member 'Type::Name' exists, and if yes, return the
+  // type. If there is no type, the QualType is null
+  QualType getTypeMember(StringRef Name, QualType Type);
+
   /// Diagnose pointers that are always non-null.
   /// \param E the expression containing the pointer
   /// \param NullKind NPCK_NotNull if E is a cast to bool, otherwise, E is
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index a8e599f7ebe049..b8b4f426ff96c7 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -1170,6 +1170,13 @@ ASTContext::getTypePackElementDecl() const {
   return TypePackElementDecl;
 }
 
+BuiltinTemplateDecl *ASTContext::getCommonTypeDecl() const {
+  if (!CommonTypeDecl)
+    CommonTypeDecl =
+        buildBuiltinTemplateDecl(BTK__common_type, getCommonTypeName());
+  return CommonTypeDecl;
+}
+
 RecordDecl *ASTContext::buildImplicitRecord(StringRef Name,
                                             RecordDecl::TagKind TK) const {
   SourceLocation Loc;
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 0c27f6f5df2da3..e4515e19a49a3d 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -5408,6 +5408,9 @@ ExpectedDecl ASTNodeImporter::VisitBuiltinTemplateDecl(BuiltinTemplateDecl *D) {
   case BuiltinTemplateKind::BTK__type_pack_element:
     ToD = Importer.getToContext().getTypePackElementDecl();
     break;
+  case BuiltinTemplateKind::BTK__common_type:
+    ToD = Importer.getToContext().getCommonTypeDecl();
+    break;
   }
   assert(ToD && "BuiltinTemplateDecl of unsupported kind!");
   Importer.MapImported(D, ToD);
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 722c7fcf0b0df7..d290c91fb82902 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1599,6 +1599,57 @@ createTypePackElementParameterList(const ASTContext &C, DeclContext *DC) {
                                        nullptr);
 }
 
+static TemplateParameterList *createCommonTypeList(const ASTContext &C,
+                                                   DeclContext *DC) {
+  // class... Args
+  auto *Args = TemplateTypeParmDecl::Create(
+      C, DC, {}, {}, /*Depth=*/1, /*Position=*/0, /*Id=*/nullptr,
+      /*Typename=*/false, /*ParameterPack=*/true);
+  Args->setImplicit();
+
+  // <class... Args>
+  auto *BaseTemplateList =
+      TemplateParameterList::Create(C, {}, {}, Args, {}, nullptr);
+
+  // template <class... Args> class BaseTemplate
+  auto *BaseTemplate = TemplateTemplateParmDecl::Create(
+      C, DC, {}, /*Depth=*/0, /*Position=*/0, /*ParameterPack=*/false, {},
+      /*Typename=*/false, BaseTemplateList);
+
+  // class TypeMember
+  auto *TypeMember = TemplateTypeParmDecl::Create(
+      C, DC, {}, {}, /*Depth=*/1, /*Position=*/0, /*Id=*/nullptr,
+      /*Typename=*/false, /*ParameterPack=*/false);
+
+  // <class TypeMember>
+  auto *HasTypeMemberList =
+      TemplateParameterList::Create(C, {}, {}, TypeMember, {}, nullptr);
+
+  // template <class TypeMember> class HasTypeMember
+  auto *HasTypeMember =
+      TemplateTemplateParmDecl::Create(C, DC, {}, /*Depth=*/0, /*Position=*/1,
+                                       /*ParameterPack=*/false, {},
+                                       /*Typename=*/false, HasTypeMemberList);
+
+  // class HasNoTypeMember
+  auto *HasNoTypeMember = TemplateTypeParmDecl::Create(
+      C, DC, {}, {}, /*Depth=*/0, /*Position=*/2, /*Id=*/nullptr,
+      /*Typename=*/false, /*ParameterPack=*/false);
+
+  // class... Ts
+  auto *Ts = TemplateTypeParmDecl::Create(
+      C, DC, {}, {}, /*Depth=*/0, /*Position=*/3,
+      /*Id=*/nullptr, /*Typename=*/false, /*ParameterPack=*/true);
+  Ts->setImplicit();
+
+  // template <template <class... Args> class BaseTemplate,
+  //   template <class TypeMember> class HasTypeMember, class HasNoTypeMember,
+  //   class... Ts>
+  return TemplateParameterList::Create(
+      C, {}, {}, {BaseTemplate, HasTypeMember, HasNoTypeMember, Ts}, {},
+      nullptr);
+}
+
 static TemplateParameterList *createBuiltinTemplateParameterList(
     const ASTContext &C, DeclContext *DC, BuiltinTemplateKind BTK) {
   switch (BTK) {
@@ -1606,6 +1657,8 @@ static TemplateParameterList *createBuiltinTemplateParameterList(
     return createMakeIntegerSeqParameterList(C, DC);
   case BTK__type_pack_element:
     return createTypePackElementParameterList(C, DC);
+  case BTK__common_type:
+    return createCommonTypeList(C, DC);
   }
 
   llvm_unreachable("unhandled BuiltinTemplateKind!");
diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp
index 3913ff08c2eb55..4a6dd13229fe02 100644
--- a/clang/lib/Lex/PPMacroExpansion.cpp
+++ b/clang/lib/Lex/PPMacroExpansion.cpp
@@ -1822,6 +1822,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
               // Report builtin templates as being builtins.
               .Case("__make_integer_seq", getLangOpts().CPlusPlus)
               .Case("__type_pack_element", getLangOpts().CPlusPlus)
+              .Case("__common_type", getLangOpts().CPlusPlus)
               // Likewise for some builtin preprocessor macros.
               // FIXME: This is inconsistent; we usually suggest detecting
               // builtin macros via #ifdef. Don't add more cases here.
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 45b9bbb23dbf7b..10c821fd367e76 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -6844,6 +6844,14 @@ CXXRecordMembersNamed(StringRef Name, Sema &S, QualType Ty) {
   return Results;
 }
 
+QualType Sema::getTypeMember(StringRef Name, QualType Type) {
+  auto Results = CXXRecordMembersNamed<TypeDecl>(Name, *this, Type);
+  assert(Results.size() <= 1);
+  if (Results.empty())
+    return {};
+  return Context.getTypeDeclType(*Results.begin());
+}
+
 /// Check if we could call '.c_str()' on an object.
 ///
 /// FIXME: This returns the wrong results in some cases (if cv-qualifiers don't
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index 7a6a64529f52ec..96551c5106b1bb 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -932,6 +932,10 @@ bool Sema::LookupBuiltin(LookupResult &R) {
           R.addDecl(getASTContext().getTypePackElementDecl());
           return true;
         }
+        if (II == getASTContext().getCommonTypeName()) {
+          R.addDecl(getASTContext().getCommonTypeDecl());
+          return true;
+        }
       }
 
       // Check if this is an OpenCL Builtin, and if so, insert its overloads.
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 9d96201625389f..bf3d35f0c1abbb 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -3058,6 +3058,141 @@ void Sema::NoteAllFoundTemplates(TemplateName Name) {
   }
 }
 
+static std::optional<QualType> commonTypeImpl(Sema &S,
+                                              TemplateName BaseTemplate,
+                                              SourceLocation TemplateLoc,
+                                              ArrayRef<TemplateArgument> Ts) {
+  auto lookUpCommonType = [&](TemplateArgument T1,
+                              TemplateArgument T2) -> std::optional<QualType> {
+    // Don't bother looking for other specializations if both types are
+    // builtins - users aren't allowed to specialize for them
+    if (T1.getAsType()->isBuiltinType() && T2.getAsType()->isBuiltinType())
+      return commonTypeImpl(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())));
+    QualType BaseTemplateInst =
+        S.CheckTemplateIdType(BaseTemplate, TemplateLoc, Args);
+    if (S.RequireCompleteType(TemplateLoc, BaseTemplateInst,
+                              diag::err_incomplete_type))
+      return std::nullopt;
+    if (QualType Type = S.getTypeMember("type", BaseTemplateInst);
+        !Type.isNull()) {
+      return Type;
+    }
+    return std::nullopt;
+  };
+
+  // Note A: For the common_type trait applied to a template parameter pack T of
+  // types, the member type shall be either defined or not present as follows:
+  switch (Ts.size()) {
+
+  // If sizeof...(T) is zero, there shall be no member type.
+  case 0:
+    return std::nullopt;
+
+  // If sizeof...(T) is one, let T0 denote the sole type constituting the
+  // pack T. The member typedef-name type shall denote the same type, if any, as
+  // common_type_t<T0, T0>; otherwise there shall be no member type.
+  case 1:
+    return lookUpCommonType(Ts[0], Ts[0]);
+
+  // If sizeof...(T) is two, let the first and second types constituting T be
+  // denoted by T1 and T2, respectively, and let D1 and D2 denote the same types
+  // as decay_t<T1> and decay_t<T2>, respectively.
+  case 2: {
+    QualType T1 = Ts[0].getAsType();
+    QualType T2 = Ts[1].getAsType();
+    QualType D1 = S.BuiltinDecay(T1, {});
+    QualType D2 = S.BuiltinDecay(T2, {});
+
+    // If is_same_v<T1, D1> is false or is_same_v<T2, D2> is false, let C denote
+    // the same type, if any, as common_type_t<D1, D2>.
+    if (!S.Context.hasSameType(T1, D1) || !S.Context.hasSameType(T2, D2)) {
+      return lookUpCommonType(D1, D2);
+    }
+
+    // Otherwise, if decay_t<decltype(false ? declval<D1>() : declval<D2>())>
+    // denotes a valid type, let C denote that type.
+    {
+      auto CheckConditionalOperands =
+          [&](bool ConstRefQual) -> std::optional<QualType> {
+        EnterExpressionEvaluationContext UnevaluatedContext(
+            S, Sema::ExpressionEvaluationContext::Unevaluated);
+        Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/true);
+        Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl());
+
+        // false
+        OpaqueValueExpr CondExpr({}, S.Context.BoolTy,
+                                 ExprValueKind::VK_PRValue);
+        ExprResult Cond = &CondExpr;
+
+        auto EVK =
+            ConstRefQual ? ExprValueKind::VK_LValue : ExprValueKind::VK_PRValue;
+        if (ConstRefQual) {
+          D1.addConst();
+          D2.addConst();
+        }
+
+        // declval<D1>()
+        OpaqueValueExpr LHSExpr(TemplateLoc, D1, EVK);
+        ExprResult LHS = &LHSExpr;
+
+        // declval<D2>()
+        OpaqueValueExpr RHSExpr(TemplateLoc, D2, EVK);
+        ExprResult RHS = &RHSExpr;
+
+        ExprValueKind VK = VK_PRValue;
+        ExprObjectKind OK = OK_Ordinary;
+
+        // decltype(false ? declval<D1>() : declval<D2>())
+        QualType Result =
+            S.CheckConditionalOperands(Cond, LHS, RHS, VK, OK, TemplateLoc);
+
+        if (Result.isNull() || SFINAE.hasErrorOccurred())
+          return std::nullopt;
+
+        // decay_t<decltype(false ? declval<D1>() : declval<D2>())>
+        return S.BuiltinDecay(Result, TemplateLoc);
+      };
+
+      if (auto Res = CheckConditionalOperands(false))
+        return Res;
+
+      // Let:
+      // CREF(A) be add_lvalue_reference_t<const remove_reference_t<A>>,
+      // COND-RES(X, Y) be
+      //   decltype(false ? declval<X(&)()>()() : declval<Y(&)()>()()).
+
+      // C++20 only
+      // Otherwise, if COND-RES(CREF(D1), CREF(D2)) denotes a type, let C denote
+      // the type decay_t<COND-RES(CREF(D1), CREF(D2))>.
+      if (!S.Context.getLangOpts().CPlusPlus20)
+        return std::nullopt;
+      return CheckConditionalOperands(true);
+    }
+  }
+
+  // If sizeof...(T) is greater than two, let T1, T2, and R, respectively,
+  // denote the first, second, and (pack of) remaining types constituting T. Let
+  // C denote the same type, if any, as common_type_t<T1, T2>. If there is such
+  // a type C, the member typedef-name type shall denote the same type, if any,
+  // as common_type_t<C, R...>. Otherwise, there shall be no member type.
+  default: {
+    std::optional<QualType> Result = Ts[Ts.size() - 1].getAsType();
+    for (size_t i = Ts.size() - 1; i != 0; --i) {
+      Result = lookUpCommonType(Ts[i - 1].getAsType(), *Result);
+      if (!Result)
+        return std::nullopt;
+    }
+    return Result;
+  }
+  }
+}
+
 static QualType
 checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD,
                            ArrayRef<TemplateArgument> Converted,
@@ -3114,7 +3249,7 @@ checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD,
                                        TemplateLoc, SyntheticTemplateArgs);
   }
 
-  case BTK__type_pack_element:
+  case BTK__type_pack_element: {
     // Specializations of
     //    __type_pack_element<Index, T_1, ..., T_N>
     // are treated like T_Index.
@@ -3140,6 +3275,29 @@ checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD,
     int64_t N = Index.getExtValue();
     return Ts.getPackAsArray()[N].getAsType();
   }
+
+  case BTK__common_type: {
+    assert(Converted.size() == 4);
+    if (Converted[0].isDependent() || Converted[1].isDependent() ||
+        Converted[2].isDependent() || Converted[3].isDependent())
+      return Context.getCanonicalTemplateSpecializationType(TemplateName(BTD),
+                                                            Converted);
+
+    TemplateName BaseTemplate = Converted[0].getAsTemplate();
+    TemplateName HasTypeMember = Converted[1].getAsTemplate();
+    QualType HasNoTypeMember = Converted[2].getAsType();
+    ArrayRef<TemplateArgument> Ts = Converted[3].getPackAsArray();
+    if (auto CT = commonTypeImpl(SemaRef, BaseTemplate, TemplateLoc, Ts)) {
+      TemplateArgumentListInfo TAs;
+      TAs.addArgument(TemplateArgumentLoc(
+          TemplateArgument(*CT), SemaRef.Context.getTrivialTypeSourceInfo(
+                                     *CT, TemplateArgs[1].getLocation())));
+
+      return SemaRef.CheckTemplateIdType(HasTypeMember, TemplateLoc, TAs);
+    }
+    return HasNoTypeMember;
+  }
+  }
   llvm_unreachable("unexpected BuiltinTemplateDecl!");
 }
 
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index afdeccaf93a9df..40ebc2c5d020f3 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -7789,6 +7789,9 @@ static Decl *getPredefinedDecl(ASTContext &Context, PredefinedDeclIDs ID) {
 
   case PREDEF_DECL_TYPE_PACK_ELEMENT_ID:
     return Context.getTypePackElementDecl();
+
+  case PREDEF_DECL_COMMON_TYPE_ID:
+    return Context.getCommonTypeDecl();
   }
   llvm_unreachable("PredefinedDeclIDs unknown enum value");
 }
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index c78d8943d6d92e..b17eeed357aac1 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -5036,6 +5036,8 @@ void ASTWriter::PrepareWritingSpecialDecls(Sema &SemaRef) {
                      PREDEF_DECL_CF_CONSTANT_STRING_TAG_ID);
   RegisterPredefDecl(Context.TypePackElementDecl,
                      PREDEF_DECL_TYPE_PACK_ELEMENT_ID);
+  RegisterPredefDecl(Context.CommonTypeDecl,
+                     PREDEF_DECL_COMMON_TYPE_ID);
 
   const TranslationUnitDecl *TU = Context.getTranslationUnitDecl();
 
diff --git a/clang/test/SemaCXX/type-trait-common-type.cpp b/clang/test/SemaCXX/type-trait-common-type.cpp
new file mode 100644
index 00000000000000..44207f066a3336
--- /dev/null
+++ b/clang/test/SemaCXX/type-trait-common-type.cpp
@@ -0,0 +1,126 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -verify -std=c++17 %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -verify -std=c++20 %s
+
+#if !__has_builtin(__common_type)
+#  error
+#endif
+
+// expected-note@*:* {{template declaration from hidden source: template <class, template <class> class, class>}}
+
+void test() {
+  __common_type<> a; // expected-error {{too few template arguments for template '__common_type'}}
+}
+
+struct empty_type {};
+
+template <class T>
+struct type_identity {
+  using type = T;
+};
+
+template <class...>
+struct common_type;
+
+template <class... Args>
+using common_type_base = __common_type<common_type, type_identity, empty_type, Args...>; // expected-error {{incomplete type 'common_type<Incomplete, Incomplete>' where a complete type is required}}
+
+template <class... Args>
+struct common_type : common_type_base<Args...> {};
+
+struct Incomplete;
+
+template<>
+struct common_type<Incomplete, Incomplete>; // expected-note {{forward declaration}}
+
+static_assert(__is_same(common_type_base<>, empty_type));
+static_assert(__is_same(common_type_base<Incomplete>, empty_type)); // expected-note {{requested here}}
+static_assert(__is_same(common_type_base<char>, type_identity<char>));
+static_assert(__is_same(common_type_base<int>, type_identity<int>));
+static_assert(__is_same(common_type_base<const int>, type_identity<int>));
+static_assert(__is_same(common_type_base<volatile int>, type_identity<int>));
+static_assert(__is_same(common_type_base<const volatile int>, type_identity<int>));
+static_assert(__is_same(common_type_base<int[]>, type_identity<int*>));
+static_assert(__is_same(common_type_base<const int[]>, type_identity<const int*>));
+static_assert(__is_same(common_type_base<void(&)()>, type_identity<void(*)()>));
+static_assert(__is_same(common_type_base<int[], int[]>, type_identity<int*>));
+
+static_assert(__is_same(common_type_base<int, int>, type_identity<int>));
+static_assert(__is_same(common_type_base<int, long>, type_identity<long>));
+static_assert(__is_same(common_type_base<long, int>, type_identity<long>));
+static_assert(__is_same(common_type_base<long, long>, type_identity<long>));
+
+static_assert(__is_same(common_type_base<const int, long>, type_identity<long>));
+static_assert(__is_same(common_type_base<const volatile int, long>, type_identity<long>));
+static_assert(__is_same(common_type_base<int, const long>, type_identity<long>));
+static_assert(__is_same(common_type_base<int, const volatile long>, type_identity<long>));
+
+static_assert(__is_same(common_type_base<int*, long*>, empty_type));
+
+static_assert(__is_same(common_type_base<int, long, float>, type_identity<float>));
+static_assert(__is_same(common_type_base<unsigned, char, long>, type_identity<long>));
+static_assert(__is_same(common_type_base<long long, long long, long>, type_identity<long long>));
+
+struct NoCommonType {};
+
+template <>
+struct common_type<NoCommonType, NoCommonType> {};
+
+struct CommonTypeInt {};
+
+template <>
+struct common_type<CommonTypeInt, CommonTypeInt> {
+  using type = int;
+};
+
+template <>
+struct common_type<CommonTypeInt, int> {
+  using type = int;
+};
+
+template <>
+struct common_type<int, CommonTypeInt> {
+  using type = int;
+};
+
+static_assert(__is_same(common_type_base<NoCommonType>, empty_type));
+static_assert(__is_same(common_type_base<CommonTypeInt>, type_identity<int>));
+static_assert(__is_same(common_type_base<NoCommonType, NoCommonType, NoCommonType>, empty_type));
+static_assert(__is_same(common_type_base<CommonTypeInt, CommonTypeInt, CommonTypeInt>, type_identity<int>));
+static_assert(__is_same(common_type_base<CommonTypeInt&, CommonTypeInt&&>, type_identity<int>));
+
+static_assert(__is_same(common_type_base<void, int>, empty_type));
+static_assert(__is_same(common_type_base<void, void>, type_identity<void>));
+static_assert(__is_same(common_type_base<const void, void>, type_identity<void>));
+static_assert(__is_same(common_type_base<void, const void>, type_identity<void>));
+
+template <class T>
+struct ConvertibleTo {
+  operator T();
+};
+
+static_assert(__is_same(common_type_base<ConvertibleTo<int>>, type_identity<ConvertibleTo<int>>));
+static_assert(__is_same(common_type_base<ConvertibleTo<int>, int>, type_identity<int>));
+static_assert(__is_same(common_type_base<ConvertibleTo<int&>, ConvertibleTo<long&>>, type_identity<long>));
+
+struct ConvertibleToB;
+
+struct ConvertibleToA {
+  operator ConvertibleToB();
+};
+
+struct ConvertibleToB {
+  operator ConvertibleToA();
+};
+
+static_assert(__is_same(common_type_base<ConvertibleToA, ConvertibleToB>, empty_type));
+
+struct const_ref_convertible {
+  operator int&() const &;
+  operator int&() && = delete;
+};
+
+#if __cplusplus >= 202002L
+static_assert(__is_same(common_type_base<const_ref_convertible, int &>, type_identity<int>));
+#else
+static_assert(__is_same(common_type_base<const_ref_convertible, int &>, empty_type));
+#endif
diff --git a/libcxx/include/__type_traits/common_type.h b/libcxx/include/__type_traits/common_type.h
index f6bd9ed71b7a47..103b58e42aa8a1 100644
--- a/libcxx/include/__type_traits/common_type.h
+++ b/libcxx/include/__type_traits/common_type.h
@@ -14,8 +14,10 @@
 #include <__type_traits/decay.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/remove_cvref.h>
+#include <__type_traits/type_identity.h>
 #include <__type_traits/void_t.h>
 #include <__utility/declval.h>
+#include <__utility/empty.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
@@ -23,7 +25,13 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if _LIBCPP_STD_VER >= 20
+#if __has_builtin(__common_type)
+
+template <class... _Args>
+struct common_type : __common_type<common_type, __type_identity, __empty, _Args...> {};
+
+#else
+#  if _LIBCPP_STD_VER >= 20
 // Let COND_RES(X, Y) be:
 template <class _Tp, class _Up>
 using __cond_type = decltype(false ? std::declval<_Tp>() : std::declval<_Up>());
@@ -39,10 +47,10 @@ struct __common_type3<_Tp, _Up, void_t<__cond_type<const _Tp&, const _Up&>>> {
 
 template <class _Tp, class _Up, class = void>
 struct __common_type2_imp : __common_type3<_Tp, _Up> {};
-#else
+#  else
 template <class _Tp, class _Up, class = void>
 struct __common_type2_imp {};
-#endif
+#  endif
 
 // sub-bullet 3 - "if decay_t<decltype(false ? declval<D1>() : declval<D2>())> ..."
 template <class _Tp, class _Up>
@@ -92,6 +100,8 @@ template <class _Tp, class _Up, class _Vp, class... _Rest>
 struct _LIBCPP_TEMPLATE_VIS common_type<_Tp, _Up, _Vp, _Rest...>
     : __common_type_impl<__common_types<_Tp, _Up, _Vp, _Rest...> > {};
 
+#endif
+
 #if _LIBCPP_STD_VER >= 14
 template <class... _Tp>
 using common_type_t = typename common_type<_Tp...>::type;
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 7608aef3f3a439..4c15c514478f69 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1880,7 +1880,9 @@ module std_private_type_traits_common_reference                          [system
 }
 module std_private_type_traits_common_type                               [system] {
   header "__type_traits/common_type.h"
+  export std_private_type_traits_type_identity
   export std_private_utility_declval
+  export std_private_utility_empty
 }
 module std_private_type_traits_conditional                               [system] { header "__type_traits/conditional.h" }
 module std_private_type_traits_conjunction                               [system] { header "__type_traits/conjunction.h" }

>From a912a763e1e4d8df4d472e64eff3ac800ab04068 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 25 Jul 2024 12:04:28 +0200
Subject: [PATCH 2/7] Address comment

---
 clang/include/clang/Sema/Sema.h               |  2 +-
 clang/lib/AST/DeclTemplate.cpp                | 39 +++++++-------
 clang/lib/Sema/SemaChecking.cpp               |  2 +-
 clang/lib/Sema/SemaLookup.cpp                 |  3 +-
 clang/lib/Sema/SemaTemplate.cpp               | 54 +++++++++----------
 clang/test/SemaCXX/type-trait-common-type.cpp | 46 +++++++++++++++-
 libcxx/include/__type_traits/common_type.h    |  9 +++-
 7 files changed, 100 insertions(+), 55 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 48c2fb86e7d9ba..9ed20e71d61a61 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2284,7 +2284,7 @@ class Sema final : public SemaBase {
 
   // Check whether a type member 'Type::Name' exists, and if yes, return the
   // type. If there is no type, the QualType is null
-  QualType getTypeMember(StringRef Name, QualType Type);
+  QualType getTypeMember(QualType Type, StringRef Name);
 
   /// Diagnose pointers that are always non-null.
   /// \param E the expression containing the pointer
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index c40ff9b18ccff8..c119d242bc292e 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1611,34 +1611,37 @@ createTypePackElementParameterList(const ASTContext &C, DeclContext *DC) {
 static TemplateParameterList *createCommonTypeList(const ASTContext &C,
                                                    DeclContext *DC) {
   // class... Args
-  auto *Args = TemplateTypeParmDecl::Create(
-      C, DC, {}, {}, /*Depth=*/1, /*Position=*/0, /*Id=*/nullptr,
-      /*Typename=*/false, /*ParameterPack=*/true);
-  Args->setImplicit();
+  auto *Args =
+      TemplateTypeParmDecl::Create(C, DC, SourceLocation(), SourceLocation(),
+                                   /*Depth=*/1, /*Position=*/0, /*Id=*/nullptr,
+                                   /*Typename=*/false, /*ParameterPack=*/true);
 
   // <class... Args>
-  auto *BaseTemplateList =
-      TemplateParameterList::Create(C, {}, {}, Args, {}, nullptr);
+  auto *BaseTemplateList = TemplateParameterList::Create(
+      C, SourceLocation(), SourceLocation(), Args, SourceLocation(), nullptr);
 
   // template <class... Args> class BaseTemplate
   auto *BaseTemplate = TemplateTemplateParmDecl::Create(
-      C, DC, {}, /*Depth=*/0, /*Position=*/0, /*ParameterPack=*/false, {},
+      C, DC, SourceLocation(), /*Depth=*/0, /*Position=*/0,
+      /*ParameterPack=*/false, /*Id=*/nullptr,
       /*Typename=*/false, BaseTemplateList);
 
   // class TypeMember
-  auto *TypeMember = TemplateTypeParmDecl::Create(
-      C, DC, {}, {}, /*Depth=*/1, /*Position=*/0, /*Id=*/nullptr,
-      /*Typename=*/false, /*ParameterPack=*/false);
+  auto *TypeMember =
+      TemplateTypeParmDecl::Create(C, DC, SourceLocation(), SourceLocation(),
+                                   /*Depth=*/1, /*Position=*/0, /*Id=*/nullptr,
+                                   /*Typename=*/false, /*ParameterPack=*/false);
 
   // <class TypeMember>
   auto *HasTypeMemberList =
-      TemplateParameterList::Create(C, {}, {}, TypeMember, {}, nullptr);
+      TemplateParameterList::Create(C, SourceLocation(), SourceLocation(),
+                                    TypeMember, SourceLocation(), nullptr);
 
   // template <class TypeMember> class HasTypeMember
-  auto *HasTypeMember =
-      TemplateTemplateParmDecl::Create(C, DC, {}, /*Depth=*/0, /*Position=*/1,
-                                       /*ParameterPack=*/false, {},
-                                       /*Typename=*/false, HasTypeMemberList);
+  auto *HasTypeMember = TemplateTemplateParmDecl::Create(
+      C, DC, SourceLocation(), /*Depth=*/0, /*Position=*/1,
+      /*ParameterPack=*/false, /*Id=*/nullptr,
+      /*Typename=*/false, HasTypeMemberList);
 
   // class HasNoTypeMember
   auto *HasNoTypeMember = TemplateTypeParmDecl::Create(
@@ -1647,15 +1650,15 @@ static TemplateParameterList *createCommonTypeList(const ASTContext &C,
 
   // class... Ts
   auto *Ts = TemplateTypeParmDecl::Create(
-      C, DC, {}, {}, /*Depth=*/0, /*Position=*/3,
+      C, DC, SourceLocation(), SourceLocation(), /*Depth=*/0, /*Position=*/3,
       /*Id=*/nullptr, /*Typename=*/false, /*ParameterPack=*/true);
-  Ts->setImplicit();
 
   // template <template <class... Args> class BaseTemplate,
   //   template <class TypeMember> class HasTypeMember, class HasNoTypeMember,
   //   class... Ts>
   return TemplateParameterList::Create(
-      C, {}, {}, {BaseTemplate, HasTypeMember, HasNoTypeMember, Ts}, {},
+      C, SourceLocation(), SourceLocation(),
+      {BaseTemplate, HasTypeMember, HasNoTypeMember, Ts}, SourceLocation(),
       nullptr);
 }
 
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 33e1442018f361..76135cfd5729f8 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -6848,7 +6848,7 @@ CXXRecordMembersNamed(StringRef Name, Sema &S, QualType Ty) {
   return Results;
 }
 
-QualType Sema::getTypeMember(StringRef Name, QualType Type) {
+QualType Sema::getTypeMember(QualType Type, StringRef Name) {
   auto Results = CXXRecordMembersNamed<TypeDecl>(Name, *this, Type);
   assert(Results.size() <= 1);
   if (Results.empty())
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index 96551c5106b1bb..a9a80831e63b71 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -928,7 +928,8 @@ bool Sema::LookupBuiltin(LookupResult &R) {
         if (II == getASTContext().getMakeIntegerSeqName()) {
           R.addDecl(getASTContext().getMakeIntegerSeqDecl());
           return true;
-        } else if (II == getASTContext().getTypePackElementName()) {
+        }
+        if (II == getASTContext().getTypePackElementName()) {
           R.addDecl(getASTContext().getTypePackElementDecl());
           return true;
         }
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index d73071c6bb95e4..01bd2ec2746993 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -3058,12 +3058,11 @@ void Sema::NoteAllFoundTemplates(TemplateName Name) {
   }
 }
 
-static std::optional<QualType> commonTypeImpl(Sema &S,
-                                              TemplateName BaseTemplate,
-                                              SourceLocation TemplateLoc,
-                                              ArrayRef<TemplateArgument> Ts) {
+static QualType commonTypeImpl(Sema &S, TemplateName BaseTemplate,
+                               SourceLocation TemplateLoc,
+                               ArrayRef<TemplateArgument> Ts) {
   auto lookUpCommonType = [&](TemplateArgument T1,
-                              TemplateArgument T2) -> std::optional<QualType> {
+                              TemplateArgument T2) -> QualType {
     // Don't bother looking for other specializations if both types are
     // builtins - users aren't allowed to specialize for them
     if (T1.getAsType()->isBuiltinType() && T2.getAsType()->isBuiltinType())
@@ -3078,12 +3077,8 @@ static std::optional<QualType> commonTypeImpl(Sema &S,
         S.CheckTemplateIdType(BaseTemplate, TemplateLoc, Args);
     if (S.RequireCompleteType(TemplateLoc, BaseTemplateInst,
                               diag::err_incomplete_type))
-      return std::nullopt;
-    if (QualType Type = S.getTypeMember("type", BaseTemplateInst);
-        !Type.isNull()) {
-      return Type;
-    }
-    return std::nullopt;
+      return QualType();
+    return S.getTypeMember(BaseTemplateInst, "type");
   };
 
   // Note A: For the common_type trait applied to a template parameter pack T of
@@ -3092,7 +3087,7 @@ static std::optional<QualType> commonTypeImpl(Sema &S,
 
   // If sizeof...(T) is zero, there shall be no member type.
   case 0:
-    return std::nullopt;
+    return QualType();
 
   // If sizeof...(T) is one, let T0 denote the sole type constituting the
   // pack T. The member typedef-name type shall denote the same type, if any, as
@@ -3111,27 +3106,25 @@ static std::optional<QualType> commonTypeImpl(Sema &S,
 
     // If is_same_v<T1, D1> is false or is_same_v<T2, D2> is false, let C denote
     // the same type, if any, as common_type_t<D1, D2>.
-    if (!S.Context.hasSameType(T1, D1) || !S.Context.hasSameType(T2, D2)) {
+    if (!S.Context.hasSameType(T1, D1) || !S.Context.hasSameType(T2, D2))
       return lookUpCommonType(D1, D2);
-    }
 
     // Otherwise, if decay_t<decltype(false ? declval<D1>() : declval<D2>())>
     // denotes a valid type, let C denote that type.
     {
       auto CheckConditionalOperands =
-          [&](bool ConstRefQual) -> std::optional<QualType> {
+          [&](bool ConstRefQual) -> QualType {
         EnterExpressionEvaluationContext UnevaluatedContext(
             S, Sema::ExpressionEvaluationContext::Unevaluated);
         Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/true);
         Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl());
 
         // false
-        OpaqueValueExpr CondExpr({}, S.Context.BoolTy,
-                                 ExprValueKind::VK_PRValue);
+        OpaqueValueExpr CondExpr(SourceLocation(), S.Context.BoolTy,
+                                 VK_PRValue);
         ExprResult Cond = &CondExpr;
 
-        auto EVK =
-            ConstRefQual ? ExprValueKind::VK_LValue : ExprValueKind::VK_PRValue;
+        auto EVK = ConstRefQual ? VK_LValue : VK_PRValue;
         if (ConstRefQual) {
           D1.addConst();
           D2.addConst();
@@ -3153,13 +3146,13 @@ static std::optional<QualType> commonTypeImpl(Sema &S,
             S.CheckConditionalOperands(Cond, LHS, RHS, VK, OK, TemplateLoc);
 
         if (Result.isNull() || SFINAE.hasErrorOccurred())
-          return std::nullopt;
+          return QualType();
 
         // decay_t<decltype(false ? declval<D1>() : declval<D2>())>
         return S.BuiltinDecay(Result, TemplateLoc);
       };
 
-      if (auto Res = CheckConditionalOperands(false))
+      if (auto Res = CheckConditionalOperands(false); !Res.isNull())
         return Res;
 
       // Let:
@@ -3171,7 +3164,7 @@ static std::optional<QualType> commonTypeImpl(Sema &S,
       // Otherwise, if COND-RES(CREF(D1), CREF(D2)) denotes a type, let C denote
       // the type decay_t<COND-RES(CREF(D1), CREF(D2))>.
       if (!S.Context.getLangOpts().CPlusPlus20)
-        return std::nullopt;
+        return QualType();
       return CheckConditionalOperands(true);
     }
   }
@@ -3182,11 +3175,11 @@ static std::optional<QualType> commonTypeImpl(Sema &S,
   // a type C, the member typedef-name type shall denote the same type, if any,
   // as common_type_t<C, R...>. Otherwise, there shall be no member type.
   default: {
-    std::optional<QualType> Result = Ts[Ts.size() - 1].getAsType();
-    for (size_t i = Ts.size() - 1; i != 0; --i) {
-      Result = lookUpCommonType(Ts[i - 1].getAsType(), *Result);
-      if (!Result)
-        return std::nullopt;
+    QualType Result = Ts.front().getAsType();
+    for (auto T : llvm::drop_begin(Ts)) {
+      Result = lookUpCommonType(Result, T.getAsType());
+      if (Result.isNull())
+        return QualType();
     }
     return Result;
   }
@@ -3287,11 +3280,12 @@ checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD,
     TemplateName HasTypeMember = Converted[1].getAsTemplate();
     QualType HasNoTypeMember = Converted[2].getAsType();
     ArrayRef<TemplateArgument> Ts = Converted[3].getPackAsArray();
-    if (auto CT = commonTypeImpl(SemaRef, BaseTemplate, TemplateLoc, Ts)) {
+    if (auto CT = commonTypeImpl(SemaRef, BaseTemplate, TemplateLoc, Ts);
+        !CT.isNull()) {
       TemplateArgumentListInfo TAs;
       TAs.addArgument(TemplateArgumentLoc(
-          TemplateArgument(*CT), SemaRef.Context.getTrivialTypeSourceInfo(
-                                     *CT, TemplateArgs[1].getLocation())));
+          TemplateArgument(CT), SemaRef.Context.getTrivialTypeSourceInfo(
+                                    CT, TemplateArgs[1].getLocation())));
 
       return SemaRef.CheckTemplateIdType(HasTypeMember, TemplateLoc, TAs);
     }
diff --git a/clang/test/SemaCXX/type-trait-common-type.cpp b/clang/test/SemaCXX/type-trait-common-type.cpp
index 44207f066a3336..64d41d7db3e138 100644
--- a/clang/test/SemaCXX/type-trait-common-type.cpp
+++ b/clang/test/SemaCXX/type-trait-common-type.cpp
@@ -5,7 +5,7 @@
 #  error
 #endif
 
-// expected-note@*:* {{template declaration from hidden source: template <class, template <class> class, class>}}
+// expected-note@*:* {{template declaration from hidden source: template <template <class ...> class, template <class> class, class, class ...>}}
 
 void test() {
   __common_type<> a; // expected-error {{too few template arguments for template '__common_type'}}
@@ -22,7 +22,8 @@ template <class...>
 struct common_type;
 
 template <class... Args>
-using common_type_base = __common_type<common_type, type_identity, empty_type, Args...>; // expected-error {{incomplete type 'common_type<Incomplete, Incomplete>' where a complete type is required}}
+using common_type_base = __common_type<common_type, type_identity, empty_type, Args...>;
+// expected-error at -1 {{incomplete type 'common_type<Incomplete, Incomplete>' where a complete type is required}}
 
 template <class... Args>
 struct common_type : common_type_base<Args...> {};
@@ -124,3 +125,44 @@ static_assert(__is_same(common_type_base<const_ref_convertible, int &>, type_ide
 #else
 static_assert(__is_same(common_type_base<const_ref_convertible, int &>, empty_type));
 #endif
+
+struct WeirdConvertible_1p2_p3 {};
+
+struct WeirdConvertible3 {
+  operator WeirdConvertible_1p2_p3();
+};
+
+struct WeirdConvertible1p2 {
+  operator WeirdConvertible_1p2_p3();
+};
+
+template <>
+struct common_type<WeirdConvertible3, WeirdConvertible1p2> {
+  using type = WeirdConvertible_1p2_p3;
+};
+
+template <>
+struct common_type<WeirdConvertible1p2, WeirdConvertible3> {
+  using type = WeirdConvertible_1p2_p3;
+};
+
+struct WeirdConvertible1 {
+  operator WeirdConvertible1p2();
+};
+
+struct WeirdConvertible2 {
+  operator WeirdConvertible1p2();
+};
+
+template <>
+struct common_type<WeirdConvertible1, WeirdConvertible2> {
+  using type = WeirdConvertible1p2;
+};
+
+template <>
+struct common_type<WeirdConvertible2, WeirdConvertible1> {
+  using type = WeirdConvertible1p2;
+};
+
+static_assert(__is_same(common_type_base<WeirdConvertible1, WeirdConvertible2, WeirdConvertible3>,
+                        type_identity<WeirdConvertible_1p2_p3>));
diff --git a/libcxx/include/__type_traits/common_type.h b/libcxx/include/__type_traits/common_type.h
index 103b58e42aa8a1..c0cb1a5f803853 100644
--- a/libcxx/include/__type_traits/common_type.h
+++ b/libcxx/include/__type_traits/common_type.h
@@ -30,6 +30,11 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class... _Args>
 struct common_type : __common_type<common_type, __type_identity, __empty, _Args...> {};
 
+#if _LIBCPP_STD_VER >= 14
+template <class... _Args>
+using common_type_t = typename __common_type<common_type, __type_identity, __empty, _Args...>::type;
+#endif
+
 #else
 #  if _LIBCPP_STD_VER >= 20
 // Let COND_RES(X, Y) be:
@@ -100,13 +105,13 @@ template <class _Tp, class _Up, class _Vp, class... _Rest>
 struct _LIBCPP_TEMPLATE_VIS common_type<_Tp, _Up, _Vp, _Rest...>
     : __common_type_impl<__common_types<_Tp, _Up, _Vp, _Rest...> > {};
 
-#endif
-
 #if _LIBCPP_STD_VER >= 14
 template <class... _Tp>
 using common_type_t = typename common_type<_Tp...>::type;
 #endif
 
+#endif
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___TYPE_TRAITS_COMMON_TYPE_H

>From f418c260512dfd5c17918a5b6daddd76825c0433 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 25 Jul 2024 12:32:52 +0200
Subject: [PATCH 3/7] Add release note and documentation

---
 clang/docs/LanguageExtensions.rst | 37 +++++++++++++++++++++++++++++++
 clang/docs/ReleaseNotes.rst       |  5 ++++-
 2 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 81784c75081bae..1c0f90f9cdfa46 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1511,6 +1511,43 @@ Attributes (N2335)                                                            C2
 ``#embed`` (N3017)                                                            C23           C89, C++
 ============================================ ================================ ============= =============
 
+Builtin type aliases
+====================
+
+Clang provides a few builtin aliases to improve the throughput of certain metaprogramming facilities.
+
+__common_type
+-------------
+
+.. code-block:: c++
+  template <template <class... Args> class BaseTemplate,
+            template <class TypeMember> class HasTypeMember,
+            class HasNoTypeMember,
+            class... Ts>
+  using __common_type = ...;
+
+This alias is used for implementing ``std::common_type``. If ``std::common_type`` should contain a ``type`` member,
+it is an alias to ``HasTypeMember<TheCommonType>``. Otherwise it is an alias to ``HasNoTypeMember``. The
+``BaseTemplate`` is usually ``std::common_type``. ``Ts`` are the arguments to ``std::common_type``.
+
+__type_pack_element
+-------------------
+
+.. code-block:: c++
+  template <std::size_t Index, class... Ts>
+  using __type_pack_element = ...;
+
+This alias returns the type at ``Index`` in the parameter pack ``Ts``.
+
+__make_integer_seq
+------------------
+
+.. code-block:: c++
+  template <template <class IntSeqT, IntSeqT... Ints> class IntSeq, class T, class N>
+  using __make_integer_seq = ...;
+
+This alias returns ``IntSeq`` instantiated with ``IntSeqT = T``and ``Ints`` being the pack ``0, ..., N - 1``.
+
 Type Trait Primitives
 =====================
 
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 0b79e952b48af2..734c571e851137 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -65,6 +65,9 @@ sections with improvements to Clang's support for those languages.
 C++ Language Changes
 --------------------
 
+- The builtin type alias ``__common_type`` has been added to improve the
+  performance of ``std::common_type``.
+
 C++17 Feature Support
 ^^^^^^^^^^^^^^^^^^^^^
 
@@ -117,7 +120,7 @@ Improvements to Clang's diagnostics
 - Some template related diagnostics have been improved.
 
   .. code-block:: c++
-    
+
      void foo() { template <typename> int i; } // error: templates can only be declared in namespace or class scope
 
      struct S {

>From eb8afc07f466bb9d13cfadad3d05fb01db9b17bf Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Thu, 25 Jul 2024 13:26:53 +0200
Subject: [PATCH 4/7] Try to fix CI; Address comment

---
 clang/docs/LanguageExtensions.rst          | 5 ++++-
 clang/lib/Sema/SemaTemplate.cpp            | 3 +--
 clang/lib/Serialization/ASTWriter.cpp      | 3 +--
 libcxx/include/__type_traits/common_type.h | 9 ++-------
 4 files changed, 8 insertions(+), 12 deletions(-)

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 1c0f90f9cdfa46..4aa5eadb480435 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1520,6 +1520,7 @@ __common_type
 -------------
 
 .. code-block:: c++
+
   template <template <class... Args> class BaseTemplate,
             template <class TypeMember> class HasTypeMember,
             class HasNoTypeMember,
@@ -1534,6 +1535,7 @@ __type_pack_element
 -------------------
 
 .. code-block:: c++
+
   template <std::size_t Index, class... Ts>
   using __type_pack_element = ...;
 
@@ -1543,7 +1545,8 @@ __make_integer_seq
 ------------------
 
 .. code-block:: c++
-  template <template <class IntSeqT, IntSeqT... Ints> class IntSeq, class T, class N>
+
+  template <template <class IntSeqT, IntSeqT... Ints> class IntSeq, class T, T N>
   using __make_integer_seq = ...;
 
 This alias returns ``IntSeq`` instantiated with ``IntSeqT = T``and ``Ints`` being the pack ``0, ..., N - 1``.
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 01bd2ec2746993..0f7d47b8851d3e 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -3112,8 +3112,7 @@ static QualType commonTypeImpl(Sema &S, TemplateName BaseTemplate,
     // Otherwise, if decay_t<decltype(false ? declval<D1>() : declval<D2>())>
     // denotes a valid type, let C denote that type.
     {
-      auto CheckConditionalOperands =
-          [&](bool ConstRefQual) -> QualType {
+      auto CheckConditionalOperands = [&](bool ConstRefQual) -> QualType {
         EnterExpressionEvaluationContext UnevaluatedContext(
             S, Sema::ExpressionEvaluationContext::Unevaluated);
         Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/true);
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index b17eeed357aac1..7d8498edbce308 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -5036,8 +5036,7 @@ void ASTWriter::PrepareWritingSpecialDecls(Sema &SemaRef) {
                      PREDEF_DECL_CF_CONSTANT_STRING_TAG_ID);
   RegisterPredefDecl(Context.TypePackElementDecl,
                      PREDEF_DECL_TYPE_PACK_ELEMENT_ID);
-  RegisterPredefDecl(Context.CommonTypeDecl,
-                     PREDEF_DECL_COMMON_TYPE_ID);
+  RegisterPredefDecl(Context.CommonTypeDecl, PREDEF_DECL_COMMON_TYPE_ID);
 
   const TranslationUnitDecl *TU = Context.getTranslationUnitDecl();
 
diff --git a/libcxx/include/__type_traits/common_type.h b/libcxx/include/__type_traits/common_type.h
index c0cb1a5f803853..103b58e42aa8a1 100644
--- a/libcxx/include/__type_traits/common_type.h
+++ b/libcxx/include/__type_traits/common_type.h
@@ -30,11 +30,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 template <class... _Args>
 struct common_type : __common_type<common_type, __type_identity, __empty, _Args...> {};
 
-#if _LIBCPP_STD_VER >= 14
-template <class... _Args>
-using common_type_t = typename __common_type<common_type, __type_identity, __empty, _Args...>::type;
-#endif
-
 #else
 #  if _LIBCPP_STD_VER >= 20
 // Let COND_RES(X, Y) be:
@@ -105,13 +100,13 @@ template <class _Tp, class _Up, class _Vp, class... _Rest>
 struct _LIBCPP_TEMPLATE_VIS common_type<_Tp, _Up, _Vp, _Rest...>
     : __common_type_impl<__common_types<_Tp, _Up, _Vp, _Rest...> > {};
 
+#endif
+
 #if _LIBCPP_STD_VER >= 14
 template <class... _Tp>
 using common_type_t = typename common_type<_Tp...>::type;
 #endif
 
-#endif
-
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___TYPE_TRAITS_COMMON_TYPE_H

>From 271232bbd25ef535886cf08b7d3d9d7ca2567926 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Wed, 31 Jul 2024 21:54:32 +0200
Subject: [PATCH 5/7] Address comments

---
 clang/include/clang/Sema/Sema.h               |  4 ----
 clang/lib/Sema/SemaChecking.cpp               |  8 -------
 clang/lib/Sema/SemaTemplate.cpp               | 16 +++++++++----
 clang/test/SemaCXX/type-trait-common-type.cpp | 24 +++++++++++++++----
 libcxx/include/__type_traits/common_type.h    |  8 ++++++-
 5 files changed, 38 insertions(+), 22 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 9ed20e71d61a61..7bfdaaae45a93e 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2282,10 +2282,6 @@ class Sema final : public SemaBase {
   /// Check to see if a given expression could have '.c_str()' called on it.
   bool hasCStrMethod(const Expr *E);
 
-  // Check whether a type member 'Type::Name' exists, and if yes, return the
-  // type. If there is no type, the QualType is null
-  QualType getTypeMember(QualType Type, StringRef Name);
-
   /// Diagnose pointers that are always non-null.
   /// \param E the expression containing the pointer
   /// \param NullKind NPCK_NotNull if E is a cast to bool, otherwise, E is
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 76135cfd5729f8..cf1196ad23c217 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -6848,14 +6848,6 @@ CXXRecordMembersNamed(StringRef Name, Sema &S, QualType Ty) {
   return Results;
 }
 
-QualType Sema::getTypeMember(QualType Type, StringRef Name) {
-  auto Results = CXXRecordMembersNamed<TypeDecl>(Name, *this, Type);
-  assert(Results.size() <= 1);
-  if (Results.empty())
-    return {};
-  return Context.getTypeDeclType(*Results.begin());
-}
-
 /// Check if we could call '.c_str()' on an object.
 ///
 /// FIXME: This returns the wrong results in some cases (if cv-qualifiers don't
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 0f7d47b8851d3e..0c49e245a64e29 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -3073,12 +3073,19 @@ static QualType commonTypeImpl(Sema &S, TemplateName BaseTemplate,
         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 (S.RequireCompleteType(TemplateLoc, BaseTemplateInst,
-                              diag::err_incomplete_type))
+
+    if (SFINAE.hasErrorOccurred())
       return QualType();
-    return S.getTypeMember(BaseTemplateInst, "type");
+
+    return BaseTemplateInst;
   };
 
   // Note A: For the common_type trait applied to a template parameter pack T of
@@ -3270,8 +3277,7 @@ checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD,
 
   case BTK__common_type: {
     assert(Converted.size() == 4);
-    if (Converted[0].isDependent() || Converted[1].isDependent() ||
-        Converted[2].isDependent() || Converted[3].isDependent())
+    if (llvm::any_of(Converted, [](auto &C) { return C.isDependent(); }))
       return Context.getCanonicalTemplateSpecializationType(TemplateName(BTD),
                                                             Converted);
 
diff --git a/clang/test/SemaCXX/type-trait-common-type.cpp b/clang/test/SemaCXX/type-trait-common-type.cpp
index 64d41d7db3e138..fd05df8cd9f6c0 100644
--- a/clang/test/SemaCXX/type-trait-common-type.cpp
+++ b/clang/test/SemaCXX/type-trait-common-type.cpp
@@ -22,8 +22,10 @@ template <class...>
 struct common_type;
 
 template <class... Args>
-using common_type_base = __common_type<common_type, type_identity, empty_type, Args...>;
-// expected-error at -1 {{incomplete type 'common_type<Incomplete, Incomplete>' where a complete type is required}}
+using common_type_t = typename common_type<Args...>::type;
+
+template <class... Args>
+using common_type_base = __common_type<common_type_t, type_identity, empty_type, Args...>;
 
 template <class... Args>
 struct common_type : common_type_base<Args...> {};
@@ -31,10 +33,10 @@ struct common_type : common_type_base<Args...> {};
 struct Incomplete;
 
 template<>
-struct common_type<Incomplete, Incomplete>; // expected-note {{forward declaration}}
+struct common_type<Incomplete, Incomplete>;
 
 static_assert(__is_same(common_type_base<>, empty_type));
-static_assert(__is_same(common_type_base<Incomplete>, empty_type)); // expected-note {{requested here}}
+static_assert(__is_same(common_type_base<Incomplete>, empty_type));
 static_assert(__is_same(common_type_base<char>, type_identity<char>));
 static_assert(__is_same(common_type_base<int>, type_identity<int>));
 static_assert(__is_same(common_type_base<const int>, type_identity<int>));
@@ -166,3 +168,17 @@ struct common_type<WeirdConvertible2, WeirdConvertible1> {
 
 static_assert(__is_same(common_type_base<WeirdConvertible1, WeirdConvertible2, WeirdConvertible3>,
                         type_identity<WeirdConvertible_1p2_p3>));
+
+struct PrivateTypeMember
+{
+  operator int();
+};
+
+template<>
+struct common_type<PrivateTypeMember, PrivateTypeMember>
+{
+private:
+  using type = int;
+};
+
+static_assert(__is_same(common_type_base<PrivateTypeMember, PrivateTypeMember, PrivateTypeMember>, empty_type));
diff --git a/libcxx/include/__type_traits/common_type.h b/libcxx/include/__type_traits/common_type.h
index 103b58e42aa8a1..a593d0ea36c6c9 100644
--- a/libcxx/include/__type_traits/common_type.h
+++ b/libcxx/include/__type_traits/common_type.h
@@ -28,7 +28,13 @@ _LIBCPP_BEGIN_NAMESPACE_STD
 #if __has_builtin(__common_type)
 
 template <class... _Args>
-struct common_type : __common_type<common_type, __type_identity, __empty, _Args...> {};
+struct common_type;
+
+template <class... _Args>
+using __common_type_t = typename common_type<_Args...>::type;
+
+template <class... _Args>
+struct common_type : __common_type<__common_type_t, __type_identity, __empty, _Args...> {};
 
 #else
 #  if _LIBCPP_STD_VER >= 20

>From 4e3cd046d6f6e1b5be8e334245940ed8c9e41260 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sat, 10 Aug 2024 11:58:39 +0200
Subject: [PATCH 6/7] Rename to __builtin_common_type

---
 clang/docs/LanguageExtensions.rst             |  4 ++--
 clang/docs/ReleaseNotes.rst                   |  2 +-
 clang/include/clang/AST/ASTContext.h          | 16 ++++++++--------
 clang/include/clang/AST/DeclID.h              |  2 +-
 clang/include/clang/Basic/Builtins.h          |  4 ++--
 clang/lib/AST/ASTContext.cpp                  | 10 +++++-----
 clang/lib/AST/ASTImporter.cpp                 |  4 ++--
 clang/lib/AST/DeclTemplate.cpp                |  8 ++++----
 clang/lib/Lex/PPMacroExpansion.cpp            |  2 +-
 clang/lib/Sema/SemaLookup.cpp                 |  4 ++--
 clang/lib/Sema/SemaTemplate.cpp               |  8 ++++----
 clang/lib/Serialization/ASTReader.cpp         |  2 +-
 clang/test/SemaCXX/type-trait-common-type.cpp |  6 +++---
 libcxx/include/__type_traits/common_type.h    |  4 ++--
 14 files changed, 38 insertions(+), 38 deletions(-)

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 4aa5eadb480435..f98f410568aa37 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1516,7 +1516,7 @@ Builtin type aliases
 
 Clang provides a few builtin aliases to improve the throughput of certain metaprogramming facilities.
 
-__common_type
+__builtin_common_type
 -------------
 
 .. code-block:: c++
@@ -1525,7 +1525,7 @@ __common_type
             template <class TypeMember> class HasTypeMember,
             class HasNoTypeMember,
             class... Ts>
-  using __common_type = ...;
+  using __builtin_common_type = ...;
 
 This alias is used for implementing ``std::common_type``. If ``std::common_type`` should contain a ``type`` member,
 it is an alias to ``HasTypeMember<TheCommonType>``. Otherwise it is an alias to ``HasNoTypeMember``. The
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 734c571e851137..eacc3d11782a6d 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -65,7 +65,7 @@ sections with improvements to Clang's support for those languages.
 C++ Language Changes
 --------------------
 
-- The builtin type alias ``__common_type`` has been added to improve the
+- The builtin type alias ``__builtin_common_type`` has been added to improve the
   performance of ``std::common_type``.
 
 C++17 Feature Support
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 0c569324a42a13..8d954d91b54099 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -399,8 +399,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// The identifier '__type_pack_element'.
   mutable IdentifierInfo *TypePackElementName = nullptr;
 
-  /// The identifier '__common_type'.
-  mutable IdentifierInfo *CommonTypeName = nullptr;
+  /// The identifier '__builtin_common_type'.
+  mutable IdentifierInfo *BuiltinCommonTypeName = nullptr;
 
   QualType ObjCConstantStringType;
   mutable RecordDecl *CFConstantStringTagDecl = nullptr;
@@ -609,7 +609,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
   mutable ExternCContextDecl *ExternCContext = nullptr;
   mutable BuiltinTemplateDecl *MakeIntegerSeqDecl = nullptr;
   mutable BuiltinTemplateDecl *TypePackElementDecl = nullptr;
-  mutable BuiltinTemplateDecl *CommonTypeDecl = nullptr;
+  mutable BuiltinTemplateDecl *BuiltinCommonTypeDecl = nullptr;
 
   /// The associated SourceManager object.
   SourceManager &SourceMgr;
@@ -1117,7 +1117,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
   ExternCContextDecl *getExternCContextDecl() const;
   BuiltinTemplateDecl *getMakeIntegerSeqDecl() const;
   BuiltinTemplateDecl *getTypePackElementDecl() const;
-  BuiltinTemplateDecl *getCommonTypeDecl() const;
+  BuiltinTemplateDecl *getBuiltinCommonTypeDecl() const;
 
   // Builtin Types.
   CanQualType VoidTy;
@@ -1995,10 +1995,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
     return TypePackElementName;
   }
 
-  IdentifierInfo *getCommonTypeName() const {
-    if (!CommonTypeName)
-      CommonTypeName = &Idents.get("__common_type");
-    return CommonTypeName;
+  IdentifierInfo *getBuiltinCommonTypeName() const {
+    if (!BuiltinCommonTypeName)
+      BuiltinCommonTypeName = &Idents.get("__builtin_common_type");
+    return BuiltinCommonTypeName;
   }
 
   /// Retrieve the Objective-C "instancetype" type, if already known;
diff --git a/clang/include/clang/AST/DeclID.h b/clang/include/clang/AST/DeclID.h
index 875e9a72b39512..2d08380dddb5a6 100644
--- a/clang/include/clang/AST/DeclID.h
+++ b/clang/include/clang/AST/DeclID.h
@@ -85,7 +85,7 @@ enum PredefinedDeclIDs {
   /// The internal '__type_pack_element' template.
   PREDEF_DECL_TYPE_PACK_ELEMENT_ID = 17,
 
-  /// The internal '__common_type' template.
+  /// The internal '__builtin_common_type' template.
   PREDEF_DECL_COMMON_TYPE_ID = 18,
 };
 
diff --git a/clang/include/clang/Basic/Builtins.h b/clang/include/clang/Basic/Builtins.h
index 4353b72f713838..89f65682ae5b41 100644
--- a/clang/include/clang/Basic/Builtins.h
+++ b/clang/include/clang/Basic/Builtins.h
@@ -311,8 +311,8 @@ enum BuiltinTemplateKind : int {
   /// This names the __type_pack_element BuiltinTemplateDecl.
   BTK__type_pack_element,
 
-  /// This names the __common_type BuiltinTemplateDecl.
-  BTK__common_type,
+  /// This names the __builtin_common_type BuiltinTemplateDecl.
+  BTK__builtin_common_type,
 };
 
 } // end namespace clang
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 9c991bd4b94c64..21b4c52429446d 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -1170,11 +1170,11 @@ ASTContext::getTypePackElementDecl() const {
   return TypePackElementDecl;
 }
 
-BuiltinTemplateDecl *ASTContext::getCommonTypeDecl() const {
-  if (!CommonTypeDecl)
-    CommonTypeDecl =
-        buildBuiltinTemplateDecl(BTK__common_type, getCommonTypeName());
-  return CommonTypeDecl;
+BuiltinTemplateDecl *ASTContext::getBuiltinCommonTypeDecl() const {
+  if (!BuiltinCommonTypeDecl)
+    BuiltinCommonTypeDecl = buildBuiltinTemplateDecl(
+        BTK__builtin_common_type, getBuiltinCommonTypeName());
+  return BuiltinCommonTypeDecl;
 }
 
 RecordDecl *ASTContext::buildImplicitRecord(StringRef Name,
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 2d62dabe0579dd..53dec82aece375 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -5413,8 +5413,8 @@ ExpectedDecl ASTNodeImporter::VisitBuiltinTemplateDecl(BuiltinTemplateDecl *D) {
   case BuiltinTemplateKind::BTK__type_pack_element:
     ToD = Importer.getToContext().getTypePackElementDecl();
     break;
-  case BuiltinTemplateKind::BTK__common_type:
-    ToD = Importer.getToContext().getCommonTypeDecl();
+  case BuiltinTemplateKind::BTK__builtin_common_type:
+    ToD = Importer.getToContext().getBuiltinCommonTypeDecl();
     break;
   }
   assert(ToD && "BuiltinTemplateDecl of unsupported kind!");
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index c119d242bc292e..6fe817c5ef1c6b 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1608,8 +1608,8 @@ createTypePackElementParameterList(const ASTContext &C, DeclContext *DC) {
                                        nullptr);
 }
 
-static TemplateParameterList *createCommonTypeList(const ASTContext &C,
-                                                   DeclContext *DC) {
+static TemplateParameterList *createBuiltinCommonTypeList(const ASTContext &C,
+                                                          DeclContext *DC) {
   // class... Args
   auto *Args =
       TemplateTypeParmDecl::Create(C, DC, SourceLocation(), SourceLocation(),
@@ -1669,8 +1669,8 @@ static TemplateParameterList *createBuiltinTemplateParameterList(
     return createMakeIntegerSeqParameterList(C, DC);
   case BTK__type_pack_element:
     return createTypePackElementParameterList(C, DC);
-  case BTK__common_type:
-    return createCommonTypeList(C, DC);
+  case BTK__builtin_common_type:
+    return createBuiltinCommonTypeList(C, DC);
   }
 
   llvm_unreachable("unhandled BuiltinTemplateKind!");
diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp
index ccc3f863ca4b40..5b4e503159a183 100644
--- a/clang/lib/Lex/PPMacroExpansion.cpp
+++ b/clang/lib/Lex/PPMacroExpansion.cpp
@@ -1826,7 +1826,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
               // Report builtin templates as being builtins.
               .Case("__make_integer_seq", getLangOpts().CPlusPlus)
               .Case("__type_pack_element", getLangOpts().CPlusPlus)
-              .Case("__common_type", getLangOpts().CPlusPlus)
+              .Case("__builtin_common_type", getLangOpts().CPlusPlus)
               // Likewise for some builtin preprocessor macros.
               // FIXME: This is inconsistent; we usually suggest detecting
               // builtin macros via #ifdef. Don't add more cases here.
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index a9a80831e63b71..beeb72e5727c54 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -933,8 +933,8 @@ bool Sema::LookupBuiltin(LookupResult &R) {
           R.addDecl(getASTContext().getTypePackElementDecl());
           return true;
         }
-        if (II == getASTContext().getCommonTypeName()) {
-          R.addDecl(getASTContext().getCommonTypeDecl());
+        if (II == getASTContext().getBuiltinCommonTypeName()) {
+          R.addDecl(getASTContext().getBuiltinCommonTypeDecl());
           return true;
         }
       }
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 0c49e245a64e29..d4aef07e298627 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -3058,7 +3058,7 @@ void Sema::NoteAllFoundTemplates(TemplateName Name) {
   }
 }
 
-static QualType commonTypeImpl(Sema &S, TemplateName BaseTemplate,
+static QualType builtinCommonTypeImpl(Sema &S, TemplateName BaseTemplate,
                                SourceLocation TemplateLoc,
                                ArrayRef<TemplateArgument> Ts) {
   auto lookUpCommonType = [&](TemplateArgument T1,
@@ -3066,7 +3066,7 @@ static QualType commonTypeImpl(Sema &S, TemplateName BaseTemplate,
     // Don't bother looking for other specializations if both types are
     // builtins - users aren't allowed to specialize for them
     if (T1.getAsType()->isBuiltinType() && T2.getAsType()->isBuiltinType())
-      return commonTypeImpl(S, BaseTemplate, TemplateLoc, {T1, T2});
+      return builtinCommonTypeImpl(S, BaseTemplate, TemplateLoc, {T1, T2});
 
     TemplateArgumentListInfo Args;
     Args.addArgument(TemplateArgumentLoc(
@@ -3275,7 +3275,7 @@ checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD,
     return Ts.getPackAsArray()[N].getAsType();
   }
 
-  case BTK__common_type: {
+  case BTK__builtin_common_type: {
     assert(Converted.size() == 4);
     if (llvm::any_of(Converted, [](auto &C) { return C.isDependent(); }))
       return Context.getCanonicalTemplateSpecializationType(TemplateName(BTD),
@@ -3285,7 +3285,7 @@ checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD,
     TemplateName HasTypeMember = Converted[1].getAsTemplate();
     QualType HasNoTypeMember = Converted[2].getAsType();
     ArrayRef<TemplateArgument> Ts = Converted[3].getPackAsArray();
-    if (auto CT = commonTypeImpl(SemaRef, BaseTemplate, TemplateLoc, Ts);
+    if (auto CT = builtinCommonTypeImpl(SemaRef, BaseTemplate, TemplateLoc, Ts);
         !CT.isNull()) {
       TemplateArgumentListInfo TAs;
       TAs.addArgument(TemplateArgumentLoc(
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 6ae243a7b30df3..9ee258f0d27f74 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -7791,7 +7791,7 @@ static Decl *getPredefinedDecl(ASTContext &Context, PredefinedDeclIDs ID) {
     return Context.getTypePackElementDecl();
 
   case PREDEF_DECL_COMMON_TYPE_ID:
-    return Context.getCommonTypeDecl();
+    return Context.getBuiltinCommonTypeDecl();
   }
   llvm_unreachable("PredefinedDeclIDs unknown enum value");
 }
diff --git a/clang/test/SemaCXX/type-trait-common-type.cpp b/clang/test/SemaCXX/type-trait-common-type.cpp
index fd05df8cd9f6c0..bc2ac54654f698 100644
--- a/clang/test/SemaCXX/type-trait-common-type.cpp
+++ b/clang/test/SemaCXX/type-trait-common-type.cpp
@@ -1,14 +1,14 @@
 // RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -verify -std=c++17 %s
 // RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -verify -std=c++20 %s
 
-#if !__has_builtin(__common_type)
+#if !__has_builtin(__builtin_common_type)
 #  error
 #endif
 
 // expected-note@*:* {{template declaration from hidden source: template <template <class ...> class, template <class> class, class, class ...>}}
 
 void test() {
-  __common_type<> a; // expected-error {{too few template arguments for template '__common_type'}}
+  __builtin_common_type<> a; // expected-error {{too few template arguments for template '__builtin_common_type'}}
 }
 
 struct empty_type {};
@@ -25,7 +25,7 @@ template <class... Args>
 using common_type_t = typename common_type<Args...>::type;
 
 template <class... Args>
-using common_type_base = __common_type<common_type_t, type_identity, empty_type, Args...>;
+using common_type_base = __builtin_common_type<common_type_t, type_identity, empty_type, Args...>;
 
 template <class... Args>
 struct common_type : common_type_base<Args...> {};
diff --git a/libcxx/include/__type_traits/common_type.h b/libcxx/include/__type_traits/common_type.h
index a593d0ea36c6c9..ef542f8bccfe02 100644
--- a/libcxx/include/__type_traits/common_type.h
+++ b/libcxx/include/__type_traits/common_type.h
@@ -25,7 +25,7 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-#if __has_builtin(__common_type)
+#if __has_builtin(__builtin_common_type)
 
 template <class... _Args>
 struct common_type;
@@ -34,7 +34,7 @@ template <class... _Args>
 using __common_type_t = typename common_type<_Args...>::type;
 
 template <class... _Args>
-struct common_type : __common_type<__common_type_t, __type_identity, __empty, _Args...> {};
+struct common_type : __builtin_common_type<__common_type_t, __type_identity, __empty, _Args...> {};
 
 #else
 #  if _LIBCPP_STD_VER >= 20

>From 656d12c554e42f0c1b9f7cb4ecb0f77a6376e842 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sat, 7 Sep 2024 10:27:45 +0200
Subject: [PATCH 7/7] Address comments

---
 clang/docs/LanguageExtensions.rst             |  2 +-
 clang/lib/Sema/SemaTemplate.cpp               |  4 +--
 clang/lib/Serialization/ASTWriter.cpp         |  2 +-
 clang/test/SemaCXX/type-trait-common-type.cpp | 30 +++++++++++++++++--
 4 files changed, 32 insertions(+), 6 deletions(-)

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 1a0186754513f3..e2e6bcae62b040 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1519,7 +1519,7 @@ Builtin type aliases
 Clang provides a few builtin aliases to improve the throughput of certain metaprogramming facilities.
 
 __builtin_common_type
--------------
+---------------------
 
 .. code-block:: c++
 
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 738589cd5a941f..0db2ac7ebb4086 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -3077,8 +3077,8 @@ void Sema::NoteAllFoundTemplates(TemplateName Name) {
 }
 
 static QualType builtinCommonTypeImpl(Sema &S, TemplateName BaseTemplate,
-                               SourceLocation TemplateLoc,
-                               ArrayRef<TemplateArgument> Ts) {
+                                      SourceLocation TemplateLoc,
+                                      ArrayRef<TemplateArgument> Ts) {
   auto lookUpCommonType = [&](TemplateArgument T1,
                               TemplateArgument T2) -> QualType {
     // Don't bother looking for other specializations if both types are
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 5da19f90e8467d..204f8079ae54c8 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -5051,7 +5051,7 @@ void ASTWriter::PrepareWritingSpecialDecls(Sema &SemaRef) {
                      PREDEF_DECL_CF_CONSTANT_STRING_TAG_ID);
   RegisterPredefDecl(Context.TypePackElementDecl,
                      PREDEF_DECL_TYPE_PACK_ELEMENT_ID);
-  RegisterPredefDecl(Context.CommonTypeDecl, PREDEF_DECL_COMMON_TYPE_ID);
+  RegisterPredefDecl(Context.BuiltinCommonTypeDecl, PREDEF_DECL_COMMON_TYPE_ID);
 
   const TranslationUnitDecl *TU = Context.getTranslationUnitDecl();
 
diff --git a/clang/test/SemaCXX/type-trait-common-type.cpp b/clang/test/SemaCXX/type-trait-common-type.cpp
index bc2ac54654f698..7190dcad76f1a3 100644
--- a/clang/test/SemaCXX/type-trait-common-type.cpp
+++ b/clang/test/SemaCXX/type-trait-common-type.cpp
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -verify -std=c++17 %s
-// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -verify -std=c++20 %s
+// 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_type)
 #  error
@@ -9,6 +9,8 @@
 
 void test() {
   __builtin_common_type<> a; // expected-error {{too few template arguments for template '__builtin_common_type'}}
+  __builtin_common_type<1> b; // expected-error {{template argument for template template parameter must be a class template or type alias template}}
+  __builtin_common_type<int, 1> c; // expected-error {{template argument for template template parameter must be a class template or type alias template}}
 }
 
 struct empty_type {};
@@ -24,6 +26,12 @@ struct common_type;
 template <class... Args>
 using common_type_t = typename common_type<Args...>::type;
 
+void test_vla() {
+  int i = 4;
+  int VLA[i];
+  __builtin_common_type<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_type_base = __builtin_common_type<common_type_t, type_identity, empty_type, Args...>;
 
@@ -63,6 +71,24 @@ static_assert(__is_same(common_type_base<int, long, float>, type_identity<float>
 static_assert(__is_same(common_type_base<unsigned, char, long>, type_identity<long>));
 static_assert(__is_same(common_type_base<long long, long long, long>, type_identity<long long>));
 
+static_assert(__is_same(common_type_base<int [[clang::address_space(1)]]>, type_identity<int [[clang::address_space(1)]]>));
+static_assert(__is_same(common_type_base<int [[clang::address_space(1)]], int>, type_identity<int>));
+static_assert(__is_same(common_type_base<long [[clang::address_space(1)]], int>, type_identity<long>));
+static_assert(__is_same(common_type_base<long [[clang::address_space(1)]], int [[clang::address_space(1)]]>, type_identity<long>));
+static_assert(__is_same(common_type_base<long [[clang::address_space(1)]], long [[clang::address_space(1)]]>, type_identity<long [[clang::address_space(1)]]>));
+static_assert(__is_same(common_type_base<long [[clang::address_space(1)]], long [[clang::address_space(2)]]>, type_identity<long>));
+
+struct S {};
+struct T : S {};
+
+static_assert(__is_same(common_type_base<int S::*, int S::*>, type_identity<int S::*>));
+static_assert(__is_same(common_type_base<int S::*, int T::*>, type_identity<int T::*>));
+static_assert(__is_same(common_type_base<int S::*, long S::*>, empty_type));
+
+static_assert(__is_same(common_type_base<int (S::*)(), int (S::*)()>, type_identity<int (S::*)()>));
+static_assert(__is_same(common_type_base<int (S::*)(), int (T::*)()>, type_identity<int (T::*)()>));
+static_assert(__is_same(common_type_base<int (S::*)(), long (S::*)()>, empty_type));
+
 struct NoCommonType {};
 
 template <>



More information about the libcxx-commits mailing list