On Thu, 2015-12-03 at 15:38 -0500, Jason Merrill wrote: > On 12/03/2015 09:55 AM, David Malcolm wrote: > > Testcase g++.dg/template/ref3.C: > > > > 1 // PR c++/28341 > > 2 > > 3 template<const int&> struct A {}; > > 4 > > 5 template<typename T> struct B > > 6 { > > 7 A<(T)0> b; // { dg-error "constant|not a valid" } > > 8 A<T(0)> a; // { dg-error "constant|not a valid" } > > 9 }; > > 10 > > 11 B<const int&> b; > > > > The output of this test for both c++11 and c++14 is unaffected > > by the patch kit: > > g++.dg/template/ref3.C: In instantiation of 'struct B<const int&>': > > g++.dg/template/ref3.C:11:15: required from here > > g++.dg/template/ref3.C:7:11: error: '0' is not a valid template argument > > for type 'const int&' because it is not an lvalue > > g++.dg/template/ref3.C:8:11: error: '0' is not a valid template argument > > for type 'const int&' because it is not an lvalue > > > > However, the c++98 output is changed: > > > > Status quo for c++98: > > g++.dg/template/ref3.C: In instantiation of 'struct B<const int&>': > > g++.dg/template/ref3.C:11:15: required from here > > g++.dg/template/ref3.C:7:11: error: a cast to a type other than an integral > > or enumeration type cannot appear in a constant-expression > > g++.dg/template/ref3.C:8:11: error: a cast to a type other than an integral > > or enumeration type cannot appear in a constant-expression > > > > (line 7 and 8 are at the closing semicolon for fields b and a) > > > > With the patchkit for c++98: > > g++.dg/template/ref3.C: In instantiation of 'struct B<const int&>': > > g++.dg/template/ref3.C:11:15: required from here > > g++.dg/template/ref3.C:7:5: error: a cast to a type other than an integral > > or enumeration type cannot appear in a constant-expression > > g++.dg/template/ref3.C:7:5: error: a cast to a type other than an integral > > or enumeration type cannot appear in a constant-expression > > > > So the 2nd: > > "error: a cast to a type other than an integral or enumeration type > > cannot appear in a constant-expression" > > moves from line 8 to line 7 (and moves them to earlier, having ranges) > > > > What's happening is that cp_parser_enclosed_template_argument_list > > builds a CAST_EXPR, the first time from cp_parser_cast_expression, > > the second time from cp_parser_functional_cast; these have locations > > representing the correct respective caret&ranges, i.e.: > > > > A<(T)0> b; > > ^~~~ > > > > and: > > > > A<T(0)> a; > > ^~~~ > > > > Eventually finish_template_type is called for each, to build a RECORD_TYPE, > > and we get a cache hit the 2nd time through here in pt.c: > > 8281 hash = spec_hasher::hash (&elt); > > 8282 entry = type_specializations->find_with_hash (&elt, hash); > > 8283 > > 8284 if (entry) > > 8285 return entry->spec; > > > > due to: > > template_args_equal (ot=<cast_expr 0x7ffff19bc400>, nt=<cast_expr > > 0x7ffff19bc480>) at ../../src/gcc/cp/pt.c:7778 > > which calls: > > cp_tree_equal (t1=<cast_expr 0x7ffff19bc400>, t2=<cast_expr > > 0x7ffff19bc480>) at ../../src/gcc/cp/tree.c:2833 > > and returns equality. > > > > Hence we get a single RECORD_TYPE for the type A<(T)(0)>, and hence > > when issuing the errors it uses the TREE_VEC for the first one, > > using the location of the first line. > > Why does the type sharing affect where the parser gives the error?
I believe what's happening is that the patchkit is setting location_t values for more expressions than before, including the expression for the template param. pt.c:tsubst_expr has this: if (EXPR_HAS_LOCATION (t)) input_location = EXPR_LOCATION (t); I believe that before (in the status quo), the substituted types didn't have location_t values, and hence the above conditional didn't fire; input_location was coming from a *token* where the expansion happened, hence we got an error message on the relevant line for each expansion. With the patch, the substituted types have location_t values within their params, hence the conditional above fires: input_location is updated to use the EXPR_LOCATION, which comes from that of the param within the type - but with type-sharing it's using the first place where the type is created. Perhaps a better fix is for cp_parser_non_integral_constant_expression to take a location_t, rather than have it rely on input_location? > > I'm not sure what the ideal fix for this is; for now I've worked > > around it by updating the dg directives to reflect the new output. > > > > gcc/testsuite/ChangeLog: > > * g++.dg/template/ref3.C: Update locations of dg directives. > > --- > > gcc/testsuite/g++.dg/template/ref3.C | 6 ++++-- > > 1 file changed, 4 insertions(+), 2 deletions(-) > > > > diff --git a/gcc/testsuite/g++.dg/template/ref3.C > > b/gcc/testsuite/g++.dg/template/ref3.C > > index 976c093..6e568c3 100644 > > --- a/gcc/testsuite/g++.dg/template/ref3.C > > +++ b/gcc/testsuite/g++.dg/template/ref3.C > > @@ -4,8 +4,10 @@ template<const int&> struct A {}; > > > > template<typename T> struct B > > { > > - A<(T)0> b; // { dg-error "constant|not a valid" } > > - A<T(0)> a; // { dg-error "constant|not a valid" } > > + A<(T)0> b; // { dg-error "constant" "" { target c++98_only } } > > + // { dg-error "not a valid" "" { target c++11 } 7 } > > + > > + A<T(0)> a; // { dg-error "not a valid" "" { target c++11 } } > > }; > > > > B<const int&> b; > > >