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

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Fri Dec 27 02:27:24 PST 2024


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

None

>From e0db7316b2705398a2ac7e69cd1e3e83ece52613 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Fri, 27 Sep 2024 22:11:14 +0200
Subject: [PATCH] [Clang] Add __builtin_common_reference

---
 clang/docs/LanguageExtensions.rst             |  17 ++
 clang/include/clang/AST/ASTContext.h          |  13 +
 clang/include/clang/AST/ASTNodeTraverser.h    |   4 +
 clang/include/clang/AST/DeclID.h              |   3 +
 clang/include/clang/AST/DeclTemplate.h        |  37 +++
 clang/include/clang/AST/RecursiveASTVisitor.h |   4 +
 clang/include/clang/Basic/Builtins.h          |   3 +
 clang/include/clang/Basic/DeclNodes.td        |   1 +
 clang/include/clang/Sema/Sema.h               |  19 ++
 clang/lib/AST/ASTContext.cpp                  |  31 ++
 clang/lib/AST/ASTImporter.cpp                 |   3 +
 clang/lib/AST/DeclBase.cpp                    |   1 +
 clang/lib/AST/DeclTemplate.cpp                | 102 +++++++
 clang/lib/CodeGen/CGDecl.cpp                  |   1 +
 clang/lib/Lex/PPMacroExpansion.cpp            |   1 +
 clang/lib/Sema/SemaExprCXX.cpp                |  92 +-----
 clang/lib/Sema/SemaLookup.cpp                 |   4 +
 clang/lib/Sema/SemaTemplate.cpp               | 275 ++++++++++++++++--
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |   5 +
 clang/lib/Sema/SemaType.cpp                   |  73 +++++
 clang/lib/Serialization/ASTCommon.cpp         |   1 +
 clang/lib/Serialization/ASTReader.cpp         |   6 +
 clang/lib/Serialization/ASTWriter.cpp         |   2 +
 .../SemaCXX/type-trait-common-reference.cpp   | 120 ++++++++
 clang/tools/libclang/CIndex.cpp               |   1 +
 .../include/__type_traits/common_reference.h  |  36 ++-
 26 files changed, 738 insertions(+), 117 deletions(-)
 create mode 100644 clang/test/SemaCXX/type-trait-common-reference.cpp

diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst
index 3d4f68b818bce7..09311f3e1233d9 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -1546,6 +1546,23 @@ Builtin type aliases
 
 Clang provides a few builtin aliases to improve the throughput of certain metaprogramming facilities.
 
+__builtin_common_reference
+--------------------------
+
+.. code-block:: c++
+
+  template <template <class, class, template <class> class, template <class> class> class BasicCommonReferenceT,
+            template <class... Args> CommonTypeT,
+            template <class> HasTypeMember,
+            class HasNoTypeMember,
+            class... Ts>
+  using __builtin_common_reference = ...;
+
+This alias is used for implementing ``std::common_refernce``. If ``std::common_reference`` should contain a ``type``
+member, it is an alias to ``HasTypeMember<TheCommonReference>``. Otherwse it is an alias to ``HasNoTypeMember``. The
+``CommonTypeT`` is usually ``std::common_type_t``. ``BasicCommonReferenceT`` is usually an alias template to
+``basic_common_reference<T, U, TX, UX>::type``.
+
 __builtin_common_type
 ---------------------
 
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 1e89a6805ce9c6..3c18e73cdc391a 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -419,6 +419,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// The identifier '__builtin_common_type'.
   mutable IdentifierInfo *BuiltinCommonTypeName = nullptr;
 
+  /// The identifier '__builtin_common_reference'.
+  mutable IdentifierInfo *BuiltinCommonReferenceName = nullptr;
+
   QualType ObjCConstantStringType;
   mutable RecordDecl *CFConstantStringTagDecl = nullptr;
   mutable TypedefDecl *CFConstantStringTypeDecl = nullptr;
@@ -627,6 +630,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
   mutable BuiltinTemplateDecl *MakeIntegerSeqDecl = nullptr;
   mutable BuiltinTemplateDecl *TypePackElementDecl = nullptr;
   mutable BuiltinTemplateDecl *BuiltinCommonTypeDecl = nullptr;
+  mutable BuiltinTemplateDecl *BuiltinCommonReferenceDecl = nullptr;
+  mutable CVRefQualifyingTemplateDecl *CVRefQualifyingDecls[12] = {};
 
   /// The associated SourceManager object.
   SourceManager &SourceMgr;
@@ -1155,6 +1160,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
   BuiltinTemplateDecl *getMakeIntegerSeqDecl() const;
   BuiltinTemplateDecl *getTypePackElementDecl() const;
   BuiltinTemplateDecl *getBuiltinCommonTypeDecl() const;
+  BuiltinTemplateDecl *getBuiltinCommonReferenceDecl() const;
+  CVRefQualifyingTemplateDecl *getCVRefQualifyingAliasDecl(QualType From) const;
 
   // Builtin Types.
   CanQualType VoidTy;
@@ -2072,6 +2079,12 @@ class ASTContext : public RefCountedBase<ASTContext> {
     return BuiltinCommonTypeName;
   }
 
+  IdentifierInfo *getBuiltinCommonReferenceName() const {
+    if (!BuiltinCommonReferenceName)
+      BuiltinCommonReferenceName = &Idents.get("__builtin_common_reference");
+    return BuiltinCommonReferenceName;
+  }
+
   /// Retrieve the Objective-C "instancetype" type, if already known;
   /// otherwise, returns a NULL type;
   QualType getObjCInstanceType() {
diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h
index f5652b295de168..993821b43b683c 100644
--- a/clang/include/clang/AST/ASTNodeTraverser.h
+++ b/clang/include/clang/AST/ASTNodeTraverser.h
@@ -684,6 +684,10 @@ class ASTNodeTraverser
     dumpTemplateParameters(D->getTemplateParameters());
   }
 
+  void VisitCVRefQualifyingTemplateDecl(const CVRefQualifyingTemplateDecl *D) {
+    dumpTemplateParameters(D->getTemplateParameters());
+  }
+
   void
   VisitVarTemplateSpecializationDecl(const VarTemplateSpecializationDecl *D) {
     dumpTemplateArgumentList(D->getTemplateArgs());
diff --git a/clang/include/clang/AST/DeclID.h b/clang/include/clang/AST/DeclID.h
index 49964b43c7d1d8..b1b1d18d762fdc 100644
--- a/clang/include/clang/AST/DeclID.h
+++ b/clang/include/clang/AST/DeclID.h
@@ -86,6 +86,9 @@ enum PredefinedDeclIDs {
   /// The internal '__builtin_common_type' template.
   PREDEF_DECL_COMMON_TYPE_ID,
 
+  /// The internal '__builtin_common_reference' template.
+  PREDEF_DECL_COMMON_REFERENCE_ID,
+
   /// The number of declaration IDs that are predefined.
   NUM_PREDEF_DECL_IDS
 };
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index d3a466a8617bb1..b765e0d17c7119 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -1777,6 +1777,43 @@ class BuiltinTemplateDecl : public TemplateDecl {
   BuiltinTemplateKind getBuiltinTemplateKind() const { return BTK; }
 };
 
+class CVRefQualifyingTemplateDecl : public TemplateDecl {
+public:
+  // LValueRef and RValueRef are mutually exclusive
+  enum CVRefQuals {
+    None = 0x0,
+    Const = 0x1,
+    Volatile = 0x2,
+    LValueRef = 0x4,
+    RValueRef = 0x8,
+    LLVM_BITMASK_LARGEST_ENUMERATOR,
+  };
+
+private:
+  CVRefQuals Quals;
+
+  CVRefQualifyingTemplateDecl(const ASTContext &C, DeclContext *DC,
+                              CVRefQuals Quals);
+
+  void anchor() override;
+
+public:
+  // Implement isa/cast/dyncast support
+  static bool classof(const Decl *D) { return classofKind(D->getKind()); }
+  static bool classofKind(Kind K) { return K == CVRefQualifyingTemplate; }
+
+  static CVRefQualifyingTemplateDecl *Create(const ASTContext &C,
+                                             DeclContext *DC, CVRefQuals Ref) {
+    return new (C, DC) CVRefQualifyingTemplateDecl(C, DC, Ref);
+  }
+
+  SourceRange getSourceRange() const override LLVM_READONLY {
+    return {};
+  }
+
+  CVRefQuals getQuals() { return Quals; }
+};
+
 /// Provides information about an explicit instantiation of a variable or class
 /// template.
 struct ExplicitInstantiationInfo {
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index d9a87b30062df8..5a2131b51cb0a7 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1960,6 +1960,10 @@ DEF_TRAVERSE_DECL(BuiltinTemplateDecl, {
   TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters()));
 })
 
