Skip to content

Commit 0793555

Browse files
committed
[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
1 parent 8ccbff4 commit 0793555

File tree

17 files changed

+1004
-46
lines changed

17 files changed

+1004
-46
lines changed

clang-tools-extra/clang-tidy/readability/QualifiedAutoCheck.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ void QualifiedAutoCheck::registerMatchers(MatchFinder *Finder) {
126126
auto UnlessFunctionType = unless(hasUnqualifiedDesugaredType(functionType()));
127127
auto IsAutoDeducedToPointer = [](const auto &...InnerMatchers) {
128128
return autoType(hasDeducedType(
129-
hasUnqualifiedDesugaredType(pointerType(pointee(InnerMatchers...)))));
129+
hasCanonicalType(pointerType(pointee(InnerMatchers...)))));
130130
};
131131

132132
Finder->addMatcher(

clang/include/clang/AST/ASTContext.h

+7-6
Original file line numberDiff line numberDiff line change
@@ -1319,8 +1319,6 @@ class ASTContext : public RefCountedBase<ASTContext> {
13191319

13201320
QualType getTypeDeclTypeSlow(const TypeDecl *Decl) const;
13211321

1322-
QualType getPipeType(QualType T, bool ReadOnly) const;
1323-
13241322
public:
13251323
/// Return the uniqued reference to the type for an address space
13261324
/// qualified type with the specified type and address space.
@@ -1500,6 +1498,9 @@ class ASTContext : public RefCountedBase<ASTContext> {
15001498
/// blocks.
15011499
QualType getBlockDescriptorType() const;
15021500

1501+
// Return a pipe type for the specified type.
1502+
QualType getPipeType(QualType T, bool ReadOnly) const;
1503+
15031504
/// Return a read_only pipe type for the specified type.
15041505
QualType getReadPipeType(QualType T) const;
15051506

@@ -1897,10 +1898,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
18971898
/// C++11 decltype.
18981899
QualType getDecltypeType(Expr *e, QualType UnderlyingType) const;
18991900

1900-
QualType getPackIndexingType(QualType Pattern, Expr *IndexExpr,
1901-
bool FullySubstituted = false,
1902-
ArrayRef<QualType> Expansions = {},
1903-
int Index = -1) const;
1901+
QualType getPackIndexingType(
1902+
QualType Pattern, Expr *IndexExpr, bool FullySubstituted = false,
1903+
ArrayRef<QualType> Expansions = {},
1904+
std::optional<unsigned> SelectedIndex = std::nullopt) const;
19041905

19051906
/// Unary type transforms
19061907
QualType getUnaryTransformType(QualType BaseType, QualType UnderlyingType,

clang/include/clang/AST/Type.h

+6-5
Original file line numberDiff line numberDiff line change
@@ -643,10 +643,10 @@ class Qualifiers {
643643
void addQualifiers(Qualifiers Q) {
644644
// If the other set doesn't have any non-boolean qualifiers, just
645645
// bit-or it in.
646-
if (!(Q.Mask & ~CVRMask))
646+
if (!(Q.Mask & ~CVRUMask))
647647
Mask |= Q.Mask;
648648
else {
649-
Mask |= (Q.Mask & CVRMask);
649+
Mask |= (Q.Mask & CVRUMask);
650650
if (Q.hasAddressSpace())
651651
addAddressSpace(Q.getAddressSpace());
652652
if (Q.hasObjCGCAttr())
@@ -662,10 +662,10 @@ class Qualifiers {
662662
void removeQualifiers(Qualifiers Q) {
663663
// If the other set doesn't have any non-boolean qualifiers, just
664664
// bit-and the inverse in.
665-
if (!(Q.Mask & ~CVRMask))
665+
if (!(Q.Mask & ~CVRUMask))
666666
Mask &= ~Q.Mask;
667667
else {
668-
Mask &= ~(Q.Mask & CVRMask);
668+
Mask &= ~(Q.Mask & CVRUMask);
669669
if (getObjCGCAttr() == Q.getObjCGCAttr())
670670
removeObjCGCAttr();
671671
if (getObjCLifetime() == Q.getObjCLifetime())
@@ -805,12 +805,13 @@ class Qualifiers {
805805

806806
static constexpr uint64_t UMask = 0x8;
807807
static constexpr uint64_t UShift = 3;
808+
static constexpr uint64_t CVRUMask = CVRMask | UMask;
808809
static constexpr uint64_t GCAttrMask = 0x30;
809810
static constexpr uint64_t GCAttrShift = 4;
810811
static constexpr uint64_t LifetimeMask = 0x1C0;
811812
static constexpr uint64_t LifetimeShift = 6;
812813
static constexpr uint64_t AddressSpaceMask =
813-
~(CVRMask | UMask | GCAttrMask | LifetimeMask);
814+
~(CVRUMask | GCAttrMask | LifetimeMask);
814815
static constexpr uint64_t AddressSpaceShift = 9;
815816
static constexpr uint64_t PtrAuthShift = 32;
816817
static constexpr uint64_t PtrAuthMask = uint64_t(0xffffffff) << PtrAuthShift;

clang/include/clang/Sema/Sema.h

+6-2
Original file line numberDiff line numberDiff line change
@@ -3193,7 +3193,8 @@ class Sema final : public SemaBase {
31933193
/// Returns the TypeDeclType for the given type declaration,
31943194
/// as ASTContext::getTypeDeclType would, but
31953195
/// performs the required semantic checks for name lookup of said entity.
3196-
QualType getTypeDeclType(DeclContext *LookupCtx, DiagCtorKind DCK,
3196+
QualType getTypeDeclType(const NestedNameSpecifier *NNS,
3197+
DeclContext *LookupCtx, DiagCtorKind DCK,
31973198
TypeDecl *TD, SourceLocation NameLoc);
31983199

31993200
/// If the identifier refers to a type name within this scope,
@@ -13985,6 +13986,8 @@ class Sema final : public SemaBase {
1398513986
FunctionDecl *SubstSpaceshipAsEqualEqual(CXXRecordDecl *RD,
1398613987
FunctionDecl *Spaceship);
1398713988

13989+
QualType resugar(const NestedNameSpecifier *NNS, QualType T);
13990+
1398813991
/// Performs template instantiation for all implicit template
1398913992
/// instantiations we have seen until this point.
1399013993
void PerformPendingInstantiations(bool LocalOnly = false,
@@ -14984,7 +14987,8 @@ class Sema final : public SemaBase {
1498414987
/// wasn't specified explicitly. This handles method types formed from
1498514988
/// function type typedefs and typename template arguments.
1498614989
void adjustMemberFunctionCC(QualType &T, bool HasThisPointer,
14987-
bool IsCtorOrDtor, SourceLocation Loc);
14990+
bool IsCtorOrDtor, bool IsDeduced,
14991+
SourceLocation Loc);
1498814992

1498914993
// Check if there is an explicit attribute, but only look through parens.
1499014994
// The intent is to look for an attribute on the current declarator, but not

clang/lib/AST/ASTContext.cpp

+8-9
Original file line numberDiff line numberDiff line change
@@ -6391,13 +6391,14 @@ QualType ASTContext::getDecltypeType(Expr *e, QualType UnderlyingType) const {
63916391
return QualType(dt, 0);
63926392
}
63936393

6394-
QualType ASTContext::getPackIndexingType(QualType Pattern, Expr *IndexExpr,
6395-
bool FullySubstituted,
6396-
ArrayRef<QualType> Expansions,
6397-
int Index) const {
6394+
QualType
6395+
ASTContext::getPackIndexingType(QualType Pattern, Expr *IndexExpr,
6396+
bool FullySubstituted,
6397+
ArrayRef<QualType> Expansions,
6398+
std::optional<unsigned> SelectedIndex) const {
63986399
QualType Canonical;
6399-
if (FullySubstituted && Index != -1) {
6400-
Canonical = getCanonicalType(Expansions[Index]);
6400+
if (FullySubstituted && SelectedIndex) {
6401+
Canonical = getCanonicalType(Expansions[*SelectedIndex]);
64016402
} else {
64026403
llvm::FoldingSetNodeID ID;
64036404
PackIndexingType::Profile(ID, *this, Pattern.getCanonicalType(), IndexExpr,
@@ -14067,9 +14068,7 @@ static QualType getCommonNonSugarTypeNode(ASTContext &Ctx, const Type *X,
1406714068
case Type::Pipe: {
1406814069
const auto *PX = cast<PipeType>(X), *PY = cast<PipeType>(Y);
1406914070
assert(PX->isReadOnly() == PY->isReadOnly());
14070-
auto MP = PX->isReadOnly() ? &ASTContext::getReadPipeType
14071-
: &ASTContext::getWritePipeType;
14072-
return (Ctx.*MP)(getCommonElementType(Ctx, PX, PY));
14071+
return Ctx.getPipeType(getCommonElementType(Ctx, PX, PY), PX->isReadOnly());
1407314072
}
1407414073
case Type::TemplateTypeParm: {
1407514074
const auto *TX = cast<TemplateTypeParmType>(X),

clang/lib/Sema/SemaCXXScopeSpec.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -643,8 +643,9 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, NestedNameSpecInfo &IdInfo,
643643
return false;
644644
}
645645

646-
QualType T =
647-
Context.getTypeDeclType(cast<TypeDecl>(SD->getUnderlyingDecl()));
646+
QualType T = resugar(
647+
SS.getScopeRep(),
648+
Context.getTypeDeclType(cast<TypeDecl>(SD->getUnderlyingDecl())));
648649

649650
if (T->isEnumeralType())
650651
Diag(IdInfo.IdentifierLoc, diag::warn_cxx98_compat_enum_nested_name_spec);

clang/lib/Sema/SemaCoroutine.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ static QualType lookupPromiseType(Sema &S, const FunctionDecl *FD,
113113
}
114114
// The promise type is required to be a class type.
115115
QualType PromiseType = S.Context.getTypeDeclType(Promise);
116+
// FIXME: resugar PromiseType.
116117

117118
auto buildElaboratedType = [&]() {
118119
auto *NNS = NestedNameSpecifier::Create(S.Context, nullptr, S.getStdNamespace());

clang/lib/Sema/SemaDecl.cpp

+5-4
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,8 @@ class TypeNameValidatorCCC final : public CorrectionCandidateCallback {
139139

140140
} // end anonymous namespace
141141

142-
QualType Sema::getTypeDeclType(DeclContext *LookupCtx, DiagCtorKind DCK,
142+
QualType Sema::getTypeDeclType(const NestedNameSpecifier *NNS,
143+
DeclContext *LookupCtx, DiagCtorKind DCK,
143144
TypeDecl *TD, SourceLocation NameLoc) {
144145
auto *LookupRD = dyn_cast_or_null<CXXRecordDecl>(LookupCtx);
145146
auto *FoundRD = dyn_cast<CXXRecordDecl>(TD);
@@ -156,7 +157,7 @@ QualType Sema::getTypeDeclType(DeclContext *LookupCtx, DiagCtorKind DCK,
156157

157158
DiagnoseUseOfDecl(TD, NameLoc);
158159
MarkAnyDeclReferenced(TD->getLocation(), TD, /*OdrUse=*/false);
159-
return Context.getTypeDeclType(TD);
160+
return resugar(NNS, Context.getTypeDeclType(TD));
160161
}
161162

162163
namespace {
@@ -534,7 +535,7 @@ ParsedType Sema::getTypeName(const IdentifierInfo &II, SourceLocation NameLoc,
534535
// C++ [class.qual]p2: A lookup that would find the injected-class-name
535536
// instead names the constructors of the class, except when naming a class.
536537
// This is ill-formed when we're not actually forming a ctor or dtor name.
537-
T = getTypeDeclType(LookupCtx,
538+
T = getTypeDeclType(SS ? SS->getScopeRep() : nullptr, LookupCtx,
538539
IsImplicitTypename ? DiagCtorKind::Implicit
539540
: DiagCtorKind::None,
540541
TD, NameLoc);
@@ -9865,7 +9866,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
98659866
if (D.isFirstDeclarationOfMember())
98669867
adjustMemberFunctionCC(
98679868
R, !(D.isStaticMember() || D.isExplicitObjectMemberFunction()),
9868-
D.isCtorOrDtor(), D.getIdentifierLoc());
9869+
D.isCtorOrDtor(), /*IsDeduced=*/false, D.getIdentifierLoc());
98699870

98709871
bool isFriend = false;
98719872
FunctionTemplateDecl *FunctionTemplate = nullptr;

clang/lib/Sema/SemaDeclCXX.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1246,7 +1246,7 @@ static QualType getTupleLikeElementType(Sema &S, SourceLocation Loc,
12461246
S.Diag(R.getRepresentativeDecl()->getLocation(), diag::note_declared_at);
12471247
return QualType();
12481248
}
1249-
1249+
// FIXME: resugar
12501250
return S.Context.getTypeDeclType(TD);
12511251
}
12521252

0 commit comments

Comments
 (0)