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

Reply via email to