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

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Thu Jul 18 04:44:11 PDT 2024


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

>From f9b9431a97952909190c69e1dee188ef234addb2 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] [Clang] Add __common_type builtin

---
 clang/include/clang/AST/ASTContext.h          |  11 +
 clang/include/clang/AST/DeclID.h              |   3 +
 clang/include/clang/Basic/Builtins.h          |   5 +-
 clang/include/clang/Sema/Sema.h               |   4 +
 clang/lib/AST/ASTContext.cpp                  |   8 +
 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               | 154 ++++++++++-
 clang/test/AST/Interp/crash-GH49103-2.cpp     |   4 +-
 clang/test/SemaCXX/crash-GH49103-2.cpp        |   4 +-
 clang/test/SemaCXX/type-trait-common-type.cpp | 125 +++++++++
 clang/test/SemaCXX/type-traits.cpp            | 240 +++++++++---------
 libcxx/include/__type_traits/common_type.h    |  10 +
 16 files changed, 511 insertions(+), 126 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 57022e75073fe..867467d01f4d1 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;
@@ -1104,6 +1108,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
   ExternCContextDecl *getExternCContextDecl() const;
   BuiltinTemplateDecl *getMakeIntegerSeqDecl() const;
   BuiltinTemplateDecl *getTypePackElementDecl() const;
+  BuiltinTemplateDecl *getCommonTypeDecl() const;
 
   // Builtin Types.
   CanQualType VoidTy;
@@ -1981,6 +1986,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 e5e27389fac60..d8243773d8d98 100644
--- a/clang/include/clang/AST/DeclID.h
+++ b/clang/include/clang/AST/DeclID.h
@@ -84,6 +84,9 @@ 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 = 17,
 };
 
 /// The number of declaration IDs that are predefined.
diff --git a/clang/include/clang/Basic/Builtins.h b/clang/include/clang/Basic/Builtins.h
index f955d21169556..defffb334d480 100644
--- a/clang/include/clang/Basic/Builtins.h
+++ b/clang/include/clang/Basic/Builtins.h
@@ -304,7 +304,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 48dff1b76cc57..1f9480418238a 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 6c89e3890ae3e..4000c2c2d6836 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -1170,6 +1170,14 @@ 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 4e1b3a5a94de7..e9d8748d4ee37 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 722c7fcf0b0df..d290c91fb8290 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 3913ff08c2eb5..4a6dd13229fe0 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 45b9bbb23dbf7..10c821fd367e7 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 7a6a64529f52e..96551c5106b1b 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 9d96201625389..3f862d06af502 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -3058,6 +3058,136 @@ 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;
+
+        // declval<D1>()
+        auto EVK = ConstRefQual ? ExprValueKind::VK_LValue
+                                     : ExprValueKind::VK_PRValue;
+        if (ConstRefQual) {
+          D1.addConst();
+          D2.addConst();
+        }
+        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;
+
+        QualType Result =
+            S.CheckConditionalOperands(Cond, LHS, RHS, VK, OK, TemplateLoc);
+
+        if (Result.isNull() || SFINAE.hasErrorOccurred())
+          return std::nullopt;
+        return Result;
+      };
+
+      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 +3244,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 +3270,28 @@ 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/test/AST/Interp/crash-GH49103-2.cpp b/clang/test/AST/Interp/crash-GH49103-2.cpp
index 82d78e2aeab0c..98fb036a2a017 100644
--- a/clang/test/AST/Interp/crash-GH49103-2.cpp
+++ b/clang/test/AST/Interp/crash-GH49103-2.cpp
@@ -9,5 +9,5 @@
 // https://github.com/llvm/llvm-project/issues/49103
 
 template<class> struct A; // expected-note 0+ {{}}
-struct S : __make_integer_seq<A, int, 42> { }; // expected-error 0+ {{}}
-S s;
+struct NoCommonType : __make_integer_seq<A, int, 42> { }; // expected-error 0+ {{}}
+NoCommonType s;
diff --git a/clang/test/SemaCXX/crash-GH49103-2.cpp b/clang/test/SemaCXX/crash-GH49103-2.cpp
index 4c17a054c73af..d2a831e43b803 100644
--- a/clang/test/SemaCXX/crash-GH49103-2.cpp
+++ b/clang/test/SemaCXX/crash-GH49103-2.cpp
@@ -9,5 +9,5 @@
 // https://github.com/llvm/llvm-project/issues/49103
 
 template<class> struct A; // expected-note 0+ {{}}
