Tested x86_64-pc-linux-gnu, any comments? Bikeshedding? -- 8< --
We already error about a type definition causing a concept check to change value, but it would be useful to diagnose this for other SFINAE contexts as well; the memoization problem also affects templates. So -Wsfinae-incomplete remembers if we've failed a requirement for a complete type in a non-tf_error context, and later warns if the type becomes complete. This warning is enabled by default; I think the signal-to-noise ratio is high enough to warrant that, and it catches things that are likely to make the program "ill-formed, no diagnostic required". The data for this warning uses GTY((cache)) to persist through GC, but allow entries to be discarded if the key is not otherwise marked. I don't think it's desirable to export/import this information in modules, it makes sense for it to be local to a single TU. -Wsfinae-incomplete=2 adds a warning at the point of failure, which is primarily intended to help with debugging warnings from the default mode. gcc/ChangeLog: * doc/invoke.texi: Document -Wsfinae-incomplete. gcc/c-family/ChangeLog: * c.opt: Add -Wsfinae-incomplete. * c.opt.urls: Regenerate. gcc/cp/ChangeLog: * constraint.cc (failed_completions_map): New. (note_failed_type_completion): Rename from note_failed_type_completion_for_satisfaction. Add -Wsfinae-incomplete handling. (failed_completion_location): New. * class.cc (finish_struct_1): Add -Wsfinae-incomplete warning. * decl.cc (require_deduced_type): Adjust. * typeck.cc (complete_type_or_maybe_complain): Adjust. (cxx_sizeof_or_alignof_type): Call note_failed_type_completion. * cp-tree.h: Adjust. libstdc++-v3/ChangeLog: * testsuite/20_util/is_complete_or_unbounded/memoization.cc * testsuite/20_util/is_complete_or_unbounded/memoization_neg.cc: Expect -Wsfinae-incomplete. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/concepts-complete1.C * g++.dg/cpp2a/concepts-complete2.C * g++.dg/cpp2a/concepts-complete4.C: Expect -Wsfinae-incomplete. --- gcc/doc/invoke.texi | 13 ++++++++ gcc/c-family/c.opt | 8 +++++ gcc/cp/cp-tree.h | 3 +- gcc/cp/class.cc | 10 ++++++ gcc/cp/constraint.cc | 32 +++++++++++++++++-- gcc/cp/decl.cc | 2 +- gcc/cp/typeck.cc | 13 ++++++-- .../g++.dg/cpp2a/concepts-complete1.C | 2 +- .../g++.dg/cpp2a/concepts-complete2.C | 2 +- .../g++.dg/cpp2a/concepts-complete4.C | 2 +- .../is_complete_or_unbounded/memoization.cc | 2 +- .../memoization_neg.cc | 2 +- gcc/c-family/c.opt.urls | 6 ++++ 13 files changed, 85 insertions(+), 12 deletions(-) diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 382cc9fa7a8..e3a6d2aced3 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -4448,6 +4448,19 @@ to filter out those warnings. Disable the warning about the case when an exception handler is shadowed by another handler, which can point out a wrong ordering of exception handlers. +@opindex Wsfinae-incomplete +@opindex Wno-sfinae-incomplete +Warn about a class that is found to be incomplete in a context where +that causes substitution failure rather than an error, and then is +defined later in the translation unit. This is problematic because +template instantiations or concept checks could have different results +if they first occur either before or after the definition. To fix +this warning, avoid depending on its completeness until after it. + +This warning is enabled by default. @option{-Wsfinae-incomplete=2} +adds a warning at the point of substitution failure, to make it easier +to track down problems flagged by the default mode. + @opindex Wstrict-null-sentinel @opindex Wno-strict-null-sentinel @item -Wstrict-null-sentinel @r{(C++ and Objective-C++ only)} diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 50ba856fedb..8af466d1ed1 100644 --- a/gcc/c-family/c.opt +++ b/gcc/c-family/c.opt @@ -1319,6 +1319,14 @@ Wsequence-point C ObjC C++ ObjC++ Var(warn_sequence_point) Warning LangEnabledBy(C ObjC C++ ObjC++,Wall) Warn about possible violations of sequence point rules. +Wsfinae-incomplete= +C++ ObjC++ Var(warn_sfinae_incomplete) Warning Init(1) Joined RejectNegative UInteger IntegerRange(0, 2) +Warn about an incomplete type affecting semantics in a non-error context. + +Wsfinae-incomplete +C++ ObjC++ Warning Alias(Wsfinae-incomplete=, 1, 0) +Warn about an incomplete type affecting semantics in a non-error context. + Wshadow-ivar ObjC ObjC++ Var(warn_shadow_ivar) EnabledBy(Wshadow) Init(1) Warning Warn if a local declaration hides an instance variable. diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index d663d6ec225..ebfc1bb4a96 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -8842,7 +8842,8 @@ extern hashval_t iterative_hash_constraint (tree, hashval_t); extern hashval_t hash_atomic_constraint (tree); extern void diagnose_constraints (location_t, tree, tree); -extern void note_failed_type_completion_for_satisfaction (tree); +extern void note_failed_type_completion (tree, tsubst_flags_t); +extern location_t failed_completion_location (tree); /* in logic.cc */ extern bool subsumes (tree, tree); diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc index db39e579870..cdfc3c3e596 100644 --- a/gcc/cp/class.cc +++ b/gcc/cp/class.cc @@ -7920,6 +7920,16 @@ finish_struct_1 (tree t) return; } + if (location_t fcloc = failed_completion_location (t)) + { + auto_diagnostic_group adg; + if (warning (OPT_Wsfinae_incomplete_, + "defining %qT, which previously failed to be complete " + "in a SFINAE context", t)) + inform (fcloc, "here. Use %qs for a diagnostic at that point", + "-Wsfinae-incomplete=2"); + } + /* If this type was previously laid out as a forward reference, make sure we lay it out again. */ TYPE_SIZE (t) = NULL_TREE; diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 90625707043..e46df11ca24 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -1836,7 +1836,7 @@ tsubst_parameter_mapping (tree map, tree args, tsubst_flags_t complain, tree in_ static bool satisfying_constraint; /* A vector of incomplete types (and of declarations with undeduced return type), - appended to by note_failed_type_completion_for_satisfaction. The + appended to by note_failed_type_completion. The satisfaction caches use this in order to keep track of "potentially unstable" satisfaction results. @@ -1845,12 +1845,17 @@ static bool satisfying_constraint; static GTY((deletable)) vec<tree, va_gc> *failed_type_completions; +/* A map of where types were found to be incomplete in SFINAE context, for + warning if they are later completed. */ + +static GTY((cache)) hash_map<tree, location_t> *failed_completions_map; + /* Called whenever a type completion (or return type deduction) failure occurs that definitely affects the meaning of the program, by e.g. inducing substitution failure. */ void -note_failed_type_completion_for_satisfaction (tree t) +note_failed_type_completion (tree t, tsubst_flags_t complain) { if (satisfying_constraint) { @@ -1858,6 +1863,29 @@ note_failed_type_completion_for_satisfaction (tree t) || (DECL_P (t) && undeduced_auto_decl (t))); vec_safe_push (failed_type_completions, t); } + if (!(complain & tf_error) && CLASS_TYPE_P (t) && !dependent_type_p (t) + && warning_enabled_at (DECL_SOURCE_LOCATION (TYPE_MAIN_DECL (t)), + OPT_Wsfinae_incomplete_)) + { + if (warn_sfinae_incomplete > 1) + warning (OPT_Wsfinae_incomplete_, + "failed to complete %qT in SFINAE context", t); + if (!failed_completions_map) + failed_completions_map = hash_map<tree, location_t>::create_ggc (); + failed_completions_map->put (TYPE_MAIN_VARIANT (t), input_location); + } +} + +/* If T was previously found to be incomplete in SFINAE context, return the + location where that happened, otherwise UNKNOWN_LOCATION. */ + +location_t +failed_completion_location (tree t) +{ + if (failed_completions_map) + if (location_t *p = failed_completions_map->get (TYPE_MAIN_VARIANT (t))) + return *p; + return UNKNOWN_LOCATION; } /* Returns true if the range [BEGIN, END) of elements within the diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 4c8a2052aee..718d0e5ede2 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -19989,7 +19989,7 @@ require_deduced_type (tree decl, tsubst_flags_t complain) /* We probably already complained about deduction failure. */; else if (complain & tf_error) error ("use of %qD before deduction of %<auto%>", decl); - note_failed_type_completion_for_satisfaction (decl); + note_failed_type_completion (decl, complain); return false; } return true; diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc index ac1eb397f01..f4b49b792e3 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -156,7 +156,7 @@ complete_type_or_maybe_complain (tree type, tree value, tsubst_flags_t complain) { if (complain & tf_error) cxx_incomplete_type_diagnostic (value, type, DK_ERROR); - note_failed_type_completion_for_satisfaction (type); + note_failed_type_completion (type, complain); return NULL_TREE; } else @@ -2084,7 +2084,14 @@ cxx_sizeof_or_alignof_type (location_t loc, tree type, enum tree_code op, bool dependent_p = dependent_type_p (type); if (!dependent_p) - complete_type (type); + { + complete_type (type); + if (!COMPLETE_TYPE_P (type)) + /* Call this here because the incompleteness diagnostic comes from + c_sizeof_or_alignof_type instead of + complete_type_or_maybe_complain. */ + note_failed_type_completion (type, complain); + } if (dependent_p /* VLA types will have a non-constant size. In the body of an uninstantiated template, we don't need to try to compute the @@ -2106,7 +2113,7 @@ cxx_sizeof_or_alignof_type (location_t loc, tree type, enum tree_code op, return c_sizeof_or_alignof_type (loc, complete_type (type), op == SIZEOF_EXPR, std_alignof, - complain); + complain & (tf_warning_or_error)); } /* Return the size of the type, without producing any warnings for diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-complete1.C b/gcc/testsuite/g++.dg/cpp2a/concepts-complete1.C index e8487bf9c09..9f2e9259f7f 100644 --- a/gcc/testsuite/g++.dg/cpp2a/concepts-complete1.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-complete1.C @@ -12,7 +12,7 @@ template <class T> char f() { return 0; } struct A; static_assert (sizeof (f<A>()) == 1); // { dg-message "first evaluated to 'false' from here" } -struct A { typedef int type; }; +struct A { typedef int type; }; // { dg-warning Wsfinae-incomplete } static_assert (sizeof (f<A>()) > 1); // { dg-error "assert" } // { dg-message "required from here" "" { target *-*-* } .-1 } static_assert (sizeof (f<A>()) > 1); diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-complete2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-complete2.C index b2c11606737..46952a4e59c 100644 --- a/gcc/testsuite/g++.dg/cpp2a/concepts-complete2.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-complete2.C @@ -18,6 +18,6 @@ template <class T> char f() { return 0; } struct A; static_assert (sizeof (f<A>()) == 1); // { dg-message "first evaluated to 'false' from here" } -struct A { typedef int type; }; +struct A { typedef int type; }; // { dg-warning Wsfinae-incomplete } static_assert (sizeof (f<A>()) > 1); // { dg-error "assert" } static_assert (sizeof (f<A>()) > 1); diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-complete4.C b/gcc/testsuite/g++.dg/cpp2a/concepts-complete4.C index 988b0ddcfdd..7be9f50af6b 100644 --- a/gcc/testsuite/g++.dg/cpp2a/concepts-complete4.C +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-complete4.C @@ -8,6 +8,6 @@ struct A; static_assert(!C<A>); -struct A { static constexpr bool value = false; }; +struct A { static constexpr bool value = false; }; // { dg-warning Wsfinae-incomplete } static_assert(C<A>); // { dg-error "assert" } diff --git a/libstdc++-v3/testsuite/20_util/is_complete_or_unbounded/memoization.cc b/libstdc++-v3/testsuite/20_util/is_complete_or_unbounded/memoization.cc index 256b84df60f..59af024969f 100644 --- a/libstdc++-v3/testsuite/20_util/is_complete_or_unbounded/memoization.cc +++ b/libstdc++-v3/testsuite/20_util/is_complete_or_unbounded/memoization.cc @@ -23,7 +23,7 @@ struct X; static_assert( !std::__is_complete_or_unbounded(std::__type_identity<X>{}), "error"); -struct X{}; +struct X{}; // { dg-warning Wsfinae-incomplete } static_assert( std::__is_complete_or_unbounded(std::__type_identity<X>{}), "Result memoized. This leads to worse diagnostics"); diff --git a/libstdc++-v3/testsuite/20_util/is_complete_or_unbounded/memoization_neg.cc b/libstdc++-v3/testsuite/20_util/is_complete_or_unbounded/memoization_neg.cc index 8e207b584dc..264efa77996 100644 --- a/libstdc++-v3/testsuite/20_util/is_complete_or_unbounded/memoization_neg.cc +++ b/libstdc++-v3/testsuite/20_util/is_complete_or_unbounded/memoization_neg.cc @@ -25,5 +25,5 @@ struct X; constexpr bool res_incomplete = std::is_move_constructible<X>::value; // { dg-error "required from here" } -struct X{}; +struct X{}; // { dg-warning Wsfinae-incomplete } constexpr bool res_complete = std::is_default_constructible<X>::value; // { dg-bogus "required from here" } diff --git a/gcc/c-family/c.opt.urls b/gcc/c-family/c.opt.urls index ad6d8a0b387..65d1221c4ad 100644 --- a/gcc/c-family/c.opt.urls +++ b/gcc/c-family/c.opt.urls @@ -756,6 +756,12 @@ UrlSuffix(gcc/Warning-Options.html#index-Wno-self-move) Wsequence-point UrlSuffix(gcc/Warning-Options.html#index-Wno-sequence-point) +Wsfinae-incomplete= +UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-Wno-sfinae-incomplete) + +Wsfinae-incomplete +UrlSuffix(gcc/C_002b_002b-Dialect-Options.html#index-Wno-sfinae-incomplete) + Wshadow-ivar UrlSuffix(gcc/Warning-Options.html#index-Wno-shadow-ivar) base-commit: ad56e4632b05e66da35d6c23e45312b3cfbb646c -- 2.49.0