diff mbox

[RFC/Patch] Latent issue in cp_parser_template_id

Message ID 53E904E9.3030503@oracle.com
State New
Headers show

Commit Message

Paolo Carlini Aug. 11, 2014, 6:01 p.m. UTC
Hi,

while working on a patchlet for the simple c++/54377 I noticed a latent 
issue, which goes normally unnoticed because template/typename17.c uses 
the catch all dg-error "". The issue is that, whereas for C++98 we give 
a sensible error message, for C++11 only the late "last resort" check in 
cp_parser_init_declarator:

   /* If the decl specifiers were bad, issue an error now that we're
      sure this was intended to be a declarator.  Then continue
      declaring the variable(s), as int, to try to cut down on further
      errors.  */
   if (decl_specifiers->any_specifiers_p
       && decl_specifiers->type == error_mark_node)
     {
       cp_parser_error (parser, "invalid type in declaration");
       decl_specifiers->type = integer_type_node;
     }

catches the problem in the typename17.C snippet. As far as I can see, 
the problem is due to the CPP_TEMPLATE_ID optimization in 
cp_parser_template_id, eg, if I disable it with an && 0 the error 
message for C++11 becomes identical to that for C++98. The reason is, 
with C++11, cp_parser_template_id is called first with none_type as 
tag_type, when the optimization mechanism triggers, and then again with 
typename_type, when normally cp_parser_template_id would return 
error_mark_node (it does in C++98) and instead succeeds, returns the 
already parsed BASELINK. At the moment my impression is that the 
CPP_TEMPLATE_ID optimization is rather fragile because in reality the 
parsing can succeed or not depending on the tag_type passes to 
cp_parser_template_id and the optimization doesn't see that. Thus, what 
to do? Something that works, is coping after the fact with the BASELINK 
in cp_parser_elaborated_type_specifier, this allows to have a sensible 
error message for typename17.C and passes testing (patchlet attached)...

