-
Notifications
You must be signed in to change notification settings - Fork 13.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[clang] Template Specialization Resugaring - TypeDecl #132441
base: users/mizvekov/clang-sugar-sugared-converted-tst
Are you sure you want to change the base?
[clang] Template Specialization Resugaring - TypeDecl #132441
Conversation
@llvm/pr-subscribers-clang-tools-extra @llvm/pr-subscribers-clang Author: Matheus Izvekov (mizvekov) ChangesThis 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 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: 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:
Differential Revision: https://reviews.llvm.org/D127695 Patch is 53.59 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/132441.diff 17 Files Affected:
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....
[truncated]
|
@llvm/pr-subscribers-lldb Author: Matheus Izvekov (mizvekov) ChangesThis 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 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: 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:
Differential Revision: https://reviews.llvm.org/D127695 Patch is 53.59 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/132441.diff 17 Files Affected:
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....
[truncated]
|
✅ With the latest revision this PR passed the Python code formatter. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really have any comments, quite a few improvements here that are valuable.
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
e621da3
to
4571fad
Compare
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 tostd::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:
Differential Revision: https://reviews.llvm.org/D127695