-struct S : __make_integer_seq<A, int, 42> { }; // expected-error 0+ {{}}
-S s;
+struct NoCommonType : __make_integer_seq<A, int, 42> { }; // expected-error 0+ {{}}
+NoCommonType s;
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 0000000000000..d9ce9c15dad4d
--- /dev/null
+++ b/clang/test/SemaCXX/type-trait-common-type.cpp
@@ -0,0 +1,125 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 %s
+// RUN: %clang_cc1 -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>));
+
+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/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp
index 7adbf4aad7afe..efcbdd18b6e75 100644
--- a/clang/test/SemaCXX/type-traits.cpp
+++ b/clang/test/SemaCXX/type-traits.cpp
@@ -4135,7 +4135,7 @@ void test_errors() {
 }
 }
 
-struct S {};
+struct NoCommonType {};
 template <class T> using remove_const_t = __remove_const(T);
 
 void check_remove_const() {
@@ -4157,14 +4157,14 @@ void check_remove_const() {
   static_assert(__is_same(remove_const_t<int (*const)()>, int (*)()));
   static_assert(__is_same(remove_const_t<int (&)()>, int (&)()));
 
-  static_assert(__is_same(remove_const_t<S>, S));
-  static_assert(__is_same(remove_const_t<const S>, S));
-  static_assert(__is_same(remove_const_t<volatile S>, volatile S));
-  static_assert(__is_same(remove_const_t<S *__restrict>, S *__restrict));
-  static_assert(__is_same(remove_const_t<const volatile S>, volatile S));
-  static_assert(__is_same(remove_const_t<S *const volatile __restrict>, S *volatile __restrict));
-  static_assert(__is_same(remove_const_t<int S::*const>, int S::*));
-  static_assert(__is_same(remove_const_t<int (S::*const)()>, int(S::*)()));
+  static_assert(__is_same(remove_const_t<NoCommonType>, NoCommonType));
+  static_assert(__is_same(remove_const_t<const NoCommonType>, NoCommonType));
+  static_assert(__is_same(remove_const_t<volatile NoCommonType>, volatile NoCommonType));
+  static_assert(__is_same(remove_const_t<NoCommonType *__restrict>, NoCommonType *__restrict));
+  static_assert(__is_same(remove_const_t<const volatile NoCommonType>, volatile NoCommonType));
+  static_assert(__is_same(remove_const_t<NoCommonType *const volatile __restrict>, NoCommonType *volatile __restrict));
+  static_assert(__is_same(remove_const_t<int NoCommonType::*const>, int NoCommonType::*));
+  static_assert(__is_same(remove_const_t<int (NoCommonType::*const)()>, int(NoCommonType::*)()));
 }
 
 template <class T> using remove_restrict_t = __remove_restrict(T);
@@ -4186,13 +4186,13 @@ void check_remove_restrict() {
   static_assert(__is_same(remove_restrict_t<int (*const volatile)()>, int (*const volatile)()));
   static_assert(__is_same(remove_restrict_t<int (&)()>, int (&)()));
 
-  static_assert(__is_same(remove_restrict_t<S>, S));
-  static_assert(__is_same(remove_restrict_t<const S>, const S));
-  static_assert(__is_same(remove_restrict_t<volatile S>, volatile S));
-  static_assert(__is_same(remove_restrict_t<S *__restrict>, S *));
-  static_assert(__is_same(remove_restrict_t<S *const volatile __restrict>, S *const volatile));
-  static_assert(__is_same(remove_restrict_t<int S::*__restrict>, int S::*));
-  static_assert(__is_same(remove_restrict_t<int (S::*const volatile)()>, int(S::*const volatile)()));
+  static_assert(__is_same(remove_restrict_t<NoCommonType>, NoCommonType));
+  static_assert(__is_same(remove_restrict_t<const NoCommonType>, const NoCommonType));
+  static_assert(__is_same(remove_restrict_t<volatile NoCommonType>, volatile NoCommonType));
+  static_assert(__is_same(remove_restrict_t<NoCommonType *__restrict>, NoCommonType *));
+  static_assert(__is_same(remove_restrict_t<NoCommonType *const volatile __restrict>, NoCommonType *const volatile));
+  static_assert(__is_same(remove_restrict_t<int NoCommonType::*__restrict>, int NoCommonType::*));
+  static_assert(__is_same(remove_restrict_t<int (NoCommonType::*const volatile)()>, int(NoCommonType::*const volatile)()));
 }
 
 template <class T> using remove_volatile_t = __remove_volatile(T);
@@ -4217,12 +4217,12 @@ void check_remove_volatile() {
   static_assert(__is_same(remove_volatile_t<int (*volatile)()>, int (*)()));
   static_assert(__is_same(remove_volatile_t<int (&)()>, int (&)()));
 
-  static_assert(__is_same(remove_volatile_t<S>, S));
-  static_assert(__is_same(remove_volatile_t<const S>, const S));
-  static_assert(__is_same(remove_volatile_t<volatile S>, S));
-  static_assert(__is_same(remove_volatile_t<const volatile S>, const S));
-  static_assert(__is_same(remove_volatile_t<int S::*volatile>, int S::*));
-  static_assert(__is_same(remove_volatile_t<int (S::*volatile)()>, int(S::*)()));
+  static_assert(__is_same(remove_volatile_t<NoCommonType>, NoCommonType));
+  static_assert(__is_same(remove_volatile_t<const NoCommonType>, const NoCommonType));
+  static_assert(__is_same(remove_volatile_t<volatile NoCommonType>, NoCommonType));
+  static_assert(__is_same(remove_volatile_t<const volatile NoCommonType>, const NoCommonType));
+  static_assert(__is_same(remove_volatile_t<int NoCommonType::*volatile>, int NoCommonType::*));
+  static_assert(__is_same(remove_volatile_t<int (NoCommonType::*volatile)()>, int(NoCommonType::*)()));
 }
 
 template <class T> using remove_cv_t = __remove_cv(T);
@@ -4247,12 +4247,12 @@ void check_remove_cv() {
   static_assert(__is_same(remove_cv_t<int (*const volatile)()>, int (*)()));
   static_assert(__is_same(remove_cv_t<int (&)()>, int (&)()));
 
-  static_assert(__is_same(remove_cv_t<S>, S));
-  static_assert(__is_same(remove_cv_t<const S>, S));
-  static_assert(__is_same(remove_cv_t<volatile S>, S));
-  static_assert(__is_same(remove_cv_t<const volatile S>, S));
-  static_assert(__is_same(remove_cv_t<int S::*const volatile>, int S::*));
-  static_assert(__is_same(remove_cv_t<int (S::*const volatile)()>, int(S::*)()));
+  static_assert(__is_same(remove_cv_t<NoCommonType>, NoCommonType));
+  static_assert(__is_same(remove_cv_t<const NoCommonType>, NoCommonType));
+  static_assert(__is_same(remove_cv_t<volatile NoCommonType>, NoCommonType));
+  static_assert(__is_same(remove_cv_t<const volatile NoCommonType>, NoCommonType));
+  static_assert(__is_same(remove_cv_t<int NoCommonType::*const volatile>, int NoCommonType::*));
+  static_assert(__is_same(remove_cv_t<int (NoCommonType::*const volatile)()>, int(NoCommonType::*)()));
 }
 
 template <class T> using add_pointer_t = __add_pointer(T);
@@ -4273,15 +4273,15 @@ void add_pointer() {
   static_assert(__is_same(add_pointer_t<int (*)()>, int (**)()));
   static_assert(__is_same(add_pointer_t<int (&)()>, int (*)()));
 
-  static_assert(__is_same(add_pointer_t<S>, S *));
-  static_assert(__is_same(add_pointer_t<const S>, const S *));
-  static_assert(__is_same(add_pointer_t<volatile S>, volatile S *));
-  static_assert(__is_same(add_pointer_t<const volatile S>, const volatile S *));
-  static_assert(__is_same(add_pointer_t<int S::*>, int S::**));
-  static_assert(__is_same(add_pointer_t<int (S::*)()>, int(S::**)()));
+  static_assert(__is_same(add_pointer_t<NoCommonType>, NoCommonType *));
+  static_assert(__is_same(add_pointer_t<const NoCommonType>, const NoCommonType *));
+  static_assert(__is_same(add_pointer_t<volatile NoCommonType>, volatile NoCommonType *));
+  static_assert(__is_same(add_pointer_t<const volatile NoCommonType>, const volatile NoCommonType *));
+  static_assert(__is_same(add_pointer_t<int NoCommonType::*>, int NoCommonType::**));
+  static_assert(__is_same(add_pointer_t<int (NoCommonType::*)()>, int(NoCommonType::**)()));
 
   static_assert(__is_same(add_pointer_t<int __attribute__((address_space(1)))>, int __attribute__((address_space(1))) *));
-  static_assert(__is_same(add_pointer_t<S __attribute__((address_space(2)))>, S __attribute__((address_space(2))) *));
+  static_assert(__is_same(add_pointer_t<NoCommonType __attribute__((address_space(2)))>, NoCommonType __attribute__((address_space(2))) *));
 }
 
 template <class T> using remove_pointer_t = __remove_pointer(T);
@@ -4308,15 +4308,15 @@ void remove_pointer() {
   static_assert(__is_same(remove_pointer_t<int (*)()>, int()));
   static_assert(__is_same(remove_pointer_t<int (&)()>, int (&)()));
 
-  static_assert(__is_same(remove_pointer_t<S>, S));
-  static_assert(__is_same(remove_pointer_t<const S>, const S));
-  static_assert(__is_same(remove_pointer_t<volatile S>, volatile S));
-  static_assert(__is_same(remove_pointer_t<const volatile S>, const volatile S));
-  static_assert(__is_same(remove_pointer_t<int S::*>, int S::*));
-  static_assert(__is_same(remove_pointer_t<int (S::*)()>, int(S::*)()));
+  static_assert(__is_same(remove_pointer_t<NoCommonType>, NoCommonType));
+  static_assert(__is_same(remove_pointer_t<const NoCommonType>, const NoCommonType));
+  static_assert(__is_same(remove_pointer_t<volatile NoCommonType>, volatile NoCommonType));
+  static_assert(__is_same(remove_pointer_t<const volatile NoCommonType>, const volatile NoCommonType));
+  static_assert(__is_same(remove_pointer_t<int NoCommonType::*>, int NoCommonType::*));
+  static_assert(__is_same(remove_pointer_t<int (NoCommonType::*)()>, int(NoCommonType::*)()));
 
   static_assert(__is_same(remove_pointer_t<int __attribute__((address_space(1))) *>, int __attribute__((address_space(1)))));
-  static_assert(__is_same(remove_pointer_t<S __attribute__((address_space(2))) *>, S  __attribute__((address_space(2)))));
+  static_assert(__is_same(remove_pointer_t<NoCommonType __attribute__((address_space(2))) *>, NoCommonType  __attribute__((address_space(2)))));
 
   static_assert(__is_same(remove_pointer_t<int (^)(char)>, int (^)(char)));
 }
@@ -4339,12 +4339,12 @@ void add_lvalue_reference() {
   static_assert(__is_same(add_lvalue_reference_t<int (*)()>, int (*&)()));
   static_assert(__is_same(add_lvalue_reference_t<int (&)()>, int (&)()));
 
-  static_assert(__is_same(add_lvalue_reference_t<S>, S &));
-  static_assert(__is_same(add_lvalue_reference_t<const S>, const S &));
-  static_assert(__is_same(add_lvalue_reference_t<volatile S>, volatile S &));
-  static_assert(__is_same(add_lvalue_reference_t<const volatile S>, const volatile S &));
-  static_assert(__is_same(add_lvalue_reference_t<int S::*>, int S::*&));
-  static_assert(__is_same(add_lvalue_reference_t<int (S::*)()>, int(S::*&)()));
+  static_assert(__is_same(add_lvalue_reference_t<NoCommonType>, NoCommonType &));
+  static_assert(__is_same(add_lvalue_reference_t<const NoCommonType>, const NoCommonType &));
+  static_assert(__is_same(add_lvalue_reference_t<volatile NoCommonType>, volatile NoCommonType &));
+  static_assert(__is_same(add_lvalue_reference_t<const volatile NoCommonType>, const volatile NoCommonType &));
+  static_assert(__is_same(add_lvalue_reference_t<int NoCommonType::*>, int NoCommonType::*&));
+  static_assert(__is_same(add_lvalue_reference_t<int (NoCommonType::*)()>, int(NoCommonType::*&)()));
 }
 
 template <class T> using add_rvalue_reference_t = __add_rvalue_reference(T);
@@ -4365,12 +4365,12 @@ void add_rvalue_reference() {
   static_assert(__is_same(add_rvalue_reference_t<int (*)()>, int (*&&)()));
   static_assert(__is_same(add_rvalue_reference_t<int (&)()>, int (&)())); // reference collapsing
 
-  static_assert(__is_same(add_rvalue_reference_t<S>, S &&));
-  static_assert(__is_same(add_rvalue_reference_t<const S>, const S &&));
-  static_assert(__is_same(add_rvalue_reference_t<volatile S>, volatile S &&));
-  static_assert(__is_same(add_rvalue_reference_t<const volatile S>, const volatile S &&));
-  static_assert(__is_same(add_rvalue_reference_t<int S::*>, int S::*&&));
-  static_assert(__is_same(add_rvalue_reference_t<int (S::*)()>, int(S::* &&)()));
+  static_assert(__is_same(add_rvalue_reference_t<NoCommonType>, NoCommonType &&));
+  static_assert(__is_same(add_rvalue_reference_t<const NoCommonType>, const NoCommonType &&));
+  static_assert(__is_same(add_rvalue_reference_t<volatile NoCommonType>, volatile NoCommonType &&));
+  static_assert(__is_same(add_rvalue_reference_t<const volatile NoCommonType>, const volatile NoCommonType &&));
+  static_assert(__is_same(add_rvalue_reference_t<int NoCommonType::*>, int NoCommonType::*&&));
+  static_assert(__is_same(add_rvalue_reference_t<int (NoCommonType::*)()>, int(NoCommonType::* &&)()));
 }
 
 template <class T> using remove_reference_t = __remove_reference_t(T);
@@ -4393,21 +4393,21 @@ void check_remove_reference() {
   static_assert(__is_same(remove_reference_t<int (*const volatile)()>, int (*const volatile)()));
   static_assert(__is_same(remove_reference_t<int (&)()>, int()));
 
-  static_assert(__is_same(remove_reference_t<S>, S));
-  static_assert(__is_same(remove_reference_t<S &>, S));
-  static_assert(__is_same(remove_reference_t<S &&>, S));
-  static_assert(__is_same(remove_reference_t<const S>, const S));
-  static_assert(__is_same(remove_reference_t<const S &>, const S));
-  static_assert(__is_same(remove_reference_t<const S &&>, const S));
-  static_assert(__is_same(remove_reference_t<volatile S>, volatile S));
-  static_assert(__is_same(remove_reference_t<volatile S &>, volatile S));
-  static_assert(__is_same(remove_reference_t<volatile S &&>, volatile S));
-  static_assert(__is_same(remove_reference_t<const volatile S>, const volatile S));
-  static_assert(__is_same(remove_reference_t<const volatile S &>, const volatile S));
-  static_assert(__is_same(remove_reference_t<const volatile S &&>, const volatile S));
-  static_assert(__is_same(remove_reference_t<int S::*const volatile &>, int S::*const volatile));
-  static_assert(__is_same(remove_reference_t<int (S::*const volatile &)()>, int(S::*const volatile)()));
-  static_assert(__is_same(remove_reference_t<int (S::*const volatile &&)() &>, int(S::*const volatile)() &));
+  static_assert(__is_same(remove_reference_t<NoCommonType>, NoCommonType));
+  static_assert(__is_same(remove_reference_t<NoCommonType &>, NoCommonType));
+  static_assert(__is_same(remove_reference_t<NoCommonType &&>, NoCommonType));
+  static_assert(__is_same(remove_reference_t<const NoCommonType>, const NoCommonType));
+  static_assert(__is_same(remove_reference_t<const NoCommonType &>, const NoCommonType));
+  static_assert(__is_same(remove_reference_t<const NoCommonType &&>, const NoCommonType));
+  static_assert(__is_same(remove_reference_t<volatile NoCommonType>, volatile NoCommonType));
+  static_assert(__is_same(remove_reference_t<volatile NoCommonType &>, volatile NoCommonType));
+  static_assert(__is_same(remove_reference_t<volatile NoCommonType &&>, volatile NoCommonType));
+  static_assert(__is_same(remove_reference_t<const volatile NoCommonType>, const volatile NoCommonType));
+  static_assert(__is_same(remove_reference_t<const volatile NoCommonType &>, const volatile NoCommonType));
+  static_assert(__is_same(remove_reference_t<const volatile NoCommonType &&>, const volatile NoCommonType));
+  static_assert(__is_same(remove_reference_t<int NoCommonType::*const volatile &>, int NoCommonType::*const volatile));
+  static_assert(__is_same(remove_reference_t<int (NoCommonType::*const volatile &)()>, int(NoCommonType::*const volatile)()));
+  static_assert(__is_same(remove_reference_t<int (NoCommonType::*const volatile &&)() &>, int(NoCommonType::*const volatile)() &));
 }
 
 template <class T> using remove_cvref_t = __remove_cvref(T);
@@ -4432,22 +4432,22 @@ void check_remove_cvref() {
   static_assert(__is_same(remove_cvref_t<int (*const volatile)()>, int (*)()));
   static_assert(__is_same(remove_cvref_t<int (&)()>, int()));
 
-  static_assert(__is_same(remove_cvref_t<S>, S));
-  static_assert(__is_same(remove_cvref_t<S &>, S));
-  static_assert(__is_same(remove_cvref_t<S &&>, S));
-  static_assert(__is_same(remove_cvref_t<const S>, S));
-  static_assert(__is_same(remove_cvref_t<const S &>, S));
-  static_assert(__is_same(remove_cvref_t<const S &&>, S));
-  static_assert(__is_same(remove_cvref_t<volatile S>, S));
-  static_assert(__is_same(remove_cvref_t<volatile S &>, S));
-  static_assert(__is_same(remove_cvref_t<volatile S &&>, S));
-  static_assert(__is_same(remove_cvref_t<const volatile S>, S));
-  static_assert(__is_same(remove_cvref_t<const volatile S &>, S));
-  static_assert(__is_same(remove_cvref_t<const volatile S &&>, S));
-  static_assert(__is_same(remove_cvref_t<int S::*const volatile>, int S::*));
-  static_assert(__is_same(remove_cvref_t<int (S::*const volatile)()>, int(S::*)()));
-  static_assert(__is_same(remove_cvref_t<int (S::*const volatile)() &>, int(S::*)() &));
-  static_assert(__is_same(remove_cvref_t<int (S::*const volatile)() &&>, int(S::*)() &&));
+  static_assert(__is_same(remove_cvref_t<NoCommonType>, NoCommonType));
+  static_assert(__is_same(remove_cvref_t<NoCommonType &>, NoCommonType));
+  static_assert(__is_same(remove_cvref_t<NoCommonType &&>, NoCommonType));
+  static_assert(__is_same(remove_cvref_t<const NoCommonType>, NoCommonType));
+  static_assert(__is_same(remove_cvref_t<const NoCommonType &>, NoCommonType));
+  static_assert(__is_same(remove_cvref_t<const NoCommonType &&>, NoCommonType));
+  static_assert(__is_same(remove_cvref_t<volatile NoCommonType>, NoCommonType));
+  static_assert(__is_same(remove_cvref_t<volatile NoCommonType &>, NoCommonType));
+  static_assert(__is_same(remove_cvref_t<volatile NoCommonType &&>, NoCommonType));
+  static_assert(__is_same(remove_cvref_t<const volatile NoCommonType>, NoCommonType));
+  static_assert(__is_same(remove_cvref_t<const volatile NoCommonType &>, NoCommonType));
+  static_assert(__is_same(remove_cvref_t<const volatile NoCommonType &&>, NoCommonType));
+  static_assert(__is_same(remove_cvref_t<int NoCommonType::*const volatile>, int NoCommonType::*));
+  static_assert(__is_same(remove_cvref_t<int (NoCommonType::*const volatile)()>, int(NoCommonType::*)()));
+  static_assert(__is_same(remove_cvref_t<int (NoCommonType::*const volatile)() &>, int(NoCommonType::*)() &));
+  static_assert(__is_same(remove_cvref_t<int (NoCommonType::*const volatile)() &&>, int(NoCommonType::*)() &&));
 }
 
 template <class T> using decay_t = __decay(T);
@@ -4477,28 +4477,28 @@ void check_decay() {
   static_assert(__is_same(decay_t<IntAr>, int *));
   static_assert(__is_same(decay_t<IntArNB>, int *));
 
-  static_assert(__is_same(decay_t<S>, S));
-  static_assert(__is_same(decay_t<S &>, S));
-  static_assert(__is_same(decay_t<S &&>, S));
-  static_assert(__is_same(decay_t<const S>, S));
-  static_assert(__is_same(decay_t<const S &>, S));
-  static_assert(__is_same(decay_t<const S &&>, S));
-  static_assert(__is_same(decay_t<volatile S>, S));
-  static_assert(__is_same(decay_t<volatile S &>, S));
-  static_assert(__is_same(decay_t<volatile S &&>, S));
-  static_assert(__is_same(decay_t<const volatile S>, S));
-  static_assert(__is_same(decay_t<const volatile S &>, S));
-  static_assert(__is_same(decay_t<const volatile S &&>, S));
-  static_assert(__is_same(decay_t<int S::*const volatile>, int S::*));
-  static_assert(__is_same(decay_t<int (S::*const volatile)()>, int(S::*)()));
-  static_assert(__is_same(decay_t<int S::*const volatile &>, int S::*));
-  static_assert(__is_same(decay_t<int (S::*const volatile &)()>, int(S::*)()));
-  static_assert(__is_same(decay_t<int S::*const volatile &&>, int S::*));
+  static_assert(__is_same(decay_t<NoCommonType>, NoCommonType));
+  static_assert(__is_same(decay_t<NoCommonType &>, NoCommonType));
+  static_assert(__is_same(decay_t<NoCommonType &&>, NoCommonType));
+  static_assert(__is_same(decay_t<const NoCommonType>, NoCommonType));
+  static_assert(__is_same(decay_t<const NoCommonType &>, NoCommonType));
+  static_assert(__is_same(decay_t<const NoCommonType &&>, NoCommonType));
+  static_assert(__is_same(decay_t<volatile NoCommonType>, NoCommonType));
+  static_assert(__is_same(decay_t<volatile NoCommonType &>, NoCommonType));
+  static_assert(__is_same(decay_t<volatile NoCommonType &&>, NoCommonType));
+  static_assert(__is_same(decay_t<const volatile NoCommonType>, NoCommonType));
+  static_assert(__is_same(decay_t<const volatile NoCommonType &>, NoCommonType));
+  static_assert(__is_same(decay_t<const volatile NoCommonType &&>, NoCommonType));
+  static_assert(__is_same(decay_t<int NoCommonType::*const volatile>, int NoCommonType::*));
+  static_assert(__is_same(decay_t<int (NoCommonType::*const volatile)()>, int(NoCommonType::*)()));
+  static_assert(__is_same(decay_t<int NoCommonType::*const volatile &>, int NoCommonType::*));
+  static_assert(__is_same(decay_t<int (NoCommonType::*const volatile &)()>, int(NoCommonType::*)()));
+  static_assert(__is_same(decay_t<int NoCommonType::*const volatile &&>, int NoCommonType::*));
 }
 
 template <class T> struct CheckAbominableFunction {};
 template <class M>
-struct CheckAbominableFunction<M S::*> {
+struct CheckAbominableFunction<M NoCommonType::*> {
   static void checks() {
     static_assert(__is_same(add_lvalue_reference_t<M>, M));
     static_assert(__is_same(add_pointer_t<M>, M));
@@ -4516,17 +4516,17 @@ struct CheckAbominableFunction<M S::*> {
 };
 
 void check_abominable_function() {
-  { CheckAbominableFunction<int (S::*)() &> x; }
-  { CheckAbominableFunction<int (S::*)() &&> x; }
-  { CheckAbominableFunction<int (S::*)() const> x; }
-  { CheckAbominableFunction<int (S::*)() const &> x; }
-  { CheckAbominableFunction<int (S::*)() const &&> x; }
-  { CheckAbominableFunction<int (S::*)() volatile> x; }
-  { CheckAbominableFunction<int (S::*)() volatile &> x; }
-  { CheckAbominableFunction<int (S::*)() volatile &&> x; }
-  { CheckAbominableFunction<int (S::*)() const volatile> x; }
-  { CheckAbominableFunction<int (S::*)() const volatile &> x; }
-  { CheckAbominableFunction<int (S::*)() const volatile &&> x; }
+  { CheckAbominableFunction<int (NoCommonType::*)() &> x; }
+  { CheckAbominableFunction<int (NoCommonType::*)() &&> x; }
+  { CheckAbominableFunction<int (NoCommonType::*)() const> x; }
+  { CheckAbominableFunction<int (NoCommonType::*)() const &> x; }
+  { CheckAbominableFunction<int (NoCommonType::*)() const &&> x; }
+  { CheckAbominableFunction<int (NoCommonType::*)() volatile> x; }
+  { CheckAbominableFunction<int (NoCommonType::*)() volatile &> x; }
+  { CheckAbominableFunction<int (NoCommonType::*)() volatile &&> x; }
+  { CheckAbominableFunction<int (NoCommonType::*)() const volatile> x; }
+  { CheckAbominableFunction<int (NoCommonType::*)() const volatile &> x; }
+  { CheckAbominableFunction<int (NoCommonType::*)() const volatile &&> x; }
 }
 
 template <class T> using make_signed_t = __make_signed(T);
@@ -4756,9 +4756,9 @@ void remove_extent() {
   static_assert(__is_same(remove_extent_t<int (*)()>, int (*)()));
   static_assert(__is_same(remove_extent_t<int (&)()>, int (&)()));
 
-  static_assert(__is_same(remove_extent_t<S>, S));
-  static_assert(__is_same(remove_extent_t<int S::*>, int S::*));
-  static_assert(__is_same(remove_extent_t<int (S::*)()>, int(S::*)()));
+  static_assert(__is_same(remove_extent_t<NoCommonType>, NoCommonType));
+  static_assert(__is_same(remove_extent_t<int NoCommonType::*>, int NoCommonType::*));
+  static_assert(__is_same(remove_extent_t<int (NoCommonType::*)()>, int(NoCommonType::*)()));
 
   using SomeArray = int[1][2];
   static_assert(__is_same(remove_extent_t<const SomeArray>, const int[2]));
@@ -4795,9 +4795,9 @@ void remove_all_extents() {
   static_assert(__is_same(remove_all_extents_t<int (*)()>, int (*)()));
   static_assert(__is_same(remove_all_extents_t<int (&)()>, int (&)()));
 
-  static_assert(__is_same(remove_all_extents_t<S>, S));
-  static_assert(__is_same(remove_all_extents_t<int S::*>, int S::*));
-  static_assert(__is_same(remove_all_extents_t<int (S::*)()>, int(S::*)()));
+  static_assert(__is_same(remove_all_extents_t<NoCommonType>, NoCommonType));
+  static_assert(__is_same(remove_all_extents_t<int NoCommonType::*>, int NoCommonType::*));
+  static_assert(__is_same(remove_all_extents_t<int (NoCommonType::*)()>, int(NoCommonType::*)()));
 
   using SomeArray = int[1][2];
   static_assert(__is_same(remove_all_extents_t<const SomeArray>, const int));
diff --git a/libcxx/include/__type_traits/common_type.h b/libcxx/include/__type_traits/common_type.h
index f6bd9ed71b7a4..8240250742776 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,6 +25,12 @@
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+#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>
@@ -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;



More information about the libcxx-commits mailing list