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