diff options
author | Patrick Palka <ppalka@redhat.com> | 2024-08-06 20:54:03 -0400 |
---|---|---|
committer | Patrick Palka <ppalka@redhat.com> | 2024-08-06 20:54:03 -0400 |
commit | 596d1ed9d40b1081fcc6c6161a0135952829b88f (patch) | |
tree | face5620e56b404afb00a16b3238e9b87944308c /gcc/cp | |
parent | b120ca0c1da1a2f6e471edf61b18481296b464be (diff) | |
download | gcc-596d1ed9d40b1081fcc6c6161a0135952829b88f.zip gcc-596d1ed9d40b1081fcc6c6161a0135952829b88f.tar.gz gcc-596d1ed9d40b1081fcc6c6161a0135952829b88f.tar.bz2 |
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 turns any errors issued within a template into
permerrors associated with a new -Wtemplate-body flag so that they can
be downgraded via e.g. -fpermissive or -Wno-error=template-body. 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 adjusts an error diagnostic appropriately if we
detect it's occurring from a template context, and additionally flags
the template as erroneous.
For example the new testcase 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
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 (erroneous_templates_t): Declare.
(erroneous_templates): 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.
* module.cc (finish_module_processing): Don't write the
module if it contains an erroneous template.
* pt.cc (maybe_diagnose_erroneous_template): Define.
(instantiate_class_template): Call it.
(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.
(-fpermissive): Mention -Wtemplate-body.
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.
* g++.dg/template/permissive-error1c.C: New test.
Reviewed-by: Jason Merrill <jason@redhat.com>
Diffstat (limited to 'gcc/cp')
-rw-r--r-- | gcc/cp/cp-tree.h | 7 | ||||
-rw-r--r-- | gcc/cp/error.cc | 67 | ||||
-rw-r--r-- | gcc/cp/module.cc | 5 | ||||
-rw-r--r-- | gcc/cp/pt.cc | 22 |
4 files changed, 100 insertions, 1 deletions
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index b81bc91..b169305 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7185,6 +7185,13 @@ extern location_t location_of (tree); extern void qualified_name_lookup_error (tree, tree, tree, location_t); +using erroneous_templates_t + = hash_map<tree, location_t, simple_hashmap_traits<tree_decl_hash, location_t>>; +extern erroneous_templates_t *erroneous_templates; + +extern bool cp_seen_error (); +#define seen_error() cp_seen_error () + /* in except.cc */ extern void init_terminate_fn (void); extern void init_exception_processing (void); diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc index da853e2..ee3868e 100644 --- a/gcc/cp/error.cc +++ b/gcc/cp/error.cc @@ -165,6 +165,72 @@ 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 (scope_chain && in_template_context && !current_instantiation ()) + if (tree ti = get_template_info (current_scope ())) + return TI_TEMPLATE (ti); + + return NULL_TREE; +} + +/* A map from TEMPLATE_DECLs that we've determined to be erroneous + at parse time to the location of the first error within. */ + +erroneous_templates_t *erroneous_templates; + +/* Callback function diagnostic_context::m_adjust_diagnostic_info. + + Errors issued when parsing a template are automatically treated like + permerrors associated with the -Wtemplate-body flag and can be + downgraded into warnings accordingly, in which case we'll still + 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) + if (tree tmpl = get_current_template ()) + { + diagnostic->option_index = OPT_Wtemplate_body; + + if (context->m_permissive) + diagnostic->kind = DK_WARNING; + + bool existed; + location_t &error_loc + = hash_map_safe_get_or_insert<false> (erroneous_templates, + tmpl, &existed); + if (!existed) + /* Remember that this template had a parse-time error so + that we'll ensure a hard error has been issued upon + its instantiation. */ + error_loc = diagnostic->richloc->get_loc (); + } +} + +/* 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 () +{ + if ((seen_error) ()) + return true; + + if (erroneous_templates) + if (tree tmpl = get_current_template ()) + if (erroneous_templates->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 +253,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/module.cc b/gcc/cp/module.cc index d1607a0..7130faf 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -20768,7 +20768,10 @@ finish_module_processing (cpp_reader *reader) cookie = new module_processing_cookie (cmi_name, tmp_name, fd, e); - if (errorcount) + if (errorcount + /* Don't write the module if it contains an erroneous template. */ + || (erroneous_templates + && !erroneous_templates->is_empty ())) warning_at (state->loc, 0, "not writing module %qs due to errors", state->get_flatname ()); else if (cookie->out.begin ()) diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 677ed7d..9a4ff55 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -12289,6 +12289,24 @@ perform_instantiation_time_access_checks (tree tmpl, tree targs) } } +/* If the template T that we're about to instantiate contained errors at + parse time that we downgraded into warnings or suppressed, diagnose the + error now to render the TU ill-formed (if the TU has not already been + deemed ill-formed by an earlier error). */ + +static void +maybe_diagnose_erroneous_template (tree t) +{ + if (erroneous_templates && !(seen_error) ()) + if (location_t *error_loc = erroneous_templates->get (t)) + { + auto_diagnostic_group d; + location_t decl_loc = location_of (t); + error_at (decl_loc, "instantiating erroneous template"); + inform (*error_loc, "first error appeared here"); + } +} + tree instantiate_class_template (tree type) { @@ -12358,6 +12376,8 @@ instantiate_class_template (tree type) if (! push_tinst_level (type)) return type; + maybe_diagnose_erroneous_template (templ); + int saved_unevaluated_operand = cp_unevaluated_operand; int saved_inhibit_evaluation_warnings = c_inhibit_evaluation_warnings; @@ -27251,6 +27271,8 @@ instantiate_decl (tree d, bool defer_ok, bool expl_inst_class_mem_p) } } + maybe_diagnose_erroneous_template (td); + code_pattern = DECL_TEMPLATE_RESULT (td); /* We should never be trying to instantiate a member of a class |