[llvm-branch-commits] [clang] [clang-tools-extra] [lldb] [clang] Template Specialization Resugaring - TypeDecl (PR #132441)

Matheus Izvekov via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Fri Mar 21 11:43:37 PDT 2025


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

>From 4571fada1ea055a845bf5c4eb3d1a20904f768c6 Mon Sep 17 00:00:00 2001
From: Matheus Izvekov <mizvekov at gmail.com>
Date: Mon, 30 May 2022 01:46:31 +0200
Subject: [PATCH] [clang] Template Specialization Resugaring - TypeDecl

This is the introductory patch for a larger work which
intends to solve the long standing C++ issue with losing
type sugar when acessing template specializations.

The well known example here is specializing a template
with `std::string`, but getting diagnostics related to
`std::basic_string<char>` instead.

This implements a transform which, upon member access,
propagates type sugar from the naming context into the
accessed entity.

It also implements a single use of this transform,
resugaring access to TypeDecls.

For more details and discussion see:
https://discourse.llvm.org/t/rfc-improving-diagnostics-with-template-specialization-resugaring/64294

This is ready for review, although maybe not finished and
there is some more stuff that could be done either here
or in follow ups.

* Its worth exploring if a global resugaring cache is
  worthwhile, besides the current operational cache.
  A global cache would be more expensive to index, so there
  is a tradeoff, and maybe should be used of the whole
  result of the operation, while keeping the existing
  cache for sub-results.
* It would be ideal if the transform could live in ASTContext
  instead of Sema. There are a few dependencies that would
  have to be tackled.
  * Template arguments deduced for partial specializations.
  * Some kinds of type adjustments currently require Sema.

Differential Revision: https://reviews.llvm.org/D127695
---
 .../readability/QualifiedAutoCheck.cpp        |   2 +-
 clang/include/clang/AST/ASTContext.h          |  13 +-
 clang/include/clang/AST/Type.h                |  11 +-
 clang/include/clang/Sema/Sema.h               |   8 +-
 clang/lib/AST/ASTContext.cpp                  |  17 +-
 clang/lib/Sema/SemaCXXScopeSpec.cpp           |   5 +-
 clang/lib/Sema/SemaCoroutine.cpp              |   1 +
 clang/lib/Sema/SemaDecl.cpp                   |   9 +-
 clang/lib/Sema/SemaDeclCXX.cpp                |   2 +-
 clang/lib/Sema/SemaTemplate.cpp               | 722 +++++++++++++++++-
 clang/lib/Sema/SemaTemplateDeduction.cpp      |   6 +-
 clang/lib/Sema/SemaType.cpp                   |  10 +-
 ...openmp-begin-declare-variant_reference.cpp |   8 +-
 clang/test/AST/ast-dump-template-name.cpp     |   7 +-
 clang/test/CXX/temp/temp.param/p15-cxx0x.cpp  |   4 +-
 clang/test/Sema/Resugar/resugar-types.cpp     | 209 +++++
 .../iterator/TestIteratorFromStdModule.py     |   6 +-
 17 files changed, 989 insertions(+), 51 deletions(-)
 create mode 100644 clang/test/Sema/Resugar/resugar-types.cpp

diff --git a/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp b/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp
index e843c593a92cc..679fbd75d2479 100644
--- a/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp
@@ -126,7 +126,7 @@ void QualifiedAutoCheck::registerMatchers(MatchFinder *Finder) {
   auto UnlessFunctionType = unless(hasUnqualifiedDesugaredType(functionType()));
   auto IsAutoDeducedToPointer = [](const auto &...InnerMatchers) {
     return autoType(hasDeducedType(
-        hasUnqualifiedDesugaredType(pointerType(pointee(InnerMatchers...)))));
+        hasCanonicalType(pointerType(pointee(InnerMatchers...)))));
   };
 
   Finder->addMatcher(
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index ef596f99262be..23d789d8466d3 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1319,8 +1319,6 @@ class ASTContext : public RefCountedBase<ASTContext> {
 
   QualType getTypeDeclTypeSlow(const TypeDecl *Decl) const;
 
-  QualType getPipeType(QualType T, bool ReadOnly) const;
-
 public:
   /// Return the uniqued reference to the type for an address space
   /// qualified type with the specified type and address space.
@@ -1500,6 +1498,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// blocks.
   QualType getBlockDescriptorType() const;
 
+  // Return a pipe type for the specified type.
+  QualType getPipeType(QualType T, bool ReadOnly) const;
+
   /// Return a read_only pipe type for the specified type.
   QualType getReadPipeType(QualType T) const;
 
@@ -1901,10 +1902,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// C++11 decltype.
   QualType getDecltypeType(Expr *e, QualType UnderlyingType) const;
 
-  QualType getPackIndexingType(QualType Pattern, Expr *IndexExpr,
-                               bool FullySubstituted = false,
-                               ArrayRef<QualType> Expansions = {},
-                               int Index = -1) const;
+  QualType getPackIndexingType(
+      QualType Pattern, Expr *IndexExpr, bool FullySubstituted = false,
+      ArrayRef<QualType> Expansions = {},
+      std::optional<unsigned> SelectedIndex = std::nullopt) const;
 
   /// Unary type transforms
   QualType getUnaryTransformType(QualType BaseType, QualType UnderlyingType,
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 642881f185c07..56ff0f853a799 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -643,10 +643,10 @@ class Qualifiers {
   void addQualifiers(Qualifiers Q) {
     // If the other set doesn't have any non-boolean qualifiers, just
     // bit-or it in.
-    if (!(Q.Mask & ~CVRMask))
+    if (!(Q.Mask & ~CVRUMask))
       Mask |= Q.Mask;
     else {
-      Mask |= (Q.Mask & CVRMask);
+      Mask |= (Q.Mask & CVRUMask);
       if (Q.hasAddressSpace())
         addAddressSpace(Q.getAddressSpace());
       if (Q.hasObjCGCAttr())
@@ -662,10 +662,10 @@ class Qualifiers {
   void removeQualifiers(Qualifiers Q) {
     // If the other set doesn't have any non-boolean qualifiers, just
     // bit-and the inverse in.
-    if (!(Q.Mask & ~CVRMask))
+    if (!(Q.Mask & ~CVRUMask))
       Mask &= ~Q.Mask;
     else {
-      Mask &= ~(Q.Mask & CVRMask);
+      Mask &= ~(Q.Mask & CVRUMask);
       if (getObjCGCAttr() == Q.getObjCGCAttr())
         removeObjCGCAttr();
       if (getObjCLifetime() == Q.getObjCLifetime())
@@ -805,12 +805,13 @@ class Qualifiers {
 
   static constexpr uint64_t UMask = 0x8;
   static constexpr uint64_t UShift = 3;
+  static constexpr uint64_t CVRUMask = CVRMask | UMask;
   static constexpr uint64_t GCAttrMask = 0x30;
   static constexpr uint64_t GCAttrShift = 4;
   static constexpr uint64_t LifetimeMask = 0x1C0;
   static constexpr uint64_t LifetimeShift = 6;
   static constexpr uint64_t AddressSpaceMask =
-      ~(CVRMask | UMask | GCAttrMask | LifetimeMask);
+      ~(CVRUMask | GCAttrMask | LifetimeMask);
   static constexpr uint64_t AddressSpaceShift = 9;
   static constexpr uint64_t PtrAuthShift = 32;
   static constexpr uint64_t PtrAuthMask = uint64_t(0xffffffff) << PtrAuthShift;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index e215f07e2bf0a..047ba88511dac 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -3193,7 +3193,8 @@ class Sema final : public SemaBase {
   /// Returns the TypeDeclType for the given type declaration,
   /// as ASTContext::getTypeDeclType would, but
   /// performs the required semantic checks for name lookup of said entity.
-  QualType getTypeDeclType(DeclContext *LookupCtx, DiagCtorKind DCK,
+  QualType getTypeDeclType(const NestedNameSpecifier *NNS,
+                           DeclContext *LookupCtx, DiagCtorKind DCK,
                            TypeDecl *TD, SourceLocation NameLoc);
 
   /// If the identifier refers to a type name within this scope,
@@ -13980,6 +13981,8 @@ class Sema final : public SemaBase {
   FunctionDecl *SubstSpaceshipAsEqualEqual(CXXRecordDecl *RD,
                                            FunctionDecl *Spaceship);
 
+  QualType resugar(const NestedNameSpecifier *NNS, QualType T);
+
   /// Performs template instantiation for all implicit template
   /// instantiations we have seen until this point.
   void PerformPendingInstantiations(bool LocalOnly = false,
@@ -14972,7 +14975,8 @@ class Sema final : public SemaBase {
   /// wasn't specified explicitly.  This handles method types formed from
   /// function type typedefs and typename template arguments.
   void adjustMemberFunctionCC(QualType &T, bool HasThisPointer,
-                              bool IsCtorOrDtor, SourceLocation Loc);
+                              bool IsCtorOrDtor, bool IsDeduced,
+                              SourceLocation Loc);
 
   // Check if there is an explicit attribute, but only look through parens.
   // The intent is to look for an attribute on the current declarator, but not
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 7a44fd4a5dd0e..79bdac607cc2a 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -6385,13 +6385,14 @@ QualType ASTContext::getDecltypeType(Expr *e, QualType UnderlyingType) const {
   return QualType(dt, 0);
 }
 
-QualType ASTContext::getPackIndexingType(QualType Pattern, Expr *IndexExpr,
-                                         bool FullySubstituted,
-                                         ArrayRef<QualType> Expansions,
-                                         int Index) const {
+QualType
+ASTContext::getPackIndexingType(QualType Pattern, Expr *IndexExpr,
+                                bool FullySubstituted,
+                                ArrayRef<QualType> Expansions,
+                                std::optional<unsigned> SelectedIndex) const {
   QualType Canonical;
-  if (FullySubstituted && Index != -1) {
-    Canonical = getCanonicalType(Expansions[Index]);
+  if (FullySubstituted && SelectedIndex) {
+    Canonical = getCanonicalType(Expansions[*SelectedIndex]);
   } else {
     llvm::FoldingSetNodeID ID;
     PackIndexingType::Profile(ID, *this, Pattern.getCanonicalType(), IndexExpr,
@@ -14103,9 +14104,7 @@ static QualType getCommonNonSugarTypeNode(ASTContext &Ctx, const Type *X,
   case Type::Pipe: {
     const auto *PX = cast<PipeType>(X), *PY = cast<PipeType>(Y);
     assert(PX->isReadOnly() == PY->isReadOnly());
-    auto MP = PX->isReadOnly() ? &ASTContext::getReadPipeType
-                               : &ASTContext::getWritePipeType;
-    return (Ctx.*MP)(getCommonElementType(Ctx, PX, PY));
+    return Ctx.getPipeType(getCommonElementType(Ctx, PX, PY), PX->isReadOnly());
   }
   case Type::TemplateTypeParm: {
     const auto *TX = cast<TemplateTypeParmType>(X),
diff --git a/clang/lib/Sema/SemaCXXScopeSpec.cpp b/clang/lib/Sema/SemaCXXScopeSpec.cpp
index 02cd699addd75..e8fe1cdd7336a 100644
--- a/clang/lib/Sema/SemaCXXScopeSpec.cpp
+++ b/clang/lib/Sema/SemaCXXScopeSpec.cpp
@@ -644,8 +644,9 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
       return false;
     }
 
-    QualType T =
-        Context.getTypeDeclType(cast<TypeDecl>(SD->getUnderlyingDecl()));
+    QualType T = resugar(
+        SS.getScopeRep(),
+        Context.getTypeDeclType(cast<TypeDecl>(SD->getUnderlyingDecl())));
 
     if (T->isEnumeralType())
       Diag(IdInfo.IdentifierLoc, diag::warn_cxx98_compat_enum_nested_name_spec);
diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp
index 53536b0d14037..88d849b27db07 100644
--- a/clang/lib/Sema/SemaCoroutine.cpp
+++ b/clang/lib/Sema/SemaCoroutine.cpp
@@ -113,6 +113,7 @@ static QualType lookupPromiseType(Sema &S, const FunctionDecl *FD,
   }
   // The promise type is required to be a class type.
   QualType PromiseType = S.Context.getTypeDeclType(Promise);
+  // FIXME: resugar PromiseType.
 
   auto buildElaboratedType = [&]() {
     auto *NNS = NestedNameSpecifier::Create(S.Context, nullptr, S.getStdNamespace());
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 043e82414c052..aded29a9daf6d 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -139,7 +139,8 @@ class TypeNameValidatorCCC final : public CorrectionCandidateCallback {
 
 } // end anonymous namespace
 
-QualType Sema::getTypeDeclType(DeclContext *LookupCtx, DiagCtorKind DCK,
+QualType Sema::getTypeDeclType(const NestedNameSpecifier *NNS,
+                               DeclContext *LookupCtx, DiagCtorKind DCK,
                                TypeDecl *TD, SourceLocation NameLoc) {
   auto *LookupRD = dyn_cast_or_null<CXXRecordDecl>(LookupCtx);
   auto *FoundRD = dyn_cast<CXXRecordDecl>(TD);
@@ -156,7 +157,7 @@ QualType Sema::getTypeDeclType(DeclContext *LookupCtx, DiagCtorKind DCK,
 
   DiagnoseUseOfDecl(TD, NameLoc);
   MarkAnyDeclReferenced(TD->getLocation(), TD, /*OdrUse=*/false);
-  return Context.getTypeDeclType(TD);
+  return resugar(NNS, Context.getTypeDeclType(TD));
 }
 
 namespace {
@@ -534,7 +535,7 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc,
     // C++ [class.qual]p2: A lookup that would find the injected-class-name
     // instead names the constructors of the class, except when naming a class.
     // This is ill-formed when we're not actually forming a ctor or dtor name.
-    T = getTypeDeclType(LookupCtx,
+    T = getTypeDeclType(SS ? SS->getScopeRep() : nullptr, LookupCtx,
                         IsImplicitTypename ? DiagCtorKind::Implicit
                                            : DiagCtorKind::None,
                         TD, NameLoc);
@@ -9872,7 +9873,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
   if (D.isFirstDeclarationOfMember())
     adjustMemberFunctionCC(
         R, !(D.isStaticMember() || D.isExplicitObjectMemberFunction()),
-        D.isCtorOrDtor(), D.getIdentifierLoc());
+        D.isCtorOrDtor(), /*IsDeduced=*/false, D.getIdentifierLoc());
 
   bool isFriend = false;
   FunctionTemplateDecl *FunctionTemplate = nullptr;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index c90ef04bbbe0a..330afbfa2d19d 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -1248,7 +1248,7 @@ static QualType getTupleLikeElementType(Sema &S, SourceLocation Loc,
       S.Diag(R.getRepresentativeDecl()->getLocation(), diag::note_declared_at);
     return QualType();
   }
-
+  // FIXME: resugar
   return S.Context.getTypeDeclType(TD);
 }
 
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 7fe38402300a9..0b26f79ea3bfc 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -18,6 +18,7 @@
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/TemplateName.h"
+#include "clang/AST/TypeOrdering.h"
 #include "clang/AST/TypeVisitor.h"
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/DiagnosticSema.h"
@@ -93,6 +94,725 @@ unsigned Sema::getTemplateDepth(Scope *S) const {
   return Depth;
 }
 
+namespace {
+
+class NameMap {
+  llvm::DenseMap<const Decl *, ArrayRef<TemplateArgument>> Map;
+
+  void insert(const Decl *AssociatedDecl, ArrayRef<TemplateArgument> Args) {
+    assert(!Args.empty());
+    Map.try_emplace(AssociatedDecl->getCanonicalDecl(), Args);
+  }
+
+public:
+  NameMap() = default;
+
+  const TemplateArgument *getArgument(const Decl *AssociatedDecl,
+                                      unsigned Index,
+                                      std::optional<unsigned> PackIndex) const {
+    auto It = Map.find(AssociatedDecl);
+    if (It == Map.end())
+      return nullptr;
+    ArrayRef<TemplateArgument> Args = It->second;
+    assert(Index < Args.size());
+    const TemplateArgument &Arg = Args[Index];
+    if (!PackIndex)
+      return &Arg;
+    ArrayRef<TemplateArgument> PackArgs = Arg.getPackAsArray();
+    assert(*PackIndex < PackArgs.size());
+    return &PackArgs[PackArgs.size() - 1 - *PackIndex];
+  }
+
+  void insert(Sema &SemaRef, const Type *T) {
+    const Type *CanonT = T->getCanonicalTypeInternal().getTypePtr();
+    if (auto TC = CanonT->getTypeClass(); TC != Type::Record) {
+      assert(TC == Type::Enum || TC == Type::InjectedClassName ||
+             T->isDependentType());
+      return;
+    }
+    const auto *TS = T->getAsNonAliasTemplateSpecializationType();
+    if (!TS)
+      return;
+    auto *CTSD = cast<ClassTemplateSpecializationDecl>(
+        cast<RecordType>(CanonT)->getDecl());
+    auto PU = CTSD->getInstantiatedFrom();
+    if (PU.isNull())
+      return;
+
+    ArrayRef<TemplateArgument> Args = TS->getConvertedArguments();
+    auto *CTPSD = PU.dyn_cast<ClassTemplatePartialSpecializationDecl *>();
+    if (!CTPSD)
+      return insert(CTSD, Args);
+    // FIXME: Don't deduce partial specialization args on resugaring.
+    TemplateParameterList *TPL = CTPSD->getTemplateParameters();
+    TemplateDeductionInfo Info(SourceLocation(), TPL->getDepth());
+    [[maybe_unused]] TemplateDeductionResult Res =
+        SemaRef.DeduceTemplateArguments(CTPSD, Args, Info);
+    assert(Res == TemplateDeductionResult::Success);
+    insert(CTSD, Info.takeSugared()->asArray());
+  }
+
+  void insert(Sema &SemaRef, const NestedNameSpecifier *NNS) {
+    for (/**/; NNS; NNS = NNS->getPrefix()) {
+      switch (NNS->getKind()) {
+      case NestedNameSpecifier::Global:
+      case NestedNameSpecifier::Namespace:
+      case NestedNameSpecifier::NamespaceAlias:
+      case NestedNameSpecifier::Super:
+        return;
+      case NestedNameSpecifier::Identifier:
+        continue;
+      case NestedNameSpecifier::TypeSpec:
+      case NestedNameSpecifier::TypeSpecWithTemplate:
+        insert(SemaRef, NNS->getAsType());
+        continue;
+      }
+      llvm_unreachable("Unknown NestedNameSpecifier Kind");
+    }
+  }
+
+  bool empty() const { return Map.empty(); }
+};
+
+class Resugarer {
+  Sema &SemaRef;
+  const NameMap *Names;
+  llvm::DenseMap<QualType, QualType> CacheTypes;
+
+public:
+  Resugarer(Sema &SemaRef, const NameMap &Names)
+      : SemaRef(SemaRef), Names(&Names) {}
+
+  template <class T>
+  SmallVector<T, 4> transform(ArrayRef<T> Es, bool &Changed) {
+    SmallVector<T, 4> TransformedEs(Es);
+    for (auto &E : TransformedEs)
+      E = transform(E, Changed);
+    return TransformedEs;
+  }
+
+  NestedNameSpecifier *transform(NestedNameSpecifier *NNS, bool &OutChanged) {
+    if (!NNS)
+      return NNS;
+
+    bool Changed = false;
+    switch (auto K = NNS->getKind()) {
+    case NestedNameSpecifier::Global:
+    case NestedNameSpecifier::Namespace:
+    case NestedNameSpecifier::NamespaceAlias:
+    case NestedNameSpecifier::Super:
+      return NNS;
+    case NestedNameSpecifier::Identifier: {
+      NestedNameSpecifier *Prefix = transform(NNS->getPrefix(), Changed);
+      if (!Changed)
+        return NNS;
+      OutChanged = true;
+      return NestedNameSpecifier::Create(SemaRef.Context, Prefix,
+                                         NNS->getAsIdentifier());
+    }
+    case NestedNameSpecifier::TypeSpec:
+    case NestedNameSpecifier::TypeSpecWithTemplate: {
+      const Type *T =
+          transform(QualType(NNS->getAsType(), 0), Changed).getTypePtr();
+      NestedNameSpecifier *Prefix;
+      if (const auto *ET = dyn_cast<ElaboratedType>(T)) {
+        Prefix = transform(ET->getQualifier(), Changed);
+        T = ET->getNamedType().getTypePtr();
+      } else {
+        Prefix = transform(NNS->getPrefix(), Changed);
+      }
+      if (!Changed)
+        return NNS;
+      OutChanged = true;
+      return NestedNameSpecifier::Create(
+          SemaRef.Context, Prefix,
+          K == NestedNameSpecifier::TypeSpecWithTemplate, T);
+    }
+    }
+    llvm_unreachable("Unknown NestedNameSpecifier Kind");
+  }
+
+  TemplateName transform(TemplateName TN, bool &OutChanged) {
+    auto build = [&](TemplateName NewTN) {
+      assert(SemaRef.Context.hasSameTemplateName(TN, NewTN));
+      OutChanged = true;
+      return NewTN;
+    };
+
+    bool Changed = false;
+    switch (TN.getKind()) {
+    case TemplateName::AssumedTemplate:
+    case TemplateName::OverloadedTemplate:
+    case TemplateName::Template:
+    case TemplateName::UsingTemplate:
+      return TN;
+    case TemplateName::DeducedTemplate: {
+      const auto *DTN = TN.getAsDeducedTemplateName();
+      TemplateName Underlying = transform(DTN->getUnderlying(), Changed);
+      DefaultArguments DefaultArgs = DTN->getDefaultArguments();
+      auto Args = transform(DefaultArgs.Args, Changed);
+      DefaultArgs.Args = Args;
+      if (!Changed)
+        return TN;
+      return build(
+          SemaRef.Context.getDeducedTemplateName(Underlying, DefaultArgs));
+    }
+    case TemplateName::DependentTemplate: {
+      const auto *DTN = TN.getAsDependentTemplateName();
+      NestedNameSpecifier *NNS = transform(DTN->getQualifier(), Changed);
+      if (!Changed)
+        return TN;
+      return build(
+          SemaRef.Context.getDependentTemplateName(NNS, DTN->getOperator()));
+    }
+    case TemplateName::QualifiedTemplate: {
+      const auto *QTN = TN.getAsQualifiedTemplateName();
+      NestedNameSpecifier *NNS = transform(QTN->getQualifier(), Changed);
+      TemplateName UTN = transform(QTN->getUnderlyingTemplate(), Changed);
+      if (!Changed)
+        return TN;
+      return build(SemaRef.Context.getQualifiedTemplateName(
+          NNS, QTN->hasTemplateKeyword(), UTN));
+    }
+    case TemplateName::SubstTemplateTemplateParm: {
+      const auto *STN = TN.getAsSubstTemplateTemplateParm();
+      const TemplateArgument *Arg = Names->getArgument(
+          STN->getAssociatedDecl(), STN->getIndex(), STN->getPackIndex());
+      if (!Arg)
+        return TN;
+      return build(Arg->getAsTemplate());
+    }
+    case TemplateName::SubstTemplateTemplateParmPack: {
+      const auto *STNP = TN.getAsSubstTemplateTemplateParmPack();
+      TemplateArgument Pack = transform(STNP->getArgumentPack(), Changed);
+      if (!Changed)
+        return TN;
+      return build(SemaRef.Context.getSubstTemplateTemplateParmPack(
+          Pack, STNP->getAssociatedDecl(), STNP->getIndex(), STNP->getFinal()));
+    }
+    }
+    llvm_unreachable("Unhandled TemplateName kind");
+  }
+
+  QualType buildType(QualType Orig, const Type *Ty, Qualifiers Quals) {
+    QualType NewT = SemaRef.Context.getQualifiedType(Ty, Quals);
+    assert(SemaRef.Context.hasSameType(Orig, NewT));
+    CacheTypes.find(Orig)->second = NewT;
+    return NewT;
+  }
+
+  QualType transform(QualType TT, bool &OutChanged) {
+    if (TT.isNull() || TT.isCanonical())
+      return TT;
+
+    if (auto [It, Created] = CacheTypes.try_emplace(TT, TT); !Created) {
+      QualType NewT = It->second;
+      OutChanged |= (NewT != TT);
+      return NewT;
+    }
+
+    SplitQualType ST = TT.split();
+    auto build = [&](QualType T) {
+      OutChanged = true;
+      return buildType(TT, T.getTypePtr(), ST.Quals);
+    };
+
+    bool Changed = false;
+    switch (ST.Ty->getTypeClass()) {
+    case Type::Adjusted: {
+      const auto *T = cast<AdjustedType>(ST.Ty);
+      QualType OT = transform(T->getOriginalType(), Changed);
+      // FIXME: Handle AdjustedType.
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getAdjustedType(OT, T->getAdjustedType()));
+    }
+    case Type::Atomic: {
+      const auto *T = cast<AtomicType>(ST.Ty);
+      QualType VT = transform(T->getValueType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getAtomicType(VT));
+    }
+    case Type::Attributed: {
+      const auto *T = cast<AttributedType>(ST.Ty);
+      QualType MT = transform(T->getModifiedType(), Changed);
+      // FIXME: Handle EquivalentType.
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getAttributedType(T->getAttrKind(), MT,
+                                                     T->getEquivalentType()));
+    }
+    case Type::Auto: {
+      const auto *T = cast<AutoType>(ST.Ty);
+      auto Args = transform(T->getTypeConstraintArguments(), Changed);
+      QualType DT = transform(T->getDeducedType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(
+          SemaRef.Context.getAutoType(DT, T->getKeyword(), DT.isNull(),
+                                      T->containsUnexpandedParameterPack(),
+                                      T->getTypeConstraintConcept(), Args));
+    }
+    case Type::BitInt:
+      return TT;
+    case Type::BlockPointer: {
+      const auto *T = cast<BlockPointerType>(ST.Ty);
+      QualType PT = transform(T->getPointeeType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getBlockPointerType(PT));
+    }
+    case Type::Builtin:
+      return TT;
+    case Type::BTFTagAttributed: {
+      const auto *T = cast<BTFTagAttributedType>(ST.Ty);
+      QualType WT = transform(T->getWrappedType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getBTFTagAttributedType(T->getAttr(), WT));
+    }
+    case Type::Complex: {
+      const auto *T = cast<ComplexType>(ST.Ty);
+      QualType ET = transform(T->getElementType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getComplexType(ET));
+    }
+    case Type::ConstantArray: {
+      const auto *T = cast<ConstantArrayType>(ST.Ty);
+      QualType ET = transform(T->getElementType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getConstantArrayType(
+          ET, T->getSize(), T->getSizeExpr(), T->getSizeModifier(),
+          T->getIndexTypeCVRQualifiers()));
+    }
+    case Type::ConstantMatrix: {
+      const auto *T = cast<ConstantMatrixType>(ST.Ty);
+      QualType ET = transform(T->getElementType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getConstantMatrixType(ET, T->getNumRows(),
+                                                         T->getNumColumns()));
+    }
+    case Type::Decltype: {
+      const auto *T = cast<DecltypeType>(ST.Ty);
+      QualType UT = transform(T->getUnderlyingType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getDecltypeType(T->getUnderlyingExpr(), UT));
+    }
+    case Type::Decayed: {
+      const auto *T = cast<DecayedType>(ST.Ty);
+      QualType OT = transform(T->getOriginalType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getDecayedType(OT));
+    }
+    case Type::DeducedTemplateSpecialization: {
+      const auto *T = cast<DeducedTemplateSpecializationType>(ST.Ty);
+      TemplateName TN = transform(T->getTemplateName(), Changed);
+      QualType DT = transform(T->getDeducedType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getDeducedTemplateSpecializationType(
+          TN, DT, DT.isNull()));
+    }
+    case Type::DependentAddressSpace: {
+      const auto *T = cast<DependentAddressSpaceType>(ST.Ty);
+      QualType PT = transform(T->getPointeeType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getDependentAddressSpaceType(
+          PT, T->getAddrSpaceExpr(), T->getAttributeLoc()));
+    }
+    case Type::DependentBitInt:
+      return TT;
+    case Type::DependentName: {
+      const auto *T = cast<DependentNameType>(ST.Ty);
+      NestedNameSpecifier *NNS = transform(T->getQualifier(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getDependentNameType(T->getKeyword(), NNS,
+                                                        T->getIdentifier()));
+    }
+    case Type::DependentSizedArray: {
+      const auto *T = cast<DependentSizedArrayType>(ST.Ty);
+      QualType ET = transform(T->getElementType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getDependentSizedArrayType(
+          ET, T->getSizeExpr(), T->getSizeModifier(),
+          T->getIndexTypeCVRQualifiers(), T->getBracketsRange()));
+    }
+    case Type::DependentSizedExtVector: {
+      const auto *T = cast<DependentSizedExtVectorType>(ST.Ty);
+      QualType ET = transform(T->getElementType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getDependentSizedExtVectorType(
+          ET, T->getSizeExpr(), T->getAttributeLoc()));
+    }
+    case Type::DependentSizedMatrix: {
+      const auto *T = cast<DependentSizedMatrixType>(ST.Ty);
+      QualType ET = transform(T->getElementType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getDependentSizedMatrixType(
+          ET, T->getRowExpr(), T->getColumnExpr(), T->getAttributeLoc()));
+    }
+    case Type::DependentVector: {
+      const auto *T = cast<DependentVectorType>(ST.Ty);
+      QualType ET = transform(T->getElementType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getDependentVectorType(
+          ET, T->getSizeExpr(), T->getAttributeLoc(), T->getVectorKind()));
+    }
+    case Type::DependentTemplateSpecialization: {
+      const auto *T = cast<DependentTemplateSpecializationType>(ST.Ty);
+      auto SpecArgs = transform(T->template_arguments(), Changed);
+      NestedNameSpecifier *NNS = transform(T->getQualifier(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getDependentTemplateSpecializationType(
+          T->getKeyword(), NNS, T->getIdentifier(), SpecArgs));
+    }
+    case Type::Elaborated: {
+      const auto *T = cast<ElaboratedType>(ST.Ty);
+      NestedNameSpecifier *NNS = transform(T->getQualifier(), Changed);
+      QualType NT = transform(T->getNamedType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getElaboratedType(T->getKeyword(), NNS, NT,
+                                                     T->getOwnedTagDecl()));
+    }
+    case Type::Enum:
+      // FIXME: Resugar.
+      return TT;
+    case Type::ExtVector: {
+      const auto *T = cast<ExtVectorType>(ST.Ty);
+      QualType ET = transform(T->getElementType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getExtVectorType(ET, T->getNumElements()));
+    }
+    case Type::FunctionNoProto: {
+      const auto *T = cast<FunctionNoProtoType>(ST.Ty);
+      QualType RT = transform(T->getReturnType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getFunctionNoProtoType(RT, T->getExtInfo()));
+    }
+    case Type::FunctionProto: {
+      const auto *T = cast<FunctionProtoType>(ST.Ty);
+      FunctionProtoType::ExtProtoInfo EPI = T->getExtProtoInfo();
+      QualType RT = transform(T->getReturnType(), Changed);
+      auto Ps = transform(T->param_types(), Changed);
+      auto Es = transform(EPI.ExceptionSpec.Exceptions, Changed);
+      if (!Changed)
+        return TT;
+      EPI.ExceptionSpec.Exceptions = Es;
+      return build(SemaRef.Context.getFunctionType(RT, Ps, EPI));
+    }
+    case Type::IncompleteArray: {
+      const auto *T = cast<IncompleteArrayType>(ST.Ty);
+      QualType ET = transform(T->getElementType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getIncompleteArrayType(
+          ET, T->getSizeModifier(), T->getIndexTypeCVRQualifiers()));
+    }
+    case Type::InjectedClassName: {
+      const auto *T = cast<InjectedClassNameType>(ST.Ty);
+      QualType TST = transform(T->getInjectedSpecializationType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getInjectedClassNameType(T->getDecl(), TST));
+    }
+    case Type::LValueReference: {
+      const auto *T = cast<LValueReferenceType>(ST.Ty);
+      QualType PT = transform(T->getPointeeTypeAsWritten(), Changed);
+      if (!Changed)
+        return TT;
+      return build(
+          SemaRef.Context.getLValueReferenceType(PT, T->isSpelledAsLValue()));
+    }
+    case Type::MacroQualified: {
+      const auto *T = cast<MacroQualifiedType>(ST.Ty);
+      QualType UT = transform(T->getUnderlyingType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(
+          SemaRef.Context.getMacroQualifiedType(UT, T->getMacroIdentifier()));
+    }
+    case Type::MemberPointer: {
+      const auto *T = cast<MemberPointerType>(ST.Ty);
+      NestedNameSpecifier *Qualifier = transform(T->getQualifier(), Changed);
+      QualType PT = transform(T->getPointeeType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getMemberPointerType(
+          PT, Qualifier, T->getMostRecentCXXRecordDecl()));
+    }
+    case Type::ObjCInterface:
+      return TT;
+    case Type::ObjCObject: {
+      const auto *T = cast<ObjCObjectType>(ST.Ty);
+      QualType BT = transform(T->getBaseType(), Changed);
+      auto Args = transform(T->getTypeArgs(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getObjCObjectType(
+          BT, Args, T->getProtocols(), T->isKindOfType()));
+    }
+    case Type::ObjCObjectPointer: {
+      const auto *T = cast<ObjCObjectPointerType>(ST.Ty);
+      QualType PT = transform(T->getPointeeType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getObjCObjectPointerType(PT));
+    }
+    case Type::ObjCTypeParam:
+      return TT;
+    case Type::PackExpansion: {
+      const auto *T = cast<PackExpansionType>(ST.Ty);
+      QualType P = transform(T->getPattern(), Changed);
+      if (!Changed)
+        return TT;
+      return build(
+          SemaRef.Context.getPackExpansionType(P, T->getNumExpansions()));
+    }
+    case Type::Paren: {
+      const auto *T = cast<ParenType>(ST.Ty);
+      QualType IT = transform(T->getInnerType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getParenType(IT));
+    }
+    case Type::Pipe: {
+      const auto *T = cast<PipeType>(ST.Ty);
+      QualType ET = transform(T->getElementType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getPipeType(ET, T->isReadOnly()));
+    }
+    case Type::Pointer: {
+      const auto *T = cast<PointerType>(ST.Ty);
+      QualType PT = transform(T->getPointeeType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getPointerType(PT));
+    }
+    case Type::Record:
+      return TT;
+    case Type::RValueReference: {
+      const auto *T = cast<RValueReferenceType>(ST.Ty);
+      QualType PT = transform(T->getPointeeTypeAsWritten(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getRValueReferenceType(PT));
+    }
+    case Type::SubstTemplateTypeParm: {
+      const auto *T = cast<SubstTemplateTypeParmType>(ST.Ty);
+      const auto *Arg = Names->getArgument(T->getAssociatedDecl(),
+                                           T->getIndex(), T->getPackIndex());
+      if (!Arg)
+        return TT;
+
+      SplitQualType Replacement = Arg->getAsType().split();
+      if (ST.Quals.hasObjCLifetime())
+        Replacement.Quals.removeObjCLifetime();
+      OutChanged = true;
+      return buildType(TT, Replacement.Ty, ST.Quals + Replacement.Quals);
+    }
+    case Type::SubstTemplateTypeParmPack: {
+      const auto *T = cast<SubstTemplateTypeParmPackType>(ST.Ty);
+      TemplateArgument P = transform(T->getArgumentPack(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getSubstTemplateTypeParmPackType(
+          T->getAssociatedDecl(), T->getIndex(), T->getFinal(), P));
+    }
+    case Type::TemplateTypeParm:
+      return TT;
+    case Type::TemplateSpecialization: {
+      const auto *T = cast<TemplateSpecializationType>(ST.Ty);
+      TemplateName TN = transform(T->getTemplateName(), Changed);
+      auto SpecArgs = transform(T->getSpecifiedArguments(), Changed);
+      auto ConvertedArgs = transform(T->getConvertedArguments(), Changed);
+      QualType UT = T->desugar();
+      if (T->isTypeAlias())
+        UT = transform(UT, Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getTemplateSpecializationType(
+          TN, SpecArgs, ConvertedArgs,
+          /*CanonicalConvertedArgs=*/std::nullopt, UT));
+    }
+    case Type::Typedef: {
+      const auto *T = cast<TypedefType>(ST.Ty);
+      QualType Underlying = transform(T->desugar(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getTypedefType(T->getDecl(), Underlying));
+    }
+    case Type::TypeOfExpr:
+      // FIXME: Resugar.
+      return TT;
+    case Type::TypeOf: {
+      const auto *T = cast<TypeOfType>(ST.Ty);
+      QualType UT = transform(T->getUnmodifiedType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getTypeOfType(UT, T->getKind()));
+    }
+    case Type::UnaryTransform: {
+      const auto *T = cast<UnaryTransformType>(ST.Ty);
+      QualType UT = transform(T->getUnderlyingType(), Changed);
+      if (!Changed)
+        return TT;
+      // FIXME: Handle BaseType.
+      return build(SemaRef.Context.getUnaryTransformType(T->getBaseType(), UT,
+                                                         T->getUTTKind()));
+    }
+    case Type::UnresolvedUsing:
+      return TT;
+    case Type::Using: {
+      const auto *T = cast<UsingType>(ST.Ty);
+      QualType Underlying = transform(T->desugar(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getUsingType(T->getFoundDecl(), Underlying));
+    }
+    case Type::VariableArray: {
+      const auto *T = cast<VariableArrayType>(ST.Ty);
+      QualType ET = transform(T->getElementType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getVariableArrayType(
+          ET, T->getSizeExpr(), T->getSizeModifier(),
+          T->getIndexTypeCVRQualifiers(), T->getBracketsRange()));
+    }
+    case Type::Vector: {
+      const auto *T = cast<VectorType>(ST.Ty);
+      QualType ET = transform(T->getElementType(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getVectorType(ET, T->getNumElements(),
+                                                 T->getVectorKind()));
+    }
+    case Type::ArrayParameter: {
+      const auto *T = cast<ArrayParameterType>(ST.Ty);
+      QualType Ty =
+          transform(T->getConstantArrayType(SemaRef.Context), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getArrayParameterType(Ty));
+    }
+    case Type::CountAttributed: {
+      const auto *T = cast<CountAttributedType>(ST.Ty);
+      QualType Ty = transform(T->desugar(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getCountAttributedType(
+          Ty, T->getCountExpr(), T->isCountInBytes(), T->isOrNull(),
+          T->getCoupledDecls()));
+    }
+    case Type::HLSLAttributedResource:
+      return TT;
+    case Type::PackIndexing: {
+      const auto *T = cast<PackIndexingType>(ST.Ty);
+      QualType Pattern = transform(T->getPattern(), Changed);
+      auto Expansions = transform(T->getExpansions(), Changed);
+      if (!Changed)
+        return TT;
+      return build(SemaRef.Context.getPackIndexingType(
+          Pattern, T->getIndexExpr(), T->isFullySubstituted(), Expansions,
+          T->getSelectedIndex()));
+    }
+    }
+    llvm_unreachable("Unhandled TypeClass");
+  }
+
+  TemplateArgument transform(TemplateArgument A, bool &OutChanged) {
+    bool Changed = false;
+    switch (auto Kind = A.getKind()) {
+    case TemplateArgument::Null:
+      llvm_unreachable("Unexpected Null TemplateArgument");
+    case TemplateArgument::Pack: {
+      ArrayRef<TemplateArgument> PackArray = A.getPackAsArray();
+      if (PackArray.empty())
+        return A;
+      auto Pack = PackArray.copy(SemaRef.Context);
+      for (auto &PA : Pack)
+        PA = transform(PA, Changed);
+      if (!Changed)
+        return A;
+      OutChanged = true;
+      return TemplateArgument(Pack);
+    }
+    case TemplateArgument::Integral:
+    case TemplateArgument::NullPtr:
+    case TemplateArgument::Declaration:
+    case TemplateArgument::StructuralValue: {
+      QualType T = transform(A.getNonTypeTemplateArgumentType(), Changed);
+      if (!Changed)
+        return A;
+      OutChanged = true;
+      switch (A.getKind()) {
+      case TemplateArgument::Integral:
+        return TemplateArgument(SemaRef.Context, A.getAsIntegral(), T);
+      case TemplateArgument::NullPtr:
+        return TemplateArgument(T, /*IsNullPtr=*/true);
+      case TemplateArgument::Declaration:
+        return TemplateArgument(A.getAsDecl(), T);
+      case TemplateArgument::StructuralValue:
+        return TemplateArgument(SemaRef.Context, T, A.getAsStructuralValue());
+      default:
+        llvm_unreachable("Not handled case");
+      }
+    }
+    case TemplateArgument::Type: {
+      QualType T = transform(A.getAsType(), Changed);
+      if (!Changed)
+        return A;
+      OutChanged = true;
+      return TemplateArgument(T);
+    }
+    case TemplateArgument::Template:
+    case TemplateArgument::TemplateExpansion: {
+      TemplateName TN = transform(A.getAsTemplateOrTemplatePattern(), Changed);
+      if (!Changed)
+        return A;
+      OutChanged = true;
+      return Kind == TemplateArgument::Template
+                 ? TemplateArgument(TN)
+                 : TemplateArgument(TN, A.getNumTemplateExpansions());
+    }
+    case TemplateArgument::Expression:
+      // FIXME: convert the type of these.
+      return A;
+    }
+    llvm_unreachable("Unexpected TemplateArgument kind");
+  }
+};
+} // namespace
+
+QualType Sema::resugar(const NestedNameSpecifier *NNS, QualType T) {
+  if (NNS == nullptr)
+    return T;
+
+  NameMap Names;
+  Names.insert(*this, NNS);
+  if (Names.empty())
+    return T;
+
+  bool Changed = false;
+  return Resugarer(*this, Names).transform(T, Changed);
+}
+
 /// \brief Determine whether the declaration found is acceptable as the name
 /// of a template and, if so, return that template declaration. Otherwise,
 /// returns null.
@@ -10947,7 +11667,7 @@ Sema::CheckTypenameType(ElaboratedTypeKeyword Keyword,
       //
       // FIXME: That's not strictly true: mem-initializer-id lookup does not
       // ignore functions, but that appears to be an oversight.
-      QualType T = getTypeDeclType(Ctx,
+      QualType T = getTypeDeclType(SS.getScopeRep(), Ctx,
                                    Keyword == ElaboratedTypeKeyword::Typename
                                        ? DiagCtorKind::Typename
                                        : DiagCtorKind::None,
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index c0dda828d4977..c6e717fa6c5f6 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -2091,11 +2091,13 @@ static TemplateDeductionResult DeduceTemplateArgumentsByTypeMatch(
       QualType PPT = MPP->getPointeeType();
       if (PPT->isFunctionType())
         S.adjustMemberFunctionCC(PPT, /*HasThisPointer=*/false,
-                                 /*IsCtorOrDtor=*/false, Info.getLocation());
+                                 /*IsCtorOrDtor=*/false, /*IsDeduced=*/true,
+                                 Info.getLocation());
       QualType APT = MPA->getPointeeType();
       if (APT->isFunctionType())
         S.adjustMemberFunctionCC(APT, /*HasThisPointer=*/false,
-                                 /*IsCtorOrDtor=*/false, Info.getLocation());
+                                 /*IsCtorOrDtor=*/false, /*IsDeduced=*/true,
+                                 Info.getLocation());
 
       unsigned SubTDF = TDF & TDF_IgnoreQualifiers;
       if (auto Result = DeduceTemplateArgumentsByTypeMatch(
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 59cd7ffadfeb4..664e4c8adcc39 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -2728,7 +2728,8 @@ QualType Sema::BuildMemberPointerType(QualType T,
       (Entity.getNameKind() == DeclarationName::CXXConstructorName) ||
       (Entity.getNameKind() == DeclarationName::CXXDestructorName);
   if (T->isFunctionType())
-    adjustMemberFunctionCC(T, /*HasThisPointer=*/true, IsCtorOrDtor, Loc);
+    adjustMemberFunctionCC(T, /*HasThisPointer=*/true, IsCtorOrDtor,
+                           /*IsDeduced=*/false, Loc);
 
   return Context.getMemberPointerType(T, Qualifier, Cls);
 }
@@ -8127,7 +8128,8 @@ bool Sema::hasExplicitCallingConv(QualType T) {
 }
 
 void Sema::adjustMemberFunctionCC(QualType &T, bool HasThisPointer,
-                                  bool IsCtorOrDtor, SourceLocation Loc) {
+                                  bool IsCtorOrDtor, bool IsDeduced,
+                                  SourceLocation Loc) {
   FunctionTypeUnwrapper Unwrapped(*this, T);
   const FunctionType *FT = Unwrapped.get();
   bool IsVariadic = (isa<FunctionProtoType>(FT) &&
@@ -8159,7 +8161,7 @@ void Sema::adjustMemberFunctionCC(QualType &T, bool HasThisPointer,
     if (CurCC != DefaultCC)
       return;
 
-    if (hasExplicitCallingConv(T))
+    if (!IsDeduced && hasExplicitCallingConv(T))
       return;
   }
 
@@ -9711,7 +9713,7 @@ QualType Sema::BuildPackIndexingType(QualType Pattern, Expr *IndexExpr,
   }
 
   return Context.getPackIndexingType(Pattern, IndexExpr, FullySubstituted,
-                                     Expansions, Index.value_or(-1));
+                                     Expansions, Index);
 }
 
 static QualType GetEnumUnderlyingType(Sema &S, QualType BaseType,
diff --git a/clang/test/AST/ast-dump-openmp-begin-declare-variant_reference.cpp b/clang/test/AST/ast-dump-openmp-begin-declare-variant_reference.cpp
index 1937a5d1c3eb3..791c753cb2cbc 100644
--- a/clang/test/AST/ast-dump-openmp-begin-declare-variant_reference.cpp
+++ b/clang/test/AST/ast-dump-openmp-begin-declare-variant_reference.cpp
@@ -195,9 +195,7 @@ int test(float &&f, short &&s) {
 // CHECK-NEXT: | |   |   `-ElaboratedType [[ADDR_47:0x[a-z0-9]*]] 'typename remove_reference<float &>::type' sugar
 // CHECK-NEXT: | |   |     `-TypedefType [[ADDR_48:0x[a-z0-9]*]] 'remove_reference<float &>::type' sugar
 // CHECK-NEXT: | |   |       |-Typedef [[ADDR_10]] 'type'
-// CHECK-NEXT: | |   |       `-SubstTemplateTypeParmType [[ADDR_11]] 'float' sugar class depth 0 index 0 _Tp
-// CHECK-NEXT: | |   |         |-ClassTemplateSpecialization [[ADDR_6]] 'remove_reference'
-// CHECK-NEXT: | |   |         `-BuiltinType [[ADDR_8]] 'float'
+// CHECK-NEXT: | |   |       `-BuiltinType [[ADDR_8]] 'float'
 // CHECK-NEXT: | |   `-ReturnStmt [[ADDR_49:0x[a-z0-9]*]] <line:13:3, col:33>
 // CHECK-NEXT: | |     `-CXXStaticCastExpr [[ADDR_50:0x[a-z0-9]*]] <col:10, col:33> '_Up':'float' xvalue static_cast<_Up &&> <NoOp>
 // CHECK-NEXT: | |       `-DeclRefExpr [[ADDR_51:0x[a-z0-9]*]] <col:30> 'float' {{.*}}ParmVar [[ADDR_43]] '__t' 'float &'
@@ -212,9 +210,7 @@ int test(float &&f, short &&s) {
 // CHECK-NEXT: |     |   `-ElaboratedType [[ADDR_57:0x[a-z0-9]*]] 'typename remove_reference<short &>::type' sugar
 // CHECK-NEXT: |     |     `-TypedefType [[ADDR_58:0x[a-z0-9]*]] 'remove_reference<short &>::type' sugar
 // CHECK-NEXT: |     |       |-Typedef [[ADDR_18]] 'type'
-// CHECK-NEXT: |     |       `-SubstTemplateTypeParmType [[ADDR_19]] 'short' sugar class depth 0 index 0 _Tp
-// CHECK-NEXT: |     |         |-ClassTemplateSpecialization [[ADDR_14]] 'remove_reference'
-// CHECK-NEXT: |     |         `-BuiltinType [[ADDR_16]] 'short'
+// CHECK-NEXT: |     |       `-BuiltinType [[ADDR_16]] 'short'
 // CHECK-NEXT: |     `-ReturnStmt [[ADDR_59:0x[a-z0-9]*]] <line:13:3, col:33>
 // CHECK-NEXT: |       `-CXXStaticCastExpr [[ADDR_60:0x[a-z0-9]*]] <col:10, col:33> '_Up':'short' xvalue static_cast<_Up &&> <NoOp>
 // CHECK-NEXT: |         `-DeclRefExpr [[ADDR_61:0x[a-z0-9]*]] <col:30> 'short' {{.*}}ParmVar [[ADDR_53]] '__t' 'short &'
diff --git a/clang/test/AST/ast-dump-template-name.cpp b/clang/test/AST/ast-dump-template-name.cpp
index acacdac857954..6fc3dcfc57b29 100644
--- a/clang/test/AST/ast-dump-template-name.cpp
+++ b/clang/test/AST/ast-dump-template-name.cpp
@@ -53,8 +53,5 @@ namespace subst {
 // CHECK-NEXT:       `-TemplateSpecializationType
 // CHECK-NEXT:         |-name: 'C':'subst::B<subst::A>::C' qualified
 // CHECK-NEXT:         | `-ClassTemplateDecl {{.+}} C
-// CHECK-NEXT:         |-TemplateArgument template 'subst::A' subst index 0
-// CHECK-NEXT:         | |-parameter: TemplateTemplateParmDecl {{.+}} depth 0 index 0 TT{{$}}
-// CHECK-NEXT:         | |-associated ClassTemplateSpecialization {{.+}} 'B'{{$}}
-// CHECK-NEXT:         | `-replacement:
-// CHECK-NEXT:         |   `-ClassTemplateDecl {{.+}} A{{$}}
+// CHECK-NEXT:         |-TemplateArgument template 'A':'subst::A' qualified
+// CHECK-NEXT:         | `-ClassTemplateDecl {{.+}} A{{$}}
diff --git a/clang/test/CXX/temp/temp.param/p15-cxx0x.cpp b/clang/test/CXX/temp/temp.param/p15-cxx0x.cpp
index 83144a494937b..556bc10eef5af 100644
--- a/clang/test/CXX/temp/temp.param/p15-cxx0x.cpp
+++ b/clang/test/CXX/temp/temp.param/p15-cxx0x.cpp
@@ -97,14 +97,14 @@ template<unsigned N, typename...Ts> struct drop {
 using T1 = take<3, int, char, double, long>::type; // expected-note {{previous}}
 // FIXME: Desguar the types on the RHS in this diagnostic.
 // desired-error {{'types<void, void, void, void>' vs 'types<int, char, double, (no argument)>'}}
-using T1 = types<void, void, void, void>; // expected-error {{'types<void, void, void, void>' vs 'types<typename inner<_>::type, typename inner<_>::type, typename inner<_>::type, (no argument)>'}}
+using T1 = types<void, void, void, void>; // expected-error {{'types<void, void, void, void>' vs 'types<typename template inner<_>::type, typename template inner<_>::type, typename template inner<_>::type, (no argument)>}}
 using D1 = drop<3, int, char, double, long>::type;
 using D1 = types<long>;
 
 using T2 = take<4, int, char, double, long>::type; // expected-note {{previous}}
 // FIXME: Desguar the types on the RHS in this diagnostic.
 // desired-error {{'types<void, void, void, void>' vs 'types<int, char, double, long>'}}
-using T2 = types<void, void, void, void>; // expected-error {{'types<void, void, void, void>' vs 'types<typename inner<_>::type, typename inner<_>::type, typename inner<_>::type, typename inner<_>::type>'}}
+using T2 = types<void, void, void, void>; // expected-error {{'types<void, void, void, void>' vs 'types<typename template inner<_>::type, typename template inner<_>::type, typename template inner<_>::type, typename template inner<_>::type>'}}
 using T2 = types<int, char, double, long>;
 using D2 = drop<4, int, char, double, long>::type;
 using D2 = types<>;
diff --git a/clang/test/Sema/Resugar/resugar-types.cpp b/clang/test/Sema/Resugar/resugar-types.cpp
new file mode 100644
index 0000000000000..baa3b4ed65f20
--- /dev/null
+++ b/clang/test/Sema/Resugar/resugar-types.cpp
@@ -0,0 +1,209 @@
+// RUN: %clang_cc1 -std=c++2b -fms-extensions -verify %s
+// expected-no-diagnostics
+
+static constexpr int alignment = 64; // Suitable large alignment.
+
+struct Baz {};
+using Bar [[gnu::aligned(alignment)]] = Baz;
+using Int [[gnu::aligned(alignment)]] = int;
+
+#define TEST(X) static_assert(alignof(X) == alignment)
+#define TEST_NOT(X) static_assert(alignof(X) != alignment)
+
+// Sanity checks.
+TEST_NOT(Baz);
+TEST(Bar);
+
+namespace t1 {
+template <class T> struct foo { using type = T; };
+template <class U> struct foo<U &> { using type = U; };
+
+TEST(typename foo<Bar>::type);
+TEST(typename foo<Bar &>::type);
+} // namespace t1
+
+namespace t2 {
+template <int, class T> struct foo1 { using type = T; };
+template <class T> struct foo2 { using type = typename foo1<1, T>::type; };
+TEST(typename foo2<Bar>::type);
+} // namespace t2
+
+namespace t3 {
+template <class T> struct foo1 {
+  template <int, class U> struct foo2 { using type1 = T; };
+  using type2 = typename foo2<1, int>::type1;
+};
+TEST(typename foo1<Bar>::type2);
+} // namespace t3
+
+namespace t4 {
+template <class T> struct foo {
+  template <class U> using type1 = T;
+  using type2 = type1<int>;
+};
+TEST(typename foo<Bar>::type2);
+} // namespace t4
+
+namespace t5 {
+template <class T> struct foo {
+  template <int, class U> using type1 = U;
+  using type2 = type1<1, T>;
+};
+TEST(typename foo<Bar>::type2);
+} // namespace t5
+
+namespace t6 {
+template <class T> struct foo1 {
+  template <int, class U> struct foo2 { using type = U; };
+  using type2 = typename foo2<1, T>::type;
+};
+TEST(typename foo1<Bar>::type2);
+}; // namespace t6
+
+namespace t7 {
+template <class T> struct foo {
+  template <int, class U> using type1 = U;
+};
+using type2 = typename foo<int>::template type1<1, Bar>;
+TEST(type2);
+} // namespace t7
+
+namespace t8 {
+template <class T> struct foo {
+  using type1 = T;
+};
+template <class T, class> using type2 = T;
+using type3 = typename type2<foo<Bar>, int>::type1;
+TEST(type3);
+} // namespace t8
+
+namespace t9 {
+template <class A, class B> struct Y {
+  using type1 = A;
+  using type2 = B;
+};
+template <class C, class D> using Z = Y<C, D>;
+template <class E> struct foo {
+  template <class F> using apply = Z<F, E>;
+};
+using T1 = foo<Bar>::apply<char>;
+TEST_NOT(T1::type1);
+TEST_NOT(T1::type2); // FIXME: Needs resugaring on the pattern of template type aliases.
+
+using T2 = foo<int>::apply<Bar>;
+TEST(T2::type1);
+TEST_NOT(T2::type2);
+} // namespace t9
+
+namespace t10 {
+template <class A1, class A2> struct Y {
+  using type1 = A1;
+  using type2 = A2;
+};
+template <typename... Bs> using Z = Y<Bs...>;
+template <typename... Cs> struct foo {
+  template <typename... Ds> using bind = Z<Ds..., Cs...>;
+};
+using T1 = foo<Bar>::bind<char>;
+TEST_NOT(T1::type1);
+TEST_NOT(T1::type2); // FIXME: Needs resugaring on the pattern of template type aliases.
+
+using T2 = foo<int>::bind<Bar>;
+TEST(T2::type1);
+TEST_NOT(T2::type2);
+} // namespace t10
+
+namespace t11 {
+template <class A1, class A2 = A1> struct A { using type1 = A2; };
+TEST(A<Bar>::type1);
+} // namespace t11
+
+namespace t12 {
+template <class T> struct W {
+  template <class Z, template <class Z1, class Z2 = Z1, class Z3 = T> class TT>
+  struct X {
+    using type1 = TT<Z>;
+  };
+};
+
+template <class Y1, class Y2, class Y3> struct Y {
+  using type2 = Y2;
+  using type3 = Y3;
+};
+
+using T1 = typename W<Bar>::X<Int, Y>::type1;
+TEST(typename T1::type2);
+TEST(typename T1::type3);
+} // namespace t12
+
+namespace t13 {
+template <template <typename...> class C, typename... Us> struct foo {
+  template <typename... Ts> using bind = C<Ts..., Us...>;
+};
+template <typename A1, typename A2> struct Y {
+  using type1 = A1;
+  using type2 = A2;
+};
+template <typename... Ts> using Z = Y<Ts...>;
+
+using T1 = typename foo<Z, Bar>::template bind<int>;
+TEST_NOT(typename T1::type1);
+TEST_NOT(typename T1::type2); // FIXME: Needs resugaring on the pattern of template type aliases.
+
+using T2 = typename foo<Z, int>::template bind<Bar>;
+TEST(typename T2::type1);
+TEST_NOT(typename T2::type2);
+} // namespace t13
+
+namespace t14 {
+template <int, class A1, class...> struct A { using B = A1; };
+template <class A2, class A3, class... A4> struct A<0, A2, A3, A4...> {
+  using B = typename A<0, A3, A4...>::B;
+};
+using T1 = typename A<0, long, short, Bar>::B;
+TEST(T1);
+} // namespace t14
+
+namespace t15 {
+template <class T, T... Ints> struct foo { using type1 = T; };
+using type2 = typename __make_integer_seq<foo, Int, 1>::type1;
+TEST(type2);
+template <typename T, T N> using type3 = __make_integer_seq<foo, T, N>;
+using type4 = type3<Int, 1>::type1;
+TEST(type4);
+} // namespace t15
+
+namespace t16 {
+template <class A1> struct A {
+  using type1 = A1;
+};
+using type2 = __type_pack_element<0, A<Bar>>::type1;
+TEST(type2);
+} // namespace t16
+
+namespace t17 {
+template <class A1> struct A {
+  using type1 = A1;
+};
+struct C : A<int> {
+  using A::type1;
+};
+TEST(C::A<Bar>::type1);
+TEST_NOT(C::A<int>::type1);
+TEST(C::A<Int>::type1);
+} // namespace t17
+
+namespace t18 {
+template <class A1> struct A;
+template <class B1> struct B {};
+template <class C1> struct A<B<C1>> {
+    using type1 = C1;
+};
+TEST(A<B<Bar>>::type1);
+} // namespace t18
+
+namespace t19 {
+template <class T> struct A { using type = T&; };
+
+TEST_NOT(A<Bar __unaligned>::type);
+} // namespace t19
diff --git a/lldb/test/API/commands/expression/import-std-module/iterator/TestIteratorFromStdModule.py b/lldb/test/API/commands/expression/import-std-module/iterator/TestIteratorFromStdModule.py
index 011906f33caef..8be253cb20cf5 100644
--- a/lldb/test/API/commands/expression/import-std-module/iterator/TestIteratorFromStdModule.py
+++ b/lldb/test/API/commands/expression/import-std-module/iterator/TestIteratorFromStdModule.py
@@ -22,7 +22,11 @@ def test(self):
         iter_type = "std::move_iterator<std::__wrap_iter<int *> >"
 
         self.expect_expr("move_begin", result_type=iter_type)
-        self.expect_expr("move_begin[0]", result_type="int", result_value="1")
+        self.expect_expr(
+            "move_begin[0]",
+            result_type="__libcpp_remove_reference_t<__reference>",
+            result_value="1",
+        )
 
         self.expect_expr("move_begin + 3 == move_end", result_value="true")
 



More information about the llvm-branch-commits mailing list