[PR c++/84979] reject auto in explicit tmpl args for tmpl-fn
authorAlexandre Oliva <aoliva@redhat.com>
Thu, 5 Apr 2018 04:26:36 +0000 (04:26 +0000)
committerAlexandre Oliva <aoliva@gcc.gnu.org>
Thu, 5 Apr 2018 04:26:36 +0000 (04:26 +0000)
With concepts, we accept auto in explicit template arguments, but we
should only accept them for template classes.  Passing them to
template functions or variables is not allowed.  So, reject it, at
parse time if possible, at specialization time otherwise.

for  gcc/cp/ChangeLog

PR c++/84979
* pt.c (check_auto_in_tmpl_args): New.
(tsubst_qualified_id): Use it to reject template args
referencing auto for non-type templates.
* parser.c (cp_parser_template_id): Likewise.
* cp-tree.h (check_auto_in_tmpl_args): Declare.
* typeck2.c (build_functional_cast): Report correct location
for invalid use of auto.

for  gcc/testsuite/ChangeLog

PR c++/84979
* g++.dg/concepts/pr84979.C: New.
* g++.dg/concepts/pr84979-2.C: New.
* g++.dg/concepts/pr84979-3.C: New.

From-SVN: r259124

gcc/cp/ChangeLog
gcc/cp/cp-tree.h
gcc/cp/parser.c
gcc/cp/pt.c
gcc/cp/typeck2.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/concepts/pr84979-2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/pr84979-3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/concepts/pr84979.C [new file with mode: 0644]

index fd33f00f8d1d440ce46ba95824f313685a7dfcd5..4ae78f73b2bdceab927381ddb5911f0d27799772 100644 (file)
@@ -1,3 +1,14 @@
+2018-04-05  Alexandre Oliva <aoliva@redhat.com>
+
+       PR c++/84979
+       * pt.c (check_auto_in_tmpl_args): New.
+       (tsubst_qualified_id): Use it to reject template args
+       referencing auto for non-type templates.
+       * parser.c (cp_parser_template_id): Likewise.
+       * cp-tree.h (check_auto_in_tmpl_args): Declare.
+       * typeck2.c (build_functional_cast): Report correct location
+       for invalid use of auto.
+
 2018-04-04  Jason Merrill  <jason@redhat.com>
 
        PR c++/85215 - ICE with copy-init from conversion.
index 2b49c6eb03fcea77f208dbc1108f11df9eb0d873..dbe34c096f0fd2c7729dc2d94abb64b23e04f8e2 100644 (file)
@@ -6500,6 +6500,7 @@ extern void maybe_show_extern_c_location (void);
 
 /* in pt.c */
 extern bool check_template_shadow              (tree);
+extern bool check_auto_in_tmpl_args             (tree, tree);
 extern tree get_innermost_template_args                (tree, int);
 extern void maybe_begin_member_template_processing (tree);
 extern void maybe_end_member_template_processing (void);
