On Fri, 2015-12-04 at 11:01 -0500, Jason Merrill wrote:
> On 12/03/2015 05:08 PM, David Malcolm wrote:
> > 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?
>
> Ah, I see, the error is coming from tsubst_copy_and_build, not
> cp_parser_non_integral_constant_expression. So indeed this is an effect
> of the canonicalization of template instances, and we aren't going to
> fix it in the context of this patchset. But this is still a bug, so I'd
> rather have an xfail and a PR than change the expected output.
Is the following what you had in mind?
gcc/testsuite/ChangeLog:
* g++.dg/template/ref3.C: Add XFAIL (PR c++/68699).
---
gcc/testsuite/g++.dg/template/ref3.C | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/gcc/testsuite/g++.dg/template/ref3.C
b/gcc/testsuite/g++.dg/template/ref3.C
index 976c093..91e3c93 100644
--- a/gcc/testsuite/g++.dg/template/ref3.C
+++ b/gcc/testsuite/g++.dg/template/ref3.C
@@ -5,7 +5,8 @@ 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)> a; // { dg-error "constant|not a valid" "" { xfail c++98_only } }
+ // PR c++/68699
};
B<const int&> b;
--
1.8.5.3