On Fri, 2 Aug 2024, Jason Merrill wrote:

> On 8/1/24 2:52 PM, Patrick Palka wrote:
> > In recent versions of GCC we've been diagnosing more and more kinds of
> > errors inside a template ahead of time.  This is a largely good thing
> > because it catches bugs, typos, dead code etc sooner.
> > 
> > But if the template never gets instantiated then such errors are
> > harmless, and can be inconvenient to work around if say the code in
> > question is third party and in maintenence mode.  So it'd be useful to
> 
> "maintenance"

Fixed

> 
> > diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
> > index d80bac822ba..0bb0a482e28 100644
> > --- a/gcc/cp/error.cc
> > +++ b/gcc/cp/error.cc
> > @@ -165,6 +165,58 @@ class cxx_format_postprocessor : public
> > format_postprocessor
> >     deferred_printed_type m_type_b;
> >   };
> >   +/* A map from TEMPLATE_DECL to the location of the first error (if any)
> > +   within the template that we permissivly downgraded to a warning.  */
> 
> "permissively"

Fixed

> 
> > +relaxed_template_errors_t *relaxed_template_errors;
> > +
> > +/* Callback function diagnostic_context::m_adjust_diagnostic_info.
> > +
> > +   In -fpermissive mode we downgrade errors within a template to
> > +   warnings, and only issue an error if we later need to instantiate
> > +   the template.  */
> > +
> > +static void
> > +cp_adjust_diagnostic_info (diagnostic_context *context,
> > +                      diagnostic_info *diagnostic)
> > +{
> > +  tree ti;
> > +  if (diagnostic->kind == DK_ERROR
> > +      && context->m_permissive
> > +      && !current_instantiation ()
> > +      && in_template_context
> > +      && (ti = get_template_info (current_scope ())))
> > +    {
> > +      if (!relaxed_template_errors)
> > +   relaxed_template_errors = new relaxed_template_errors_t;
> > +
> > +      tree tmpl = TI_TEMPLATE (ti);
> > +      if (!relaxed_template_errors->get (tmpl))
> > +   relaxed_template_errors->put (tmpl, diagnostic->richloc->get_loc ());
> > +      diagnostic->kind = DK_WARNING;
> 
> Rather than check m_permissive directly and downgrade to DK_WARNING, how about
> downgrading to DK_PERMERROR?  That way people will get the [-fpermissive]
> clue.
> 
> ...though I suppose DK_PERMERROR doesn't work where you call this hook in
> report_diagnostic, at which point we've already reassigned it into DK_WARNING
> or DK_ERROR in diagnostic_impl.
> 
> But we could still set diagnostic->option_index even for DK_ERROR, whether to
> context->m_opt_permissive or to its own warning flag, perhaps
> -Wno-template-body?

Fixed by adding an enabled-by-default -Wtemplate-body flag and setting
option_index to it for each downgraded error.  Thus -permissive
-Wno-template-body would suppress the downgraded warnings entirely, and
only issue a generic error upon instantiation of the erroneous template.

> 
> > +/* A generalization of seen_error which also returns true if we've
> > +   permissively downgraded an error to a warning inside a template.  */
> > +
> > +bool
> > +cp_seen_error ()
> > +{
> > +#undef seen_error
> > +  if (seen_error ())
> > +    return true;
> > +
> > +  tree ti;
> > +  if (relaxed_template_errors
> > +      && in_template_context
> > +      && (ti = get_template_info (current_scope ()))
> 
> Let's factor the "in template body" checks in this function and the previous
> one into a separate function; I expect we want the !current_instantiation test
> in this case as well?

Done.
> 
> > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> > index 77fa5907c3d..b58ccb318d5 100644
> > --- a/gcc/cp/pt.cc
> > +++ b/gcc/cp/pt.cc
> > @@ -12376,6 +12376,22 @@ instantiate_class_template (tree type)
> >     if (! push_tinst_level (type))
> >       return type;
> >   +  if (relaxed_template_errors)
> > +    if (location_t *error_loc = relaxed_template_errors->get (templ))
> > +      {
> > +   /* We're trying to instantiate a template pattern containing
> > +      an error that we've permissively downgraded to a warning.
> > +      Issue a hard error now.  */
> > +   location_t decl_loc = location_of (templ);
> > +   error_at (decl_loc, "instantiating erroneous template pattern");
> 
> I wouldn't use the internal term "pattern" in diagnostics, I think just
> "erroneous template" is enough.

Fixed

> 
> > @@ -27291,6 +27307,20 @@ instantiate_decl (tree d, bool defer_ok, bool
> > expl_inst_class_mem_p)
> >     }
> >       }
> >   +  if (relaxed_template_errors)
> > +    if (location_t *error_loc = relaxed_template_errors->get (td))
> > +      {
> > +   /* We're trying to instantiate a template pattern containing
> > +      an error that we've permissively downgraded to a warning.
> > +      Issue a hard error now.  */
> > +   location_t decl_loc = location_of (td);
> > +   error_at (decl_loc, "instantiating erroneous template pattern");
> 
> Likewise.

Fixed.

> 
> > diff --git a/gcc/diagnostic.cc b/gcc/diagnostic.cc
> > index 71d2f44e40c..cbd2c82f19d 100644
> > --- a/gcc/diagnostic.cc
> > +++ b/gcc/diagnostic.cc
> > @@ -219,6 +219,7 @@ diagnostic_context::initialize (int n_opts)
> >     m_warn_system_headers = false;
> >     m_max_errors = 0;
> >     m_internal_error = nullptr;
> > +  m_adjust_diagnostic_info = nullptr;
> >     m_text_callbacks.m_begin_diagnostic = default_diagnostic_starter;
> >     m_text_callbacks.m_start_span = default_diagnostic_start_span_fn;
> >     m_text_callbacks.m_end_diagnostic = default_diagnostic_finalizer;
> > @@ -1409,6 +1410,9 @@ diagnostic_context::report_diagnostic (diagnostic_info
> > *diagnostic)
> >        flush diagnostics with on_end_group when the topmost group is ended.
> > */
> >     gcc_assert (m_diagnostic_groups.m_nesting_depth > 0);
> >   +  if (m_adjust_diagnostic_info)
> > +    m_adjust_diagnostic_info (this, diagnostic);
> 
> I wonder about the ordering of calling the hook vs setting was_warning. I
> suppose it doesn't make a practical difference in this case; if it ends up as
> a warning it'll be suppressed by -w or -Wno-system-header whether or not
> was_warning is set.
> 
> It would make a difference if a hook wanted to upgrade warning to error; in
> this position -w would not effect such an upgraded diagnostic.
> 
> I lean toward moving the call after the was_warning bits, so was_warning
> reflects whether the diagnostic was a warning before this adjustment just as
> it does before other adjustments.

Makes sense, fixed.

Here's v2, bootstrap and regtest in progress, like so?

-- >8 --

Subject: [PATCH] c++: permit errors inside uninstantiated templates [PR116064]

In recent versions of GCC we've been diagnosing more and more kinds of
errors inside a template ahead of time.  This is a largely good thing
because it catches bugs, typos, dead code etc sooner.

But if the template never gets instantiated then such errors are harmless
and can be inconvenient to work around if say the code in question is
third party and in maintenance mode.  So it'd be handy to be able to
prevent these template errors from rendering the entire TU uncompilable.
(Note that such code is "ill-formed no diagnostic required" according
the standard.)

To that end this patch extends -fpermissive to downgrade any errors
issued within a template to warnings.  If the template containing a
downgraded error later needs to be instantiated, we'll issue an error
then.  But if the template never gets instantiated then the downgraded
error won't affect validity of the rest of the TU.

This is implemented via a diagnostic hook that gets called for each
diagnostic, and which downgrades an error diagnostic if we detect it's
occurring from a template context (and -fpermissive is active) and
additionally flags this template context as containing a "relaxed"
error.

As an example, permissive-error1a.C gives:

gcc/testsuite/g++.dg/template/permissive-error1a.C: In function ‘void f()’:
gcc/testsuite/g++.dg/template/permissive-error1a.C:7:5: warning: increment of 
read-only variable ‘n’ [-Wtemplate-body]
    7 |   ++n;
      |     ^
...
gcc/testsuite/g++.dg/template/permissive-error1a.C: In instantiation of ‘void 
f() [with T = int]’:
gcc/testsuite/g++.dg/template/permissive-error1a.C:26:9:   required from here
   26 |   f<int>();
      |   ~~~~~~^~
gcc/testsuite/g++.dg/template/permissive-error1a.C:5:6: error: instantiating 
erroneous template pattern
    5 | void f() {
      |      ^
gcc/testsuite/g++.dg/template/permissive-error1a.C:7:5: note: first error 
appeared here
    7 |   ++n; // {
      |     ^
...

        PR c++/116064

gcc/c-family/ChangeLog:

        * c.opt (Wtemplate-body): New warning.

gcc/cp/ChangeLog:

        * cp-tree.h (relaxed_template_errors_t): Declare.
        (related_template_errors): Declare.
        (cp_seen_error): Declare.
        (seen_error): #define to cp_seen_error.
        * error.cc (get_current_template): Define.
        (relaxed_template_errors): Define.
        (cp_adjust_diagnostic_info): Define.
        (cp_seen_error): Define.
        (cxx_initialize_diagnostics): Set
        diagnostic_context::m_adjust_diagnostic_info.
        * pt.cc (instantiate_class_template): Issue a hard error
        when trying to instantiate a template pattern containing
        a permissively downgraded error.
        (instantiate_decl): Likewise.

gcc/ChangeLog:

        * diagnostic.cc (diagnostic_context::initialize): Set
        m_adjust_diagnostic_info.
        (diagnostic_context::report_diagnostic): Call
        m_adjust_diagnostic_info.
        * diagnostic.h (diagnostic_context::m_adjust_diagnostic_info):
        New data member.
        * doc/invoke.texi (-Wno-template-body): Document.

gcc/testsuite/ChangeLog:

        * g++.dg/ext/typedef-init.C: Downgrade error inside template
        to warning due to -fpermissive.
        * g++.dg/pr84492.C: Likewise.
        * g++.old-deja/g++.pt/crash51.C: Remove unneeded dg-options.
        * g++.dg/template/permissive-error1.C: New test.
        * g++.dg/template/permissive-error1a.C: New test.
        * g++.dg/template/permissive-error1b.C: New test.
---
 gcc/c-family/c.opt                            |  4 ++
 gcc/cp/cp-tree.h                              |  5 ++
 gcc/cp/error.cc                               | 61 +++++++++++++++++++
 gcc/cp/pt.cc                                  | 30 +++++++++
 gcc/diagnostic.cc                             |  4 ++
 gcc/diagnostic.h                              |  4 ++
 gcc/doc/invoke.texi                           | 10 ++-
 gcc/testsuite/g++.dg/ext/typedef-init.C       |  2 +-
 gcc/testsuite/g++.dg/pr84492.C                |  4 +-
 .../g++.dg/template/permissive-error1.C       | 20 ++++++
 .../g++.dg/template/permissive-error1a.C      | 33 ++++++++++
 .../g++.dg/template/permissive-error1b.C      | 30 +++++++++
 gcc/testsuite/g++.old-deja/g++.pt/crash51.C   |  1 -
 13 files changed, 203 insertions(+), 5 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/template/permissive-error1.C
 create mode 100644 gcc/testsuite/g++.dg/template/permissive-error1a.C
 create mode 100644 gcc/testsuite/g++.dg/template/permissive-error1b.C

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index a52682d835c..78f299dd4a6 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1420,6 +1420,10 @@ Wtautological-compare
 C ObjC C++ ObjC++ Var(warn_tautological_compare) Warning LangEnabledBy(C ObjC 
C++ ObjC++,Wall)
 Warn if a comparison always evaluates to true or false.
 
+Wtemplate-body
+C++ ObjC++ Var(warn_template_body) Warning Init(1)
+Downgrade errors within templates into warnings in -fpermissive mode.
+
 Wtemplate-id-cdtor
 C++ ObjC++ Var(warn_template_id_cdtor) Warning
 Warn about simple-template-id in a constructor or destructor.
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 238d786b067..ffdc0ba38ae 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7189,6 +7189,11 @@ extern bool pedwarn_cxx98                       
(location_t, int, const char *,
 extern location_t location_of                   (tree);
 extern void qualified_name_lookup_error                (tree, tree, tree,
                                                 location_t);
+using relaxed_template_errors_t
+  = hash_map<tree, location_t, simple_hashmap_traits<tree_decl_hash, 
location_t>>;
+extern relaxed_template_errors_t *relaxed_template_errors;
+extern bool cp_seen_error                      (void);
+#define seen_error cp_seen_error
 
 /* in except.cc */
 extern void init_terminate_fn                  (void);
diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc
index d80bac822ba..92ab30efb03 100644
--- a/gcc/cp/error.cc
+++ b/gcc/cp/error.cc
@@ -165,6 +165,66 @@ class cxx_format_postprocessor : public 
format_postprocessor
   deferred_printed_type m_type_b;
 };
 
+/* Return the in-scope template that's currently being parsed, or
+   NULL_TREE otherwise.  */
+
+static tree
+get_current_template ()
+{
+  if (in_template_context && !current_instantiation ())
+    if (tree ti = get_template_info (current_scope ()))
+      return TI_TEMPLATE (ti);
+
+  return NULL_TREE;
+}
+
+/* A map from TEMPLATE_DECL to the location of the first error (if any)
+   within the template that we permissively downgraded to a warning.  */
+
+relaxed_template_errors_t *relaxed_template_errors;
+
+/* Callback function diagnostic_context::m_adjust_diagnostic_info.
+
+   In -fpermissive mode we downgrade errors within a template to
+   warnings, and only issue an error if we later need to instantiate
+   the template.  */
+
+static void
+cp_adjust_diagnostic_info (diagnostic_context *context,
+                          diagnostic_info *diagnostic)
+{
+  if (diagnostic->kind == DK_ERROR && context->m_permissive)
+    if (tree tmpl = get_current_template ())
+      {
+       if (!relaxed_template_errors)
+         relaxed_template_errors = new relaxed_template_errors_t;
+
+       if (!relaxed_template_errors->get (tmpl))
+         relaxed_template_errors->put (tmpl, diagnostic->richloc->get_loc ());
+       diagnostic->kind = DK_WARNING;
+       diagnostic->option_index = OPT_Wtemplate_body;
+      }
+}
+
+/* A generalization of seen_error which also returns true if we've
+   permissively downgraded an error to a warning inside a template.  */
+
+bool
+cp_seen_error ()
+{
+  /* cp-tree.h #defines seen_error to cp_seen_error.  */
+#undef seen_error
+  if (seen_error ())
+    return true;
+
+  if (relaxed_template_errors)
+    if (tree tmpl = get_current_template ())
+      if (relaxed_template_errors->get (tmpl))
+       return true;
+
+  return false;
+}
+
 /* CONTEXT->printer is a basic pretty printer that was constructed
    presumably by diagnostic_initialize(), called early in the
    compiler's initialization process (in general_init) Before the FE
@@ -187,6 +247,7 @@ cxx_initialize_diagnostics (diagnostic_context *context)
   diagnostic_starter (context) = cp_diagnostic_starter;
   /* diagnostic_finalizer is already c_diagnostic_finalizer.  */
   diagnostic_format_decoder (context) = cp_printer;
+  context->m_adjust_diagnostic_info = cp_adjust_diagnostic_info;
   pp_format_postprocessor (pp) = new cxx_format_postprocessor ();
 }
 
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 77fa5907c3d..3aa64b3d41c 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -12376,6 +12376,22 @@ instantiate_class_template (tree type)
   if (! push_tinst_level (type))
     return type;
 
+  if (relaxed_template_errors)
+    if (location_t *error_loc = relaxed_template_errors->get (templ))
+      {
+       /* We're trying to instantiate a template pattern containing
+          an error that we've permissively downgraded to a warning.
+          Issue a hard error now.  */
+       location_t decl_loc = location_of (templ);
+       error_at (decl_loc, "instantiating erroneous template");
+       inform (*error_loc, "first error appeared here");
+
+       pop_tinst_level ();
+       TYPE_BEING_DEFINED (type) = false;
+       CLASSTYPE_ERRONEOUS (type) = true;
+       return type;
+      }
+
   int saved_unevaluated_operand = cp_unevaluated_operand;
   int saved_inhibit_evaluation_warnings = c_inhibit_evaluation_warnings;
 
@@ -27291,6 +27307,20 @@ instantiate_decl (tree d, bool defer_ok, bool 
expl_inst_class_mem_p)
        }
     }
 
+  if (relaxed_template_errors)
+    if (location_t *error_loc = relaxed_template_errors->get (td))
+      {
+       /* We're trying to instantiate a template pattern containing
+          an error that we've permissively downgraded to a warning.
+          Issue a hard error now.  */
+       location_t decl_loc = location_of (td);
+       error_at (decl_loc, "instantiating erroneous template");
+       inform (*error_loc, "first error appeared here");
+
+       pop_tinst_level ();
+       return d;
+      }
+
   code_pattern = DECL_TEMPLATE_RESULT (td);
 
   /* We should never be trying to instantiate a member of a class
diff --git a/gcc/diagnostic.cc b/gcc/diagnostic.cc
index 71d2f44e40c..e90c121cb95 100644
--- a/gcc/diagnostic.cc
+++ b/gcc/diagnostic.cc
@@ -219,6 +219,7 @@ diagnostic_context::initialize (int n_opts)
   m_warn_system_headers = false;
   m_max_errors = 0;
   m_internal_error = nullptr;
+  m_adjust_diagnostic_info = nullptr;
   m_text_callbacks.m_begin_diagnostic = default_diagnostic_starter;
   m_text_callbacks.m_start_span = default_diagnostic_start_span_fn;
   m_text_callbacks.m_end_diagnostic = default_diagnostic_finalizer;
@@ -1416,6 +1417,9 @@ diagnostic_context::report_diagnostic (diagnostic_info 
*diagnostic)
   if (was_warning && m_inhibit_warnings)
     return false;
 
+  if (m_adjust_diagnostic_info)
+    m_adjust_diagnostic_info (this, diagnostic);
+
   if (diagnostic->kind == DK_PEDWARN)
     {
       diagnostic->kind = m_pedantic_errors ? DK_ERROR : DK_WARNING;
diff --git a/gcc/diagnostic.h b/gcc/diagnostic.h
index 79386ccbf85..d3d77c85e9a 100644
--- a/gcc/diagnostic.h
+++ b/gcc/diagnostic.h
@@ -699,6 +699,10 @@ public:
   /* Client hook to report an internal error.  */
   void (*m_internal_error) (diagnostic_context *, const char *, va_list *);
 
+  /* Client hook to adjust properties of the given diagnostic that we're
+     about to issue, such as its kind.  */
+  void (*m_adjust_diagnostic_info)(diagnostic_context *, diagnostic_info *);
+
 private:
   /* Client-supplied callbacks for working with options.  */
   struct {
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index ef2213b4e84..598016cc58a 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -270,7 +270,8 @@ in the following sections.
 -Wno-non-template-friend  -Wold-style-cast
 -Woverloaded-virtual  -Wno-pmf-conversions -Wself-move -Wsign-promo
 -Wsized-deallocation  -Wsuggest-final-methods
--Wsuggest-final-types  -Wsuggest-override  -Wno-template-id-cdtor
+-Wsuggest-final-types  -Wsuggest-override  -Wno-template-body
+-Wno-template-id-cdtor
 -Wno-terminate  -Wno-vexing-parse  -Wvirtual-inheritance
 -Wno-virtual-move-assign  -Wvolatile  -Wzero-as-null-pointer-constant}
 
@@ -4634,6 +4635,13 @@ namespaces, and this may be used to enforce that rule.  
The warning is
 inactive inside a system header file, such as the STL, so one can still
 use the STL.  One may also use using directives and qualified names.
 
+@opindex Wtemplate-body
+@opindex Wno-template-body
+@item -Wno-template-body @r{(C++ and Objective-C++ only)}
+In @option{-fpermissive} mode, errors inside templates are downgraded
+into warnings, and a proper error is issued only upon instantiation of
+the erroneous template.  This option controls emission of these warnings.
+
 @opindex Wtemplate-id-cdtor
 @opindex Wno-template-id-cdtor
 @item -Wno-template-id-cdtor @r{(C++ and Objective-C++ only)}
diff --git a/gcc/testsuite/g++.dg/ext/typedef-init.C 
b/gcc/testsuite/g++.dg/ext/typedef-init.C
index 153303d217b..47a6642de51 100644
--- a/gcc/testsuite/g++.dg/ext/typedef-init.C
+++ b/gcc/testsuite/g++.dg/ext/typedef-init.C
@@ -32,5 +32,5 @@ struct S {
 
 template<int> void foo()
 {
-    typedef int i = 0; /* { dg-error "is initialized" } */
+    typedef int i = 0; /* { dg-warning "is initialized" } */
 }
diff --git a/gcc/testsuite/g++.dg/pr84492.C b/gcc/testsuite/g++.dg/pr84492.C
index 1a2922096d1..08f368ff29b 100644
--- a/gcc/testsuite/g++.dg/pr84492.C
+++ b/gcc/testsuite/g++.dg/pr84492.C
@@ -3,7 +3,7 @@
 
 template<int> int foo()
 {
-  return ({ foo; }); // { dg-error "insufficient context" }
+  return ({ foo; }); // { dg-warning "insufficient context" }
 }
 
 int bar()
@@ -35,6 +35,6 @@ class C
   }
   bool g(int)
   {
-    return ({ g; }); // { dg-error "insufficient context" }
+    return ({ g; }); // { dg-warning "insufficient context" }
   }
 };
diff --git a/gcc/testsuite/g++.dg/template/permissive-error1.C 
b/gcc/testsuite/g++.dg/template/permissive-error1.C
new file mode 100644
index 00000000000..e4536a8b90e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/permissive-error1.C
@@ -0,0 +1,20 @@
+// PR c++/116064
+// { dg-additional-options -fpermissive }
+
+template<class T>
+void f() {
+  const int n = 42;
+  ++n; // { dg-warning "read-only" }
+}
+
+template<class T>
+struct A {
+  void f(typename A::type); // { dg-warning "does not name a type" }
+};
+
+template<class T>
+struct B {
+  void f() {
+    this->g(); // { dg-warning "no member" }
+  }
+};
diff --git a/gcc/testsuite/g++.dg/template/permissive-error1a.C 
b/gcc/testsuite/g++.dg/template/permissive-error1a.C
new file mode 100644
index 00000000000..c4b5f760ff5
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/permissive-error1a.C
@@ -0,0 +1,33 @@
+// PR c++/116064
+// { dg-additional-options -fpermissive }
+// Like permissive-error1.C but verify instantiating the errorneous
+// templates gives an error after all.
+
+template<class T>
+void f() {  // { dg-error "instantiating erroneous template" }
+  const int n = 42;
+  ++n; // { dg-warning "read-only" }
+       // { dg-message "first error appeared here" "" { target *-*-* } .-1 }
+}
+
+template<class T>
+struct A { // { dg-error "instantiating erroneous template" }
+  void f(typename A::type); // { dg-warning "does not name a type" }
+                           // { dg-message "first error appeared here" "" { 
target *-*-* } .-1 }
+};
+
+template<class T>
+struct B {
+  void f() {   // { dg-error "instantiating erroneous template" }
+    this->g(); // { dg-warning "no member" }
+              // { dg-message "first error appeared here" "" { target *-*-* } 
.-1 }
+  }
+};
+
+int main() {
+  f<int>(); // { dg-message "required from here" }
+  A<int> a; // { dg-message "required from here" }
+           // { dg-error "incomplete type" "" { target *-*-* } .-1 }
+  B<int> b; // { dg-bogus "" }
+  b.f();    // { dg-message "required from here" }
+}
diff --git a/gcc/testsuite/g++.dg/template/permissive-error1b.C 
b/gcc/testsuite/g++.dg/template/permissive-error1b.C
new file mode 100644
index 00000000000..3d3462c956e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/template/permissive-error1b.C
@@ -0,0 +1,30 @@
+// PR c++/116064
+// { dg-additional-options "-fpermissive -Wno-template-body" }
+// Like permissive-error1a.C but verify -Wno-template-body suppresses
+// the downgraded warnings.
+
+template<class T>
+void f() {  // { dg-error "instantiating erroneous template" }
+  const int n = 42;
+  ++n; // { dg-message "first error appeared here" "" { target *-*-* } }
+}
+
+template<class T>
+struct A { // { dg-error "instantiating erroneous template" }
+  void f(typename A::type); // { dg-message "first error appeared here" "" { 
target *-*-* } }
+};
+
+template<class T>
+struct B {
+  void f() {   // { dg-error "instantiating erroneous template" }
+    this->g(); // { dg-message "first error appeared here" "" { target *-*-* } 
}
+  }
+};
+
+int main() {
+  f<int>(); // { dg-message "required from here" }
+  A<int> a; // { dg-message "required from here" }
+           // { dg-error "incomplete type" "" { target *-*-* } .-1 }
+  B<int> b; // { dg-bogus "" }
+  b.f();    // { dg-message "required from here" }
+}
diff --git a/gcc/testsuite/g++.old-deja/g++.pt/crash51.C 
b/gcc/testsuite/g++.old-deja/g++.pt/crash51.C
index a3fbc17f163..c5cbde521ad 100644
--- a/gcc/testsuite/g++.old-deja/g++.pt/crash51.C
+++ b/gcc/testsuite/g++.old-deja/g++.pt/crash51.C
@@ -1,5 +1,4 @@
 // { dg-do assemble  }
-// { dg-options "-fpermissive -w" }
 // Origin: Mark Mitchell <m...@codesourcery.com>
 
 char foo[26];
-- 
2.46.0.39.g891ee3b9db

Reply via email to