index 59eb822611244d5b2edea1dd3cfbf50daf475e6e..0ffa13de5376d1252e30f273378497ae55c97cd0 100644 (file)
@@ -15826,8 +15826,16 @@ cp_parser_template_id (cp_parser *parser,
   location_t combined_loc
     = make_location (token->location, token->location, finish_loc);
 
+  /* Check for concepts autos where they don't belong.  We could
+     identify types in some cases of idnetifier TEMPL, looking ahead
+     for a CPP_SCOPE, but that would buy us nothing: we accept auto in
+     types.  We reject them in functions, but if what we have is an
+     identifier, even with none_type we can't conclude it's NOT a
+     type, we have to wait for template substitution.  */
+  if (flag_concepts && check_auto_in_tmpl_args (templ, arguments))
+    template_id = error_mark_node;
   /* Build a representation of the specialization.  */
-  if (identifier_p (templ))
+  else if (identifier_p (templ))
     template_id = build_min_nt_loc (combined_loc,
                                    TEMPLATE_ID_EXPR,
                                    templ, arguments);
index eafc110dbde9578b933cadf022425c0c34910c7a..741c578b65ba188c1a9b04e8f0231ac33887fe78 100644 (file)
@@ -14906,6 +14906,15 @@ tsubst_qualified_id (tree qualified_id, tree args,
 
   if (is_template)
     {
+      /* We may be repeating a check already done during parsing, but
+        if it was well-formed and passed then, it will pass again
+        now, and if it didn't, we wouldn't have got here.  The case
+        we want to catch is when we couldn't tell then, and can now,
+        namely when templ prior to substitution was an
+        identifier.  */
+      if (flag_concepts && check_auto_in_tmpl_args (expr, template_args))
+       return error_mark_node;
+
       if (variable_template_p (expr))
        expr = lookup_and_finish_template_variable (expr, template_args,
                                                    complain);
@@ -26550,6 +26559,49 @@ type_uses_auto (tree type)
     return find_type_usage (type, is_auto);
 }
 
+/* Report ill-formed occurrences of auto types in ARGUMENTS.  If
+   concepts are enabled, auto is acceptable in template arguments, but
+   only when TEMPL identifies a template class.  Return TRUE if any
+   such errors were reported.  */
+
+bool
+check_auto_in_tmpl_args (tree tmpl, tree args)
+{
+  /* If there were previous errors, nevermind.  */
+  if (!args || TREE_CODE (args) != TREE_VEC)
+    return false;
+
+  /* If TMPL is an identifier, we're parsing and we can't tell yet
+     whether TMPL is supposed to be a type, a function or a variable.
+     We'll only be able to tell during template substitution, so we
+     expect to be called again then.  If concepts are enabled and we
+     know we have a type, we're ok.  */
+  if (flag_concepts
+      && (identifier_p (tmpl)
+         || (DECL_P (tmpl)
+             &&  (DECL_TYPE_TEMPLATE_P (tmpl)
+                  || DECL_TEMPLATE_TEMPLATE_PARM_P (tmpl)))))
+    return false;
+
+  /* Quickly search for any occurrences of auto; usually there won't
+     be any, and then we'll avoid allocating the vector.  */
+  if (!type_uses_auto (args))
+    return false;
+
+  bool errors = false;
+
+  tree vec = extract_autos (args);
+  for (int i = 0; i < TREE_VEC_LENGTH (vec); i++)
+    {
+      tree xauto = TREE_VALUE (TREE_VEC_ELT (vec, i));
+      error_at (DECL_SOURCE_LOCATION (xauto),
+               "invalid use of %qT in template argument", xauto);
+      errors = true;
+    }
+
+  return errors;
+}
+
 /* For a given template T, return the vector of typedefs referenced
    in T for which access check is needed at T instantiation time.
    T is either  a FUNCTION_DECL or a RECORD_TYPE.
index 3aae0a362d5d8b6df4488f41b58a011655981780..3bdeae1501f5b3733230fd97bdf3d36fd2bd191e 100644 (file)
@@ -2081,7 +2081,8 @@ build_functional_cast (tree exp, tree parms, tsubst_flags_t complain)
       if (!CLASS_PLACEHOLDER_TEMPLATE (anode))
        {
          if (complain & tf_error)
-           error ("invalid use of %qT", anode);
+           error_at (DECL_SOURCE_LOCATION (TEMPLATE_TYPE_DECL (anode)),
+                     "invalid use of %qT", anode);
          return error_mark_node;
        }
       else if (!parms)
index 33abe91ede8dd7e4c5bd9a3c45fd16632bde4387..570bfdca4dfc456b36a72ac25107f784a7c86d58 100644 (file)
@@ -1,3 +1,10 @@
+2018-04-05  Alexandre Oliva <aoliva@redhat.com>
+
+       PR c++/84979
+       * g++.dg/concepts/pr84979.C: New.
+       * g++.dg/concepts/pr84979-2.C: New.
+       * g++.dg/concepts/pr84979-3.C: New.
+
 2018-04-04  Paolo Carlini  <paolo.carlini@oracle.com>
 
        PR c++/80026
diff --git a/gcc/testsuite/g++.dg/concepts/pr84979-2.C b/gcc/testsuite/g++.dg/concepts/pr84979-2.C
new file mode 100644 (file)
index 0000000..ce69a0f
--- /dev/null
@@ -0,0 +1,41 @@
+// { dg-do compile { target c++11 } }
+// { dg-options "-fconcepts" }
+
+template <typename T>
+void foo1(T& t) {
+  typename T::template C<void> tcv = t;
+  typename T::template C<auto> u = tcv;
+  T::template C<auto>::f (tcv, u); // { dg-error "incomplete" }
+  (typename T::template D<auto> (t)); // { dg-error "invalid" }
+}
+
+struct T1 {
+  template <typename T> struct C {
+    C(T1&);
+    static void f(T1&, C&);
+  };
+  template <typename T> struct D {
+    D(T1&);
+  };
+};
+
+template <typename T>
+void foo2(T& t) {
+  typename T::template C<void> tcv = t;
+  typename T::template C<auto> u = tcv;
+  T::template C<auto>::f (tcv, u); // { dg-error "incomplete" }
+  T::template D<auto> (t); // { dg-error "invalid" }
+}
+
+struct T2 {
+  template <typename T> struct C {
+    C(T2&);
+    static void f(T2&, C&);
+  };
+  template <typename T> static void D(T2&);
+};
+
+void f(T1& t1, T2& t2) {
+  foo1 (t1);
+  foo2 (t2);
+}
diff --git a/gcc/testsuite/g++.dg/concepts/pr84979-3.C b/gcc/testsuite/g++.dg/concepts/pr84979-3.C
new file mode 100644 (file)
index 0000000..3809c2d
--- /dev/null
@@ -0,0 +1,45 @@
+// { dg-do compile { target c++11 } }
+// { dg-options "-fconcepts" }
+
+// This is like pr84979-2.C, except that we swap the types passed to
+// the template functions foo1 and foo2, so that the expectations WRT
+// D's typeness are not met.
+
+template <typename T>
+void foo1(T& t) {
+  typename T::template C<void> tcv = t;
+  typename T::template C<auto> u = tcv;
+  T::template C<auto>::f (tcv, u); // { dg-error "incomplete" }
+  (typename T::template D<auto> (t)); // { dg-error "invalid" }
+}
+
+struct T1 {
+  template <typename T> struct C {
+    C(T1&);
+    static void f(T1&, C&);
+  };
+  template <typename T> struct D {
+    D(T1&);
+  };
+};
+
+template <typename T>
+void foo2(T& t) {
+  typename T::template C<void> tcv = t;
+  typename T::template C<auto> u = tcv;
+  T::template C<auto>::f (tcv, u); // { dg-error "incomplete" }
+  T::template D<auto> (t); // { dg-error "yields a type" }
+}
+
+struct T2 {
+  template <typename T> struct C {
+    C(T2&);
+    static void f(T2&, C&);
+  };
+  template <typename T> static void D(T2&);
+};
+
+void f(T1& t1, T2& t2) {
+  foo1 (t2);
+  foo2 (t1);
+}
diff --git a/gcc/testsuite/g++.dg/concepts/pr84979.C b/gcc/testsuite/g++.dg/concepts/pr84979.C
new file mode 100644 (file)
index 0000000..9bd40df
--- /dev/null
@@ -0,0 +1,9 @@
+// { dg-do compile { target c++11 } }
+// { dg-options "-fconcepts" }
+
+template<typename> void foo() {}
+
+void bar()
+{
+  foo<auto>(); // { dg-error "invalid" }
+}