[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