https://github.com/mizvekov updated 
https://github.com/llvm/llvm-project/pull/190495

>From 7b73b23be73d40bb5367d5d2e34d24afd4b04ced Mon Sep 17 00:00:00 2001
From: Matheus Izvekov <[email protected]>
Date: Sat, 4 Apr 2026 20:44:35 -0300
Subject: [PATCH] [clang] implement CWG2064: ignore value dependence for
 decltype

The 'decltype' for a value-dependent (but non-type-dependent) should be known,
so this patch makes them non-opaque instead.

Readds a few test cases from da98651

Fixes #8740
Fixes #61818
Fixes #190388
---
 clang/docs/ReleaseNotes.rst                   |   1 +
 clang/include/clang/AST/ASTContext.h          |  48 +-
 clang/include/clang/AST/DependenceFlags.h     |   8 +-
 clang/include/clang/AST/TypeBase.h            |   3 +-
 clang/lib/AST/ASTContext.cpp                  | 517 ++++++++++++++++--
 clang/lib/AST/ASTDiagnostic.cpp               |   2 +-
 clang/lib/AST/DeclTemplate.cpp                |   4 +-
 clang/lib/AST/ItaniumMangle.cpp               |  15 +-
 clang/lib/AST/Type.cpp                        |  41 +-
 clang/lib/Sema/SemaOverload.cpp               |   4 +-
 clang/lib/Sema/SemaTemplate.cpp               |  44 +-
 clang/test/CXX/drs/cwg20xx.cpp                |  17 +-
 .../test/CXX/temp/temp.decls/temp.mem/p5.cpp  |   2 +-
 clang/test/CodeGenCXX/GH190495.cpp            |  29 +
 clang/test/CodeGenCXX/mangle-subst.cpp        |  13 +-
 clang/test/Sema/invalid-bitwidth-expr.mm      |   1 +
 clang/test/SemaCXX/decltype.cpp               |  11 +-
 clang/test/SemaCXX/source_location.cpp        |   7 +-
 clang/test/SemaCXX/typeof.cpp                 |   2 +-
 .../SemaTemplate/concepts-out-of-line-def.cpp |   6 +-
 clang/test/SemaTemplate/concepts.cpp          |  10 +
 clang/test/SemaTemplate/deduction-guide.cpp   |  23 +-
 clang/test/SemaTemplate/dependent-expr.cpp    |   8 +-
 .../test/SemaTemplate/injected-class-name.cpp |  44 ++
 .../SemaTemplate/instantiation-dependence.cpp |  90 ++-
 .../SemaTemplate/partial-spec-instantiate.cpp |   7 +
 .../SemaTemplate/temp_arg_template_p0522.cpp  |  11 +-
 27 files changed, 836 insertions(+), 132 deletions(-)
 create mode 100644 clang/test/CodeGenCXX/GH190495.cpp

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index b2e62106506f0..af8f7141f6f74 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -435,6 +435,7 @@ Bug Fixes to C++ Support
 - Fixed a crash when pack expansions are used as arguments for non-pack 
