From: Jason Merrill <jason@redhat.com>
To: Jonathan Wakely <jwakely@redhat.com>, gcc-patches@gcc.gnu.org
Cc: libstdc++@gcc.gnu.org
Subject: Re: [PATCH v2] c++: Fix type completeness checks for type traits [PR106838]
Date: Thu, 8 Sep 2022 12:12:27 -0400 [thread overview]
Message-ID: <65658c39-e0fb-6ad3-9136-fac36d763f4a@redhat.com> (raw)
In-Reply-To: <20220908125606.3272086-1-jwakely@redhat.com>
On 9/8/22 08:56, Jonathan Wakely wrote:
> On Wed, 7 Sept 2022 at 16:11, Jason Merrill wrote:
>>
>> On 9/7/22 08:18, Jonathan Wakely wrote:
>>> @@ -12080,23 +12098,37 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
>>> case CPTK_HAS_TRIVIAL_COPY:
>>> case CPTK_HAS_TRIVIAL_DESTRUCTOR:
>>> case CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS:
>>> - case CPTK_HAS_VIRTUAL_DESTRUCTOR:
>>> - case CPTK_IS_ABSTRACT:
>>> + if (!check_trait_type (type1))
>>> + return error_mark_node;
>>> + break;
>>> +
>>> case CPTK_IS_AGGREGATE:
>>
>> Hmm, why does std::is_aggregate require a complete array element type?
>> An array is an aggregate regardless of its element type. I'd think it
>> should be kind 4 instead.
>
> That makes sense. It doesn't match the precondition for the library
> trait in the standard, but violating that library precondition is
> undefined, not ill-formed, so it seems harmless for libstdc++ to also
> relax that precondition and allow arrays of incomplete type.
>
>>
>>> - case CPTK_IS_EMPTY:
>>> - case CPTK_IS_FINAL:
>>> case CPTK_IS_LITERAL_TYPE:
>>> case CPTK_IS_POD:
>>> - case CPTK_IS_POLYMORPHIC:
>>> case CPTK_IS_STD_LAYOUT:
>>> case CPTK_IS_TRIVIAL:
>>> case CPTK_IS_TRIVIALLY_COPYABLE:
>>> - if (!check_trait_type (type1))
>>> + if (!check_trait_type (type1, /* kind = */ 2))
>>> + return error_mark_node;
>>> + break;
>>> +
>>> + case CPTK_IS_EMPTY:
>>
>> I also wonder why std::is_empty excludes unions, since there can now be
>> an empty field of union type with the addition of [[no_unique_address]]?
>
> True, although the main purpose of is_empty is to decide whether to
> derive from it to get the EBO. With [[no_unique_address]] there's no
> reason to do that, you can just add the attribute unconditionally and if
> it's not empty, the attribute is a no-op. So I find it hard to care
> about this case :-)
>
> If the FE trait requires unions to be complete, then the library trait
> would need to special-case unions to not use the FE trait, so that it
> can still give the same answer (i.e. false) for incomplete union types.
> I don't think changing this one has any advantage.
>
>>
>>> + case CPTK_IS_POLYMORPHIC:
>>> + case CPTK_IS_ABSTRACT:
>>> + case CPTK_HAS_VIRTUAL_DESTRUCTOR:
>>> + if (!check_trait_type (type1, /* kind = */ 3))
>>> + return error_mark_node;
>>> + break;
>>> +
>>> + case CPTK_IS_FINAL:
>>
>> And I wonder a bit why std::is_final cares about the completeness of
>> unions, which can't be (or have) base classes, but I guess you can still
>> ask about whether the keyword was present, even though it has no effect.
>
> Yes, as currently specified, it tells you whether the specifier was
> present, nothing more.
>
> Thanks for the review, here's an updated patch that moves __is_aggregate
> to kind 4. I didn't change __is_empty or __is_final though, did you want
> those changed?
>
> Tested x86_64-linux.
That's fine, the patch is OK with the typo fix below:
> -- >8 --
>
> The check_trait_type function is used for a number of different type
> traits that have different requirements on their arguments. For example,
> __is_constructible allows arrays of unknown bound even if the array
> element is an incomplete type, but __is_aggregate does not, it always
> requires the array element type to be complete. Other traits have
> different requirements again, e.g. __is_empty allows incomplete unions,
> and arrays (of known or unknown bound) of incomplete types.
>
> This alters the check_trait_type function to take an additional KIND
> parameter which indicates which set of type trait requirements to check.
>
> As noted in a comment, the requirements for __is_aggregate deviate from
> the ones for std::is_aggregate in the standard. It's not necessary for
> the elements of an array to be complete types, because arrays are always
> aggregates.
>
> The type_has_virtual_destructor change is needed to avoid an ICE.
> Previously it could never be called for incomplete union types as they
> were (incorrectly) rejected by check_trait_type.
>
> This change causes some additional diagnostics in some libstdc++ tests,
> where the front end was not previously complaining about invalid types
> that the library assertions diagnosed. We should consider removing the
> library assertions from traits where the front end implements the
> correct checks now.
>
> PR c++/106838
>
> gcc/cp/ChangeLog:
>
> * class.cc (type_has_virtual_destructor): Return false for
> union types.
> * semantics.cc (check_trait_type): Add KIND parameter to support
> different sets of requirements.
> (finish_trait_expr): Pass KIND argument for relevant traits.
>
> gcc/ChangeLog:
>
> * doc/extend.texi (Type Traits): Fix requirements. Document
> __is_aggregate and __is_final.
>
> gcc/testsuite/ChangeLog:
>
> * g++.dg/ext/array4.C: Fix invalid use of __is_constructible.
> * g++.dg/ext/unary_trait_incomplete.C: Fix tests for traits with
> different requirements.
>
> libstdc++-v3/ChangeLog:
>
> * testsuite/20_util/is_complete_or_unbounded/memoization_neg.cc:
> Prune additional errors from front-end.
> * testsuite/20_util/is_move_constructible/incomplete_neg.cc:
> Likewise.
> * testsuite/20_util/is_nothrow_swappable/incomplete_neg.cc:
> Likewise.
> * testsuite/20_util/is_nothrow_swappable_with/incomplete_neg.cc:
> Likewise.
> * testsuite/20_util/is_swappable_with/incomplete_neg.cc:
> Likewise.
> ---
> gcc/cp/class.cc | 2 +-
> gcc/cp/semantics.cc | 58 +++++++--
> gcc/doc/extend.texi | 30 +++--
> gcc/testsuite/g++.dg/ext/array4.C | 3 +-
> .../g++.dg/ext/unary_trait_incomplete.C | 116 ++++++++++++++----
> .../memoization_neg.cc | 2 +
> .../is_move_constructible/incomplete_neg.cc | 1 +
> .../is_nothrow_swappable/incomplete_neg.cc | 1 +
> .../incomplete_neg.cc | 1 +
> .../is_swappable_with/incomplete_neg.cc | 1 +
> 10 files changed, 164 insertions(+), 51 deletions(-)
>
> diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc
> index a12d3673b96..b84f4227e7e 100644
> --- a/gcc/cp/class.cc
> +++ b/gcc/cp/class.cc
> @@ -5620,7 +5620,7 @@ type_has_virtual_destructor (tree type)
> {
> tree dtor;
>
> - if (!CLASS_TYPE_P (type))
> + if (!NON_UNION_CLASS_TYPE_P (type))
> return false;
>
> gcc_assert (COMPLETE_TYPE_P (type));
> diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc
> index 3d58d67ec11..aa3bfc457c1 100644
> --- a/gcc/cp/semantics.cc
> +++ b/gcc/cp/semantics.cc
> @@ -12028,11 +12028,23 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2)
> }
> }
>
> -/* If TYPE is an array of unknown bound, or (possibly cv-qualified)
> - void, or a complete type, returns true, otherwise false. */
> +/* Returns true if TYPE meets the requirements for the specified KIND,
> + false otherwise.
> +
> + When KIND == 1, TYPE must be an array of unknown bound,
> + or (possibly cv-qualified) void, or a complete type.
> +
> + When KIND == 2, TYPE must be a complete type, or array of complete type,
> + or (possibly cv-qualified) void.
> +
> + When KIND == 3:
> + If TYPE is a non-union class type, it must be complete.
> +
> + When KIND == 4:
> + If TYPE is a class type, it must be complete. */
>
> static bool
> -check_trait_type (tree type)
> +check_trait_type (tree type, int kind = 1)
> {
> if (type == NULL_TREE)
> return true;
> @@ -12041,8 +12053,14 @@ check_trait_type (tree type)
> return (check_trait_type (TREE_VALUE (type))
> && check_trait_type (TREE_CHAIN (type)));
>
> - if (TREE_CODE (type) == ARRAY_TYPE && !TYPE_DOMAIN (type))
> - return true;
> + if (kind == 1 && TREE_CODE (type) == ARRAY_TYPE && !TYPE_DOMAIN (type))
> + return true; // Array of unknown bound. Don't care about completeness.
> +
> + if (kind == 3 && !NON_UNION_CLASS_TYPE_P (type))
> + return true; // Not a non-union class type. Don't care about completeness.
> +
> + if (kind == 4 && TREE_CODE (type) == ARRAY_TYPE)
> + return true; // Not a class type. Don't care about completeness.
>
> if (VOID_TYPE_P (type))
> return true;
> @@ -12080,23 +12098,39 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2)
> case CPTK_HAS_TRIVIAL_COPY:
> case CPTK_HAS_TRIVIAL_DESTRUCTOR:
> case CPTK_HAS_UNIQUE_OBJ_REPRESENTATIONS:
> - case CPTK_HAS_VIRTUAL_DESTRUCTOR:
> - case CPTK_IS_ABSTRACT:
> - case CPTK_IS_AGGREGATE:
> - case CPTK_IS_EMPTY:
> - case CPTK_IS_FINAL:
> + if (!check_trait_type (type1))
> + return error_mark_node;
> + break;
> +
> case CPTK_IS_LITERAL_TYPE:
> case CPTK_IS_POD:
> - case CPTK_IS_POLYMORPHIC:
> case CPTK_IS_STD_LAYOUT:
> case CPTK_IS_TRIVIAL:
> case CPTK_IS_TRIVIALLY_COPYABLE:
> - if (!check_trait_type (type1))
> + if (!check_trait_type (type1, /* kind = */ 2))
> + return error_mark_node;
> + break;
> +
> + case CPTK_IS_EMPTY:
> + case CPTK_IS_POLYMORPHIC:
> + case CPTK_IS_ABSTRACT:
> + case CPTK_HAS_VIRTUAL_DESTRUCTOR:
> + if (!check_trait_type (type1, /* kind = */ 3))
> + return error_mark_node;
> + break;
> +
> + /* N.B. std::is_aggregate is kind=2 but we don't need a complete element
> + type to know whether an array is an aggregate, so use kind=2 here. */
*kind=4
> + case CPTK_IS_AGGREGATE:
> + case CPTK_IS_FINAL:
> + if (!check_trait_type (type1, /* kind = */ 4))
> return error_mark_node;
> break;
>
> case CPTK_IS_ASSIGNABLE:
> case CPTK_IS_CONSTRUCTIBLE:
> + if (!check_trait_type (type1))
> + return error_mark_node;
> break;
>
> case CPTK_IS_TRIVIALLY_ASSIGNABLE:
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index 0fedab96610..be3ea890c47 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -25174,14 +25174,17 @@ Requires: @code{type} shall be a complete type, (possibly cv-qualified)
> @item __has_virtual_destructor (type)
> If @code{type} is a class type with a virtual destructor
> ([class.dtor]) then the trait is @code{true}, else it is @code{false}.
> -Requires: @code{type} shall be a complete type, (possibly cv-qualified)
> -@code{void}, or an array of unknown bound.
> +Requires: If @code{type} is a non-union class type, it shall be a complete type.
>
> @item __is_abstract (type)
> If @code{type} is an abstract class ([class.abstract]) then the trait
> is @code{true}, else it is @code{false}.
> -Requires: @code{type} shall be a complete type, (possibly cv-qualified)
> -@code{void}, or an array of unknown bound.
> +Requires: If @code{type} is a non-union class type, it shall be a complete type.
> +
> +@item __is_aggregate (type)
> +If @code{type} is an aggregate type ([dcl.init.aggr]) the trait is
> +@code{true}, else it is @code{false}.
> +Requires: If @code{type} is a class type, it shall be a complete type.
>
> @item __is_base_of (base_type, derived_type)
> If @code{base_type} is a base class of @code{derived_type}
> @@ -25206,13 +25209,17 @@ any, are bit-fields of length 0, and @code{type} has no virtual
> members, and @code{type} has no virtual base classes, and @code{type}
> has no base classes @code{base_type} for which
> @code{__is_empty (base_type)} is @code{false}.
> -Requires: @code{type} shall be a complete type, (possibly cv-qualified)
> -@code{void}, or an array of unknown bound.
> +Requires: If @code{type} is a non-union class type, it shall be a complete type.
>
> @item __is_enum (type)
> If @code{type} is a cv enumeration type ([basic.compound]) the trait is
> @code{true}, else it is @code{false}.
>
> +@item __is_final (type)
> +If @code{type} is a class or union type marked @code{final}, then the trait
> +is @code{true}, else it is @code{false}.
> +Requires: If @code{type} is a class type, it shall be a complete type.
> +
> @item __is_literal_type (type)
> If @code{type} is a literal type ([basic.types]) the trait is
> @code{true}, else it is @code{false}.
> @@ -25228,20 +25235,19 @@ Requires: @code{type} shall be a complete type, (possibly cv-qualified)
> @item __is_polymorphic (type)
> If @code{type} is a polymorphic class ([class.virtual]) then the trait
> is @code{true}, else it is @code{false}.
> -Requires: @code{type} shall be a complete type, (possibly cv-qualified)
> -@code{void}, or an array of unknown bound.
> +Requires: If @code{type} is a non-union class type, it shall be a complete type.
>
> @item __is_standard_layout (type)
> If @code{type} is a standard-layout type ([basic.types]) the trait is
> @code{true}, else it is @code{false}.
> -Requires: @code{type} shall be a complete type, (possibly cv-qualified)
> -@code{void}, or an array of unknown bound.
> +Requires: @code{type} shall be a complete type, an array of complete types,
> +or (possibly cv-qualified) @code{void}.
>
> @item __is_trivial (type)
> If @code{type} is a trivial type ([basic.types]) the trait is
> @code{true}, else it is @code{false}.
> -Requires: @code{type} shall be a complete type, (possibly cv-qualified)
> -@code{void}, or an array of unknown bound.
> +Requires: @code{type} shall be a complete type, an array of complete types,
> +or (possibly cv-qualified) @code{void}.
>
> @item __is_union (type)
> If @code{type} is a cv union type ([basic.compound]) the trait is
> diff --git a/gcc/testsuite/g++.dg/ext/array4.C b/gcc/testsuite/g++.dg/ext/array4.C
> index 0068ea854e1..6adf9a3b0ee 100644
> --- a/gcc/testsuite/g++.dg/ext/array4.C
> +++ b/gcc/testsuite/g++.dg/ext/array4.C
> @@ -16,7 +16,6 @@ template <typename _Tp>
> constexpr integral_constant<true> __is_complete_or_unbounded(_Tp) {
> return {};
> }
> -struct Trans_NS_std_formatter;
> template <typename _Tp>
> struct is_default_constructible : integral_constant<false> {
> static_assert(__is_complete_or_unbounded(_Tp{}), "");
> @@ -53,7 +52,7 @@ template <typename> struct basic_string_view { basic_string_view(int, int); };
> template <typename, typename> struct formatter;
> template <typename, typename>
> using has_formatter =
> - __bool_constant<__is_constructible(Trans_NS_std_formatter)>;
> + __bool_constant<__is_constructible(void)>;
> struct fallback_formatter;
> template <typename Context> struct custom_value {
> using parse_context = typename Context::parse_context_type;
> diff --git a/gcc/testsuite/g++.dg/ext/unary_trait_incomplete.C b/gcc/testsuite/g++.dg/ext/unary_trait_incomplete.C
> index 6c83279c030..1dfa449a8d2 100644
> --- a/gcc/testsuite/g++.dg/ext/unary_trait_incomplete.C
> +++ b/gcc/testsuite/g++.dg/ext/unary_trait_incomplete.C
> @@ -2,6 +2,7 @@
>
> struct I; // { dg-message "forward declaration" }
> struct C { };
> +union U; // { dg-message "forward declaration" }
>
> bool nas1 = __has_nothrow_assign(I); // { dg-error "incomplete type" }
> bool nas2 = __has_nothrow_assign(C[]);
> @@ -39,38 +40,105 @@ bool tcp3 = __has_trivial_copy(I[]);
> bool tcp4 = __has_trivial_copy(void);
> bool tcp5 = __has_trivial_copy(const void);
>
> -bool vde1 = __has_virtual_destructor(I); // { dg-error "incomplete type" }
> -bool vde2 = __has_virtual_destructor(C[]);
> -bool vde3 = __has_virtual_destructor(I[]);
> -bool vde4 = __has_virtual_destructor(void);
> -bool vde5 = __has_virtual_destructor(const void);
> -
> bool tde1 = __has_trivial_destructor(I); // { dg-error "incomplete type" }
> bool tde2 = __has_trivial_destructor(C[]);
> bool tde3 = __has_trivial_destructor(I[]);
> bool tde4 = __has_trivial_destructor(void);
> bool tde5 = __has_trivial_destructor(const void);
>
> -bool abs1 = __is_abstract(I); // { dg-error "incomplete type" }
> -bool abs2 = __is_abstract(C[]);
> -bool abs3 = __is_abstract(I[]);
> -bool abs4 = __is_abstract(void);
> -bool abs5 = __is_abstract(const void);
> +// T shall be a complete type, cv void, or an array of unknown bound.
>
> -bool pod1 = __is_pod(I); // { dg-error "incomplete type" }
> -bool pod2 = __is_pod(C[]);
> -bool pod3 = __is_pod(I[]);
> -bool pod4 = __is_pod(void);
> -bool pod5 = __is_pod(const void);
> +bool con1 = __is_constructible(C);
> +bool con2 = __is_constructible(I); // { dg-error "incomplete type" }
> +bool con3 = __is_constructible(U); // { dg-error "incomplete type" }
> +bool con4 = __is_constructible(C[]);
> +bool con5 = __is_constructible(I[]);
> +bool con6 = __is_constructible(U[]);
> +bool con7 = __is_constructible(C[1]);
> +bool con8 = __is_constructible(I[1]); // { dg-error "incomplete type" }
> +bool con9 = __is_constructible(U[1]); // { dg-error "incomplete type" }
> +bool con10 = __is_constructible(void);
> +bool con11 = __is_constructible(const void);
> +
> +// If T is a non-union class type, T shall be a complete type.
> +
> +bool vde1 = __has_virtual_destructor(I); // { dg-error "incomplete type" }
> +bool vde2 = __has_virtual_destructor(U);
> +bool vde3 = __has_virtual_destructor(C[]);
> +bool vde4 = __has_virtual_destructor(I[]);
> +bool vde5 = __has_virtual_destructor(U[]);
> +bool vde6 = __has_virtual_destructor(C[1]);
> +bool vde7 = __has_virtual_destructor(I[1]);
> +bool vde8 = __has_virtual_destructor(U[1]);
> +bool vde9 = __has_virtual_destructor(void);
> +bool vde10 = __has_virtual_destructor(const void);
> +
> +bool abs1 = __is_abstract(I); // { dg-error "incomplete type" }
> +bool abs2 = __is_abstract(U);
> +bool abs3 = __is_abstract(C[]);
> +bool abs4 = __is_abstract(I[]);
> +bool abs5 = __is_abstract(U[]);
> +bool abs6 = __is_abstract(C[1]);
> +bool abs7 = __is_abstract(I[1]);
> +bool abs8 = __is_abstract(U[1]);
> +bool abs9 = __is_abstract(void);
> +bool abs10 = __is_abstract(const void);
>
> bool emp1 = __is_empty(I); // { dg-error "incomplete type" }
> -bool emp2 = __is_empty(C[]);
> -bool emp3 = __is_empty(I[]);
> -bool emp4 = __is_empty(void);
> -bool emp5 = __is_empty(const void);
> +bool emp2 = __is_empty(U);
> +bool emp3 = __is_empty(C[]);
> +bool emp4 = __is_empty(I[]);
> +bool emp5 = __is_empty(U[]);
> +bool emp6 = __is_empty(C[1]);
> +bool emp7 = __is_empty(I[1]);
> +bool emp8 = __is_empty(U[1]);
> +bool emp9 = __is_empty(void);
> +bool emp10 = __is_empty(const void);
>
> bool pol1 = __is_polymorphic(I); // { dg-error "incomplete type" }
> -bool pol2 = __is_polymorphic(C[]);
> -bool pol3 = __is_polymorphic(I[]);
> -bool pol4 = __is_polymorphic(void);
> -bool pol5 = __is_polymorphic(const void);
> +bool pol2 = __is_polymorphic(U);
> +bool pol3 = __is_polymorphic(C[]);
> +bool pol4 = __is_polymorphic(I[]);
> +bool pol5 = __is_polymorphic(U[]);
> +bool pol6 = __is_polymorphic(C[1]);
> +bool pol7 = __is_polymorphic(I[1]);
> +bool pol8 = __is_polymorphic(U[1]);
> +bool pol9 = __is_polymorphic(void);
> +bool pol10 = __is_polymorphic(const void);
> +
> +// If T is a class type, T shall be a complete type.
> +
> +bool agg1 = __is_aggregate(I); // { dg-error "incomplete type" }
> +bool agg2 = __is_aggregate(U); // { dg-error "incomplete type" }
> +bool agg3 = __is_aggregate(C[]);
> +bool agg4 = __is_aggregate(I[]);
> +bool agg5 = __is_aggregate(U[]);
> +bool agg6 = __is_aggregate(C[1]);
> +bool agg7 = __is_aggregate(I[1]);
> +bool agg8 = __is_aggregate(U[1]);
> +bool agg9 = __is_aggregate(void);
> +bool agg10 = __is_aggregate(const void);
> +
> +bool fin1 = __is_final(I); // { dg-error "incomplete type" }
> +bool fin2 = __is_final(U); // { dg-error "incomplete type" }
> +bool fin3 = __is_final(C[]);
> +bool fin4 = __is_final(I[]);
> +bool fin5 = __is_final(U[]);
> +bool fin6 = __is_final(C[1]);
> +bool fin7 = __is_final(I[1]);
> +bool fin8 = __is_final(U[1]);
> +bool fin9 = __is_final(void);
> +bool fin10 = __is_final(const void);
> +
> +// remove_all_extents_t<T> shall be a complete type or cv void.
> +
> +bool pod1 = __is_pod(I); // { dg-error "incomplete type" }
> +bool pod2 = __is_pod(U); // { dg-error "incomplete type" }
> +bool pod3 = __is_pod(C[]);
> +bool pod4 = __is_pod(I[]); // { dg-error "incomplete type" }
> +bool pod5 = __is_pod(U[]); // { dg-error "incomplete type" }
> +bool pod6 = __is_pod(C[1]);
> +bool pod7 = __is_pod(I[1]); // { dg-error "incomplete type" }
> +bool pod8 = __is_pod(U[1]); // { dg-error "incomplete type" }
> +bool pod9 = __is_pod(void);
> +bool pod10 = __is_pod(const void);
> 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 1870e501723..fc0b70b319c 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
> @@ -1,5 +1,7 @@
> // { dg-do compile { target c++11 } }
> // { dg-prune-output "must be a complete" }
> +// { dg-prune-output "'value' is not a member of 'std::is_move_cons" }
> +// { dg-prune-output "invalid use of incomplete type" }
>
> // Copyright (C) 2019-2022 Free Software Foundation, Inc.
> //
> diff --git a/libstdc++-v3/testsuite/20_util/is_move_constructible/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_move_constructible/incomplete_neg.cc
> index 7bd453d7851..7c34b5f1224 100644
> --- a/libstdc++-v3/testsuite/20_util/is_move_constructible/incomplete_neg.cc
> +++ b/libstdc++-v3/testsuite/20_util/is_move_constructible/incomplete_neg.cc
> @@ -18,6 +18,7 @@
> // <http://www.gnu.org/licenses/>.
>
> // { dg-error "must be a complete class" "" { target *-*-* } 0 }
> +// { dg-prune-output "invalid use of incomplete type" }
>
> #include <type_traits>
>
> diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_swappable/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_swappable/incomplete_neg.cc
> index 88c9c01f37c..d3a34cca7a7 100644
> --- a/libstdc++-v3/testsuite/20_util/is_nothrow_swappable/incomplete_neg.cc
> +++ b/libstdc++-v3/testsuite/20_util/is_nothrow_swappable/incomplete_neg.cc
> @@ -18,6 +18,7 @@
> // <http://www.gnu.org/licenses/>.
>
> // { dg-error "must be a complete class" "" { target *-*-* } 0 }
> +// { dg-prune-output "invalid use of incomplete type" }
>
> #include <type_traits>
>
> diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_swappable_with/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_swappable_with/incomplete_neg.cc
> index da0f7713659..6dfa3363d88 100644
> --- a/libstdc++-v3/testsuite/20_util/is_nothrow_swappable_with/incomplete_neg.cc
> +++ b/libstdc++-v3/testsuite/20_util/is_nothrow_swappable_with/incomplete_neg.cc
> @@ -18,6 +18,7 @@
> // <http://www.gnu.org/licenses/>.
>
> // { dg-error "must be a complete class" "" { target *-*-* } 0 }
> +// { dg-prune-output "invalid use of incomplete type" }
>
> #include <type_traits>
>
> diff --git a/libstdc++-v3/testsuite/20_util/is_swappable_with/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_swappable_with/incomplete_neg.cc
> index 74ad291a931..d5fa42256ea 100644
> --- a/libstdc++-v3/testsuite/20_util/is_swappable_with/incomplete_neg.cc
> +++ b/libstdc++-v3/testsuite/20_util/is_swappable_with/incomplete_neg.cc
> @@ -18,6 +18,7 @@
> // <http://www.gnu.org/licenses/>.
>
> // { dg-error "must be a complete class" "" { target *-*-* } 0 }
> +// { dg-prune-output "invalid use of incomplete type" }
>
> #include <type_traits>
>
next prev parent reply other threads:[~2022-09-08 16:13 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-09-07 12:18 [PATCH] " Jonathan Wakely
2022-09-07 15:11 ` Jason Merrill
2022-09-08 12:56 ` [PATCH v2] " Jonathan Wakely
2022-09-08 16:12 ` Jason Merrill [this message]
2022-09-08 16:38 ` Jonathan Wakely
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=65658c39-e0fb-6ad3-9136-fac36d763f4a@redhat.com \
--to=jason@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=jwakely@redhat.com \
--cc=libstdc++@gcc.gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).