+DEF_TRAVERSE_DECL(CVRefQualifyingTemplateDecl, {
+  TRY_TO(TraverseTemplateParameterListHelper(D->getTemplateParameters()));
+})
+
 template <typename Derived>
 bool RecursiveASTVisitor<Derived>::TraverseTemplateTypeParamDeclConstraints(
     const TemplateTypeParmDecl *D) {
diff --git a/clang/include/clang/Basic/Builtins.h b/clang/include/clang/Basic/Builtins.h
index e27d8ccce73664..17e616825f74b7 100644
--- a/clang/include/clang/Basic/Builtins.h
+++ b/clang/include/clang/Basic/Builtins.h
@@ -311,6 +311,9 @@ enum BuiltinTemplateKind : int {
 
   /// This names the __builtin_common_type BuiltinTemplateDecl.
   BTK__builtin_common_type,
+
+  /// This names the __builtin_common_reference BuiltinTemplateDecl.
+  BTK__builtin_common_reference,
 };
 
 } // end namespace clang
diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td
index 48396e85c5adac..3905504f0dfa7b 100644
--- a/clang/include/clang/Basic/DeclNodes.td
+++ b/clang/include/clang/Basic/DeclNodes.td
@@ -71,6 +71,7 @@ def Named : DeclNode<Decl, "named declarations", 1>;
       def TypeAliasTemplate : DeclNode<RedeclarableTemplate>;
     def TemplateTemplateParm : DeclNode<Template>;
     def BuiltinTemplate : DeclNode<Template>;