parameters of built-in templates. (#GH180307)
 - Fixed a bug where captured variables in non-mutable lambdas were incorrectly 
treated as mutable 
   when used inside decltype in the return type. (#GH180460)
+- `decltype(expr)` now ignores value-dependence on the expression. (#GH61818)
 - Fixed a crash when evaluating uninitialized GCC vector/ext_vector_type 
vectors in ``constexpr``. (#GH180044)
 - Fixed a crash when `explicit(bool)` is used with an incomplete enumeration. 
(#GH183887)
 - Fixed a crash on ``typeid`` of incomplete local types during template 
instantiation. (#GH63242), (#GH176397)
diff --git a/clang/include/clang/AST/ASTContext.h 
b/clang/include/clang/AST/ASTContext.h
index ba1b58489c327..2a827376047f9 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -401,6 +401,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// current file before they are compared locally.
   unsigned NextStringLiteralVersion = 0;
 
+  /// A cache mapping from types to their functionally equivalent canonical
+  /// type.
+  mutable llvm::DenseMap<const Type *, QualType>
+      FunctionallyEquivalentTypeCache;
+
   /// MD5 hash of CUID. It is calculated when first used and cached by this
   /// data member.
   mutable std::string CUIDHash;
@@ -1975,9 +1980,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
                                         Decl *AssociatedDecl, unsigned Index,
                                         UnsignedOrNone PackIndex,
                                         bool Final) const;
-  QualType getSubstTemplateTypeParmPackType(Decl *AssociatedDecl,
-                                            unsigned Index, bool Final,
-                                            const TemplateArgument &ArgPack);
+  QualType
+  getSubstTemplateTypeParmPackType(Decl *AssociatedDecl, unsigned Index,
+                                   bool Final,
+                                   const TemplateArgument &ArgPack) const;
   QualType getSubstBuiltinTemplatePack(const TemplateArgument &ArgPack);
 
   QualType
@@ -1993,7 +1999,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
   getTemplateSpecializationType(ElaboratedTypeKeyword Keyword, TemplateName T,
                                 ArrayRef<TemplateArgument> SpecifiedArgs,
                                 ArrayRef<TemplateArgument> CanonicalArgs,
-                                QualType Underlying = QualType()) const;
+                                QualType Underlying = QualType(),
+                                bool Unique = false) const;
 
   QualType
   getTemplateSpecializationType(ElaboratedTypeKeyword Keyword, TemplateName T,
@@ -2964,6 +2971,15 @@ class ASTContext : public RefCountedBase<ASTContext> {
     return getCanonicalType(T1) == getCanonicalType(T2);
   }
 
+  QualType getCanonicalType(QualType QT, bool FunctionallyEquivalent) const;
+  QualType getCanonicalType(QualType QT, bool FunctionallyEquivalent,
+                            bool &AnyNonCanonical) const {
+    QualType R = getCanonicalType(QT, FunctionallyEquivalent);
+    AnyNonCanonical |= !R.isCanonical();
+    return R;
+  }
+  bool hasFunctionallyEquivalentType(QualType T1, QualType T2) const;
+
   /// Determine whether the given expressions \p X and \p Y are equivalent.
   bool hasSameExpr(const Expr *X, const Expr *Y) const;
 
@@ -3118,14 +3134,30 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// The canonical template argument is the simplest template argument
   /// (which may be a type, value, expression, or declaration) that
   /// expresses the value of the argument.
-  TemplateArgument getCanonicalTemplateArgument(const TemplateArgument &Arg)
-    const;
+  TemplateArgument getCanonicalTemplateArgument(const TemplateArgument &Arg,
+                                                bool FunctionallyEquivalent,
+                                                bool &AnyNonCanonical) const;
+  TemplateArgument
+  getCanonicalTemplateArgument(const TemplateArgument &Arg,
+                               bool FunctionallyEquivalent = false) const {
+    bool AnyNonCanonical = false;
+    return getCanonicalTemplateArgument(Arg, FunctionallyEquivalent,
+                                        AnyNonCanonical);
+  }
 
   /// Canonicalize the given template argument list.
   ///
   /// Returns true if any arguments were non-canonical, false otherwise.
+  bool canonicalizeTemplateArguments(MutableArrayRef<TemplateArgument> Args,
+                                     bool FunctionallyEquivalent,
+                                     bool &AnyNonCanonical) const;
   bool
-  canonicalizeTemplateArguments(MutableArrayRef<TemplateArgument> Args) const;
+  canonicalizeTemplateArguments(MutableArrayRef<TemplateArgument> Args,
+                                bool FunctionallyEquivalent = false) const {
+    bool AnyNonCanonical = false;
+    return canonicalizeTemplateArguments(Args, FunctionallyEquivalent,
+                                         AnyNonCanonical);
+  }
 
   /// Canonicalize the given TemplateTemplateParmDecl.
   TemplateTemplateParmDecl *
@@ -3268,6 +3300,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
   // Helper for integer ordering
   unsigned getIntegerRank(const Type *T) const;
 
+  QualType buildFunctionallyEquivalentCanonicalType(const Type *T) const;
+
 public:
   
//===--------------------------------------------------------------------===//
   //                    Type Compatibility Predicates
diff --git a/clang/include/clang/AST/DependenceFlags.h 
b/clang/include/clang/AST/DependenceFlags.h
index c4395259f0758..739af906a3165 100644
--- a/clang/include/clang/AST/DependenceFlags.h
+++ b/clang/include/clang/AST/DependenceFlags.h
@@ -194,7 +194,13 @@ class Dependence {
   TypeDependence type() const {
     return translate(V, UnexpandedPack, TypeDependence::UnexpandedPack) |
            translate(V, Instantiation, TypeDependence::Instantiation) |
-           translate(V, Dependent, TypeDependence::Dependent) |
+           // There's a non-obvious choice here: Should Value dependence be
+           // translated to type dependence or not. After CWG2064, `decltype`
+           // only syntactically depends on the value of the expression. For 
all
+           // the other use cases where the value is dependended on
+           // semantically, this will be modeled through special purpose type
+           // nodes which are always type dependent anyway.
+           translate(V, Type, TypeDependence::Dependent) |
            translate(V, Error, TypeDependence::Error) |
            translate(V, VariablyModified, TypeDependence::VariablyModified);
   }
diff --git a/clang/include/clang/AST/TypeBase.h 
b/clang/include/clang/AST/TypeBase.h
index 8802b15d99034..cc561dc1fe424 100644
--- a/clang/include/clang/AST/TypeBase.h
+++ b/clang/include/clang/AST/TypeBase.h
@@ -7496,7 +7496,8 @@ class TemplateSpecializationType : public TypeWithKeyword,
   void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx);
   static void Profile(llvm::FoldingSetNodeID &ID, ElaboratedTypeKeyword 
Keyword,
                       TemplateName T, ArrayRef<TemplateArgument> Args,
-                      QualType Underlying, const ASTContext &Context);
+                      bool IsTypeAlias, QualType Underlying,
+                      const ASTContext &Context);
 
   static bool classof(const Type *T) {
     return T->getTypeClass() == TemplateSpecialization;
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index ee7f823b014b2..65d53ea30e2d5 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -3172,21 +3172,34 @@ ASTContext::getASTObjCInterfaceLayout(const 
ObjCInterfaceDecl *D) const {
 
 static auto getCanonicalTemplateArguments(const ASTContext &C,
                                           ArrayRef<TemplateArgument> Args,
-                                          bool &AnyNonCanonArgs) {
+                                          bool &AnyChanged,
+                                          bool FunctionallyEquivalent,
+                                          bool &AnyNonCanonical) {
   SmallVector<TemplateArgument, 16> CanonArgs(Args);
-  AnyNonCanonArgs |= C.canonicalizeTemplateArguments(CanonArgs);
+  AnyChanged |= C.canonicalizeTemplateArguments(
+      CanonArgs, FunctionallyEquivalent, AnyNonCanonical);
   return CanonArgs;
 }
 
+static auto getCanonicalTemplateArguments(const ASTContext &C,
+                                          ArrayRef<TemplateArgument> Args,
+                                          bool &AnyChanged) {
+  bool AnyNonCanonical = false;
+  return getCanonicalTemplateArguments(
+      C, Args, AnyChanged, /*FunctionallyEquivalent=*/false, AnyNonCanonical);
+}
+
 bool ASTContext::canonicalizeTemplateArguments(
-    MutableArrayRef<TemplateArgument> Args) const {
-  bool AnyNonCanonArgs = false;
+    MutableArrayRef<TemplateArgument> Args, bool FunctionallyEquivalent,
+    bool &AnyNonCanonical) const {
+  bool AnyChanged = false;
   for (auto &Arg : Args) {
     TemplateArgument OrigArg = Arg;
-    Arg = getCanonicalTemplateArgument(Arg);
-    AnyNonCanonArgs |= !Arg.structurallyEquals(OrigArg);
+    Arg = getCanonicalTemplateArgument(Arg, FunctionallyEquivalent,
+                                       AnyNonCanonical);
+    AnyChanged |= !Arg.structurallyEquals(OrigArg);
   }
-  return AnyNonCanonArgs;
+  return AnyChanged;
 }
 
 
//===----------------------------------------------------------------------===//
@@ -5861,10 +5874,9 @@ QualType 
ASTContext::getSubstTemplateTypeParmType(QualType Replacement,
   return QualType(SubstParm, 0);
 }
 
-QualType
-ASTContext::getSubstTemplateTypeParmPackType(Decl *AssociatedDecl,
-                                             unsigned Index, bool Final,
-                                             const TemplateArgument &ArgPack) {
+QualType ASTContext::getSubstTemplateTypeParmPackType(
+    Decl *AssociatedDecl, unsigned Index, bool Final,
+    const TemplateArgument &ArgPack) const {
 #ifndef NDEBUG
   for (const auto &P : ArgPack.pack_elements())
     assert(P.getKind() == TemplateArgument::Type && "Pack contains a 
non-type");
@@ -6041,8 +6053,8 @@ QualType 
ASTContext::getCanonicalTemplateSpecializationType(
 #endif
 
   llvm::FoldingSetNodeID ID;
-  TemplateSpecializationType::Profile(ID, Keyword, Template, Args, QualType(),
-                                      *this);
+  TemplateSpecializationType::Profile(ID, Keyword, Template, Args,
+                                      /*IsTypeAlias=*/false, QualType(), 
*this);
   void *InsertPos = nullptr;
   if (auto *T = TemplateSpecializationTypes.FindNodeOrInsertPos(ID, InsertPos))
     return QualType(T, 0);
@@ -6063,9 +6075,27 @@ QualType 
ASTContext::getCanonicalTemplateSpecializationType(
 QualType ASTContext::getTemplateSpecializationType(
     ElaboratedTypeKeyword Keyword, TemplateName Template,
     ArrayRef<TemplateArgument> SpecifiedArgs,
-    ArrayRef<TemplateArgument> CanonicalArgs, QualType Underlying) const {
-  const auto *TD = Template.getAsTemplateDecl(/*IgnoreDeduced=*/true);
-  bool IsTypeAlias = TD && TD->isTypeAlias();
+    ArrayRef<TemplateArgument> CanonicalArgs, QualType Underlying,
+    bool Unique) const {
+
+  bool IsTypeAlias = false;
+  if (!Underlying.isNull()) {
+    const auto *TD = Template.getAsTemplateDecl(/*IgnoreDeduced=*/true);
+    IsTypeAlias = TD && TD->isTypeAlias();
+    if (!IsTypeAlias)
+      Underlying = getCanonicalType(Underlying);
+  }
+
+  llvm::FoldingSetNodeID ID;
+  void *InsertPos = nullptr;
+  if (Unique) {
+    TemplateSpecializationType::Profile(ID, Keyword, Template, SpecifiedArgs,
+                                        IsTypeAlias, Underlying, *this);
+    if (auto *T =
+            TemplateSpecializationTypes.FindNodeOrInsertPos(ID, InsertPos))
+      return QualType(T, 0);
+  }
+
   if (Underlying.isNull()) {
     TemplateName CanonTemplate =
         getCanonicalTemplateName(Template, /*IgnoreDeduced=*/true);
@@ -6087,18 +6117,16 @@ QualType ASTContext::getTemplateSpecializationType(
           });
     }
 
-    // We can get here with an alias template when the specialization
-    // contains a pack expansion that does not match up with a parameter
-    // pack, or a builtin template which cannot be resolved due to dependency.
-    assert((!isa_and_nonnull<TypeAliasTemplateDecl>(TD) ||
-            hasAnyPackExpansions(CanonicalArgs)) &&
-           "Caller must compute aliased type");
-    IsTypeAlias = false;
-
     Underlying = getCanonicalTemplateSpecializationType(
         CanonKeyword, CanonTemplate, CanonicalArgs);
     if (!NonCanonical)
       return Underlying;
+
+    if (Unique) {
+      [[maybe_unused]] auto *T =
+          TemplateSpecializationTypes.FindNodeOrInsertPos(ID, InsertPos);
+      assert(!T && "broken canonical type");
+    }
   }
   void *Mem = Allocate(sizeof(TemplateSpecializationType) +
                            sizeof(TemplateArgument) * SpecifiedArgs.size() +
@@ -6107,6 +6135,8 @@ QualType ASTContext::getTemplateSpecializationType(
   auto *Spec = new (Mem) TemplateSpecializationType(
       Keyword, Template, IsTypeAlias, SpecifiedArgs, Underlying);
   Types.push_back(Spec);
+  if (Unique)
+    TemplateSpecializationTypes.InsertNode(Spec, InsertPos);
   return QualType(Spec, 0);
 }
 
@@ -6691,12 +6721,11 @@ QualType ASTContext::getReferenceQualifiedType(const 
Expr *E) const {
 /// expression, and would not give a significant memory saving, since there
 /// is an Expr tree under each such type.
 QualType ASTContext::getDecltypeType(Expr *E, QualType UnderlyingType) const {
-  // C++11 [temp.type]p2:
-  //   If an expression e involves a template parameter, decltype(e) denotes a
-  //   unique dependent type. Two such decltype-specifiers refer to the same
-  //   type only if their expressions are equivalent (14.5.6.1).
+  // C++26 [temp.type]p4: If an expression e is type-dependent, decltype(e)
+  // denotes a unique dependent type. Two such decltype-specifiers refer to the
+  // same type only if their expressions are equivalent ([temp.over.link]).
   QualType CanonType;
-  if (!E->isInstantiationDependent()) {
+  if (!E->isTypeDependent()) {
     CanonType = getCanonicalType(UnderlyingType);
   } else if (!UnderlyingType.isNull()) {
     CanonType = getDecltypeType(E, QualType());
@@ -7888,51 +7917,66 @@ bool ASTContext::isSameEntity(const NamedDecl *X, const 
NamedDecl *Y) const {
 }
 
 TemplateArgument
-ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg) const {
+ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg,
+                                         bool FunctionallyEquivalent,
+                                         bool &AnyNonCanonical) const {
   switch (Arg.getKind()) {
     case TemplateArgument::Null:
       return Arg;
 
     case TemplateArgument::Expression:
+      // FIXME: Do functionally equivalent expressions need different 
profiling?
       return TemplateArgument(Arg.getAsExpr(), /*IsCanonical=*/true,
                               Arg.getIsDefaulted());
 
-    case TemplateArgument::Declaration: {
-      auto *D = cast<ValueDecl>(Arg.getAsDecl()->getCanonicalDecl());
-      return TemplateArgument(D, getCanonicalType(Arg.getParamTypeForDecl()),
-                              Arg.getIsDefaulted());
-    }
+    case TemplateArgument::Declaration:
+      return TemplateArgument(
+          cast<ValueDecl>(Arg.getAsDecl()->getCanonicalDecl()),
+          getCanonicalType(Arg.getParamTypeForDecl(), FunctionallyEquivalent,
+                           AnyNonCanonical),
+          Arg.getIsDefaulted());
 
     case TemplateArgument::NullPtr:
-      return TemplateArgument(getCanonicalType(Arg.getNullPtrType()),
-                              /*isNullPtr*/ true, Arg.getIsDefaulted());
+      return TemplateArgument(getCanonicalType(Arg.getNullPtrType(),
+                                               FunctionallyEquivalent,
+                                               AnyNonCanonical),
+                              /*isNullPtr=*/true, Arg.getIsDefaulted());
 
     case TemplateArgument::Template:
+      // FIXME: Implement functional canonicalization of template names.
       return TemplateArgument(getCanonicalTemplateName(Arg.getAsTemplate()),
                               Arg.getIsDefaulted());
 
     case TemplateArgument::TemplateExpansion:
+      // FIXME: Implement functional canonicalization of template names.
       return TemplateArgument(
           getCanonicalTemplateName(Arg.getAsTemplateOrTemplatePattern()),
           Arg.getNumTemplateExpansions(), Arg.getIsDefaulted());
 
     case TemplateArgument::Integral:
-      return TemplateArgument(Arg, getCanonicalType(Arg.getIntegralType()));
+      return TemplateArgument(Arg, getCanonicalType(Arg.getIntegralType(),
+                                                    FunctionallyEquivalent,
+                                                    AnyNonCanonical));
 
     case TemplateArgument::StructuralValue:
       return TemplateArgument(*this,
-                              getCanonicalType(Arg.getStructuralValueType()),
+                              getCanonicalType(Arg.getStructuralValueType(),
+                                               FunctionallyEquivalent,
+                                               AnyNonCanonical),
                               Arg.getAsStructuralValue(), 
Arg.getIsDefaulted());
 
     case TemplateArgument::Type:
-      return TemplateArgument(getCanonicalType(Arg.getAsType()),
-                              /*isNullPtr*/ false, Arg.getIsDefaulted());
+      return TemplateArgument(getCanonicalType(Arg.getAsType(),
+                                               FunctionallyEquivalent,
+                                               AnyNonCanonical),
+                              /*isNullPtr=*/false, Arg.getIsDefaulted());
 
     case TemplateArgument::Pack: {
-      bool AnyNonCanonArgs = false;
+      bool AnyChanged = false;
       auto CanonArgs = ::getCanonicalTemplateArguments(
-          *this, Arg.pack_elements(), AnyNonCanonArgs);
-      if (!AnyNonCanonArgs)
+          *this, Arg.pack_elements(), AnyChanged, FunctionallyEquivalent,
+          AnyNonCanonical);
+      if (!AnyChanged)
         return Arg;
       auto NewArg = TemplateArgument::CreatePackCopy(
           const_cast<ASTContext &>(*this), CanonArgs);
@@ -13858,6 +13902,391 @@ unsigned ASTContext::getTargetAddressSpace(LangAS AS) 
const {
   return getTargetInfo().getTargetAddressSpace(AS);
 }
 
+static NestedNameSpecifier
+getFunctionallyEquivalentCanonicalQualifier(const ASTContext &Context,
+                                            NestedNameSpecifier Qualifier,
+                                            bool &AnyNonCanonical) {
+  if (Qualifier.getKind() == NestedNameSpecifier::Kind::Type)
+    return NestedNameSpecifier(
+        Context
+            .getCanonicalType(QualType(Qualifier.getAsType(), 0),
+                              /*FunctionallyEquivalent=*/true, AnyNonCanonical)
+            .getTypePtr());
+  return Qualifier.getCanonical();
+}
+
+QualType ASTContext::getCanonicalType(QualType QT,
+                                      bool FunctionallyEquivalent) const {
+  if (!FunctionallyEquivalent)
+    return QT.getCanonicalType();
+
+  // A canonical type is functionally equivalent to itself.
+  if (QT.isCanonical())
+    return QT;
+
+  // A non-instantiation-dependent type is functionally equivalent to its
+  // canonical type.
+  if (!QT->isInstantiationDependentType())
+    return getCanonicalType(QT);
+  auto [T, Qualifiers] = QT.split();
+
+  auto It = FunctionallyEquivalentTypeCache.find(T);
+  if (It != FunctionallyEquivalentTypeCache.end())
+    return It->second;
+
+  QualType R = buildFunctionallyEquivalentCanonicalType(T);
+  assert(hasSameType(QualType(T, 0), R));
+
+  auto [_, Inserted] = FunctionallyEquivalentTypeCache.try_emplace(T, R);
+  assert(Inserted && "Unexpected cache entry for type");
+
+  return getQualifiedType(R, Qualifiers);
+}
+
+QualType
+ASTContext::buildFunctionallyEquivalentCanonicalType(const Type *T) const {
+  assert(!T->isCanonicalUnqualified());
+  assert(T->isInstantiationDependentType());
+
+  // If none of the inputs became non-canonical, just return the canonical 
type.
+  // It's not helpful for the applications of this transform to track whether
+  // the inputs changed, because they will mostly refer to template parameters
+  // that haven't been canonicalized.
+  bool AnyNonCanonical = false;
+  switch (T->getTypeClass()) {
+  case Type::Builtin:
+    llvm_unreachable(
+        "always canonical types should have been handled by this point");
+  case Type::PredefinedSugar:
+    llvm_unreachable("never-instantation-dependent types should have been "
+                     "handled by this point");
+  case Type::Complex:
+  case Type::FunctionNoProto:
+  case Type::Adjusted:
+  case Type::Decayed:
+  case Type::Auto:
+  case Type::DeducedTemplateSpecialization:
+  case Type::ObjCObject:
+  case Type::ObjCInterface:
+  case Type::ObjCObjectPointer:
+  case Type::Atomic:
+  case Type::Record:
+  case Type::Enum:
+  case Type::DependentVector:
+  case Type::Vector:
+  case Type::ArrayParameter:
+  case Type::BTFTagAttributed:
+  case Type::BitInt:
+  case Type::CountAttributed:
+  case Type::HLSLAttributedResource:
+  case Type::HLSLInlineSpirv:
+  case Type::MacroQualified:
+  case Type::ObjCTypeParam:
+  case Type::Pipe:
+  case Type::TypeOf:
+  case Type::TypeOfExpr:
+  case Type::Using:
+  case Type::DependentBitInt:
+  case Type::OverflowBehavior:
+  case Type::SubstBuiltinTemplatePack:
+  case Type::UnresolvedUsing:
+    T->dump();
+    llvm_unreachable("unimplemented");
+  case Type::FunctionProto: {
+    const auto *TT = cast<FunctionProtoType>(T);
+    QualType RT = getCanonicalType(
+        TT->getReturnType(), /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+    SmallVector<QualType, 8> PTs(TT->getParamTypes().size());
+    llvm::transform(TT->getParamTypes(), PTs.begin(), [&](QualType PT) {
+      return getCanonicalType(PT, /*FunctionallyEquivalent=*/true,
+                              AnyNonCanonical);
+    });
+    if (!AnyNonCanonical)
+      break;
+    return getFunctionType(RT, PTs, TT->getExtProtoInfo());
+  }
+  case Type::Pointer: {
+    const auto *TT = cast<PointerType>(T);
+    QualType Pointee = getCanonicalType(
+        TT->getPointeeType(), /*FunctionallyEquivalent=*/true, 
AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getPointerType(Pointee);
+  }
+  case Type::LValueReference: {
+    const auto *TT = cast<LValueReferenceType>(T);
+    QualType Pointee = getCanonicalType(
+        TT->getPointeeType(), /*FunctionallyEquivalent=*/true, 
AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getLValueReferenceType(Pointee);
+  }
+  case Type::RValueReference: {
+    const auto *TT = cast<RValueReferenceType>(T);
+    QualType Pointee = getCanonicalType(
+        TT->getPointeeType(), /*FunctionallyEquivalent=*/true, 
AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getRValueReferenceType(Pointee);
+  }
+  case Type::InjectedClassName: {
+    const auto *TT = cast<InjectedClassNameType>(T);
+
+    ElaboratedTypeKeyword Keyword =
+        ::getCanonicalElaboratedTypeKeyword(TT->getKeyword());
+    NestedNameSpecifier Qualifier =
+        ::getFunctionallyEquivalentCanonicalQualifier(*this, 
TT->getQualifier(),
+                                                      AnyNonCanonical);
+    const TagDecl *ND = TT->getDecl()->getCanonicalDecl();
+    if (!AnyNonCanonical)
+      break;
+    return getTagType(Keyword, Qualifier, ND, /*OwnsTag=*/false);
+  }
+  case Type::Attributed:
+    return getCanonicalType(cast<AttributedType>(T)->desugar(),
+                            /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+  case Type::Paren:
+    return getCanonicalType(cast<ParenType>(T)->desugar(),
+                            /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+  case Type::PackExpansion: {
+    const auto *TT = cast<PackExpansionType>(T);
+    QualType Pattern = getCanonicalType(
+        TT->getPattern(), /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getPackExpansionType(Pattern, TT->getNumExpansions(),
+                                /*ExpectPackInType=*/false);
+  }
+  case Type::PackIndexing: {
+    const auto *TT = cast<PackIndexingType>(T);
+
+    // This type holds on to instantiation-dependence.
+    assert(!TT->isSugared());
+
+    QualType Pattern = getCanonicalType(
+        TT->getPattern(), /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+    SmallVector<QualType, 8> Expansions(TT->getExpansions().size());
+    llvm::transform(TT->getExpansions(), Expansions.begin(), [&](QualType E) {
+      return getCanonicalType(E, /*FunctionallyEquivalent=*/true,
+                              AnyNonCanonical);
+    });
+    if (!AnyNonCanonical)
+      break;
+    return getPackIndexingType(Pattern, TT->getIndexExpr(),
+                               TT->isFullySubstituted(), Expansions,
+                               TT->getSelectedIndex());
+  }
+  case Type::UnaryTransform:
+    // FIXME: Unimplemented. These appear to have broken canonicalization.
+    break;
+  case Type::MemberPointer: {
+    const auto *TT = cast<MemberPointerType>(T);
+    const auto *Cls = TT->getMostRecentCXXRecordDecl();
+    NestedNameSpecifier Qualifier =
+        ::getFunctionallyEquivalentCanonicalQualifier(*this, 
TT->getQualifier(),
+                                                      AnyNonCanonical);
+    // If the qualifier became canonical and we have a class (ie it is
+    // non-dependent), then we can drop the qualifier entirely.
+    if (Cls && !AnyNonCanonical)
+      Qualifier = std::nullopt;
+    QualType Pointee = getCanonicalType(
+        TT->getPointeeType(), /*FunctionallyEquivalent=*/true, 
AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getMemberPointerType(Pointee, Qualifier, Cls);
+  }
+  case Type::BlockPointer: {
+    const auto *TT = cast<BlockPointerType>(T);
+    QualType Pointee = getCanonicalType(
+        TT->getPointeeType(), /*FunctionallyEquivalent=*/true, 
AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getBlockPointerType(Pointee);
+  }
+  case Type::IncompleteArray: {
+    const auto *TT = cast<IncompleteArrayType>(T);
+    QualType ElementType = getCanonicalType(
+        TT->getElementType(), /*FunctionallyEquivalent=*/true, 
AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getIncompleteArrayType(ElementType, TT->getSizeModifier(),
+                                  TT->getIndexTypeCVRQualifiers());
+  }
+  case Type::ConstantArray: {
+    const auto *TT = cast<ConstantArrayType>(T);
+    QualType ElementType = getCanonicalType(
+        TT->getElementType(), /*FunctionallyEquivalent=*/true, 
AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getConstantArrayType(ElementType, TT->getSize(), TT->getSizeExpr(),
+                                TT->getSizeModifier(),
+                                TT->getIndexTypeCVRQualifiers());
+  }
+  case Type::VariableArray: {
+    const auto *TT = cast<VariableArrayType>(T);
+    QualType ElementType = getCanonicalType(
+        TT->getElementType(), /*FunctionallyEquivalent=*/true, 
AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getVariableArrayType(ElementType, TT->getSizeExpr(),
+                                TT->getSizeModifier(),
+                                TT->getIndexTypeCVRQualifiers());
+  }
+  case Type::DependentSizedArray: {
+    const auto *TT = cast<DependentSizedArrayType>(T);
+    QualType ElementType = getCanonicalType(
+        TT->getElementType(), /*FunctionallyEquivalent=*/true, 
AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getDependentSizedArrayType(ElementType, TT->getSizeExpr(),
+                                      TT->getSizeModifier(),
+                                      TT->getIndexTypeCVRQualifiers());
+  }
+  case Type::ExtVector: {
+    const auto *TT = cast<ExtVectorType>(T);
+    QualType ElementType = getCanonicalType(
+        TT->getElementType(), /*FunctionallyEquivalent=*/true, 
AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getExtVectorType(ElementType, TT->getNumElements());
+  }
+  case Type::DependentSizedExtVector: {
+    const auto *TT = cast<DependentSizedExtVectorType>(T);
+    QualType ElementType = getCanonicalType(
+        TT->getElementType(), /*FunctionallyEquivalent=*/true, 
AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getDependentSizedExtVectorType(ElementType, TT->getSizeExpr(),
+                                          TT->getAttributeLoc());
+  }
+  case Type::ConstantMatrix: {
+    const auto *TT = cast<ConstantMatrixType>(T);
+    QualType ElementType = getCanonicalType(
+        TT->getElementType(), /*FunctionallyEquivalent=*/true, 
AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getConstantMatrixType(ElementType, TT->getNumRows(),
+                                 TT->getNumColumns());
+  }
+  case Type::DependentSizedMatrix: {
+    const auto *TT = cast<DependentSizedMatrixType>(T);
+    QualType ElementType = getCanonicalType(
+        TT->getElementType(), /*FunctionallyEquivalent=*/true, 
AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getDependentSizedMatrixType(ElementType, TT->getRowExpr(),
+                                       TT->getColumnExpr(),
+                                       TT->getAttributeLoc());
+  }
+  case Type::DependentAddressSpace: {
+    const auto *TT = cast<DependentAddressSpaceType>(T);
+    QualType Pointee = getCanonicalType(
+        TT->getPointeeType(), /*FunctionallyEquivalent=*/true, 
AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getDependentAddressSpaceType(Pointee, TT->getAddrSpaceExpr(),
+                                        TT->getAttributeLoc());
+  }
+  case Type::SubstTemplateTypeParmPack: {
+    const auto *TT = cast<SubstTemplateTypeParmPackType>(T);
+    TemplateArgument ArgPack = getCanonicalTemplateArgument(
+        TT->getArgumentPack(), /*FunctionallyEquivalent=*/true,
+        AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getSubstTemplateTypeParmPackType(
+        TT->getAssociatedDecl()->getCanonicalDecl(), TT->getIndex(),
+        TT->getFinal(), ArgPack);
+  }
+  case Type::DependentName: {
+    const auto *TT = cast<DependentNameType>(T);
+    ElaboratedTypeKeyword Keyword =
+        ::getCanonicalElaboratedTypeKeyword(TT->getKeyword());
+    NestedNameSpecifier Qualifier =
+        ::getFunctionallyEquivalentCanonicalQualifier(*this, 
TT->getQualifier(),
+                                                      AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getDependentNameType(Keyword, Qualifier, TT->getIdentifier());
+  }
+  case Type::TemplateSpecialization: {
+    const auto *TT = cast<TemplateSpecializationType>(T);
+    // For a type alias, we want to keep it only if there are unused
+    // instantiation-dependent template arguments.
+    if (TT->isTypeAlias()) {
+      const TemplateDecl *TD = TT->getTemplateName().getAsTemplateDecl();
+      auto As = TT->template_arguments();
+      bool AnyUnusedInstantiationDependentArgs = false;
+      for (const NamedDecl *PD : TD->getTemplateParameters()->asArray()) {
+        if (As.empty())
+          break;
+        auto CurAs = As;
+        // If this is a parameter pack, a use of this parameter means all of 
the
+        // remaining template arguments are used.
+        if (!PD->isTemplateParameterPack())
+          CurAs = CurAs.take_front(1);
+        if (!PD->isReferenced()) {
+          auto Dep = TemplateArgumentDependence::None;
+          for (const auto &A : CurAs)
+            Dep |= A.getDependence();
+          if (Dep & TemplateArgumentDependence::Instantiation) {
+            AnyUnusedInstantiationDependentArgs = true;
+            break;
+          }
+        }
+        As = As.drop_front(CurAs.size());
+      }
+      if (!AnyUnusedInstantiationDependentArgs)
+        return getCanonicalType(TT->getAliasedType(),
+                                /*FunctionallyEquivalent=*/true,
+                                AnyNonCanonical);
+      // Otherwise, we are keeping the type alias. This on itself makes the
+      // resulting type non-canonical.
+      AnyNonCanonical = true;
+    }
+    ElaboratedTypeKeyword Keyword =
+        ::getCanonicalElaboratedTypeKeyword(TT->getKeyword());
+    // FIXME: Implement getFunctionallyEquivalentCanonicalTemplateName and use
+    // it here.
+    TemplateName TN =
+        getCanonicalTemplateName(TT->getTemplateName(), 
/*IgnoreDeduced=*/true);
+    // FIXME: We can't avoid rebuilding if nothing changed, because we can't
+    // rely on this TST to have been uniqued.
+    bool AnyChanged = false;
+    auto As = ::getCanonicalTemplateArguments(
+        *this, TT->template_arguments(), AnyChanged,
+        /*FunctionallyEquivalent=*/true, AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getTemplateSpecializationType(
+        Keyword, TN, As,
+        /*CanonicalArgs=*/ArrayRef<TemplateArgument>(),
+        TT->desugar().getCanonicalType(),
+        /*Unique=*/true);
+  }
+  case Type::Decltype: {
+    const auto *TT = cast<DecltypeType>(T);
+    AnyNonCanonical |= TT->isSugared();
+    if (!AnyNonCanonical)
+      break;
+    return QualType(TT, 0);
+  }
+  // These types don't have other types in their spelling, so they always
+  // canonicalize to the equivalent form.
+  case Type::Typedef:
+  case Type::SubstTemplateTypeParm:
+  case Type::TemplateTypeParm:
+    break;
+  }
+  return T->getCanonicalTypeInternal();
+}
+
+bool ASTContext::hasFunctionallyEquivalentType(QualType T1, QualType T2) const 
{
+  return hasSameType(T1, T2) &&
+         getCanonicalType(T1, /*FunctionallyEquivalent=*/true) ==
+             getCanonicalType(T2, /*FunctionallyEquivalent=*/true);
+}
+
 bool ASTContext::hasSameExpr(const Expr *X, const Expr *Y) const {
   if (X == Y)
     return true;
diff --git a/clang/lib/AST/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp
index b8023cb6fa10f..9f79635ad4c54 100644
--- a/clang/lib/AST/ASTDiagnostic.cpp
+++ b/clang/lib/AST/ASTDiagnostic.cpp
@@ -126,7 +126,7 @@ QualType clang::desugarForDiagnostic(ASTContext &Context, 
QualType QT,
           ShouldAKA = true;
           QT = Context.getTemplateSpecializationType(
               TST->getKeyword(), TST->getTemplateName(), Args,
-              /*CanonicalArgs=*/{}, QT);
+              /*CanonicalArgs=*/{}, QT.getCanonicalType());
         }
         break;
       }
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 99d02fdc99e92..29c10c45e62da 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -1202,11 +1202,13 @@ CanQualType
 ClassTemplatePartialSpecializationDecl::getCanonicalInjectedSpecializationType(
     const ASTContext &Ctx) const {
   if (CanonInjectedTST.isNull()) {
+    SmallVector<TemplateArgument, 4> 
CanonicalArgs(getTemplateArgs().asArray());
+    Ctx.canonicalizeTemplateArguments(CanonicalArgs);
     CanonInjectedTST =
         CanQualType::CreateUnsafe(Ctx.getCanonicalTemplateSpecializationType(
             ElaboratedTypeKeyword::None,
             TemplateName(getSpecializedTemplate()->getCanonicalDecl()),
-            getTemplateArgs().asArray()));
+            CanonicalArgs));
   }
   return CanonInjectedTST;
 }
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index f58faa03bfa8c..1b22e401c5fb7 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -3043,6 +3043,11 @@ void CXXNameMangler::mangleType(QualType T) {
         if (!TST->isTypeAlias())
           break;
 
+      // instantiation-dependent decltypes are mangled through their
+      // expressions.
+      if (T->isInstantiationDependentType() && isa<DecltypeType>(T))
+        break;
+
       // FIXME: We presumably shouldn't strip off ElaboratedTypes with
       // instantation-dependent qualifiers. See
       // https://github.com/itanium-cxx-abi/cxx-abi/issues/114.
@@ -7040,8 +7045,9 @@ static bool hasMangledSubstitutionQualifiers(QualType T) {
 
 bool CXXNameMangler::mangleSubstitution(QualType T) {
   if (!hasMangledSubstitutionQualifiers(T)) {
-    if (const auto *RD = T->getAsCXXRecordDecl())
-      return mangleSubstitution(RD);
+    if (const auto *TT = dyn_cast<TagType>(T);
+        isa_and_nonnull<RecordType, InjectedClassNameType>(TT))
+      return mangleSubstitution(TT->getDecl());
   }
 
   uintptr_t TypePtr = reinterpret_cast<uintptr_t>(T.getAsOpaquePtr());
@@ -7210,8 +7216,9 @@ bool CXXNameMangler::mangleStandardSubstitution(const 
NamedDecl *ND) {
 
 void CXXNameMangler::addSubstitution(QualType T) {
   if (!hasMangledSubstitutionQualifiers(T)) {
-    if (const auto *RD = T->getAsCXXRecordDecl()) {
-      addSubstitution(RD);
+    if (const auto *TT = dyn_cast<TagType>(T);
+        isa_and_nonnull<RecordType, InjectedClassNameType>(TT)) {
+      addSubstitution(TT->getDecl());
       return;
     }
   }
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 78983fd38410d..992e72c8e8e14 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -428,9 +428,16 @@ BitIntType::BitIntType(bool IsUnsigned, unsigned NumBits)
       NumBits(NumBits) {}
 
 DependentBitIntType::DependentBitIntType(bool IsUnsigned, Expr *NumBitsExpr)
+    // DependentBitIntType must always be type-dependent.
+    // The expression must be value-dependent, so will also be
+    // instantiation-dependent.
     : Type(DependentBitInt, QualType{},
-           toTypeDependence(NumBitsExpr->getDependence())),
-      ExprAndUnsigned(NumBitsExpr, IsUnsigned) {}
+           toTypeDependence(NumBitsExpr->getDependence()) |
+               TypeDependence::Dependent),
+      ExprAndUnsigned(NumBitsExpr, IsUnsigned) {
+  assert(NumBitsExpr->isValueDependent() &&
+         "NumBitsExpr must be value-dependent");
+}
 
 bool DependentBitIntType::isUnsigned() const {
   return ExprAndUnsigned.getInt();
@@ -4219,15 +4226,10 @@ DecltypeType::DecltypeType(Expr *E, QualType 
underlyingType, QualType can)
     // C++11 [temp.type]p2: "If an expression e involves a template parameter,
     // decltype(e) denotes a unique dependent type." Hence a decltype type is
     // type-dependent even if its expression is only instantiation-dependent.
-    : Type(Decltype, can,
-           toTypeDependence(E->getDependence()) |
-               (E->isInstantiationDependent() ? TypeDependence::Dependent
-                                              : TypeDependence::None) |
-               (E->getType()->getDependence() &
-                TypeDependence::VariablyModified)),
-      E(E), UnderlyingType(underlyingType) {}
+    : Type(Decltype, can, toTypeDependence(E->getDependence())), E(E),
+      UnderlyingType(underlyingType) {}
 
-bool DecltypeType::isSugared() const { return !E->isInstantiationDependent(); }
+bool DecltypeType::isSugared() const { return !E->isTypeDependent(); }
 
 QualType DecltypeType::desugar() const {
   if (isSugared())
@@ -4711,23 +4713,30 @@ QualType TemplateSpecializationType::getAliasedType() 
const {
 }
 
 bool clang::TemplateSpecializationType::isSugared() const {
-  return !isDependentType() || isCurrentInstantiation() || isTypeAlias() ||
-         (isPackProducingBuiltinTemplateName(Template) &&
-          isa<SubstBuiltinTemplatePackType>(*getCanonicalTypeInternal()));
+  return isTypeAlias() ||
+         !isa<TemplateSpecializationType>(getCanonicalTypeInternal());
 }
 
 void TemplateSpecializationType::Profile(llvm::FoldingSetNodeID &ID,
                                          const ASTContext &Ctx) {
-  Profile(ID, getKeyword(), Template, template_arguments(),
-          isSugared() ? desugar() : QualType(), Ctx);
+  Profile(ID, getKeyword(), Template, template_arguments(), isTypeAlias(),
+          desugar(), Ctx);
 }
 
 void TemplateSpecializationType::Profile(llvm::FoldingSetNodeID &ID,
                                          ElaboratedTypeKeyword Keyword,
                                          TemplateName T,
                                          ArrayRef<TemplateArgument> Args,
-                                         QualType Underlying,
+                                         bool IsTypeAlias, QualType Underlying,
                                          const ASTContext &Context) {
+  assert(IsTypeAlias || Underlying.isNull() || Underlying.isCanonical());
+  if (!Underlying.isNull()) {
+    if (!IsTypeAlias && isa<TemplateSpecializationType>(Underlying))
+      Underlying = QualType();
+  } else {
+    assert(!IsTypeAlias);
+  }
+
   ID.AddInteger(llvm::to_underlying(Keyword));
   T.Profile(ID);
   Underlying.Profile(ID);
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 11e771bc240f1..0c26e5f5b1426 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -1409,7 +1409,7 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, 
FunctionDecl *New,
     bool SameTemplateParameterList = SemaRef.TemplateParameterListsAreEqual(
         NewTemplate, NewTemplate->getTemplateParameters(), OldTemplate,
         OldTemplate->getTemplateParameters(), false, Sema::TPL_TemplateMatch);
-    bool SameReturnType = SemaRef.Context.hasSameType(
+    bool SameReturnType = SemaRef.Context.hasFunctionallyEquivalentType(
         Old->getDeclaredReturnType(), New->getDeclaredReturnType());
     // FIXME(GH58571): Match template parameter list even for non-constrained
     // template heads. This currently ensures that the code prior to C++20 is
@@ -3583,7 +3583,7 @@ bool Sema::FunctionParamTypesAreEqual(ArrayRef<QualType> 
Old,
     QualType NewType =
         Context.removePtrSizeAddrSpace((New.begin() + 
J)->getUnqualifiedType());
 
-    if (!Context.hasSameType(OldType, NewType)) {
+    if (!Context.hasFunctionallyEquivalentType(OldType, NewType)) {
       if (ArgPos)
         *ArgPos = Idx;
       return false;
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index c436b7018a2bd..94fbf3014787b 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -3561,7 +3561,13 @@ static QualType checkBuiltinTemplateIdType(
     // Synthesize a new template argument list, removing duplicates.
     for (auto T : Ts.getPackAsArray()) {
       assert(T.getKind() == clang::TemplateArgument::Type);
-      if (!Seen.insert(T.getAsType().getCanonicalType()).second)
+      // FIXME: BTK__builtin_dedup_pack is not considered an alias template, so
+      // the template specialization cannot store a non-canonical underlying
+      // type.
+      // When that is fixed, this can use getCommonSugar so it doesn't preserve
+      // arbitrary sugar.
+      T = Context.getCanonicalTemplateArgument(T);
+      if (!Seen.insert(T.getAsType()).second)
         continue;
       OutArgs.push_back(T);
     }
@@ -8907,14 +8913,14 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
   if (isPartialSpecialization) {
     if (CheckTemplatePartialSpecializationArgs(TemplateNameLoc, ClassTemplate,
                                                TemplateArgs.size(),
-                                               CTAI.CanonicalConverted))
+                                               CTAI.SugaredConverted))
       return true;
 
     // FIXME: Move this to CheckTemplatePartialSpecializationArgs so we
     // also do it during instantiation.
     if (!Name.isDependent() &&
         !TemplateSpecializationType::anyDependentTemplateArguments(
-            TemplateArgs, CTAI.CanonicalConverted)) {
+            TemplateArgs, CTAI.SugaredConverted)) {
       Diag(TemplateNameLoc, diag::err_partial_spec_fully_specialized)
         << ClassTemplate->getDeclName();
       isPartialSpecialization = false;
@@ -8922,15 +8928,20 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
     }
   }
 
+  SmallVector<TemplateArgument, 4> FunctionallyEquivalentConverted =
+      CTAI.SugaredConverted;
+  Context.canonicalizeTemplateArguments(FunctionallyEquivalentConverted,
+                                        /*FunctionallyEquivalent=*/true);
+
   void *InsertPos = nullptr;
   ClassTemplateSpecializationDecl *PrevDecl = nullptr;
 
   if (isPartialSpecialization)
     PrevDecl = ClassTemplate->findPartialSpecialization(
-        CTAI.CanonicalConverted, TemplateParams, InsertPos);
+        FunctionallyEquivalentConverted, TemplateParams, InsertPos);
   else
-    PrevDecl =
-        ClassTemplate->findSpecialization(CTAI.CanonicalConverted, InsertPos);
+    PrevDecl = ClassTemplate->findSpecialization(
+        FunctionallyEquivalentConverted, InsertPos);
 
   ClassTemplateSpecializationDecl *Specialization = nullptr;
 
@@ -8947,7 +8958,8 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
     // this explicit specialization or friend declaration.
     Specialization = ClassTemplateSpecializationDecl::Create(
         Context, Kind, ClassTemplate->getDeclContext(), KWLoc, TemplateNameLoc,
-        ClassTemplate, CTAI.CanonicalConverted, CTAI.StrictPackMatch, 
PrevDecl);
+        ClassTemplate, FunctionallyEquivalentConverted, CTAI.StrictPackMatch,
+        PrevDecl);
     Specialization->setTemplateArgsAsWritten(TemplateArgs);
     SetNestedNameSpecifier(*this, Specialization, SS);
     if (TemplateParameterLists.size() > 0) {
@@ -8958,14 +8970,13 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
     if (!PrevDecl)
       ClassTemplate->AddSpecialization(Specialization, InsertPos);
   } else {
-    CanQualType CanonType = CanQualType::CreateUnsafe(
-        Context.getCanonicalTemplateSpecializationType(
-            ElaboratedTypeKeyword::None,
-            TemplateName(ClassTemplate->getCanonicalDecl()),
-            CTAI.CanonicalConverted));
-    if (Context.hasSameType(
-            CanonType,
-            ClassTemplate->getCanonicalInjectedSpecializationType(Context)) &&
+    QualType CanonType = Context.getTemplateSpecializationType(
+        ElaboratedTypeKeyword::None,
+        TemplateName(ClassTemplate->getCanonicalDecl()),
+        FunctionallyEquivalentConverted, /*CanonicalArgs=*/{},
+        /*Underlying=*/QualType(), /*Unique=*/true);
+    if (CanonType ==
+            ClassTemplate->getCanonicalInjectedSpecializationType(Context) &&
         (!Context.getLangOpts().CPlusPlus20 ||
          !TemplateParams->hasAssociatedConstraints())) {
       // C++ [temp.class.spec]p9b3:
@@ -8992,7 +9003,8 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
     ClassTemplatePartialSpecializationDecl *Partial =
         ClassTemplatePartialSpecializationDecl::Create(
             Context, Kind, DC, KWLoc, TemplateNameLoc, TemplateParams,
-            ClassTemplate, CTAI.CanonicalConverted, CanonType, PrevPartial);
+            ClassTemplate, FunctionallyEquivalentConverted,
+            Context.getCanonicalType(CanonType), PrevPartial);
     Partial->setTemplateArgsAsWritten(TemplateArgs);
     SetNestedNameSpecifier(*this, Partial, SS);
     if (TemplateParameterLists.size() > 1 && SS.isSet()) {
diff --git a/clang/test/CXX/drs/cwg20xx.cpp b/clang/test/CXX/drs/cwg20xx.cpp
index 75b4094283db0..f669d460f4d09 100644
--- a/clang/test/CXX/drs/cwg20xx.cpp
+++ b/clang/test/CXX/drs/cwg20xx.cpp
@@ -64,6 +64,19 @@ namespace cwg2061 { // cwg2061: 2.7
 #endif // C++11
 } // namespace cwg2061
 
+namespace cwg2064 { // cwg2064: 23
+#if __cplusplus >= 201103L
+  template<typename T> struct X {
+    template<typename U> struct Y {};
+  };
+  template<typename T> void g() {
+    X<decltype(sizeof(T))>::Y<int> y; // ok
+    return X<decltype(sizeof(T))>::f();
+    // expected-error@-1 {{no member named 'f' in 'cwg2064::X<unsigned long>'}}
+  }
+#endif
+}
+
 namespace cwg2076 { // cwg2076: 13
 #if __cplusplus >= 201103L
   namespace std_example {
@@ -86,7 +99,7 @@ namespace cwg2076 { // cwg2076: 13
     operator string_view() const;
   };
 
-  void foo(const string &); // #cwg2076-foo 
+  void foo(const string &); // #cwg2076-foo
   void bar(string_view); // #cwg2076-bar
 
   void func(const string &arg) {
@@ -369,7 +382,7 @@ int f()
   return 0;
 }
 } // namespace GH42233
-} // namespace cwg2091 
+} // namespace cwg2091
 
 namespace cwg2094 { // cwg2094: 5
   struct A { int n; };
diff --git a/clang/test/CXX/temp/temp.decls/temp.mem/p5.cpp 
b/clang/test/CXX/temp/temp.decls/temp.mem/p5.cpp
index 65d8345ecc3aa..a0c9e931c3298 100644
--- a/clang/test/CXX/temp/temp.decls/temp.mem/p5.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.mem/p5.cpp
@@ -92,7 +92,7 @@ template X0::operator B<0>() const; // expected-error 
{{undefined function templ
 // index expression as non-canonical is extra bad.
 template X0::operator C<int[1]>() const; // expected-error {{undefined 
function template 'operator C<type-parameter-0-0[V]>'}}
 #if __cplusplus >= 201103L
-template X0::operator D<int, 0>() const; // expected-error {{undefined 
function template 'operator D<decltype(value-parameter-0-0), 
value-parameter-0-0>'}}
+template X0::operator D<int, 0>() const; // expected-error {{undefined 
function template 'operator D<int, value-parameter-0-0>'}}
 #endif
 
 void test_X0(X0 x0, const X0 &x0c) {
diff --git a/clang/test/CodeGenCXX/GH190495.cpp 
b/clang/test/CodeGenCXX/GH190495.cpp
new file mode 100644
index 0000000000000..87984f8c34ac1
--- /dev/null
+++ b/clang/test/CodeGenCXX/GH190495.cpp
@@ -0,0 +1,29 @@
+// RUN: %clang_cc1 -std=c++26 -emit-llvm %s -o - -triple=x86_64-apple-darwin9
+
+namespace std {
+class partial_ordering {};
+class strong_ordering {
+public:
+  operator partial_ordering();
+};
+using size_t = decltype(sizeof(int));
+template <size_t> struct __priority_tag {};
+namespace __partial_order {
+struct __fn {
+  template <class _Tp, class _Up>
+  constexpr auto __go(_Tp __t, _Up __u, __priority_tag<2>)
+      -> decltype(partial_ordering(partial_order(__t, __u))) {}
+  template <class _Tp, class _Up> auto operator()(_Tp __t, _Up __u) {
+    __go(__t, __u, __priority_tag<2>());
+  }
+};
+}
+auto partial_order = __partial_order::__fn{};
+namespace {
+struct A {};
+strong_ordering partial_order(A, A) {
+  A a;
+  std::partial_order(a, a);
+}
+}
+}
diff --git a/clang/test/CodeGenCXX/mangle-subst.cpp 
b/clang/test/CodeGenCXX/mangle-subst.cpp
index 524e0febe479a..fa59ca2dfb3d0 100644
--- a/clang/test/CodeGenCXX/mangle-subst.cpp
+++ b/clang/test/CodeGenCXX/mangle-subst.cpp
@@ -69,7 +69,7 @@ namespace NS {
 
 namespace NS {
   // CHECK: @_ZN2NS1fERNS_1CE
-  void f(C&) { } 
+  void f(C&) { }
 }
 
 namespace Test1 {
@@ -123,3 +123,14 @@ struct Inst : public A::Impl<A::Wrap> {};
 void Test() { Inst a; }
 
 }
+
+namespace InstantiationDependentDecltype {
+  struct a { a(char); };
+  struct b { a c(); };
+  // FIXME: This mangling is incorrect; the second decltype type should be a
+  // substitution for the first.
+  // CHECK: 
@_ZN30InstantiationDependentDecltype1fINS_1bEEEvDTcvNS_1aEcldtcvT__E1cEEDTcvS2_cldtcvS3__E1cEES3_S3_S2_S2_
+  // FIXME: 
@_ZN30InstantiationDependentDecltype1fINS_1bEEEvDTcvNS_1aEcldtcvT__E1cEES4_S3_S3_S2_S2_
+  template<typename d> void f(decltype(a(d().c())), decltype(a(d().c())), d, 
d, a, a);
+  void g(a a, b b) { f(a, a, b, b, a, a); }
+}
diff --git a/clang/test/Sema/invalid-bitwidth-expr.mm 
b/clang/test/Sema/invalid-bitwidth-expr.mm
index 9e577300eb1c8..25930e5d4ef7e 100644
--- a/clang/test/Sema/invalid-bitwidth-expr.mm
+++ b/clang/test/Sema/invalid-bitwidth-expr.mm
@@ -26,6 +26,7 @@ auto func() {
 auto func() {
   // error-bit should be propagated from TemplateArgument to NestNameSpecifier.
   class Base<decltype(Foo(T()))>::type C; // expected-error {{no matching 
function for call to 'Foo'}}
+  // expected-error@-1 {{no class named 'type' in 'Base<bool>'}}
   return C;
 }
 struct Z {
diff --git a/clang/test/SemaCXX/decltype.cpp b/clang/test/SemaCXX/decltype.cpp
index 45a4c4cf1ac86..004d48415b0e7 100644
--- a/clang/test/SemaCXX/decltype.cpp
+++ b/clang/test/SemaCXX/decltype.cpp
@@ -135,7 +135,7 @@ namespace GH97646 {
   template<bool B>
   void f() {
     decltype(B) x = false;
-    !x;
+    !x; // expected-warning {{expression result unused}}
   }
 }
 
@@ -241,6 +241,15 @@ void test() { (void)C::XBitMask<0>; }
 }
 #endif
 
+namespace value_dependent {
+  template<int V> void f() {
+    decltype(V) x = nullptr;
+    // expected-error@-1 {{cannot initialize a variable of type 'decltype(V)' 
(aka 'int') with an rvalue of type 'std::nullptr_t'}}
+  }
+  template<typename T> decltype(int(T())) g() {}
+  template<typename T> decltype(int(T(0))) g() {}
+} // namespace value_dependent
+
 template<typename>
 class conditional {
 };
diff --git a/clang/test/SemaCXX/source_location.cpp 
b/clang/test/SemaCXX/source_location.cpp
index eaa6cb04c5d1c..13846553421ce 100644
--- a/clang/test/SemaCXX/source_location.cpp
+++ b/clang/test/SemaCXX/source_location.cpp
@@ -9,7 +9,9 @@
 // RUN: %clang_cc1 -std=c++2b -fcxx-exceptions -DUSE_CONSTEVAL -DPAREN_INIT 
-fexceptions -fexperimental-new-constant-interpreter -DNEW_INTERP -verify %s
 // RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fms-extensions -DMS 
-fexceptions -fexperimental-new-constant-interpreter -DNEW_INTERP 
-fms-compatibility -verify %s
 // RUN: %clang_cc1 -std=c++2a -fcxx-exceptions -fms-extensions -DMS 
-DUSE_CONSTEVAL -fexceptions -fexperimental-new-constant-interpreter 
-DNEW_INTERP -verify -fms-compatibility %s
+#ifndef MS
 // expected-no-diagnostics
+#endif
 
 #define assert(...) ((__VA_ARGS__) ? ((void)0) : throw 42)
 #define CURRENT_FROM_MACRO() SL::current()
@@ -1088,6 +1090,9 @@ namespace GH178324 {
     using e = int;
   };
   void current(const char * = __builtin_FUNCSIG());
-  template <class> void c() { decltype(a(current()))::e; }
+  template <class> void c() {
+    decltype(a(current()))::e;
+    // expected-warning@-1 {{declaration does not declare anything}}
+  }
 } // namespace GH178324
 #endif
diff --git a/clang/test/SemaCXX/typeof.cpp b/clang/test/SemaCXX/typeof.cpp
index 421cfc59f5311..e6d3dbccf7321 100644
--- a/clang/test/SemaCXX/typeof.cpp
+++ b/clang/test/SemaCXX/typeof.cpp
@@ -8,6 +8,6 @@ namespace GH97646 {
   template<bool B>
   void f() {
     __typeof__(B) x = false;
-    !x;
+    !x; // expected-warning {{expression result unused}}
   }
 }
diff --git a/clang/test/SemaTemplate/concepts-out-of-line-def.cpp 
b/clang/test/SemaTemplate/concepts-out-of-line-def.cpp
index 9811b18f4301b..0939a2a7fbb5f 100644
--- a/clang/test/SemaTemplate/concepts-out-of-line-def.cpp
+++ b/clang/test/SemaTemplate/concepts-out-of-line-def.cpp
@@ -533,12 +533,12 @@ template <class T>
 void X<T>::foo() requires requires { requires is_not_same_v<T, int>; } {} // ok
 
 template <class T>
-void X<T>::bar(decltype(requires { requires something_interesting<T>; })) {}
-// expected-error@-1{{definition of 'bar' does not match any declaration}}
-// expected-note@#defined-here{{defined here}}
+void X<T>::bar(decltype(requires { requires something_interesting<T>; })) {} 
// #GH74314-bar-prev-def-here
 
 template <class T>
 void X<T>::bar(decltype(requires { requires is_not_same_v<T, int>; })) {}
+// expected-error@-1 {{redefinition of 'bar'}}
+// expected-note@#GH74314-bar-prev-def-here {{previous definition is here}}
 } // namespace GH74314
 
 namespace GH56482 {
diff --git a/clang/test/SemaTemplate/concepts.cpp 
b/clang/test/SemaTemplate/concepts.cpp
index ac80d16b4ccf8..7a3bf419eab68 100644
--- a/clang/test/SemaTemplate/concepts.cpp
+++ b/clang/test/SemaTemplate/concepts.cpp
@@ -1824,6 +1824,16 @@ namespace GH176402 {
     recursiveLambda(recursiveLambda, 5);
   }
 }
+
+namespace GH61818 {
+  template <typename T> concept C = true;
+  template <typename T> struct A;
+  template <> struct A<bool> { using type = bool; };
+
+  template <typename T>
+  void f(A<decltype(C<T>)>::type); // OK, no 'typename' needed
+} // namespace GH61818
+
 namespace GH191016 {
   template <typename T = int>
   struct S {
diff --git a/clang/test/SemaTemplate/deduction-guide.cpp 
b/clang/test/SemaTemplate/deduction-guide.cpp
index 9e5756ffec3fc..29693e1918263 100644
--- a/clang/test/SemaTemplate/deduction-guide.cpp
+++ b/clang/test/SemaTemplate/deduction-guide.cpp
@@ -854,17 +854,17 @@ CC c{};
 
 // CHECK-LABEL: Dumping GH133132::<deduction guide for CC>:
 // CHECK-NEXT:  FunctionTemplateDecl {{.+}} implicit <deduction guide for CC>
-// CHECK-NEXT:  |-NonTypeTemplateParmDecl {{.+}} 'int' depth 0 index 0 N
-// CHECK-NEXT:  | `-TemplateArgument {{.+}} expr '42'
-// CHECK-NEXT:  |   `-IntegerLiteral {{.+}} 'int' 42
-// CHECK-NEXT:  |-TemplateTypeParmDecl {{.+}} class depth 0 index 1 U
-// CHECK-NEXT:  | `-TemplateArgument type 'A<decltype(N)>'
-// CHECK-NEXT:  |   `-TemplateSpecializationType {{.+}} 'A<decltype(N)>' 
dependent
+// CHECK-NEXT:  |-TemplateTypeParmDecl {{.+}} class depth 0 index 0 U
+// CHECK-NEXT:  | `-TemplateArgument type 'A<decltype(N)>':'GH133132::A<int>'
+// CHECK-NEXT:  |   `-TemplateSpecializationType {{.+}} 'A<decltype(N)>' sugar 
instantiation_dependent
 // CHECK-NEXT:  |     |-name: 'A':'GH133132::A' qualified
 // CHECK-NEXT:  |     | `-ClassTemplateDecl {{.+}} A
-// CHECK-NEXT:  |     `-TemplateArgument type 'decltype(N)'
-// CHECK-NEXT:  |       `-DecltypeType {{.+}} 'decltype(N)' dependent
-// CHECK-NEXT:  |         `-DeclRefExpr {{.+}} 'int' NonTypeTemplateParm 
{{.+}} 'N' 'int'
+// CHECK-NEXT:  |     |-TemplateArgument type 'decltype(N)':'int'
+// CHECK-NEXT:  |     | `-DecltypeType {{.+}} 'decltype(N)' sugar 
instantiation_dependent
+// CHECK-NEXT:  |     |   |-DeclRefExpr {{.+}} 'int' NonTypeTemplateParm 
{{.+}} 'N' 'int'
+// CHECK-NEXT:  |     |   `-BuiltinType {{.+}} 'int'
+// CHECK-NEXT:  |     `-RecordType {{.+}} 'GH133132::A<int>' canonical
+// CHECK-NEXT:  |       `-ClassTemplateSpecialization {{.+}} 'A'
 // CHECK-NEXT:  |-TypeTraitExpr {{.+}} 'bool' __is_deducible
 // CHECK-NEXT:  | |-DeducedTemplateSpecializationType {{.+}} 'GH133132::CC' 
dependent
 // CHECK-NEXT:  | | `-name: 'GH133132::CC'
@@ -872,14 +872,13 @@ CC c{};
 // CHECK-NEXT:  | `-TemplateSpecializationType {{.+}} 'GH133132::A<U>' 
dependent
 // CHECK-NEXT:  |   |-name: 'GH133132::A'
 // CHECK-NEXT:  |   | `-ClassTemplateDecl {{.+}} A
-// CHECK-NEXT:  |   `-TemplateArgument type 'U':'type-parameter-0-1'
+// CHECK-NEXT:  |   `-TemplateArgument type 'U':'type-parameter-0-0'
 // CHECK-NEXT:  |     `-SubstTemplateTypeParmType {{.+}} 'U' sugar dependent 
class depth 0 index 0 _Ty
 // CHECK-NEXT:  |       |-FunctionTemplate {{.+}} '<deduction guide for A>'
-// CHECK-NEXT:  |       `-TemplateTypeParmType {{.+}} 'U' dependent depth 0 
index 1
+// CHECK-NEXT:  |       `-TemplateTypeParmType {{.+}} 'U' dependent depth 0 
index 0
 // CHECK-NEXT:  |         `-TemplateTypeParm {{.+}} 'U'
 // CHECK-NEXT:  |-CXXDeductionGuideDecl {{.+}} implicit <deduction guide for 
CC> 'auto () -> GH133132::A<U>'
 // CHECK-NEXT:  `-CXXDeductionGuideDecl {{.+}} implicit used <deduction guide 
for CC> 'auto () -> GH133132::A<GH133132::A<int>>' implicit_instantiation
-// CHECK-NEXT:    |-TemplateArgument integral '42'
 // CHECK-NEXT:    `-TemplateArgument type 'GH133132::A<int>'
 // CHECK-NEXT:      `-RecordType {{.+}} 'GH133132::A<int>'
 // CHECK-NEXT:        `-ClassTemplateSpecialization {{.+}} 'A'
diff --git a/clang/test/SemaTemplate/dependent-expr.cpp 
b/clang/test/SemaTemplate/dependent-expr.cpp
index ce210d9b74f6d..31ee43ab9501c 100644
--- a/clang/test/SemaTemplate/dependent-expr.cpp
+++ b/clang/test/SemaTemplate/dependent-expr.cpp
@@ -13,12 +13,12 @@ namespace PR6045 {
     static const unsigned int member = r;
     void f();
   };
-  
+
   template<unsigned int r>
   const unsigned int A<r>::member;
-  
+
   template<unsigned int r>
-  void A<r>::f() 
+  void A<r>::f()
   {
     unsigned k;
     (void)(k % member);
@@ -129,7 +129,7 @@ namespace PR45083 {
   template<typename> void f() {
     decltype(({})) x; // expected-error {{incomplete type}}
   }
-  template void f<int>(); // expected-note {{instantiation of}}
+  template void f<int>();
 
   template<typename> auto g() {
     auto c = [](auto, int) -> decltype(({})) {};
diff --git a/clang/test/SemaTemplate/injected-class-name.cpp 
b/clang/test/SemaTemplate/injected-class-name.cpp
index 93a7231b8c7b7..e1a0573393d11 100644
--- a/clang/test/SemaTemplate/injected-class-name.cpp
+++ b/clang/test/SemaTemplate/injected-class-name.cpp
@@ -69,3 +69,47 @@ namespace ConflictingRedecl {
     template<typename> struct Nested; // expected-error {{member 'Nested' has 
the same name as its class}}
   };
 }
+
+namespace TwoLevels {
+  template <class> struct A {
+    template <bool> A f();
+  };
+  template <class T> template <bool> A<T> A<T>::f() {}
+} // namespace TwoLevels
+
+namespace TwoLevelsAlias1 {
+  template <class> struct A;
+  template <class T> using alias = T;
+  template <class T> struct B {
+    using type = alias<T>;
+    template <class> A<type> f();
+  };
+  template <class T> template <class>
+  A<typename B<T>::type> B<T>::f() {}
+} // namespace TwoLevelsAlias
+
+namespace TwoLevelsAlias2 {
+  template <class> struct A;
+  template <class T> using alias = typename A<T>::type;
+  template <class T> struct B {
+    template <class> typename A<T>::type f();
+  };
+  template <class T> template <class> alias<T> B<T>::f() {}
+} // namespace TwoLevelsAlias2
+
+namespace TwoLevelsAlias3 {
+  template <class T> using void_t = void;
+  template <class T> struct A { // expected-note {{defined here}}
+    template <int> void_t<typename T::type> f();
+  };
+  template <class T> template <int> void A<T>::f() {} // expected-error {{does 
not match}}
+} // namspace TwoLevelsAlias3
+
+namespace Unique {
+  template <class T> struct A {
+    template <class> A<T> f1();
+    template <class> A<T> f2();
+  };
+  template <class T> template <class> A<T> A<T>::f1() {}
+  template <class T> template <class> A<T> A<T>::f2() {}
+} // namespace Unique
diff --git a/clang/test/SemaTemplate/instantiation-dependence.cpp 
b/clang/test/SemaTemplate/instantiation-dependence.cpp
index 1a97dbf769ec6..df2acd4525e93 100644
--- a/clang/test/SemaTemplate/instantiation-dependence.cpp
+++ b/clang/test/SemaTemplate/instantiation-dependence.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++23 -verify %s
+// RUN: %clang_cc1 -std=c++26 -verify %s
 
 // Ensure we substitute into instantiation-dependent but non-dependent
 // constructs. The poster-child for this is...
@@ -48,17 +48,13 @@ namespace PR46791 { // also PR45782
     static constexpr int specialization = 0;
   };
 
-  // FIXME: Per a strict interpretation of the C++ rules, the two void_t<...>
-  // types below are equivalent -- we only (effectively) do token-by-token
-  // comparison for *expressions* appearing within types. But all other
-  // implementations accept this, using rules that are unclear.
   template<typename T>
-  struct trait<T, void_t<typename T::value_type>> { // expected-note 
{{previous}} FIXME-note {{matches}}
+  struct trait<T, void_t<typename T::value_type>> { // expected-note 
{{matches}}
     static constexpr int specialization = 1;
   };
 
   template<typename T>
-  struct trait<T, void_t<typename T::element_type>> { // expected-error 
{{redefinition}} FIXME-note {{matches}}
+  struct trait<T, void_t<typename T::element_type>> { // expected-note 
{{matches}}
     static constexpr int specialization = 2;
   };
 
@@ -68,11 +64,9 @@ namespace PR46791 { // also PR45782
   struct D : B, C {};
 
   static_assert(trait<A>::specialization == 0);
-  static_assert(trait<B>::specialization == 1); // FIXME expected-error 
{{failed}} \
-                                                // expected-note {{evaluates 
to '0 == 1'}}
-  static_assert(trait<C>::specialization == 2); // FIXME expected-error 
{{failed}} \
-                                                // expected-note {{evaluates 
to '0 == 2'}}
-  static_assert(trait<D>::specialization == 0); // FIXME-error {{ambiguous 
partial specialization}}
+  static_assert(trait<B>::specialization == 1);
+  static_assert(trait<C>::specialization == 2);
+  static_assert(trait<D>::specialization == 0); // expected-error {{ambiguous 
partial specialization}}
 }
 
 namespace TypeQualifier {
@@ -103,3 +97,75 @@ namespace MemberOfInstantiationDependentBase {
   void q(C1<int> *c) { c->f(0); }
   void q(C2<int> *c) { c->f(0); }
 }
+
+namespace GH8740 {
+  struct A { typedef int T; };
+  template<int> struct U { typedef int T; };
+  template<typename> struct S {
+    A a;
+    int n = decltype(a)::T();
+    int m = U<sizeof(a)>::T();
+  };
+  S<char> s;
+} // namespace GH8740
+
+namespace NonInstDependentArgs1 {
+  template<typename T, typename = void> struct X;
+  template<typename T> struct X<T, void_t<char>> {}; // expected-note  
{{previous}}
+  template<typename T> struct X<T, void_t<void>> {}; // expected-error 
{{redefinition}}
+} // namespace NonInstDependentArgs1
+
+namespace NonInstDependentArgs2 {
+  template<typename T, typename = void> struct X;
+  template<typename T> struct X<T, void_t<T, void>> {};
+  template<typename T> struct X<T, void_t<T, char>> {};
+} // namespace NonInstDependentArgs2
+
+namespace Level1 {
+  template<typename T, typename = void> struct X;
+  template<typename T> struct X<T, void_t<T>> {};
+  template<typename T> struct X<T, void_t<T*>> {};
+} // namespace Level1
+
+namespace Level2 {
+  template<typename T, typename = void> struct X;
+  template<typename T> struct X<T, void_t<void_t<T>>> {};
+  template<typename T> struct X<T, void_t<void_t<T*>>> {};
+} // namespace Level2
+
+namespace IndirectAlias1 {
+  template<class T> using alias = void_t<T>;
+  template<typename T, typename = void> struct X;
+  template<typename T> struct X<T, void_t<T>> {}; // expected-note  
{{previous}}
+  template<typename T> struct X<T, alias<T>> {};  // expected-error 
{{redefinition}}
+} // namspace IndirectAlias1
+
+namespace IndirectAlias2 {
+  template<class T, class> using alias1 = T;
+  template<class T, class U> using alias2 = alias1<T, U>;
+  template<typename T, typename = void> struct X;
+  template<typename T> struct X<T, T> {};
+  template<typename T> struct X<T, alias1<T, T>> {}; // expected-note  
{{previous}}
+  template<typename T> struct X<T, alias2<T, T>> {}; // expected-error 
{{redefinition}}
+} // namespaceIndirectAlias2
+
+namespace PackIndexing {
+  // FIXME: This should not be a redefinition.
+  template<class ...Ts> using alias = Ts...[0];
+  template<typename T, typename = void> struct X;
+  template<typename T> struct X<T, T> {};                          // 
expected-note  {{previous}}
+  template<typename T> struct X<T, alias<T, typename T::type>> {}; // 
expected-error {{redefinition}}
+} // namespace PackIndexing
+
+namespace DeclType {
+  template<typename T, typename = void> struct X;
+  template<typename T> struct X<T, decltype(void())> {};
+  template<typename T> struct X<T, decltype(void_t<typename T::type>())> {}; 
// FIXME-note  {{previous}}
+  template<typename T> struct X<T, decltype(void_t<typename T::type>())> {}; 
// FIXME-error {{redefinition}}
+} // namespace DeclType
+
+namespace UnaryTransformDecay {
+  template<class T, class U = void> struct X;
+  template<class T> struct X<T, __decay(int[T()])> {}; // FIXME-note  
{{previous}}
+  template<class T> struct X<T, __decay(int[T()])> {}; // FIXME-error 
{{redefinition}}
+} // namespace UnaryTransformDecay
diff --git a/clang/test/SemaTemplate/partial-spec-instantiate.cpp 
b/clang/test/SemaTemplate/partial-spec-instantiate.cpp
index 44b58008a1d33..ab48050938991 100644
--- a/clang/test/SemaTemplate/partial-spec-instantiate.cpp
+++ b/clang/test/SemaTemplate/partial-spec-instantiate.cpp
@@ -165,3 +165,10 @@ namespace GH162855 {
   template struct C<D<int>>;
 } // namespace GH162855
 #endif
+
+namespace DependentWithQualifier {
+  template <class> struct A;
+  template <class> struct B;
+  template <class T> struct A<const B<T> > {};
+  template struct A<const B<int> >;
+} // namespace DependentWithQualifier
diff --git a/clang/test/SemaTemplate/temp_arg_template_p0522.cpp 
b/clang/test/SemaTemplate/temp_arg_template_p0522.cpp
index bde811c3bf685..1d97deeb9214a 100644
--- a/clang/test/SemaTemplate/temp_arg_template_p0522.cpp
+++ b/clang/test/SemaTemplate/temp_arg_template_p0522.cpp
@@ -135,8 +135,17 @@ namespace Auto {
 
   int n;
   template<auto A, decltype(A) B = &n> struct SubstFailure;
-  TInt<SubstFailure> isf; // FIXME: this should be ill-formed
+  // expected-error@-1 {{value of type 'int *' is not implicitly convertible 
to 'decltype(value-parameter-0-0)' (aka 'int')}}
+  // expected-note@#TInt {{while checking a default template argument used 
here}}
+  TInt<SubstFailure> isf;
+  // expected-note@-1 {{template template argument has different template 
parameters than its corresponding template template parameter}}
   TIntPtr<SubstFailure> ipsf;
+
+  template<template<auto A, auto B, decltype(A)> typename C> struct 
TAutoAutoFirst {};
+  template<auto A, auto B, decltype(A)> struct AutoAutoFirst;
+  template<auto A, auto B, decltype(B)> struct AutoAutoSecond;
+  TAutoAutoFirst<AutoAutoFirst> aaf;
+  TAutoAutoFirst<AutoAutoSecond> aas; // FIXME: this should be rejected due to 
parameter mismatch
 }
 
 namespace GH62529 {

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to