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

Reply via email to