[clang] [clang-tools-extra] [lldb] [clang] implement CWG2064: instantiation-dependency improvements (PR #190495)

Matheus Izvekov via cfe-commits cfe-commits at lists.llvm.org
Wed Apr 15 18:48:29 PDT 2026


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

>From f6c6be1439a7e9c8242482ace2d27cced054711b Mon Sep 17 00:00:00 2001
From: Matheus Izvekov <mizvekov at gmail.com>
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.

This patch also implements what's neceessary to allow overloading
on pure differences in instantiation dependence, making `std::void_t`
usable for SFINAE purposes.

This also readds a few test cases from da98651, which was a previous attempt
at resolving CWG2064.

Fixes #8740
Fixes #61818
Fixes #190388
---
 .../clang-tidy/modernize/LoopConvertUtils.cpp |    4 +-
 .../clang-tidy/utils/ASTUtils.cpp             |    7 +-
 clang-tools-extra/clangd/AST.cpp              |    6 +-
 clang-tools-extra/clangd/InlayHints.cpp       |    2 +-
 clang/docs/ReleaseNotes.rst                   |    3 +
 clang/include/clang/AST/ASTContext.h          |  109 +-
 clang/include/clang/AST/DeclTemplate.h        |    3 +-
 clang/include/clang/AST/DependenceFlags.h     |    8 +-
 clang/include/clang/AST/Expr.h                |   10 +-
 clang/include/clang/AST/PropertiesBase.td     |    7 +-
 clang/include/clang/AST/RecursiveASTVisitor.h |    5 +-
 clang/include/clang/AST/Stmt.h                |   10 +-
 clang/include/clang/AST/TemplateBase.h        |   19 +-
 clang/include/clang/AST/TextNodeDumper.h      |    1 +
 clang/include/clang/AST/TypeBase.h            |   46 +-
 clang/include/clang/AST/TypeLoc.h             |    5 +-
 clang/include/clang/AST/TypeProperties.td     |    5 +-
 clang/include/clang/Basic/Specifiers.h        |   74 ++
 clang/include/clang/Sema/Sema.h               |    1 +
 .../clang/Serialization/ASTRecordReader.h     |   17 +-
 .../clang/Serialization/ASTRecordWriter.h     |    4 +
 clang/lib/AST/ASTContext.cpp                  | 1063 ++++++++++++++---
 clang/lib/AST/ASTDiagnostic.cpp               |   22 +-
 clang/lib/AST/ASTImporter.cpp                 |    5 +-
 clang/lib/AST/DeclTemplate.cpp                |   22 +-
 clang/lib/AST/ItaniumMangle.cpp               |  246 ++--
 clang/lib/AST/JSONNodeDumper.cpp              |   12 +-
 clang/lib/AST/ODRHash.cpp                     |    6 +-
 clang/lib/AST/StmtProfile.cpp                 |   70 +-
 clang/lib/AST/TemplateBase.cpp                |   16 +-
 clang/lib/AST/TemplateName.cpp                |    7 +-
 clang/lib/AST/TextNodeDumper.cpp              |   25 +-
 clang/lib/AST/Type.cpp                        |  144 +--
 clang/lib/AST/TypePrinter.cpp                 |    3 +-
 .../Frontend/Rewrite/RewriteModernObjC.cpp    |    6 +-
 clang/lib/Sema/HLSLExternalSemaSource.cpp     |    7 +-
 clang/lib/Sema/SemaCXXScopeSpec.cpp           |   43 +-
 clang/lib/Sema/SemaChecking.cpp               |    4 +-
 clang/lib/Sema/SemaConcept.cpp                |   14 +-
 clang/lib/Sema/SemaDecl.cpp                   |    5 +-
 clang/lib/Sema/SemaDeclCXX.cpp                |    4 +-
 clang/lib/Sema/SemaExceptionSpec.cpp          |    9 +-
 clang/lib/Sema/SemaExpr.cpp                   |    2 +-
 clang/lib/Sema/SemaExprCXX.cpp                |    1 +
 clang/lib/Sema/SemaExprMember.cpp             |    4 +-
 clang/lib/Sema/SemaLookup.cpp                 |    3 +-
 clang/lib/Sema/SemaOpenACCAtomic.cpp          |   12 +-
 clang/lib/Sema/SemaOpenMP.cpp                 |   52 +-
 clang/lib/Sema/SemaOverload.cpp               |   45 +-
 clang/lib/Sema/SemaTemplate.cpp               |  125 +-
 clang/lib/Sema/SemaTemplateDeduction.cpp      |   62 +-
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |   23 +-
 clang/lib/Sema/SemaTemplateVariadic.cpp       |   15 +-
 clang/lib/Sema/SemaType.cpp                   |    4 +-
 clang/lib/Sema/TreeTransform.h                |   24 +-
 clang/lib/Serialization/ASTReader.cpp         |    7 +-
 clang/lib/Serialization/ASTReaderDecl.cpp     |    9 +-
 clang/lib/Serialization/ASTWriter.cpp         |    1 +
 clang/test/AST/ast-dump-templates.cpp         |    8 +-
 clang/test/CXX/drs/cwg20xx.cpp                |   13 +
 .../CXX/temp/temp.decls/temp.alias/p3.cpp     |    5 +-
 .../test/CXX/temp/temp.decls/temp.mem/p5.cpp  |    2 +-
 .../test/CodeGenCXX/mangle-exception-spec.cpp |   11 +-
 clang/test/CodeGenCXX/mangle-subst.cpp        |   10 +-
 clang/test/CodeGenCXX/mangle-template.cpp     |   37 +-
 .../CodeGenCXX/microsoft-abi-default-cc.cpp   |    2 +-
 clang/test/Sema/array-parameter.cpp           |    2 +-
 clang/test/Sema/invalid-bitwidth-expr.mm      |    1 +
 clang/test/SemaCXX/alias-template.cpp         |    4 +-
 clang/test/SemaCXX/decltype.cpp               |   15 +-
 clang/test/SemaCXX/source_location.cpp        |    7 +-
 clang/test/SemaCXX/sugar-common-types.cpp     |   18 +-
 clang/test/SemaCXX/typeof.cpp                 |    2 +-
 clang/test/SemaTemplate/GH164330.cpp          |    2 +-
 .../SemaTemplate/concepts-out-of-line-def.cpp |    7 +-
 clang/test/SemaTemplate/concepts.cpp          |   10 +
 clang/test/SemaTemplate/deduction-guide.cpp   |   23 +-
 clang/test/SemaTemplate/dependent-expr.cpp    |    2 +-
 .../SemaTemplate/dependent-type-identity.cpp  |    8 +-
 .../test/SemaTemplate/injected-class-name.cpp |   44 +
 .../test/SemaTemplate/instantiate-expr-1.cpp  |   10 +
 .../SemaTemplate/instantiation-dependence.cpp |  299 ++++-
 .../SemaTemplate/partial-spec-instantiate.cpp |    7 +
 .../SemaTemplate/temp_arg_nontype_cxx1z.cpp   |   18 +-
 .../SemaTemplate/temp_arg_template_p0522.cpp  |   11 +-
 clang/unittests/AST/TypePrinterTest.cpp       |    6 +-
 lldb/unittests/Symbol/TestTypeSystemClang.cpp |    3 +-
 87 files changed, 2303 insertions(+), 752 deletions(-)

diff --git a/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp b/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp
index e9932ba444bc9..2abbeb9df5c54 100644
--- a/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/LoopConvertUtils.cpp
@@ -230,7 +230,7 @@ template <typename ContainerT>
 static bool containsExpr(ASTContext *Context, const ContainerT *Container,
                          const Expr *E) {
   llvm::FoldingSetNodeID ID;
-  E->Profile(ID, *Context, true);
+  E->Profile(ID, *Context, CanonicalizationKind::Structural);
   return llvm::any_of(*Container,
                       [&](const auto &I) { return ID == I.second; });
 }
@@ -468,7 +468,7 @@ void ForLoopIndexUseVisitor::addComponents(const ComponentVector &Components) {
 void ForLoopIndexUseVisitor::addComponent(const Expr *E) {
   llvm::FoldingSetNodeID ID;
   const Expr *Node = E->IgnoreParenImpCasts();
-  Node->Profile(ID, *Context, true);
+  Node->Profile(ID, *Context, CanonicalizationKind::Structural);
   DependentExprs.emplace_back(Node, ID);
 }
 
diff --git a/clang-tools-extra/clang-tidy/utils/ASTUtils.cpp b/clang-tools-extra/clang-tidy/utils/ASTUtils.cpp
index 4baef7539008a..d01f15512ff6f 100644
--- a/clang-tools-extra/clang-tidy/utils/ASTUtils.cpp
+++ b/clang-tools-extra/clang-tidy/utils/ASTUtils.cpp
@@ -106,8 +106,11 @@ bool areStatementsIdentical(const Stmt *FirstStmt, const Stmt *SecondStmt,
   }
 
   llvm::FoldingSetNodeID DataFirst, DataSecond;
-  FirstStmt->Profile(DataFirst, Context, Canonical);
-  SecondStmt->Profile(DataSecond, Context, Canonical);
+  auto CanonKind =
+      Canonical ? CanonicalizationKindOrNone(CanonicalizationKind::Structural)
+                : std::nullopt;
+  FirstStmt->Profile(DataFirst, Context, CanonKind);
+  SecondStmt->Profile(DataSecond, Context, CanonKind);
   return DataFirst == DataSecond;
 }
 
diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp
index 2ebc9b49cac55..826f92681865f 100644
--- a/clang-tools-extra/clangd/AST.cpp
+++ b/clang-tools-extra/clangd/AST.cpp
@@ -562,8 +562,12 @@ class DeducedTypeVisitor : public RecursiveASTVisitor<DeducedTypeVisitor> {
     //  decltype(I) J = I;
     //  decltype(J) K = J;
     const DecltypeType *DT = dyn_cast<DecltypeType>(TL.getTypePtr());
-    while (DT && !DT->getUnderlyingType().isNull()) {
+    while (DT) {
       DeducedType = DT->getUnderlyingType();
+      if (DeducedType.isNull()) {
+        DeducedType = DT->getUnderlyingExpr()->getType();
+        break;
+      }
       DT = dyn_cast<DecltypeType>(DeducedType.getTypePtr());
     }
     return true;
diff --git a/clang-tools-extra/clangd/InlayHints.cpp b/clang-tools-extra/clangd/InlayHints.cpp
index 951177e3546f2..124ee4da08aa4 100644
--- a/clang-tools-extra/clangd/InlayHints.cpp
+++ b/clang-tools-extra/clangd/InlayHints.cpp
@@ -400,7 +400,7 @@ class InlayHintVisitor : public RecursiveASTVisitor<InlayHintVisitor> {
 
   bool VisitTypeLoc(TypeLoc TL) {
     if (const auto *DT = llvm::dyn_cast<DecltypeType>(TL.getType()))
-      if (QualType UT = DT->getUnderlyingType(); !UT->isDependentType())
+      if (QualType UT = DT->getUnderlyingType(); !UT.isNull())
         addTypeHint(TL.getSourceRange(), UT, ": ");
     return true;
   }
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 3e2d287d1eb1f..d817cc7d6efa7 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -442,6 +442,9 @@ 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)
+- Clang now supports overloading on signatures with differing requirements for
+  instantiation failure, effectively supporting SFINAE on `void_t`. (#GH190388)
+- `decltype(expr)` is not dependent anymore when the value of the expression is dependent. (#GH8740) (#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..86e1b5340f27f 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -256,8 +256,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
     FunctionProtoTypes;
   mutable llvm::ContextualFoldingSet<DependentTypeOfExprType, ASTContext &>
       DependentTypeOfExprTypes;
-  mutable llvm::ContextualFoldingSet<DependentDecltypeType, ASTContext &>
-      DependentDecltypeTypes;
+  mutable llvm::ContextualFoldingSet<DecltypeType, ASTContext &> DecltypeTypes;
 
   mutable llvm::ContextualFoldingSet<PackIndexingType, ASTContext &>
       DependentPackIndexingTypes;
@@ -401,6 +400,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;
@@ -1545,7 +1549,14 @@ class ASTContext : public RefCountedBase<ASTContext> {
                                     QualType NewResultType);
 
   /// Adjust the given function result type.
-  CanQualType getCanonicalFunctionResultType(QualType ResultType) const;
+  QualType getCanonicalFunctionResultType(QualType ResultType,
+                                          CanonicalizationKind Kind,
+                                          bool &AnyNonCanonical) const;
+  CanQualType getCanonicalFunctionResultType(QualType ResultType) const {
+    bool AnyNonCanonical = false;
+    return CanQualType::CreateUnsafe(getCanonicalFunctionResultType(
+        ResultType, CanonicalizationKind::Structural, AnyNonCanonical));
+  }
 
   /// Change the result type of a function type once it is deduced.
   void adjustDeducedFunctionResultType(FunctionDecl *FD, QualType ResultType);
@@ -1975,9 +1986,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 +2005,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,
@@ -2066,7 +2079,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
   QualType getReferenceQualifiedType(const Expr *e) const;
 
   /// C++11 decltype.
-  QualType getDecltypeType(Expr *e, QualType UnderlyingType) const;
+  QualType getDecltypeType(Expr *E, CanonicalizationKindOrNone ExprCanonKind,
+                           QualType UnderlyingType) const;
 
   QualType getPackIndexingType(QualType Pattern, Expr *IndexExpr,
                                bool FullySubstituted = false,
@@ -2954,7 +2968,16 @@ class ASTContext : public RefCountedBase<ASTContext> {
   ///
   /// Qualifiers are stripped off, functions are turned into function
   /// pointers, and arrays decay one level into pointers.
-  CanQualType getCanonicalParamType(QualType T) const;
+  QualType getCanonicalParamType(QualType T, CanonicalizationKind Kind,
+                                 bool &AnyNonCanonical) const;
+  QualType getCanonicalParamType(QualType T, CanonicalizationKind Kind) const {
+    bool AnyNonCanonical = false;
+    return getCanonicalParamType(T, Kind, AnyNonCanonical);
+  }
+  CanQualType getCanonicalParamType(QualType T) const {
+    return CanQualType::CreateUnsafe(
+        getCanonicalParamType(T, CanonicalizationKind::Structural));
+  }
 
   /// Determine whether the given types \p T1 and \p T2 are equivalent.
   static bool hasSameType(QualType T1, QualType T2) {
@@ -2964,8 +2987,27 @@ class ASTContext : public RefCountedBase<ASTContext> {
     return getCanonicalType(T1) == getCanonicalType(T2);
   }
 
+  QualType getCanonicalType(QualType QT, CanonicalizationKind Kind) const;
+  QualType getCanonicalType(QualType QT, CanonicalizationKind Kind,
+                            bool &AnyNonCanonical) const {
+    QualType R = getCanonicalType(QT, Kind);
+    AnyNonCanonical |= !R.isCanonical();
+    return R;
+  }
+
+  bool hasFunctionallyEquivalentType(QualType T1, QualType T2) const {
+    return hasSameType(T1, T2) &&
+           getCanonicalType(T1, CanonicalizationKind::Functional) ==
+               getCanonicalType(T2, CanonicalizationKind::Functional);
+  }
+  bool isFunctionallyCanonicalType(QualType T) const {
+    return getCanonicalType(T, CanonicalizationKind::Functional) == T;
+  }
+
   /// Determine whether the given expressions \p X and \p Y are equivalent.
-  bool hasSameExpr(const Expr *X, const Expr *Y) const;
+  bool hasSameExpr(const Expr *X, const Expr *Y,
+                   CanonicalizationKindOrNone CanonKind =
+                       CanonicalizationKind::Structural) const;
 
   /// Return this type as a completely-unqualified array type,
   /// capturing the qualifiers in \p Quals.
@@ -3050,6 +3092,17 @@ class ASTContext : public RefCountedBase<ASTContext> {
   CallingConv getDefaultCallingConvention(bool IsVariadic,
                                           bool IsCXXMethod) const;
 
+  NestedNameSpecifier
+  getCanonicalNestedNameSpecifier(NestedNameSpecifier NNS,
+                                  CanonicalizationKind CanonKind,
+                                  bool &AnyNonCanonical) const;
+  NestedNameSpecifier
+  getCanonicalNestedNameSpecifier(NestedNameSpecifier NNS,
+                                  CanonicalizationKind CanonKind) const {
+    bool AnyNonCanonical = false;
+    return getCanonicalNestedNameSpecifier(NNS, CanonKind, AnyNonCanonical);
+  }
+
   /// Retrieves the "canonical" template name that refers to a
   /// given template.
   ///
@@ -3068,8 +3121,16 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// template name uses the shortest form of the dependent
   /// nested-name-specifier, which itself contains all canonical
   /// types, values, and templates.
-  TemplateName getCanonicalTemplateName(TemplateName Name,
-                                        bool IgnoreDeduced = false) const;
+  TemplateName getCanonicalTemplateName(TemplateName Name, bool IgnoreDeduced,
+                                        CanonicalizationKind CanonKind,
+                                        bool &AnyNonCanonical) const;
+  TemplateName getCanonicalTemplateName(
+      TemplateName Name, bool IgnoreDeduced = false,
+      CanonicalizationKind CanonKind = CanonicalizationKind::Structural) const {
+    bool AnyNonCanonical = false;
+    return getCanonicalTemplateName(Name, IgnoreDeduced, CanonKind,
+                                    AnyNonCanonical);
+  }
 
   /// Determine whether the given template names refer to the same
   /// template.
@@ -3118,14 +3179,28 @@ 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,
+                                                CanonicalizationKind Kind,
+                                                bool &AnyNonCanonical) const;
+  TemplateArgument getCanonicalTemplateArgument(
+      const TemplateArgument &Arg,
+      CanonicalizationKind Kind = CanonicalizationKind::Structural) const {
+    bool AnyNonCanonical = false;
+    return getCanonicalTemplateArgument(Arg, Kind, AnyNonCanonical);
+  }
 
   /// Canonicalize the given template argument list.
   ///
   /// Returns true if any arguments were non-canonical, false otherwise.
-  bool
-  canonicalizeTemplateArguments(MutableArrayRef<TemplateArgument> Args) const;
+  bool canonicalizeTemplateArguments(MutableArrayRef<TemplateArgument> Args,
+                                     CanonicalizationKind Kind,
+                                     bool &AnyNonCanonical) const;
+  bool canonicalizeTemplateArguments(
+      MutableArrayRef<TemplateArgument> Args,
+      CanonicalizationKind Kind = CanonicalizationKind::Structural) const {
+    bool AnyNonCanonical = false;
+    return canonicalizeTemplateArguments(Args, Kind, AnyNonCanonical);
+  }
 
   /// Canonicalize the given TemplateTemplateParmDecl.
   TemplateTemplateParmDecl *
@@ -3268,6 +3343,8 @@ class ASTContext : public RefCountedBase<ASTContext> {
   // Helper for integer ordering
   unsigned getIntegerRank(const Type *T) const;
 
+  SplitQualType buildFunctionallyEquivalentCanonicalType(const Type *T) const;
+
 public:
   //===--------------------------------------------------------------------===//
   //                    Type Compatibility Predicates
diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h
index a4a1bb9c13c79..dcf6966f4d068 100644
--- a/clang/include/clang/AST/DeclTemplate.h
+++ b/clang/include/clang/AST/DeclTemplate.h
@@ -2132,7 +2132,6 @@ class ClassTemplatePartialSpecializationDecl
       ASTContext &Context, TagKind TK, DeclContext *DC, SourceLocation StartLoc,
       SourceLocation IdLoc, TemplateParameterList *Params,
       ClassTemplateDecl *SpecializedTemplate, ArrayRef<TemplateArgument> Args,
-      CanQualType CanonInjectedTST,
       ClassTemplatePartialSpecializationDecl *PrevDecl);
 
   ClassTemplatePartialSpecializationDecl(ASTContext &C)
@@ -2149,7 +2148,7 @@ class ClassTemplatePartialSpecializationDecl
   Create(ASTContext &Context, TagKind TK, DeclContext *DC,
          SourceLocation StartLoc, SourceLocation IdLoc,
          TemplateParameterList *Params, ClassTemplateDecl *SpecializedTemplate,
-         ArrayRef<TemplateArgument> Args, CanQualType CanonInjectedTST,
+         ArrayRef<TemplateArgument> Args,
          ClassTemplatePartialSpecializationDecl *PrevDecl);
 
   static ClassTemplatePartialSpecializationDecl *
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/Expr.h b/clang/include/clang/AST/Expr.h
index a0ab599fa82d2..d838334f9b003 100644
--- a/clang/include/clang/AST/Expr.h
+++ b/clang/include/clang/AST/Expr.h
@@ -7483,12 +7483,10 @@ class HLSLOutArgExpr : public Expr {
 /// range covered by the expression.
 ///
 /// By default, RecoveryExpr uses dependence-bits to take advantage of existing
-/// machinery to deal with dependent code in C++, e.g. RecoveryExpr is preserved
-/// in `decltype(<broken-expr>)` as part of the `DependentDecltypeType`. In
-/// addition to that, clang does not report most errors on dependent
-/// expressions, so we get rid of bogus errors for free. However, note that
-/// unlike other dependent expressions, RecoveryExpr can be produced in
-/// non-template contexts.
+/// machinery to deal with dependent code in C++. In addition to that, clang
+/// does not report most errors on dependent expressions, so we get rid of bogus
+/// errors for free. However, note that unlike other dependent expressions,
+/// RecoveryExpr can be produced in non-template contexts.
 ///
 /// We will preserve the type in RecoveryExpr when the type is known, e.g.
 /// preserving the return type for a broken non-overloaded function call, a
diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td
index fd3cce10be303..5b8e72739702b 100644
--- a/clang/include/clang/AST/PropertiesBase.td
+++ b/clang/include/clang/AST/PropertiesBase.td
@@ -148,6 +148,7 @@ def TypeOfKind : EnumPropertyType<"TypeOfKind">;
 def UInt32 : CountPropertyType<"uint32_t">;
 def UInt64 : CountPropertyType<"uint64_t">;
 def UnsignedOrNone : PropertyType;
+def CanonicalizationKindOrNone : PropertyType;
 def UnaryTypeTransformKind : EnumPropertyType<"UnaryTransformType::UTTKind">;
 def VectorKind : EnumPropertyType<"VectorKind">;
 def TypeCoupledDeclRefInfo : PropertyType;
@@ -905,14 +906,14 @@ let Class = PropertyTypeCase<TemplateArgument, "Expression"> in {
   def : Property<"expression", ExprRef> {
     let Read = [{ node.getAsExpr() }];
   }
-  def : Property<"IsCanonical", Bool> {
-    let Read = [{ node.isCanonicalExpr() }];
+  def : Property<"CanonKind", CanonicalizationKindOrNone> {
+    let Read = [{ node.getExprCanonKind() }];
   }
   def : Property<"isDefaulted", Bool> {
     let Read = [{ node.getIsDefaulted() }];
   }
   def : Creator<[{
-    return TemplateArgument(expression, IsCanonical, isDefaulted);
+    return TemplateArgument(expression, CanonKind, isDefaulted);
   }]>;
 }
 let Class = PropertyTypeCase<TemplateArgument, "Pack"> in {
diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h
index 1a14dd2c666b5..e328011b80b6b 100644
--- a/clang/include/clang/AST/RecursiveASTVisitor.h
+++ b/clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1456,9 +1456,8 @@ DEF_TRAVERSE_TYPELOC(TypeOfType, {
 })
 
 // FIXME: location of underlying expr
-DEF_TRAVERSE_TYPELOC(DecltypeType, {
-  TRY_TO(TraverseStmt(TL.getTypePtr()->getUnderlyingExpr()));
-})
+DEF_TRAVERSE_TYPELOC(DecltypeType,
+                     { TRY_TO(TraverseStmt(TL.getUnderlyingExpr())); })
 
 DEF_TRAVERSE_TYPELOC(PackIndexingType, {
   TRY_TO(TraverseType(TL.getPattern()));
diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index 1711d05a16d93..3fa1a8573d166 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -1612,8 +1612,14 @@ class alignas(void *) Stmt {
   /// other lambda expressions. When true, the lambda expressions with the same
   /// implementation will be considered to be the same. ProfileLambdaExpr should
   /// only be true when we try to merge two declarations within modules.
-  void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
-               bool Canonical, bool ProfileLambdaExpr = false) const;
+  /// \returns The maximum canonicalization kind this expression would have
+  /// profiled identically as.
+  /// FIXME: This is not supported for CanonKind == nullopt yet, and in that
+  /// case nullopt will always be returned.
+  CanonicalizationKindOrNone Profile(llvm::FoldingSetNodeID &ID,
+                                     const ASTContext &Context,
+                                     CanonicalizationKindOrNone CanonKind,
+                                     bool ProfileLambdaExpr = false) const;
 
   /// Calculate a unique representation for a statement that is
   /// stable across compiler invocations.
diff --git a/clang/include/clang/AST/TemplateBase.h b/clang/include/clang/AST/TemplateBase.h
index c0db55ea92cd5..95e5b9f1e9f8a 100644
--- a/clang/include/clang/AST/TemplateBase.h
+++ b/clang/include/clang/AST/TemplateBase.h
@@ -167,8 +167,7 @@ class TemplateArgument {
     unsigned Kind : 31;
     LLVM_PREFERRED_TYPE(bool)
     unsigned IsDefaulted : 1;
-    LLVM_PREFERRED_TYPE(bool)
-    unsigned IsCanonicalExpr : 1;
+    unsigned ExprCanonKind : 2;
     uintptr_t V;
   };
   union {
@@ -190,7 +189,11 @@ class TemplateArgument {
 public:
   /// Construct an empty, invalid template argument.
   constexpr TemplateArgument()
-      : TypeOrValue{Null, /*IsDefaulted=*/0, /*IsCanonicalExpr=*/0, /*V=*/0} {}
+      : TypeOrValue{
+            Null, /*IsDefaulted=*/0,
+            /*ExprCanonKind=*/
+            CanonicalizationKindOrNone(std::nullopt).toInternalRepresentation(),
+            /*V=*/0} {}
 
   /// Construct a template type argument.
   TemplateArgument(QualType T, bool isNullPtr = false,
@@ -265,10 +268,11 @@ class TemplateArgument {
   /// This form of template argument only occurs in template argument
   /// lists used for dependent types and for expression; it will not
   /// occur in a non-dependent, canonical template argument list.
-  TemplateArgument(Expr *E, bool IsCanonical, bool IsDefaulted = false) {
+  TemplateArgument(Expr *E, CanonicalizationKindOrNone CanonKind,
+                   bool IsDefaulted = false) {
     TypeOrValue.Kind = Expression;
     TypeOrValue.IsDefaulted = IsDefaulted;
-    TypeOrValue.IsCanonicalExpr = IsCanonical;
+    TypeOrValue.ExprCanonKind = CanonKind.toInternalRepresentation();
     TypeOrValue.V = reinterpret_cast<uintptr_t>(E);
   }
 
@@ -413,9 +417,10 @@ class TemplateArgument {
     return reinterpret_cast<Expr *>(TypeOrValue.V);
   }
 
-  bool isCanonicalExpr() const {
+  CanonicalizationKindOrNone getExprCanonKind() const {
     assert(getKind() == Expression && "Unexpected kind");
-    return TypeOrValue.IsCanonicalExpr;
+    return CanonicalizationKindOrNone::fromInternalRepresentation(
+        TypeOrValue.ExprCanonKind);
   }
 
   /// Iterator that traverses the elements of a template argument pack.
diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h
index 32e83ebb5c8eb..8cf6b9d8fe251 100644
--- a/clang/include/clang/AST/TextNodeDumper.h
+++ b/clang/include/clang/AST/TextNodeDumper.h
@@ -334,6 +334,7 @@ class TextNodeDumper
   void VisitVectorType(const VectorType *T);
   void VisitFunctionType(const FunctionType *T);
   void VisitFunctionProtoType(const FunctionProtoType *T);
+  void VisitDecltypeType(const DecltypeType *T);
   void VisitUnresolvedUsingType(const UnresolvedUsingType *T);
   void VisitUsingType(const UsingType *T);
   void VisitTypedefType(const TypedefType *T);
diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h
index a64bbb99b13d2..ba20b2504b765 100644
--- a/clang/include/clang/AST/TypeBase.h
+++ b/clang/include/clang/AST/TypeBase.h
@@ -27,6 +27,7 @@
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/LangOptions.h"
 #include "clang/Basic/Linkage.h"
+#include "clang/Basic/OptionalUnsigned.h"
 #include "clang/Basic/PartialDiagnostic.h"
 #include "clang/Basic/PointerAuthOptions.h"
 #include "clang/Basic/SourceLocation.h"
@@ -2135,6 +2136,15 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
     unsigned AttrKind : 32 - NumTypeBits;
   };
 
+  class DecltypeTypeBitfields {
+    friend class DecltypeType;
+
+    LLVM_PREFERRED_TYPE(TypeBitfields)
+    unsigned : NumTypeBits;
+
+    unsigned ExprCanonKind : 2;
+  };
+
   class DeducedTypeBitfields {
     friend class DeducedType;
 
@@ -2361,6 +2371,7 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase {
     ArrayTypeBitfields ArrayTypeBits;
     ConstantArrayTypeBitfields ConstantArrayTypeBits;
     AttributedTypeBitfields AttributedTypeBits;
+    DecltypeTypeBitfields DecltypeTypeBits;
     DeducedTypeBitfields DeducedTypeBits;
     AutoTypeBitfields AutoTypeBits;
     TypeOfBitfields TypeOfBits;
@@ -6342,44 +6353,42 @@ class TypeOfType : public Type {
 };
 
 /// Represents the type `decltype(expr)` (C++11).
-class DecltypeType : public Type {
+class DecltypeType : public Type, public llvm::FoldingSetNode {
   Expr *E;
   QualType UnderlyingType;
 
 protected:
   friend class ASTContext; // ASTContext creates these.
 
-  DecltypeType(Expr *E, QualType underlyingType, QualType can = QualType());
+  DecltypeType(Expr *E, CanonicalizationKindOrNone ExprCanonKind,
+               QualType UnderlyingType, QualType CanonType = QualType());
 
 public:
   Expr *getUnderlyingExpr() const { return E; }
+
   QualType getUnderlyingType() const { return UnderlyingType; }
 
+  CanonicalizationKindOrNone getExprCanonicalizationKind() const {
+    return CanonicalizationKindOrNone::fromInternalRepresentation(
+        DecltypeTypeBits.ExprCanonKind);
+  }
+
   /// Remove a single level of sugar.
   QualType desugar() const;
 
   /// Returns whether this type directly provides sugar.
   bool isSugared() const;
 
-  static bool classof(const Type *T) { return T->getTypeClass() == Decltype; }
-};
-
-/// Internal representation of canonical, dependent
-/// decltype(expr) types.
-///
-/// This class is used internally by the ASTContext to manage
-/// canonical, dependent types, only. Clients will only see instances
-/// of this class via DecltypeType nodes.
-class DependentDecltypeType : public DecltypeType, public llvm::FoldingSetNode {
-public:
-  DependentDecltypeType(Expr *E);
-
   void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context) {
-    Profile(ID, Context, getUnderlyingExpr());
+    Profile(ID, Context, getUnderlyingExpr(), getExprCanonicalizationKind(),
+            getUnderlyingType());
   }
 
   static void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
-                      Expr *E);
+                      Expr *E, CanonicalizationKindOrNone ExprCanonKind,
+                      QualType UnderlyingType);
+
+  static bool classof(const Type *T) { return T->getTypeClass() == Decltype; }
 };
 
 class PackIndexingType final
@@ -7496,7 +7505,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/include/clang/AST/TypeLoc.h b/clang/include/clang/AST/TypeLoc.h
index 24df18dbaace4..92c099b474956 100644
--- a/clang/include/clang/AST/TypeLoc.h
+++ b/clang/include/clang/AST/TypeLoc.h
@@ -2275,6 +2275,7 @@ class TypeOfTypeLoc
 // FIXME: add LParenLoc, it is tricky to support due to the limitation of
 // annotated-decltype token.
 struct DecltypeTypeLocInfo {
+  Expr *UnderlyingExpr;
   SourceLocation DecltypeLoc;
   SourceLocation RParenLoc;
 };
@@ -2282,7 +2283,8 @@ class DecltypeTypeLoc
     : public ConcreteTypeLoc<UnqualTypeLoc, DecltypeTypeLoc, DecltypeType,
                              DecltypeTypeLocInfo> {
 public:
-  Expr *getUnderlyingExpr() const { return getTypePtr()->getUnderlyingExpr(); }
+  Expr *getUnderlyingExpr() const { return getLocalData()->UnderlyingExpr; }
+  void setUnderlyingExpr(Expr *E) { getLocalData()->UnderlyingExpr = E; }
 
   SourceLocation getDecltypeLoc() const { return getLocalData()->DecltypeLoc; }
   void setDecltypeLoc(SourceLocation Loc) { getLocalData()->DecltypeLoc = Loc; }
@@ -2295,6 +2297,7 @@ class DecltypeTypeLoc
   }
 
   void initializeLocal(ASTContext &Context, SourceLocation Loc) {
+    setUnderlyingExpr(getTypePtr()->getUnderlyingExpr());
     setDecltypeLoc(Loc);
     setRParenLoc(Loc);
   }
diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td
index 0f3722b36774a..7425978562491 100644
--- a/clang/include/clang/AST/TypeProperties.td
+++ b/clang/include/clang/AST/TypeProperties.td
@@ -470,9 +470,12 @@ let Class = DecltypeType in {
   def : Property<"expression", ExprRef> {
     let Read = [{ node->getUnderlyingExpr() }];
   }
+  def : Property<"ExprCanonKind", CanonicalizationKindOrNone> {
+    let Read = [{ node->getExprCanonicalizationKind() }];
+  }
 
   def : Creator<[{
-    return ctx.getDecltypeType(expression, underlyingType);
+    return ctx.getDecltypeType(expression, ExprCanonKind, underlyingType);
   }]>;
 }
 
diff --git a/clang/include/clang/Basic/Specifiers.h b/clang/include/clang/Basic/Specifiers.h
index 8da6fd4cf454a..7523e452ca474 100644
--- a/clang/include/clang/Basic/Specifiers.h
+++ b/clang/include/clang/Basic/Specifiers.h
@@ -345,6 +345,80 @@ namespace clang {
     SD_Dynamic         ///< Dynamic storage duration.
   };
 
+  /// Kinds of canonicalization.
+  ///
+  /// Canonicalization is a process for transforming (or viewing) a type or
+  /// expression in a way that ignores certain differences that don't affect
+  /// certain aspects of the semantics of the program.
+  ///
+  /// These kinds of canonicalization are used to represent certain equivalence
+  /// classes, where the classes are themselves ordered, one stronger or a
+  /// subset of the other.
+  enum class CanonicalizationKind : uint8_t {
+    /// This kind of canonicalization ignores any differences that don't affect
+    /// the semantics of the program.
+    /// This never ignores references to template parameters, but is otherwise
+    /// the same as the 'Structural' kind when these references don't occur.
+    /// This preserves enough structure to guarantee that any two types in this
+    /// equivalence class have identical results under the same template
+    /// parameter substitution. Substitution will either succeed for both and
+    /// produce 'Structural'-equivalent types, or it will fail for both, for the
+    /// same set of potential substitutions.
+    ///
+    /// This guarantees that these types will exhibit identical SFINAE behavior.
+    /// But this is a weaker guarantee than the functional equivalence proposed
+    /// in the C++ standard, which would require that types which are not
+    /// functionally-equivalent SFINAE differently in at least one way, which
+    /// is not really feasible to implement.
+    ///
+    /// This relationship follows syntactically from type to type.
+    /// For example, the underlying type of a type alias can contain
+    /// instantiation-dependent-only types which may fail substitution later.
+    /// But this underlying type exists in a separate context, and this failure
+    /// can still happen in that context when the type alias itself is
+    /// instantiated.
+    /// So functional canonicalization can ignore these
+    /// instantiation-dependent-only types, because ignoring them won't change
+    /// the behaviour of the program (it will still be invalid).
+    /// But a typedef can't be ignored completely: It can be referred to using a
+    /// qualified name, and this name qualification can contain
+    /// instantiation-dependent-only types which may fail substitution later.
+    /// This substitution failure can appear only in this spelling of the type,
+    /// so it can't be ignored under functional canonicalization.
+    ///
+    /// This is the kind of canonicalization used to determine whether two
+    /// declarations refer to the same entity, and for mangling.
+    Functional,
+    /// Like 'Functional' canonicalization, but ignores any differences that
+    /// could arise from substitution failures.
+    ///
+    /// By using this kind of canonicalization, it is implicitly assumed that
+    /// substitution for any referred template parameters will be valid.
+    ///
+    /// For a template type alias where some template parameters are unused,
+    /// any template arguments to these parameters will be ignored under this
+    /// canonicalization, unlike 'Functional' canonicalization where they would
+    /// be considered part of the type.
+    ///
+    /// This is a superset of 'Functional' canonicalization: all elements which
+    /// are equivalent under 'Functional' canonicalization are also equivalent
+    /// under 'Structural' canonicalization, but not vice versa.
+    ///
+    /// This is the kind of canonicalization used to determine whether two
+    /// template specializations refer to the same entity.
+    Structural,
+  };
+
+  /// This compares CanonicalizationKinds such that 'Structural' is greater than
+  /// 'Functional'.
+  constexpr bool operator<(CanonicalizationKind LHS, CanonicalizationKind RHS) {
+    return llvm::to_underlying(LHS) < llvm::to_underlying(RHS);
+  }
+
+  /// An optional CanonicalizationKind. Following standard convention, a nullopt
+  /// is considered lesser than any value.
+  using CanonicalizationKindOrNone = OptionalUnsigned<CanonicalizationKind>;
+
   /// Describes the nullability of a particular type.
   enum class NullabilityKind : uint8_t {
     /// Values of this type can never be null.
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 760555d9c8b9b..cd34b6276f935 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10268,6 +10268,7 @@ class Sema final : public SemaBase {
   /// If `Reversed` is true, the parameters of `NewType` will be compared in
   /// reverse order. That's useful if one of the functions is being used as a
   /// C++20 synthesized operator overload with a reversed parameter order.
+  /// The parameter types are assumed to be functionally-canonicalized.
   bool FunctionParamTypesAreEqual(ArrayRef<QualType> Old,
                                   ArrayRef<QualType> New,
                                   unsigned *ArgPos = nullptr,
diff --git a/clang/include/clang/Serialization/ASTRecordReader.h b/clang/include/clang/Serialization/ASTRecordReader.h
index aed1b7d309001..caba5917721cb 100644
--- a/clang/include/clang/Serialization/ASTRecordReader.h
+++ b/clang/include/clang/Serialization/ASTRecordReader.h
@@ -233,11 +233,10 @@ class ASTRecordReader
   /// Read a template argument, advancing Idx. (inherited)
   // TemplateArgument readTemplateArgument();
   using DataStreamBasicReader::readTemplateArgument;
-  TemplateArgument readTemplateArgument(bool Canonicalize) {
+  TemplateArgument readTemplateArgument(CanonicalizationKindOrNone CanonKind) {
     TemplateArgument Arg = readTemplateArgument();
-    if (Canonicalize) {
-      Arg = getContext().getCanonicalTemplateArgument(Arg);
-    }
+    if (CanonKind)
+      Arg = getContext().getCanonicalTemplateArgument(Arg, *CanonKind);
     return Arg;
   }
 
@@ -245,8 +244,9 @@ class ASTRecordReader
   TemplateParameterList *readTemplateParameterList();
 
   /// Read a template argument array, advancing Idx.
-  void readTemplateArgumentList(SmallVectorImpl<TemplateArgument> &TemplArgs,
-                                bool Canonicalize = false);
+  void
+  readTemplateArgumentList(SmallVectorImpl<TemplateArgument> &TemplArgs,
+                           CanonicalizationKindOrNone CanonKind = std::nullopt);
 
   /// Read a UnresolvedSet structure, advancing Idx.
   void readUnresolvedSet(LazyASTUnresolvedSet &Set);
@@ -324,6 +324,11 @@ class ASTRecordReader
     return UnsignedOrNone::fromInternalRepresentation(unsigned(readInt()));
   }
 
+  CanonicalizationKindOrNone readCanonicalizationKindOrNone() {
+    return CanonicalizationKindOrNone::fromInternalRepresentation(
+        unsigned(readInt()));
+  }
+
   /// Read a string, advancing Idx.
   std::string readString() {
     return Reader->ReadString(Record, Idx);
diff --git a/clang/include/clang/Serialization/ASTRecordWriter.h b/clang/include/clang/Serialization/ASTRecordWriter.h
index 9849ea6b395ab..43e09d1682622 100644
--- a/clang/include/clang/Serialization/ASTRecordWriter.h
+++ b/clang/include/clang/Serialization/ASTRecordWriter.h
@@ -193,6 +193,10 @@ class ASTRecordWriter
     Record->push_back(Value.toInternalRepresentation());
   }
 
+  void writeCanonicalizationKindOrNone(CanonicalizationKindOrNone Value) {
+    Record->push_back(Value.toInternalRepresentation());
+  }
+
   /// Emit an integral value.
   void AddAPInt(const llvm::APInt &Value) {
     writeAPInt(Value);
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index ee7f823b014b2..4eff6ad64c87a 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -886,7 +886,7 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM,
       DependentAddressSpaceTypes(this_()), DependentVectorTypes(this_()),
       DependentSizedMatrixTypes(this_()),
       FunctionProtoTypes(this_(), FunctionProtoTypesLog2InitSize),
-      DependentTypeOfExprTypes(this_()), DependentDecltypeTypes(this_()),
+      DependentTypeOfExprTypes(this_()), DecltypeTypes(this_()),
       DependentPackIndexingTypes(this_()), TemplateSpecializationTypes(this_()),
       DependentBitIntTypes(this_()), SubstTemplateTemplateParmPacks(this_()),
       DeducedTemplates(this_()), ArrayParameterTypes(this_()),
@@ -3172,21 +3172,33 @@ ASTContext::getASTObjCInterfaceLayout(const ObjCInterfaceDecl *D) const {
 
 static auto getCanonicalTemplateArguments(const ASTContext &C,
                                           ArrayRef<TemplateArgument> Args,
-                                          bool &AnyNonCanonArgs) {
+                                          bool &AnyChanged,
+                                          CanonicalizationKind Kind,
+                                          bool &AnyNonCanonical) {
   SmallVector<TemplateArgument, 16> CanonArgs(Args);
-  AnyNonCanonArgs |= C.canonicalizeTemplateArguments(CanonArgs);
+  AnyChanged |=
+      C.canonicalizeTemplateArguments(CanonArgs, Kind, AnyNonCanonical);
   return CanonArgs;
 }
 
+static auto getCanonicalTemplateArguments(const ASTContext &C,
+                                          ArrayRef<TemplateArgument> Args,
+                                          bool &AnyChanged) {
+  bool AnyNonCanonical = false;
+  return getCanonicalTemplateArguments(
+      C, Args, AnyChanged, CanonicalizationKind::Structural, AnyNonCanonical);
+}
+
 bool ASTContext::canonicalizeTemplateArguments(
-    MutableArrayRef<TemplateArgument> Args) const {
-  bool AnyNonCanonArgs = false;
+    MutableArrayRef<TemplateArgument> Args, CanonicalizationKind Kind,
+    bool &AnyNonCanonical) const {
+  bool AnyChanged = false;
   for (auto &Arg : Args) {
     TemplateArgument OrigArg = Arg;
-    Arg = getCanonicalTemplateArgument(Arg);
-    AnyNonCanonArgs |= !Arg.structurallyEquals(OrigArg);
+    Arg = getCanonicalTemplateArgument(Arg, Kind, AnyNonCanonical);
+    AnyChanged |= !Arg.structurallyEquals(OrigArg);
   }
-  return AnyNonCanonArgs;
+  return AnyChanged;
 }
 
 //===----------------------------------------------------------------------===//
@@ -4921,16 +4933,17 @@ ASTContext::getFunctionNoProtoType(QualType ResultTy,
   return QualType(New, 0);
 }
 
-CanQualType
-ASTContext::getCanonicalFunctionResultType(QualType ResultType) const {
-  CanQualType CanResultType = getCanonicalType(ResultType);
+QualType
+ASTContext::getCanonicalFunctionResultType(QualType ResultType,
+                                           CanonicalizationKind Kind,
+                                           bool &AnyNonCanonical) const {
+  QualType CanResultType = getCanonicalType(ResultType, Kind, AnyNonCanonical);
+  Qualifiers Qs = CanResultType.getQualifiers();
 
   // Canonical result types do not have ARC lifetime qualifiers.
-  if (CanResultType.getQualifiers().hasObjCLifetime()) {
-    Qualifiers Qs = CanResultType.getQualifiers();
+  if (Qs.hasObjCLifetime()) {
     Qs.removeObjCLifetime();
-    return CanQualType::CreateUnsafe(
-             getQualifiedType(CanResultType.getUnqualifiedType(), Qs));
+    CanResultType = getQualifiedType(CanResultType.getUnqualifiedType(), Qs);
   }
 
   return CanResultType;
@@ -4970,6 +4983,67 @@ static bool isCanonicalExceptionSpecification(
   return false;
 }
 
+static FunctionProtoType::ExceptionSpecInfo
+getCanonicalExceptionSpec(const ASTContext &Ctx,
+                          FunctionProtoType::ExceptionSpecInfo ESI,
+                          SmallVectorImpl<QualType> &ExceptionTypeStorage,
+                          CanonicalizationKind Kind, bool &AnyNonCanonical) {
+  bool NoexceptInType = Ctx.getLangOpts().CPlusPlus17;
+  if (Kind == CanonicalizationKind::Structural && !NoexceptInType)
+    return {};
+  switch (ESI.Type) {
+  case EST_Unparsed:
+  case EST_Unevaluated:
+  case EST_Uninstantiated:
+    // We don't know yet. It shouldn't matter what we pick here; no-one
+    // should ever look at this.
+    [[fallthrough]];
+  case EST_None:
+  case EST_MSAny:
+  case EST_NoexceptFalse:
+    ESI.Type = EST_None;
+    break;
+
+    // A dynamic exception specification is almost always "not noexcept",
+    // with the exception that a pack expansion might expand to no types.
+  case EST_Dynamic: {
+    bool AnyPacks = false, AnyInstantiationDependent = false;
+    for (QualType ET : ESI.Exceptions) {
+      AnyPacks |= ET->getAsCanonical<PackExpansionType>() != nullptr;
+      AnyInstantiationDependent |= ET->isInstantiationDependentType();
+      ExceptionTypeStorage.push_back(
+          Ctx.getCanonicalType(ET, Kind, AnyNonCanonical));
+    }
+    assert((!AnyPacks || AnyInstantiationDependent) &&
+           "pack expansion should be instantiation-dependent");
+    bool DynamicExceptionIsCanonical = NoexceptInType && AnyPacks;
+    if (DynamicExceptionIsCanonical ||
+        (Kind == CanonicalizationKind::Functional &&
+         AnyInstantiationDependent)) {
+      ESI.Type = EST_Dynamic;
+      ESI.Exceptions = ExceptionTypeStorage;
+      AnyNonCanonical |= !DynamicExceptionIsCanonical;
+    } else {
+      ESI.Type = EST_None;
+    }
+    break;
+  }
+
+  case EST_DynamicNone:
+  case EST_BasicNoexcept:
+  case EST_NoexceptTrue:
+  case EST_NoThrow:
+    ESI.Type = NoexceptInType ? EST_BasicNoexcept : EST_None;
+    break;
+
+  case EST_DependentNoexcept:
+    AnyNonCanonical |= !NoexceptInType;
+    // dependent noexcept is already canonical
+    break;
+  }
+  return ESI;
+}
+
 QualType ASTContext::getFunctionTypeInternal(
     QualType ResultTy, ArrayRef<QualType> ArgArray,
     const FunctionProtoType::ExtProtoInfo &EPI, bool OnlyWantCanonical) const {
@@ -5033,48 +5107,11 @@ QualType ASTContext::getFunctionTypeInternal(
     FunctionProtoType::ExtProtoInfo CanonicalEPI = EPI;
     CanonicalEPI.HasTrailingReturn = false;
 
-    if (IsCanonicalExceptionSpec) {
-      // Exception spec is already OK.
-    } else if (NoexceptInType) {
-      switch (EPI.ExceptionSpec.Type) {
-      case EST_Unparsed: case EST_Unevaluated: case EST_Uninstantiated:
-        // We don't know yet. It shouldn't matter what we pick here; no-one
-        // should ever look at this.
-        [[fallthrough]];
-      case EST_None: case EST_MSAny: case EST_NoexceptFalse:
-        CanonicalEPI.ExceptionSpec.Type = EST_None;
-        break;
-
-        // A dynamic exception specification is almost always "not noexcept",
-        // with the exception that a pack expansion might expand to no types.
-      case EST_Dynamic: {
-        bool AnyPacks = false;
-        for (QualType ET : EPI.ExceptionSpec.Exceptions) {
-          if (ET->getAs<PackExpansionType>())
-            AnyPacks = true;
-          ExceptionTypeStorage.push_back(getCanonicalType(ET));
-        }
-        if (!AnyPacks)
-          CanonicalEPI.ExceptionSpec.Type = EST_None;
-        else {
-          CanonicalEPI.ExceptionSpec.Type = EST_Dynamic;
-          CanonicalEPI.ExceptionSpec.Exceptions = ExceptionTypeStorage;
-        }
-        break;
-      }
-
-      case EST_DynamicNone:
-      case EST_BasicNoexcept:
-      case EST_NoexceptTrue:
-      case EST_NoThrow:
-        CanonicalEPI.ExceptionSpec.Type = EST_BasicNoexcept;
-        break;
-
-      case EST_DependentNoexcept:
-        llvm_unreachable("dependent noexcept is already canonical");
-      }
-    } else {
-      CanonicalEPI.ExceptionSpec = FunctionProtoType::ExceptionSpecInfo();
+    if (!IsCanonicalExceptionSpec) {
+      bool AnyNonCanonical = false;
+      CanonicalEPI.ExceptionSpec = getCanonicalExceptionSpec(
+          *this, EPI.ExceptionSpec, ExceptionTypeStorage,
+          CanonicalizationKind::Structural, AnyNonCanonical);
     }
 
     // Adjust the canonical function result type.
@@ -5861,10 +5898,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 +6077,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 +6099,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 +6141,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 +6159,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);
 }
 
@@ -6209,7 +6263,7 @@ TemplateArgument ASTContext::getInjectedTemplateArg(NamedDecl *Param) const {
 
     if (NTTP->isParameterPack())
       E = new (*this) PackExpansionExpr(E, NTTP->getLocation(), std::nullopt);
-    Arg = TemplateArgument(E, /*IsCanonical=*/false);
+    Arg = TemplateArgument(E, /*CanonKind=*/std::nullopt);
   } else {
     auto *TTP = cast<TemplateTemplateParmDecl>(Param);
     TemplateName Name = getQualifiedTemplateName(
@@ -6690,34 +6744,35 @@ QualType ASTContext::getReferenceQualifiedType(const Expr *E) const {
 /// nodes. This would never be helpful, since each such type has its own
 /// 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).
-  QualType CanonType;
-  if (!E->isInstantiationDependent()) {
-    CanonType = getCanonicalType(UnderlyingType);
-  } else if (!UnderlyingType.isNull()) {
-    CanonType = getDecltypeType(E, QualType());
-  } else {
-    llvm::FoldingSetNodeID ID;
-    DependentDecltypeType::Profile(ID, *this, E);
+QualType ASTContext::getDecltypeType(Expr *E,
+                                     CanonicalizationKindOrNone ExprCanonKind,
+                                     QualType UnderlyingType) const {
+  llvm::FoldingSetNodeID ID;
+  DecltypeType::Profile(ID, *this, E, ExprCanonKind, UnderlyingType);
 
-    void *InsertPos = nullptr;
-    if (DependentDecltypeType *Canon =
-            DependentDecltypeTypes.FindNodeOrInsertPos(ID, InsertPos))
-      return QualType(Canon, 0);
+  void *InsertPos = nullptr;
+  if (auto *T = DecltypeTypes.FindNodeOrInsertPos(ID, InsertPos))
+    return QualType(T, 0);
 
-    // Build a new, canonical decltype(expr) type.
-    auto *DT =
-        new (*this, alignof(DependentDecltypeType)) DependentDecltypeType(E);
-    DependentDecltypeTypes.InsertNode(DT, InsertPos);
-    Types.push_back(DT);
-    return QualType(DT, 0);
+  // 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->isTypeDependent()) {
+    CanonType = UnderlyingType.getCanonicalType();
+  } else if (ExprCanonKind != CanonicalizationKind::Structural) {
+    UnderlyingType = QualType();
+    CanonType =
+        getDecltypeType(E, CanonicalizationKind::Structural, QualType());
+    // Find the insertion position again.
+    [[maybe_unused]] auto *DT =
+        DecltypeTypes.FindNodeOrInsertPos(ID, InsertPos);
+    assert(!DT && "broken canonicalization");
   }
+
   auto *DT = new (*this, alignof(DecltypeType))
-      DecltypeType(E, UnderlyingType, CanonType);
+      DecltypeType(E, ExprCanonKind, UnderlyingType, CanonType);
+  DecltypeTypes.InsertNode(DT, InsertPos);
   Types.push_back(DT);
   return QualType(DT, 0);
 }
@@ -7040,24 +7095,49 @@ QualType ASTContext::getProcessIDType() const {
 //                              Type Operators
 //===----------------------------------------------------------------------===//
 
-CanQualType ASTContext::getCanonicalParamType(QualType T) const {
+QualType ASTContext::getCanonicalParamType(QualType T,
+                                           CanonicalizationKind Kind,
+                                           bool &AnyNonCanonical) const {
   // Push qualifiers into arrays, and then discard any remaining
   // qualifiers.
-  T = getCanonicalType(T);
+  if (Kind == CanonicalizationKind::Functional)
+    if (const auto *Ty = dyn_cast<DecayedType>(T))
+      T = Ty->getOriginalType();
+  T = getCanonicalType(T, Kind, AnyNonCanonical);
   T = getVariableArrayDecayedType(T);
-  const Type *Ty = T.getTypePtr();
-  QualType Result;
-  if (getLangOpts().HLSL && isa<ConstantArrayType>(Ty)) {
-    Result = getArrayParameterType(QualType(Ty, 0));
-  } else if (isa<ArrayType>(Ty)) {
-    Result = getArrayDecayedType(QualType(Ty,0));
-  } else if (isa<FunctionType>(Ty)) {
-    Result = getPointerType(QualType(Ty, 0));
-  } else {
-    Result = QualType(Ty, 0);
+  if (getLangOpts().HLSL && isa<ConstantArrayType>(T))
+    return getArrayParameterType(T);
+  if (isa<ArrayType>(T)) {
+    if (Kind == CanonicalizationKind::Functional) {
+      switch (T->getTypeClass()) {
+      case Type::ConstantArray:
+        if (const Expr *E = cast<ConstantArrayType>(T)->getSizeExpr();
+            E && E->isInstantiationDependent()) {
+          AnyNonCanonical = true;
+          return T.getUnqualifiedType();
+        }
+        break;
+      case Type::VariableArray:
+        if (const Expr *E = cast<VariableArrayType>(T)->getSizeExpr();
+            E && E->isInstantiationDependent()) {
+          AnyNonCanonical = true;
+          return T.getUnqualifiedType();
+        }
+        break;
+      case Type::DependentSizedArray: {
+        AnyNonCanonical = true;
+        return T.getUnqualifiedType();
+      }
+      default:
+        break;
+      }
+    }
+    return getArrayDecayedType(T);
   }
-
-  return CanQualType::CreateUnsafe(Result);
+  T = T.getUnqualifiedType();
+  if (isa<FunctionType>(T))
+    return getPointerType(T);
+  return T;
 }
 
 QualType ASTContext::getUnqualifiedArrayType(QualType type,
@@ -7324,12 +7404,10 @@ getDefaultTemplateArgumentOrNone(const NamedDecl *P) {
   }
 }
 
-TemplateName ASTContext::getCanonicalTemplateName(TemplateName Name,
-                                                  bool IgnoreDeduced) const {
-  while (std::optional<TemplateName> UnderlyingOrNone =
-             Name.desugar(IgnoreDeduced))
-    Name = *UnderlyingOrNone;
-
+TemplateName
+ASTContext::getCanonicalTemplateName(TemplateName Name, bool IgnoreDeduced,
+                                     CanonicalizationKind CanonKind,
+                                     bool &AnyNonCanonical) const {
   switch (Name.getKind()) {
   case TemplateName::Template: {
     TemplateDecl *Template = Name.getAsTemplateDecl();
@@ -7351,7 +7429,8 @@ TemplateName ASTContext::getCanonicalTemplateName(TemplateName Name,
     DependentTemplateName *DTN = Name.getAsDependentTemplateName();
     assert(DTN && "Non-dependent template names must refer to template decls.");
     NestedNameSpecifier Qualifier = DTN->getQualifier();
-    NestedNameSpecifier CanonQualifier = Qualifier.getCanonical();
+    NestedNameSpecifier CanonQualifier =
+        getCanonicalNestedNameSpecifier(Qualifier, CanonKind, AnyNonCanonical);
     if (Qualifier != CanonQualifier || !DTN->hasTemplateKeyword())
       return getDependentTemplateName({CanonQualifier, DTN->getName(),
                                        /*HasTemplateKeyword=*/true});
@@ -7361,23 +7440,25 @@ TemplateName ASTContext::getCanonicalTemplateName(TemplateName Name,
   case TemplateName::SubstTemplateTemplateParmPack: {
     SubstTemplateTemplateParmPackStorage *subst =
         Name.getAsSubstTemplateTemplateParmPack();
-    TemplateArgument canonArgPack =
-        getCanonicalTemplateArgument(subst->getArgumentPack());
+    TemplateArgument canonArgPack = getCanonicalTemplateArgument(
+        subst->getArgumentPack(), CanonKind, AnyNonCanonical);
     return getSubstTemplateTemplateParmPack(
         canonArgPack, subst->getAssociatedDecl()->getCanonicalDecl(),
         subst->getIndex(), subst->getFinal());
   }
   case TemplateName::DeducedTemplate: {
-    assert(IgnoreDeduced == false);
     DeducedTemplateStorage *DTS = Name.getAsDeducedTemplateName();
-    DefaultArguments DefArgs = DTS->getDefaultArguments();
     TemplateName Underlying = DTS->getUnderlying();
+    if (IgnoreDeduced)
+      return getCanonicalTemplateName(Underlying, IgnoreDeduced, CanonKind,
+                                      AnyNonCanonical);
+    DefaultArguments DefArgs = DTS->getDefaultArguments();
 
-    TemplateName CanonUnderlying =
-        getCanonicalTemplateName(Underlying, /*IgnoreDeduced=*/true);
-    bool NonCanonical = CanonUnderlying != Underlying;
+    TemplateName CanonUnderlying = getCanonicalTemplateName(
+        Underlying, /*IgnoreDeduced=*/true, CanonKind, AnyNonCanonical);
+    bool AnyChanged = CanonUnderlying != Underlying;
     auto CanonArgs =
-        getCanonicalTemplateArguments(*this, DefArgs.Args, NonCanonical);
+        getCanonicalTemplateArguments(*this, DefArgs.Args, AnyChanged);
 
     ArrayRef<NamedDecl *> Params =
         CanonUnderlying.getAsTemplateDecl()->getTemplateParameters()->asArray();
@@ -7398,17 +7479,50 @@ TemplateName ASTContext::getCanonicalTemplateName(TemplateName Name,
       // Keep popping from the back any deault arguments which are the same.
       if (I == int(CanonArgs.size() - 1))
         CanonArgs.pop_back();
-      NonCanonical = true;
+      AnyChanged = true;
+    }
+    return AnyChanged ? getDeducedTemplateName(
+                            CanonUnderlying,
+                            /*DefaultArgs=*/{DefArgs.StartPos, CanonArgs})
+                      : Name;
+  }
+  case TemplateName::QualifiedTemplate: {
+    auto *S = Name.getAsQualifiedTemplateName();
+    TemplateName Underlying = S->getUnderlyingTemplate();
+    if (CanonKind == CanonicalizationKind::Structural) {
+      return getCanonicalTemplateName(S->getUnderlyingTemplate(), IgnoreDeduced,
+                                      CanonKind, AnyNonCanonical);
+    }
+    // The qualifier needs to be attached to whatever entity it belongs to.
+    // Don't simply canonicalize the Underlying template, as that can turn a
+    // UsingShadowDecl into it's target, forming a nonsensical qualified name.
+    switch (Underlying.getKind()) {
+    case TemplateName::Template:
+      Underlying = TemplateName(cast<TemplateDecl>(
+          Underlying.getAsTemplateDecl()->getCanonicalDecl()));
+      break;
+    case TemplateName::UsingTemplate:
+      Underlying = TemplateName(cast<UsingShadowDecl>(
+          Underlying.getAsUsingShadowDecl()->getCanonicalDecl()));
+      break;
+    default:
+      llvm_unreachable(
+          "unexpected underlying template name kind for qualified template");
     }
-    return NonCanonical ? getDeducedTemplateName(
-                              CanonUnderlying,
-                              /*DefaultArgs=*/{DefArgs.StartPos, CanonArgs})
-                        : Name;
+    NestedNameSpecifier Qualifier = getCanonicalNestedNameSpecifier(
+        S->getQualifier(), CanonKind, AnyNonCanonical);
+    if (!AnyNonCanonical)
+      return Underlying;
+    return getQualifiedTemplateName(Qualifier, /*TemplateKeyword=*/false,
+                                    Underlying);
   }
   case TemplateName::UsingTemplate:
-  case TemplateName::QualifiedTemplate:
+    return TemplateName(cast<TemplateDecl>(
+        Name.getAsUsingShadowDecl()->getTargetDecl()->getCanonicalDecl()));
   case TemplateName::SubstTemplateTemplateParm:
-    llvm_unreachable("always sugar node");
+    return getCanonicalTemplateName(
+        Name.getAsSubstTemplateTemplateParm()->getReplacement(), IgnoreDeduced,
+        CanonKind, AnyNonCanonical);
   }
 
   llvm_unreachable("bad template name!");
@@ -7437,9 +7551,12 @@ bool ASTContext::isSameConstraintExpr(const Expr *XCE, const Expr *YCE) const {
   if (!XCE)
     return true;
 
+  // FIXME: This probably should use functional equivalence instead.
   llvm::FoldingSetNodeID XCEID, YCEID;
-  XCE->Profile(XCEID, *this, /*Canonical=*/true, /*ProfileLambdaExpr=*/true);
-  YCE->Profile(YCEID, *this, /*Canonical=*/true, /*ProfileLambdaExpr=*/true);
+  XCE->Profile(XCEID, *this, CanonicalizationKind::Structural,
+               /*ProfileLambdaExpr=*/true);
+  YCE->Profile(YCEID, *this, CanonicalizationKind::Structural,
+               /*ProfileLambdaExpr=*/true);
   return XCEID == YCEID;
 }
 
@@ -7551,9 +7668,10 @@ bool ASTContext::isSameDefaultTemplateArgument(const NamedDecl *X,
         NTTPX->getDefaultArgument().getArgument().getAsExpr()->IgnoreImpCasts();
     Expr *DefaultArgumentY =
         NTTPY->getDefaultArgument().getArgument().getAsExpr()->IgnoreImpCasts();
+    // FIXME: This should probably use functional equivalence instead.
     llvm::FoldingSetNodeID XID, YID;
-    DefaultArgumentX->Profile(XID, *this, /*Canonical=*/true);
-    DefaultArgumentY->Profile(YID, *this, /*Canonical=*/true);
+    DefaultArgumentX->Profile(XID, *this, CanonicalizationKind::Structural);
+    DefaultArgumentY->Profile(YID, *this, CanonicalizationKind::Structural);
     return XID == YID;
   }
 
@@ -7636,8 +7754,10 @@ static bool hasSameOverloadableAttrs(const FunctionDecl *A,
     Cand1ID.clear();
     Cand2ID.clear();
 
-    (*Cand1A)->getCond()->Profile(Cand1ID, A->getASTContext(), true);
-    (*Cand2A)->getCond()->Profile(Cand2ID, B->getASTContext(), true);
+    (*Cand1A)->getCond()->Profile(Cand1ID, A->getASTContext(),
+                                  CanonicalizationKind::Structural);
+    (*Cand2A)->getCond()->Profile(Cand2ID, B->getASTContext(),
+                                  CanonicalizationKind::Structural);
 
     // Return false if any of the enable_if expressions of A and B are
     // different.
@@ -7888,51 +8008,71 @@ bool ASTContext::isSameEntity(const NamedDecl *X, const NamedDecl *Y) const {
 }
 
 TemplateArgument
-ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg) const {
+ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg,
+                                         CanonicalizationKind Kind,
+                                         bool &AnyNonCanonical) const {
   switch (Arg.getKind()) {
     case TemplateArgument::Null:
       return Arg;
 
-    case TemplateArgument::Expression:
-      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::Expression: {
+      auto CanonKind =
+          std::max(CanonicalizationKindOrNone(Kind), Arg.getExprCanonKind());
+      // If we are asked to produce a functionally equivalent template argument,
+      // try to downgrade the canonicalization kind to equivalent if possible.
+      // This avoids more expensive work later.
+      if (CanonKind == CanonicalizationKind::Functional) {
+        llvm::FoldingSetNodeID ID;
+        CanonKind = Arg.getAsExpr()->Profile(ID, *this, CanonKind);
+      }
+      AnyNonCanonical |= CanonKind != CanonicalizationKind::Structural;
+      return TemplateArgument(Arg.getAsExpr(), CanonKind, Arg.getIsDefaulted());
     }
 
+    case TemplateArgument::Declaration:
+      return TemplateArgument(
+          cast<ValueDecl>(Arg.getAsDecl()->getCanonicalDecl()),
+          getCanonicalType(Arg.getParamTypeForDecl(), Kind, AnyNonCanonical),
+          Arg.getIsDefaulted());
+
     case TemplateArgument::NullPtr:
-      return TemplateArgument(getCanonicalType(Arg.getNullPtrType()),
-                              /*isNullPtr*/ true, Arg.getIsDefaulted());
+      return TemplateArgument(
+          getCanonicalType(Arg.getNullPtrType(), Kind, AnyNonCanonical),
+          /*isNullPtr=*/true, Arg.getIsDefaulted());
 
     case TemplateArgument::Template:
-      return TemplateArgument(getCanonicalTemplateName(Arg.getAsTemplate()),
+      return TemplateArgument(getCanonicalTemplateName(Arg.getAsTemplate(),
+                                                       /*IgnoreDeduced=*/false,
+                                                       Kind, AnyNonCanonical),
                               Arg.getIsDefaulted());
 
     case TemplateArgument::TemplateExpansion:
       return TemplateArgument(
-          getCanonicalTemplateName(Arg.getAsTemplateOrTemplatePattern()),
+          getCanonicalTemplateName(Arg.getAsTemplateOrTemplatePattern(),
+                                   /*IgnoreDeduced=*/false, Kind,
+                                   AnyNonCanonical),
           Arg.getNumTemplateExpansions(), Arg.getIsDefaulted());
 
     case TemplateArgument::Integral:
-      return TemplateArgument(Arg, getCanonicalType(Arg.getIntegralType()));
+      return TemplateArgument(
+          Arg, getCanonicalType(Arg.getIntegralType(), Kind, AnyNonCanonical));
 
     case TemplateArgument::StructuralValue:
-      return TemplateArgument(*this,
-                              getCanonicalType(Arg.getStructuralValueType()),
-                              Arg.getAsStructuralValue(), Arg.getIsDefaulted());
+      return TemplateArgument(
+          *this,
+          getCanonicalType(Arg.getStructuralValueType(), Kind, AnyNonCanonical),
+          Arg.getAsStructuralValue(), Arg.getIsDefaulted());
 
     case TemplateArgument::Type:
-      return TemplateArgument(getCanonicalType(Arg.getAsType()),
-                              /*isNullPtr*/ false, Arg.getIsDefaulted());
+      return TemplateArgument(
+          getCanonicalType(Arg.getAsType(), Kind, 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, Kind, AnyNonCanonical);
+      if (!AnyChanged)
         return Arg;
       auto NewArg = TemplateArgument::CreatePackCopy(
           const_cast<ASTContext &>(*this), CanonArgs);
@@ -7978,8 +8118,8 @@ bool ASTContext::isSameTemplateArgument(const TemplateArgument &Arg1,
 
   case TemplateArgument::Expression: {
     llvm::FoldingSetNodeID ID1, ID2;
-    Arg1.getAsExpr()->Profile(ID1, *this, /*Canonical=*/true);
-    Arg2.getAsExpr()->Profile(ID2, *this, /*Canonical=*/true);
+    Arg1.getAsExpr()->Profile(ID1, *this, CanonicalizationKind::Structural);
+    Arg2.getAsExpr()->Profile(ID2, *this, CanonicalizationKind::Structural);
     return ID1 == ID2;
   }
 
@@ -8090,7 +8230,11 @@ QualType ASTContext::getArrayDecayedType(QualType Ty) const {
   const ArrayType *PrettyArrayType = getAsArrayType(Ty);
   assert(PrettyArrayType && "Not an array type!");
 
-  QualType PtrTy = getPointerType(PrettyArrayType->getElementType());
+  // Add outer qualifiers to element type.
+  QualType ElementType = getQualifiedType(PrettyArrayType->getElementType(),
+                                          Ty.getLocalQualifiers());
+
+  QualType PtrTy = getPointerType(ElementType);
 
   // int x[restrict 4] ->  int *restrict
   QualType Result = getQualifiedType(PtrTy,
@@ -13858,14 +14002,538 @@ unsigned ASTContext::getTargetAddressSpace(LangAS AS) const {
   return getTargetInfo().getTargetAddressSpace(AS);
 }
 
-bool ASTContext::hasSameExpr(const Expr *X, const Expr *Y) const {
+NestedNameSpecifier
+ASTContext::getCanonicalNestedNameSpecifier(NestedNameSpecifier Qualifier,
+                                            CanonicalizationKind CanonKind,
+                                            bool &AnyNonCanonical) const {
+  if (CanonKind == CanonicalizationKind::Functional &&
+      Qualifier.getKind() == NestedNameSpecifier::Kind::Type)
+    return NestedNameSpecifier(
+        getCanonicalType(QualType(Qualifier.getAsType(), 0),
+                         CanonicalizationKind::Functional, AnyNonCanonical)
+            .getTypePtr());
+  return Qualifier.getCanonical();
+}
+
+QualType ASTContext::getCanonicalType(QualType QT,
+                                      CanonicalizationKind Kind) const {
+  if (Kind == CanonicalizationKind::Structural)
+    return QT.getCanonicalType();
+  assert(Kind == CanonicalizationKind::Functional);
+
+  // A structural canonical type is functionally equivalent to itself.
+  if (QT.isCanonical())
+    return QT;
+
+  auto [T, Qualifiers] = QT.split();
+
+  // A non-instantiation-dependent type doesn't refer to any template
+  // parameters. The template parameters themselves never fail substitution, so
+  // they are functionally equivalent to their structural canonical type.
+  // A template type parameter substitution can't refer to any unsubstituted
+  // template parameters syntactically.
+  if (!T->isInstantiationDependentType() ||
+      isa<TemplateTypeParmType, SubstTemplateTypeParmType>(T))
+    return QT.getCanonicalType();
+
+  SplitQualType ST;
+  auto It = FunctionallyEquivalentTypeCache.find(T);
+  if (It != FunctionallyEquivalentTypeCache.end())
+    ST = It->second.split();
+  else {
+    ST = buildFunctionallyEquivalentCanonicalType(T);
+    QualType R = getQualifiedType(ST);
+
+    assert(hasSameType(QualType(T, 0), R));
+
+    auto [_, Inserted] = FunctionallyEquivalentTypeCache.try_emplace(T, R);
+    assert(Inserted && "Unexpected cache entry for type");
+  }
+  ST.Quals += Qualifiers;
+  // Remove redundant top level qualifiers.
+  ST.Quals -= ST.Ty->getCanonicalTypeInternal().getLocalQualifiers();
+  QualType R = getQualifiedType(ST);
+  assert(hasSameType(QT, R));
+  return R;
+}
+
+SplitQualType
+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::SubstTemplateTypeParm:
+  case Type::TemplateTypeParm:
+    llvm_unreachable("template parameters and their substitutions should have "
+                     "been handled by this point");
+  case Type::Complex:
+  case Type::FunctionNoProto:
+  case Type::Adjusted:
+  case Type::ObjCObject:
+  case Type::ObjCInterface:
+  case Type::ObjCObjectPointer:
+  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::ObjCTypeParam:
+  case Type::Pipe:
+  case Type::TypeOf:
+  case Type::DependentBitInt:
+  case Type::OverflowBehavior:
+  case Type::SubstBuiltinTemplatePack:
+    // FIXME: These are all unimplemented.
+    // Just return the structural canonical type, which is the easy thing to do.
+    break;
+  case Type::Atomic: {
+    const auto *TT = cast<AtomicType>(T);
+    QualType Value = getCanonicalType(
+        TT->getValueType(), CanonicalizationKind::Functional, AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getAtomicType(Value).split();
+  }
+  case Type::Auto: {
+    const auto *TT = cast<AutoType>(T);
+    SmallVector<TemplateArgument, 4> Args(TT->getTypeConstraintArguments());
+    canonicalizeTemplateArguments(Args, CanonicalizationKind::Functional,
+                                  AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    QualType DeducedAsType = TT->getDeducedType();
+    TemplateDecl *TD = TT->getTypeConstraintConcept();
+    return getAutoType(
+               TT->getDeducedKind(),
+               DeducedAsType.isNull() ? QualType()
+                                      : DeducedAsType.getCanonicalType(),
+               TT->getKeyword(),
+               TD ? cast<TemplateDecl>(TD->getCanonicalDecl()) : nullptr, Args)
+        .split();
+  }
+  case Type::FunctionProto: {
+    const auto *TT = cast<FunctionProtoType>(T);
+    FunctionProtoType::ExtProtoInfo EPI = TT->getExtProtoInfo();
+    EPI.HasTrailingReturn = false;
+    SmallVector<QualType, 8> ExceptionTypeStorage;
+    EPI.ExceptionSpec = ::getCanonicalExceptionSpec(
+        *this, EPI.ExceptionSpec, ExceptionTypeStorage,
+        CanonicalizationKind::Functional, AnyNonCanonical);
+    QualType RT = getCanonicalFunctionResultType(
+        TT->getReturnType(), CanonicalizationKind::Functional, AnyNonCanonical);
+    SmallVector<QualType, 8> PTs(TT->getParamTypes().size());
+    llvm::transform(TT->getParamTypes(), PTs.begin(), [&](QualType PT) {
+      return getCanonicalParamType(PT, CanonicalizationKind::Functional,
+                                   AnyNonCanonical);
+    });
+    if (!AnyNonCanonical)
+      break;
+    return getFunctionType(RT, PTs, EPI).split();
+  }
+  case Type::Pointer: {
+    const auto *TT = cast<PointerType>(T);
+    QualType Pointee =
+        getCanonicalType(TT->getPointeeType(), CanonicalizationKind::Functional,
+                         AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getPointerType(Pointee).split();
+  }
+  case Type::LValueReference: {
+    const auto *TT = cast<LValueReferenceType>(T);
+    QualType Pointee =
+        getCanonicalType(TT->getPointeeType(), CanonicalizationKind::Functional,
+                         AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getLValueReferenceType(Pointee).split();
+  }
+  case Type::RValueReference: {
+    const auto *TT = cast<RValueReferenceType>(T);
+    QualType Pointee =
+        getCanonicalType(TT->getPointeeType(), CanonicalizationKind::Functional,
+                         AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getRValueReferenceType(Pointee).split();
+  }
+  case Type::Enum:
+  case Type::Record:
+  case Type::InjectedClassName: {
+    const auto *TT = cast<TagType>(T);
+    NestedNameSpecifier Qualifier = getCanonicalNestedNameSpecifier(
+        TT->getQualifier(), CanonicalizationKind::Functional, AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getTagType(ElaboratedTypeKeyword::None, Qualifier,
+                      TT->getDecl()->getCanonicalDecl(),
+                      /*OwnsTag=*/false)
+        .split();
+  }
+  case Type::UnresolvedUsing: {
+    const auto *TT = cast<UnresolvedUsingType>(T);
+    NestedNameSpecifier Qualifier = getCanonicalNestedNameSpecifier(
+        TT->getQualifier(), CanonicalizationKind::Functional, AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getUnresolvedUsingType(ElaboratedTypeKeyword::None, Qualifier,
+                                  TT->getDecl()->getCanonicalDecl())
+        .split();
+  }
+  case Type::Using: {
+    const auto *TT = cast<UsingType>(T);
+    NestedNameSpecifier Qualifier = getCanonicalNestedNameSpecifier(
+        TT->getQualifier(), CanonicalizationKind::Functional, AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getUsingType(ElaboratedTypeKeyword::None, Qualifier,
+                        TT->getDecl()->getCanonicalDecl(),
+                        TT->getCanonicalTypeInternal())
+        .split();
+  }
+  case Type::Typedef: {
+    const auto *TT = cast<TypedefType>(T);
+    NestedNameSpecifier Qualifier = getCanonicalNestedNameSpecifier(
+        TT->getQualifier(), CanonicalizationKind::Functional, AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getTypedefType(ElaboratedTypeKeyword::None, Qualifier,
+                          TT->getDecl()->getCanonicalDecl())
+        .split();
+  }
+  case Type::Attributed:
+    return getCanonicalType(cast<AttributedType>(T)->desugar(),
+                            CanonicalizationKind::Functional, AnyNonCanonical)
+        .split();
+  case Type::MacroQualified:
+    return getCanonicalType(cast<MacroQualifiedType>(T)->desugar(),
+                            CanonicalizationKind::Functional, AnyNonCanonical)
+        .split();
+  case Type::Paren:
+    return getCanonicalType(cast<ParenType>(T)->desugar(),
+                            CanonicalizationKind::Functional, AnyNonCanonical)
+        .split();
+  case Type::PackExpansion: {
+    const auto *TT = cast<PackExpansionType>(T);
+    QualType Pattern = getCanonicalType(
+        TT->getPattern(), CanonicalizationKind::Functional, AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getPackExpansionType(Pattern, TT->getNumExpansions(),
+                                /*ExpectPackInType=*/false)
+        .split();
+  }
+  case Type::PackIndexing: {
+    const auto *TT = cast<PackIndexingType>(T);
+
+    // This type holds on to instantiation-dependence.
+    assert(!TT->isSugared());
+
+    QualType Pattern = getCanonicalType(
+        TT->getPattern(), CanonicalizationKind::Functional, AnyNonCanonical);
+    SmallVector<QualType, 8> Expansions(TT->getExpansions().size());
+    llvm::transform(TT->getExpansions(), Expansions.begin(), [&](QualType E) {
+      return getCanonicalType(E, CanonicalizationKind::Functional,
+                              AnyNonCanonical);
+    });
+    if (!AnyNonCanonical)
+      break;
+    return getPackIndexingType(Pattern, TT->getIndexExpr(),
+                               TT->isFullySubstituted(), Expansions,
+                               TT->getSelectedIndex())
+        .split();
+  }
+  case Type::UnaryTransform:
+    // FIXME: Unimplemented. These appear to have broken uniquing.
+    break;
+  case Type::MemberPointer: {
+    const auto *TT = cast<MemberPointerType>(T);
+    const auto *Cls = TT->getMostRecentCXXRecordDecl();
+    NestedNameSpecifier Qualifier = getCanonicalNestedNameSpecifier(
+        TT->getQualifier(), CanonicalizationKind::Functional, 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(), CanonicalizationKind::Functional,
+                         AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getMemberPointerType(Pointee, Qualifier, Cls).split();
+  }
+  case Type::BlockPointer: {
+    const auto *TT = cast<BlockPointerType>(T);
+    QualType Pointee =
+        getCanonicalType(TT->getPointeeType(), CanonicalizationKind::Functional,
+                         AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getBlockPointerType(Pointee).split();
+  }
+  // Arrays canonicalize by having their qualifiers pushed outside.
+  case Type::IncompleteArray: {
+    const auto *TT = cast<IncompleteArrayType>(T);
+    QualType ElementType =
+        getCanonicalType(TT->getElementType(), CanonicalizationKind::Functional,
+                         AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    auto [ETy, EQuals] = ElementType.split();
+    return SplitQualType(getIncompleteArrayType(QualType(ETy, 0),
+                                                TT->getSizeModifier(),
+                                                TT->getIndexTypeCVRQualifiers())
+                             .getTypePtr(),
+                         EQuals);
+  }
+  case Type::ConstantArray: {
+    const auto *TT = cast<ConstantArrayType>(T);
+    QualType ElementType =
+        getCanonicalType(TT->getElementType(), CanonicalizationKind::Functional,
+                         AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    auto [ETy, EQuals] = ElementType.split();
+    return SplitQualType(getConstantArrayType(QualType(ETy, 0), TT->getSize(),
+                                              TT->getSizeExpr(),
+                                              TT->getSizeModifier(),
+                                              TT->getIndexTypeCVRQualifiers())
+                             .getTypePtr(),
+                         EQuals);
+  }
+  case Type::VariableArray: {
+    const auto *TT = cast<VariableArrayType>(T);
+    QualType ElementType =
+        getCanonicalType(TT->getElementType(), CanonicalizationKind::Functional,
+                         AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    auto [ETy, EQuals] = ElementType.split();
+    return SplitQualType(getVariableArrayType(QualType(ETy, 0),
+                                              TT->getSizeExpr(),
+                                              TT->getSizeModifier(),
+                                              TT->getIndexTypeCVRQualifiers())
+                             .getTypePtr(),
+                         EQuals);
+  }
+  case Type::DependentSizedArray: {
+    const auto *TT = cast<DependentSizedArrayType>(T);
+    QualType ElementType =
+        getCanonicalType(TT->getElementType(), CanonicalizationKind::Functional,
+                         AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    auto [ETy, EQuals] = ElementType.split();
+    return SplitQualType(
+        getDependentSizedArrayType(QualType(ETy, 0), TT->getSizeExpr(),
+                                   TT->getSizeModifier(),
+                                   TT->getIndexTypeCVRQualifiers())
+            .getTypePtr(),
+        EQuals);
+  }
+  case Type::ExtVector: {
+    const auto *TT = cast<ExtVectorType>(T);
+    QualType ElementType =
+        getCanonicalType(TT->getElementType(), CanonicalizationKind::Functional,
+                         AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getExtVectorType(ElementType, TT->getNumElements()).split();
+  }
+  case Type::DependentSizedExtVector: {
+    const auto *TT = cast<DependentSizedExtVectorType>(T);
+    QualType ElementType =
+        getCanonicalType(TT->getElementType(), CanonicalizationKind::Functional,
+                         AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getDependentSizedExtVectorType(ElementType, TT->getSizeExpr(),
+                                          TT->getAttributeLoc())
+        .split();
+  }
+  case Type::ConstantMatrix: {
+    const auto *TT = cast<ConstantMatrixType>(T);
+    QualType ElementType =
+        getCanonicalType(TT->getElementType(), CanonicalizationKind::Functional,
+                         AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getConstantMatrixType(ElementType, TT->getNumRows(),
+                                 TT->getNumColumns())
+        .split();
+  }
+  case Type::DependentSizedMatrix: {
+    const auto *TT = cast<DependentSizedMatrixType>(T);
+    QualType ElementType =
+        getCanonicalType(TT->getElementType(), CanonicalizationKind::Functional,
+                         AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getDependentSizedMatrixType(ElementType, TT->getRowExpr(),
+                                       TT->getColumnExpr(),
+                                       TT->getAttributeLoc())
+        .split();
+  }
+  case Type::DependentAddressSpace: {
+    const auto *TT = cast<DependentAddressSpaceType>(T);
+    QualType Pointee =
+        getCanonicalType(TT->getPointeeType(), CanonicalizationKind::Functional,
+                         AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getDependentAddressSpaceType(Pointee, TT->getAddrSpaceExpr(),
+                                        TT->getAttributeLoc())
+        .split();
+  }
+  case Type::SubstTemplateTypeParmPack: {
+    const auto *TT = cast<SubstTemplateTypeParmPackType>(T);
+    TemplateArgument ArgPack = getCanonicalTemplateArgument(
+        TT->getArgumentPack(), CanonicalizationKind::Functional,
+        AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getSubstTemplateTypeParmPackType(
+               TT->getAssociatedDecl()->getCanonicalDecl(), TT->getIndex(),
+               TT->getFinal(), ArgPack)
+        .split();
+  }
+  case Type::DependentName: {
+    const auto *TT = cast<DependentNameType>(T);
+    NestedNameSpecifier Qualifier = getCanonicalNestedNameSpecifier(
+        TT->getQualifier(), CanonicalizationKind::Functional, AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getDependentNameType(
+               ::getCanonicalElaboratedTypeKeyword(TT->getKeyword()), Qualifier,
+               TT->getIdentifier())
+        .split();
+  }
+  case Type::TemplateSpecialization: {
+    const auto *TT = cast<TemplateSpecializationType>(T);
+    TemplateName TN = getCanonicalTemplateName(
+        TT->getTemplateName(), /*IgnoreDeduced=*/true,
+        CanonicalizationKind::Functional, AnyNonCanonical);
+
+    // For a type alias, additionally we want to keep if there are unused
+    // instantiation-dependent template arguments.
+    if (!AnyNonCanonical && TT->isTypeAlias()) {
+      const TemplateDecl *TD = TT->getTemplateName().getAsTemplateDecl();
+      auto As = TT->template_arguments();
+      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() &&
+            llvm::any_of(CurAs, [](const TemplateArgument &A) {
+              return A.isInstantiationDependent();
+            })) {
+          AnyNonCanonical = true;
+          break;
+        }
+        As = As.drop_front(CurAs.size());
+      }
+      if (!AnyNonCanonical)
+        return getCanonicalType(TT->getAliasedType(),
+                                CanonicalizationKind::Functional,
+                                AnyNonCanonical)
+            .split();
+    }
+    // FIXME: We can't avoid rebuilding if nothing changed, because we can't
+    // rely on this TST to have been uniqued.
+    // FIXME: This should contain default arguments as well, but we don't have a
+    // cheap way to get those here.
+    bool AnyChanged = false;
+    auto As = ::getCanonicalTemplateArguments(
+        *this, TT->template_arguments(), AnyChanged,
+        CanonicalizationKind::Functional, AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getTemplateSpecializationType(
+               TN.isDependent()
+                   ? ::getCanonicalElaboratedTypeKeyword(TT->getKeyword())
+                   : ElaboratedTypeKeyword::None,
+               TN, As,
+               /*CanonicalArgs=*/ArrayRef<TemplateArgument>(),
+               TT->desugar().getCanonicalType(),
+               /*Unique=*/true)
+        .split();
+  }
+  case Type::DeducedTemplateSpecialization: {
+    const auto *TT = cast<DeducedTemplateSpecializationType>(T);
+    TemplateName TN =
+        getCanonicalTemplateName(TT->getTemplateName(), /*IgnoreDeduced=*/true,
+                                 CanonicalizationKind::Functional);
+    if (!AnyNonCanonical)
+      break;
+    return getDeducedTemplateSpecializationType(
+               TT->getDeducedKind(), TT->desugar().getCanonicalType(),
+               ElaboratedTypeKeyword::None, TN)
+        .split();
+  }
+  case Type::Decayed: {
+    const auto *TT = cast<DecayedType>(T);
+    QualType OriginalType =
+        getCanonicalType(TT->getOriginalType(),
+                         CanonicalizationKind::Functional, AnyNonCanonical);
+    if (!AnyNonCanonical)
+      break;
+    return getDecayedType(OriginalType, TT->getDecayedType().getCanonicalType())
+        .split();
+  }
+  case Type::Decltype: {
+    const auto *TT = cast<DecltypeType>(T);
+    QualType UnderlyingType = TT->getUnderlyingType();
+    if (!UnderlyingType.isNull()) {
+      AnyNonCanonical = true;
+      UnderlyingType = UnderlyingType.getCanonicalType();
+    }
+    auto ExprCanonKind = std::max<CanonicalizationKindOrNone>(
+        TT->getExprCanonicalizationKind(), CanonicalizationKind::Functional);
+    return getDecltypeType(TT->getUnderlyingExpr(), ExprCanonKind,
+                           UnderlyingType)
+        .split();
+  }
+  case Type::TypeOfExpr: {
+    const auto *TT = cast<TypeOfExprType>(T);
+    // FIXME: Can't be implemented, as sugar TypeOfExprTypes are not uniqued,
+    // and they can't be canonicalzied on the expression CanonKind.
+    if (!TT->isSugared())
+      break;
+    AnyNonCanonical = true;
+    return SplitQualType(T, Qualifiers());
+  }
+  }
+  return T->getCanonicalTypeInternal().split();
+}
+
+bool ASTContext::hasSameExpr(const Expr *X, const Expr *Y,
+                             CanonicalizationKindOrNone CanonKind) const {
   if (X == Y)
     return true;
   if (!X || !Y)
     return false;
   llvm::FoldingSetNodeID IDX, IDY;
-  X->Profile(IDX, *this, /*Canonical=*/true);
-  Y->Profile(IDY, *this, /*Canonical=*/true);
+  X->Profile(IDX, *this, CanonKind);
+  Y->Profile(IDY, *this, CanonKind);
   return IDX == IDY;
 }
 
@@ -14242,6 +14910,29 @@ ASTContext::mergeExceptionSpecs(FunctionProtoType::ExceptionSpecInfo ESI1,
   llvm_unreachable("invalid ExceptionSpecificationType");
 }
 
+static CanonicalizationKindOrNone
+getCommonExprCanonicalizationKind(const ASTContext &Ctx, const Expr *EX,
+                                  CanonicalizationKindOrNone KX, const Expr *EY,
+                                  CanonicalizationKindOrNone KY) {
+  CanonicalizationKindOrNone MaxExprCanonKind = std::max(KX, KY);
+  CanonicalizationKindOrNone ExprCanonKind = CanonicalizationKind::Structural;
+  // FIXME: Also try FunctionallyEquivalent canonicalization.
+  if (!MaxExprCanonKind && Ctx.hasSameExpr(EX, EY,
+                                           /*CanonKind=*/std::nullopt))
+    ExprCanonKind = std::nullopt;
+  return ExprCanonKind;
+}
+
+static std::tuple<bool, CanonicalizationKindOrNone>
+getCommonExprCanonicalizationKindChecked(const ASTContext &Ctx, const Expr *EX,
+                                         CanonicalizationKindOrNone KX,
+                                         const Expr *EY,
+                                         CanonicalizationKindOrNone KY) {
+  if (!Ctx.hasSameExpr(EX, EY, CanonicalizationKind::Structural))
+    return {true, std::nullopt};
+  return {false, ::getCommonExprCanonicalizationKind(Ctx, EX, KX, EY, KY)};
+}
+
 static QualType getCommonNonSugarTypeNode(const ASTContext &Ctx, const Type *X,
                                           Qualifiers &QX, const Type *Y,
                                           Qualifiers &QY) {
@@ -14511,9 +15202,17 @@ static QualType getCommonNonSugarTypeNode(const ASTContext &Ctx, const Type *X,
     [[maybe_unused]] const auto *DY = cast<DecltypeType>(Y);
     assert(DX->isDependentType());
     assert(DY->isDependentType());
-    assert(Ctx.hasSameExpr(DX->getUnderlyingExpr(), DY->getUnderlyingExpr()));
-    // As Decltype is not uniqued, building a common type would be wasteful.
-    return QualType(DX, 0);
+    assert(DX->getUnderlyingType().isNull());
+    assert(DY->getUnderlyingType().isNull());
+
+    Expr *E = DX->getUnderlyingExpr();
+    assert(Ctx.hasSameExpr(E, DY->getUnderlyingExpr()));
+
+    CanonicalizationKindOrNone ExprCanonKind =
+        ::getCommonExprCanonicalizationKind(
+            Ctx, E, DX->getExprCanonicalizationKind(), DY->getUnderlyingExpr(),
+            DY->getExprCanonicalizationKind());
+    return Ctx.getDecltypeType(E, ExprCanonKind, QualType());
   }
   case Type::PackIndexing: {
     const auto *DX = cast<PackIndexingType>(X);
@@ -14689,9 +15388,25 @@ static QualType getCommonSugarTypeNode(const ASTContext &Ctx, const Type *X,
                            Ctx.getQualifiedType(Underlying), AX->getKeyword(),
                            CD, As);
   }
+  case Type::Decltype: {
+    const auto *DX = cast<DecltypeType>(X);
+    [[maybe_unused]] const auto *DY = cast<DecltypeType>(Y);
+    assert(!DX->isDependentType());
+    assert(!DY->isDependentType());
+    assert(!DX->getUnderlyingType().isNull());
+    assert(!DY->getUnderlyingType().isNull());
+
+    Expr *E = DX->getUnderlyingExpr();
+    auto [NotEquivalent, ExprCanonKind] =
+        ::getCommonExprCanonicalizationKindChecked(
+            Ctx, E, DX->getExprCanonicalizationKind(), DY->getUnderlyingExpr(),
+            DY->getExprCanonicalizationKind());
+    if (NotEquivalent)
+      return QualType();
+    return Ctx.getDecltypeType(E, ExprCanonKind,
+                               Ctx.getQualifiedType(Underlying));
+  }
   case Type::PackIndexing:
-  case Type::Decltype:
-    return QualType();
   case Type::DeducedTemplateSpecialization:
     // FIXME: Try to merge these.
     return QualType();
diff --git a/clang/lib/AST/ASTDiagnostic.cpp b/clang/lib/AST/ASTDiagnostic.cpp
index b8023cb6fa10f..edb19e9426c61 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;
       }
@@ -1534,8 +1534,8 @@ class TemplateDiff {
       return false;
 
     llvm::FoldingSetNodeID FromID, ToID;
-    FromExpr->Profile(FromID, Context, true);
-    ToExpr->Profile(ToID, Context, true);
+    FromExpr->Profile(FromID, Context, CanonicalizationKind::Structural);
+    ToExpr->Profile(ToID, Context, CanonicalizationKind::Structural);
     return FromID == ToID;
   }
 
@@ -2149,15 +2149,21 @@ class TemplateDiff {
       return;
     }
 
+    TemplateDecl *FromOrigTD = FromOrigTST->getTemplateName().getAsTemplateDecl(
+        /*IgnoreDeduced=*/true);
+    TemplateDecl *ToOrigTD = ToOrigTST->getTemplateName().getAsTemplateDecl(
+        /*IgnoreDeduced=*/true);
+    // If either side does not have a template declaration, then there are no
+    // template parameters, and nothing further to diff.
+    if (!FromOrigTD || !ToOrigTD)
+      return;
+
     FromQual -= QualType(FromOrigTST, 0).getQualifiers();
     ToQual -= QualType(ToOrigTST, 0).getQualifiers();
 
     // Same base template, but different arguments.
-    Tree.SetTemplateDiff(
-        FromOrigTST->getTemplateName().getAsTemplateDecl(
-            /*IgnoreDeduced=*/true),
-        ToOrigTST->getTemplateName().getAsTemplateDecl(/*IgnoreDeduced=*/true),
-        FromQual, ToQual, false /*FromDefault*/, false /*ToDefault*/);
+    Tree.SetTemplateDiff(FromOrigTD, ToOrigTD, FromQual, ToQual,
+                         false /*FromDefault*/, false /*ToDefault*/);
 
     DiffTemplate(FromOrigTST, ToOrigTST);
   }
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 41ba98c53247d..2cb51c2625f91 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -913,7 +913,7 @@ ASTNodeImporter::import(const TemplateArgument &From) {
 
   case TemplateArgument::Expression:
     if (ExpectedExpr ToExpr = import(From.getAsExpr()))
-      return TemplateArgument(*ToExpr, From.isCanonicalExpr(),
+      return TemplateArgument(*ToExpr, From.getExprCanonKind(),
                               From.getIsDefaulted());
     else
       return ToExpr.takeError();
@@ -1710,7 +1710,7 @@ ExpectedType ASTNodeImporter::VisitDecltypeType(const DecltypeType *T) {
     return ToUnderlyingTypeOrErr.takeError();
 
   return Importer.getToContext().getDecltypeType(
-      *ToExprOrErr, *ToUnderlyingTypeOrErr);
+      *ToExprOrErr, T->getExprCanonicalizationKind(), *ToUnderlyingTypeOrErr);
 }
 
 ExpectedType
@@ -6491,7 +6491,6 @@ ExpectedDecl ASTNodeImporter::VisitClassTemplateSpecializationDecl(
     if (GetImportedOrCreateDecl<ClassTemplatePartialSpecializationDecl>(
             D2, D, Importer.getToContext(), D->getTagKind(), DC, *BeginLocOrErr,
             *IdLocOrErr, ToTPList, ClassTemplate, ArrayRef(TemplateArgs),
-            /*CanonInjectedTST=*/CanQualType(),
             cast_or_null<ClassTemplatePartialSpecializationDecl>(PrevDecl)))
       return D2;
 
diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp
index 99d02fdc99e92..1b9c09bda9035 100644
--- a/clang/lib/AST/DeclTemplate.cpp
+++ b/clang/lib/AST/DeclTemplate.cpp
@@ -139,16 +139,17 @@ void TemplateParameterList::Profile(llvm::FoldingSetNodeID &ID,
   const Expr *RC = getRequiresClause();
   ID.AddBoolean(RC != nullptr);
   if (RC)
-    RC->Profile(ID, C, /*Canonical=*/true);
+    RC->Profile(ID, C, CanonicalizationKind::Functional);
   ID.AddInteger(size());
   for (NamedDecl *D : *this) {
     if (const auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(D)) {
       ID.AddInteger(0);
       ID.AddBoolean(NTTP->isParameterPack());
-      NTTP->getType().getCanonicalType().Profile(ID);
+      C.getCanonicalType(NTTP->getType(), CanonicalizationKind::Functional)
+          .Profile(ID);
       ID.AddBoolean(NTTP->hasPlaceholderTypeConstraint());
       if (const Expr *E = NTTP->getPlaceholderTypeConstraint())
-        E->Profile(ID, C, /*Canonical=*/true);
+        E->Profile(ID, C, CanonicalizationKind::Functional);
       continue;
     }
     if (const auto *TTP = dyn_cast<TemplateTypeParmDecl>(D)) {
@@ -156,8 +157,8 @@ void TemplateParameterList::Profile(llvm::FoldingSetNodeID &ID,
       ID.AddBoolean(TTP->isParameterPack());
       ID.AddBoolean(TTP->hasTypeConstraint());
       if (const TypeConstraint *TC = TTP->getTypeConstraint())
-        TC->getImmediatelyDeclaredConstraint()->Profile(ID, C,
-                                                        /*Canonical=*/true);
+        TC->getImmediatelyDeclaredConstraint()->Profile(
+            ID, C, CanonicalizationKind::Functional);
       continue;
     }
     const auto *TTP = cast<TemplateTemplateParmDecl>(D);
@@ -1165,15 +1166,13 @@ ClassTemplatePartialSpecializationDecl::ClassTemplatePartialSpecializationDecl(
     ASTContext &Context, TagKind TK, DeclContext *DC, SourceLocation StartLoc,
     SourceLocation IdLoc, TemplateParameterList *Params,
     ClassTemplateDecl *SpecializedTemplate, ArrayRef<TemplateArgument> Args,
-    CanQualType CanonInjectedTST,
     ClassTemplatePartialSpecializationDecl *PrevDecl)
     : ClassTemplateSpecializationDecl(
           Context, ClassTemplatePartialSpecialization, TK, DC, StartLoc, IdLoc,
           // Tracking StrictPackMatch for Partial
           // Specializations is not needed.
           SpecializedTemplate, Args, /*StrictPackMatch=*/false, PrevDecl),
-      TemplateParams(Params), InstantiatedFromMember(nullptr, false),
-      CanonInjectedTST(CanonInjectedTST) {
+      TemplateParams(Params), InstantiatedFromMember(nullptr, false) {
   if (AdoptTemplateParameterList(Params, this))
     setInvalidDecl();
 }
@@ -1183,11 +1182,10 @@ ClassTemplatePartialSpecializationDecl::Create(
     ASTContext &Context, TagKind TK, DeclContext *DC, SourceLocation StartLoc,
     SourceLocation IdLoc, TemplateParameterList *Params,
     ClassTemplateDecl *SpecializedTemplate, ArrayRef<TemplateArgument> Args,
-    CanQualType CanonInjectedTST,
     ClassTemplatePartialSpecializationDecl *PrevDecl) {
   auto *Result = new (Context, DC) ClassTemplatePartialSpecializationDecl(
       Context, TK, DC, StartLoc, IdLoc, Params, SpecializedTemplate, Args,
-      CanonInjectedTST, PrevDecl);
+      PrevDecl);
   Result->setSpecializationKind(TSK_ExplicitSpecialization);
   return Result;
 }
@@ -1202,11 +1200,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..96b6ffe5ca250 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -544,8 +544,9 @@ class CXXNameMangler {
   void manglePrefix(NestedNameSpecifier Qualifier);
   void manglePrefix(const DeclContext *DC, bool NoFunction=false);
   void manglePrefix(QualType type);
+  void mangleNameWithPrefix(GlobalDecl GD, NestedNameSpecifier Qualifier);
   void mangleTemplatePrefix(GlobalDecl GD, bool NoFunction=false);
-  void mangleTemplatePrefix(TemplateName Template);
+  [[nodiscard]] bool mangleTemplatePrefix(TemplateName Template, bool InPrefix);
   const NamedDecl *getClosurePrefix(const Decl *ND);
   void mangleClosurePrefix(const NamedDecl *ND, bool NoFunction = false);
   bool mangleUnresolvedTypeOrSimpleId(QualType DestroyedType,
@@ -559,7 +560,6 @@ class CXXNameMangler {
 
   // Declare manglers for every type class.
 #define ABSTRACT_TYPE(CLASS, PARENT)
-#define NON_CANONICAL_TYPE(CLASS, PARENT)
 #define TYPE(CLASS, PARENT) void mangleType(const CLASS##Type *T);
 #include "clang/AST/TypeNodes.inc"
 
@@ -1206,7 +1206,7 @@ void CXXNameMangler::mangleUnscopedTemplateName(
     assert(!AdditionalAbiTags &&
            "template template param cannot have abi tags");
     mangleTemplateParameter(TTP->getDepth(), TTP->getIndex());
-  } else if (isa<BuiltinTemplateDecl>(ND) || isa<ConceptDecl>(ND)) {
+  } else if (isa<BuiltinTemplateDecl, ConceptDecl, TypeAliasTemplateDecl>(ND)) {
     mangleUnscopedName(GD, DC, AdditionalAbiTags);
   } else {
     mangleUnscopedName(GD.getWithDecl(ND->getTemplatedDecl()), DC,
@@ -1320,7 +1320,7 @@ void CXXNameMangler::mangleCallOffset(int64_t NonVirtual, int64_t Virtual) {
 void CXXNameMangler::manglePrefix(QualType type) {
   if (const auto *TST = type->getAs<TemplateSpecializationType>()) {
     if (!mangleSubstitution(QualType(TST, 0))) {
-      mangleTemplatePrefix(TST->getTemplateName());
+      (void)mangleTemplatePrefix(TST->getTemplateName(), /*InPrefix=*/true);
 
       // FIXME: GCC does not appear to mangle the template arguments when
       // the template in question is a dependent template name. Should we
@@ -2259,39 +2259,102 @@ void CXXNameMangler::manglePrefix(const DeclContext *DC, bool NoFunction) {
   addSubstitution(ND);
 }
 
-void CXXNameMangler::mangleTemplatePrefix(TemplateName Template) {
+bool CXXNameMangler::mangleTemplatePrefix(TemplateName Template,
+                                          bool InPrefix) {
   // <template-prefix> ::= <prefix> <template unqualified-name>
   //                   ::= <template-param>
   //                   ::= <substitution>
-  if (TemplateDecl *TD = Template.getAsTemplateDecl())
-    return mangleTemplatePrefix(TD);
+  switch (Template.getKind()) {
+  case clang::TemplateName::Template:
+  case clang::TemplateName::UsingTemplate: {
+    const TemplateDecl *TD = Template.getAsTemplateDecl();
+    const DeclContext *DC = Context.getEffectiveDeclContext(TD);
+
+    if (DC->isTranslationUnit() || isStdNamespace(DC)) {
+      mangleUnscopedTemplateName(TD, DC, nullptr);
+      return false;
+    }
+    if (!InPrefix)
+      Out << 'N';
+    mangleTemplatePrefix(TD);
+    return true;
+  }
+  case clang::TemplateName::QualifiedTemplate: {
+    const QualifiedTemplateName *S = Template.getAsQualifiedTemplateName();
 
-  DependentTemplateName *Dependent = Template.getAsDependentTemplateName();
-  assert(Dependent && "unexpected template name kind");
+    if (!InPrefix)
+      Out << 'N';
+    manglePrefix(S->getQualifier());
 
-  // Clang 11 and before mangled the substitution for a dependent template name
-  // after already having emitted (a substitution for) the prefix.
-  bool Clang11Compat = isCompatibleWith(LangOptions::ClangABI::Ver11);
-  if (!Clang11Compat && mangleSubstitution(Template))
-    return;
+    TemplateName Underlying = S->getUnderlyingTemplate();
+    const NamedDecl *ND;
+    switch (Underlying.getKind()) {
+    case TemplateName::Template:
+      ND = Underlying.getAsTemplateDecl();
+      break;
+    case TemplateName::UsingTemplate:
+      ND = Underlying.getAsUsingShadowDecl();
+      break;
+    default:
+      llvm_unreachable(
+          "unexpected underlying template name kind for qualified template");
+    }
+    mangleTemplatePrefix(ND);
+    return true;
+    break;
+  }
+  case clang::TemplateName::DependentTemplate: {
+    DependentTemplateName *Dependent = Template.getAsDependentTemplateName();
+    assert(Dependent && "unexpected template name kind");
 
-  manglePrefix(Dependent->getQualifier());
+    if (!InPrefix)
+      Out << 'N';
 
-  if (Clang11Compat && mangleSubstitution(Template))
-    return;
+    // Clang 11 and before mangled the substitution for a dependent template
+    // name after already having emitted (a substitution for) the prefix.
+    bool Clang11Compat = isCompatibleWith(LangOptions::ClangABI::Ver11);
+    if (!Clang11Compat && mangleSubstitution(Template))
+      return true;
 
-  if (IdentifierOrOverloadedOperator Name = Dependent->getName();
-      const IdentifierInfo *Id = Name.getIdentifier())
-    mangleSourceName(Id);
-  else
-    mangleOperatorName(Name.getOperator(), UnknownArity);
+    manglePrefix(Dependent->getQualifier());
+
+    if (Clang11Compat && mangleSubstitution(Template))
+      return true;
+
+    if (IdentifierOrOverloadedOperator Name = Dependent->getName();
+        const IdentifierInfo *Id = Name.getIdentifier())
+      mangleSourceName(Id);
+    else
+      mangleOperatorName(Name.getOperator(), UnknownArity);
+
+    addSubstitution(Template);
+    return true;
+  }
+  case clang::TemplateName::SubstTemplateTemplateParm:
+  case clang::TemplateName::AssumedTemplate:
+  case clang::TemplateName::DeducedTemplate:
+  case clang::TemplateName::SubstTemplateTemplateParmPack:
+  case clang::TemplateName::OverloadedTemplate:
+    llvm_unreachable("these shouldn't survive to mangling");
+    break;
+  }
+  llvm_unreachable("unhandled template name kind");
+}
 
-  addSubstitution(Template);
+void CXXNameMangler::mangleNameWithPrefix(GlobalDecl GD,
+                                          NestedNameSpecifier Qualifier) {
+  if (Qualifier) {
+    Out << 'N';
+    manglePrefix(Qualifier);
+  }
+  mangleName(GD);
+  if (Qualifier)
+    Out << 'E';
 }
 
 void CXXNameMangler::mangleTemplatePrefix(GlobalDecl GD,
                                           bool NoFunction) {
-  const TemplateDecl *ND = cast<TemplateDecl>(GD.getDecl());
+  const NamedDecl *ND = cast<NamedDecl>(GD.getDecl());
   // <template-prefix> ::= <prefix> <template unqualified-name>
   //                   ::= <template-param>
   //                   ::= <substitution>
@@ -2309,9 +2372,12 @@ void CXXNameMangler::mangleTemplatePrefix(GlobalDecl GD,
     manglePrefix(DC, NoFunction);
     if (isa<BuiltinTemplateDecl>(ND) || isa<ConceptDecl>(ND))
       mangleUnqualifiedName(GD, DC, nullptr);
-    else
-      mangleUnqualifiedName(GD.getWithDecl(ND->getTemplatedDecl()), DC,
-                            nullptr);
+    else {
+      const auto *SD = dyn_cast<UsingShadowDecl>(ND);
+      const NamedDecl *TD =
+          SD ? SD->getTargetDecl() : cast<TemplateDecl>(ND)->getTemplatedDecl();
+      mangleUnqualifiedName(GD.getWithDecl(TD), DC, nullptr);
+    }
   }
 
   addSubstitution(ND);
@@ -3008,53 +3074,13 @@ void CXXNameMangler::mangleType(QualType T) {
   // augmented via semantic analysis (i.e., with implicit conversions and
   // default template arguments) for any instantiation-dependent type.
   // Unfortunately, that requires several changes to our AST:
-  //   - Instantiation-dependent TemplateSpecializationTypes will need to be
-  //     uniqued, so that we can handle substitutions properly
   //   - Default template arguments will need to be represented in the
   //     TemplateSpecializationType, since they need to be mangled even though
   //     they aren't written.
   //   - Conversions on non-type template arguments need to be expressed, since
   //     they can affect the mangling of sizeof/alignof.
-  //
-  // FIXME: This is wrong when mapping to the canonical type for a dependent
-  // type discards instantiation-dependent portions of the type, such as for:
-  //
-  //   template<typename T, int N> void f(T (&)[sizeof(N)]);
-  //   template<typename T> void f(T() throw(typename T::type)); (pre-C++17)
-  //
-  // It's also wrong in the opposite direction when instantiation-dependent,
-  // canonically-equivalent types differ in some irrelevant portion of inner
-  // type sugar. In such cases, we fail to form correct substitutions, eg:
-  //
-  //   template<int N> void f(A<sizeof(N)> *, A<sizeof(N)> (*));
-  //
-  // We should instead canonicalize the non-instantiation-dependent parts,
-  // regardless of whether the type as a whole is dependent or instantiation
-  // dependent.
-  if (!T->isInstantiationDependentType() || T->isDependentType())
-    T = T.getCanonicalType();
-  else {
-    // Desugar any types that are purely sugar.
-    do {
-      // Don't desugar through template specialization types that aren't
-      // type aliases. We need to mangle the template arguments as written.
-      if (const TemplateSpecializationType *TST
-                                      = dyn_cast<TemplateSpecializationType>(T))
-        if (!TST->isTypeAlias())
-          break;
-
-      // FIXME: We presumably shouldn't strip off ElaboratedTypes with
-      // instantation-dependent qualifiers. See
-      // https://github.com/itanium-cxx-abi/cxx-abi/issues/114.
-
-      QualType Desugared
-        = T.getSingleStepDesugaredType(Context.getASTContext());
-      if (Desugared == T)
-        break;
-
-      T = Desugared;
-    } while (true);
-  }
+  T = Context.getASTContext().getCanonicalType(
+      T, CanonicalizationKind::Functional);
   auto [ty, quals] = T.split();
 
   bool isSubstitutable =
@@ -3088,10 +3114,6 @@ void CXXNameMangler::mangleType(QualType T) {
   } else {
     switch (ty->getTypeClass()) {
 #define ABSTRACT_TYPE(CLASS, PARENT)
-#define NON_CANONICAL_TYPE(CLASS, PARENT) \
-    case Type::CLASS: \
-      llvm_unreachable("can't mangle non-canonical type " #CLASS "Type"); \
-      return;
 #define TYPE(CLASS, PARENT) \
     case Type::CLASS: \
       mangleType(static_cast<const CLASS##Type*>(ty)); \
@@ -3799,8 +3821,8 @@ void CXXNameMangler::mangleBareFunctionType(const FunctionProtoType *Proto,
       }
 
       // Mangle the type.
-      QualType ParamTy = Proto->getParamType(I);
-      mangleType(Context.getASTContext().getSignatureParameterType(ParamTy));
+      mangleType(Context.getASTContext().getCanonicalParamType(
+          Proto->getParamType(I), CanonicalizationKind::Functional));
 
       if (FD) {
         if (auto *Attr = FD->getParamDecl(I)->getAttr<PassObjectSizeAttr>()) {
@@ -3828,10 +3850,8 @@ void CXXNameMangler::mangleBareFunctionType(const FunctionProtoType *Proto,
   FunctionTypeDepth.pop(saved);
 }
 
-// <type>            ::= <class-enum-type>
-// <class-enum-type> ::= <name>
 void CXXNameMangler::mangleType(const UnresolvedUsingType *T) {
-  mangleName(T->getDecl());
+  llvm_unreachable("UnresolvedUsingType shouldn't survive to mangling");
 }
 
 // <type>            ::= <class-enum-type>
@@ -3843,7 +3863,7 @@ void CXXNameMangler::mangleType(const RecordType *T) {
   mangleType(static_cast<const TagType*>(T));
 }
 void CXXNameMangler::mangleType(const TagType *T) {
-  mangleName(T->getDecl()->getDefinitionOrSelf());
+  mangleNameWithPrefix(T->getDecl(), T->getQualifier());
 }
 
 // <type>       ::= <array-type>
@@ -4480,18 +4500,15 @@ void CXXNameMangler::mangleType(const InjectedClassNameType *T) {
 }
 
 void CXXNameMangler::mangleType(const TemplateSpecializationType *T) {
-  if (TemplateDecl *TD = T->getTemplateName().getAsTemplateDecl()) {
-    mangleTemplateName(TD, T->template_arguments());
-  } else {
-    Out << 'N';
-    mangleTemplatePrefix(T->getTemplateName());
-
-    // FIXME: GCC does not appear to mangle the template arguments when
-    // the template in question is a dependent template name. Should we
-    // emulate that badness?
-    mangleTemplateArgs(T->getTemplateName(), T->template_arguments());
+  TemplateName Template = T->getTemplateName();
+  bool Nested = mangleTemplatePrefix(Template, /*InPrefix=*/false);
+
+  // FIXME: GCC does not appear to mangle the template arguments when
+  // the template in question is a dependent template name. Should we
+  // emulate that badness?
+  mangleTemplateArgs(Template, T->template_arguments());
+  if (Nested)
     Out << 'E';
-  }
 }
 
 void CXXNameMangler::mangleType(const DependentNameType *T) {
@@ -4658,6 +4675,45 @@ void CXXNameMangler::mangleType(const ArrayParameterType *T) {
   mangleType(cast<ConstantArrayType>(T));
 }
 
+void CXXNameMangler::mangleType(const TypedefType *T) {
+  mangleNameWithPrefix(T->getDecl(), T->getQualifier());
+}
+
+void CXXNameMangler::mangleType(const UsingType *T) {
+  mangleNameWithPrefix(T->getDecl(), T->getQualifier());
+}
+
+void CXXNameMangler::mangleType(const DecayedType *T) {
+  llvm_unreachable("can't mangle non-canonical type");
+}
+void CXXNameMangler::mangleType(const AdjustedType *T) {
+  llvm_unreachable("can't mangle non-canonical type");
+}
+void CXXNameMangler::mangleType(const AttributedType *T) {
+  llvm_unreachable("can't mangle non-canonical type");
+}
+void CXXNameMangler::mangleType(const ObjCTypeParamType *T) {
+  llvm_unreachable("can't mangle non-canonical type");
+}
+void CXXNameMangler::mangleType(const MacroQualifiedType *T) {
+  llvm_unreachable("can't mangle non-canonical type");
+}
+void CXXNameMangler::mangleType(const CountAttributedType *T) {
+  llvm_unreachable("can't mangle non-canonical type");
+}
+void CXXNameMangler::mangleType(const PredefinedSugarType *T) {
+  llvm_unreachable("can't mangle non-canonical type");
+}
+void CXXNameMangler::mangleType(const BTFTagAttributedType *T) {
+  llvm_unreachable("can't mangle non-canonical type");
+}
+void CXXNameMangler::mangleType(const SubstTemplateTypeParmType *T) {
+  llvm_unreachable("can't mangle non-canonical type");
+}
+void CXXNameMangler::mangleType(const ParenType *T) {
+  llvm_unreachable("can't mangle non-canonical type");
+}
+
 void CXXNameMangler::mangleType(const HLSLAttributedResourceType *T) {
   llvm::SmallString<64> Str("_Res");
   const HLSLAttributedResourceType::Attributes &Attrs = T->getAttrs();
@@ -7040,8 +7096,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 +7267,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/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp
index 3138f95e6a83b..84b18f96f2dc9 100644
--- a/clang/lib/AST/JSONNodeDumper.cpp
+++ b/clang/lib/AST/JSONNodeDumper.cpp
@@ -1762,8 +1762,16 @@ void JSONNodeDumper::VisitTemplateExpansionTemplateArgument(
 void JSONNodeDumper::VisitExpressionTemplateArgument(
     const TemplateArgument &TA) {
   JOS.attribute("isExpr", true);
-  if (TA.isCanonicalExpr())
-    JOS.attribute("isCanonical", true);
+  if (auto Kind = TA.getExprCanonKind()) {
+    switch (*Kind) {
+    case CanonicalizationKind::Structural:
+      JOS.attribute("CanonicalKind", "Structural");
+      break;
+    case CanonicalizationKind::Functional:
+      JOS.attribute("CanonicalKind", "Functional");
+      break;
+    }
+  }
 }
 void JSONNodeDumper::VisitPackTemplateArgument(const TemplateArgument &TA) {
   JOS.attribute("isPack", true);
diff --git a/clang/lib/AST/ODRHash.cpp b/clang/lib/AST/ODRHash.cpp
index 46a4e256ea3e5..1eab0fc3c2cd1 100644
--- a/clang/lib/AST/ODRHash.cpp
+++ b/clang/lib/AST/ODRHash.cpp
@@ -994,14 +994,12 @@ class ODRTypeVisitor : public TypeVisitor<ODRTypeVisitor> {
   }
 
   void VisitDecltypeType(const DecltypeType *T) {
+    ID.AddInteger(T->getExprCanonicalizationKind().toInternalRepresentation());
+    // FIXME: The kind above should affect how the expression is hashed.
     Hash.AddStmt(T->getUnderlyingExpr());
     VisitType(T);
   }
 
-  void VisitDependentDecltypeType(const DependentDecltypeType *T) {
-    VisitDecltypeType(T);
-  }
-
   void VisitDeducedType(const DeducedType *T) {
     AddQualType(T->getDeducedType());
     VisitType(T);
diff --git a/clang/lib/AST/StmtProfile.cpp b/clang/lib/AST/StmtProfile.cpp
index bd65f20214791..80dea5a6d512b 100644
--- a/clang/lib/AST/StmtProfile.cpp
+++ b/clang/lib/AST/StmtProfile.cpp
@@ -28,13 +28,16 @@ namespace {
   class StmtProfiler : public ConstStmtVisitor<StmtProfiler> {
   protected:
     llvm::FoldingSetNodeID &ID;
-    bool Canonical;
+    const ASTContext *Context;
+    CanonicalizationKindOrNone CanonKind;
     bool ProfileLambdaExpr;
+    bool AnyNonCanonical = false;
 
   public:
-    StmtProfiler(llvm::FoldingSetNodeID &ID, bool Canonical,
-                 bool ProfileLambdaExpr)
-        : ID(ID), Canonical(Canonical), ProfileLambdaExpr(ProfileLambdaExpr) {}
+    StmtProfiler(llvm::FoldingSetNodeID &ID, const ASTContext *Context,
+                 CanonicalizationKindOrNone CanonKind, bool ProfileLambdaExpr)
+        : ID(ID), Context(Context), CanonKind(CanonKind),
+          ProfileLambdaExpr(ProfileLambdaExpr) {}
 
     virtual ~StmtProfiler() {}
 
@@ -46,6 +49,8 @@ namespace {
 
     virtual void HandleStmtClass(Stmt::StmtClass SC) = 0;
 
+    bool hasAnyNonCanonical() const { return AnyNonCanonical; }
+
 #define STMT(Node, Base) void Visit##Node(const Node *S);
 #include "clang/AST/StmtNodes.inc"
 
@@ -81,13 +86,12 @@ namespace {
   };
 
   class StmtProfilerWithPointers : public StmtProfiler {
-    const ASTContext &Context;
-
   public:
     StmtProfilerWithPointers(llvm::FoldingSetNodeID &ID,
-                             const ASTContext &Context, bool Canonical,
+                             const ASTContext &Context,
+                             CanonicalizationKindOrNone CanonKind,
                              bool ProfileLambdaExpr)
-        : StmtProfiler(ID, Canonical, ProfileLambdaExpr), Context(Context) {}
+        : StmtProfiler(ID, &Context, CanonKind, ProfileLambdaExpr) {}
 
   private:
     void HandleStmtClass(Stmt::StmtClass SC) override {
@@ -97,7 +101,7 @@ namespace {
     void VisitDecl(const Decl *D) override {
       ID.AddInteger(D ? D->getKind() : 0);
 
-      if (Canonical && D) {
+      if (CanonKind && D) {
         if (const NonTypeTemplateParmDecl *NTTP =
                 dyn_cast<NonTypeTemplateParmDecl>(D)) {
           ID.AddInteger(NTTP->getDepth());
@@ -111,7 +115,7 @@ namespace {
           //
           // TODO: Why do we need to include the type in the profile? It's not
           // part of the mangling.
-          VisitType(Context.getUnconstrainedType(NTTP->getType()));
+          VisitType(Context->getUnconstrainedType(NTTP->getType()));
           return;
         }
 
@@ -153,8 +157,8 @@ namespace {
     }
 
     void VisitType(QualType T) override {
-      if (Canonical && !T.isNull())
-        T = Context.getCanonicalType(T);
+      if (CanonKind && !T.isNull())
+        T = Context->getCanonicalType(T, *CanonKind, AnyNonCanonical);
 
       ID.AddPointer(T.getAsOpaquePtr());
     }
@@ -168,14 +172,16 @@ namespace {
     }
 
     void VisitNestedNameSpecifier(NestedNameSpecifier NNS) override {
-      if (Canonical)
-        NNS = NNS.getCanonical();
+      if (CanonKind)
+        NNS = Context->getCanonicalNestedNameSpecifier(NNS, *CanonKind,
+                                                       AnyNonCanonical);
       NNS.Profile(ID);
     }
 
     void VisitTemplateName(TemplateName Name) override {
-      if (Canonical)
-        Name = Context.getCanonicalTemplateName(Name);
+      if (CanonKind)
+        Name = Context->getCanonicalTemplateName(Name, /*IgnoreDeduced=*/false,
+                                                 *CanonKind, AnyNonCanonical);
 
       Name.Profile(ID);
     }
@@ -185,7 +191,8 @@ namespace {
     ODRHash &Hash;
   public:
     StmtProfilerWithoutPointers(llvm::FoldingSetNodeID &ID, ODRHash &Hash)
-        : StmtProfiler(ID, /*Canonical=*/false, /*ProfileLambdaExpr=*/false),
+        : StmtProfiler(ID, /*Context=*/nullptr, /*CanonKind=*/std::nullopt,
+                       /*ProfileLambdaExpr=*/false),
           Hash(Hash) {}
 
   private:
@@ -1404,10 +1411,10 @@ void StmtProfiler::VisitConstantExpr(const ConstantExpr *S) {
 
 void StmtProfiler::VisitDeclRefExpr(const DeclRefExpr *S) {
   VisitExpr(S);
-  if (!Canonical)
+  if (!CanonKind)
     VisitNestedNameSpecifier(S->getQualifier());
   VisitDecl(S->getDecl());
-  if (!Canonical) {
+  if (!CanonKind) {
     ID.AddBoolean(S->hasExplicitTemplateArgs());
     if (S->hasExplicitTemplateArgs())
       VisitTemplateArguments(S->getTemplateArgs(), S->getNumTemplateArgs());
@@ -1440,8 +1447,8 @@ void StmtProfiler::VisitIntegerLiteral(const IntegerLiteral *S) {
   S->getValue().Profile(ID);
 
   QualType T = S->getType();
-  if (Canonical)
-    T = T.getCanonicalType();
+  if (CanonKind)
+    T = Context->getCanonicalType(T, *CanonKind, AnyNonCanonical);
   ID.AddInteger(T->getTypeClass());
   if (auto BitIntT = T->getAs<BitIntType>())
     BitIntT->Profile(ID);
@@ -1561,7 +1568,7 @@ void StmtProfiler::VisitCallExpr(const CallExpr *S) {
 void StmtProfiler::VisitMemberExpr(const MemberExpr *S) {
   VisitExpr(S);
   VisitDecl(S->getMemberDecl());
-  if (!Canonical)
+  if (!CanonKind)
     VisitNestedNameSpecifier(S->getQualifier());
   ID.AddBoolean(S->isArrow());
 }
@@ -1569,6 +1576,7 @@ void StmtProfiler::VisitMemberExpr(const MemberExpr *S) {
 void StmtProfiler::VisitCompoundLiteralExpr(const CompoundLiteralExpr *S) {
   VisitExpr(S);
   ID.AddBoolean(S->isFileScope());
+  VisitType(S->getTypeSourceInfo()->getType());
 }
 
 void StmtProfiler::VisitCastExpr(const CastExpr *S) {
@@ -1615,6 +1623,7 @@ void StmtProfiler::VisitAddrLabelExpr(const AddrLabelExpr *S) {
 
 void StmtProfiler::VisitStmtExpr(const StmtExpr *S) {
   VisitExpr(S);
+  ID.AddInteger(S->getTemplateDepth());
 }
 
 void StmtProfiler::VisitShuffleVectorExpr(const ShuffleVectorExpr *S) {
@@ -2216,6 +2225,7 @@ void StmtProfiler::VisitCXXReflectExpr(const CXXReflectExpr *E) {
 void
 StmtProfiler::VisitCXXScalarValueInitExpr(const CXXScalarValueInitExpr *S) {
   VisitExpr(S);
+  VisitType(S->getTypeSourceInfo()->getType());
 }
 
 void StmtProfiler::VisitCXXDeleteExpr(const CXXDeleteExpr *S) {
@@ -2256,6 +2266,7 @@ void StmtProfiler::VisitOverloadExpr(const OverloadExpr *S) {
   VisitExpr(S);
   bool DescribingDependentVarTemplate =
       S->getNumDecls() == 1 && isa<VarTemplateDecl>(*S->decls_begin());
+  ID.AddBoolean(DescribingDependentVarTemplate);
   if (DescribingDependentVarTemplate) {
     VisitDecl(*S->decls_begin());
   } else {
@@ -2978,10 +2989,19 @@ void StmtProfiler::VisitHLSLOutArgExpr(const HLSLOutArgExpr *S) {
   VisitStmt(S);
 }
 
-void Stmt::Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context,
-                   bool Canonical, bool ProfileLambdaExpr) const {
-  StmtProfilerWithPointers Profiler(ID, Context, Canonical, ProfileLambdaExpr);
+CanonicalizationKindOrNone Stmt::Profile(llvm::FoldingSetNodeID &ID,
+                                         const ASTContext &Context,
+                                         CanonicalizationKindOrNone CanonKind,
+                                         bool ProfileLambdaExpr) const {
+  StmtProfilerWithPointers Profiler(ID, Context, CanonKind, ProfileLambdaExpr);
   Profiler.Visit(this);
+
+  // FIXME: The profiler does not yet support returning the maximum
+  // canonicalization kind for the no-canonicalization case.
+  if (!CanonKind)
+    return std::nullopt;
+  return Profiler.hasAnyNonCanonical() ? CanonicalizationKind::Functional
+                                       : CanonicalizationKind::Structural;
 }
 
 void Stmt::ProcessODRHash(llvm::FoldingSetNodeID &ID,
diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp
index a5e0dde45364c..100e91f454d84 100644
--- a/clang/lib/AST/TemplateBase.cpp
+++ b/clang/lib/AST/TemplateBase.cpp
@@ -426,13 +426,9 @@ void TemplateArgument::Profile(llvm::FoldingSetNodeID &ID,
     break;
 
   case Expression: {
-    const Expr *E = getAsExpr();
-    bool IsCanonical = isCanonicalExpr();
-    ID.AddBoolean(IsCanonical);
-    if (IsCanonical)
-      E->Profile(ID, Context, true);
-    else
-      ID.AddPointer(E);
+    CanonicalizationKindOrNone Kind = getExprCanonKind();
+    ID.AddInteger(Kind.toInternalRepresentation());
+    getAsExpr()->Profile(ID, Context, Kind);
     break;
   }
 
@@ -453,7 +449,7 @@ bool TemplateArgument::structurallyEquals(const TemplateArgument &Other) const {
     return TypeOrValue.V == Other.TypeOrValue.V;
   case Expression:
     return TypeOrValue.V == Other.TypeOrValue.V &&
-           TypeOrValue.IsCanonicalExpr == Other.TypeOrValue.IsCanonicalExpr;
+           TypeOrValue.ExprCanonKind == Other.TypeOrValue.ExprCanonKind;
 
   case Template:
   case TemplateExpansion:
@@ -499,7 +495,7 @@ TemplateArgument TemplateArgument::getPackExpansionPattern() const {
 
   case Expression:
     return TemplateArgument(cast<PackExpansionExpr>(getAsExpr())->getPattern(),
-                            isCanonicalExpr());
+                            getExprCanonKind());
 
   case TemplateExpansion:
     return TemplateArgument(getAsTemplateOrTemplatePattern());
@@ -572,7 +568,7 @@ void TemplateArgument::print(const PrintingPolicy &Policy, raw_ostream &Out,
 
   case Expression: {
     PrintingPolicy ExprPolicy = Policy;
-    ExprPolicy.PrintAsCanonical = isCanonicalExpr();
+    ExprPolicy.PrintAsCanonical = bool(getExprCanonKind());
     getAsExpr()->printPretty(Out, nullptr, ExprPolicy);
     break;
   }
diff --git a/clang/lib/AST/TemplateName.cpp b/clang/lib/AST/TemplateName.cpp
index 797a354c5d0fa..a1e90fdc78301 100644
--- a/clang/lib/AST/TemplateName.cpp
+++ b/clang/lib/AST/TemplateName.cpp
@@ -361,12 +361,7 @@ TemplateNameDependence TemplateName::getDependence() const {
       if (TTP->isParameterPack())
         D |= TemplateNameDependence::UnexpandedPack;
     }
-    // FIXME: Hack, getDeclContext() can be null if Template is still
-    // initializing due to PCH reading, so we check it before using it.
-    // Should probably modify TemplateSpecializationType to allow constructing
-    // it without the isDependent() checking.
-    if (Template->getDeclContext() &&
-        Template->getDeclContext()->isDependentContext())
+    if (Template->getDeclContext()->isDependentContext())
       D |= TemplateNameDependence::DependentInstantiation;
     return D;
   }
diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp
index 250ec8b666e05..4648e1517a0d2 100644
--- a/clang/lib/AST/TextNodeDumper.cpp
+++ b/clang/lib/AST/TextNodeDumper.cpp
@@ -1394,8 +1394,16 @@ void TextNodeDumper::VisitTemplateExpansionTemplateArgument(
 void TextNodeDumper::VisitExpressionTemplateArgument(
     const TemplateArgument &TA) {
   OS << " expr";
-  if (TA.isCanonicalExpr())
-    OS << " canonical";
+  if (auto Kind = TA.getExprCanonKind()) {
+    switch (*Kind) {
+    case CanonicalizationKind::Structural:
+      OS << " canonical";
+      break;
+    case CanonicalizationKind::Functional:
+      OS << " canonical-functional";
+      break;
+    }
+  }
   dumpTemplateArgument(TA);
 }
 
@@ -2151,6 +2159,19 @@ void TextNodeDumper::VisitFunctionProtoType(const FunctionProtoType *T) {
   VisitFunctionType(T);
 }
 
+void TextNodeDumper::VisitDecltypeType(const DecltypeType *T) {
+  if (auto K = T->getExprCanonicalizationKind()) {
+    switch (*K) {
+    case CanonicalizationKind::Structural:
+      OS << " equiv_expr";
+      break;
+    case CanonicalizationKind::Functional:
+      OS << " fequiv_expr";
+      break;
+    }
+  }
+}
+
 void TextNodeDumper::VisitUnresolvedUsingType(const UnresolvedUsingType *T) {
   if (ElaboratedTypeKeyword K = T->getKeyword();
       K != ElaboratedTypeKeyword::None)
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 1cc318697d936..fdb84d1e0be18 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -275,7 +275,7 @@ void ConstantArrayType::Profile(llvm::FoldingSetNodeID &ID,
   ID.AddInteger(TypeQuals);
   ID.AddBoolean(SizeExpr != nullptr);
   if (SizeExpr)
-    SizeExpr->Profile(ID, Context, true);
+    SizeExpr->Profile(ID, Context, CanonicalizationKind::Structural);
 }
 
 QualType ArrayParameterType::getConstantArrayType(const ASTContext &Ctx) const {
@@ -297,7 +297,7 @@ void DependentSizedArrayType::Profile(llvm::FoldingSetNodeID &ID,
   ID.AddInteger(llvm::to_underlying(SizeMod));
   ID.AddInteger(TypeQuals);
   if (E)
-    E->Profile(ID, Context, true);
+    E->Profile(ID, Context, CanonicalizationKind::Structural);
 }
 
 DependentVectorType::DependentVectorType(QualType ElementType,
@@ -318,7 +318,7 @@ void DependentVectorType::Profile(llvm::FoldingSetNodeID &ID,
                                   VectorKind VecKind) {
   ID.AddPointer(ElementType.getAsOpaquePtr());
   ID.AddInteger(llvm::to_underlying(VecKind));
-  SizeExpr->Profile(ID, Context, true);
+  SizeExpr->Profile(ID, Context, CanonicalizationKind::Structural);
 }
 
 DependentSizedExtVectorType::DependentSizedExtVectorType(QualType ElementType,
@@ -337,7 +337,7 @@ void DependentSizedExtVectorType::Profile(llvm::FoldingSetNodeID &ID,
                                           QualType ElementType,
                                           Expr *SizeExpr) {
   ID.AddPointer(ElementType.getAsOpaquePtr());
-  SizeExpr->Profile(ID, Context, true);
+  SizeExpr->Profile(ID, Context, CanonicalizationKind::Structural);
 }
 
 DependentAddressSpaceType::DependentAddressSpaceType(QualType PointeeType,
@@ -356,7 +356,7 @@ void DependentAddressSpaceType::Profile(llvm::FoldingSetNodeID &ID,
                                         QualType PointeeType,
                                         Expr *AddrSpaceExpr) {
   ID.AddPointer(PointeeType.getAsOpaquePtr());
-  AddrSpaceExpr->Profile(ID, Context, true);
+  AddrSpaceExpr->Profile(ID, Context, CanonicalizationKind::Structural);
 }
 
 MatrixType::MatrixType(TypeClass tc, QualType matrixType, QualType canonType,
@@ -402,8 +402,8 @@ void DependentSizedMatrixType::Profile(llvm::FoldingSetNodeID &ID,
                                        QualType ElementType, Expr *RowExpr,
                                        Expr *ColumnExpr) {
   ID.AddPointer(ElementType.getAsOpaquePtr());
-  RowExpr->Profile(ID, CTX, true);
-  ColumnExpr->Profile(ID, CTX, true);
+  RowExpr->Profile(ID, CTX, CanonicalizationKind::Structural);
+  ColumnExpr->Profile(ID, CTX, CanonicalizationKind::Structural);
 }
 
 VectorType::VectorType(QualType vecType, unsigned nElements, QualType canonType,
@@ -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();
@@ -444,7 +451,7 @@ void DependentBitIntType::Profile(llvm::FoldingSetNodeID &ID,
                                   const ASTContext &Context, bool IsUnsigned,
                                   Expr *NumBitsExpr) {
   ID.AddBoolean(IsUnsigned);
-  NumBitsExpr->Profile(ID, Context, true);
+  NumBitsExpr->Profile(ID, Context, CanonicalizationKind::Structural);
 }
 
 bool BoundsAttributedType::referencesFieldDecls() const {
@@ -4000,7 +4007,10 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result,
     for (QualType Ex : epi.ExceptionSpec.Exceptions)
       ID.AddPointer(Ex.getAsOpaquePtr());
   } else if (isComputedNoexcept(epi.ExceptionSpec.Type)) {
-    epi.ExceptionSpec.NoexceptExpr->Profile(ID, Context, Canonical);
+    epi.ExceptionSpec.NoexceptExpr->Profile(
+        ID, Context,
+        Canonical ? CanonicalizationKindOrNone(CanonicalizationKind::Structural)
+                  : std::nullopt);
   } else if (epi.ExceptionSpec.Type == EST_Uninstantiated ||
              epi.ExceptionSpec.Type == EST_Unevaluated) {
     ID.AddPointer(epi.ExceptionSpec.SourceDecl->getCanonicalDecl());
@@ -4104,13 +4114,10 @@ TypedefType::TypedefType(TypeClass TC, ElaboratedTypeKeyword Keyword,
                          NestedNameSpecifier Qualifier,
                          const TypedefNameDecl *D, QualType UnderlyingType,
                          bool HasTypeDifferentFromDecl)
-    : TypeWithKeyword(
-          Keyword, TC, UnderlyingType.getCanonicalType(),
-          toSemanticDependence(UnderlyingType->getDependence()) |
-              (Qualifier
-                   ? toTypeDependence(Qualifier.getDependence() &
-                                      ~NestedNameSpecifierDependence::Dependent)
-                   : TypeDependence{})),
+    : TypeWithKeyword(Keyword, TC, UnderlyingType.getCanonicalType(),
+                      toSemanticDependence(UnderlyingType->getDependence()) |
+                          toSyntacticDependence(
+                              toTypeDependence(Qualifier.getDependence()))),
       Decl(const_cast<TypedefNameDecl *>(D)) {
   if ((TypedefBits.hasQualifier = !!Qualifier))
     *getTrailingObjects<NestedNameSpecifier>() = Qualifier;
@@ -4127,13 +4134,10 @@ UnresolvedUsingType::UnresolvedUsingType(ElaboratedTypeKeyword Keyword,
                                          NestedNameSpecifier Qualifier,
                                          const UnresolvedUsingTypenameDecl *D,
                                          const Type *CanonicalType)
-    : TypeWithKeyword(
-          Keyword, UnresolvedUsing, QualType(CanonicalType, 0),
-          TypeDependence::DependentInstantiation |
-              (Qualifier
-                   ? toTypeDependence(Qualifier.getDependence() &
-                                      ~NestedNameSpecifierDependence::Dependent)
-                   : TypeDependence{})),
+    : TypeWithKeyword(Keyword, UnresolvedUsing, QualType(CanonicalType, 0),
+                      TypeDependence::DependentInstantiation |
+                          toSyntacticDependence(
+                              toTypeDependence(Qualifier.getDependence()))),
       Decl(const_cast<UnresolvedUsingTypenameDecl *>(D)) {
   if ((UnresolvedUsingBits.hasQualifier = !!Qualifier))
     *getTrailingObjects<NestedNameSpecifier>() = Qualifier;
@@ -4143,7 +4147,9 @@ UsingType::UsingType(ElaboratedTypeKeyword Keyword,
                      NestedNameSpecifier Qualifier, const UsingShadowDecl *D,
                      QualType UnderlyingType)
     : TypeWithKeyword(Keyword, Using, UnderlyingType.getCanonicalType(),
-                      toSemanticDependence(UnderlyingType->getDependence())),
+                      toSemanticDependence(UnderlyingType->getDependence()) |
+                          toSyntacticDependence(
+                              toTypeDependence(Qualifier.getDependence()))),
       D(const_cast<UsingShadowDecl *>(D)), UnderlyingType(UnderlyingType) {
   if ((UsingBits.hasQualifier = !!Qualifier))
     *getTrailingObjects() = Qualifier;
@@ -4193,7 +4199,7 @@ QualType TypeOfExprType::desugar() const {
 void DependentTypeOfExprType::Profile(llvm::FoldingSetNodeID &ID,
                                       const ASTContext &Context, Expr *E,
                                       bool IsUnqual) {
-  E->Profile(ID, Context, true);
+  E->Profile(ID, Context, CanonicalizationKind::Structural);
   ID.AddBoolean(IsUnqual);
 }
 
@@ -4215,19 +4221,17 @@ QualType TypeOfType::desugar() const {
              : QT;
 }
 
-DecltypeType::DecltypeType(Expr *E, QualType underlyingType, QualType can)
+DecltypeType::DecltypeType(Expr *E, CanonicalizationKindOrNone ExprCanonKind,
+                           QualType UnderlyingType, QualType CanonType)
     // 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, CanonType, toTypeDependence(E->getDependence())), E(E),
+      UnderlyingType(UnderlyingType) {
+  DecltypeTypeBits.ExprCanonKind = ExprCanonKind.toInternalRepresentation();
+}
 
-bool DecltypeType::isSugared() const { return !E->isInstantiationDependent(); }
+bool DecltypeType::isSugared() const { return !E->isTypeDependent(); }
 
 QualType DecltypeType::desugar() const {
   if (isSugared())
@@ -4236,12 +4240,13 @@ QualType DecltypeType::desugar() const {
   return QualType(this, 0);
 }
 
-DependentDecltypeType::DependentDecltypeType(Expr *E)
-    : DecltypeType(E, QualType()) {}
-
-void DependentDecltypeType::Profile(llvm::FoldingSetNodeID &ID,
-                                    const ASTContext &Context, Expr *E) {
-  E->Profile(ID, Context, true);
+void DecltypeType::Profile(llvm::FoldingSetNodeID &ID,
+                           const ASTContext &Context, Expr *E,
+                           CanonicalizationKindOrNone ExprCanonKind,
+                           QualType UnderlyingType) {
+  ID.AddInteger(ExprCanonKind.toInternalRepresentation());
+  E->Profile(ID, Context, ExprCanonKind);
+  UnderlyingType.Profile(ID);
 }
 
 PackIndexingType::PackIndexingType(QualType Canonical, QualType Pattern,
@@ -4303,7 +4308,7 @@ void PackIndexingType::Profile(llvm::FoldingSetNodeID &ID,
                                Expr *E, bool FullySubstituted,
                                ArrayRef<QualType> Expansions) {
 
-  E->Profile(ID, Context, true);
+  E->Profile(ID, Context, CanonicalizationKind::Structural);
   ID.AddBoolean(FullySubstituted);
   if (!Expansions.empty()) {
     ID.AddInteger(Expansions.size());
@@ -4654,29 +4659,25 @@ bool TemplateSpecializationType::anyInstantiationDependentTemplateArguments(
   return false;
 }
 
-static TypeDependence
-getTemplateSpecializationTypeDependence(QualType Underlying, TemplateName T) {
-  TypeDependence D = Underlying.isNull()
-                         ? TypeDependence::DependentInstantiation
-                         : toSemanticDependence(Underlying->getDependence());
-  D |= toTypeDependence(T.getDependence()) & TypeDependence::UnexpandedPack;
-  if (isPackProducingBuiltinTemplateName(T)) {
-    if (Underlying.isNull()) // Dependent, will produce a pack on substitution.
-      D |= TypeDependence::UnexpandedPack;
-    else
-      D |= (Underlying->getDependence() & TypeDependence::UnexpandedPack);
-  }
-  return D;
-}
-
 TemplateSpecializationType::TemplateSpecializationType(
     ElaboratedTypeKeyword Keyword, TemplateName T, bool IsAlias,
     ArrayRef<TemplateArgument> Args, QualType Underlying)
-    : TypeWithKeyword(Keyword, TemplateSpecialization,
-                      Underlying.isNull() ? QualType(this, 0)
-                                          : Underlying.getCanonicalType(),
-                      getTemplateSpecializationTypeDependence(Underlying, T)),
+    : TypeWithKeyword(
+          Keyword, TemplateSpecialization,
+          Underlying.isNull() ? QualType(this, 0)
+                              : Underlying.getCanonicalType(),
+          toSyntacticDependence(toTypeDependence(T.getDependence()))),
       Template(T) {
+  addDependence(Underlying.isNull()
+                    ? TypeDependence::DependentInstantiation
+                    : toSemanticDependence(Underlying->getDependence()));
+
+  // FIXME: Ugly hack, pack producing templates break the syntactic/semantic
+  // dependence distinction.
+  if ((Underlying.isNull() || Underlying->containsUnexpandedParameterPack()) &&
+      isPackProducingBuiltinTemplateName(T))
+    addDependence(TypeDependence::UnexpandedPack);
+
   TemplateSpecializationTypeBits.NumArgs = Args.size();
   TemplateSpecializationTypeBits.TypeAlias = IsAlias;
 
@@ -4711,23 +4712,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/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 80f5b90ba35c4..8c22478b6d847 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -1339,7 +1339,8 @@ void TypePrinter::printDecltypeBefore(const DecltypeType *T, raw_ostream &OS) {
   OS << "decltype(";
   if (const Expr *E = T->getUnderlyingExpr()) {
     PrintingPolicy ExprPolicy = Policy;
-    ExprPolicy.PrintAsCanonical = T->isCanonicalUnqualified();
+    // FIXME: Support printing as functionally equivalent canonical type.
+    ExprPolicy.PrintAsCanonical = bool(T->getExprCanonicalizationKind());
     E->printPretty(OS, nullptr, ExprPolicy);
   }
   OS << ')';
diff --git a/clang/lib/Frontend/Rewrite/RewriteModernObjC.cpp b/clang/lib/Frontend/Rewrite/RewriteModernObjC.cpp
index dee29fe004f42..9e88364f9a067 100644
--- a/clang/lib/Frontend/Rewrite/RewriteModernObjC.cpp
+++ b/clang/lib/Frontend/Rewrite/RewriteModernObjC.cpp
@@ -883,7 +883,8 @@ RewriteModernObjC::getIvarAccessString(ObjCIvarDecl *D) {
                                         ICIS_NoInit);
       MemberExpr *ME = MemberExpr::CreateImplicit(
           *Context, PE, true, FD, FD->getType(), VK_LValue, OK_Ordinary);
-      IvarT = Context->getDecltypeType(ME, ME->getType());
+      IvarT = Context->getDecltypeType(ME, /*ExprCanonKind=*/std::nullopt,
+                                       ME->getType());
     }
   }
   convertObjCTypeToCStyleType(IvarT);
@@ -7484,7 +7485,8 @@ Stmt *RewriteModernObjC::RewriteObjCIvarRefExpr(ObjCIvarRefExpr *IV) {
                                             /*Mutable=*/true, ICIS_NoInit);
           MemberExpr *ME = MemberExpr::CreateImplicit(
               *Context, PE, true, FD, FD->getType(), VK_LValue, OK_Ordinary);
-          IvarT = Context->getDecltypeType(ME, ME->getType());
+          IvarT = Context->getDecltypeType(ME, /*ExprCanonKind=*/std::nullopt,
+                                           ME->getType());
         }
       }
       convertObjCTypeToCStyleType(IvarT);
diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index 235ede8eb0bf0..4abab547d667c 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -308,16 +308,11 @@ addVectorTexturePartialSpecialization(Sema &S, NamespaceDecl *HLSLNamespace,
       SourceLocation());
 
   // Create the partial specialization declaration.
-  QualType CanonInjectedTST =
-      AST.getCanonicalType(AST.getTemplateSpecializationType(
-          ElaboratedTypeKeyword::Class, TemplateName(TextureTemplate),
-          {TemplateArgument(VectorType)}, {}));
-
   auto *PartialSpec = ClassTemplatePartialSpecializationDecl::Create(
       AST, TagDecl::TagKind::Class, HLSLNamespace, SourceLocation(),
       SourceLocation(), TemplateParams, TextureTemplate,
       {TemplateArgument(VectorType)},
-      CanQualType::CreateUnsafe(CanonInjectedTST), nullptr);
+      /*PrevDecl=*/nullptr);
 
   // Set the template arguments as written.
   TemplateArgument Arg(VectorType);
diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp
index 255c22d9c2a31..333405430a1d9 100644
--- a/clang/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp
@@ -61,18 +61,22 @@ DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS,
     if (EnteringContext) {
       if (NNS.getKind() != NestedNameSpecifier::Kind::Type)
         return nullptr;
-      const Type *NNSType = NNS.getAsType();
+
+      bool AnyNonCanonical = false;
+      QualType NNSType = Context.getCanonicalType(
+          QualType(NNS.getAsType(), 0), CanonicalizationKind::Functional,
+          AnyNonCanonical);
 
       // Look through type alias templates, per C++0x [temp.dep.type]p1.
-      NNSType = Context.getCanonicalType(NNSType);
       if (const auto *SpecType =
-              dyn_cast<TemplateSpecializationType>(NNSType)) {
+              NNSType->getAsNonAliasTemplateSpecializationType()) {
         // We are entering the context of the nested name specifier, so try to
         // match the nested name specifier to either a primary class template
         // or a class template partial specialization.
         if (ClassTemplateDecl *ClassTemplate =
                 dyn_cast_or_null<ClassTemplateDecl>(
-                    SpecType->getTemplateName().getAsTemplateDecl())) {
+                    SpecType->getTemplateName().getAsTemplateDecl(
+                        /*IgnoreDeduced=*/true))) {
           // FIXME: The fallback on the search of partial
           // specialization using ContextType should be eventually removed since
           // it doesn't handle the case of constrained template parameters
@@ -88,9 +92,33 @@ DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS,
                                return TPL->getDepth() == Depth;
                              });
             if (L != TemplateParamLists.end()) {
+              SmallVector<TemplateArgument, 8> SpecArgs;
+              if (AnyNonCanonical) {
+                // FIXME: Horrid hack to get back a converted template argument
+                // list.
+                TemplateArgumentListInfo TemplateArgs;
+                for (const auto &Arg : SpecType->template_arguments())
+                  TemplateArgs.addArgument(getTrivialTemplateArgumentLoc(
+                      Arg, /*NTTPType=*/QualType(), SourceLocation()));
+                CheckTemplateArgumentInfo CTAI;
+                DefaultArguments DefaultArgs;
+                SFINAETrap Trap(*this);
+                [[maybe_unused]] bool Res = CheckTemplateArgumentList(
+                    ClassTemplate, ClassTemplate->getTemplateParameters(),
+                    SourceLocation(), TemplateArgs, DefaultArgs,
+                    /*PartialTemplateArgs=*/false, CTAI);
+                assert(
+                    !Res && !Trap.hasErrorOccurred() &&
+                    "template argument list should have been checked already");
+                SpecArgs = std::move(CTAI.SugaredConverted);
+                Context.canonicalizeTemplateArguments(
+                    SpecArgs, CanonicalizationKind::Functional);
+              }
+
               void *Pos = nullptr;
               PartialSpec = ClassTemplate->findPartialSpecialization(
-                  SpecType->template_arguments(), *L, Pos);
+                  AnyNonCanonical ? SpecArgs : SpecType->template_arguments(),
+                  *L, Pos);
             }
           } else {
             PartialSpec =
@@ -119,9 +147,9 @@ DeclContext *Sema::computeDeclContext(const CXXScopeSpec &SS,
           if (Context.hasSameType(Injected, QualType(SpecType, 0)))
             return ClassTemplate->getTemplatedDecl();
         }
-      } else if (const auto *RecordT = dyn_cast<RecordType>(NNSType)) {
+      } else if (auto *RD = NNSType->getAsCXXRecordDecl()) {
         // The nested name specifier refers to a member of a class template.
-        return RecordT->getDecl()->getDefinitionOrSelf();
+        return RD;
       }
     }
 
@@ -853,6 +881,7 @@ bool Sema::ActOnCXXNestedNameSpecifierDecltype(CXXScopeSpec &SS,
 
   TypeLocBuilder TLB;
   DecltypeTypeLoc DecltypeTL = TLB.push<DecltypeTypeLoc>(T);
+  DecltypeTL.setUnderlyingExpr(DS.getRepAsExpr());
   DecltypeTL.setDecltypeLoc(DS.getTypeSpecTypeLoc());
   DecltypeTL.setRParenLoc(DS.getTypeofParensRange().getEnd());
   SS.Make(Context, TLB.getTypeLocInContext(Context, T), ColonColonLoc);
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index d53c3b6ab2674..94e46543dd88e 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -10878,10 +10878,10 @@ bool Sema::CheckSizeofMemaccessArgument(const Expr *LenExpr, const Expr *Dest,
   QualType PointeeTy = DestPtrTy->getPointeeType();
 
   if (SizeOfArgID == llvm::FoldingSetNodeID())
-    SizeOfArg->Profile(SizeOfArgID, Context, true);
+    SizeOfArg->Profile(SizeOfArgID, Context, CanonicalizationKind::Structural);
 
   llvm::FoldingSetNodeID DestID;
-  Dest->Profile(DestID, Context, true);
+  Dest->Profile(DestID, Context, CanonicalizationKind::Structural);
   if (DestID == SizeOfArgID) {
     // TODO: For strncpy() and friends, this could suggest sizeof(dst)
     //       over sizeof(src) as well.
diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp
index 3f04922a5647e..72e05e6f05f07 100644
--- a/clang/lib/Sema/SemaConcept.cpp
+++ b/clang/lib/Sema/SemaConcept.cpp
@@ -182,7 +182,7 @@ struct SatisfactionStackRAII {
 static bool DiagRecursiveConstraintEval(
     Sema &S, llvm::FoldingSetNodeID &ID, const NamedDecl *Templ, const Expr *E,
     const MultiLevelTemplateArgumentList *MLTAL = nullptr) {
-  E->Profile(ID, S.Context, /*Canonical=*/true);
+  E->Profile(ID, S.Context, CanonicalizationKind::Structural);
   if (MLTAL) {
     for (const auto &List : *MLTAL)
       for (const auto &TemplateArg : List.Args)
@@ -1627,9 +1627,11 @@ bool Sema::AreConstraintExpressionsEqual(const NamedDecl *Old,
       return false;
   }
 
+  // FIXME: This should use Functional Equivalence comparison.
+  // FIXME: Use Context.hasSameExpr instead.
   llvm::FoldingSetNodeID ID1, ID2;
-  OldConstr->Profile(ID1, Context, /*Canonical=*/true);
-  NewConstr->Profile(ID2, Context, /*Canonical=*/true);
+  OldConstr->Profile(ID1, Context, CanonicalizationKind::Structural);
+  NewConstr->Profile(ID2, Context, CanonicalizationKind::Structural);
   return ID1 == ID2;
 }
 
@@ -2621,9 +2623,11 @@ bool Sema::MaybeEmitAmbiguousAtomicConstraintsDiagnostic(
 
     // Not the same source level expression - are the expressions
     // identical?
+    // FIXME: This should use Functional Equivalence comparison.
+    // FIXME: Use Context.hasSameExpr instead.
     llvm::FoldingSetNodeID IDA, IDB;
-    EA->Profile(IDA, Context, /*Canonical=*/true);
-    EB->Profile(IDB, Context, /*Canonical=*/true);
+    EA->Profile(IDA, Context, CanonicalizationKind::Structural);
+    EB->Profile(IDB, Context, CanonicalizationKind::Structural);
     if (IDA != IDB)
       return false;
 
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 9f8fc5a187b0a..1c6f34cb58b47 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -2549,10 +2549,9 @@ bool Sema::isIncompatibleTypedef(const TypeDecl *Old, TypedefNameDecl *New) {
     return true;
   }
 
-  if (OldType != NewType &&
-      !OldType->isDependentType() &&
+  if (OldType != NewType && !OldType->isDependentType() &&
       !NewType->isDependentType() &&
-      !Context.hasSameType(OldType, NewType)) {
+      !Context.hasFunctionallyEquivalentType(OldType, NewType)) {
     int Kind = isa<TypeAliasDecl>(Old) ? 1 : 0;
     Diag(New->getLocation(), diag::err_redefinition_different_typedef)
       << Kind << NewType << OldType;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 6647e52535114..24a2728e22154 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -13903,8 +13903,8 @@ Decl *Sema::ActOnAliasDeclaration(Scope *S, AccessSpecifier AS,
 
         TypeAliasDecl *OldTD = OldDecl->getTemplatedDecl();
         if (!Invalid &&
-            !Context.hasSameType(OldTD->getUnderlyingType(),
-                                 NewTD->getUnderlyingType())) {
+            !Context.hasFunctionallyEquivalentType(
+                OldTD->getUnderlyingType(), NewTD->getUnderlyingType())) {
           // FIXME: The C++0x standard does not clearly say this is ill-formed,
           // but we can't reasonably accept it.
           Diag(NewTD->getLocation(), diag::err_redefinition_different_typedef)
diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp
index 40d530a1f3925..d7b3ab7c0ffad 100644
--- a/clang/lib/Sema/SemaExceptionSpec.cpp
+++ b/clang/lib/Sema/SemaExceptionSpec.cpp
@@ -580,13 +580,10 @@ static bool CheckEquivalentExceptionSpecImpl(
   // C++14 [except.spec]p3:
   //   Two exception-specifications are compatible if [...] both have the form
   //   noexcept(constant-expression) and the constant-expressions are equivalent
-  if (OldEST == EST_DependentNoexcept && NewEST == EST_DependentNoexcept) {
-    llvm::FoldingSetNodeID OldFSN, NewFSN;
-    Old->getNoexceptExpr()->Profile(OldFSN, S.Context, true);
-    New->getNoexceptExpr()->Profile(NewFSN, S.Context, true);
-    if (OldFSN == NewFSN)
+  // FIXME: This should use Functional Equivalence comparison.
+  if (OldEST == EST_DependentNoexcept && NewEST == EST_DependentNoexcept)
+    if (S.Context.hasSameExpr(Old->getNoexceptExpr(), New->getNoexceptExpr()))
       return false;
-  }
 
   // Dynamic exception specifications with the same set of adjusted types
   // are compatible.
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index cf235095d489d..32b35e823479a 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -2349,7 +2349,7 @@ Sema::ActOnStringLiteral(ArrayRef<Token> StringToks, Scope *UDLScope) {
 
   case LOLR_Template: {
     TemplateArgumentListInfo ExplicitArgs;
-    TemplateArgument Arg(Lit, /*IsCanonical=*/false);
+    TemplateArgument Arg(Lit, /*CanonKind=*/std::nullopt);
     TemplateArgumentLocInfo ArgInfo(Lit);
     ExplicitArgs.addArgument(TemplateArgumentLoc(Arg, ArgInfo));
     return BuildLiteralOperatorCall(R, OpNameInfo, {}, StringTokLocs.back(),
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 39c5e3b0671bb..485d4bc1becd9 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -7415,6 +7415,7 @@ ExprResult Sema::ActOnPseudoDestructorExpr(Scope *S, Expr *Base,
   case DeclSpec::TST_decltype: {
     T = BuildDecltypeType(DS.getRepAsExpr(), /*AsUnevaluated=*/false);
     DecltypeTypeLoc DecltypeTL = TLB.push<DecltypeTypeLoc>(T);
+    DecltypeTL.setUnderlyingExpr(DS.getRepAsExpr());
     DecltypeTL.setDecltypeLoc(DS.getTypeSpecTypeLoc());
     DecltypeTL.setRParenLoc(DS.getTypeofParensRange().getEnd());
     break;
diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp
index a4504410cae28..1a49b88162928 100644
--- a/clang/lib/Sema/SemaExprMember.cpp
+++ b/clang/lib/Sema/SemaExprMember.cpp
@@ -1888,8 +1888,6 @@ Sema::BuildImplicitMemberExpr(const CXXScopeSpec &SS,
                               bool IsKnownInstance, const Scope *S) {
   assert(!R.empty() && !R.isAmbiguous());
 
-  SourceLocation loc = R.getNameLoc();
-
   // If this is known to be an instance access, go ahead and build an
   // implicit 'this' expression now.
   QualType ThisTy = getCurrentThisType();
@@ -1900,7 +1898,7 @@ Sema::BuildImplicitMemberExpr(const CXXScopeSpec &SS,
     SourceLocation Loc = R.getNameLoc();
     if (SS.getRange().isValid())
       Loc = SS.getRange().getBegin();
-    baseExpr = BuildCXXThisExpr(loc, ThisTy, /*IsImplicit=*/true);
+    baseExpr = BuildCXXThisExpr(Loc, ThisTy, /*IsImplicit=*/true);
   }
 
   return BuildMemberReferenceExpr(
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index b96065f8619d2..994d5812b80af 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -3751,7 +3751,8 @@ Sema::LookupLiteralOperator(Scope *S, LookupResult &R,
           SFINAETrap Trap(*this);
           CheckTemplateArgumentInfo CTAI;
           TemplateArgumentLoc Arg(
-              TemplateArgument(StringLit, /*IsCanonical=*/false), StringLit);
+              TemplateArgument(StringLit, /*CanonKind=*/std::nullopt),
+              StringLit);
           if (CheckTemplateArgument(
                   Params->getParam(0), Arg, FD, R.getNameLoc(), R.getNameLoc(),
                   /*ArgumentPackIndex=*/0, CTAI, CTAK_Specified) ||
diff --git a/clang/lib/Sema/SemaOpenACCAtomic.cpp b/clang/lib/Sema/SemaOpenACCAtomic.cpp
index ad21129d30c15..f381b96d1678d 100644
--- a/clang/lib/Sema/SemaOpenACCAtomic.cpp
+++ b/clang/lib/Sema/SemaOpenACCAtomic.cpp
@@ -424,9 +424,9 @@ class AtomicOperandChecker {
 
     llvm::FoldingSetNodeID LHS_ID, InnerLHS_ID, InnerRHS_ID;
     AssignInf.LHS->Profile(LHS_ID, SemaRef.getASTContext(),
-                           /*Canonical=*/true);
+                           CanonicalizationKind::Structural);
     BinInf->LHS->Profile(InnerLHS_ID, SemaRef.getASTContext(),
-                         /*Canonical=*/true);
+                         CanonicalizationKind::Structural);
 
     // This is X = X binop expr;
     // Check the RHS is an expression.
@@ -440,7 +440,7 @@ class AtomicOperandChecker {
           IDACInfo::AssignBinOp, AssignInf.LHS};
 
     BinInf->RHS->Profile(InnerRHS_ID, SemaRef.getASTContext(),
-                         /*Canonical=*/true);
+                         CanonicalizationKind::Structural);
     // This is X = expr binop X;
     // Check the LHS is an expression
     if (LHS_ID == InnerRHS_ID)
@@ -581,11 +581,7 @@ class AtomicOperandChecker {
 
   bool CheckVarRefsSame(IDACInfo::ExprKindTy FirstKind, const Expr *FirstX,
                         IDACInfo::ExprKindTy SecondKind, const Expr *SecondX) {
-    llvm::FoldingSetNodeID First_ID, Second_ID;
-    FirstX->Profile(First_ID, SemaRef.getASTContext(), /*Canonical=*/true);
-    SecondX->Profile(Second_ID, SemaRef.getASTContext(), /*Canonical=*/true);
-
-    if (First_ID == Second_ID)
+    if (SemaRef.getASTContext().hasSameExpr(FirstX, SecondX))
       return false;
 
     PartialDiagnostic PD =
diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp
index ec9b2e68ef6cf..304669d978d25 100644
--- a/clang/lib/Sema/SemaOpenMP.cpp
+++ b/clang/lib/Sema/SemaOpenMP.cpp
@@ -3513,13 +3513,14 @@ getAllocatorKind(Sema &S, DSAStackTy *Stack, Expr *Allocator) {
   auto AllocatorKindRes = OMPAllocateDeclAttr::OMPUserDefinedMemAlloc;
   llvm::FoldingSetNodeID AEId;
   const Expr *AE = Allocator->IgnoreParenImpCasts();
-  AE->IgnoreImpCasts()->Profile(AEId, S.getASTContext(), /*Canonical=*/true);
+  AE->IgnoreImpCasts()->Profile(AEId, S.getASTContext(),
+                                CanonicalizationKind::Structural);
   for (int I = 0; I < OMPAllocateDeclAttr::OMPUserDefinedMemAlloc; ++I) {
     auto AllocatorKind = static_cast<OMPAllocateDeclAttr::AllocatorTypeTy>(I);
     const Expr *DefAllocator = Stack->getAllocator(AllocatorKind);
     llvm::FoldingSetNodeID DAEId;
     DefAllocator->IgnoreImpCasts()->Profile(DAEId, S.getASTContext(),
-                                            /*Canonical=*/true);
+                                            CanonicalizationKind::Structural);
     if (AEId == DAEId) {
       AllocatorKindRes = AllocatorKind;
       break;
@@ -3541,12 +3542,8 @@ static bool checkPreviousOMPAllocateAttribute(
   if (AllocatorsMatch &&
       AllocatorKind == OMPAllocateDeclAttr::OMPUserDefinedMemAlloc &&
       Allocator && PrevAllocator) {
-    const Expr *AE = Allocator->IgnoreParenImpCasts();
-    const Expr *PAE = PrevAllocator->IgnoreParenImpCasts();
-    llvm::FoldingSetNodeID AEId, PAEId;
-    AE->Profile(AEId, S.Context, /*Canonical=*/true);
-    PAE->Profile(PAEId, S.Context, /*Canonical=*/true);
-    AllocatorsMatch = AEId == PAEId;
+    AllocatorsMatch = S.Context.hasSameExpr(
+        Allocator->IgnoreParenImpCasts(), PrevAllocator->IgnoreParenImpCasts());
   }
   if (!AllocatorsMatch) {
     SmallString<256> AllocatorBuffer;
@@ -11789,11 +11786,11 @@ bool OpenMPAtomicUpdateChecker::checkBinaryOperation(
         Expr *RHS = AtomicInnerBinOp->getRHS();
         llvm::FoldingSetNodeID XId, LHSId, RHSId;
         X->IgnoreParenImpCasts()->Profile(XId, SemaRef.getASTContext(),
-                                          /*Canonical=*/true);
+                                          CanonicalizationKind::Structural);
         LHS->IgnoreParenImpCasts()->Profile(LHSId, SemaRef.getASTContext(),
-                                            /*Canonical=*/true);
+                                            CanonicalizationKind::Structural);
         RHS->IgnoreParenImpCasts()->Profile(RHSId, SemaRef.getASTContext(),
-                                            /*Canonical=*/true);
+                                            CanonicalizationKind::Structural);
         if (XId == LHSId) {
           E = RHS;
           IsXLHSInRHSPart = true;
@@ -11935,7 +11932,8 @@ bool OpenMPAtomicUpdateChecker::checkStatement(Stmt *S, unsigned DiagId,
 /// Get the node id of the fixed point of an expression \a S.
 llvm::FoldingSetNodeID getNodeId(ASTContext &Context, const Expr *S) {
   llvm::FoldingSetNodeID Id;
-  S->IgnoreParenImpCasts()->Profile(Id, Context, true);
+  S->IgnoreParenImpCasts()->Profile(Id, Context,
+                                    CanonicalizationKind::Structural);
   return Id;
 }
 
@@ -13162,8 +13160,10 @@ StmtResult SemaOpenMP::ActOnOpenMPAtomicDirective(ArrayRef<OMPClause *> Clauses,
             // Check that the first expression has form v = x.
             Expr *PossibleX = BinOp->getRHS()->IgnoreParenImpCasts();
             llvm::FoldingSetNodeID XId, PossibleXId;
-            Checker.getX()->Profile(XId, Context, /*Canonical=*/true);
-            PossibleX->Profile(PossibleXId, Context, /*Canonical=*/true);
+            Checker.getX()->Profile(XId, Context,
+                                    CanonicalizationKind::Structural);
+            PossibleX->Profile(PossibleXId, Context,
+                               CanonicalizationKind::Structural);
             IsUpdateExprFound = XId == PossibleXId;
             if (IsUpdateExprFound) {
               V = BinOp->getLHS();
@@ -13193,8 +13193,10 @@ StmtResult SemaOpenMP::ActOnOpenMPAtomicDirective(ArrayRef<OMPClause *> Clauses,
               // Check that the second expression has form v = x.
               Expr *PossibleX = BinOp->getRHS()->IgnoreParenImpCasts();
               llvm::FoldingSetNodeID XId, PossibleXId;
-              Checker.getX()->Profile(XId, Context, /*Canonical=*/true);
-              PossibleX->Profile(PossibleXId, Context, /*Canonical=*/true);
+              Checker.getX()->Profile(XId, Context,
+                                      CanonicalizationKind::Structural);
+              PossibleX->Profile(PossibleXId, Context,
+                                 CanonicalizationKind::Structural);
               IsUpdateExprFound = XId == PossibleXId;
               if (IsUpdateExprFound) {
                 V = BinOp->getLHS();
@@ -13237,10 +13239,10 @@ StmtResult SemaOpenMP::ActOnOpenMPAtomicDirective(ArrayRef<OMPClause *> Clauses,
                   Expr *PossibleXLHSInSecond =
                       SecondBinOp->getLHS()->IgnoreParenImpCasts();
                   llvm::FoldingSetNodeID X1Id, X2Id;
-                  PossibleXRHSInFirst->Profile(X1Id, Context,
-                                               /*Canonical=*/true);
-                  PossibleXLHSInSecond->Profile(X2Id, Context,
-                                                /*Canonical=*/true);
+                  PossibleXRHSInFirst->Profile(
+                      X1Id, Context, CanonicalizationKind::Structural);
+                  PossibleXLHSInSecond->Profile(
+                      X2Id, Context, CanonicalizationKind::Structural);
                   IsUpdateExprFound = X1Id == X2Id;
                   if (IsUpdateExprFound) {
                     V = FirstBinOp->getLHS();
@@ -21154,13 +21156,9 @@ static bool actOnOMPReductionKindClause(
           (DeclareReductionRef.isUsable() && IsParentBOK) ||
           (IsParentBOK && BOK != ParentBOK) || IsParentReductionOp) {
         bool EmitError = true;
-        if (IsParentReductionOp && DeclareReductionRef.isUsable()) {
-          llvm::FoldingSetNodeID RedId, ParentRedId;
-          ParentReductionOp->Profile(ParentRedId, Context, /*Canonical=*/true);
-          DeclareReductionRef.get()->Profile(RedId, Context,
-                                             /*Canonical=*/true);
-          EmitError = RedId != ParentRedId;
-        }
+        if (IsParentReductionOp && DeclareReductionRef.isUsable())
+          EmitError = !Context.hasSameExpr(ParentReductionOp,
+                                           DeclareReductionRef.get());
         if (EmitError) {
           S.Diag(ReductionId.getBeginLoc(),
                  diag::err_omp_reduction_identifier_mismatch)
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 11e771bc240f1..52e7f3cd94e00 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -1361,8 +1361,10 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
     return true;
 
   // Is the function New an overload of the function Old?
-  QualType OldQType = SemaRef.Context.getCanonicalType(Old->getType());
-  QualType NewQType = SemaRef.Context.getCanonicalType(New->getType());
+  QualType OldQType = SemaRef.Context.getCanonicalType(
+      Old->getType(), CanonicalizationKind::Functional);
+  QualType NewQType = SemaRef.Context.getCanonicalType(
+      New->getType(), CanonicalizationKind::Functional);
 
   // Compare the signatures (C++ 1.3.10) of the two functions to
   // determine whether they are overloads. If we find any mismatch
@@ -1409,7 +1411,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
@@ -1605,10 +1607,7 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
        NewI != NewE || OldI != OldE; ++NewI, ++OldI) {
     if (NewI == NewE || OldI == OldE)
       return true;
-    llvm::FoldingSetNodeID NewID, OldID;
-    NewI->getCond()->Profile(NewID, SemaRef.Context, true);
-    OldI->getCond()->Profile(OldID, SemaRef.Context, true);
-    if (NewID != OldID)
+    if (!SemaRef.Context.hasSameExpr(NewI->getCond(), OldI->getCond()))
       return true;
   }
 
@@ -3508,7 +3507,7 @@ void Sema::HandleFunctionTypeMismatch(PartialDiagnostic &PDiag,
   }
 
   // No extra info for same types.
-  if (Context.hasSameType(FromType, ToType)) {
+  if (Context.hasFunctionallyEquivalentType(FromType, ToType)) {
     PDiag << ft_default;
     return;
   }
@@ -3538,8 +3537,8 @@ void Sema::HandleFunctionTypeMismatch(PartialDiagnostic &PDiag,
   }
 
   // Handle different return type.
-  if (!Context.hasSameType(FromFunction->getReturnType(),
-                           ToFunction->getReturnType())) {
+  if (!Context.hasFunctionallyEquivalentType(FromFunction->getReturnType(),
+                                             ToFunction->getReturnType())) {
     PDiag << ft_return_type << ToFunction->getReturnType()
           << FromFunction->getReturnType();
     return;
@@ -3583,7 +3582,10 @@ bool Sema::FunctionParamTypesAreEqual(ArrayRef<QualType> Old,
     QualType NewType =
         Context.removePtrSizeAddrSpace((New.begin() + J)->getUnqualifiedType());
 
-    if (!Context.hasSameType(OldType, NewType)) {
+    assert(Context.isFunctionallyCanonicalType(OldType));
+    assert(Context.isFunctionallyCanonicalType(NewType));
+
+    if (OldType != NewType) {
       if (ArgPos)
         *ArgPos = Idx;
       return false;
@@ -3595,6 +3597,10 @@ bool Sema::FunctionParamTypesAreEqual(ArrayRef<QualType> Old,
 bool Sema::FunctionParamTypesAreEqual(const FunctionProtoType *OldType,
                                       const FunctionProtoType *NewType,
                                       unsigned *ArgPos, bool Reversed) {
+  OldType = cast<FunctionProtoType>(Context.getCanonicalType(
+      QualType(OldType, 0), CanonicalizationKind::Functional));
+  NewType = cast<FunctionProtoType>(Context.getCanonicalType(
+      QualType(NewType, 0), CanonicalizationKind::Functional));
   return FunctionParamTypesAreEqual(OldType->param_types(),
                                     NewType->param_types(), ArgPos, Reversed);
 }
@@ -3613,8 +3619,15 @@ bool Sema::FunctionNonObjectParamTypesAreEqual(const FunctionDecl *OldFunction,
   unsigned NewIgnore =
       unsigned(NewFunction->hasCXXExplicitFunctionObjectParameter());
 
-  auto *OldPT = cast<FunctionProtoType>(OldFunction->getFunctionType());
-  auto *NewPT = cast<FunctionProtoType>(NewFunction->getFunctionType());
+  QualType OldFunctionType =
+      Context.getCanonicalType(QualType(OldFunction->getFunctionType(), 0),
+                               CanonicalizationKind::Functional);
+  QualType NewFunctionType =
+      Context.getCanonicalType(QualType(NewFunction->getFunctionType(), 0),
+                               CanonicalizationKind::Functional);
+
+  auto *OldPT = cast<FunctionProtoType>(OldFunctionType);
+  auto *NewPT = cast<FunctionProtoType>(NewFunctionType);
 
   return FunctionParamTypesAreEqual(OldPT->param_types().slice(OldIgnore),
                                     NewPT->param_types().slice(NewIgnore),
@@ -10748,8 +10761,10 @@ static Comparison compareEnableIfAttrs(const Sema &S, const FunctionDecl *Cand1,
     Cand1ID.clear();
     Cand2ID.clear();
 
-    (*Cand1A)->getCond()->Profile(Cand1ID, S.getASTContext(), true);
-    (*Cand2A)->getCond()->Profile(Cand2ID, S.getASTContext(), true);
+    (*Cand1A)->getCond()->Profile(Cand1ID, S.getASTContext(),
+                                  CanonicalizationKind::Structural);
+    (*Cand2A)->getCond()->Profile(Cand2ID, S.getASTContext(),
+                                  CanonicalizationKind::Structural);
     if (Cand1ID != Cand2ID)
       return Comparison::Worse;
   }
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 16b4815be2010..51bdb1c1900c3 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -957,7 +957,8 @@ static TemplateArgumentLoc translateTemplateArgument(Sema &SemaRef,
 
   case ParsedTemplateArgument::NonType: {
     Expr *E = Arg.getAsExpr();
-    return TemplateArgumentLoc(TemplateArgument(E, /*IsCanonical=*/false), E);
+    return TemplateArgumentLoc(TemplateArgument(E, /*CanonKind=*/std::nullopt),
+                               E);
   }
 
   case ParsedTemplateArgument::Template: {
@@ -1654,7 +1655,7 @@ NamedDecl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D,
 
     Param->setDefaultArgument(
         Context, getTrivialTemplateArgumentLoc(
-                     TemplateArgument(Default, /*IsCanonical=*/false),
+                     TemplateArgument(Default, /*CanonKind=*/std::nullopt),
                      QualType(), SourceLocation()));
   }
 
@@ -3561,7 +3562,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);
     }
@@ -4172,8 +4179,7 @@ static bool isTemplateArgumentTemplateParameter(const TemplateArgument &Arg,
 
   case TemplateArgument::Type: {
     QualType Type = Arg.getAsType();
-    const TemplateTypeParmType *TPT =
-        Arg.getAsType()->getAsCanonical<TemplateTypeParmType>();
+    const auto *TPT = dyn_cast<TemplateTypeParmType>(Type);
     return TPT && !Type.hasQualifiers() &&
            TPT->getDepth() == Depth && TPT->getIndex() == Index;
   }
@@ -4196,9 +4202,12 @@ static bool isTemplateArgumentTemplateParameter(const TemplateArgument &Arg,
   llvm_unreachable("unexpected kind of template argument");
 }
 
-static bool isSameAsPrimaryTemplate(TemplateParameterList *Params,
+static bool isSameAsPrimaryTemplate(Sema &S, TemplateParameterList *Params,
                                     TemplateParameterList *SpecParams,
                                     ArrayRef<TemplateArgument> Args) {
+  if (SpecParams->hasAssociatedConstraints())
+    return false;
+
   if (Params->size() != Args.size() || Params->size() != SpecParams->size())
     return false;
 
@@ -4225,8 +4234,20 @@ static bool isSameAsPrimaryTemplate(TemplateParameterList *Params,
     if (auto *SpecNTTP =
             dyn_cast<NonTypeTemplateParmDecl>(SpecParams->getParam(I))) {
       auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Params->getParam(I));
-      if (!NTTP || NTTP->getType().getCanonicalType() !=
-                       SpecNTTP->getType().getCanonicalType())
+      if (!NTTP || !S.Context.hasFunctionallyEquivalentType(
+                       NTTP->getType(), SpecNTTP->getType()))
+        return false;
+    }
+
+    // For TTPs, further specialization is allowed by the template template
+    // parameter's template-head.
+    if (auto *SpecTTP =
+            dyn_cast<TemplateTemplateParmDecl>(SpecParams->getParam(I))) {
+      auto *TTP = dyn_cast<TemplateTemplateParmDecl>(Params->getParam(I));
+      if (!TTP ||
+          !S.TemplateParameterListsAreEqual(
+              TTP->getTemplateParameters(), SpecTTP->getTemplateParameters(),
+              /*Complain=*/false, Sema::TPL_TemplateParamsEquivalent))
         return false;
     }
   }
@@ -4407,28 +4428,34 @@ DeclResult Sema::ActOnVarTemplateSpecialization(
                                 /*UpdateArgsWithConversions=*/true))
     return true;
 
+  // Only needed for partial specializations.
+  SmallVector<TemplateArgument, 4> FunctionallyEquivalentConverted;
+
   // Find the variable template (partial) specialization declaration that
   // corresponds to these arguments.
   if (IsPartialSpecialization) {
     if (CheckTemplatePartialSpecializationArgs(TemplateNameLoc, VarTemplate,
                                                TemplateArgs.size(),
-                                               CTAI.CanonicalConverted))
+                                               CTAI.SugaredConverted))
       return true;
 
     // FIXME: Move these checks to CheckTemplatePartialSpecializationArgs so
     // we also do them during instantiation.
     if (!Name.isDependent() &&
         !TemplateSpecializationType::anyDependentTemplateArguments(
-            TemplateArgs, CTAI.CanonicalConverted)) {
+            TemplateArgs, CTAI.SugaredConverted)) {
       Diag(TemplateNameLoc, diag::err_partial_spec_fully_specialized)
           << VarTemplate->getDeclName();
       IsPartialSpecialization = false;
     }
 
-    if (isSameAsPrimaryTemplate(VarTemplate->getTemplateParameters(),
-                                TemplateParams, CTAI.CanonicalConverted) &&
-        (!Context.getLangOpts().CPlusPlus20 ||
-         !TemplateParams->hasAssociatedConstraints())) {
+    FunctionallyEquivalentConverted = CTAI.SugaredConverted;
+    Context.canonicalizeTemplateArguments(FunctionallyEquivalentConverted,
+                                          CanonicalizationKind::Functional);
+
+    if (isSameAsPrimaryTemplate(*this, VarTemplate->getTemplateParameters(),
+                                TemplateParams,
+                                FunctionallyEquivalentConverted)) {
       // C++ [temp.class.spec]p9b3:
       //
       //   -- The argument list of the specialization shall not be identical
@@ -4448,7 +4475,7 @@ DeclResult Sema::ActOnVarTemplateSpecialization(
 
   if (IsPartialSpecialization)
     PrevDecl = VarTemplate->findPartialSpecialization(
-        CTAI.CanonicalConverted, TemplateParams, InsertPos);
+        FunctionallyEquivalentConverted, TemplateParams, InsertPos);
   else
     PrevDecl =
         VarTemplate->findSpecialization(CTAI.CanonicalConverted, InsertPos);
@@ -4478,7 +4505,7 @@ DeclResult Sema::ActOnVarTemplateSpecialization(
         VarTemplatePartialSpecializationDecl::Create(
             Context, VarTemplate->getDeclContext(), TemplateKWLoc,
             TemplateNameLoc, TemplateParams, VarTemplate, TSI->getType(), TSI,
-            SC, CTAI.CanonicalConverted);
+            SC, FunctionallyEquivalentConverted);
     Partial->setTemplateArgsAsWritten(TemplateArgs);
 
     if (!PrevPartial)
@@ -4894,10 +4921,11 @@ ExprResult Sema::CheckConceptTemplateId(
 
   DiagnoseUseOfDecl(NamedConcept, ConceptNameInfo.getLoc());
 
+  // FIXME: Is this still an issue?
   // There's a bug with CTAI.CanonicalConverted.
-  // If the template argument contains a DependentDecltypeType that includes a
+  // If the template argument contains a DecltypeType that includes a
   // TypeAliasType, and the same written type had occurred previously in the
-  // source, then the DependentDecltypeType would be canonicalized to that
+  // source, then the DecltypeType would be canonicalized to that
   // previous type which would mess up the substitution.
   // FIXME: Reland https://github.com/llvm/llvm-project/pull/101782 properly!
   auto *CSD = ImplicitConceptSpecializationDecl::Create(
@@ -5608,7 +5636,7 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, TemplateArgumentLoc &ArgLoc,
       // If the resulting expression is new, then use it in place of the
       // old expression in the template argument.
       if (R != E) {
-        TemplateArgument TA(R, /*IsCanonical=*/false);
+        TemplateArgument TA(R, /*CanonKind=*/std::nullopt);
         ArgLoc = TemplateArgumentLoc(TA, R);
       }
       break;
@@ -6855,7 +6883,7 @@ static bool CheckTemplateArgumentAddressOfObjectOrFunction(
   // Stop checking the precise nature of the argument if it is value dependent,
   // it should be checked when instantiated.
   if (Arg->isValueDependent()) {
-    SugaredConverted = TemplateArgument(ArgIn, /*IsCanonical=*/false);
+    SugaredConverted = TemplateArgument(ArgIn, /*CanonKind=*/std::nullopt);
     CanonicalConverted =
         S.Context.getCanonicalTemplateArgument(SugaredConverted);
     return false;
@@ -7044,7 +7072,7 @@ static bool CheckTemplateArgumentPointerToMember(
     if (VD->getType()->isMemberPointerType()) {
       if (isa<NonTypeTemplateParmDecl>(VD)) {
         if (Arg->isTypeDependent() || Arg->isValueDependent()) {
-          SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
+          SugaredConverted = TemplateArgument(Arg, /*CanonKind=*/std::nullopt);
           CanonicalConverted =
               S.Context.getCanonicalTemplateArgument(SugaredConverted);
         } else {
@@ -7110,7 +7138,7 @@ static bool CheckTemplateArgumentPointerToMember(
     // Okay: this is the address of a non-static member, and therefore
     // a member pointer constant.
     if (Arg->isTypeDependent() || Arg->isValueDependent()) {
-      SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
+      SugaredConverted = TemplateArgument(Arg, /*CanonKind=*/std::nullopt);
       CanonicalConverted =
           S.Context.getCanonicalTemplateArgument(SugaredConverted);
     } else {
@@ -7235,7 +7263,7 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
         return ExprError();
       setDeductionArg(E.get());
     }
-    SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
+    SugaredConverted = TemplateArgument(Arg, /*CanonKind=*/std::nullopt);
     CanonicalConverted = TemplateArgument(
         Context.getCanonicalTemplateArgument(SugaredConverted));
     return Arg;
@@ -7266,7 +7294,7 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
   // normal template rules apply: we accept the template if it would be valid
   // for any number of expansions (i.e. none).
   if (ArgPE && !StrictCheck) {
-    SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
+    SugaredConverted = TemplateArgument(Arg, /*CanonKind=*/std::nullopt);
     CanonicalConverted = TemplateArgument(
         Context.getCanonicalTemplateArgument(SugaredConverted));
     return Arg;
@@ -7292,7 +7320,7 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
       return Arg;
     }
     if (isa<NonTypeTemplateParmDecl>(ND)) {
-      SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
+      SugaredConverted = TemplateArgument(Arg, /*CanonKind=*/std::nullopt);
       CanonicalConverted =
           Context.getCanonicalTemplateArgument(SugaredConverted);
       return Arg;
@@ -7349,7 +7377,7 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
     // permitted (and expected) to be unable to determine a value.
     if (ArgResult.get()->isValueDependent()) {
       setDeductionArg(ArgResult.get());
-      SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
+      SugaredConverted = TemplateArgument(Arg, /*CanonKind=*/std::nullopt);
       CanonicalConverted =
           Context.getCanonicalTemplateArgument(SugaredConverted);
       return Arg;
@@ -7387,7 +7415,7 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
           Value.getLValuePath()[0].getAsArrayIndex() == 0 &&
           !Value.isLValueOnePastTheEnd() && ParamType->isPointerType()) {
         if (ArgPE) {
-          SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
+          SugaredConverted = TemplateArgument(Arg, /*CanonKind=*/std::nullopt);
           CanonicalConverted =
               Context.getCanonicalTemplateArgument(SugaredConverted);
         } else {
@@ -7418,7 +7446,7 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
       return Diag(StartLoc, diag::err_non_type_template_arg_addr_label_diff);
 
     if (ArgPE) {
-      SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
+      SugaredConverted = TemplateArgument(Arg, /*CanonKind=*/std::nullopt);
       CanonicalConverted =
           Context.getCanonicalTemplateArgument(SugaredConverted);
     } else {
@@ -7466,7 +7494,7 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
 
       // We can't check arbitrary value-dependent arguments.
       if (Arg->isValueDependent()) {
-        SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
+        SugaredConverted = TemplateArgument(Arg, /*CanonKind=*/std::nullopt);
         CanonicalConverted =
             Context.getCanonicalTemplateArgument(SugaredConverted);
         return Arg;
@@ -7555,7 +7583,7 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
     if (Arg->isValueDependent()) {
       // The argument is value-dependent. Create a new
       // TemplateArgument with the converted expression.
-      SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
+      SugaredConverted = TemplateArgument(Arg, /*CanonKind=*/std::nullopt);
       CanonicalConverted =
           Context.getCanonicalTemplateArgument(SugaredConverted);
       return Arg;
@@ -7723,7 +7751,7 @@ ExprResult Sema::CheckTemplateArgument(NamedDecl *Param, QualType ParamType,
   // Deal with parameters of type std::nullptr_t.
   if (ParamType->isNullPtrType()) {
     if (Arg->isTypeDependent() || Arg->isValueDependent()) {
-      SugaredConverted = TemplateArgument(Arg, /*IsCanonical=*/false);
+      SugaredConverted = TemplateArgument(Arg, /*CanonKind=*/std::nullopt);
       CanonicalConverted =
           Context.getCanonicalTemplateArgument(SugaredConverted);
       return Arg;
@@ -8273,7 +8301,7 @@ static bool MatchTemplateParameterKind(
       //   placeholder types
       QualType OldType = S.Context.getUnconstrainedType(OldNTTP->getType());
       QualType NewType = S.Context.getUnconstrainedType(NewNTTP->getType());
-      if (!S.Context.hasSameType(OldType, NewType)) {
+      if (!S.Context.hasFunctionallyEquivalentType(OldType, NewType)) {
         if (Complain) {
           unsigned NextDiag = diag::err_template_nontype_parm_different_type;
           if (TemplateArgLoc.isValid()) {
@@ -8918,14 +8946,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;
@@ -8933,15 +8961,23 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
     }
   }
 
+  // Only needed for partial specializations.
+  SmallVector<TemplateArgument, 4> FunctionallyEquivalentConverted;
+
   void *InsertPos = nullptr;
   ClassTemplateSpecializationDecl *PrevDecl = nullptr;
 
-  if (isPartialSpecialization)
+  if (isPartialSpecialization) {
+    FunctionallyEquivalentConverted = CTAI.SugaredConverted;
+    Context.canonicalizeTemplateArguments(FunctionallyEquivalentConverted,
+                                          CanonicalizationKind::Functional);
+
     PrevDecl = ClassTemplate->findPartialSpecialization(
-        CTAI.CanonicalConverted, TemplateParams, InsertPos);
-  else
+        FunctionallyEquivalentConverted, TemplateParams, InsertPos);
+  } else {
     PrevDecl =
         ClassTemplate->findSpecialization(CTAI.CanonicalConverted, InsertPos);
+  }
 
   ClassTemplateSpecializationDecl *Specialization = nullptr;
 
@@ -8969,16 +9005,9 @@ 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)) &&
-        (!Context.getLangOpts().CPlusPlus20 ||
-         !TemplateParams->hasAssociatedConstraints())) {
+    if (isSameAsPrimaryTemplate(*this, ClassTemplate->getTemplateParameters(),
+                                TemplateParams,
+                                FunctionallyEquivalentConverted)) {
       // C++ [temp.class.spec]p9b3:
       //
       //   -- The argument list of the specialization shall not be identical
@@ -9003,7 +9032,7 @@ DeclResult Sema::ActOnClassTemplateSpecialization(
     ClassTemplatePartialSpecializationDecl *Partial =
         ClassTemplatePartialSpecializationDecl::Create(
             Context, Kind, DC, KWLoc, TemplateNameLoc, TemplateParams,
-            ClassTemplate, CTAI.CanonicalConverted, CanonType, PrevPartial);
+            ClassTemplate, FunctionallyEquivalentConverted, PrevPartial);
     Partial->setTemplateArgsAsWritten(TemplateArgs);
     SetNestedNameSpecifier(*this, Partial, SS);
     if (TemplateParameterLists.size() > 1 && SS.isSet()) {
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index c71c40526ccdc..90b4c68c6e7e5 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -368,11 +368,7 @@ checkDeducedTemplateArguments(ASTContext &Context,
     if (Y.getKind() != TemplateArgument::Expression)
       return checkDeducedTemplateArguments(Context, Y, X);
 
-    // Compare the expressions for equality
-    llvm::FoldingSetNodeID ID1, ID2;
-    X.getAsExpr()->Profile(ID1, Context, true);
-    Y.getAsExpr()->Profile(ID2, Context, true);
-    if (ID1 == ID2)
+    if (Context.hasSameExpr(X.getAsExpr(), Y.getAsExpr()))
       return X.wasDeducedFromArrayBound() ? Y : X;
 
     // Differing dependent expressions are incompatible.
@@ -550,8 +546,9 @@ DeduceNullPtrTemplateArgument(Sema &S, TemplateParameterList *TemplateParams,
                                                         : CK_NullToPointer)
                     .get();
   return DeduceNonTypeTemplateArgument(
-      S, TemplateParams, NTTP, TemplateArgument(Value, /*IsCanonical=*/false),
-      Value->getType(), Info, PartialOrdering, Deduced, HasDeducedAnyParam);
+      S, TemplateParams, NTTP,
+      TemplateArgument(Value, /*CanonKind=*/std::nullopt), Value->getType(),
+      Info, PartialOrdering, Deduced, HasDeducedAnyParam);
 }
 
 /// Deduce the value of the given non-type template parameter
@@ -565,8 +562,9 @@ DeduceNonTypeTemplateArgument(Sema &S, TemplateParameterList *TemplateParams,
                               SmallVectorImpl<DeducedTemplateArgument> &Deduced,
                               bool *HasDeducedAnyParam) {
   return DeduceNonTypeTemplateArgument(
-      S, TemplateParams, NTTP, TemplateArgument(Value, /*IsCanonical=*/false),
-      Value->getType(), Info, PartialOrdering, Deduced, HasDeducedAnyParam);
+      S, TemplateParams, NTTP,
+      TemplateArgument(Value, /*CanonKind=*/std::nullopt), Value->getType(),
+      Info, PartialOrdering, Deduced, HasDeducedAnyParam);
 }
 
 /// Deduce the value of the given non-type template parameter
@@ -2874,7 +2872,8 @@ Sema::getTrivialTemplateArgumentLoc(const TemplateArgument &Arg,
     Expr *E = BuildExpressionFromDeclTemplateArgument(Arg, NTTPType, Loc,
                                                       TemplateParam)
                   .getAs<Expr>();
-    return TemplateArgumentLoc(TemplateArgument(E, /*IsCanonical=*/false), E);
+    return TemplateArgumentLoc(TemplateArgument(E, /*CanonKind=*/std::nullopt),
+                               E);
   }
 
   case TemplateArgument::NullPtr: {
@@ -2889,7 +2888,8 @@ Sema::getTrivialTemplateArgumentLoc(const TemplateArgument &Arg,
   case TemplateArgument::Integral:
   case TemplateArgument::StructuralValue: {
     Expr *E = BuildExpressionFromNonTypeTemplateArgument(Arg, Loc).get();
-    return TemplateArgumentLoc(TemplateArgument(E, /*IsCanonical=*/false), E);
+    return TemplateArgumentLoc(TemplateArgument(E, /*CanonKind=*/std::nullopt),
+                               E);
   }
 
     case TemplateArgument::Template:
@@ -6263,11 +6263,14 @@ struct TemplateArgumentListAreEqual {
 
     for (unsigned I = 0, E = Args1.size(); I < E; ++I) {
       // We use profile, instead of structural comparison of the arguments,
-      // because canonicalization can't do the right thing for dependent
-      // expressions.
+      // because canonicalization can't do the right thing for expressions.
       llvm::FoldingSetNodeID IDA, IDB;
-      Args1[I].Profile(IDA, Ctx);
-      Args2[I].Profile(IDB, Ctx);
+      Ctx.getCanonicalTemplateArgument(Args1[I],
+                                       CanonicalizationKind::Functional)
+          .Profile(IDA, Ctx);
+      Ctx.getCanonicalTemplateArgument(Args2[I],
+                                       CanonicalizationKind::Functional)
+          .Profile(IDB, Ctx);
       if (IDA != IDB)
         return false;
     }
@@ -6282,13 +6285,14 @@ struct TemplateArgumentListAreEqual {
 
     for (unsigned I = 0, E = Args1.size(); I < E; ++I) {
       // We use profile, instead of structural comparison of the arguments,
-      // because canonicalization can't do the right thing for dependent
-      // expressions.
+      // because canonicalization can't do the right thing for expressions.
       llvm::FoldingSetNodeID IDA, IDB;
-      Args1[I].Profile(IDA, Ctx);
-      // Unlike the specialization arguments, the injected arguments are not
-      // always canonical.
-      Ctx.getCanonicalTemplateArgument(Args2[I]).Profile(IDB, Ctx);
+      Ctx.getCanonicalTemplateArgument(Args1[I],
+                                       CanonicalizationKind::Functional)
+          .Profile(IDA, Ctx);
+      Ctx.getCanonicalTemplateArgument(Args2[I],
+                                       CanonicalizationKind::Functional)
+          .Profile(IDB, Ctx);
       if (IDA != IDB)
         return false;
     }
@@ -6436,11 +6440,17 @@ Sema::getMoreSpecializedPartialSpecialization(
   assert(PS1->getSpecializedTemplate() == PS2->getSpecializedTemplate() &&
          "the partial specializations being compared should specialize"
          " the same template.");
+  SmallVector<TemplateArgument, 8> Ps1Args(PS1->getTemplateArgs().asArray());
+  Context.canonicalizeTemplateArguments(Ps1Args);
+
+  SmallVector<TemplateArgument, 8> Ps2Args(PS2->getTemplateArgs().asArray());
+  Context.canonicalizeTemplateArguments(Ps2Args);
+
   TemplateName Name(PS1->getSpecializedTemplate()->getCanonicalDecl());
   QualType PT1 = Context.getCanonicalTemplateSpecializationType(
-      ElaboratedTypeKeyword::None, Name, PS1->getTemplateArgs().asArray());
+      ElaboratedTypeKeyword::None, Name, Ps1Args);
   QualType PT2 = Context.getCanonicalTemplateSpecializationType(
-      ElaboratedTypeKeyword::None, Name, PS2->getTemplateArgs().asArray());
+      ElaboratedTypeKeyword::None, Name, Ps2Args);
 
   TemplateDeductionInfo Info(Loc);
   return getMoreSpecialized(*this, PT1, PT2, PS1, PS2, Info);
@@ -6455,10 +6465,14 @@ bool Sema::isMoreSpecializedThanPrimary(
       Primary->getInjectedTemplateArgs(Context));
   Context.canonicalizeTemplateArguments(PrimaryCanonArgs);
 
+  SmallVector<TemplateArgument, 8> PartialCanonArgs(
+      Spec->getTemplateArgs().asArray());
+  Context.canonicalizeTemplateArguments(PartialCanonArgs);
+
   QualType PrimaryT = Context.getCanonicalTemplateSpecializationType(
       ElaboratedTypeKeyword::None, Name, PrimaryCanonArgs);
   QualType PartialT = Context.getCanonicalTemplateSpecializationType(
-      ElaboratedTypeKeyword::None, Name, Spec->getTemplateArgs().asArray());
+      ElaboratedTypeKeyword::None, Name, PartialCanonArgs);
 
   VarTemplatePartialSpecializationDecl *MaybeSpec =
       getMoreSpecialized(*this, PartialT, PrimaryT, Spec, Primary, Info);
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 09c2482168ab7..82e0bb411560e 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -4915,14 +4915,19 @@ TemplateDeclInstantiator::InstantiateClassTemplatePartialSpecialization(
   // Check these arguments are valid for a template partial specialization.
   if (SemaRef.CheckTemplatePartialSpecializationArgs(
           PartialSpec->getLocation(), ClassTemplate, InstTemplateArgs.size(),
-          CTAI.CanonicalConverted))
+          CTAI.SugaredConverted))
     return nullptr;
 
+  SmallVector<TemplateArgument, 4> FunctionallyEquivalentConverted =
+      CTAI.SugaredConverted;
+  SemaRef.Context.canonicalizeTemplateArguments(
+      FunctionallyEquivalentConverted, CanonicalizationKind::Functional);
+
   // Figure out where to insert this class template partial specialization
   // in the member template's set of class template partial specializations.
   void *InsertPos = nullptr;
   ClassTemplateSpecializationDecl *PrevDecl =
-      ClassTemplate->findPartialSpecialization(CTAI.CanonicalConverted,
+      ClassTemplate->findPartialSpecialization(FunctionallyEquivalentConverted,
                                                InstParams, InsertPos);
 
   // Create the class template partial specialization declaration.
@@ -4930,8 +4935,7 @@ TemplateDeclInstantiator::InstantiateClassTemplatePartialSpecialization(
       ClassTemplatePartialSpecializationDecl::Create(
           SemaRef.Context, PartialSpec->getTagKind(), Owner,
           PartialSpec->getBeginLoc(), PartialSpec->getLocation(), InstParams,
-          ClassTemplate, CTAI.CanonicalConverted,
-          /*CanonInjectedTST=*/CanQualType(),
+          ClassTemplate, FunctionallyEquivalentConverted,
           /*PrevDecl=*/nullptr);
 
   InstPartialSpec->setTemplateArgsAsWritten(InstTemplateArgs);
@@ -5024,14 +5028,19 @@ TemplateDeclInstantiator::InstantiateVarTemplatePartialSpecialization(
   // Check these arguments are valid for a template partial specialization.
   if (SemaRef.CheckTemplatePartialSpecializationArgs(
           PartialSpec->getLocation(), VarTemplate, InstTemplateArgs.size(),
-          CTAI.CanonicalConverted))
+          CTAI.SugaredConverted))
     return nullptr;
 
+  SmallVector<TemplateArgument, 4> FunctionallyEquivalentConverted =
+      CTAI.SugaredConverted;
+  SemaRef.Context.canonicalizeTemplateArguments(
+      FunctionallyEquivalentConverted, CanonicalizationKind::Functional);
+
   // Figure out where to insert this variable template partial specialization
   // in the member template's set of variable template partial specializations.
   void *InsertPos = nullptr;
   VarTemplateSpecializationDecl *PrevDecl =
-      VarTemplate->findPartialSpecialization(CTAI.CanonicalConverted,
+      VarTemplate->findPartialSpecialization(FunctionallyEquivalentConverted,
                                              InstParams, InsertPos);
 
   // Do substitution on the type of the declaration
@@ -5053,7 +5062,7 @@ TemplateDeclInstantiator::InstantiateVarTemplatePartialSpecialization(
       VarTemplatePartialSpecializationDecl::Create(
           SemaRef.Context, Owner, PartialSpec->getInnerLocStart(),
           PartialSpec->getLocation(), InstParams, VarTemplate, TSI->getType(),
-          TSI, PartialSpec->getStorageClass(), CTAI.CanonicalConverted);
+          TSI, PartialSpec->getStorageClass(), FunctionallyEquivalentConverted);
 
   InstPartialSpec->setTemplateArgsAsWritten(InstTemplateArgs);
 
diff --git a/clang/lib/Sema/SemaTemplateVariadic.cpp b/clang/lib/Sema/SemaTemplateVariadic.cpp
index 5b1aad3fa8470..797e535bc0957 100644
--- a/clang/lib/Sema/SemaTemplateVariadic.cpp
+++ b/clang/lib/Sema/SemaTemplateVariadic.cpp
@@ -1412,13 +1412,18 @@ TemplateArgumentLoc Sema::getTemplateArgumentPackExpansionPattern(
   }
 
   case TemplateArgument::Expression: {
-    PackExpansionExpr *Expansion
-      = cast<PackExpansionExpr>(Argument.getAsExpr());
+    CanonicalizationKindOrNone CanonKind = std::nullopt;
+    Expr *OrigE = OrigLoc.getSourceExpression(), *E = OrigE;
+    if (!E) { // FIXME: Does this ever happen?
+      E = Argument.getAsExpr();
+      CanonKind = Argument.getExprCanonKind();
+    }
+    auto *Expansion = cast<PackExpansionExpr>(E);
     Expr *Pattern = Expansion->getPattern();
-    Ellipsis = Expansion->getEllipsisLoc();
+    if (OrigE)
+      Ellipsis = Expansion->getEllipsisLoc();
     NumExpansions = Expansion->getNumExpansions();
-    return TemplateArgumentLoc(
-        TemplateArgument(Pattern, Argument.isCanonicalExpr()), Pattern);
+    return TemplateArgumentLoc(TemplateArgument(Pattern, CanonKind), Pattern);
   }
 
   case TemplateArgument::TemplateExpansion:
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 3dc8ceb70f3cd..f7355144c0dd7 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -6071,6 +6071,7 @@ namespace {
     }
     void VisitDecltypeTypeLoc(DecltypeTypeLoc TL) {
       assert(DS.getTypeSpecType() == DeclSpec::TST_decltype);
+      TL.setUnderlyingExpr(DS.getRepAsExpr());
       TL.setDecltypeLoc(DS.getTypeSpecTypeLoc());
       TL.setRParenLoc(DS.getTypeofParensRange().getEnd());
     }
@@ -10061,7 +10062,8 @@ QualType Sema::BuildDecltypeType(Expr *E, bool AsUnevaluated) {
     // used to build SFINAE gadgets.
     Diag(E->getExprLoc(), diag::warn_side_effects_unevaluated_context);
   }
-  return Context.getDecltypeType(E, getDecltypeForExpr(E));
+  return Context.getDecltypeType(E, /*ExprCanonKind=*/std::nullopt,
+                                 getDecltypeForExpr(E));
 }
 
 QualType Sema::ActOnPackIndexingType(QualType Pattern, Expr *IndexExpr,
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 40187f71231bd..d01da7d3d4fd3 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -4078,7 +4078,7 @@ class TreeTransform {
         return TemplateArgumentLoc();
 
       return TemplateArgumentLoc(TemplateArgument(Result.get(),
-                                                  /*IsCanonical=*/false),
+                                                  /*CanonKind=*/std::nullopt),
                                  Result.get());
     }
 
@@ -5108,7 +5108,7 @@ bool TreeTransform<Derived>::TransformTemplateArgument(
     if (E.isInvalid())
       return true;
     Output = TemplateArgumentLoc(
-        TemplateArgument(E.get(), /*IsCanonical=*/false), E.get());
+        TemplateArgument(E.get(), /*CanonKind=*/std::nullopt), E.get());
     return false;
   }
   }
@@ -7056,17 +7056,15 @@ QualType TreeTransform<Derived>::TransformTypeOfType(TypeLocBuilder &TLB,
   return Result;
 }
 
-template<typename Derived>
+template <typename Derived>
 QualType TreeTransform<Derived>::TransformDecltypeType(TypeLocBuilder &TLB,
                                                        DecltypeTypeLoc TL) {
-  const DecltypeType *T = TL.getTypePtr();
-
   // decltype expressions are not potentially evaluated contexts
   EnterExpressionEvaluationContext Unevaluated(
       SemaRef, Sema::ExpressionEvaluationContext::Unevaluated, nullptr,
       Sema::ExpressionEvaluationContextRecord::EK_Decltype);
 
-  ExprResult E = getDerived().TransformExpr(T->getUnderlyingExpr());
+  ExprResult E = getDerived().TransformExpr(TL.getUnderlyingExpr());
   if (E.isInvalid())
     return QualType();
 
@@ -7075,15 +7073,15 @@ QualType TreeTransform<Derived>::TransformDecltypeType(TypeLocBuilder &TLB,
     return QualType();
 
   QualType Result = TL.getType();
-  if (getDerived().AlwaysRebuild() ||
-      E.get() != T->getUnderlyingExpr()) {
+  if (getDerived().AlwaysRebuild() || E.get() != TL.getUnderlyingExpr()) {
     Result = getDerived().RebuildDecltypeType(E.get(), TL.getDecltypeLoc());
     if (Result.isNull())
       return QualType();
-  }
-  else E.get();
+  } else
+    E.get();
 
   DecltypeTypeLoc NewTL = TLB.push<DecltypeTypeLoc>(Result);
+  NewTL.setUnderlyingExpr(E.get());
   NewTL.setDecltypeLoc(TL.getDecltypeLoc());
   NewTL.setRParenLoc(TL.getRParenLoc());
   return Result;
@@ -16533,7 +16531,7 @@ TreeTransform<Derived>::TransformSizeOfPackExpr(SizeOfPackExpr *E) {
         ArgStorage = TemplateArgument(
             new (getSema().Context)
                 PackExpansionExpr(DRE.get(), E->getPackLoc(), std::nullopt),
-            /*IsCanonical=*/false);
+            /*CanonKind=*/std::nullopt);
       }
       PackArgs = ArgStorage;
     }
@@ -16748,9 +16746,11 @@ ExprResult TreeTransform<Derived>::TransformSubstNonTypeTemplateParmExpr(
     Replacement = E->getReplacement();
   }
 
+  // FIXME: Track in SubstNonTypeTemplateParmExpr the canonical kind of the
+  // expression.
   return getDerived().RebuildSubstNonTypeTemplateParmExpr(
       AssociatedDecl, E->getParameter(), E->getNameLoc(),
-      TemplateArgument(Replacement.get(), /*IsCanonical=*/false),
+      TemplateArgument(Replacement.get(), /*CanonKind=*/std::nullopt),
       E->getPackIndex(), E->getFinal());
 }
 
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index 049dc227821cf..a042aaac745a5 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -7590,6 +7590,7 @@ void TypeLocReader::VisitTypeOfTypeLoc(TypeOfTypeLoc TL) {
 }
 
 void TypeLocReader::VisitDecltypeTypeLoc(DecltypeTypeLoc TL) {
+  TL.setUnderlyingExpr(Reader.readExpr());
   TL.setDecltypeLoc(readSourceLocation());
   TL.setRParenLoc(readSourceLocation());
 }
@@ -10246,12 +10247,12 @@ ASTRecordReader::readTemplateParameterList() {
 }
 
 void ASTRecordReader::readTemplateArgumentList(
-                        SmallVectorImpl<TemplateArgument> &TemplArgs,
-                        bool Canonicalize) {
+    SmallVectorImpl<TemplateArgument> &TemplArgs,
+    CanonicalizationKindOrNone CanonKind) {
   unsigned NumTemplateArgs = readInt();
   TemplArgs.reserve(NumTemplateArgs);
   while (NumTemplateArgs--)
-    TemplArgs.push_back(readTemplateArgument(Canonicalize));
+    TemplArgs.push_back(readTemplateArgument(CanonKind));
 }
 
 /// Read a UnresolvedSet structure.
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 9033ea55bc5e2..8fe5c473bb61c 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -946,7 +946,8 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
 
     // Template arguments.
     SmallVector<TemplateArgument, 8> TemplArgs;
-    Record.readTemplateArgumentList(TemplArgs, /*Canonicalize*/ true);
+    Record.readTemplateArgumentList(TemplArgs,
+                                    CanonicalizationKind::Structural);
 
     // Template args as written.
     TemplateArgumentListInfo TemplArgsWritten;
@@ -2440,7 +2441,7 @@ void ASTDeclReader::VisitImplicitConceptSpecializationDecl(
   VisitDecl(D);
   llvm::SmallVector<TemplateArgument, 4> Args;
   for (unsigned I = 0; I < D->NumTemplateArgs; ++I)
-    Args.push_back(Record.readTemplateArgument(/*Canonicalize=*/false));
+    Args.push_back(Record.readTemplateArgument());
   D->setTemplateArguments(Args);
 }
 
@@ -2543,7 +2544,7 @@ RedeclarableResult ASTDeclReader::VisitClassTemplateSpecializationDeclImpl(
   }
 
   SmallVector<TemplateArgument, 8> TemplArgs;
-  Record.readTemplateArgumentList(TemplArgs, /*Canonicalize*/ true);
+  Record.readTemplateArgumentList(TemplArgs, CanonicalizationKind::Functional);
   D->TemplateArgs = TemplateArgumentList::CreateCopy(C, TemplArgs);
   D->PointOfInstantiation = readSourceLocation();
   D->SpecializationKind = (TemplateSpecializationKind)Record.readInt();
@@ -2657,7 +2658,7 @@ RedeclarableResult ASTDeclReader::VisitVarTemplateSpecializationDeclImpl(
     D->setTemplateArgsAsWritten(Record.readASTTemplateArgumentListInfo());
 
   SmallVector<TemplateArgument, 8> TemplArgs;
-  Record.readTemplateArgumentList(TemplArgs, /*Canonicalize*/ true);
+  Record.readTemplateArgumentList(TemplArgs, CanonicalizationKind::Functional);
   D->TemplateArgs = TemplateArgumentList::CreateCopy(C, TemplArgs);
   D->PointOfInstantiation = readSourceLocation();
   D->SpecializationKind = (TemplateSpecializationKind)Record.readInt();
diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp
index 632137f01d767..8f2e061c0dca3 100644
--- a/clang/lib/Serialization/ASTWriter.cpp
+++ b/clang/lib/Serialization/ASTWriter.cpp
@@ -531,6 +531,7 @@ void TypeLocWriter::VisitTypeOfTypeLoc(TypeOfTypeLoc TL) {
 }
 
 void TypeLocWriter::VisitDecltypeTypeLoc(DecltypeTypeLoc TL) {
+  Record.AddStmt(TL.getUnderlyingExpr());
   addSourceLocation(TL.getDecltypeLoc());
   addSourceLocation(TL.getRParenLoc());
 }
diff --git a/clang/test/AST/ast-dump-templates.cpp b/clang/test/AST/ast-dump-templates.cpp
index 135191fae2e0b..c76a1fb9eb6bc 100644
--- a/clang/test/AST/ast-dump-templates.cpp
+++ b/clang/test/AST/ast-dump-templates.cpp
@@ -7565,7 +7565,7 @@ namespace TestAbbreviatedTemplateDecls {
 // JSON-NEXT:           {
 // JSON-NEXT:            "kind": "TemplateArgument",
 // JSON-NEXT:            "isExpr": true,
-// JSON-NEXT:            "isCanonical": true,
+// JSON-NEXT:            "CanonicalKind": "Structural",
 // JSON-NEXT:            "inner": [
 // JSON-NEXT:             {
 // JSON-NEXT:              "id": "0x{{.*}}",
@@ -7604,7 +7604,7 @@ namespace TestAbbreviatedTemplateDecls {
 // JSON-NEXT:       {
 // JSON-NEXT:        "kind": "TemplateArgument",
 // JSON-NEXT:        "isExpr": true,
-// JSON-NEXT:        "isCanonical": true,
+// JSON-NEXT:        "CanonicalKind": "Structural",
 // JSON-NEXT:        "inner": [
 // JSON-NEXT:         {
 // JSON-NEXT:          "id": "0x{{.*}}",
@@ -7864,7 +7864,7 @@ namespace TestAbbreviatedTemplateDecls {
 // JSON-NEXT:           {
 // JSON-NEXT:            "kind": "TemplateArgument",
 // JSON-NEXT:            "isExpr": true,
-// JSON-NEXT:            "isCanonical": true,
+// JSON-NEXT:            "CanonicalKind": "Structural",
 // JSON-NEXT:            "inner": [
 // JSON-NEXT:             {
 // JSON-NEXT:              "id": "0x{{.*}}",
@@ -7903,7 +7903,7 @@ namespace TestAbbreviatedTemplateDecls {
 // JSON-NEXT:       {
 // JSON-NEXT:        "kind": "TemplateArgument",
 // JSON-NEXT:        "isExpr": true,
-// JSON-NEXT:        "isCanonical": true,
+// JSON-NEXT:        "CanonicalKind": "Structural",
 // JSON-NEXT:        "inner": [
 // JSON-NEXT:         {
 // JSON-NEXT:          "id": "0x{{.*}}",
diff --git a/clang/test/CXX/drs/cwg20xx.cpp b/clang/test/CXX/drs/cwg20xx.cpp
index 75b4094283db0..5bb00e9f623f8 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 at -1 {{no member named 'f' in 'cwg2064::X<unsigned long>'}}
+  }
+#endif
+}
+
 namespace cwg2076 { // cwg2076: 13
 #if __cplusplus >= 201103L
   namespace std_example {
diff --git a/clang/test/CXX/temp/temp.decls/temp.alias/p3.cpp b/clang/test/CXX/temp/temp.decls/temp.alias/p3.cpp
index 2d46502e1d9b3..e885b69838021 100644
--- a/clang/test/CXX/temp/temp.decls/temp.alias/p3.cpp
+++ b/clang/test/CXX/temp/temp.decls/temp.alias/p3.cpp
@@ -8,10 +8,9 @@ template<class T> struct A {
 };
 B<short> b;
 
-template<typename T> using U = int;
+template<typename T> using U = int; // expected-note {{previous definition}}
 
 template<typename ...T> void f(U<T> ...xs);
 void g() { f<void,void,void>(1, 2, 3); }
 
-// FIXME: This is illegal, but probably only because CWG1044 missed this paragraph.
-template<typename T> using U = U<T>;
+template<typename T> using U = U<T>; // expected-error {{redefinition with different types}}
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/mangle-exception-spec.cpp b/clang/test/CodeGenCXX/mangle-exception-spec.cpp
index 15f7a8b6cb504..2fdb383bb8cf2 100644
--- a/clang/test/CodeGenCXX/mangle-exception-spec.cpp
+++ b/clang/test/CodeGenCXX/mangle-exception-spec.cpp
@@ -35,15 +35,8 @@ template auto h<>(int()) -> int (*)();
 // CHECK: define {{.*}} @_Z1hIJfEEPDwDpT_iEFivEPDwiS1_EFivE(
 template auto h<float>(int()) -> int (*)();
 
-// FIXME: The C++11 manglings here are wrong; they should be the same as the
-// C++17 manglings.
-// The mangler mishandles substitutions for instantiation-dependent types that
-// differ only in type sugar that is not relevant for mangling. (In this case,
-// the types differ in presence/absence of ParenType nodes under the pointer.)
 template<typename...T> auto i(int() throw(int, T...)) -> int (*)() throw(int, T...) { return nullptr; }
-// CHECK-CXX11: define {{.*}} @_Z1iIJEEPDwiDpT_EFivEPS2_(
-// CHECK-CXX17: define {{.*}} @_Z1iIJEEPDwiDpT_EFivES3_(
+// CHECK: define {{.*}} @_Z1iIJEEPDwiDpT_EFivES3_(
 template auto i<>(int()) -> int (*)();
-// CHECK-CXX11: define {{.*}} @_Z1iIJfEEPDwiDpT_EFivEPS2_(
-// CHECK-CXX17: define {{.*}} @_Z1iIJfEEPDwiDpT_EFivES3_(
+// CHECK: define {{.*}} @_Z1iIJfEEPDwiDpT_EFivES3_(
 template auto i<float>(int()) -> int (*)();
diff --git a/clang/test/CodeGenCXX/mangle-subst.cpp b/clang/test/CodeGenCXX/mangle-subst.cpp
index 524e0febe479a..e06c39b12809a 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,11 @@ struct Inst : public A::Impl<A::Wrap> {};
 void Test() { Inst a; }
 
 }
+
+namespace InstantiationDependentDecltype {
+  struct a { a(char); };
+  struct b { a c(); };
+  // CHECK: @_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/CodeGenCXX/mangle-template.cpp b/clang/test/CodeGenCXX/mangle-template.cpp
index 2fca98faadb16..9da992d4fe445 100644
--- a/clang/test/CodeGenCXX/mangle-template.cpp
+++ b/clang/test/CodeGenCXX/mangle-template.cpp
@@ -383,9 +383,7 @@ namespace fixed_size_parameter_pack {
 namespace type_qualifier {
   template<typename T> using int_t = int;
   template<typename T> void f(decltype(int_t<T*>() + 1)) {}
-  // FIXME: This mangling doesn't work: we need to mangle the
-  // instantiation-dependent 'int_t' operand.
-  // CHECK: @_ZN14type_qualifier1fIPiEEvDTplcvi_ELi1EE
+  // CHECK: @_ZN14type_qualifier1fIPiEEvDTplcvNS_5int_tIPT_EE_ELi1EE
   template void f<int*>(int);
 
   // Note that this template has different constraints but would mangle the
@@ -395,9 +393,38 @@ namespace type_qualifier {
   struct impl { using type = void; };
   template<typename T> using alias = impl;
   template<typename T> void g(decltype(alias<T*>::type(), 1)) {}
-  // FIXME: Similarly we need to mangle the `T*` in here.
-  // CHECK: @_ZN14type_qualifier1gIPiEEvDTcmcvv_ELi1EE
+  // CHECK: @_ZN14type_qualifier1gIPiEEvDTcmcvNS_5aliasIPT_ENS_4impl4typeEE_ELi1EE
   template void g<int*>(int);
+
+  struct impl2 : private impl { using impl::type; };
+  template<typename T> using alias2 = impl2;
+  template<typename T> void h(decltype(alias2<T*>::type(), 1)) {}
+  // CHECK: @_ZN14type_qualifier1hIPiEEvDTcmcvNS_6alias2IPT_ENS_5impl24typeEE_ELi1EE
+  template void h<int*>(int);
+
+  struct impl3 {
+    template<class> using type = void;
+  };
+  template<typename T> using alias3 = impl3;
+  template<typename T> void i(decltype(alias3<T*>::type<char>(), 1)) {}
+  // CHECK: @_ZN14type_qualifier1iIPiEEvDTcmcvNS_6alias3IPT_ES_5impl34typeIcEE_ELi1EE
+  template void i<int*>(int);
+
+  struct impl4 : impl3 {
+    using impl3::type;
+  };
+  template<typename T> using alias4 = impl4;
+  template<typename T> void j(decltype(alias4<T*>::type<char>(), 1)) {}
+  // CHECK: @_ZN14type_qualifier1jIPiEEvDTcmcvNS_6alias4IPT_ES_5impl44typeIcEE_ELi1EE
+  template void j<int*>(int);
+
+  struct impl5 {
+    struct type {};
+  };
+  template<typename T> using alias5 = impl5;
+  template<typename T> void k(decltype(alias5<T*>::type(), 1)) {}
+  // CHECK: @_ZN14type_qualifier1kIPiEEvDTcmcvNS_6alias5IPT_ENS_5impl54typeEE_ELi1EE
+  template void k<int*>(int);
 }
 
 namespace unresolved_template_specialization_type {
diff --git a/clang/test/CodeGenCXX/microsoft-abi-default-cc.cpp b/clang/test/CodeGenCXX/microsoft-abi-default-cc.cpp
index 81d505588785a..0d1a8e6d06fb4 100644
--- a/clang/test/CodeGenCXX/microsoft-abi-default-cc.cpp
+++ b/clang/test/CodeGenCXX/microsoft-abi-default-cc.cpp
@@ -49,7 +49,7 @@ void static_qux() {}
 namespace PR31656 {
 template <int I>
 void __cdecl callee(int args[I]);
-// GCABI-LABEL: declare void @_ZN7PR316566calleeILi1EEEvPi(
+// GCABI-LABEL: declare void @_ZN7PR316566calleeILi1EEEvAT__i(
 // MSABI: declare dso_local void @"??$callee@$00 at PR31656@@YAXQAH at Z"(
 
 void caller() { callee<1>(0); }
diff --git a/clang/test/Sema/array-parameter.cpp b/clang/test/Sema/array-parameter.cpp
index 14cc88f2e36cb..dbf369a0941df 100644
--- a/clang/test/Sema/array-parameter.cpp
+++ b/clang/test/Sema/array-parameter.cpp
@@ -4,7 +4,7 @@ template <int N>
 void func(int i[10]); // expected-note {{previously declared as 'int[10]' here}}
 
 template <int N>
-void func(int i[N]); // expected-warning {{argument 'i' of type 'int[N]' with mismatched bound}}
+void func(int i[11]); // expected-warning {{argument 'i' of type 'int[11]' with mismatched bound}}
 
 template <int N>
 void func(int (&Val)[N]);
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 at -1 {{no class named 'type' in 'Base<bool>'}}
   return C;
 }
 struct Z {
diff --git a/clang/test/SemaCXX/alias-template.cpp b/clang/test/SemaCXX/alias-template.cpp
index b49d36a6267e6..57d8e1a93ea83 100644
--- a/clang/test/SemaCXX/alias-template.cpp
+++ b/clang/test/SemaCXX/alias-template.cpp
@@ -2,8 +2,8 @@
 
 namespace RedeclAliasTypedef {
   template<typename U> using T = int;
-  template<typename U> using T = int;
-  template<typename U> using T = T<U>;
+  template<typename U> using T = int;  // expected-note  {{previous definition}}
+  template<typename U> using T = T<U>; // expected-error {{redefinition with different types}}
 }
 
 namespace IllegalTypeIds {
diff --git a/clang/test/SemaCXX/decltype.cpp b/clang/test/SemaCXX/decltype.cpp
index 45a4c4cf1ac86..aa6fd35ee2994 100644
--- a/clang/test/SemaCXX/decltype.cpp
+++ b/clang/test/SemaCXX/decltype.cpp
@@ -46,8 +46,8 @@ namespace pr10154 {
 
 template<typename T> struct S {};
 template<typename T> auto f(T t) -> decltype(S<int>(t)) {
-  using U = decltype(S<int>(t));
-  using U = S<int>;
+  using U = decltype(S<int>(t)); // expected-note  {{previous definition}}
+  using U = S<int>;              // expected-error {{redefinition with different types ('S<...>' vs 'S<...>')}}
   return S<int>(t);
 }
 
@@ -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 ValueDependent {
+  template<int V> void f() {
+    decltype(V) x = nullptr;
+    // expected-error at -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 ValueDependent
+
 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 at -1 {{declaration does not declare anything}}
+  }
 } // namespace GH178324
 #endif
diff --git a/clang/test/SemaCXX/sugar-common-types.cpp b/clang/test/SemaCXX/sugar-common-types.cpp
index 4c704db195c54..949ab504c57bf 100644
--- a/clang/test/SemaCXX/sugar-common-types.cpp
+++ b/clang/test/SemaCXX/sugar-common-types.cpp
@@ -143,7 +143,7 @@ namespace GH67603 {
     using C = B;
     using D = B;
     N t = 0 ? A<decltype(C())>() : A<decltype(D())>();
-    // expected-error at -1 {{rvalue of type 'A<decltype(C())>' (aka 'long')}}
+    // expected-error at -1 {{rvalue of type 'A<decltype(type-parameter-0-0())>' (aka 'long')}}
   }
   template void h<int>();
 } // namespace GH67603
@@ -266,3 +266,19 @@ namespace FunctionTypeExtInfo {
     // expected-error at -1 {{lvalue of type 'void (*)(__attribute__((swift_async_context)) B *)'}}
   } // namespace TypedefType
 } // namespace FunctionTypeExtInfo
+
+namespace Decltype {
+  N t1 = 0 ? decltype((X1*){}){} : decltype((Y1*){}){};
+  // expected-error at -1 {{rvalue of type 'decltype((int *){})' (aka 'int *')}}
+
+  using R = X1*;
+  extern R a;
+  using D1 = decltype(a);
+
+  using R = Y1*;
+  extern R a;
+  using E1 = decltype(a);
+
+  N t2 = 0 ? D1{} : E1{};
+  // expected-error at -1 {{rvalue of type 'decltype(a)' (aka 'int *')}}
+} // namespace Decltype
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/GH164330.cpp b/clang/test/SemaTemplate/GH164330.cpp
index 767420de272e6..b58fb69290622 100644
--- a/clang/test/SemaTemplate/GH164330.cpp
+++ b/clang/test/SemaTemplate/GH164330.cpp
@@ -162,7 +162,7 @@ template <typename T> struct PathFieldId {
 };
 template <PathFieldId...> constexpr auto PathImplHelper();
 
-template <int N> using FieldName = FieldName<N>;
+template <int N> using FieldName = FieldNameEnum::type;
 enum class FieldNumber;
 template <PathFieldId... fields>
 constexpr auto Path = PathImplHelper<fields...>();
diff --git a/clang/test/SemaTemplate/concepts-out-of-line-def.cpp b/clang/test/SemaTemplate/concepts-out-of-line-def.cpp
index 9811b18f4301b..527a2aa575c91 100644
--- a/clang/test/SemaTemplate/concepts-out-of-line-def.cpp
+++ b/clang/test/SemaTemplate/concepts-out-of-line-def.cpp
@@ -519,15 +519,15 @@ concept something_interesting = requires {
 
 template <class T>
 struct X { // #defined-here
-      void foo() requires requires { requires is_not_same_v<T, int>; };
-      void bar(decltype(requires { requires is_not_same_v<T, int>; }));
+      void foo() requires requires { requires is_not_same_v<T, int>; }; // #foo-decl
+      void bar(decltype(requires { requires is_not_same_v<T, int>; })); // #bar-decl
 };
 
 template <class T>
 void X<T>::foo() requires requires { requires something_interesting<T>; } {}
 // expected-error at -1{{definition of 'foo' does not match any declaration}}
 // expected-note@#defined-here{{defined here}}
-// expected-note at -8{{member declaration nearly matches}}
+// expected-note@#foo-decl{{member declaration nearly matches}}
 
 template <class T>
 void X<T>::foo() requires requires { requires is_not_same_v<T, int>; } {} // ok
@@ -536,6 +536,7 @@ template <class T>
 void X<T>::bar(decltype(requires { requires something_interesting<T>; })) {}
 // expected-error at -1{{definition of 'bar' does not match any declaration}}
 // expected-note@#defined-here{{defined here}}
+// expected-note@#bar-decl{{member declaration nearly matches}}
 
 template <class T>
 void X<T>::bar(decltype(requires { requires is_not_same_v<T, int>; })) {}
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..9b9980dd00b16 100644
--- a/clang/test/SemaTemplate/dependent-expr.cpp
+++ b/clang/test/SemaTemplate/dependent-expr.cpp
@@ -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/dependent-type-identity.cpp b/clang/test/SemaTemplate/dependent-type-identity.cpp
index c826268c864c4..8d2791b790293 100644
--- a/clang/test/SemaTemplate/dependent-type-identity.cpp
+++ b/clang/test/SemaTemplate/dependent-type-identity.cpp
@@ -148,9 +148,7 @@ namespace PR21289 {
   void g() { f<void, void, void>(); }
 
   template<typename ...Ts> void h(S<int>) {}
-  // Pending a core issue, it's not clear if these are redeclarations, but they
-  // are probably intended to be... even though substitution can succeed for one
-  // of them but fail for the other!
-  template<typename ...Ts> void h(S<X<Ts>...>) {} // expected-note {{previous}}
-  template<typename ...Ts> void h(S<Y<Ts, sizeof(Ts)>...>) {} // expected-error {{redefinition}}
+
+  template<typename ...Ts> void h(S<X<Ts>...>) {}
+  template<typename ...Ts> void h(S<Y<Ts, sizeof(Ts)>...>) {}
 }
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/instantiate-expr-1.cpp b/clang/test/SemaTemplate/instantiate-expr-1.cpp
index 20d3edd86f391..b4c79c0c1cf29 100644
--- a/clang/test/SemaTemplate/instantiate-expr-1.cpp
+++ b/clang/test/SemaTemplate/instantiate-expr-1.cpp
@@ -206,3 +206,13 @@ void foo() {
 
 void test() { foo<void>(); }
 } // namespace TestAsmCleanup
+
+namespace GH190495 {
+  template <bool> struct __all;
+  template <class> bool __is_nothrow_swappable_v;
+  template <class _Tp> struct __tuple_impl {
+    void swap() noexcept(__all<__is_nothrow_swappable_v<_Tp>>::value);
+    void swap() const
+        noexcept(__all<__is_nothrow_swappable_v<const _Tp>>::value);
+  };
+} // namespace GH190495
diff --git a/clang/test/SemaTemplate/instantiation-dependence.cpp b/clang/test/SemaTemplate/instantiation-dependence.cpp
index 1a97dbf769ec6..9939be4927825 100644
--- a/clang/test/SemaTemplate/instantiation-dependence.cpp
+++ b/clang/test/SemaTemplate/instantiation-dependence.cpp
@@ -1,8 +1,9 @@
-// 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...
 template<class ...> using void_t = void;
+template<class T, class> using alias = T;
 
 namespace PR24076 {
   template<class T> T declval();
@@ -48,17 +49,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 +65,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 +98,283 @@ 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<class T, class = void> struct X;
+  template<class T> struct X<T, void_t<char>> {}; // expected-note  {{previous}}
+  template<class T> struct X<T, void_t<void>> {}; // expected-error {{redefinition}}
+
+  template<class T, class = void> bool x;
+  template<class T> bool x<T, void_t<char>>;  // expected-note  {{previous}}
+  template<class T> bool x<T, void_t<void>>; // expected-error {{redefinition}}
+} // namespace NonInstDependentArgs1
+
+namespace NonInstDependentArgs2 {
+  template<class T, class = void> struct X;
+  template<class T> struct X<T, void_t<T, void>> {};
+  template<class T> struct X<T, void_t<T, char>> {};
+
+  template<class T, class = void> bool x;
+  template<class T> bool x<T, void_t<T, char>>;
+  template<class T> bool x<T, void_t<T, void>>;
+} // namespace NonInstDependentArgs2
+
+namespace Level1 {
+  template<class T, class = void> struct X;
+  template<class T> struct X<T, void_t<T>> {};
+  template<class T> struct X<T, void_t<T*>> {};
+} // namespace Level1
+
+namespace Level2 {
+  template<class T, class = void> struct X;
+  template<class T> struct X<T, void_t<void_t<T>>> {};
+  template<class T> struct X<T, void_t<void_t<T*>>> {};
+} // namespace Level2
+
+namespace IndirectAlias1 {
+  template<class T> using alias2 = void_t<T>;
+  template<class T, class = void> struct X;
+  template<class T> struct X<T, void_t<T>> {}; // expected-note  {{previous}}
+  template<class T> struct X<T, alias2<T>> {}; // expected-error {{redefinition}}
+} // namspace IndirectAlias1
+
+namespace IndirectAlias2 {
+  template<class T, class U> using alias2 = alias<T, U>;
+  template<class T, class = void> struct X;
+  template<class T> struct X<T, T> {};
+  template<class T> struct X<T, alias<T, T>> {};  // expected-note  {{previous}}
+  template<class T> struct X<T, alias2<T, T>> {}; // expected-error {{redefinition}}
+} // namespaceIndirectAlias2
+
+namespace PackIndexing {
+  // FIXME: This should not be a redefinition.
+  template<class ...Ts> using aliaspack = Ts...[0];
+  template<class T, class = void> struct X;
+  template<class T> struct X<T, T> {};                              // expected-note  {{previous}}
+  template<class T> struct X<T, aliaspack<T, typename T::type>> {}; // expected-error {{redefinition}}
+} // namespace PackIndexing
+
+namespace DeclType1 {
+  template<class T, class = void> struct X;
+  template<class T> struct X<T, decltype(void())> {};         // expected-note  {{previous}}
+  template<class T> struct X<T, decltype(void_t<char>())> {}; // expected-error {{redefinition}}
+} // namespace DeclType1
+
+namespace DeclType2 {
+  template<class T, class = void> struct X;
+  template<class T> struct X<T, decltype(void())> {};
+  template<class T> struct X<T, decltype(void_t<typename T::type>())> {}; // expected-note  {{previous}}
+  template<class T> struct X<T, decltype(void_t<typename T::type>())> {}; // expected-error {{redefinition}}
+} // namespace DeclType2
+
+namespace DeclType3 {
+  template<class T, class = void> struct X;
+  template<class T> struct X<T, decltype(void())> {};
+  template<class T> struct X<T, decltype(void_t<typename T::type>())> {};
+  template<class T> struct X<T, decltype(void_t<typename T::bar>())> {};
+} // namespace DeclType3
+
+namespace DeclType4 {
+  template<class T> using X = decltype(void());                     // expected-note  {{previous}}
+  template<class T> using X = decltype(void_t<typename T::type>()); // expected-error {{redefinition with different types}}
+} // namespace DeclType4
+
+namespace DeclType5 {
+  template<class T> using X = decltype(void_t<typename T::bar>());  // expected-note  {{previous}}
+  template<class T> using X = decltype(void_t<typename T::type>()); // expected-error {{redefinition with different types}}
+} // namespace DeclType5
+
+namespace TempArg1 {
+  template<void*> struct A;
+  template<class T> using X = A<(void*){}>;                     // expected-note  {{previous}}
+  template<class T> using X = A<(void_t<typename T::type>*){}>; // expected-error {{redefinition with different types}}
+} // namespace TempArg1
+
+namespace TempArg2 {
+  template<void*> struct A;
+  template<class T> using X = A<(void_t<typename T::bar>*){}>;  // expected-note  {{previous}}
+  template<class T> using X = A<(void_t<typename T::type>*){}>; // expected-error {{redefinition with different types}}
+} // namespace TempArg2
+
+namespace ConstTempParam1 {
+  // FIXME: Create a disambiguation rule for this.
+  //        Maybe: If one candidate has functional types in the signature, the
+  //        other does not, the former is more specialized.
+  template<class T, T N> struct Test0; // expected-note {{declared here}}
+  template<class T, alias<T, typename T::type> N>
+    struct Test0<T, N>;
+  // expected-error at -1 {{not more specialized than the primary template}}
+} // namespace ConstTempParam1
+
+namespace NestedClassSpec1 {
+  template<class T> struct A {
+    template<class> struct Cls;
+    template<class U> struct Cls<alias<U, typename U::type>> {};
+    template<class U> struct Cls<alias<U, typename U::bar>> {};
+  };
+} // nestedClassSpec1
+
+namespace NestedClassSpec2 {
+  template<class T> struct A {
+    template<class> struct Cls;
+    template<class U> struct Cls<alias<U, void>> {};
+    // expected-error at -1 {{does not specialize any template argument}}
+    template<class U> struct Cls<alias<U, typename U::type>> {}; // expected-note {{previous definition is here}}
+    template<class U> struct Cls<alias<U, typename U::type>> {};
+    // expected-error at -1 {{redefinition}}
+  };
+} // nestedClassSpec2
+
+namespace NestedClassSpec3 {
+  template<template<class...> class TT> struct A {
+    template<class,class> struct Cls;
+    template<class T> struct Cls<int, TT<T, typename T::type>> {};
+    template<class T> struct Cls<int, TT<T, typename T::bar>> {};
+  };
+  template struct A<alias>;
+} // nestedClassSpec3
+
+namespace NestedVarSpec1 {
+  template<class T> struct A {
+    template<class> static int Var;
+    template<class U> static int Var<alias<U, typename U::type>>;
+    template<class U> static int Var<alias<U, typename U::bar>>;
+  };
+} // namespace NestedVarSpec1
+
+namespace NestedVarSpec2 {
+  template<class T> struct A {
+    template<class> static int Var;
+    template<class U> static int Var<alias<U, void>>;
+    // expected-error at -1 {{does not specialize any template argument}}
+    template<class U> static int Var<alias<U, typename U::type>>; // expected-note {{previous declaration is here}}
+    template<class U> static int Var<alias<U, typename U::type>>;
+    // expected-error at -1 {{duplicate member 'Var'}}
+  };
+} // namespace NestedVarSpec2
+
+namespace NestedVarSpec3 {
+  template<template<class...> class TT> struct A {
+    template<class,class> static int Var;
+    template<class T> static int Var<int, TT<T, typename T::type>>;
+    template<class T> static int Var<int, TT<T, typename T::bar>>;
+  };
+  template struct A<alias>;
+} // nestedClassSpec3
+
+namespace VarRedecl1 {
+  // FIXME: This should not be a valid redeclaration.
+  template<class T> extern void *x;
+  template<class T> extern void_t<T> *x;
+} // namespace VarRedecl1
+
+namespace VarRedecl2 {
+  template<class T> extern void_t<T> *x;
+  template<class T> extern void_t<T> *x;
+} // namespace VarRedecl2
+
+namespace VarRedecl3 {
+  // FIXME: This should not be a valid redeclaration.
+  template<class T> extern void_t<T, typename T::foo> *x;
+  template<class T> extern void_t<T, typename T::bar> *x;
+} // namespace VarRedecl3
+
+namespace FuncParamDecay1 {
+  template<int N> constexpr int f(char *) { return 1; }
+  template<int N> constexpr int f(char[N]) = delete;
+  static_assert(f<0>(nullptr) == 1);
+} // namespace FuncParamDecay1
+
+namespace ArrayQualifiers1 {
+  template<class T, class...> using alias = const int[5];
+  template<class T> void f() {
+    using X = const alias<T>;
+    using X = alias<T>;
+  };
+} // namespace ArrayQualifiers1
+
+namespace ArrayQualifiers2 {
+  template<class T, class...> using alias = const int;
+  template<class T> void f() {
+    using X = const alias<T>[5];
+    using X = alias<T>[5];
+  };
+} // namespace ArrayQualifiers2
+
+namespace TempTempParam {
+  template<class T, template<void*> class>      void f() {}
+  template<class T, template<void_t<T>*> class> void f() {}
+
+  template<void*> struct A;
+
+  template<class, class> struct B;
+  template<class T, void* V>      struct B<T, A<V>> {};
+  template<class T, void_t<T>* V> struct B<T, A<V>> {};
+
+  template<class,class> struct C;
+  template<class T, template<void*> class TT>      struct C<T, TT<nullptr>> {};
+  template<class T, template<void_t<T>*> class TT> struct C<T, TT<nullptr>> {};
+} // namespace TempTempParam
+
+
+namespace OverloadExpr1 {
+  template <class T> T &&declval();
+
+  int g(int);
+  template <class T> typename T::type g(T);
+
+  template <class T, class = void> struct X;
+
+  template <class T>
+  struct X<T, decltype((g(declval<T>()), void()))> {
+    static constexpr auto val = 1;
+  };
+
+  template <class T>
+  struct X<T, decltype((g<>(declval<T>()), void()))> {
+    static constexpr auto val = 2;
+  };
+
+  static_assert(X<int>::val == 1);
+} // namespace OverloadExpr1
+
+namespace NameQualifierUsing {
+  struct Base {
+    using type = int;
+  };
+  template <class T> struct S : Base {
+    using Base::type;
+
+    void f(S<alias<T, typename T::foo>>::type) {}
+    void f(S<alias<T, typename T::bar>>::type) {}
+  };
+} // namespace QualfierUsing
+
+namespace NameQualifierUnresolvedUsing {
+  template <class T> struct A : T {
+    using typename T::foo;
+    void f(foo) {}
+    void f(A<alias<T, typename T::bar>>::foo) {}
+    void f(A<alias<T, typename T::baz>>::foo) {}
+  };
+} // namespace NameQualifierUnresolvedUsing
+
+// FIXME: This is a flaky test
+#if 0
+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
+#endif
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_nontype_cxx1z.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp
index 9c25e26f43c36..f9460d976174a 100644
--- a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp
+++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp
@@ -406,14 +406,14 @@ namespace PR42362 {
 namespace QualConv {
   int *X;
   template<const int *const *P> void f() {
-    using T = decltype(P);
-    using T = const int* const*;
+    using T = decltype(P);       // expected-note  {{previous definition}}
+    using T = const int* const*; // expected-error {{redefinition with different types ('const int *const *' vs 'decltype(P)' (aka 'const int *const *'))}}
   }
   template void f<&X>();
 
   template<const int *const &R> void g() {
-    using T = decltype(R);
-    using T = const int *const &;
+    using T = decltype(R);        // expected-note  {{previous definition}}
+    using T = const int *const &; // expected-error {{redefinition with different types ('const int *const &' vs 'decltype(R)' (aka 'const int *const &'))}}
   }
   template void g<(const int *const&)X>();
 }
@@ -421,15 +421,17 @@ namespace QualConv {
 namespace FunctionConversion {
   struct a { void c(char *) noexcept; };
   template<void (a::*f)(char*)> void g() {
-    using T = decltype(f);
+    using T = decltype(f);        // expected-note  {{previous definition}}
     using T = void (a::*)(char*); // (not 'noexcept')
+    // expected-error at -1 {{redefinition with different types ('void (a::*)(char *)' vs 'decltype(f)' (aka 'void (a::*)(char *)'))}}
   }
   template void g<&a::c>();
 
   void c() noexcept;
   template<void (*p)()> void h() {
-    using T = decltype(p);
+    using T = decltype(p);// expected-note  {{previous definition}}
     using T = void (*)(); // (not 'noexcept')
+    // expected-error at -1 {{redefinition with different types ('void (*)()' vs 'decltype(p)' (aka 'void (*)()'))}}
   }
   template void h<&c>();
 }
@@ -437,8 +439,8 @@ namespace FunctionConversion {
 namespace VoidPtr {
   // Note, this is an extension in C++17 but valid in C++20.
   template<void *P> void f() {
-    using T = decltype(P);
-    using T = void*;
+    using T = decltype(P); // expected-note  {{previous definition}}
+    using T = void*;       // expected-error {{redefinition with different types ('void *' vs 'decltype(P)' (aka 'void *'))}}
   }
   int n;
   template void f<(void*)&n>();
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 at -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 at -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 {
diff --git a/clang/unittests/AST/TypePrinterTest.cpp b/clang/unittests/AST/TypePrinterTest.cpp
index 5023b5e093ec3..7af9fc8f83d7a 100644
--- a/clang/unittests/AST/TypePrinterTest.cpp
+++ b/clang/unittests/AST/TypePrinterTest.cpp
@@ -254,7 +254,7 @@ TEST(TypePrinter, TemplateArgumentsSubstitution_Expressions) {
     const int Result = 42;
     auto *ConstExpr = createBinOpExpr(LHS, RHS, Result);
     // Arg is instantiated with '40 + 2'
-    TemplateArgument Arg(ConstExpr, /*IsCanonical=*/false);
+    TemplateArgument Arg(ConstExpr, /*CanonKind=*/std::nullopt);
 
     // Param has default expr of '42'
     auto const *Param = Params->getParam(1);
@@ -270,7 +270,7 @@ TEST(TypePrinter, TemplateArgumentsSubstitution_Expressions) {
     auto *ConstExpr = createBinOpExpr(LHS, RHS, Result);
 
     // Arg is instantiated with '40 + 1'
-    TemplateArgument Arg(ConstExpr, /*IsCanonical=*/false);
+    TemplateArgument Arg(ConstExpr, /*CanonKind=*/std::nullopt);
 
     // Param has default expr of '42'
     auto const *Param = Params->getParam(1);
@@ -286,7 +286,7 @@ TEST(TypePrinter, TemplateArgumentsSubstitution_Expressions) {
     auto *ConstExpr = createBinOpExpr(LHS, RHS, Result);
 
     // Arg is instantiated with '4 + 0'
-    TemplateArgument Arg(ConstExpr, /*IsCanonical=*/false);
+    TemplateArgument Arg(ConstExpr, /*CanonKind=*/std::nullopt);
 
     // Param has is value-dependent expression (i.e., sizeof(T))
     auto const *Param = Params->getParam(3);
diff --git a/lldb/unittests/Symbol/TestTypeSystemClang.cpp b/lldb/unittests/Symbol/TestTypeSystemClang.cpp
index 2b5c8f6e14048..1abfd84223cb3 100644
--- a/lldb/unittests/Symbol/TestTypeSystemClang.cpp
+++ b/lldb/unittests/Symbol/TestTypeSystemClang.cpp
@@ -1022,7 +1022,8 @@ static QualType makeConstInt(clang::ASTContext &ctxt) {
 TEST_F(TestTypeSystemClang, TestGetTypeClassDeclType) {
   clang::ASTContext &ctxt = m_ast->getASTContext();
   auto *nullptr_expr = new (ctxt) CXXNullPtrLiteralExpr(ctxt.NullPtrTy, SourceLocation());
-  QualType t = ctxt.getDecltypeType(nullptr_expr, makeConstInt(ctxt));
+  QualType t = ctxt.getDecltypeType(nullptr_expr, makeConstInt(ctxt),
+                                    /*ExprCanonKind=*/std::nullopt);
   EXPECT_EQ(lldb::eTypeClassBuiltin, m_ast->GetTypeClass(t.getAsOpaquePtr()));
 }
 



More information about the cfe-commits mailing list