c++/coroutines: check for members we use in handle_types [PR105475]

Message ID 20240801163846.2471761-1-arsen@aarsen.me
State Committed
Headers
Series c++/coroutines: check for members we use in handle_types [PR105475] |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Build passed

Commit Message

Arsen Arsenović Aug. 1, 2024, 4:34 p.m. UTC
  Tested on x86_64-pc-linux-gnu.

OK for trunk?

TIA, have a lovely day.
---------- >8 ----------
Currently, it is possible to ICE GCC by giving it sufficiently broken
code, where sufficiently broken means a std::coroutine_handle missing a
default on the promise_type template argument, and missing members.
As the code generator relies on lookups in the coroutine_handle never
failing (and has no way to signal that error), lets do it ahead of time,
save the result, and use that.  This saves us some lookups and allows us
to propagate an error.

PR c++/105475 - coroutines: ICE in coerce_template_parms, at cp/pt.cc:9183

gcc/cp/ChangeLog:

	PR c++/105475
	* coroutines.cc (struct coroutine_info): Add from_address_bl.
	Carries the from_address member we looked up earlier.
	(coro_resume_identifier): Remove.  Unused.
	(coro_init_identifiers): Do not initialize the above.
	(void_coro_handle_address): New variable.  Contains the baselink
	for the std::coroutine_handle<void>::address() instance method.
	(get_handle_type_address_bl): New function.  Looks up and
	validates handle_type::address in a given handle_type.
	(get_handle_type_from_address_bl): New function.  Looks up and
	validates handle_type::from_address in a given handle_type.
	(ensure_coro_initialized):Remove reliance on
	coroutine_handle<> defaulting the promise type to void.
	Initialize void_coro_handle_address with
	get_handle_type_from_address_bl.
	(coro_promise_type_found_p): Initialize
	coro_info->from_address_bl with the result of
	get_handle_type_from_address_bl.
	(get_coroutine_from_address_baselink): New helper.  Gets the
	handle_type::from_address BASELINK from a coroutine_info.
	(build_actor_fn): Use the get_coroutine_from_address_baselink
	helper and void_coro_handle_address.

gcc/testsuite/ChangeLog:

	PR c++/105475
	* g++.dg/coroutines/pr103868.C: Add std::coroutine_handle
	members we check for now.
	* g++.dg/coroutines/pr105287.C: Ditto.
	* g++.dg/coroutines/pr105301.C: Ditto.
	* g++.dg/coroutines/pr94528.C: Ditto.
	* g++.dg/coroutines/pr94879-folly-1.C: Ditto.
	* g++.dg/coroutines/pr94883-folly-2.C: Ditto.
	* g++.dg/coroutines/pr98118.C: Ditto.
	* g++.dg/coroutines/pr105475.C: New test.
	* g++.dg/coroutines/pr105475-1.C: New test.
	* g++.dg/coroutines/pr105475-2.C: New test.
	* g++.dg/coroutines/pr105475-3.C: New test.
	* g++.dg/coroutines/pr105475-4.c: New test.
	* g++.dg/coroutines/pr105475-broken-spec.C: New test.
	* g++.dg/coroutines/pr105475-broken-spec-2.C: New test.
---
 gcc/cp/coroutines.cc                          | 140 ++++++++++++++++--
 gcc/testsuite/g++.dg/coroutines/pr103868.C    |   2 +
 gcc/testsuite/g++.dg/coroutines/pr105287.C    |   2 +
 gcc/testsuite/g++.dg/coroutines/pr105301.C    |   5 +-
 gcc/testsuite/g++.dg/coroutines/pr105475-1.C  |  27 ++++
 gcc/testsuite/g++.dg/coroutines/pr105475-2.C  |  29 ++++
 gcc/testsuite/g++.dg/coroutines/pr105475-3.C  |  29 ++++
 gcc/testsuite/g++.dg/coroutines/pr105475-4.c  |  33 +++++
 .../coroutines/pr105475-broken-spec-2.C       |  33 +++++
 .../g++.dg/coroutines/pr105475-broken-spec.C  |  29 ++++
 gcc/testsuite/g++.dg/coroutines/pr105475.C    |  28 ++++
 gcc/testsuite/g++.dg/coroutines/pr94528.C     |  11 +-
 .../g++.dg/coroutines/pr94879-folly-1.C       |  10 +-
 .../g++.dg/coroutines/pr94883-folly-2.C       |  10 +-
 gcc/testsuite/g++.dg/coroutines/pr98118.C     |  10 +-
 15 files changed, 378 insertions(+), 20 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/coroutines/pr105475-1.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/pr105475-2.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/pr105475-3.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/pr105475-4.c
 create mode 100644 gcc/testsuite/g++.dg/coroutines/pr105475-broken-spec-2.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/pr105475-broken-spec.C
 create mode 100644 gcc/testsuite/g++.dg/coroutines/pr105475.C
  

