Skip to content

Reland: [clang] Improved canonicalization for template specialization types #135414

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

Merged
merged 4 commits into from
Apr 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion clang-tools-extra/clangd/AST.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,8 @@ QualType declaredType(const TypeDecl *D) {
if (const auto *CTSD = llvm::dyn_cast<ClassTemplateSpecializationDecl>(D))
if (const auto *Args = CTSD->getTemplateArgsAsWritten())
return Context.getTemplateSpecializationType(
TemplateName(CTSD->getSpecializedTemplate()), Args->arguments());
TemplateName(CTSD->getSpecializedTemplate()), Args->arguments(),
/*CanonicalArgs=*/std::nullopt);
return Context.getTypeDeclType(D);
}

Expand Down
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,8 @@ Improvements to Clang's diagnostics
- Clang now better preserves the sugared types of pointers to member.
- Clang now better preserves the presence of the template keyword with dependent
prefixes.
- Clang now in more cases avoids printing 'type-parameter-X-X' instead of the name of
the template parameter.
- Clang now respects the current language mode when printing expressions in
diagnostics. This fixes a bunch of `bool` being printed as `_Bool`, and also
a bunch of HLSL types being printed as their C++ equivalents.
Expand Down
49 changes: 32 additions & 17 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -243,8 +243,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
mutable llvm::FoldingSet<PackExpansionType> PackExpansionTypes;
mutable llvm::FoldingSet<ObjCObjectTypeImpl> ObjCObjectTypes;
mutable llvm::FoldingSet<ObjCObjectPointerType> ObjCObjectPointerTypes;
mutable llvm::FoldingSet<DependentUnaryTransformType>
DependentUnaryTransformTypes;
mutable llvm::FoldingSet<UnaryTransformType> UnaryTransformTypes;
// An AutoType can have a dependency on another AutoType via its template
// arguments. Since both dependent and dependency are on the same set,
// we can end up in an infinite recursion when looking for a node if we used
Expand Down Expand Up @@ -367,9 +366,6 @@ class ASTContext : public RefCountedBase<ASTContext> {
const ASTContext&>
CanonTemplateTemplateParms;

TemplateTemplateParmDecl *
getCanonicalTemplateTemplateParmDecl(TemplateTemplateParmDecl *TTP) const;

/// The typedef for the __int128_t type.
mutable TypedefDecl *Int128Decl = nullptr;

Expand Down Expand Up @@ -1811,22 +1807,26 @@ class ASTContext : public RefCountedBase<ASTContext> {
bool ParameterPack,
TemplateTypeParmDecl *ParmDecl = nullptr) const;

QualType getTemplateSpecializationType(TemplateName T,
ArrayRef<TemplateArgument> Args,
QualType Canon = QualType()) const;
QualType getCanonicalTemplateSpecializationType(
TemplateName T, ArrayRef<TemplateArgument> CanonicalArgs) const;

QualType
getCanonicalTemplateSpecializationType(TemplateName T,
ArrayRef<TemplateArgument> Args) const;
getTemplateSpecializationType(TemplateName T,
ArrayRef<TemplateArgument> SpecifiedArgs,
ArrayRef<TemplateArgument> CanonicalArgs,
QualType Underlying = QualType()) const;

QualType getTemplateSpecializationType(TemplateName T,
ArrayRef<TemplateArgumentLoc> Args,
QualType Canon = QualType()) const;
QualType
getTemplateSpecializationType(TemplateName T,
ArrayRef<TemplateArgumentLoc> SpecifiedArgs,
ArrayRef<TemplateArgument> CanonicalArgs,
QualType Canon = QualType()) const;

TypeSourceInfo *
getTemplateSpecializationTypeInfo(TemplateName T, SourceLocation TLoc,
const TemplateArgumentListInfo &Args,
QualType Canon = QualType()) const;
TypeSourceInfo *getTemplateSpecializationTypeInfo(
TemplateName T, SourceLocation TLoc,
const TemplateArgumentListInfo &SpecifiedArgs,
ArrayRef<TemplateArgument> CanonicalArgs,
QualType Canon = QualType()) const;

QualType getParenType(QualType NamedType) const;

Expand Down Expand Up @@ -2942,6 +2942,21 @@ class ASTContext : public RefCountedBase<ASTContext> {
TemplateArgument getCanonicalTemplateArgument(const TemplateArgument &Arg)
const;

/// Canonicalize the given template argument list.
///
/// Returns true if any arguments were non-canonical, false otherwise.
bool
canonicalizeTemplateArguments(MutableArrayRef<TemplateArgument> Args) const;

/// Canonicalize the given TemplateTemplateParmDecl.
TemplateTemplateParmDecl *
getCanonicalTemplateTemplateParmDecl(TemplateTemplateParmDecl *TTP) const;

TemplateTemplateParmDecl *findCanonicalTemplateTemplateParmDeclInternal(
TemplateTemplateParmDecl *TTP) const;
TemplateTemplateParmDecl *insertCanonicalTemplateTemplateParmDeclInternal(
TemplateTemplateParmDecl *CanonTTP) const;

/// Type Query functions. If the type is an instance of the specified class,
/// return the Type pointer for the underlying maximally pretty type. This
/// is a member of ASTContext because this may need to do some amount of
Expand Down
5 changes: 4 additions & 1 deletion clang/include/clang/AST/PropertiesBase.td
Original file line number Diff line number Diff line change
Expand Up @@ -877,11 +877,14 @@ let Class = PropertyTypeCase<TemplateArgument, "Expression"> in {
def : Property<"expression", ExprRef> {
let Read = [{ node.getAsExpr() }];
}
def : Property<"IsCanonical", Bool> {
let Read = [{ node.isCanonicalExpr() }];
}
def : Property<"isDefaulted", Bool> {
let Read = [{ node.getIsDefaulted() }];
}
def : Creator<[{
return TemplateArgument(expression, isDefaulted);
return TemplateArgument(expression, IsCanonical, isDefaulted);
}]>;
}
let Class = PropertyTypeCase<TemplateArgument, "Pack"> in {
Expand Down
13 changes: 11 additions & 2 deletions clang/include/clang/AST/TemplateBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,8 @@ class TemplateArgument {
unsigned Kind : 31;
LLVM_PREFERRED_TYPE(bool)
unsigned IsDefaulted : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned IsCanonicalExpr : 1;
uintptr_t V;
};
union {
Expand All @@ -187,7 +189,8 @@ class TemplateArgument {

public:
/// Construct an empty, invalid template argument.
constexpr TemplateArgument() : TypeOrValue({Null, 0, /* IsDefaulted */ 0}) {}
constexpr TemplateArgument()
: TypeOrValue{Null, /*IsDefaulted=*/0, /*IsCanonicalExpr=*/0, /*V=*/0} {}

/// Construct a template type argument.
TemplateArgument(QualType T, bool isNullPtr = false,
Expand Down Expand Up @@ -262,9 +265,10 @@ class TemplateArgument {
/// This form of template argument only occurs in template argument
/// lists used for dependent types and for expression; it will not
/// occur in a non-dependent, canonical template argument list.
explicit TemplateArgument(Expr *E, bool IsDefaulted = false) {
TemplateArgument(Expr *E, bool IsCanonical, bool IsDefaulted = false) {
TypeOrValue.Kind = Expression;
TypeOrValue.IsDefaulted = IsDefaulted;
TypeOrValue.IsCanonicalExpr = IsCanonical;
TypeOrValue.V = reinterpret_cast<uintptr_t>(E);
}

Expand Down Expand Up @@ -407,6 +411,11 @@ class TemplateArgument {
return reinterpret_cast<Expr *>(TypeOrValue.V);
}

bool isCanonicalExpr() const {
assert(getKind() == Expression && "Unexpected kind");
return TypeOrValue.IsCanonicalExpr;
}

/// Iterator that traverses the elements of a template argument pack.
using pack_iterator = const TemplateArgument *;

Expand Down
33 changes: 10 additions & 23 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -5918,7 +5918,7 @@ class DecltypeType : public Type {
/// of this class via DecltypeType nodes.
class DependentDecltypeType : public DecltypeType, public llvm::FoldingSetNode {
public:
DependentDecltypeType(Expr *E, QualType UnderlyingTpe);
DependentDecltypeType(Expr *E);

void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context) {
Profile(ID, Context, getUnderlyingExpr());
Expand Down Expand Up @@ -6003,7 +6003,7 @@ class PackIndexingType final
};

/// A unary type transform, which is a type constructed from another.
class UnaryTransformType : public Type {
class UnaryTransformType : public Type, public llvm::FoldingSetNode {
public:
enum UTTKind {
#define TRANSFORM_TYPE_TRAIT_DEF(Enum, _) Enum,
Expand Down Expand Up @@ -6037,28 +6037,16 @@ class UnaryTransformType : public Type {
static bool classof(const Type *T) {
return T->getTypeClass() == UnaryTransform;
}
};

/// Internal representation of canonical, dependent
/// __underlying_type(type) types.
///
/// This class is used internally by the ASTContext to manage
/// canonical, dependent types, only. Clients will only see instances
/// of this class via UnaryTransformType nodes.
class DependentUnaryTransformType : public UnaryTransformType,
public llvm::FoldingSetNode {
public:
DependentUnaryTransformType(const ASTContext &C, QualType BaseType,
UTTKind UKind);

void Profile(llvm::FoldingSetNodeID &ID) {
Profile(ID, getBaseType(), getUTTKind());
Profile(ID, getBaseType(), getUnderlyingType(), getUTTKind());
}

static void Profile(llvm::FoldingSetNodeID &ID, QualType BaseType,
UTTKind UKind) {
ID.AddPointer(BaseType.getAsOpaquePtr());
ID.AddInteger((unsigned)UKind);
QualType UnderlyingType, UTTKind UKind) {
BaseType.Profile(ID);
UnderlyingType.Profile(ID);
ID.AddInteger(UKind);
}
};

Expand Down Expand Up @@ -6676,10 +6664,9 @@ class TemplateSpecializationType : public Type, public llvm::FoldingSetNode {
/// replacement must, recursively, be one of these).
TemplateName Template;

TemplateSpecializationType(TemplateName T,
TemplateSpecializationType(TemplateName T, bool IsAlias,
ArrayRef<TemplateArgument> Args,
QualType Canon,
QualType Aliased);
QualType Underlying);

public:
/// Determine whether any of the given template arguments are dependent.
Expand Down Expand Up @@ -6747,7 +6734,7 @@ class TemplateSpecializationType : public Type, public llvm::FoldingSetNode {

void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Ctx);
static void Profile(llvm::FoldingSetNodeID &ID, TemplateName T,
ArrayRef<TemplateArgument> Args,
ArrayRef<TemplateArgument> Args, QualType Underlying,
const ASTContext &Context);

static bool classof(const Type *T) {
Expand Down
30 changes: 5 additions & 25 deletions clang/include/clang/AST/TypeProperties.td
Original file line number Diff line number Diff line change
Expand Up @@ -737,39 +737,19 @@ let Class = DependentAddressSpaceType in {
}

let Class = TemplateSpecializationType in {
def : Property<"dependent", Bool> {
let Read = [{ node->isDependentType() }];
}
def : Property<"templateName", TemplateName> {
let Read = [{ node->getTemplateName() }];
}
def : Property<"templateArguments", Array<TemplateArgument>> {
def : Property<"args", Array<TemplateArgument>> {
let Read = [{ node->template_arguments() }];
}
def : Property<"underlyingType", Optional<QualType>> {
let Read = [{
node->isTypeAlias()
? std::optional<QualType>(node->getAliasedType())
: node->isCanonicalUnqualified()
? std::nullopt
: std::optional<QualType>(node->getCanonicalTypeInternal())
}];
def : Property<"UnderlyingType", QualType> {
let Read = [{ node->isCanonicalUnqualified() ? QualType() :
node->desugar() }];
}

def : Creator<[{
QualType result;
if (!underlyingType) {
result = ctx.getCanonicalTemplateSpecializationType(templateName,
templateArguments);
} else {
result = ctx.getTemplateSpecializationType(templateName,
templateArguments,
*underlyingType);
}
if (dependent)
const_cast<Type *>(result.getTypePtr())
->addDependence(TypeDependence::DependentInstantiation);
return result;
return ctx.getTemplateSpecializationType(templateName, args, std::nullopt, UnderlyingType);
}]>;
}

Expand Down
Loading
Loading