On Tue, 24 Jun 2025 at 13:53, Patrick Palka <ppa...@redhat.com> wrote:
>
> On Tue, 24 Jun 2025, Jonathan Wakely wrote:
>
> > On Tue, 24 Jun 2025 at 03:20, Patrick Palka <ppa...@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?

Yeah, I find that easier to follow even with a head full of potato ;-)

OK for trunk, thanks.

>
> -- >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
>

Reply via email to