+    def CVRefQualifyingTemplate : DeclNode<Template>;
     def Concept : DeclNode<Template>;
   def BaseUsing : DeclNode<Named, "", 1>;
     def Using : DeclNode<BaseUsing>;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 5ee7ea48cc983c..6dbec5a4b69447 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -14897,15 +14897,34 @@ class Sema final : public SemaBase {
   QualType BuiltinDecay(QualType BaseType, SourceLocation Loc);
   QualType BuiltinAddReference(QualType BaseType, UTTKind UKind,
                                SourceLocation Loc);
+
+  QualType BuiltinAddRValueReference(QualType BaseType, SourceLocation Loc) {
+    return BuiltinAddReference(BaseType, UnaryTransformType::AddRvalueReference,
+                               Loc);
+  }
+
+  QualType BuiltinAddLValueReference(QualType BaseType, SourceLocation Loc) {
+    return BuiltinAddReference(BaseType, UnaryTransformType::AddLvalueReference,
+                               Loc);
+  }
+
   QualType BuiltinRemoveExtent(QualType BaseType, UTTKind UKind,
                                SourceLocation Loc);
   QualType BuiltinRemoveReference(QualType BaseType, UTTKind UKind,
                                   SourceLocation Loc);
+
+  QualType BuiltinRemoveCVRef(QualType BaseType, SourceLocation Loc) {
+    return BuiltinRemoveReference(BaseType, UTTKind::RemoveCVRef, Loc);
+  }
+
   QualType BuiltinChangeCVRQualifiers(QualType BaseType, UTTKind UKind,
                                       SourceLocation Loc);
   QualType BuiltinChangeSignedness(QualType BaseType, UTTKind UKind,
                                    SourceLocation Loc);
 
+  bool BuiltinIsConvertible(QualType From, QualType To, SourceLocation Loc,
+                            bool CheckNothrow = false);
+
   /// Ensure that the type T is a literal type.
   ///
   /// This routine checks whether the type @p T is a literal type. If @p T is an
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 6ec927e13a7552..5bd5cecf475052 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -1218,6 +1218,37 @@ BuiltinTemplateDecl *ASTContext::getBuiltinCommonTypeDecl() const {
   return BuiltinCommonTypeDecl;
 }
 
+BuiltinTemplateDecl *ASTContext::getBuiltinCommonReferenceDecl() const {
+  if (!BuiltinCommonReferenceDecl)
+    BuiltinCommonReferenceDecl = buildBuiltinTemplateDecl(
+        BTK__builtin_common_reference, getBuiltinCommonReferenceName());
+  return BuiltinCommonReferenceDecl;
+}
+
+CVRefQualifyingTemplateDecl *
+ASTContext::getCVRefQualifyingAliasDecl(QualType From) const {
+  using CVRefQuals = CVRefQualifyingTemplateDecl::CVRefQuals;
+
+  CVRefQuals Q = CVRefQuals::None;
+  if (From->isReferenceType()) {
+    Q |= From->isLValueReferenceType() ? CVRefQuals::LValueRef
+                                       : CVRefQuals::RValueRef;
+    From = From.getNonReferenceType();
+  }
+
+  if (From.isConstQualified())
+    Q |= CVRefQuals::Const;
+  if (From.isVolatileQualified())
+    Q |= CVRefQuals::Volatile;
+  auto *Decl = CVRefQualifyingDecls[Q];
+  if (!Decl)
+    Decl =
+        CVRefQualifyingTemplateDecl::Create(*this, getTranslationUnitDecl(), Q);
+  Decl->setImplicit();
+  getTranslationUnitDecl()->addDecl(Decl);
+  return Decl;
+}
+
 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 26d33b0d94795f..e06194ecd1a852 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -5469,6 +5469,9 @@ ExpectedDecl ASTNodeImporter::VisitBuiltinTemplateDecl(BuiltinTemplateDecl *D) {
   case BuiltinTemplateKind::BTK__builtin_common_type:
     ToD = Importer.getToContext().getBuiltinCommonTypeDecl();
     break;
+  case BuiltinTemplateKind::BTK__builtin_common_reference:
+    ToD = Importer.getToContext().getBuiltinCommonReferenceDecl();
+    break;
   }
   assert(ToD && "BuiltinTemplateDecl of unsupported kind!");
   Importer.MapImported(D, ToD);
diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp
index fb701f76231bcd..a5f99d5325f091 100644
--- a/clang/lib/AST/DeclBase.cpp
+++ b/clang/lib/AST/DeclBase.cpp
@@ -967,6 +967,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
 
     case UsingDirective:
     case BuiltinTemplate:
+    case CVRefQualifyingTemplate:
     case ClassTemplateSpecialization:
     case ClassTemplatePartialSpecialization:
     case VarTemplateSpecialization:
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 40ee3753c24227..635e2eb3480cd2 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -306,6 +306,7 @@ bool TemplateDecl::isTypeAlias() const {
   switch (getKind()) {
   case TemplateDecl::TypeAliasTemplate:
   case TemplateDecl::BuiltinTemplate:
+  case TemplateDecl::CVRefQualifyingTemplate:
     return true;
   default:
     return false;
@@ -1696,6 +1697,83 @@ static TemplateParameterList *createBuiltinCommonTypeList(const ASTContext &C,
       nullptr);
 }
 
+static TemplateTypeParmDecl *BICreateTemplateParm(const ASTContext &C,
+                                                  DeclContext *DC,
+                                                  unsigned Depth,
+                                                  unsigned Position,
+                                                  bool ParameterPack = false) {
+  return TemplateTypeParmDecl::Create(
+      C, DC, SourceLocation(), SourceLocation(), Depth, Position,
+      /*Id=*/nullptr, /*Typename=*/false, ParameterPack);
+}
+
+static TemplateTemplateParmDecl *
+BICreateTemplateTemplateParm(const ASTContext &C, DeclContext *DC,
+                             ArrayRef<NamedDecl *> Parms, unsigned Depth,
+                             unsigned Position, bool ParameterPack = false) {
+  auto *List = TemplateParameterList::Create(
+      C, SourceLocation(), SourceLocation(), Parms, SourceLocation(), nullptr);
+  return TemplateTemplateParmDecl::Create(C, DC, SourceLocation(), Depth,
+                                          Position, ParameterPack,
+                                          /*Id=*/nullptr, false, List);
+}
+
+static TemplateTemplateParmDecl *
+createBuiltinCommonReferenceBasicCommonReferenceT(const ASTContext &C,
+                                                  DeclContext *DC) {
+  auto *T = BICreateTemplateParm(C, DC, /*Depth=*/1, /*Position=*/0);
+  auto *U = BICreateTemplateParm(C, DC, /*Depth=*/1, /*Position=*/1);
+  auto *TXArg = BICreateTemplateParm(C, DC, /*Depth=*/2, /*Position=*/0);
+  auto *TX =
+      BICreateTemplateTemplateParm(C, DC, {TXArg}, /*Depth=*/1, /*Position=*/2);
+  auto *UXArg = BICreateTemplateParm(C, DC, /*Depth=*/2, /*Position=*/0);
+  auto *UX =
+      BICreateTemplateTemplateParm(C, DC, {UXArg}, /*Depth=*/1, /*Position=*/2);
+  return BICreateTemplateTemplateParm(C, DC, {T, U, TX, UX}, /*Depth=*/0,
+                                      /*Position=*/0);
+}
+
+static TemplateParameterList *createBuiltinCommonReferenceList(const ASTContext &C,
+                                                          DeclContext *DC) {
+  // template <class, class, template <class> class, template <class> class>
+  // class
+  auto *BasicCommonReference =
+      createBuiltinCommonReferenceBasicCommonReferenceT(C, DC);
+  // class... Args
+  auto *Args = BICreateTemplateParm(C, DC, /*Depth=*/1, /*Position=*/0,
+                                    /*ParameterPack=*/true);
+
+  // template <class... Args>
+  auto *BaseTemplate =
+      BICreateTemplateTemplateParm(C, DC, Args, /*Depth=*/0, /*Position=*/1);
+
+  // class TypeMember
+  auto *TypeMember = BICreateTemplateParm(C, DC, /*Depth=*/1, /*Position=*/0);
+
+  // template <class TypeMember>
+  auto *HasTypeMember = BICreateTemplateTemplateParm(
+      C, DC, TypeMember, /*Depth=*/0, /*Position=*/2);
+
+  // class HasNoTypeMember
+  auto *HasNoTypeMember =
+      BICreateTemplateParm(C, DC, /*Depth=*/0, /*Position=*/3);
+
+  // class... Ts
+  auto *Ts = BICreateTemplateParm(C, DC, /*Depth=*/0, /*Position=*/4,
+                                  /*ParameterPack=*/true);
+
+  // template <template <class, class, template <class> class, template <class>
+  //               class BasicCommonReference,
+  //           template <class... Args> class CommonType,
+  //           template <class TypeMember> class HasTypeMember,
+  //           class HasNoTypeMember,
+  //           class... Ts>
+  return TemplateParameterList::Create(
+      C, SourceLocation(), SourceLocation(),
+      {BasicCommonReference, BaseTemplate, HasTypeMember, HasNoTypeMember, Ts},
+      SourceLocation(), nullptr);
+}
+
 static TemplateParameterList *createBuiltinTemplateParameterList(
     const ASTContext &C, DeclContext *DC, BuiltinTemplateKind BTK) {
   switch (BTK) {
@@ -1705,6 +1783,8 @@ static TemplateParameterList *createBuiltinTemplateParameterList(
     return createTypePackElementParameterList(C, DC);
   case BTK__builtin_common_type:
     return createBuiltinCommonTypeList(C, DC);
+  case BTK__builtin_common_reference:
+    return createBuiltinCommonReferenceList(C, DC);
   }
 
   llvm_unreachable("unhandled BuiltinTemplateKind!");
@@ -1719,6 +1799,26 @@ BuiltinTemplateDecl::BuiltinTemplateDecl(const ASTContext &C, DeclContext *DC,
                    createBuiltinTemplateParameterList(C, DC, BTK)),
       BTK(BTK) {}
 
+void CVRefQualifyingTemplateDecl::anchor() {}
+
+static TemplateParameterList *
+createCVRefQualifyingTemplateParameterList(const ASTContext &C,
+                                           DeclContext *DC) {
+  auto *T = TemplateTypeParmDecl::Create(
+      C, DC, SourceLocation(), SourceLocation(),
+      /*Depth=*/0, /*Position=*/0, /*Id=*/nullptr, false, false);
+  return TemplateParameterList::Create(C, SourceLocation(), SourceLocation(),
+                                       {T}, SourceLocation(), nullptr);
+}
+
+CVRefQualifyingTemplateDecl::CVRefQualifyingTemplateDecl(const ASTContext &C,
+                                                         DeclContext *DC,
+                                                         CVRefQuals Quals)
+    : TemplateDecl(CVRefQualifyingTemplate, DC, SourceLocation(),
+                   DeclarationName(),
+                   createCVRefQualifyingTemplateParameterList(C, DC)),
+      Quals(Quals) {}
+
 TemplateParamObjectDecl *TemplateParamObjectDecl::Create(const ASTContext &C,
                                                          QualType T,
                                                          const APValue &V) {
@@ -1784,6 +1884,8 @@ TemplateParameterList *clang::getReplacedTemplateParameterList(Decl *D) {
     return cast<TypeAliasTemplateDecl>(D)->getTemplateParameters();
   case Decl::Kind::BuiltinTemplate:
     return cast<BuiltinTemplateDecl>(D)->getTemplateParameters();
+  case Decl::Kind::CVRefQualifyingTemplate:
+    return cast<CVRefQualifyingTemplateDecl>(D)->getTemplateParameters();
   case Decl::Kind::CXXDeductionGuide:
   case Decl::Kind::CXXConversion:
   case Decl::Kind::CXXConstructor:
diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp
index 47b21bc9f63f04..f7f9d553ad3a96 100644
--- a/clang/lib/CodeGen/CGDecl.cpp
+++ b/clang/lib/CodeGen/CGDecl.cpp
@@ -50,6 +50,7 @@ static_assert(clang::Sema::MaximumAlignment <= llvm::Value::MaximumAlignment,
 void CodeGenFunction::EmitDecl(const Decl &D) {
   switch (D.getKind()) {
   case Decl::BuiltinTemplate:
+  case Decl::CVRefQualifyingTemplate:
   case Decl::TranslationUnit:
   case Decl::ExternCContext:
   case Decl::Namespace:
diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp
index 347c13da0ad215..28db246c160fea 100644
--- a/clang/lib/Lex/PPMacroExpansion.cpp
+++ b/clang/lib/Lex/PPMacroExpansion.cpp
@@ -1834,6 +1834,7 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) {
               .Case("__make_integer_seq", getLangOpts().CPlusPlus)
               .Case("__type_pack_element", getLangOpts().CPlusPlus)
               .Case("__builtin_common_type", getLangOpts().CPlusPlus)
+              .Case("__builtin_common_reference", 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/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index e04ece0bda82eb..da2f9129a89e09 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -5742,76 +5742,6 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, TypeTrait UTT,
 static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceInfo *Lhs,
                                     const TypeSourceInfo *Rhs, SourceLocation KeyLoc);
 
-static ExprResult CheckConvertibilityForTypeTraits(
-    Sema &Self, const TypeSourceInfo *Lhs, const TypeSourceInfo *Rhs,
-    SourceLocation KeyLoc, llvm::BumpPtrAllocator &OpaqueExprAllocator) {
-
-  QualType LhsT = Lhs->getType();
-  QualType RhsT = Rhs->getType();
-
-  // C++0x [meta.rel]p4:
-  //   Given the following function prototype:
-  //
-  //     template <class T>
-  //       typename add_rvalue_reference<T>::type create();
-  //
-  //   the predicate condition for a template specialization
-  //   is_convertible<From, To> shall be satisfied if and only if
-  //   the return expression in the following code would be
-  //   well-formed, including any implicit conversions to the return
-  //   type of the function:
-  //
-  //     To test() {
-  //       return create<From>();
-  //     }
-  //
-  //   Access checking is performed as if in a context unrelated to To and
-  //   From. Only the validity of the immediate context of the expression
-  //   of the return-statement (including conversions to the return type)
-  //   is considered.
-  //
-  // We model the initialization as a copy-initialization of a temporary
-  // of the appropriate type, which for this expression is identical to the
-  // return statement (since NRVO doesn't apply).
-
-  // Functions aren't allowed to return function or array types.
-  if (RhsT->isFunctionType() || RhsT->isArrayType())
-    return ExprError();
-
-  // A function definition requires a complete, non-abstract return type.
-  if (!Self.isCompleteType(Rhs->getTypeLoc().getBeginLoc(), RhsT) ||
-      Self.isAbstractType(Rhs->getTypeLoc().getBeginLoc(), RhsT))
-    return ExprError();
-
-  // Compute the result of add_rvalue_reference.
-  if (LhsT->isObjectType() || LhsT->isFunctionType())
-    LhsT = Self.Context.getRValueReferenceType(LhsT);
-
-  // Build a fake source and destination for initialization.
-  InitializedEntity To(InitializedEntity::InitializeTemporary(RhsT));
-  Expr *From = new (OpaqueExprAllocator.Allocate<OpaqueValueExpr>())
-      OpaqueValueExpr(KeyLoc, LhsT.getNonLValueExprType(Self.Context),
-                      Expr::getValueKindForType(LhsT));
-  InitializationKind Kind =
-      InitializationKind::CreateCopy(KeyLoc, SourceLocation());
-
-  // Perform the initialization in an unevaluated context within a SFINAE
-  // trap at translation unit scope.
-  EnterExpressionEvaluationContext Unevaluated(
-      Self, Sema::ExpressionEvaluationContext::Unevaluated);
-  Sema::SFINAETrap SFINAE(Self, /*AccessCheckingSFINAE=*/true);
-  Sema::ContextRAII TUContext(Self, Self.Context.getTranslationUnitDecl());
-  InitializationSequence Init(Self, To, Kind, From);
-  if (Init.Failed())
-    return ExprError();
-
-  ExprResult Result = Init.Perform(Self, To, Kind, From);
-  if (Result.isInvalid() || SFINAE.hasErrorOccurred())
-    return ExprError();
-
-  return Result;
-}
-
 static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
                                      SourceLocation KWLoc,
                                      ArrayRef<TypeSourceInfo *> Args,
@@ -5933,9 +5863,8 @@ static bool EvaluateBooleanTypeTrait(Sema &S, TypeTrait Kind,
           S.Context.getPointerType(T.getNonReferenceType()));
       TypeSourceInfo *UPtr = S.Context.CreateTypeSourceInfo(
           S.Context.getPointerType(U.getNonReferenceType()));
-      return !CheckConvertibilityForTypeTraits(S, UPtr, TPtr, RParenLoc,
-                                               OpaqueExprAllocator)
-                  .isInvalid();
+      return S.BuiltinIsConvertible(UPtr->getType(), TPtr->getType(),
+                                    RParenLoc);
     }
 
     if (Kind == clang::TT_IsNothrowConstructible)
@@ -6166,20 +6095,9 @@ static bool EvaluateBinaryTypeTrait(Sema &Self, TypeTrait BTT, const TypeSourceI
   }
   case BTT_IsConvertible:
   case BTT_IsConvertibleTo:
-  case BTT_IsNothrowConvertible: {
-    if (RhsT->isVoidType())
-      return LhsT->isVoidType();
-    llvm::BumpPtrAllocator OpaqueExprAllocator;
-    ExprResult Result = CheckConvertibilityForTypeTraits(Self, Lhs, Rhs, KeyLoc,
-                                                         OpaqueExprAllocator);
-    if (Result.isInvalid())
-      return false;
-
-    if (BTT != BTT_IsNothrowConvertible)
-      return true;
-
-    return Self.canThrow(Result.get()) == CT_Cannot;
-  }
+  case BTT_IsNothrowConvertible:
+    return Self.BuiltinIsConvertible(LhsT, RhsT, KeyLoc,
+                                     BTT == BTT_IsNothrowConvertible);
 
   case BTT_IsAssignable:
   case BTT_IsNothrowAssignable:
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index e1171d4284c763..2028793d897067 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -936,6 +936,10 @@ bool Sema::LookupBuiltin(LookupResult &R) {
           R.addDecl(getASTContext().getBuiltinCommonTypeDecl());
           return true;
         }
+        if (II == getASTContext().getBuiltinCommonReferenceName()) {
+          R.addDecl(getASTContext().getBuiltinCommonReferenceDecl());
+          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 5e7a3c8484c88f..4706013ff79225 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -3072,6 +3072,33 @@ void Sema::NoteAllFoundTemplates(TemplateName Name) {
   }
 }
 
+static QualType InstantiateTemplate(Sema &S, TemplateName Template,
+                                    ArrayRef<TemplateArgument> Args,
+                                    SourceLocation Loc) {
+  TemplateArgumentListInfo ArgList;
+  for (auto Arg : Args) {
+    if (Arg.getKind() == TemplateArgument::Type) {
+      ArgList.addArgument(TemplateArgumentLoc(
+          Arg, S.Context.getTrivialTypeSourceInfo(Arg.getAsType())));
+    } else {
+      ArgList.addArgument(
+          S.getTrivialTemplateArgumentLoc(Arg, QualType(), Loc));
+    }
+  }
+
+  EnterExpressionEvaluationContext UnevaluatedContext(
+      S, Sema::ExpressionEvaluationContext::Unevaluated);
+  Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/true);
+  Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl());
+
+  QualType Instantiation = S.CheckTemplateIdType(Template, Loc, ArgList);
+
+  if (SFINAE.hasErrorOccurred())
+    return QualType();
+
+  return Instantiation;
+}
+
 static QualType builtinCommonTypeImpl(Sema &S, TemplateName BaseTemplate,
                                       SourceLocation TemplateLoc,
                                       ArrayRef<TemplateArgument> Ts) {
@@ -3082,24 +3109,7 @@ static QualType builtinCommonTypeImpl(Sema &S, TemplateName BaseTemplate,
     if (T1.getAsType()->isBuiltinType() && T2.getAsType()->isBuiltinType())
       return builtinCommonTypeImpl(S, BaseTemplate, TemplateLoc, {T1, T2});
 
-    TemplateArgumentListInfo Args;
-    Args.addArgument(TemplateArgumentLoc(
-        T1, S.Context.getTrivialTypeSourceInfo(T1.getAsType())));
-    Args.addArgument(TemplateArgumentLoc(
-        T2, S.Context.getTrivialTypeSourceInfo(T2.getAsType())));
-
-    EnterExpressionEvaluationContext UnevaluatedContext(
-        S, Sema::ExpressionEvaluationContext::Unevaluated);
-    Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/true);
-    Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl());
-
-    QualType BaseTemplateInst =
-        S.CheckTemplateIdType(BaseTemplate, TemplateLoc, Args);
-
-    if (SFINAE.hasErrorOccurred())
-      return QualType();
-
-    return BaseTemplateInst;
+    return InstantiateTemplate(S, BaseTemplate, {T1, T2}, TemplateLoc);
   };
 
   // Note A: For the common_type trait applied to a template parameter pack T of
@@ -3206,6 +3216,195 @@ static QualType builtinCommonTypeImpl(Sema &S, TemplateName BaseTemplate,
   }
 }
 
+static QualType CopyCV(QualType From, QualType To) {
+  if (From.isConstQualified())
+    To.addConst();
+  if (From.isVolatileQualified())
+    To.addVolatile();
+  return To;
+}
+
+// COND-RES(X, Y) be decltype(false ? declval<X(&)()>()() : declval<Y(&)()>()())
+static QualType CondRes(Sema &S, QualType X, QualType Y, SourceLocation Loc) {
+  EnterExpressionEvaluationContext UnevaluatedContext(
+      S, Sema::ExpressionEvaluationContext::Unevaluated);
+  Sema::SFINAETrap SFINAE(S, /*AccessCheckingSFINAE=*/true);
+  Sema::ContextRAII TUContext(S, S.Context.getTranslationUnitDecl());
+
+  // false
+  OpaqueValueExpr CondExpr(SourceLocation(), S.Context.BoolTy,
+                            VK_PRValue);
+  ExprResult Cond = &CondExpr;
+
+  // declval<X(&)()>()()
+  OpaqueValueExpr LHSExpr(Loc, X.getNonLValueExprType(S.Context),
+                              Expr::getValueKindForType(X));
+  ExprResult LHS = &LHSExpr;
+
+  // declval<Y(&)()>()()
+  OpaqueValueExpr RHSExpr(Loc, Y.getNonLValueExprType(S.Context),
+                              Expr::getValueKindForType(Y));
+  ExprResult RHS = &RHSExpr;
+
+  ExprValueKind VK = VK_PRValue;
+  ExprObjectKind OK = OK_Ordinary;
+
+  // decltype(false ? declval<X(&)()>()() : declval<Y(&)()>()())
+  QualType Result =
+      S.CheckConditionalOperands(Cond, LHS, RHS, VK, OK, Loc);
+
+  if (SFINAE.hasErrorOccurred())
+    return QualType();
+  if (VK == VK_LValue)
+    return S.BuiltinAddLValueReference(Result, Loc);
+  return Result;
+}
+
+static QualType CommonRef(Sema &S, QualType A, QualType B,
+                             SourceLocation Loc) {
+  // Given types A and B, let X be remove_reference_t<A>, let Y be
+  // remove_reference_t<B>, and let COMMON-​REF(A, B) be:
+  assert(A->isReferenceType() && B->isReferenceType() &&
+         "A and B have to be ref qualified for a COMMON-REF");
+  auto X = A.getNonReferenceType();
+  auto Y = B.getNonReferenceType();
+
+  // If A and B are both lvalue reference types, COMMON-REF(A, B) is
+  // COND-RES(COPYCV(X, Y) &, COPYCV(​Y, X) &) if that type exists and is a
+  // reference type.
+  if (A->isLValueReferenceType() && B->isLValueReferenceType()) {
+    auto CR = CondRes(S, S.BuiltinAddLValueReference(CopyCV(X, Y), Loc),
+                      S.BuiltinAddLValueReference(CopyCV(Y, X), Loc), Loc);
+    if (CR.isNull() || !CR->isReferenceType())
+      return QualType();
+    return CR;
+  }
+
+  // Otherwise, let C be remove_reference_t<COMMON-REF(X&, Y&)>&&. If A and B
+  // are both rvalue reference types, C is well-formed, and
+  // is_convertible_v<A, C> && is_convertible_v<B, C> is true, then
+  // COMMON-REF(A, B) is C.
+  if (A->isRValueReferenceType() && B->isRValueReferenceType()) {
+    auto C = CommonRef(S, S.BuiltinAddLValueReference(X, Loc),
+                       S.BuiltinAddRValueReference(Y, Loc), Loc)
+                 .getNonReferenceType();
+    if (!C.isNull() && S.BuiltinIsConvertible(A, C, Loc) &&
+        S.BuiltinIsConvertible(B, C, Loc))
+      return C;
+    return QualType();
+  }
+
+  // Otherwise, if A is an lvalue reference and B is an rvalue reference, then
+  // COMMON-REF(A, B) is COMMON-REF(B, A).
+  if (A->isLValueReferenceType() && B->isRValueReferenceType())
+    std::swap(A, B);
+
+  // Otherwise, let D be COMMON-REF(const X&, Y&). If A is an rvalue reference
+  // and B is an lvalue reference and D is well-formed and
+  // is_convertible_v<A, D> is true, then COMMON-REF(A, B) is D.
+  if (A->isRValueReferenceType() && B->isLValueReferenceType()) {
+    auto X2 = X;
+    X2.addConst();
+    auto D = CommonRef(S, S.BuiltinAddLValueReference(X2, Loc),
+                       S.BuiltinAddLValueReference(Y, Loc), Loc);
+    if (!D.isNull() && S.BuiltinIsConvertible(A, D, Loc))
+      return D;
+    return QualType();
+  }
+
+  // Otherwise, COMMON-REF(A, B) is ill-formed.
+  // This is implemented by returning from the individual branches above.
+
+  llvm_unreachable("The above cases should be exhaustive");
+}
+
+static QualType builtinCommonReferenceImpl(Sema &S,
+                                           TemplateName CommonReference,
+                                           TemplateName CommonType,
+                                           SourceLocation TemplateLoc,
+                                           ArrayRef<TemplateArgument> Ts) {
+  switch (Ts.size()) {
+  // If sizeof...(T) is zero, there shall be no member type.
+  case 0:
+    return QualType();
+
+  // Otherwise, if sizeof...(T) is one, let T0 denote the sole type in the
+  // pack T. The member typedef type shall denote the same type as T0.
+  case 1:
+    return Ts[0].getAsType();
+
+  // Otherwise, if sizeof...(T) is two, let T1 and T2 denote the two types in
+  // the pack T. Then
+  case 2: {
+    auto T1 = Ts[0].getAsType();
+    auto T2 = Ts[1].getAsType();
+
+    // Let R be COMMON-REF(T1, T2). If T1 and T2 are reference types, R is
+    // well-formed, and is_convertible_v<add_pointer_t<T1>, add_pointer_t<R>> &&
+    // is_convertible_v<add_pointer_t<T2>, add_pointer_t<R>> is true, then the
+    // member typedef type denotes R.
+    if (T1->isReferenceType() && T2->isReferenceType()) {
+      QualType R = CommonRef(S, T1, T2, TemplateLoc);
+      if (!R.isNull()) {
+        if (S.BuiltinIsConvertible(S.BuiltinAddPointer(T1, TemplateLoc),
+                                   S.BuiltinAddPointer(R, TemplateLoc),
+                                   TemplateLoc) &&
+            S.BuiltinIsConvertible(S.BuiltinAddPointer(T2, TemplateLoc),
+                                   S.BuiltinAddPointer(R, TemplateLoc),
+                                   TemplateLoc)) {
+          return R;
+        }
+      }
+    }
+
+    // Otherwise, if basic_common_reference<remove_cvref_t<T1>,
+    // remove_cvref_t<T2>, ​XREF(​T1), XREF(T2)>​::​type is well-formed,
+    // then the member typedef type denotes that type.
+    {
+      auto BCR = InstantiateTemplate(
+          S, CommonReference,
+          {S.BuiltinRemoveCVRef(T1, TemplateLoc),
+           S.BuiltinRemoveCVRef(T2, TemplateLoc),
+           TemplateName{S.Context.getCVRefQualifyingAliasDecl(T1)},
+           TemplateName{S.Context.getCVRefQualifyingAliasDecl(T2)}},
+          TemplateLoc);
+      if (!BCR.isNull())
+        return BCR;
+    }
+
+    // Otherwise, if COND-RES(T1, T2) is well-formed, then the member typedef
+    // type denotes that type.
+    if (auto CR = CondRes(S, T1, T2, TemplateLoc); !CR.isNull())
+      return CR;
+
+    // Otherwise, if common_type_t<T1, T2> is well-formed, then the member
+    // typedef type denotes that type.
+    if (auto CT = InstantiateTemplate(S, CommonType, {T1, T2}, TemplateLoc);
+        !CT.isNull())
+      return CT;
+
+    // Otherwise, there shall be no member type.
+    return QualType();
+  }
+
+  // Otherwise, if sizeof...(T) is greater than two, let T1, T2, and Rest,
+  // respectively, denote the first, second, and (pack of) remaining types
+  // comprising T. Let C be the type common_reference_t<T1, T2>. Then:
+  default: {
+    auto T1 = Ts[0];
+    auto T2 = Ts[1];
+    auto Rest = Ts.drop_front(2);
+    auto C = builtinCommonReferenceImpl(S, CommonReference, CommonType, TemplateLoc, {T1, T2});
+    if (C.isNull())
+      return QualType();
+    llvm::SmallVector<TemplateArgument, 4> Args;
+    Args.emplace_back(C);
+    Args.append(Rest.begin(), Rest.end());
+    return builtinCommonReferenceImpl(S, CommonReference, CommonType, TemplateLoc, Args);
+  }
+  }
+}
+
 static QualType
 checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD,
                            ArrayRef<TemplateArgument> Converted,
@@ -3310,6 +3509,29 @@ checkBuiltinTemplateIdType(Sema &SemaRef, BuiltinTemplateDecl *BTD,
     }
     return HasNoTypeMember;
   }
+
+  case BTK__builtin_common_reference: {
+    assert(Converted.size() == 5);
+    if (llvm::any_of(Converted, [](auto &C) { return C.isDependent(); }))
+      return Context.getCanonicalTemplateSpecializationType(TemplateName(BTD),
+                                                            Converted);
+
+    TemplateName BasicCommonReference = Converted[0].getAsTemplate();
+    TemplateName CommonType = Converted[1].getAsTemplate();
+    TemplateName HasTypeMember = Converted[2].getAsTemplate();
+    QualType HasNoTypeMember = Converted[3].getAsType();
+    ArrayRef<TemplateArgument> Ts = Converted[4].getPackAsArray();
+    if (auto CR = builtinCommonReferenceImpl(SemaRef, BasicCommonReference,
+                                             CommonType, TemplateLoc, Ts);
+        !CR.isNull()) {
+      TemplateArgumentListInfo TAs;
+      TAs.addArgument(TemplateArgumentLoc(
+          TemplateArgument(CR), SemaRef.Context.getTrivialTypeSourceInfo(
+                                    CR, TemplateArgs[1].getLocation())));
+      return SemaRef.CheckTemplateIdType(HasTypeMember, TemplateLoc, TAs);
+    }
+    return HasNoTypeMember;
+  }
   }
   llvm_unreachable("unexpected BuiltinTemplateDecl!");
 }
@@ -3575,6 +3797,20 @@ QualType Sema::CheckTemplateIdType(TemplateName Name,
   } else if (auto *BTD = dyn_cast<BuiltinTemplateDecl>(Template)) {
     CanonType = checkBuiltinTemplateIdType(*this, BTD, SugaredConverted,
                                            TemplateLoc, TemplateArgs);
+  } else if (auto *QTD = dyn_cast<CVRefQualifyingTemplateDecl>(Template)) {
+    assert(SugaredConverted.size() == 1);
+    CanonType = SugaredConverted[0].getAsType();
+
+    using CVRefQuals = CVRefQualifyingTemplateDecl::CVRefQuals;
+    auto Quals = QTD->getQuals();
+    if (Quals & CVRefQuals::Const)
+      CanonType.addConst();
+    if (Quals & CVRefQuals::Volatile)
+      CanonType.addVolatile();
+    if (Quals & CVRefQuals::LValueRef)
+      CanonType = BuiltinAddLValueReference(CanonType, TemplateLoc);
+    else if (Quals & CVRefQuals::RValueRef)
+      CanonType = BuiltinAddRValueReference(CanonType, TemplateLoc);
   } else if (Name.isDependent() ||
              TemplateSpecializationType::anyDependentTemplateArguments(
                  TemplateArgs, CanonicalConverted)) {
@@ -7339,7 +7575,8 @@ bool Sema::CheckTemplateTemplateArgument(TemplateTemplateParmDecl *Param,
   if (!isa<ClassTemplateDecl>(Template) &&
       !isa<TemplateTemplateParmDecl>(Template) &&
       !isa<TypeAliasTemplateDecl>(Template) &&
-      !isa<BuiltinTemplateDecl>(Template)) {
+      !isa<BuiltinTemplateDecl>(Template) &&
+      !isa<CVRefQualifyingTemplateDecl>(Template)) {
     assert(isa<FunctionTemplateDecl>(Template) &&
            "Only function templates are possible here");
     Diag(Arg.getLocation(), diag::err_template_arg_not_valid_template);
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index e058afe81da589..6b296e8676be85 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1694,6 +1694,11 @@ TemplateDeclInstantiator::VisitBuiltinTemplateDecl(BuiltinTemplateDecl *D) {
   llvm_unreachable("BuiltinTemplateDecls cannot be instantiated.");
 }
 
+Decl *TemplateDeclInstantiator::VisitCVRefQualifyingTemplateDecl(
+    CVRefQualifyingTemplateDecl *D) {
+  llvm_unreachable("CVRefQualifyingTemplateDecls cannot be instantiated.");
+}
+
 Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) {
   bool isFriend = (D->getFriendObjectKind() != Decl::FOK_None);
 
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 83464c50b4b238..f912e4a4eba14c 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -32,6 +32,8 @@
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Sema/DeclSpec.h"
 #include "clang/Sema/DelayedDiagnostic.h"
+#include "clang/Sema/EnterExpressionEvaluationContext.h"
+#include "clang/Sema/Initialization.h"
 #include "clang/Sema/Lookup.h"
 #include "clang/Sema/ParsedAttr.h"
 #include "clang/Sema/ParsedTemplate.h"
@@ -9909,6 +9911,77 @@ QualType Sema::BuiltinChangeSignedness(QualType BaseType, UTTKind UKind,
   return Context.getQualifiedType(Underlying, BaseType.getQualifiers());
 }
 
+bool Sema::BuiltinIsConvertible(QualType From, QualType To, SourceLocation Loc,
+                                bool CheckNothrow) {
+  if (To->isVoidType())
+    return From->isVoidType();
+
+  if (RequireCompleteType(Loc, From,
+                          diag::err_incomplete_type_used_in_type_trait_expr) ||
+      RequireCompleteType(Loc, To,
+                          diag::err_incomplete_type_used_in_type_trait_expr))
+    return false;
+
+  // C++11 [meta.rel]p4:
+  //   Given the following function prototype:
+  //
+  //     template <class T>
+  //       typename add_rvalue_reference<T>::type create();
+  //
+  //   the predicate condition for a template specialization
+  //   is_convertible<From, To> shall be satisfied if and only if
+  //   the return expression in the following code would be
+  //   well-formed, including any implicit conversions to the return
+  //   type of the function:
+  //
+  //     To test() {
+  //       return create<From>();
+  //     }
+  //
+  //   Access checking is performed as if in a context unrelated to To and
+  //   From. Only the validity of the immediate context of the expression
+  //   of the return-statement (including conversions to the return type)
+  //   is considered.
+  //
+  // We model the initialization as a copy-initialization of a temporary
+  // of the appropriate type, which for this expression is identical to the
+  // return statement (since NRVO doesn't apply).
+
+  // Functions aren't allowed to return function or array types.
+  if (From->isFunctionType() || To->isArrayType())
+    return false;
+
+  // A function definition requires a non-abstract return type.
+  if (isAbstractType(Loc, To))
+    return false;
+
+  From = BuiltinAddRValueReference(From, Loc);
+
+  // Build a fake source and destination for initialization.
+  InitializedEntity ToEntity(InitializedEntity::InitializeTemporary(To));
+  OpaqueValueExpr FromExpr(Loc, From.getNonLValueExprType(Context),
+                           Expr::getValueKindForType(From));
+  InitializationKind Kind =
+      InitializationKind::CreateCopy(Loc, SourceLocation());
+
+  // Perform the initialization in an unevaluated context within a SFINAE
+  // trap at translation unit scope.
+  EnterExpressionEvaluationContext Unevaluated(
+      *this, Sema::ExpressionEvaluationContext::Unevaluated);
+  Sema::SFINAETrap SFINAE(*this, /*AccessCheckingSFINAE=*/true);
+  Sema::ContextRAII TUContext(*this, Context.getTranslationUnitDecl());
+  Expr *FromExprPtr = &FromExpr;
+  InitializationSequence Init(*this, ToEntity, Kind, FromExprPtr);
+  if (Init.Failed())
+    return false;
+
+  ExprResult Result = Init.Perform(*this, ToEntity, Kind, FromExprPtr);
+  if (Result.isInvalid() || SFINAE.hasErrorOccurred())
+    return false;
+
+  return !CheckNothrow || canThrow(Result.get()) == CT_Cannot;
+}
+
 QualType Sema::BuildUnaryTransformType(QualType BaseType, UTTKind UKind,
                                        SourceLocation Loc) {
   if (BaseType->isDependentType())
diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp
index ec18e84255ca8e..178739aa071c70 100644
--- a/clang/lib/Serialization/ASTCommon.cpp
+++ b/clang/lib/Serialization/ASTCommon.cpp
@@ -448,6 +448,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) {
   case Decl::OMPDeclareReduction:
   case Decl::OMPDeclareMapper:
   case Decl::BuiltinTemplate:
+  case Decl::CVRefQualifyingTemplate:
   case Decl::Decomposition:
   case Decl::Binding:
   case Decl::Concept:
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 21f6b2ecc58c4f..7d0c92e18967f1 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -8035,6 +8035,12 @@ Decl *ASTReader::getPredefinedDecl(PredefinedDeclIDs ID) {
     NewLoaded = Context.getBuiltinCommonTypeDecl();
     break;
 
+  case PREDEF_DECL_COMMON_REFERENCE_ID:
+    if (Context.BuiltinCommonReferenceDecl)
+      return Context.BuiltinCommonReferenceDecl;
+    NewLoaded = Context.getBuiltinCommonReferenceDecl();
+    break;
+
   case NUM_PREDEF_DECL_IDS:
     llvm_unreachable("Invalid decl ID");
     break;
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 6db2262a7952ec..072cf6ac1863b2 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -5299,6 +5299,8 @@ void ASTWriter::PrepareWritingSpecialDecls(Sema &SemaRef) {
   RegisterPredefDecl(Context.TypePackElementDecl,
                      PREDEF_DECL_TYPE_PACK_ELEMENT_ID);
   RegisterPredefDecl(Context.BuiltinCommonTypeDecl, PREDEF_DECL_COMMON_TYPE_ID);
+  RegisterPredefDecl(Context.BuiltinCommonReferenceDecl,
+                     PREDEF_DECL_COMMON_REFERENCE_ID);
 
   const TranslationUnitDecl *TU = Context.getTranslationUnitDecl();
 
diff --git a/clang/test/SemaCXX/type-trait-common-reference.cpp b/clang/test/SemaCXX/type-trait-common-reference.cpp
new file mode 100644
index 00000000000000..bb475821fbc9b7
--- /dev/null
+++ b/clang/test/SemaCXX/type-trait-common-reference.cpp
@@ -0,0 +1,120 @@
+// 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_reference)
+#  error
+#endif
+
+// expected-note@*:* {{template <template <class, class, template <class> class, template <class> class> class, template <class ...> class, template <class> class, class, class ...>}}
+
+void test() {
+  __builtin_common_reference<> a; // expected-error {{too few template arguments for template '__builtin_common_reference'}}
+  __builtin_common_reference<1> b; // expected-error {{template argument for template template parameter must be a class template or type alias template}}
+  __builtin_common_reference<int, 1> c; // expected-error {{template argument for template template parameter must be a class template or type alias template}}
+}
+
+struct empty_type {};
+
+template <class T>
+struct type_identity {
+  using type = T;
+};
+
+template <class...>
+struct common_type;
+
+template <class... Args>
+using common_type_t = typename common_type<Args...>::type;
+
+template <class, class, template <class> class, template <class> class>
+struct basic_common_reference {};
+
+template <class T, class U, template <class> class TX, template <class> class UX>
+using basic_common_reference_t = typename basic_common_reference<T, U, TX, UX>::type;
+
+void test_vla() {
+  int i = 4;
+  int VLA[i];
+  __builtin_common_reference<basic_common_reference_t, 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_reference_base = __builtin_common_reference<basic_common_reference_t, common_type_t, type_identity, empty_type, Args...>;
+
+template <class... Args>
+struct common_reference : common_reference_base<Args...> {};
+
+template <class... Args>
+using common_reference_t = typename __builtin_common_reference<basic_common_reference_t, common_type_t, type_identity, empty_type, Args...>::type;
+
+struct Incomplete;
+
+template<>
+struct common_type<Incomplete, Incomplete>;
+
+static_assert(__is_same(common_reference_base<>, empty_type));
+
+static_assert(__is_same(common_reference_base<Incomplete>, type_identity<Incomplete>));
+static_assert(__is_same(common_reference_base<char>, type_identity<char>));
+static_assert(__is_same(common_reference_base<int>, type_identity<int>));
+static_assert(__is_same(common_reference_base<const int>, type_identity<const int>));
+static_assert(__is_same(common_reference_base<volatile int>, type_identity<volatile int>));
+static_assert(__is_same(common_reference_base<const volatile int>, type_identity<const volatile int>));
+static_assert(__is_same(common_reference_base<int[]>, type_identity<int[]>));
+static_assert(__is_same(common_reference_base<const int[]>, type_identity<const int[]>));
+static_assert(__is_same(common_reference_base<void(&)()>, type_identity<void(&)()>));
+
+static_assert(__is_same(common_reference_base<int[], int[]>, type_identity<int*>));
+static_assert(__is_same(common_reference_base<int, int>, type_identity<int>));
+static_assert(__is_same(common_reference_base<int, long>, type_identity<long>));
+static_assert(__is_same(common_reference_base<long, int>, type_identity<long>));
+static_assert(__is_same(common_reference_base<long, long>, type_identity<long>));
+
+static_assert(__is_same(common_reference_base<const int, long>, type_identity<long>));
+static_assert(__is_same(common_reference_base<const volatile int, long>, type_identity<long>));
+static_assert(__is_same(common_reference_base<int, const long>, type_identity<long>));
+static_assert(__is_same(common_reference_base<int, const volatile long>, type_identity<long>));
+
+static_assert(__is_same(common_reference_base<int*, long*>, empty_type));
+static_assert(__is_same(common_reference_base<const unsigned int *const &, const unsigned int *const &>, type_identity<const unsigned int *const &>));
+
+static_assert(__is_same(common_reference_base<int, long, float>, type_identity<float>));
+static_assert(__is_same(common_reference_base<unsigned, char, long>, type_identity<long>));
+static_assert(__is_same(common_reference_base<long long, long long, long>, type_identity<long long>));
+
+static_assert(__is_same(common_reference_base<int [[clang::address_space(1)]]>, type_identity<int [[clang::address_space(1)]]>));
+static_assert(__is_same(common_reference_base<int [[clang::address_space(1)]], int>, type_identity<int>));
+static_assert(__is_same(common_reference_base<long [[clang::address_space(1)]], int>, type_identity<long>));
+static_assert(__is_same(common_reference_base<long [[clang::address_space(1)]], int [[clang::address_space(1)]]>, type_identity<long>));
+static_assert(__is_same(common_reference_base<long [[clang::address_space(1)]], long [[clang::address_space(1)]]>, type_identity<long>));
+static_assert(__is_same(common_reference_base<long [[clang::address_space(1)]], long [[clang::address_space(2)]]>, type_identity<long>));
+
+struct S {};
+struct T : S {};
+struct U {};
+
+static_assert(__is_same(common_reference_base<int S::*, int S::*>, type_identity<int S::*>));
+static_assert(__is_same(common_reference_base<int S::*, int T::*>, type_identity<int T::*>));
+static_assert(__is_same(common_reference_base<int S::*, long S::*>, empty_type));
+
+static_assert(__is_same(common_reference_base<int (S::*)(), int (S::*)()>, type_identity<int (S::*)()>));
+static_assert(__is_same(common_reference_base<int (S::*)(), int (T::*)()>, type_identity<int (T::*)()>));
+static_assert(__is_same(common_reference_base<int (S::*)(), long (S::*)()>, empty_type));
+
+static_assert(__is_same(common_reference_base<int&, int&>, type_identity<int&>));
+static_assert(__is_same(common_reference_base<int&, const int&>, type_identity<const int&>));
+static_assert(__is_same(common_reference_base<volatile int&, const int&>, type_identity<const volatile int&>));
+
+template <class T, class U>
+struct my_pair;
+
+template <class T1, class U1, class T2, class U2, template <class> class TX, template <class> class UX>
+struct basic_common_reference<my_pair<T1, U1>, my_pair<T2, U2>, TX, UX> {
+  using type = my_pair<common_reference_t<TX<T1>, UX<T2>>, common_reference_t<TX<U1>, UX<U2>>>;
+};
+
+static_assert(__is_same(common_reference_base<my_pair<const int&, int&>, my_pair<int&, volatile int&>>, type_identity<my_pair<const int&, volatile int&>>));
+static_assert(__is_same(common_reference_base<const my_pair<int, int>&, my_pair<int&, volatile int&>>, type_identity<my_pair<const int&, const volatile int&>>));
+static_assert(__is_same(common_reference_base<const int&, const volatile int&>, type_identity<const volatile int&>));
+static_assert(__is_same(common_reference_base<int&&, const volatile int&>, type_identity<int>));
+static_assert(__is_same(common_reference_base<my_pair<int, int>&&, my_pair<int&, volatile int&>>, type_identity<my_pair<const int&, int>>));
diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp
index 9bdc4c9f8ce238..e2a1af61f2c1c7 100644
--- a/clang/tools/libclang/CIndex.cpp
+++ b/clang/tools/libclang/CIndex.cpp
@@ -7163,6 +7163,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) {
   case Decl::OMPRequires:
   case Decl::ObjCTypeParam:
   case Decl::BuiltinTemplate:
+  case Decl::CVRefQualifyingTemplate:
   case Decl::PragmaComment:
   case Decl::PragmaDetectMismatch:
   case Decl::UsingPack:
diff --git a/libcxx/include/__type_traits/common_reference.h b/libcxx/include/__type_traits/common_reference.h
index c802902eb19fc3..a41143670bc6e6 100644
--- a/libcxx/include/__type_traits/common_reference.h
+++ b/libcxx/include/__type_traits/common_reference.h
@@ -18,16 +18,36 @@
 #include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_cvref.h>
 #include <__type_traits/remove_reference.h>
+#include <__type_traits/type_identity.h>
 #include <__utility/declval.h>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 #endif
 
+#if _LIBCPP_STD_VER >= 20
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-// common_reference
-#if _LIBCPP_STD_VER >= 20
+template <class...>
+struct common_reference;
+
+template <class... _Types>
+using common_reference_t = typename common_reference<_Types...>::type;
+
+template <class, class, template <class> class, template <class> class>
+struct basic_common_reference {};
+
+#if __has_builtin(__builtin_common_reference)
+
+template <class _Tp, class _Up, template <class> class _Tx, template <class> class _Ux>
+using __basic_common_reference_t = basic_common_reference<_Tp, _Up, _Tx, _Ux>::type;
+
+template <class... _Args>
+struct common_reference : __builtin_common_reference<__basic_common_reference_t, common_type_t, type_identity, __empty, _Args...> {};
+
+#else
+
 // Let COND_RES(X, Y) be:
 template <class _Xp, class _Yp>
 using __cond_res = decltype(false ? std::declval<_Xp (&)()>()() : std::declval<_Yp (&)()>()());
@@ -109,12 +129,6 @@ struct __common_ref {};
 
 // Note C: For the common_reference trait applied to a parameter pack [...]
 
-template <class...>
-struct common_reference;
-
-template <class... _Types>
-using common_reference_t = typename common_reference<_Types...>::type;
-
 // bullet 1 - sizeof...(T) == 0
 template <>
 struct common_reference<> {};
@@ -146,8 +160,6 @@ struct __common_reference_sub_bullet1<_Tp, _Up> {
 
 // sub-bullet 2 - Otherwise, if basic_common_reference<remove_cvref_t<T1>, remove_cvref_t<T2>, XREF(T1), XREF(T2)>::type
 // is well-formed, then the member typedef `type` denotes that type.
-template <class, class, template <class> class, template <class> class>
-struct basic_common_reference {};
 
 template <class _Tp, class _Up>
 using __basic_common_reference_t =
@@ -186,8 +198,10 @@ struct common_reference<_Tp, _Up, _Vp, _Rest...> : common_reference<common_refer
 template <class...>
 struct common_reference {};
 
-#endif // _LIBCPP_STD_VER >= 20
+#endif // __has_builtin(__builtin_common_reference)
 
 _LIBCPP_END_NAMESPACE_STD
 
+#endif // _LIBCPP_STD_VER >= 20
+
 #endif // _LIBCPP___TYPE_TRAITS_COMMON_REFERENCE_H



More information about the libcxx-commits mailing list