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.
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)
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)
+ && warn_sfinae_incomplete == 1)
+ 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;
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.
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, decl_location_traits> *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 (dependent_template_arg_p (t))
+ return;
+
+ gcc_checking_assert ((TYPE_P (t) && !COMPLETE_TYPE_P (t))
+ || (DECL_P (t) && undeduced_auto_decl (t)));
+
if (satisfying_constraint)
+ vec_safe_push (failed_type_completions, t);
+
+ if (TYPE_P (t))
+ {
+ if (!CLASS_TYPE_P (t))
+ return;
+ t = TYPE_MAIN_DECL (t);
+ }
+ if (!(complain & tf_error)
+ && warning_enabled_at (DECL_SOURCE_LOCATION (t),
+ OPT_Wsfinae_incomplete_))
+ {
+ if (warn_sfinae_incomplete > 1)
+ {
+ if (TREE_CODE (t) == TYPE_DECL)
+ warning (OPT_Wsfinae_incomplete_,
+ "failed to complete %qT in SFINAE context", TREE_TYPE (t));
+ else
+ warning (OPT_Wsfinae_incomplete_,
+ "failed to deduce %qD in SFINAE context", t);
+ }
+ if (!failed_completions_map)
+ failed_completions_map
+ = hash_map<tree, location_t, decl_location_traits>::create_ggc ();
+ failed_completions_map->put (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)
{
- gcc_checking_assert ((TYPE_P (t) && !COMPLETE_TYPE_P (t))
- || (DECL_P (t) && undeduced_auto_decl (t)));
- vec_safe_push (failed_type_completions, t);
+ if (TYPE_P (t))
+ t = TYPE_MAIN_DECL (t);
+ if (location_t *p = failed_completions_map->get (t))
+ return *p;
}
+ return UNKNOWN_LOCATION;
}
/* Returns true if the range [BEGIN, END) of elements within the
extern bool value_dependent_expression_p (tree);
extern bool instantiation_dependent_uneval_expression_p (tree);
extern bool any_value_dependent_elements_p (const_tree);
+extern bool dependent_template_arg_p (tree);
extern bool dependent_omp_for_p (tree, tree, tree, tree, tree);
extern tree resolve_typename_type (tree, bool);
extern tree template_for_substitution (tree);
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);
}
}
+ if (FNDECL_USED_AUTO (fndecl)
+ && TREE_TYPE (fntype) != DECL_SAVED_AUTO_RETURN_TYPE (fndecl))
+ if (location_t fcloc = failed_completion_location (fndecl))
+ {
+ auto_diagnostic_group adg;
+ if (warning (OPT_Wsfinae_incomplete_,
+ "defining %qD, which previously failed to be deduced "
+ "in a SFINAE context", fndecl)
+ && warn_sfinae_incomplete == 1)
+ inform (fcloc, "here. Use %qs for a diagnostic at that point",
+ "-Wsfinae-incomplete=2");
+ }
+
/* Remember that we were in class scope. */
if (current_class_name)
ctype = current_class_type;
/* 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;
static tree copy_default_args_to_explicit_spec_1 (tree, tree);
static void copy_default_args_to_explicit_spec (tree);
static bool invalid_nontype_parm_type_p (tree, tsubst_flags_t);
-static bool dependent_template_arg_p (tree);
static bool dependent_type_p_r (tree);
static tree tsubst_stmt (tree, tree, tsubst_flags_t, tree);
static tree tsubst_decl (tree, tree, tsubst_flags_t, bool = true);
{
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
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
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
-Wnoexcept -Wnoexcept-type -Wnon-virtual-dtor
-Wpessimizing-move -Wno-placement-new -Wplacement-new=@var{n}
-Wrange-loop-construct -Wredundant-move -Wredundant-tags
--Wreorder -Wregister
+-Wreorder -Wregister -Wno-sfinae-incomplete
-Wstrict-null-sentinel -Wno-subobject-linkage -Wtemplates
-Wno-non-template-friend -Wold-style-cast
-Woverloaded-virtual -Wno-pmf-conversions -Wself-move -Wsign-promo
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, or a function with
+auto return type that has not yet been deduced, in a context where
+that causes substitution failure rather than an error, and then the
+class or function 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.
+
+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)}
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);
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);
struct A { auto foo(); };
static_assert (sizeof (f<A>()) == 1); // { dg-message "first evaluated to 'false' from here" }
-auto A::foo() { }
+auto A::foo() { } // { dg-warning Wsfinae-incomplete }
static_assert (sizeof (f<A>()) > 1); // { dg-error "assert" }
static_assert (sizeof (f<A>()) > 1);
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" }
static constexpr size_t value = current;
template<typename>
- friend auto counterFlag(CounterReader<tag, current>) noexcept {}
+ friend auto counterFlag(CounterReader<tag, current>) noexcept {} // { dg-warning -Wsfinae-incomplete }
};
template<auto tag, auto unique, size_t current = 0, size_t mask = size_t(1) << (sizeof(size_t) * 8 - 1)>
[[nodiscard]] constexpr size_t counterAdvance() noexcept {
if constexpr (!mask) {
return CounterWriter<tag, current + 1>::value;
- } else if constexpr (requires { counterFlag<void>(CounterReader<tag, current | mask>()); }) {
+ } else if constexpr (requires { counterFlag<void>(CounterReader<tag, current | mask>()); }) { // { dg-message "here" }
return counterAdvance<tag, unique, current | mask, (mask >> 1)>();
}
else {
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");
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" }