Comments

Jason Merrill Aug. 1, 2024, 9:17 p.m. UTC | #1
On 8/1/24 12:34 PM, Arsen Arsenović wrote:
> Tested on x86_64-pc-linux-gnu.
> 
> OK for trunk?
> 
> TIA, have a lovely day.
> ---------- >8 ----------
> Currently, it is possible to ICE GCC by giving it sufficiently broken
> code, where sufficiently broken means a std::coroutine_handle missing a
> default on the promise_type template argument, and missing members.
> As the code generator relies on lookups in the coroutine_handle never
> failing (and has no way to signal that error), lets do it ahead of time,
> save the result, and use that.  This saves us some lookups and allows us
> to propagate an error.
> 
> PR c++/105475 - coroutines: ICE in coerce_template_parms, at cp/pt.cc:9183
> 
> gcc/cp/ChangeLog:
> 
> 	PR c++/105475
> 	* coroutines.cc (struct coroutine_info): Add from_address_bl.
> 	Carries the from_address member we looked up earlier.
> 	(coro_resume_identifier): Remove.  Unused.
> 	(coro_init_identifiers): Do not initialize the above.
> 	(void_coro_handle_address): New variable.  Contains the baselink
> 	for the std::coroutine_handle<void>::address() instance method.
> 	(get_handle_type_address_bl): New function.  Looks up and
> 	validates handle_type::address in a given handle_type.
> 	(get_handle_type_from_address_bl): New function.  Looks up and
> 	validates handle_type::from_address in a given handle_type.
> 	(ensure_coro_initialized):Remove reliance on
> 	coroutine_handle<> defaulting the promise type to void.
> 	Initialize void_coro_handle_address with
> 	get_handle_type_from_address_bl.
> 	(coro_promise_type_found_p): Initialize
> 	coro_info->from_address_bl with the result of
> 	get_handle_type_from_address_bl.
> 	(get_coroutine_from_address_baselink): New helper.  Gets the
> 	handle_type::from_address BASELINK from a coroutine_info.
> 	(build_actor_fn): Use the get_coroutine_from_address_baselink
> 	helper and void_coro_handle_address.

I don't think these names need to mention "baselink", but I'm not 
strongly against it.

A few other tweaks below:

> @@ -90,6 +90,7 @@ struct GTY((for_user)) coroutine_info
>     tree self_h_proxy;  /* A handle instance that is used as the proxy for the
>   			 one that will eventually be allocated in the coroutine
>   			 frame.  */
> +  tree from_address_bl;  /* handle_type from_address function (BASELINK).  */

Inserting this here breaks the "Likewise" reference in the next line.

