diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 979f17a7e3232c0c1eba891ae54f0951a0e9611f..491aa02e1a39b8e4c2f6cad18e534bfb9e144b9f 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) +Diagnose errors when parsing a template. + 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 b81bc91208fa958c0834148de0628c5a2312d5b1..b1693051231b1e8ea9216abdc8257048c6c3e55c 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 da853e203dbc3dc3cb6d0ec087d6309fd69dff39..ee3868efaed6269b6df29dd9b01be496b5f5da6b 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 d1607a06757551008dace2f3135bcabb746ce6ef..7130faf26f52f6ddfd9c95fe5aac81a0a8d4a994 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 677ed7d128966efcfd8f1d1f3254952f9fb5d17e..9a4ff553b2f307e8ef8e077fdbea6d05143ffa8d 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 diff --git a/gcc/diagnostic.cc b/gcc/diagnostic.cc index 3fc81ad47f5698b1d955d90e9ebdde5ff8e6d7be..92bd4f80845338ec4dbb31d74a6041f564cdaa27 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; @@ -1429,6 +1430,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 83180ded414ddc06ee60296eac0a974085ec2c13..2a9f2751dca28e229ce60d15238c6c04b8c51e6e 100644 --- a/gcc/diagnostic.h +++ b/gcc/diagnostic.h @@ -701,6 +701,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 0fe99ca8ef6e8868f60369f6329fe29599d89159..27539a017851d928229574962371089e80620f86 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,14 @@ 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)} +Disable diagnosing errors when parsing a template, and instead issue an +error only upon instantiation of the template. This flag can also be +used to downgrade such errors into warnings with @option{Wno-error=} or +@option{-fpermissive}. + @opindex Wtemplate-id-cdtor @opindex Wno-template-id-cdtor @item -Wno-template-id-cdtor @r{(C++ and Objective-C++ only)} @@ -6361,6 +6370,7 @@ that have their own flag: -Wint-conversion @r{(C and Objective-C only)} -Wnarrowing @r{(C++ and Objective-C++ only)} -Wreturn-mismatch @r{(C and Objective-C only)} +-Wtemplate-body @r{(C++ and Objective-C++ only)} } The @option{-fpermissive} option is the default for historic C language diff --git a/gcc/testsuite/g++.dg/ext/typedef-init.C b/gcc/testsuite/g++.dg/ext/typedef-init.C index 153303d217b5012baab63b39071df651ca7db25f..47a6642de5108d7d7e3cd9b58d3b45eb56c6d2b3 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 1a2922096d19102c54e51b6775986e2a7fab71ae..08f368ff29b60409950b2c1a67556ab58cf9953e 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 0000000000000000000000000000000000000000..e4536a8b90e069a1112583f20c9aa625bfff4afb --- /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 0000000000000000000000000000000000000000..ac47151f0dba9216ca66c64d3ca8a6e474debf19 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/permissive-error1a.C @@ -0,0 +1,31 @@ +// 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 variable 'n' .-Wtemplate-body." } + // { dg-message "first error appeared here" "" { target *-*-* } .-1 } + // { dg-error "read-only variable 'n'\[\n\r\]" "" { target *-*-* } .-2 } +} + +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" } + } +}; + +int main() { + f<int>(); // { dg-message "required from here" } + A<int> a; + B<int> b; + b.f(); +} 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 0000000000000000000000000000000000000000..a67b73746256f4d8e0e18ad2e31c0556072a4e16 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/permissive-error1b.C @@ -0,0 +1,30 @@ +// PR c++/116064 +// { dg-additional-options -Wno-template-body } +// Like permissive-error1a.C but verify -Wno-template-body suppresses +// diagnostics. + +template<class T> +void f() { // { dg-error "instantiating erroneous template" } + const int n = 42; + ++n; // { dg-message "first error appeared here" "" { target *-*-* } } + // { dg-error "read-only variable 'n'\[\n\r\]" "" { target *-*-* } .-1 } +} + +template<class T> +struct A { + void f(typename A::type); +}; + +template<class T> +struct B { + void f() { + this->g(); + } +}; + +int main() { + f<int>(); // { dg-message "required from here" } + A<int> a; + B<int> b; + b.f(); +} diff --git a/gcc/testsuite/g++.dg/template/permissive-error1c.C b/gcc/testsuite/g++.dg/template/permissive-error1c.C new file mode 100644 index 0000000000000000000000000000000000000000..fd5f26655e5400f02f89f4450330363b6870a2a4 --- /dev/null +++ b/gcc/testsuite/g++.dg/template/permissive-error1c.C @@ -0,0 +1,31 @@ +// PR c++/116064 +// { dg-additional-options -Wno-error=template-body } +// Like permissive-error1a.C but verify the diagnostics can also +// be downgraded via Wno-error=template-body. + +template<class T> +void f() { // { dg-error "instantiating erroneous template" } + const int n = 42; + ++n; // { dg-warning "read-only variable 'n' .-Wtemplate-body." } + // { dg-message "first error appeared here" "" { target *-*-* } .-1 } + // { dg-error "read-only variable 'n'\[\n\r\]" "" { target *-*-* } .-2 } +} + +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" } + } +}; + +int main() { + f<int>(); // { dg-message "required from here" } + A<int> a; + B<int> b; + b.f(); +} diff --git a/gcc/testsuite/g++.old-deja/g++.pt/crash51.C b/gcc/testsuite/g++.old-deja/g++.pt/crash51.C index a3fbc17f163b42e9a44398e461107100df5d39b0..c5cbde521ade4d0857b0492a5dd5e9e7ebdd3392 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 <mark@codesourcery.com> char foo[26];