https://gcc.gnu.org/bugzilla/show_bug.cgi?id=121244
Jason Merrill <jason at gcc dot gnu.org> changed: What |Removed |Added ---------------------------------------------------------------------------- CC| |jason at gcc dot gnu.org, | |ville.voutilainen at gmail dot com --- Comment #4 from Jason Merrill <jason at gcc dot gnu.org> --- (In reply to Allan Jensen from comment #0) > I seems to me when it warns about a previous failure to complete SFINAE, it > should tell where that was by default and not instruct us to compile again > with the warning set to report more false positives. The issue is that failure to complete is not a problem in itself; it's OK to have incomplete classes. By default the warning only checks whether such a type is later completed. Ideally rather than suggest running the compiler again with another flag we would store away the full context of the previous use for the delayed diagnostic, but that's impractical. With your testcase I need to specify -Wsystem-headers along with -Wsfinae-incomplete=2 to get the interesting output, because the incompleteness in SFINAE context happens in std::data, in the standard library. At that point the QAnyStringView warnings point to these lines: static_assert(QtPrivate::IsContainerCompatibleWithQStringView<QAnyStringView>::value == false); static_assert(QtPrivate::IsContainerCompatibleWithQUtf8StringView<QAnyStringView>::value == false); which are within the body of QAnyStringView. One of the full diagnostics looks like wa.ii: In substitution of ‘template<class _Container> constexpr decltype (__cont.data()) std::data(const _Container&) [with _Container = QAnyStringView]’: wa.ii:97042:52: required by substitution of ‘template<class T> struct QtPrivate::IsContainerCompatibleWithQStringView<T, typename std::enable_if<conjunction_v<QtPrivate::IsCompatiblePointer<decltype (std::data(declval<const T&>()))>, std::is_convertible<decltype (std::size(declval<const T&>())), long long int>, QtPrivate::IsCompatibleCharType<typename std::iterator_traits<decltype (std::begin(declval<const T&>()))>::value_type>, std::is_convertible<decltype ((std::begin(declval<const T&>()) != std::end(declval<const T&>()))), bool>, std::negation<std::is_same<typename std::decay<_Tp>::type, QString> >, std::negation<std::is_same<typename std::decay<_Tp>::type, QStringView> > >, void>::type> [with T = QAnyStringView]’ 97042 | IsCompatiblePointer<decltype( std::data(std::declval<const T &>()) )>, | ~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~ wa.ii:98178:82: required from here 98178 | static_assert(QtPrivate::IsContainerCompatibleWithQStringView<QAnyStringView>::value == false); | ^~ wa.ii:50528:24: warning: failed to complete ‘QAnyStringView’ in SFINAE context [-Wsfinae-incomplete=] 50528 | -> decltype(__cont.data()) | ~~~~~~~^~~~ This reduces roughly to template <class T> auto data (const T& t) -> decltype (t.data()); template <class T> concept Data = requires (const T& t) { data(t); }; struct A { void data() const; static_assert (!Data<A>); // data(A()) ill-formed here }; static_assert (Data<A>); // data(A()) well-formed here At the static_assert in the class body, instantiating data<A> is ill-formed because A isn't complete yet, so Data<A> is not satisfied. After the class body, it's well-formed, so Data<A> is satisfied. This change of satisfaction is IFNDR, and GCC complains about it with a "satisfaction changed" error. -Wsfinae-incomplete warns about this situation sooner, at the point of completing the definition of A, that something was previously affected by its incompleteness. The warning for the Qt code is a true positive; they should probably just move the static_asserts out of the class body.