>     tree promise_proxy; /* Likewise, a proxy promise instance.  */
>     tree return_void;   /* The expression for p.return_void() if it exists.  */
>     location_t first_coro_keyword; /* The location of the keyword that made this
> @@ -389,7 +389,105 @@ find_coro_handle_template_decl (location_t kw)
>       return handle_decl;
>   }
>   
> -/* Instantiate the handle template for a given promise type.  */
> +/* Get and validate HANDLE_TYPE#address.  The resulting function, if any, will

:: instead of #

> +   be a non-overloaded member function that takes no arguments and returns
> +   void*.  If that is not the case, signals an error and returns NULL_TREE.  */
> +
> +static tree
> +get_handle_type_address_bl (location_t kw, tree handle_type)
> +{
> +  tree addr_getter = lookup_member (handle_type, coro_address_identifier, 1,
> +				    0, tf_warning_or_error);
> +  if (!addr_getter || addr_getter == error_mark_node)
> +    {
> +      error_at (kw, "could not find %qE in %qT",
> +		coro_address_identifier, handle_type);

How about using qualified_name_lookup_error?

> +  gcc_assert (TREE_CODE (addr_getter) == BASELINK);

This looks like it will ICE on an invalid handle type (e.g. where 
"address" is a type or data member) instead of giving an error.

> +  tree fn_t = TREE_TYPE (addr_getter);
> +  if (TREE_CODE (fn_t) != METHOD_TYPE)
> +    {
> +      error_at (kw, "%qE must be a non-overloaded method", addr_getter);
> +      return NULL_TREE;
> +    }
> +
> +  tree arg = TYPE_ARG_TYPES (fn_t);
> +  /* Given that this is a method, we have an argument to skip (the this
> +     pointer).  */
> +  gcc_checking_assert (TREE_CHAIN (arg));

This assert seems redundant with the following checks.

> +  arg = TREE_CHAIN (arg);
> +
> +  /* Check that from_addr has the argument list ().  */
> +  if (!arg
> +      || !same_type_p (TREE_VALUE (arg), void_type_node)
> +      || TREE_CHAIN (arg))
> +    {
> +      error_at (kw, "%qE must take no arguments", addr_getter);
> +      return NULL_TREE;
> +    }

I think this could just be

if (arg != void_list_node)

as we share the terminal void between all non-varargs function types.

> +  tree ret_t = TREE_TYPE (fn_t);
> +  if (!same_type_p (ret_t, ptr_type_node))
> +    {
> +      error_at (kw, "%qE must return %qT, not %qT",
> +		addr_getter, ptr_type_node, ret_t);
> +      return NULL_TREE;
> +    }

Do you also want to check that it has public access?

> @@ -454,10 +552,15 @@ ensure_coro_initialized (location_t loc)
>   
>         /*  We can also instantiate the void coroutine_handle<>  */
>         void_coro_handle_type =
> -	instantiate_coro_handle_for_promise_type (loc, NULL_TREE);
> +	instantiate_coro_handle_for_promise_type (loc, void_type_node);
>         if (void_coro_handle_type == NULL_TREE)
>   	return false;
>   
> +      void_coro_handle_address =
> +	get_handle_type_address_bl (loc, void_coro_handle_type);

The = should be at the beginning of the second line for both variables.

> @@ -2365,8 +2480,9 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
>     tree ash = build_class_member_access_expr (actor_frame, ash_m, NULL_TREE,
>   					     false, tf_warning_or_error);
>     /* So construct the self-handle from the frame address.  */
> -  tree hfa_m = lookup_member (handle_type, coro_from_address_identifier, 1,
> -			      0, tf_warning_or_error);
> +  tree hfa_m = get_coroutine_from_address_baselink (orig);
> +  /* Should have been set earlier by coro_promise_type_found_p.  */
> +  gcc_assert (hfa_m);

Maybe move this assert into the new get function?

Jason
  

Patch

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index 9e8382269ed0..552aca64429c 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -90,6 +90,7 @@  struct GTY((for_user)) coroutine_info
   tree self_h_proxy;  /* A handle instance that is used as the proxy for the
 			 one that will eventually be allocated in the coroutine
 			 frame.  */
+  tree from_address_bl;  /* handle_type from_address function (BASELINK).  */
   tree promise_proxy; /* Likewise, a proxy promise instance.  */
   tree return_void;   /* The expression for p.return_void() if it exists.  */
   location_t first_coro_keyword; /* The location of the keyword that made this
@@ -203,7 +204,6 @@  static GTY(()) tree coro_final_suspend_identifier;
 static GTY(()) tree coro_return_void_identifier;
 static GTY(()) tree coro_return_value_identifier;
 static GTY(()) tree coro_yield_value_identifier;
-static GTY(()) tree coro_resume_identifier;
 static GTY(()) tree coro_address_identifier;
 static GTY(()) tree coro_from_address_identifier;
 static GTY(()) tree coro_get_return_object_identifier;
@@ -243,7 +243,6 @@  coro_init_identifiers ()
   coro_return_void_identifier = get_identifier ("return_void");
   coro_return_value_identifier = get_identifier ("return_value");
   coro_yield_value_identifier = get_identifier ("yield_value");
-  coro_resume_identifier = get_identifier ("resume");
   coro_address_identifier = get_identifier ("address");
   coro_from_address_identifier = get_identifier ("from_address");
   coro_get_return_object_identifier = get_identifier ("get_return_object");
@@ -271,6 +270,7 @@  coro_init_identifiers ()
 static GTY(()) tree coro_traits_templ;
 static GTY(()) tree coro_handle_templ;
 static GTY(()) tree void_coro_handle_type;
+static GTY(()) tree void_coro_handle_address;
 
 /* ================= Parse, Semantics and Type checking ================= */
 
@@ -389,7 +389,105 @@  find_coro_handle_template_decl (location_t kw)
     return handle_decl;
 }
 
-/* Instantiate the handle template for a given promise type.  */
+/* Get and validate HANDLE_TYPE#address.  The resulting function, if any, will
+   be a non-overloaded member function that takes no arguments and returns
+   void*.  If that is not the case, signals an error and returns NULL_TREE.  */
+
+static tree
+get_handle_type_address_bl (location_t kw, tree handle_type)
+{
+  tree addr_getter = lookup_member (handle_type, coro_address_identifier, 1,
+				    0, tf_warning_or_error);
+  if (!addr_getter || addr_getter == error_mark_node)
+    {
+      error_at (kw, "could not find %qE in %qT",
+		coro_address_identifier, handle_type);
+      return NULL_TREE;
+    }
+
+  gcc_assert (TREE_CODE (addr_getter) == BASELINK);
+
+  tree fn_t = TREE_TYPE (addr_getter);
+  if (TREE_CODE (fn_t) != METHOD_TYPE)
+    {
+      error_at (kw, "%qE must be a non-overloaded method", addr_getter);
+      return NULL_TREE;
+    }
+
+  tree arg = TYPE_ARG_TYPES (fn_t);
+  /* Given that this is a method, we have an argument to skip (the this
+     pointer).  */
+  gcc_checking_assert (TREE_CHAIN (arg));
+  arg = TREE_CHAIN (arg);
+
+  /* Check that from_addr has the argument list ().  */
+  if (!arg
+      || !same_type_p (TREE_VALUE (arg), void_type_node)
+      || TREE_CHAIN (arg))
+    {
+      error_at (kw, "%qE must take no arguments", addr_getter);
+      return NULL_TREE;
+    }
+
+  tree ret_t = TREE_TYPE (fn_t);
+  if (!same_type_p (ret_t, ptr_type_node))
+    {
+      error_at (kw, "%qE must return %qT, not %qT",
+		addr_getter, ptr_type_node, ret_t);
+      return NULL_TREE;
+    }
+
+  return addr_getter;
+}
+
+/* Get and validate HANDLE_TYPE::from_address.  The resulting function, if
+   any, will be a non-overloaded static function that takes a single void* and
+   returns HANDLE_TYPE.  If that is not the case, signals an error and returns
+   NULL_TREE.  */
+
+static tree
+get_handle_type_from_address_bl (location_t kw, tree handle_type)
+{
+  tree from_addr = lookup_member (handle_type, coro_from_address_identifier, 1,
+				  0, tf_warning_or_error);
+  if (!from_addr || from_addr == error_mark_node)
+    {
+      error_at (kw, "could not find %qE in %qT",
+		coro_from_address_identifier, handle_type);
+      return NULL_TREE;
+    }
+
+  gcc_assert (TREE_CODE (from_addr) == BASELINK);
+
+  tree fn_t = TREE_TYPE (from_addr);
+  if (TREE_CODE (fn_t) != FUNCTION_TYPE)
+    {
+      error_at (kw, "%qE must be a non-overloaded static function", from_addr);
+      return NULL_TREE;
+    }
+
+  tree arg = TYPE_ARG_TYPES (fn_t);
+  /* Check that from_addr has the argument list (void*).  */
+  if (!arg
+      || !same_type_p (TREE_VALUE (arg), ptr_type_node)
+      || (arg = TREE_CHAIN (arg), !arg)
+      || !same_type_p (TREE_VALUE (arg), void_type_node)
+      || TREE_CHAIN (arg))
+    {
+      error_at (kw, "%qE must take a single %qT", from_addr, ptr_type_node);
+      return NULL_TREE;
+    }
+
+  tree ret_t = TREE_TYPE (fn_t);
+  if (!same_type_p (ret_t, handle_type))
+    {
+      error_at (kw, "%qE must return %qT, not %qT",
+		from_addr, handle_type, ret_t);
+      return NULL_TREE;
+    }
+
+  return from_addr;
+}
 
 static tree
 instantiate_coro_handle_for_promise_type (location_t kw, tree promise_type)
@@ -454,10 +552,15 @@  ensure_coro_initialized (location_t loc)
 
       /*  We can also instantiate the void coroutine_handle<>  */
       void_coro_handle_type =
-	instantiate_coro_handle_for_promise_type (loc, NULL_TREE);
+	instantiate_coro_handle_for_promise_type (loc, void_type_node);
       if (void_coro_handle_type == NULL_TREE)
 	return false;
 
+      void_coro_handle_address =
+	get_handle_type_address_bl (loc, void_coro_handle_type);
+      if (!void_coro_handle_address)
+	return false;
+
       /* A table to hold the state, per coroutine decl.  */
       gcc_checking_assert (coroutine_info_table == NULL);
       coroutine_info_table =
@@ -556,9 +659,13 @@  coro_promise_type_found_p (tree fndecl, location_t loc)
 	instantiate_coro_handle_for_promise_type (loc, coro_info->promise_type);
       if (handle_type == NULL_TREE)
 	return false;
+      tree from_address = get_handle_type_from_address_bl (loc, handle_type);
+      if (from_address == NULL_TREE)
+	return false;
 
       /* Complete this, we're going to use it.  */
       coro_info->handle_type = complete_type_or_else (handle_type, fndecl);
+      coro_info->from_address_bl = from_address;
 
       /* Diagnostic would be emitted by complete_type_or_else.  */
       if (!coro_info->handle_type)
@@ -674,6 +781,15 @@  get_coroutine_promise_proxy (tree decl)
   return NULL_TREE;
 }
 
