[clang] c36b03e - The type of a reference to a non-type template parameter pack should

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Thu Jun 18 17:52:21 PDT 2020


Author: Richard Smith
Date: 2020-06-18T17:52:13-07:00
New Revision: c36b03e32556a966e584386ac7dbb110bc7e4bc5

URL: https://github.com/llvm/llvm-project/commit/c36b03e32556a966e584386ac7dbb110bc7e4bc5
DIFF: https://github.com/llvm/llvm-project/commit/c36b03e32556a966e584386ac7dbb110bc7e4bc5.diff

LOG: The type of a reference to a non-type template parameter pack should
not be a pack expansion type.

Using a pack expansion type for a pack declaration makes sense, but
general expressions should never have pack expansion types. If we have a
pack `T *...V`, then the type of `V` is the type `T *`, which contains
an unexpanded pack, and is a pointer type.

This allows us to better diagnose issues where a template is invalid due
to some non-dependent portion of a dependent type of a non-type template
parameter pack.

Added: 
    

Modified: 
    clang/include/clang/AST/Type.h
    clang/lib/AST/ASTContext.cpp
    clang/lib/AST/Type.cpp
    clang/lib/Sema/SemaExpr.cpp
    clang/test/AST/ast-dump-expr-json.cpp
    clang/test/AST/ast-dump-expr.cpp
    clang/test/Import/pack-expansion-expr/test.cpp
    clang/test/SemaTemplate/temp_arg_nontype.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index d32e657843da..10b8b41efeeb 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -944,6 +944,12 @@ class QualType {
   /// from non-class types (in C++) or all types (in C).
   QualType getNonLValueExprType(const ASTContext &Context) const;
 
+  /// Remove an outer pack expansion type (if any) from this type. Used as part
+  /// of converting the type of a declaration to the type of an expression that
+  /// references that expression. It's meaningless for an expression to have a
+  /// pack expansion type.
+  QualType getNonPackExpansionType() const;
+
   /// Return the specified type with any "sugar" removed from
   /// the type.  This takes off typedefs, typeof's etc.  If the outer level of
   /// the type is already concrete, it returns it unmodified.  This is similar

diff  --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 01a737e307d0..2ba643f12a82 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -4724,7 +4724,7 @@ TemplateArgument ASTContext::getInjectedTemplateArg(NamedDecl *Param) {
   } else if (auto *NTTP = dyn_cast<NonTypeTemplateParmDecl>(Param)) {
     Expr *E = new (*this) DeclRefExpr(
         *this, NTTP, /*enclosing*/ false,
-        NTTP->getType().getNonLValueExprType(*this),
+        NTTP->getType().getNonPackExpansionType().getNonLValueExprType(*this),
         Expr::getValueKindForType(NTTP->getType()), NTTP->getLocation());
 
     if (NTTP->isParameterPack())

diff  --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 7dd85d1e16ab..ba0d86befe1b 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -3049,6 +3049,13 @@ StringRef BuiltinType::getName(const PrintingPolicy &Policy) const {
   llvm_unreachable("Invalid builtin type.");
 }
 
+QualType QualType::getNonPackExpansionType() const {
+  // We never wrap type sugar around a PackExpansionType.
+  if (auto *PET = dyn_cast<PackExpansionType>(getTypePtr()))
+    return PET->getPattern();
+  return *this;
+}
+
 QualType QualType::getNonLValueExprType(const ASTContext &Context) const {
   if (const auto *RefType = getTypePtr()->getAs<ReferenceType>())
     return RefType->getPointeeType();

diff  --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 64cb47eab70a..59e7d88b7691 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -3151,6 +3151,11 @@ ExprResult Sema::BuildDeclarationNameExpr(
       return ExprError();
     ExprValueKind valueKind = VK_RValue;
 
+    // In 'T ...V;', the type of the declaration 'V' is 'T...', but the type of
+    // a reference to 'V' is simply (unexpanded) 'T'. The type, like the value,
+    // is expanded by some outer '...' in the context of the use.
+    type = type.getNonPackExpansionType();
+
     switch (D->getKind()) {
     // Ignore all the non-ValueDecl kinds.
 #define ABSTRACT_DECL(kind)

diff  --git a/clang/test/AST/ast-dump-expr-json.cpp b/clang/test/AST/ast-dump-expr-json.cpp
index 403ce670f193..245c56cafdae 100644
--- a/clang/test/AST/ast-dump-expr-json.cpp
+++ b/clang/test/AST/ast-dump-expr-json.cpp
@@ -5042,7 +5042,7 @@ void TestNonADLCall3() {
 // CHECK-NEXT:             }
 // CHECK-NEXT:            },
 // CHECK-NEXT:            "type": {
-// CHECK-NEXT:             "qualType": "Ts..."
+// CHECK-NEXT:             "qualType": "Ts"
 // CHECK-NEXT:            },
 // CHECK-NEXT:            "valueCategory": "lvalue",
 // CHECK-NEXT:            "referencedDecl": {
@@ -6622,7 +6622,7 @@ void TestNonADLCall3() {
 // CHECK-NEXT:             }
 // CHECK-NEXT:            },
 // CHECK-NEXT:            "type": {
-// CHECK-NEXT:             "qualType": "Ts..."
+// CHECK-NEXT:             "qualType": "Ts"
 // CHECK-NEXT:            },
 // CHECK-NEXT:            "valueCategory": "lvalue",
 // CHECK-NEXT:            "referencedDecl": {
@@ -7603,7 +7603,7 @@ void TestNonADLCall3() {
 // CHECK-NEXT:           }
 // CHECK-NEXT:          },
 // CHECK-NEXT:          "type": {
-// CHECK-NEXT:           "qualType": "Ts..."
+// CHECK-NEXT:           "qualType": "Ts"
 // CHECK-NEXT:          },
 // CHECK-NEXT:          "valueCategory": "lvalue",
 // CHECK-NEXT:          "referencedDecl": {
@@ -7656,7 +7656,7 @@ void TestNonADLCall3() {
 // CHECK-NEXT:           }
 // CHECK-NEXT:          },
 // CHECK-NEXT:          "type": {
-// CHECK-NEXT:           "qualType": "Ts..."
+// CHECK-NEXT:           "qualType": "Ts"
 // CHECK-NEXT:          },
 // CHECK-NEXT:          "valueCategory": "lvalue",
 // CHECK-NEXT:          "referencedDecl": {
@@ -7707,7 +7707,7 @@ void TestNonADLCall3() {
 // CHECK-NEXT:           }
 // CHECK-NEXT:          },
 // CHECK-NEXT:          "type": {
-// CHECK-NEXT:           "qualType": "Ts..."
+// CHECK-NEXT:           "qualType": "Ts"
 // CHECK-NEXT:          },
 // CHECK-NEXT:          "valueCategory": "lvalue",
 // CHECK-NEXT:          "referencedDecl": {

diff  --git a/clang/test/AST/ast-dump-expr.cpp b/clang/test/AST/ast-dump-expr.cpp
index f04c311c6347..d52caf329f99 100644
--- a/clang/test/AST/ast-dump-expr.cpp
+++ b/clang/test/AST/ast-dump-expr.cpp
@@ -327,7 +327,7 @@ void PrimaryExpressions(Ts... a) {
   // CHECK-NEXT: CompoundStmt
   // CHECK-NEXT: FieldDecl 0x{{[^ ]*}} <col:4> col:4 implicit 'Ts...'
   // CHECK-NEXT: ParenListExpr 0x{{[^ ]*}} <col:4> 'NULL TYPE'
-  // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} <col:4> 'Ts...' lvalue ParmVar 0x{{[^ ]*}} 'a' 'Ts...'
+  // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} <col:4> 'Ts' lvalue ParmVar 0x{{[^ ]*}} 'a' 'Ts...'
   // CHECK-NEXT: CompoundStmt 0x{{[^ ]*}} <col:9, col:10>
 
   [=]{};
@@ -440,7 +440,7 @@ void PrimaryExpressions(Ts... a) {
   // CHECK-NEXT: FieldDecl 0x{{[^ ]*}} <col:4> col:4 implicit 'Ts...'
   // CHECK-NEXT: FieldDecl 0x{{[^ ]*}} <col:10> col:10 implicit 'int':'int'
   // CHECK-NEXT: ParenListExpr 0x{{[^ ]*}} <col:4> 'NULL TYPE'
-  // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} <col:4> 'Ts...' lvalue ParmVar 0x{{[^ ]*}} 'a' 'Ts...'
+  // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} <col:4> 'Ts' lvalue ParmVar 0x{{[^ ]*}} 'a' 'Ts...'
   // CHECK-NEXT: IntegerLiteral 0x{{[^ ]*}} <col:14> 'int' 12
   // CHECK-NEXT: CompoundStmt 0x{{[^ ]*}} <col:17, col:18>
 
@@ -514,17 +514,17 @@ void PrimaryExpressions(Ts... a) {
 
   (a + ...);
   // CHECK: CXXFoldExpr 0x{{[^ ]*}} <line:[[@LINE-1]]:3, col:11> '<dependent type>'
-  // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} <col:4> 'Ts...' lvalue ParmVar 0x{{[^ ]*}} 'a' 'Ts...'
+  // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} <col:4> 'Ts' lvalue ParmVar 0x{{[^ ]*}} 'a' 'Ts...'
   // CHECK-NEXT: <<<NULL>>>
 
   (... + a);
   // CHECK: CXXFoldExpr 0x{{[^ ]*}} <line:[[@LINE-1]]:3, col:11> '<dependent type>'
   // CHECK-NEXT: <<<NULL>>>
-  // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} <col:10> 'Ts...' lvalue ParmVar 0x{{[^ ]*}} 'a' 'Ts...'
+  // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} <col:10> 'Ts' lvalue ParmVar 0x{{[^ ]*}} 'a' 'Ts...'
 
   (a + ... + b);
   // CHECK: CXXFoldExpr 0x{{[^ ]*}} <line:[[@LINE-1]]:3, col:15> '<dependent type>'
-  // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} <col:4> 'Ts...' lvalue ParmVar 0x{{[^ ]*}} 'a' 'Ts...'
+  // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} <col:4> 'Ts' lvalue ParmVar 0x{{[^ ]*}} 'a' 'Ts...'
   // CHECK-NEXT: DeclRefExpr 0x{{[^ ]*}} <col:14> 'int' lvalue Var 0x{{[^ ]*}} 'b' 'int'
 }
 

diff  --git a/clang/test/Import/pack-expansion-expr/test.cpp b/clang/test/Import/pack-expansion-expr/test.cpp
index 3e9867b1a736..6866c41cfbcf 100644
--- a/clang/test/Import/pack-expansion-expr/test.cpp
+++ b/clang/test/Import/pack-expansion-expr/test.cpp
@@ -3,9 +3,10 @@
 // CHECK: PackExpansionExpr
 // CHECK-SAME: '<dependent type>'
 // CHECK-NEXT: DeclRefExpr
-// CHECK-SAME: 'T...'
+// CHECK-SAME: 'T'
 // CHECK-SAME: ParmVar
 // CHECK-SAME: 'a'
+// CHECK-SAME: 'T...'
 
 void expr() {
   f();

diff  --git a/clang/test/SemaTemplate/temp_arg_nontype.cpp b/clang/test/SemaTemplate/temp_arg_nontype.cpp
index 245b9a60269d..dd086dbb25bc 100644
--- a/clang/test/SemaTemplate/temp_arg_nontype.cpp
+++ b/clang/test/SemaTemplate/temp_arg_nontype.cpp
@@ -506,3 +506,11 @@ namespace complete_array_from_incomplete {
   extern const char *const kStrs[3] = {};
   Derived<T, kStrs> d;
 }
+
+namespace type_of_pack {
+  template<typename ...T> struct A { // expected-warning 0-1{{extension}}
+    template<T *...V> void f() {
+      g(V.f() ...); // expected-error {{base type 'T *' is not a structure or union}}
+    }
+  };
+}


        


More information about the cfe-commits mailing list