[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:16:11 PDT 2025
https://github.com/mizvekov created https://github.com/llvm/llvm-project/pull/132441
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
Even though this patch is ready for review, some dependent patches are not, and might not be ready for some time.
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
>From e621da3ff081f9efee76ed83b1a21627e0904ed0 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 | 2 +-
17 files changed, 985 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..fb15b715cca49 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,7 @@ 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