If you are curious why I care that much about this issue, I have been 
also playing with removing the "last resort" check above, which would 
avoid a lot of redundant lines in the error messages (which other 
front-end doesn't seem to ever emit). If I do that, after adjusting for 
the redundant error messages, there are *no* regressions in the 
testsuite, *besides* exactly typename17.C in C++11 mode... I'm attaching 
a complete wip patch which passes testing.

Thanks!
Paolo.

///////////////////////

Index: gcc/cp/parser.c
===================================================================
--- gcc/cp/parser.c	(revision 213809)
+++ gcc/cp/parser.c	(working copy)
@@ -15194,6 +15188,12 @@ cp_parser_elaborated_type_specifier (cp_parser* pa
 	 identifier.  */
       if (!template_p && !cp_parser_parse_definitely (parser))
 	;
+      else if (tag_type == typename_type
+	       && BASELINK_P (decl))
+	{
+	  cp_parser_diagnose_invalid_type_name (parser, decl, token->location);
+	  type = error_mark_node;
+	}
       /* If DECL is a TEMPLATE_ID_EXPR, and the `typename' keyword is
 	 in effect, then we must assume that, upon instantiation, the
 	 template will correspond to a class.  */
@@ -16897,17 +16895,6 @@ cp_parser_init_declarator (cp_parser* parser,
      possibly be looking at any other construct.  */
   cp_parser_commit_to_tentative_parse (parser);
 
-  /* If the decl specifiers were bad, issue an error now that we're
-     sure this was intended to be a declarator.  Then continue
-     declaring the variable(s), as int, to try to cut down on further
-     errors.  */
-  if (decl_specifiers->any_specifiers_p
-      && decl_specifiers->type == error_mark_node)
-    {
-      cp_parser_error (parser, "invalid type in declaration");
-      decl_specifiers->type = integer_type_node;
-    }
-
   /* Enter the newly declared entry in the symbol table.  If we're
      processing a declaration in a class-specifier, we wait until
      after processing the initializer.  */
Index: gcc/testsuite/g++.dg/cpp0x/alias-decl-4.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/alias-decl-4.C	(revision 213809)
+++ gcc/testsuite/g++.dg/cpp0x/alias-decl-4.C	(working copy)
@@ -11,4 +11,4 @@ template <class T> using B = typename A<T>::U; //
 template <class T> struct A {
     typedef B<T> U;
 };
-B<short> b; // { dg-error "invalid type" }
+B<short> b;
Index: gcc/testsuite/g++.dg/cpp0x/decltype2.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/decltype2.C	(revision 213809)
+++ gcc/testsuite/g++.dg/cpp0x/decltype2.C	(working copy)
@@ -45,7 +45,6 @@ int bar(int);
 CHECK_DECLTYPE(decltype(foo), int(char));
 
 decltype(bar) z; // { dg-error "overload" "overload" }
-// { dg-error "invalid type" "invalid" { target *-*-* } 47 }
 
 CHECK_DECLTYPE(decltype(&foo), int(*)(char));
 CHECK_DECLTYPE(decltype(*&foo), int(&)(char));
Index: gcc/testsuite/g++.dg/cpp0x/decltype3.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/decltype3.C	(revision 213809)
+++ gcc/testsuite/g++.dg/cpp0x/decltype3.C	(working copy)
@@ -55,7 +55,7 @@ class B {
 
 CHECK_DECLTYPE(decltype(aa.*&A::a), int&);
 decltype(aa.*&A::b) zz; // { dg-error "cannot create pointer to reference member" "cannot" }
-// { dg-error "invalid type" "invalid type" { target *-*-* } 57 }
+
 CHECK_DECLTYPE(decltype(caa.*&A::a), const int&);
 
 class X { 
Index: gcc/testsuite/g++.dg/cpp0x/pr60249.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/pr60249.C	(revision 213809)
+++ gcc/testsuite/g++.dg/cpp0x/pr60249.C	(working copy)
@@ -2,5 +2,3 @@
 // { dg-do compile { target c++11 } }
 
 decltype(""_) x; // { dg-error "unable to find string literal operator" }
-
-// { dg-error "invalid type in declaration before" "invalid" { target *-*-* } 4 }
Index: gcc/testsuite/g++.dg/cpp0x/variadic-ex10.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/variadic-ex10.C	(revision 213809)
+++ gcc/testsuite/g++.dg/cpp0x/variadic-ex10.C	(working copy)
@@ -6,4 +6,3 @@ Tuple<int> t1; // Types contains one argument: int
 Tuple<int, float> t2; // Types contains two arguments: int and float
 Tuple<0> error; // { dg-error "mismatch" "mismatch" }
 // { dg-message "expected a type" "expected a type" { target *-*-* } 7 }
-// { dg-error "in declaration" "in declaration" { target *-*-* } 7 }
Index: gcc/testsuite/g++.dg/cpp0x/variadic-ex14.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/variadic-ex14.C	(revision 213809)
+++ gcc/testsuite/g++.dg/cpp0x/variadic-ex14.C	(working copy)
@@ -10,10 +10,8 @@ template<template<class...> class Q> class Y { /*
 X<A> xA; // okay
 X<B> xB; // { dg-error "mismatch" "mismatch" }
 // { dg-message "expected a template" "expected" { target *-*-* } 11 }
-// { dg-error "invalid type" "invalid" { target *-*-* } 11 }
 X<C> xC; // { dg-error "mismatch" "mismatch" }
-// { dg-message "expected a template" "expected" { target *-*-* } 14 }
-// { dg-error "invalid type" "invalid" { target *-*-* } 14 }
+// { dg-message "expected a template" "expected" { target *-*-* } 13 }
 Y<A> yA;
 Y<B> yB;
 Y<C> yC; // okay
Index: gcc/testsuite/g++.dg/cpp0x/variadic2.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/variadic2.C	(revision 213809)
+++ gcc/testsuite/g++.dg/cpp0x/variadic2.C	(working copy)
@@ -9,7 +9,6 @@ template<typename T1, typename T2, typename... Res
 struct two_or_more {}; // { dg-error "provided for" }
 
 typedef two_or_more<int> bad; // { dg-error "2 or more" "2 or more" }
-// { dg-error "invalid type" "invalid type" { target *-*-* } 11 }
 
 void f()
 {
Index: gcc/testsuite/g++.dg/cpp0x/variadic74.C
===================================================================
--- gcc/testsuite/g++.dg/cpp0x/variadic74.C	(revision 213809)
+++ gcc/testsuite/g++.dg/cpp0x/variadic74.C	(working copy)
@@ -20,9 +20,7 @@ A<int*, float*>::X<&i, &f> apple1;
 B<int, float>::X<&i, &f> banana1;
 
 A<int*, float*>::X<&i> apple2; // { dg-error "wrong number of template arguments" "wrong number" }
-// { dg-error "invalid type" "invalid" { target *-*-* } 22 }
 A<int*, float*>::X<&i, &f, &f> apple3; // { dg-error "wrong number of template arguments" "wrong number" }
-// { dg-error "invalid type" "invalid" { target *-*-* } 24 }
 A<int, float> apple4;
 
 // { dg-prune-output "provided for" }
Index: gcc/testsuite/g++.dg/parse/error10.C
===================================================================
--- gcc/testsuite/g++.dg/parse/error10.C	(revision 213809)
+++ gcc/testsuite/g++.dg/parse/error10.C	(working copy)
@@ -14,6 +14,4 @@ template <typename T> void foo()
   enum typename A<T>::E    e4;
 }
 
-// Here, columns nums are not very accurate either. Still acceptable though
-// { dg-error "30:invalid type in declaration before ';' token" "invalid" { target *-*-* } 14 }
-// { dg-error "30:two or more data types in declaration of 'e4'" "2 or more" { target *-*-* } 14 }
+// { dg-error "28:two or more data types in declaration of 'e4'" "2 or more" { target *-*-* } 14 }
Index: gcc/testsuite/g++.dg/parse/error15.C
===================================================================
--- gcc/testsuite/g++.dg/parse/error15.C	(revision 213809)
+++ gcc/testsuite/g++.dg/parse/error15.C	(working copy)
@@ -16,7 +16,6 @@ N::C::INVALID f4;     // { dg-error "7:'INVALID' i
 N::K f6;              // { dg-error "4:'K' in namespace 'N' does not name a type" }
 typename N::A f7;
 // { dg-error "13:invalid use of template-name 'N::A' without an argument list" "13" { target *-*-* } 17 }
-// { dg-error "17:invalid type in declaration before ';' token" "17" { target *-*-* } 17 }
 
 struct B
 {
@@ -25,7 +24,7 @@ struct B
   N::C::INVALID f4;   // { dg-error "9:'INVALID' in 'struct N::C' does not name a type" }
   N::K f6;            // { dg-error "6:'K' in namespace 'N' does not name a type" }
   typename N::A f7;
-// { dg-error "15:invalid use of template-name 'N::A' without an argument list" "15" { target *-*-* } 27 }
+// { dg-error "15:invalid use of template-name 'N::A' without an argument list" "15" { target *-*-* } 26 }
 };
 
 template <int>
Index: gcc/testsuite/g++.dg/parse/error2.C
===================================================================
--- gcc/testsuite/g++.dg/parse/error2.C	(revision 213809)
+++ gcc/testsuite/g++.dg/parse/error2.C	(working copy)
@@ -12,4 +12,3 @@ Foo<func(g)> f; // { dg-error "5:'int func.double.
 // { dg-error "10:'g' cannot appear in a constant-expression" "g" { target *-*-* } 11 }
 // { dg-error "11:a function call cannot appear in a constant-expression" "call" { target *-*-* } 11 }
 // { dg-error "12:template argument 1 is invalid" "invalid template argument" { target *-*-* } 11 }
-// { dg-error "15:invalid type in declaration before ';' token" "invalid type" { target *-*-* } 11 }
Index: gcc/testsuite/g++.dg/template/crash106.C
===================================================================
--- gcc/testsuite/g++.dg/template/crash106.C	(revision 213809)
+++ gcc/testsuite/g++.dg/template/crash106.C	(working copy)
@@ -9,6 +9,6 @@ struct A
 
 template<T N = 0, void (A::*)() = &A::foo<N> > struct B {}; // { dg-error "type|declared" }
 
-B<> b; // { dg-error "type|declaration" }
+B<> b; // { dg-message "non-type" }
 
 // { dg-prune-output "could not convert" }
Index: gcc/testsuite/g++.dg/template/crash89.C
===================================================================
--- gcc/testsuite/g++.dg/template/crash89.C	(revision 213809)
+++ gcc/testsuite/g++.dg/template/crash89.C	(working copy)
@@ -5,6 +5,4 @@ template<typename T, int = T()[0]> struct A // { d
   typedef A<T> B;
 };
 
-A<int> a; // { dg-error "declaration" }
-
-// { dg-prune-output "template argument 2 is invalid" }
+A<int> a; // { dg-error "template argument 2 is invalid" }
Index: gcc/testsuite/g++.dg/template/nontype7.C
===================================================================
--- gcc/testsuite/g++.dg/template/nontype7.C	(revision 213809)
+++ gcc/testsuite/g++.dg/template/nontype7.C	(working copy)
@@ -10,6 +10,3 @@ char p[] = "Vivisectionist";
 
 X<int,"Studebaker"> x1;    // { dg-error "string literal" }
 X<int, p> x2;
-
-// { dg-bogus "" "additional errors" { xfail *-*-* } 11 }
-
Index: gcc/testsuite/g++.dg/template/void3.C
===================================================================
--- gcc/testsuite/g++.dg/template/void3.C	(revision 213809)
+++ gcc/testsuite/g++.dg/template/void3.C	(working copy)
@@ -1,5 +1,5 @@
 //PR c++/28637
 
 template<void> struct A {};  // { dg-error "not a valid type" }
-A<0> a;                      // { dg-error "type" }
+A<0> a;                      // { dg-message "non-type" }
 
Index: gcc/testsuite/g++.dg/template/void7.C
===================================================================
--- gcc/testsuite/g++.dg/template/void7.C	(revision 213809)
+++ gcc/testsuite/g++.dg/template/void7.C	(working copy)
@@ -5,4 +5,4 @@ template<void> struct A         // { dg-error "not
   static int i;
 };
 
-A<0> a;                        // { dg-error "invalid type|not a valid type" }
+A<0> a;                        // { dg-message "non-type" }
Index: libstdc++-v3/testsuite/26_numerics/random/linear_congruential_engine/requirements/non_uint_neg.cc
===================================================================
--- libstdc++-v3/testsuite/26_numerics/random/linear_congruential_engine/requirements/non_uint_neg.cc	(revision 213809)
+++ libstdc++-v3/testsuite/26_numerics/random/linear_congruential_engine/requirements/non_uint_neg.cc	(working copy)
@@ -21,8 +21,7 @@
 // { dg-do compile }
 // { dg-options "-std=c++0x" }
 // { dg-require-cstdint "" }
-// { dg-error "not a valid type" "" { target *-*-* } 32 }
-// { dg-error "invalid type"     "" { target *-*-* } 32 }
+// { dg-error "not a valid type" "" { target *-*-* } 31 }
 
 // 26.4.3.1 class template linear_congruential_engine [rand.eng.lcong]
 // 26.4.2.2 Concept RandomNumberEngine [rand.concept.eng]
@@ -30,4 +29,3 @@
 #include <random>
 
 std::linear_congruential_engine<double, 48271, 0, 2147483647> x;
-
Index: libstdc++-v3/testsuite/tr1/5_numerical_facilities/random/linear_congruential/requirements/non_uint_neg.cc
===================================================================
--- libstdc++-v3/testsuite/tr1/5_numerical_facilities/random/linear_congruential/requirements/non_uint_neg.cc	(revision 213809)
+++ libstdc++-v3/testsuite/tr1/5_numerical_facilities/random/linear_congruential/requirements/non_uint_neg.cc	(working copy)
@@ -19,8 +19,7 @@
 
 // { dg-do compile }
 // { dg-options "-D_GLIBCXX_CONCEPT_CHECKS" }
-// { dg-error "not a valid type" "" { target *-*-* } 30 }
-// { dg-error "invalid type"     "" { target *-*-* } 30 }
+// { dg-error "not a valid type" "" { target *-*-* } 29 }
 
 // 5.1.4.1 class template linear_congruential [tr.rand.eng.lcong]
 // 5.1.4.1 [4]
@@ -28,4 +27,3 @@
 #include <tr1/random>
  
 std::tr1::linear_congruential<double, 48271, 0, 2147483647> x;
-

Comments

Jason Merrill Aug. 11, 2014, 6:22 p.m. UTC | #1
OK with a comment explaining how we can get there.

Jason
diff mbox

Patch

Index: gcc/cp/parser.c
===================================================================
--- gcc/cp/parser.c	(revision 213809)
+++ gcc/cp/parser.c	(working copy)
@@ -15194,6 +15188,12 @@  cp_parser_elaborated_type_specifier (cp_parser* pa
 	 identifier.  */
       if (!template_p && !cp_parser_parse_definitely (parser))
 	;
+      else if (tag_type == typename_type
+	       && BASELINK_P (decl))
+	{
+	  cp_parser_diagnose_invalid_type_name (parser, decl, token->location);
+	  type = error_mark_node;
+	}
       /* If DECL is a TEMPLATE_ID_EXPR, and the `typename' keyword is
 	 in effect, then we must assume that, upon instantiation, the
 	 template will correspond to a class.  */