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.
This commit is contained in:
Richard Smith 2020-06-18 17:45:55 -07:00
parent 9dda41e433
commit c36b03e325
8 changed files with 39 additions and 12 deletions

View File

@ -944,6 +944,12 @@ public:
/// 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

View File

@ -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())

View File

@ -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();

View File

@ -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)

View File

@ -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": {

View File

@ -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'
}

View File

@ -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();

View File

@ -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}}
}
};
}