[clang] 8b4279b - [clang][TemplateBase] Add IsDefaulted bit to TemplateArgument
Michael Buch via cfe-commits
cfe-commits at lists.llvm.org
Thu Jan 26 18:35:12 PST 2023
Author: Michael Buch
Date: 2023-01-27T02:24:33Z
New Revision: 8b4279b66fc2f535184642b739b573ead1733711
URL: https://github.com/llvm/llvm-project/commit/8b4279b66fc2f535184642b739b573ead1733711
DIFF: https://github.com/llvm/llvm-project/commit/8b4279b66fc2f535184642b739b573ead1733711.diff
LOG: [clang][TemplateBase] Add IsDefaulted bit to TemplateArgument
**Summary**
This patch adds a `IsDefaulted` field to `clang::TemplateArgument`.
To prevent memory footprint increase we still 1 bit from `ArgKind`.
**Changes**
1. `getIsDefaulted`/`setIsDefaulted` to allow clients to communicate
an argument's defaulted-ness to the TypePrinter
2. The `TemplateArgument` properties description had to be changed
to make sure we correctly mark the defaulted-ness of arguments
that came from a deserialized AST (caught by the HLSL test-suite)
3. The `TemplateArgument` constructors now accept a `IsDefaulted`
parameter to simplify construction from the tablegen description.
Though if people don't want to clutter the constructors we can
instead call `setIsDefaulted` from tablegen
4. When `clang::Sema` checks the template arguments against template
parameters we now call `setIsDefaulted`. This makes sure that
whenever a specialization decl gets constructed, the defaulted-ness
of the associated `TemplateArgument`s has already been deduced.
This preserves the immutability of `TemplateArgumentList`s
**Background**
In LLDB we construct ASTs from debug-info and hand it to clang
to perform actions such as printing/formatting a typenames.
Some debug formats, specifically DWARF, may only encode information
about class template instantiations, losing the structure of the generic
class definition. However, the `clang::TypePrinter` needs a properly
constructed `ClassTemplateDecl` with generic default argument decls
to be able to deduce whether a `ClassTemplateSpecializationDecl` was
instantiatiated with `TemplateArgument`s that correspond to the
defaults. LLDB does know whether a particular template argument was
defaulted, but can't currently tell clang about it.
This patch allows LLDB to set the defaulted-ness of a `TemplateArgument`
and thus benefit more from `clang::TypePrinter`.
See discussion in https://reviews.llvm.org/D140423
**Testing**
* Added unit-test
* LLDB/clang/llvm test-suite passes
Differential Revision: https://reviews.llvm.org/D141826
Added:
Modified:
clang/include/clang/AST/PropertiesBase.td
clang/include/clang/AST/TemplateBase.h
clang/lib/AST/ASTContext.cpp
clang/lib/AST/TemplateBase.cpp
clang/lib/Sema/SemaTemplate.cpp
clang/unittests/AST/DeclTest.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/AST/PropertiesBase.td b/clang/include/clang/AST/PropertiesBase.td
index c2823c660f364..f289c9582c232 100644
--- a/clang/include/clang/AST/PropertiesBase.td
+++ b/clang/include/clang/AST/PropertiesBase.td
@@ -745,8 +745,11 @@ let Class = PropertyTypeCase<TemplateArgument, "Type"> in {
def : Property<"type", QualType> {
let Read = [{ node.getAsType() }];
}
+ def : Property<"isDefaulted", Bool> {
+ let Read = [{ node.getIsDefaulted() }];
+ }
def : Creator<[{
- return TemplateArgument(type);
+ return TemplateArgument(type, /* isNullPtr */ false, isDefaulted);
}]>;
}
let Class = PropertyTypeCase<TemplateArgument, "Declaration"> in {
@@ -756,16 +759,22 @@ let Class = PropertyTypeCase<TemplateArgument, "Declaration"> in {
def : Property<"parameterType", QualType> {
let Read = [{ node.getParamTypeForDecl() }];
}
+ def : Property<"isDefaulted", Bool> {
+ let Read = [{ node.getIsDefaulted() }];
+ }
def : Creator<[{
- return TemplateArgument(declaration, parameterType);
+ return TemplateArgument(declaration, parameterType, isDefaulted);
}]>;
}
let Class = PropertyTypeCase<TemplateArgument, "NullPtr"> in {
def : Property<"type", QualType> {
let Read = [{ node.getNullPtrType() }];
}
+ def : Property<"isDefaulted", Bool> {
+ let Read = [{ node.getIsDefaulted() }];
+ }
def : Creator<[{
- return TemplateArgument(type, /*nullptr*/ true);
+ return TemplateArgument(type, /*nullptr*/ true, isDefaulted);
}]>;
}
let Class = PropertyTypeCase<TemplateArgument, "Integral"> in {
@@ -775,16 +784,22 @@ let Class = PropertyTypeCase<TemplateArgument, "Integral"> in {
def : Property<"type", QualType> {
let Read = [{ node.getIntegralType() }];
}
+ def : Property<"isDefaulted", Bool> {
+ let Read = [{ node.getIsDefaulted() }];
+ }
def : Creator<[{
- return TemplateArgument(ctx, value, type);
+ return TemplateArgument(ctx, value, type, isDefaulted);
}]>;
}
let Class = PropertyTypeCase<TemplateArgument, "Template"> in {
def : Property<"name", TemplateName> {
let Read = [{ node.getAsTemplateOrTemplatePattern() }];
}
+ def : Property<"isDefaulted", Bool> {
+ let Read = [{ node.getIsDefaulted() }];
+ }
def : Creator<[{
- return TemplateArgument(name);
+ return TemplateArgument(name, isDefaulted);
}]>;
}
let Class = PropertyTypeCase<TemplateArgument, "TemplateExpansion"> in {
@@ -798,19 +813,25 @@ let Class = PropertyTypeCase<TemplateArgument, "TemplateExpansion"> in {
[](unsigned i) { return uint32_t(i); })
}];
}
+ def : Property<"isDefaulted", Bool> {
+ let Read = [{ node.getIsDefaulted() }];
+ }
def : Creator<[{
auto numExpansionsUnsigned = llvm::transformOptional(
numExpansions, [](uint32_t i) { return unsigned(i); });
- return TemplateArgument(name, numExpansionsUnsigned);
+ return TemplateArgument(name, numExpansionsUnsigned, isDefaulted);
}]>;
}
let Class = PropertyTypeCase<TemplateArgument, "Expression"> in {
def : Property<"expression", ExprRef> {
let Read = [{ node.getAsExpr() }];
}
+ def : Property<"isDefaulted", Bool> {
+ let Read = [{ node.getIsDefaulted() }];
+ }
def : Creator<[{
- return TemplateArgument(expression);
+ return TemplateArgument(expression, isDefaulted);
}]>;
}
let Class = PropertyTypeCase<TemplateArgument, "Pack"> in {
diff --git a/clang/include/clang/AST/TemplateBase.h b/clang/include/clang/AST/TemplateBase.h
index 66aef2121d814..8e6b4d8197406 100644
--- a/clang/include/clang/AST/TemplateBase.h
+++ b/clang/include/clang/AST/TemplateBase.h
@@ -103,12 +103,14 @@ class TemplateArgument {
/// The kind of template argument we're storing.
struct DA {
- unsigned Kind;
+ unsigned Kind : 31;
+ unsigned IsDefaulted : 1;
void *QT;
ValueDecl *D;
};
struct I {
- unsigned Kind;
+ unsigned Kind : 31;
+ unsigned IsDefaulted : 1;
// We store a decomposed APSInt with the data allocated by ASTContext if
// BitWidth > 64. The memory may be shared between multiple
// TemplateArgument instances.
@@ -124,17 +126,20 @@ class TemplateArgument {
void *Type;
};
struct A {
- unsigned Kind;
+ unsigned Kind : 31;
+ unsigned IsDefaulted : 1;
unsigned NumArgs;
const TemplateArgument *Args;
};
struct TA {
- unsigned Kind;
+ unsigned Kind : 31;
+ unsigned IsDefaulted : 1;
unsigned NumExpansions;
void *Name;
};
struct TV {
- unsigned Kind;
+ unsigned Kind : 31;
+ unsigned IsDefaulted : 1;
uintptr_t V;
};
union {
@@ -147,27 +152,31 @@ class TemplateArgument {
public:
/// Construct an empty, invalid template argument.
- constexpr TemplateArgument() : TypeOrValue({Null, 0}) {}
+ constexpr TemplateArgument() : TypeOrValue({Null, 0, /* IsDefaulted */ 0}) {}
/// Construct a template type argument.
- TemplateArgument(QualType T, bool isNullPtr = false) {
+ TemplateArgument(QualType T, bool isNullPtr = false,
+ bool IsDefaulted = false) {
TypeOrValue.Kind = isNullPtr ? NullPtr : Type;
+ TypeOrValue.IsDefaulted = IsDefaulted;
TypeOrValue.V = reinterpret_cast<uintptr_t>(T.getAsOpaquePtr());
}
/// Construct a template argument that refers to a
/// declaration, which is either an external declaration or a
/// template declaration.
- TemplateArgument(ValueDecl *D, QualType QT) {
+ TemplateArgument(ValueDecl *D, QualType QT, bool IsDefaulted = false) {
assert(D && "Expected decl");
DeclArg.Kind = Declaration;
+ DeclArg.IsDefaulted = IsDefaulted;
DeclArg.QT = QT.getAsOpaquePtr();
DeclArg.D = D;
}
/// Construct an integral constant template argument. The memory to
/// store the value is allocated with Ctx.
- TemplateArgument(ASTContext &Ctx, const llvm::APSInt &Value, QualType Type);
+ TemplateArgument(ASTContext &Ctx, const llvm::APSInt &Value, QualType Type,
+ bool IsDefaulted = false);
/// Construct an integral constant template argument with the same
/// value as Other but a
diff erent type.
@@ -184,8 +193,12 @@ class TemplateArgument {
/// is taken.
///
/// \param Name The template name.
- TemplateArgument(TemplateName Name) {
+ ///
+ /// \param IsDefaulted If 'true', implies that this TemplateArgument
+ /// corresponds to a default template parameter
+ TemplateArgument(TemplateName Name, bool IsDefaulted = false) {
TemplateArg.Kind = Template;
+ TemplateArg.IsDefaulted = IsDefaulted;
TemplateArg.Name = Name.getAsVoidPointer();
TemplateArg.NumExpansions = 0;
}
@@ -201,8 +214,13 @@ class TemplateArgument {
///
/// \param NumExpansions The number of expansions that will be generated by
/// instantiating
- TemplateArgument(TemplateName Name, std::optional<unsigned> NumExpansions) {
+ ///
+ /// \param IsDefaulted If 'true', implies that this TemplateArgument
+ /// corresponds to a default template parameter
+ TemplateArgument(TemplateName Name, std::optional<unsigned> NumExpansions,
+ bool IsDefaulted = false) {
TemplateArg.Kind = TemplateExpansion;
+ TemplateArg.IsDefaulted = IsDefaulted;
TemplateArg.Name = Name.getAsVoidPointer();
if (NumExpansions)
TemplateArg.NumExpansions = *NumExpansions + 1;
@@ -215,8 +233,9 @@ 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.
- TemplateArgument(Expr *E) {
+ TemplateArgument(Expr *E, bool IsDefaulted = false) {
TypeOrValue.Kind = Expression;
+ TypeOrValue.IsDefaulted = IsDefaulted;
TypeOrValue.V = reinterpret_cast<uintptr_t>(E);
}
@@ -226,12 +245,11 @@ class TemplateArgument {
/// outlives the TemplateArgument itself.
explicit TemplateArgument(ArrayRef<TemplateArgument> Args) {
this->Args.Kind = Pack;
+ this->Args.IsDefaulted = false;
this->Args.Args = Args.data();
this->Args.NumArgs = Args.size();
}
- TemplateArgument(TemplateName, bool) = delete;
-
static TemplateArgument getEmptyPack() {
return TemplateArgument(std::nullopt);
}
@@ -334,6 +352,14 @@ class TemplateArgument {
Integer.Type = T.getAsOpaquePtr();
}
+ /// Set to 'true' if this TemplateArgument corresponds to a
+ /// default template parameter.
+ void setIsDefaulted(bool v) { TypeOrValue.IsDefaulted = v; }
+
+ /// If returns 'true', this TemplateArgument corresponds to a
+ /// default template parameter.
+ bool getIsDefaulted() const { return (bool)TypeOrValue.IsDefaulted; }
+
/// If this is a non-type template argument, get its type. Otherwise,
/// returns a null QualType.
QualType getNonTypeTemplateArgumentType() const;
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 84434ced242d3..06807ffc48267 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -6821,26 +6821,29 @@ ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg) const {
case TemplateArgument::Declaration: {
auto *D = cast<ValueDecl>(Arg.getAsDecl()->getCanonicalDecl());
- return TemplateArgument(D, getCanonicalType(Arg.getParamTypeForDecl()));
+ return TemplateArgument(D, getCanonicalType(Arg.getParamTypeForDecl()),
+ Arg.getIsDefaulted());
}
case TemplateArgument::NullPtr:
return TemplateArgument(getCanonicalType(Arg.getNullPtrType()),
- /*isNullPtr*/true);
+ /*isNullPtr*/ true, Arg.getIsDefaulted());
case TemplateArgument::Template:
- return TemplateArgument(getCanonicalTemplateName(Arg.getAsTemplate()));
+ return TemplateArgument(getCanonicalTemplateName(Arg.getAsTemplate()),
+ Arg.getIsDefaulted());
case TemplateArgument::TemplateExpansion:
- return TemplateArgument(getCanonicalTemplateName(
- Arg.getAsTemplateOrTemplatePattern()),
- Arg.getNumTemplateExpansions());
+ return TemplateArgument(
+ getCanonicalTemplateName(Arg.getAsTemplateOrTemplatePattern()),
+ Arg.getNumTemplateExpansions(), Arg.getIsDefaulted());
case TemplateArgument::Integral:
return TemplateArgument(Arg, getCanonicalType(Arg.getIntegralType()));
case TemplateArgument::Type:
- return TemplateArgument(getCanonicalType(Arg.getAsType()));
+ return TemplateArgument(getCanonicalType(Arg.getAsType()),
+ /*isNullPtr*/ false, Arg.getIsDefaulted());
case TemplateArgument::Pack: {
bool AnyNonCanonArgs = false;
diff --git a/clang/lib/AST/TemplateBase.cpp b/clang/lib/AST/TemplateBase.cpp
index ceff7a3137162..f3d74b27e3267 100644
--- a/clang/lib/AST/TemplateBase.cpp
+++ b/clang/lib/AST/TemplateBase.cpp
@@ -161,8 +161,9 @@ static bool needsAmpersandOnTemplateArg(QualType paramType, QualType argType) {
//===----------------------------------------------------------------------===//
TemplateArgument::TemplateArgument(ASTContext &Ctx, const llvm::APSInt &Value,
- QualType Type) {
+ QualType Type, bool IsDefaulted) {
Integer.Kind = Integral;
+ Integer.IsDefaulted = IsDefaulted;
// Copy the APSInt value into our decomposed form.
Integer.BitWidth = Value.getBitWidth();
Integer.IsUnsigned = Value.isUnsigned();
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index b40bd0978a8ab..3a56c26f36407 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -5897,6 +5897,11 @@ bool Sema::CheckTemplateArgumentList(
CTAK_Specified))
return true;
+ CanonicalConverted.back().setIsDefaulted(
+ clang::isSubstitutedDefaultArgument(
+ Context, NewArgs[ArgIdx].getArgument(), *Param,
+ CanonicalConverted, Params->getDepth()));
+
bool PackExpansionIntoNonPack =
NewArgs[ArgIdx].getArgument().isPackExpansion() &&
(!(*Param)->isTemplateParameterPack() || getExpandedPackSize(*Param));
@@ -6072,6 +6077,8 @@ bool Sema::CheckTemplateArgumentList(
CTAK_Specified))
return true;
+ CanonicalConverted.back().setIsDefaulted(true);
+
// Core issue 150 (assumed resolution): if this is a template template
// parameter, keep track of the default template arguments from the
// template definition.
diff --git a/clang/unittests/AST/DeclTest.cpp b/clang/unittests/AST/DeclTest.cpp
index 518f71ea4fea7..29072d1cb657b 100644
--- a/clang/unittests/AST/DeclTest.cpp
+++ b/clang/unittests/AST/DeclTest.cpp
@@ -490,3 +490,34 @@ TEST(Decl, ImplicitlyDeclaredAllocationFunctionsInModules) {
ASSERT_TRUE(AlignedArrayDelete->getOwningModule());
EXPECT_TRUE(AlignedArrayDelete->getOwningModule()->isGlobalModule());
}
+
+TEST(Decl, TemplateArgumentDefaulted) {
+ llvm::Annotations Code(R"cpp(
+ template<typename T1, typename T2>
+ struct Alloc {};
+
+ template <typename T1,
+ typename T2 = double,
+ int T3 = 42,
+ typename T4 = Alloc<T1, T2>>
+ struct Foo {
+ };
+
+ Foo<char, int, 42, Alloc<char, int>> X;
+ )cpp");
+
+ auto AST =
+ tooling::buildASTFromCodeWithArgs(Code.code(), /*Args=*/{"-std=c++20"});
+ ASTContext &Ctx = AST->getASTContext();
+
+ auto const *CTSD = selectFirst<ClassTemplateSpecializationDecl>(
+ "id",
+ match(classTemplateSpecializationDecl(hasName("Foo")).bind("id"), Ctx));
+ ASSERT_NE(CTSD, nullptr);
+ auto const &ArgList = CTSD->getTemplateArgs();
+
+ EXPECT_FALSE(ArgList.get(0).getIsDefaulted());
+ EXPECT_FALSE(ArgList.get(1).getIsDefaulted());
+ EXPECT_TRUE(ArgList.get(2).getIsDefaulted());
+ EXPECT_TRUE(ArgList.get(3).getIsDefaulted());
+}
More information about the cfe-commits
mailing list