public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
From: Jason Merrill <jason@redhat.com>
To: Jonathan Wakely <jwakely@redhat.com>,
	gcc-patches@gcc.gnu.org, libstdc++@gcc.gnu.org
Subject: Re: [PATCH] c++: Fix type completeness checks for type traits [PR106838]
Date: Wed, 7 Sep 2022 11:11:03 -0400	[thread overview]
Message-ID: <a62c78f8-191f-e15e-c9a2-ae636d075db8@redhat.com> (raw)
In-Reply-To: <20220907121827.1858435-1-jwakely@redhat.com>

On 9/7/22 08:18, Jonathan Wakely wrote:
> Tested powerpc64le-linux. OK for trunk?
> 
> -- >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.
> 
> 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                           |  54 +++++++--
>   gcc/doc/extend.texi                           |  31 ++++--
>   gcc/testsuite/g++.dg/ext/array4.C             |   3 +-
>   .../g++.dg/ext/unary_trait_incomplete.C       | 105 ++++++++++++++----
>   .../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, 151 insertions(+), 50 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..ee361ae22cb 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,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.

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

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

> +      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..1a05dace97e 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -25174,14 +25174,18 @@ 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: @code{type} shall be a complete type, an array of complete types,
> +or (possibly cv-qualified) @code{void}.
>   
>   @item __is_base_of (base_type, derived_type)
>   If @code{base_type} is a base class of @code{derived_type}
> @@ -25206,13 +25210,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 +25236,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..236437b7f5b 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,94 @@ 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 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>
>   


  reply	other threads:[~2022-09-07 15:11 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-09-07 12:18 Jonathan Wakely
2022-09-07 15:11 ` Jason Merrill [this message]
2022-09-08 12:56   ` [PATCH v2] " Jonathan Wakely
2022-09-08 16:12     ` Jason Merrill
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=a62c78f8-191f-e15e-c9a2-ae636d075db8@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).