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 be able to prevent these template errors from rendering the entire TU ill-formed. (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 it's occurring in 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’ 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/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 (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. 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. --- gcc/cp/cp-tree.h | 5 ++ gcc/cp/error.cc | 53 +++++++++++++++++++ gcc/cp/pt.cc | 30 +++++++++++ gcc/diagnostic.cc | 4 ++ gcc/diagnostic.h | 4 ++ 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 | 31 +++++++++++ gcc/testsuite/g++.old-deja/g++.pt/crash51.C | 1 - 10 files changed, 150 insertions(+), 4 deletions(-) create mode 100644 gcc/testsuite/g++.dg/template/permissive-error1.C create mode 100644 gcc/testsuite/g++.dg/template/permissive-error1a.C 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..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. */ + +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; + } +} + +/* 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 ())) + && relaxed_template_errors->get (TI_TEMPLATE (ti))) + 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 +239,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..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"); + 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 pattern"); + 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..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); + /* Give preference to being able to inhibit warnings, before they get reclassified to something else. */ bool was_warning = (diagnostic->kind == 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/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..747a483b99f --- /dev/null +++ b/gcc/testsuite/g++.dg/template/permissive-error1a.C @@ -0,0 +1,31 @@ +// PR c++/116064 +// { dg-additional-options -fpermissive } + +template<class T> +void f() { // { dg-error "instantiating erroneous template pattern" } + 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 pattern" } + 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 pattern" } + 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++.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