[PATCH] libstdc++: Unnecessary type completion in __is_complete_or_unbounded [PR120717]
Patrick Palka
ppalka@redhat.com
Tue Jun 24 12:53:18 GMT 2025
On Tue, 24 Jun 2025, Jonathan Wakely wrote:
> On Tue, 24 Jun 2025 at 03:20, Patrick Palka <ppalka@redhat.com> wrote:
> >
> > Tested on x86_64-pc-linux-gnu, does this look OK for trunk?
> >
> > -- >8 --
> >
> > When checking __is_complete_or_unbounded on a reference to incomplete
> > type, we overeagerly try to instantiate/complete the referenced type
> > which besides being unnecessary may also produce a -Wsfinae-incomplete
> > warning (added in r16-1527) if the referenced type is later defined.
> >
> > This patch fixes this by effectively restricting the sizeof check to
> > object (except unknown-bound array) types. In passing simplify the
> > implementation by using is_object instead of is_function/reference/void.
> >
> > PR libstdc++/120717
> >
> > libstdc++-v3/ChangeLog:
> >
> > * include/std/type_traits (__is_complete_or_unbounded): Don't
> > check sizeof on a reference or unbounded array type. Simplify
> > using is_object. Correct formatting.
> > * testsuite/20_util/is_complete_or_unbounded/120717.cc: New test.
> > ---
> > libstdc++-v3/include/std/type_traits | 34 +++++++++----------
> > .../is_complete_or_unbounded/120717.cc | 20 +++++++++++
> > 2 files changed, 37 insertions(+), 17 deletions(-)
> > create mode 100644 libstdc++-v3/testsuite/20_util/is_complete_or_unbounded/120717.cc
> >
> > diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
> > index abff9f880001..28960befd2c7 100644
> > --- a/libstdc++-v3/include/std/type_traits
> > +++ b/libstdc++-v3/include/std/type_traits
> > @@ -280,11 +280,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >
> > // Forward declarations
> > template<typename>
> > - struct is_reference;
> > - template<typename>
> > - struct is_function;
> > - template<typename>
> > - struct is_void;
> > + struct is_object;
> > template<typename>
> > struct remove_cv;
> > template<typename>
> > @@ -297,18 +293,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > // Helper functions that return false_type for incomplete classes,
> > // incomplete unions and arrays of known bound from those.
> >
> > - template <typename _Tp, size_t = sizeof(_Tp)>
> > - constexpr true_type __is_complete_or_unbounded(__type_identity<_Tp>)
> > - { return {}; }
> > -
> > - template <typename _TypeIdentity,
> > - typename _NestedType = typename _TypeIdentity::type>
> > - constexpr typename __or_<
> > - is_reference<_NestedType>,
> > - is_function<_NestedType>,
> > - is_void<_NestedType>,
> > - __is_array_unknown_bounds<_NestedType>
> > - >::type __is_complete_or_unbounded(_TypeIdentity)
> > + // More specialized overload for complete object types.
> > + template<typename _Tp,
> > + typename = __enable_if_t<!__or_<__not_<is_object<_Tp>>,
> > + __is_array_unknown_bounds<_Tp>>::value>,
>
> Maybe it's because I'm congested and my head feels like it's full of
> potatoes, but the double negative is confusing for me.
>
> Would __and_<is_object<T>, __not_<__is_array_unknown_bounds<T>> work?
>
> We could even name that:
>
> // An object type which is not an unbounded array.
> // It might still be an incomplete type, but if this is false_type
> // then we can be certain it's not a complete object type.
> template<typename _Tp>
> using __maybe_complete_object_type = ...;
I like it.
>
>
> > + size_t = sizeof(_Tp)>
> > + constexpr true_type
> > + __is_complete_or_unbounded(__type_identity<_Tp>)
> > + { return {}; };
> > +
> > + // Less specialized overload for reference and unknown-bound array types, and
> > + // incomplete types.
> > + template<typename _TypeIdentity,
> > + typename _NestedType = typename _TypeIdentity::type>
> > + constexpr typename __or_<__not_<is_object<_NestedType>>,
> > + __is_array_unknown_bounds<_NestedType>>::type
>
> Then this would be __not_<__maybe_complete_object_type<_NestedType>>
> but maybe that's as bad as the double negative above.
The new helper definitely makes things clearer to me. Like so?
-- >8 --
Subject: [PATCH] libstdc++: Unnecessary type completion in
__is_complete_or_unbounded [PR120717]
When checking __is_complete_or_unbounded on a reference to incomplete
type, we overeagerly try to instantiate/complete the referenced type
which besides being unnecessary may also produce a -Wsfinae-incomplete
warning (added in r16-1527) if the referenced type is later defined.
This patch fixes this by effectively restricting the sizeof check to
object (except unknown-bound array) types. In passing simplify the
implementation by using is_object instead of is_function/reference/void
and introducing a __maybe_complete_object_type helper.
PR libstdc++/120717
libstdc++-v3/ChangeLog:
* include/std/type_traits (__maybe_complete_object_type): New
trait, factored out from ...
(__is_complete_or_unbounded): ... here. Only check sizeof on a
__maybe_complete_object_type type. Fix formatting.
* testsuite/20_util/is_complete_or_unbounded/120717.cc: New test.
---
libstdc++-v3/include/std/type_traits | 39 +++++++++++--------
.../is_complete_or_unbounded/120717.cc | 20 ++++++++++
2 files changed, 42 insertions(+), 17 deletions(-)
create mode 100644 libstdc++-v3/testsuite/20_util/is_complete_or_unbounded/120717.cc
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index abff9f880001..055411195f17 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -280,11 +280,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// Forward declarations
template<typename>
- struct is_reference;
- template<typename>
- struct is_function;
- template<typename>
- struct is_void;
+ struct is_object;
template<typename>
struct remove_cv;
template<typename>
@@ -294,21 +290,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename>
struct __is_array_unknown_bounds;
+ // An object type which is not an unbounded array.
+ // It might still be an incomplete type, but if this is false_type
+ // then we can be certain it's not a complete object type.
+ template<typename _Tp>
+ using __maybe_complete_object_type
+ = __and_<is_object<_Tp>, __not_<__is_array_unknown_bounds<_Tp>>>;
+
// Helper functions that return false_type for incomplete classes,
// incomplete unions and arrays of known bound from those.
- template <typename _Tp, size_t = sizeof(_Tp)>
- constexpr true_type __is_complete_or_unbounded(__type_identity<_Tp>)
- { return {}; }
-
- template <typename _TypeIdentity,
- typename _NestedType = typename _TypeIdentity::type>
- constexpr typename __or_<
- is_reference<_NestedType>,
- is_function<_NestedType>,
- is_void<_NestedType>,
- __is_array_unknown_bounds<_NestedType>
- >::type __is_complete_or_unbounded(_TypeIdentity)
+ // More specialized overload for complete object types (returning true_type).
+ template<typename _Tp,
+ typename = __enable_if_t<__maybe_complete_object_type<_Tp>::value>,
+ size_t = sizeof(_Tp)>
+ constexpr true_type
+ __is_complete_or_unbounded(__type_identity<_Tp>)
+ { return {}; };
+
+ // Less specialized overload for reference and unknown-bound array types
+ // (returning true_type), and incomplete types (returning false_type).
+ template<typename _TypeIdentity,
+ typename _NestedType = typename _TypeIdentity::type>
+ constexpr typename __not_<__maybe_complete_object_type<_NestedType>>::type
+ __is_complete_or_unbounded(_TypeIdentity)
{ return {}; }
// __remove_cv_t (std::remove_cv_t for C++11).
diff --git a/libstdc++-v3/testsuite/20_util/is_complete_or_unbounded/120717.cc b/libstdc++-v3/testsuite/20_util/is_complete_or_unbounded/120717.cc
new file mode 100644
index 000000000000..31fdf8fe9227
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/is_complete_or_unbounded/120717.cc
@@ -0,0 +1,20 @@
+// PR libstdc++/120717
+// { dg-do compile { target c++11 } }
+// { dg-additional-options "-Wsfinae-incomplete" }
+
+#include <type_traits>
+
+// Verify __is_complete_or_unbounded doesn't try to instantiate the underlying
+// type of a reference or array of unknown bound.
+template<class T> struct A { static_assert(false, "do not instantiate"); };
+static_assert(std::__is_complete_or_unbounded(std::__type_identity<A<int>&>{}), "");
+static_assert(std::__is_complete_or_unbounded(std::__type_identity<A<int>&&>{}), "");
+static_assert(std::__is_complete_or_unbounded(std::__type_identity<A<int>[]>{}), "");
+
+// Verify __is_complete_or_unbounded doesn't produce -Wsfinae-incomplete
+// warnings.
+struct B;
+static_assert(std::__is_complete_or_unbounded(std::__type_identity<B&>{}), "");
+static_assert(std::__is_complete_or_unbounded(std::__type_identity<B&&>{}), "");
+static_assert(std::__is_complete_or_unbounded(std::__type_identity<B[]>{}), "");
+struct B { }; // { dg-bogus "-Wsfinae-incomplete" }
--
2.50.0.81.gcb3b40381e
More information about the Libstdc++
mailing list