+static tree
+get_coroutine_from_address_baselink (tree decl)
+{
+  if (coroutine_info *info = get_coroutine_info (decl))
+    return info->from_address_bl;
+
+  return NULL_TREE;
+}
+
 static tree
 lookup_promise_method (tree fndecl, tree member_id, location_t loc,
 		       bool musthave)
@@ -2212,7 +2328,6 @@  build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
 {
   verify_stmt_tree (fnbody);
   /* Some things we inherit from the original function.  */
-  tree handle_type = get_coroutine_handle_type (orig);
   tree promise_type = get_coroutine_promise_type (orig);
   tree promise_proxy = get_coroutine_promise_proxy (orig);
 
@@ -2365,8 +2480,9 @@  build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
   tree ash = build_class_member_access_expr (actor_frame, ash_m, NULL_TREE,
 					     false, tf_warning_or_error);
   /* So construct the self-handle from the frame address.  */
-  tree hfa_m = lookup_member (handle_type, coro_from_address_identifier, 1,
-			      0, tf_warning_or_error);
+  tree hfa_m = get_coroutine_from_address_baselink (orig);
+  /* Should have been set earlier by coro_promise_type_found_p.  */
+  gcc_assert (hfa_m);
 
   r = build1 (CONVERT_EXPR, build_pointer_type (void_type_node), actor_fp);
   vec<tree, va_gc> *args = make_tree_vector_single (r);
@@ -2461,12 +2577,14 @@  build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
   r = build_stmt (loc, LABEL_EXPR, continue_label);
   add_stmt (r);
 
+  /* Should have been set earlier by the coro_initialized code.  */
+  gcc_assert (void_coro_handle_address);
+
   /* We want to force a tail-call even for O0/1, so this expands the resume
      call into its underlying implementation.  */
-  tree addr = lookup_member (void_coro_handle_type, coro_address_identifier,
-			       1, 0, tf_warning_or_error);
-  addr = build_new_method_call (continuation, addr, NULL, NULL_TREE,
-				  LOOKUP_NORMAL, NULL, tf_warning_or_error);
+  tree addr = build_new_method_call (continuation, void_coro_handle_address,
+				     NULL, NULL_TREE, LOOKUP_NORMAL, NULL,
+				     tf_warning_or_error);
   tree resume = build_call_expr_loc
     (loc, builtin_decl_explicit (BUILT_IN_CORO_RESUME), 1, addr);
 
diff --git a/gcc/testsuite/g++.dg/coroutines/pr103868.C b/gcc/testsuite/g++.dg/coroutines/pr103868.C
index 8687effb5637..fd05769db3db 100644
--- a/gcc/testsuite/g++.dg/coroutines/pr103868.C
+++ b/gcc/testsuite/g++.dg/coroutines/pr103868.C
@@ -79,6 +79,8 @@  namespace std {
 template <typename...> struct coroutine_traits;
 template <typename = void> struct coroutine_handle {
   operator coroutine_handle<>();
+  static coroutine_handle from_address(void*);
+  void* address();
 };
 struct suspend_always {
   bool await_ready();
diff --git a/gcc/testsuite/g++.dg/coroutines/pr105287.C b/gcc/testsuite/g++.dg/coroutines/pr105287.C
index 9790945287da..c54d1fd5b845 100644
--- a/gcc/testsuite/g++.dg/coroutines/pr105287.C
+++ b/gcc/testsuite/g++.dg/coroutines/pr105287.C
@@ -4,6 +4,8 @@  namespace std {
 template <typename _Result> struct coroutine_traits : _Result {};
 template <typename = void> struct coroutine_handle {
   operator coroutine_handle<>();
+  static coroutine_handle from_address(void*);
+  void* address();
 };
 }
 struct coro1 {
diff --git a/gcc/testsuite/g++.dg/coroutines/pr105301.C b/gcc/testsuite/g++.dg/coroutines/pr105301.C
index 33a0b03cf5d9..b3a3fe1365e3 100644
--- a/gcc/testsuite/g++.dg/coroutines/pr105301.C
+++ b/gcc/testsuite/g++.dg/coroutines/pr105301.C
@@ -29,7 +29,10 @@  struct suspend_always {
 
 namespace std {
 template <class PromiseType = void>
-struct coroutine_handle {};
+struct coroutine_handle {
+  static coroutine_handle from_address(void*);
+  void* address();
+};
 }
 
 struct bad_promise_6 {
diff --git a/gcc/testsuite/g++.dg/coroutines/pr105475-1.C b/gcc/testsuite/g++.dg/coroutines/pr105475-1.C
new file mode 100644
index 000000000000..0a5a9386b61b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/pr105475-1.C
@@ -0,0 +1,27 @@ 
+// https://gcc.gnu.org/PR105475
+// Test the case where we have everything we need for coroutine_handle<void>,
+// but not for coroutine_handle<promise>.
+namespace std {
+struct awaitable {
+  bool await_ready() noexcept { return false; }
+  void await_suspend(auto) noexcept {}
+  bool await_resume() noexcept { return true; }
+};
+
+template <typename T> struct coroutine_handle {
+  static coroutine_handle from_address(void *address);
+};
+
+template <typename T = void> struct coroutine_traits {
+  struct promise_type {
+    awaitable initial_suspend() { return {}; }
+    awaitable final_suspend() noexcept { return {}; }
+    void return_void() {}
+    T get_return_object() { return T(); }
+    void unhandled_exception() {}
+  };
+};
+} // namespace std
+
+void foo() { co_return; }
+// { dg-error "could not find 'address' in 'std::coroutine_handle<void>'" "" { target *-*-* } {.-1} }
diff --git a/gcc/testsuite/g++.dg/coroutines/pr105475-2.C b/gcc/testsuite/g++.dg/coroutines/pr105475-2.C
new file mode 100644
index 000000000000..dda6e1f26d6f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/pr105475-2.C
@@ -0,0 +1,29 @@ 
+// { dg-do compile }
+// https://gcc.gnu.org/PR105475
+// Test the case where we lack 'from_address' (i.e. the member required from
+// coroutine_handle<void>), but not address()
+namespace std {
+
+struct awaitable {
+  bool await_ready() noexcept { return false; }
+  void await_suspend(auto) noexcept {}
+  bool await_resume() noexcept { return true; }
+};
+
+template <typename T> struct coroutine_handle {
+  void* address();
+};
+
+template <typename T = void> struct coroutine_traits {
+  struct promise_type {
+    awaitable initial_suspend() { return {}; }
+    awaitable final_suspend() noexcept { return {}; }
+    void return_void() {}
+    T get_return_object() { return T(); }
+    void unhandled_exception() {}
+  };
+};
+} // namespace std
+
+void foo() { co_return; }
+// { dg-error "could not find 'from_address' in 'std::coroutine_handle<std::coroutine_traits<void>::promise_type>'" "" { target *-*-* } {.-1} }
diff --git a/gcc/testsuite/g++.dg/coroutines/pr105475-3.C b/gcc/testsuite/g++.dg/coroutines/pr105475-3.C
new file mode 100644
index 000000000000..75e0624ad719
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/pr105475-3.C
@@ -0,0 +1,29 @@ 
+// https://gcc.gnu.org/PR105475
+// Test the case where we create a non-static from_address.
+#include <coroutine>
+
+struct promise;
+
+struct task
+{ using promise_type = promise; };
+
+struct promise
+{
+  void return_void () {}
+  std::suspend_never final_suspend() noexcept { return {}; }
+  std::suspend_never initial_suspend() noexcept { return {}; }
+  void unhandled_exception () {}
+  task get_return_object() { return {}; }
+};
+
+/* Invalid.  */
+namespace std
+{
+  template<>
+  struct coroutine_handle<promise>
+  { coroutine_handle from_address(void*); };
+};
+
+task foo()
+{ co_return; }
+// { dg-error "std::__n4861::coroutine_handle<promise>::from_address' must be a non-overloaded static function" "" { target *-*-* } {.-1} }
diff --git a/gcc/testsuite/g++.dg/coroutines/pr105475-4.c b/gcc/testsuite/g++.dg/coroutines/pr105475-4.c
new file mode 100644
index 000000000000..97f99af3d7ff
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/pr105475-4.c
@@ -0,0 +1,33 @@ 
+// https://gcc.gnu.org/PR105475
+// Test the case where we create a static address.
+#include <coroutine>
+
+struct promise;
+
+struct task
+{ using promise_type = promise; };
+
+struct promise
+{
+  void return_void () {}
+  std::suspend_never final_suspend() noexcept { return {}; }
+  std::suspend_never initial_suspend() noexcept { return {}; }
+  void unhandled_exception () {}
+  task get_return_object() { return {}; }
+};
+
+/* Invalid.  */
+namespace std
+{
+  template<>
+  struct coroutine_handle<promise>
+  {
+    static coroutine_handle from_address(void*);
+
+    static void* address();
+  };
+};
+
+task foo()
+{ co_return; }
+// { dg-error "std::__n4861::coroutine_handle<void>::address' must be a non-overloaded member function" "" { target *-*-* } {.-1} }
diff --git a/gcc/testsuite/g++.dg/coroutines/pr105475-broken-spec-2.C b/gcc/testsuite/g++.dg/coroutines/pr105475-broken-spec-2.C
new file mode 100644
index 000000000000..97929a03228c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/pr105475-broken-spec-2.C
@@ -0,0 +1,33 @@ 
+// https://gcc.gnu.org/PR105475
+// Test the case where we specialize coroutine_handle<void> and remove
+// 'address'.
+namespace std {
+
+struct awaitable {
+  bool await_ready() noexcept { return false; }
+  void await_suspend(auto) noexcept {}
+  bool await_resume() noexcept { return true; }
+};
+
+template <typename T> struct coroutine_handle {
+  static coroutine_handle from_address(void *address);
+  void* address();
+};
+
+template <> struct coroutine_handle<void> {
+  static coroutine_handle from_address(void *address);
+};
+
+template <typename T = void> struct coroutine_traits {
+  struct promise_type {
+    awaitable initial_suspend() { return {}; }
+    awaitable final_suspend() noexcept { return {}; }
+    void return_void() {}
+    T get_return_object() { return T(); }
+    void unhandled_exception() {}
+  };
+};
+} // namespace std
+
+void foo() { co_return; }
+// { dg-error "could not find 'address' in 'std::coroutine_handle<void>'" "" { target *-*-* } {.-1} }
diff --git a/gcc/testsuite/g++.dg/coroutines/pr105475-broken-spec.C b/gcc/testsuite/g++.dg/coroutines/pr105475-broken-spec.C
new file mode 100644
index 000000000000..31899412b1b3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/pr105475-broken-spec.C
@@ -0,0 +1,29 @@ 
+// https://gcc.gnu.org/PR105475
+// Test the case where we specialize coroutine_handle and break it.
+#include <coroutine>
+
+struct promise;
+
+struct task
+{ using promise_type = promise; };
+
+struct promise
+{
+  void return_void () {}
+  std::suspend_never final_suspend() noexcept { return {}; }
+  std::suspend_never initial_suspend() noexcept { return {}; }
+  void unhandled_exception () {}
+  task get_return_object() { return {}; }
+};
+
+/* Invalid.  */
+namespace std
+{
+  template<>
+  struct coroutine_handle<promise>
+  {};
+};
+
+task foo()
+{ co_return; }
+// { dg-error "could not find 'from_address' in 'std::__n4861::coroutine_handle<promise>'" "" { target *-*-* } {.-1} }
diff --git a/gcc/testsuite/g++.dg/coroutines/pr105475.C b/gcc/testsuite/g++.dg/coroutines/pr105475.C
new file mode 100644
index 000000000000..50768768e01b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/pr105475.C
@@ -0,0 +1,28 @@ 
+// https://gcc.gnu.org/PR105475
+namespace std {
+
+struct handle {};
+
+struct awaitable {
+  bool await_ready() noexcept { return false; }
+  void await_suspend(handle) noexcept {}
+  bool await_resume() noexcept { return true; }
+};
+
+template <typename T> struct coroutine_handle {
+  static handle from_address(void *address) noexcept { return {}; }
+};
+
+template <typename T = void> struct coroutine_traits {
+  struct promise_type {
+    awaitable initial_suspend() { return {}; }
+    awaitable final_suspend() noexcept { return {}; }
+    void return_void() {}
+    T get_return_object() { return T(); }
+    void unhandled_exception() {}
+  };
+};
+} // namespace std
+
+void foo() { co_return; }
+// { dg-error "could not find 'address' in 'std::coroutine_handle<void>'" "" { target *-*-* } {.-1} }
diff --git a/gcc/testsuite/g++.dg/coroutines/pr94528.C b/gcc/testsuite/g++.dg/coroutines/pr94528.C
index 80e7273f1789..f5adba5749a4 100644
--- a/gcc/testsuite/g++.dg/coroutines/pr94528.C
+++ b/gcc/testsuite/g++.dg/coroutines/pr94528.C
@@ -5,8 +5,15 @@  template <typename _Result, typename> struct coroutine_traits {
   using promise_type = _Result::promise_type;
 };
 template <typename = void> struct coroutine_handle;
-template <> struct coroutine_handle<> { public: };
-template <typename> struct coroutine_handle : coroutine_handle<> {};
+template <> struct coroutine_handle<> {
+public:
+  static coroutine_handle from_address(void*);
+  void* address();
+};
+template <typename> struct coroutine_handle : coroutine_handle<> {
+  static coroutine_handle from_address(void*);
+  void* address();
+};
 struct suspend_always {
   bool await_ready();
   void await_suspend(coroutine_handle<>);
diff --git a/gcc/testsuite/g++.dg/coroutines/pr94879-folly-1.C b/gcc/testsuite/g++.dg/coroutines/pr94879-folly-1.C
index 11bcce04b7dc..6e091526fe77 100644
--- a/gcc/testsuite/g++.dg/coroutines/pr94879-folly-1.C
+++ b/gcc/testsuite/g++.dg/coroutines/pr94879-folly-1.C
@@ -5,8 +5,14 @@  template <typename a> a b(a &&);
 template <typename c> struct d { c e; };
 template <typename f, typename> struct coroutine_traits : f {};
 template <typename = void> struct coroutine_handle;
-template <> struct coroutine_handle<> {};
-template <typename> struct coroutine_handle : coroutine_handle<> {};
+template <> struct coroutine_handle<> {
+  static coroutine_handle from_address(void*);
+  void* address();
+};
+template <typename> struct coroutine_handle : coroutine_handle<> {
+  static coroutine_handle from_address(void*);
+  void* address();
+};
 struct g {};
 } // namespace std
 
diff --git a/gcc/testsuite/g++.dg/coroutines/pr94883-folly-2.C b/gcc/testsuite/g++.dg/coroutines/pr94883-folly-2.C
index ce06cfddb0ad..98c5a7e3eeef 100644
--- a/gcc/testsuite/g++.dg/coroutines/pr94883-folly-2.C
+++ b/gcc/testsuite/g++.dg/coroutines/pr94883-folly-2.C
@@ -2,8 +2,14 @@ 
 namespace std {
 template <typename a, typename...> struct coroutine_traits : a {};
 template <typename = void> struct coroutine_handle;
-template <> struct coroutine_handle<> {};
-template <typename> struct coroutine_handle : coroutine_handle<> {};
+template <> struct coroutine_handle<> {
+  static coroutine_handle from_address(void*);
+  void* address();
+};
+template <typename> struct coroutine_handle : coroutine_handle<> {
+  static coroutine_handle from_address(void*);
+  void* address();
+};
 struct b {
   bool await_ready();
   void await_suspend(coroutine_handle<>);
diff --git a/gcc/testsuite/g++.dg/coroutines/pr98118.C b/gcc/testsuite/g++.dg/coroutines/pr98118.C
index d09ffff21426..9a37dc3596a9 100644
--- a/gcc/testsuite/g++.dg/coroutines/pr98118.C
+++ b/gcc/testsuite/g++.dg/coroutines/pr98118.C
@@ -2,8 +2,14 @@  namespace std {
 inline namespace __n4861 {
 template <typename _Result, typename> struct coroutine_traits : _Result {};
 template <typename = void> struct coroutine_handle;
-template <> struct coroutine_handle<> {};
-template <typename> struct coroutine_handle : coroutine_handle<> {};
+template <> struct coroutine_handle<> {
+  static coroutine_handle from_address(void*);
+  void* address();
+};
+template <typename> struct coroutine_handle : coroutine_handle<> {
+  static coroutine_handle from_address(void*);
+  void* address();
+};
 struct suspend_never {
   bool await_ready() noexcept;
   void await_suspend(coroutine_handle<>) noexcept;