[gcc r15-3019] gnat: fix lto-type-mismatch between C_Version_String and gnat_version_string [PR115917]

2024-08-19 Thread Arsen Arsenovic via Gcc-cvs
https://gcc.gnu.org/g:9cbcf8d1de159e6113fafb5dc2feb4a7e467a302

commit r15-3019-g9cbcf8d1de159e6113fafb5dc2feb4a7e467a302
Author: Arsen Arsenović 
Date:   Thu Aug 15 19:17:41 2024 +0200

gnat: fix lto-type-mismatch between C_Version_String and 
gnat_version_string [PR115917]

gcc/ada/ChangeLog:

PR ada/115917
* gnatvsn.ads: Add note about the duplication of this value in
version.c.
* version.c (VER_LEN_MAX): Define to the same value as
Gnatvsn.Ver_Len_Max.
(gnat_version_string): Use VER_LEN_MAX as bound.

Diff:
---
 gcc/ada/gnatvsn.ads | 3 ++-
 gcc/ada/version.c   | 5 -
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/gcc/ada/gnatvsn.ads b/gcc/ada/gnatvsn.ads
index 6cf170dc4ca..ca7744b9767 100644
--- a/gcc/ada/gnatvsn.ads
+++ b/gcc/ada/gnatvsn.ads
@@ -83,7 +83,8 @@ package Gnatvsn is
--  space to store any possible version string value for checks. This
--  value should never be decreased in the future, but it would be
--  OK to increase it if absolutely necessary. If it is increased,
-   --  be sure to increase GNAT.Compiler.Version.Ver_Len_Max as well.
+   --  be sure to increase GNAT.Compiler.Version.Ver_Len_Max, and to update
+   --  the VER_LEN_MAX define in version.c as well.
 
Ver_Prefix : constant String := "GNAT Version: ";
--  Prefix generated by binder. If it is changed, be sure to change
diff --git a/gcc/ada/version.c b/gcc/ada/version.c
index 5e64edd0b17..2fa9b8c2c85 100644
--- a/gcc/ada/version.c
+++ b/gcc/ada/version.c
@@ -31,4 +31,7 @@
 
 #include "version.h"
 
-char gnat_version_string[] = version_string;
+/* Logically a reference to Gnatvsn.Ver_Len_Max.  Please keep in sync.  */
+#define VER_LEN_MAX 256
+
+char gnat_version_string[VER_LEN_MAX] = version_string;


[gcc r15-3202] coroutines: diagnose usage of alloca in coroutines

2024-08-26 Thread Arsen Arsenovic via Gcc-cvs
https://gcc.gnu.org/g:c73d7f3c66c0b5865edd6880cd0d6be723cfbb8d

commit r15-3202-gc73d7f3c66c0b5865edd6880cd0d6be723cfbb8d
Author: Arsen Arsenović 
Date:   Fri Aug 2 13:17:40 2024 +0200

coroutines: diagnose usage of alloca in coroutines

We do not support it currently, and the resulting memory can only be
used inside a single resumption, so best not confuse the user with it.

PR c++/115858 - Incompatibility of coroutines and alloca()

gcc/ChangeLog:

* coroutine-passes.cc (execute_early_expand_coro_ifns): Emit a
sorry if a statement is an alloca call.

gcc/testsuite/ChangeLog:

* g++.dg/coroutines/pr115858.C: New test.

Diff:
---
 gcc/coroutine-passes.cc| 10 ++
 gcc/testsuite/g++.dg/coroutines/pr115858.C | 23 +++
 2 files changed, 33 insertions(+)

diff --git a/gcc/coroutine-passes.cc b/gcc/coroutine-passes.cc
index c0d6eca7c070..9124ecae5916 100644
--- a/gcc/coroutine-passes.cc
+++ b/gcc/coroutine-passes.cc
@@ -29,6 +29,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple.h"
 #include "tree-pass.h"
 #include "ssa.h"
+#include "calls.h"
 #include "cgraph.h"
 #include "pretty-print.h"
 #include "diagnostic-core.h"
@@ -306,6 +307,15 @@ execute_early_expand_coro_ifns (void)
   {
gimple *stmt = gsi_stmt (gsi);
 
+   /* Tell the user about 'alloca', we don't support it yet.  */
+   if (gimple_alloca_call_p (stmt))
+ {
+   sorry_at (gimple_location (stmt),
+ "% is not yet supported in coroutines");
+   gsi_next (&gsi);
+   continue;
+ }
+
if (!is_gimple_call (stmt) || !gimple_call_internal_p (stmt))
  {
gsi_next (&gsi);
diff --git a/gcc/testsuite/g++.dg/coroutines/pr115858.C 
b/gcc/testsuite/g++.dg/coroutines/pr115858.C
new file mode 100644
index ..3dfe820dbdfd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/pr115858.C
@@ -0,0 +1,23 @@
+#include 
+
+struct task
+{
+  struct promise_type
+  {
+void return_void () {}
+task get_return_object () { return {}; }
+void unhandled_exception () {}
+std::suspend_never initial_suspend () { return {}; }
+std::suspend_never final_suspend () noexcept { return {}; }
+  };
+};
+
+task
+f ()
+{
+  void* a = __builtin_alloca (10);
+  // { dg-message "sorry, unimplemented: 'alloca' is not yet supported in 
coroutines" "" { target *-*-* } {.-1} }
+  void* b = __builtin_alloca_with_align (10, 16);
+  // { dg-message "sorry, unimplemented: 'alloca' is not yet supported in 
coroutines" "" { target *-*-* } {.-1} }
+  co_return;
+}


[gcc r15-3203] c++/coros: do not assume coros don't nest [PR113457]

2024-08-26 Thread Arsen Arsenovic via Gcc-cvs
https://gcc.gnu.org/g:5cca7517c5868b7b9aa13992145eb6082ac5d5b9

commit r15-3203-g5cca7517c5868b7b9aa13992145eb6082ac5d5b9
Author: Arsen Arsenović 
Date:   Fri Aug 23 20:19:05 2024 +0200

c++/coros: do not assume coros don't nest [PR113457]

In the testcase presented in the PR, during template expansion, an
tsubst of an operand causes a lambda coroutine to be processed, causing
it to get an initial suspend and final suspend.  The code for assigning
awaitable var names (get_awaitable_var) assumed that the sequence Is ->
Is -> Fs -> Fs is impossible (i.e. that one could only 'open' one
coroutine before closing it at a time), and reset the counter used for
unique numbering each time a final suspend occured.  This assumption is
false in a few cases, usually when lambdas are involved.

Instead of storing this counter in a static-storage variable, we can
store it in coroutine_info.  This struct is local to each function, so
we don't need to worry about "cross-contamination" nor resetting.

PR c++/113457

gcc/cp/ChangeLog:

* coroutines.cc (struct coroutine_info): Add integer field
awaitable_number.  This is a counter used for assigning unique
names to awaitable temporaries.
(get_awaitable_var): Use awaitable_number from coroutine_info
instead of the static int awn.

gcc/testsuite/ChangeLog:

* g++.dg/coroutines/pr113457-1.C: New test.
* g++.dg/coroutines/pr113457.C: New test.

Diff:
---
 gcc/cp/coroutines.cc |  19 ++-
 gcc/testsuite/g++.dg/coroutines/pr113457-1.C |  25 
 gcc/testsuite/g++.dg/coroutines/pr113457.C   | 178 +++
 3 files changed, 216 insertions(+), 6 deletions(-)

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index c3e08221cc91..5bfd7943fb81 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -96,6 +96,10 @@ struct GTY((for_user)) coroutine_info
   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
function into a coroutine.  */
+
+  /* Temporary variable number assigned by get_awaitable_var.  */
+  int awaitable_number = 0;
+
   /* Flags to avoid repeated errors for per-function issues.  */
   bool coro_ret_type_error_emitted;
   bool coro_promise_error_emitted;
@@ -995,15 +999,18 @@ enum suspend_point_kind {
 static tree
 get_awaitable_var (suspend_point_kind suspend_kind, tree v_type)
 {
-  static int awn = 0;
+  auto cinfo = get_coroutine_info (current_function_decl);
+  gcc_checking_assert (cinfo);
   char *buf;
   switch (suspend_kind)
 {
-  default: buf = xasprintf ("Aw%d", awn++); break;
-  case CO_YIELD_SUSPEND_POINT: buf =  xasprintf ("Yd%d", awn++); break;
-  case INITIAL_SUSPEND_POINT: buf =  xasprintf ("Is"); break;
-  case FINAL_SUSPEND_POINT: buf =  xasprintf ("Fs"); awn = 0; break;
-  }
+default: buf = xasprintf ("Aw%d", cinfo->awaitable_number++); break;
+case CO_YIELD_SUSPEND_POINT:
+  buf = xasprintf ("Yd%d", cinfo->awaitable_number++);
+  break;
+case INITIAL_SUSPEND_POINT: buf = xasprintf ("Is"); break;
+case FINAL_SUSPEND_POINT: buf = xasprintf ("Fs"); break;
+}
   tree ret = get_identifier (buf);
   free (buf);
   ret = build_lang_decl (VAR_DECL, ret, v_type);
diff --git a/gcc/testsuite/g++.dg/coroutines/pr113457-1.C 
b/gcc/testsuite/g++.dg/coroutines/pr113457-1.C
new file mode 100644
index ..fcf67e15271c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/pr113457-1.C
@@ -0,0 +1,25 @@
+// https://gcc.gnu.org/PR113457
+#include 
+
+struct coro
+{
+  struct promise_type
+  {
+std::suspend_never initial_suspend ();
+std::suspend_never final_suspend () noexcept;
+void return_void ();
+void unhandled_exception ();
+coro get_return_object ();
+  };
+};
+
+struct not_quite_suspend_never : std::suspend_never
+{};
+
+coro
+foo ()
+{
+  co_await std::suspend_never{},
+[] () -> coro { co_return; },
+co_await not_quite_suspend_never{};
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/pr113457.C 
b/gcc/testsuite/g++.dg/coroutines/pr113457.C
new file mode 100644
index ..add45a2f8921
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/pr113457.C
@@ -0,0 +1,178 @@
+// https://gcc.gnu.org/PR113457
+namespace std {
+template  _Up __declval(int);
+template  auto declval() noexcept -> decltype(__declval<_Tp>(0));
+template  struct remove_cv {
+  using type = __remove_cv(_Tp);
+};
+template  using remove_cv_t = typename remove_cv<_Tp>::type;
+template  struct remove_reference {
+  using type = __remove_reference(_Tp);
+};
+template 
+using remove_reference_t = typename remove_reference<_Tp>::type;
+template  inline constexpr bool is_array_v = __is_array(_Tp);
+template  struct remove_cvref {};
+namespace ranges 

[gcc r15-3234] c++/coroutines: fix actor cases not being added to the current switch [PR109867]

2024-08-27 Thread Arsen Arsenovic via Gcc-cvs
https://gcc.gnu.org/g:ff0cba200af72f2514ebc987a99027f314d4cc99

commit r15-3234-gff0cba200af72f2514ebc987a99027f314d4cc99
Author: Arsen Arsenović 
Date:   Wed Jul 24 20:43:01 2024 +0200

c++/coroutines: fix actor cases not being added to the current switch 
[PR109867]

Previously, we were building and inserting case_labels manually, which
led to them not being added into the currently running switch via
c_add_case_label.  This led to false diagnostics that the user could not
act on.

PR c++/109867

gcc/cp/ChangeLog:

* coroutines.cc (expand_one_await_expression): Replace uses of
build_case_label with finish_case_label.
(build_actor_fn): Ditto.
(create_anon_label_with_ctx): Remove now-unused function.

gcc/testsuite/ChangeLog:

* g++.dg/coroutines/torture/pr109867.C: New test.

Reviewed-by: Iain Sandoe 

Diff:
---
 gcc/cp/coroutines.cc   | 52 +-
 gcc/testsuite/g++.dg/coroutines/torture/pr109867.C | 23 ++
 2 files changed, 34 insertions(+), 41 deletions(-)

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index 31dc39afeee2..f243fe9adae2 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -1708,20 +1708,6 @@ coro_build_artificial_var (location_t loc, const char 
*name, tree type,
type, ctx, init);
 }
 
-/* Helpers for label creation:
-   1. Create a named label in the specified context.  */
-
-static tree
-create_anon_label_with_ctx (location_t loc, tree ctx)
-{
-  tree lab = build_decl (loc, LABEL_DECL, NULL_TREE, void_type_node);
-
-  DECL_CONTEXT (lab) = ctx;
-  DECL_ARTIFICIAL (lab) = true;
-  DECL_IGNORED_P (lab) = true;
-  TREE_USED (lab) = true;
-  return lab;
-}
 
 /*  2. Create a named label in the specified context.  */
 
@@ -1935,22 +1921,16 @@ expand_one_await_expression (tree *stmt, tree 
*await_expr, void *d)
data->coro_fp);
   r = cp_build_init_expr (cond, r);
   finish_switch_cond (r, sw);
-  r = build_case_label (integer_zero_node, NULL_TREE,
-   create_anon_label_with_ctx (loc, actor));
-  add_stmt (r); /* case 0: */
+  finish_case_label (loc, integer_zero_node, NULL_TREE); /*  case 0: */
   /* Implement the suspend, a scope exit without clean ups.  */
   r = build_call_expr_internal_loc (loc, IFN_CO_SUSPN, void_type_node, 1,
is_cont ? cont : susp);
   r = coro_build_cvt_void_expr_stmt (r, loc);
   add_stmt (r); /*   goto ret;  */
-  r = build_case_label (integer_one_node, NULL_TREE,
-   create_anon_label_with_ctx (loc, actor));
-  add_stmt (r); /* case 1:  */
+  finish_case_label (loc, integer_one_node, NULL_TREE); /*  case 1:  */
   r = build1_loc (loc, GOTO_EXPR, void_type_node, resume_label);
   add_stmt (r); /*  goto resume;  */
-  r = build_case_label (NULL_TREE, NULL_TREE,
-   create_anon_label_with_ctx (loc, actor));
-  add_stmt (r); /* default:;  */
+  finish_case_label (loc, NULL_TREE, NULL_TREE); /* default:;  */
   r = build1_loc (loc, GOTO_EXPR, void_type_node, destroy_label);
   add_stmt (r); /* goto destroy;  */
 
@@ -2291,9 +2271,7 @@ build_actor_fn (location_t loc, tree coro_frame_type, 
tree actor, tree fnbody,
 
   tree destroy_dispatcher = begin_switch_stmt ();
   finish_switch_cond (rat, destroy_dispatcher);
-  tree ddeflab = build_case_label (NULL_TREE, NULL_TREE,
-  create_anon_label_with_ctx (loc, actor));
-  add_stmt (ddeflab);
+  tree ddeflab = finish_case_label (loc, NULL_TREE, NULL_TREE);
   tree b = build_call_expr_loc (loc, builtin_decl_explicit (BUILT_IN_TRAP), 0);
   b = coro_build_cvt_void_expr_stmt (b, loc);
   add_stmt (b);
@@ -2304,18 +2282,15 @@ build_actor_fn (location_t loc, tree coro_frame_type, 
tree actor, tree fnbody,
  frame itself.  */
   tree del_promise_label
 = create_named_label_with_ctx (loc, "coro.delete.promise", actor);
-  b = build_case_label (build_int_cst (short_unsigned_type_node, 1), NULL_TREE,
-   create_anon_label_with_ctx (loc, actor));
-  add_stmt (b);
+  finish_case_label (loc, build_int_cst (short_unsigned_type_node, 1),
+NULL_TREE);
   add_stmt (build_stmt (loc, GOTO_EXPR, del_promise_label));
 
   short unsigned lab_num = 3;
   for (unsigned destr_pt = 0; destr_pt < body_count; destr_pt++)
 {
   tree l_num = build_int_cst (short_unsigned_type_node, lab_num);
-  b = build_case_label (l_num, NULL_TREE,
-   create_anon_label_with_ctx (loc, actor));
-  add_stmt (b);
+  finish_case_label (loc, l_num, NULL_TREE);
   b = build_call_expr_internal_loc (loc, IFN_CO_ACTOR, void_type_node, 1,
l_num);
   b = coro_build_cvt_void_expr_stmt (b, loc);
@@ -2333,15 +2308,12 @@ build_actor_fn (location_t loc

[gcc r15-3285] c++: don't remove labels during coro-early-expand-ifns [PR105104]

2024-08-29 Thread Arsen Arsenovic via Gcc-cvs
https://gcc.gnu.org/g:d9c54e9a036189e8961ec17e118fccf794d7bfab

commit r15-3285-gd9c54e9a036189e8961ec17e118fccf794d7bfab
Author: Arsen Arsenović 
Date:   Fri Aug 16 19:07:01 2024 +0200

c++: don't remove labels during coro-early-expand-ifns [PR105104]

In some scenarios, it is possible for the CFG cleanup to cause one of
the labels mentioned in CO_YIELD, which coro-early-expand-ifns intends
to remove, to become part of some statement.  As a result, when that
label is removed, the statement it became part of becomes invalid,
crashing the compiler.

There doesn't appear to be a reason to remove the labels (anymore, at
least), so let's not do that.

PR c++/105104

gcc/ChangeLog:

* coroutine-passes.cc (execute_early_expand_coro_ifns): Don't
remove any labels.

gcc/testsuite/ChangeLog:

* g++.dg/coroutines/torture/pr105104.C: New test.

Diff:
---
 gcc/coroutine-passes.cc| 26 --
 gcc/testsuite/g++.dg/coroutines/torture/pr105104.C | 40 ++
 2 files changed, 40 insertions(+), 26 deletions(-)

diff --git a/gcc/coroutine-passes.cc b/gcc/coroutine-passes.cc
index 9124ecae5916..0f8e24f8d551 100644
--- a/gcc/coroutine-passes.cc
+++ b/gcc/coroutine-passes.cc
@@ -294,9 +294,6 @@ execute_early_expand_coro_ifns (void)
   /* Some of the possible YIELD points will hopefully have been removed by
  earlier optimisations; record the ones that are still present.  */
   hash_map, tree> destinations;
-  /* Labels we added to carry the CFG changes, we need to remove these to
- avoid confusing EH.  */
-  hash_set to_remove;
   /* List of dispatch points to update.  */
   auto_vec actor_worklist;
   basic_block bb;
@@ -384,8 +381,6 @@ execute_early_expand_coro_ifns (void)
}
  else
dst_dest = dst_tgt;
- to_remove.add (res_tgt);
- to_remove.add (dst_tgt);
  /* lose the co_yield.  */
  gsi_remove (&gsi, true);
  stmt = gsi_stmt (gsi); /* next. */
@@ -473,27 +468,6 @@ execute_early_expand_coro_ifns (void)
}
 }
 
-  /* Remove the labels we inserted to map our hidden CFG, this
- avoids confusing block merges when there are also EH labels.  */
-  FOR_EACH_BB_FN (bb, cfun)
-for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
-  {
-   gimple *stmt = gsi_stmt (gsi);
-   if (glabel *glab = dyn_cast (stmt))
- {
-   tree rem = gimple_label_label (glab);
-   if (to_remove.contains (rem))
- {
-   gsi_remove (&gsi, true);
-   to_remove.remove (rem);
-   continue; /* We already moved to the next insn.  */
- }
- }
-   else
- break;
-   gsi_next (&gsi);
-  }
-
   /* Changed the CFG.  */
   todoflags |= TODO_cleanup_cfg;
   return todoflags;
diff --git a/gcc/testsuite/g++.dg/coroutines/torture/pr105104.C 
b/gcc/testsuite/g++.dg/coroutines/torture/pr105104.C
new file mode 100644
index ..fcc783e3066d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/torture/pr105104.C
@@ -0,0 +1,40 @@
+// https://gcc.gnu.org/PR105104
+// { dg-additional-options "-O" }
+
+#include 
+
+// ICE during GIMPLE pass: coro-early-expand-ifs. final_awaiter::await_resume 
is
+// non-void, and optimizations are enabled.
+
+struct return_object
+{
+  struct promise_type
+  {
+static constexpr std::suspend_always initial_suspend () noexcept
+{
+  return {};
+}
+
+struct final_awaiter
+{
+  static constexpr bool await_ready () noexcept { return false; }
+  static constexpr void await_suspend (std::coroutine_handle<>) noexcept {}
+  static constexpr int await_resume () noexcept { return {}; }
+};
+static constexpr final_awaiter final_suspend () noexcept { return {}; }
+
+static void unhandled_exception () { throw; }
+
+return_object get_return_object () { return {}; }
+
+static constexpr void return_void () noexcept {}
+  };
+};
+
+return_object
+coroutine ()
+{
+  co_return;
+}
+
+return_object f = coroutine ();


[gcc r15-3453] coros: mark .CO_YIELD as LEAF [PR106973]

2024-09-04 Thread Arsen Arsenovic via Gcc-cvs
https://gcc.gnu.org/g:7b7ad3f4b2455072f42e7884b93fd96ebb920bc8

commit r15-3453-g7b7ad3f4b2455072f42e7884b93fd96ebb920bc8
Author: Arsen Arsenović 
Date:   Tue Sep 3 17:14:13 2024 +0200

coros: mark .CO_YIELD as LEAF [PR106973]

We rely on .CO_YIELD calls being followed by an assignment (optionally)
and then a switch/if in the same basic block.  This implies that a
.CO_YIELD can never end a block.  However, since a call to .CO_YIELD is
still a call, if the function containing it calls setjmp, GCC thinks
that the .CO_YIELD can introduce abnormal control flow, and generates an
edge for the call.

We know this is not the case; .CO_YIELD calls get removed quite early on
and have no effect, and result in no other calls, so .CO_YIELD can be
considered a leaf function, preventing generating an edge when calling
it.

PR c++/106973 - coroutine generator and setjmp

PR c++/106973

gcc/ChangeLog:

* internal-fn.def (CO_YIELD): Mark as ECF_LEAF.

gcc/testsuite/ChangeLog:

* g++.dg/coroutines/pr106973.C: New test.

Diff:
---
 gcc/internal-fn.def|  2 +-
 gcc/testsuite/g++.dg/coroutines/pr106973.C | 22 ++
 2 files changed, 23 insertions(+), 1 deletion(-)

diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index 75b527b1ab0..23b4ab02b30 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -569,7 +569,7 @@ DEF_INTERNAL_FN (DIVMOD, ECF_CONST | ECF_LEAF, NULL)
 
 /* For coroutines.  */
 DEF_INTERNAL_FN (CO_ACTOR, ECF_NOTHROW | ECF_LEAF, NULL)
-DEF_INTERNAL_FN (CO_YIELD, ECF_NOTHROW, NULL)
+DEF_INTERNAL_FN (CO_YIELD, ECF_NOTHROW | ECF_LEAF, NULL)
 DEF_INTERNAL_FN (CO_SUSPN, ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (CO_FRAME, ECF_PURE | ECF_NOTHROW | ECF_LEAF, NULL)
 
diff --git a/gcc/testsuite/g++.dg/coroutines/pr106973.C 
b/gcc/testsuite/g++.dg/coroutines/pr106973.C
new file mode 100644
index 000..6db6cbc7711
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/pr106973.C
@@ -0,0 +1,22 @@
+// https://gcc.gnu.org/PR106973
+// { dg-require-effective-target indirect_jumps }
+#include 
+#include 
+
+struct generator;
+struct generator_promise {
+  generator get_return_object();
+  std::suspend_always initial_suspend();
+  std::suspend_always final_suspend() noexcept;
+  std::suspend_always yield_value(int);
+  void unhandled_exception();
+};
+
+struct generator {
+  using promise_type = generator_promise;
+};
+jmp_buf foo_env;
+generator foo() {
+  setjmp(foo_env);
+  co_yield 1;
+}


[gcc r15-3454] c++: add a testcase for [PR 108620]

2024-09-04 Thread Arsen Arsenovic via Gcc-cvs
https://gcc.gnu.org/g:858918ef4233c837ab85819ad159bf452df3a7fb

commit r15-3454-g858918ef4233c837ab85819ad159bf452df3a7fb
Author: Arsen Arsenović 
Date:   Tue Sep 3 20:58:55 2024 +0200

c++: add a testcase for [PR 108620]

Fixed by r15-2540-g32e678b2ed7521.  Add a testcase, as the original ones
do not cover this particular failure mode.

gcc/testsuite/ChangeLog:

PR c++/108620
* g++.dg/coroutines/pr108620.C: New test.

Diff:
---
 gcc/testsuite/g++.dg/coroutines/pr108620.C | 95 ++
 1 file changed, 95 insertions(+)

diff --git a/gcc/testsuite/g++.dg/coroutines/pr108620.C 
b/gcc/testsuite/g++.dg/coroutines/pr108620.C
new file mode 100644
index 000..e8016b9f8a2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/pr108620.C
@@ -0,0 +1,95 @@
+// https://gcc.gnu.org/PR108620
+#include 
+#include 
+#include 
+
+template
+struct task;
+
+template 
+struct task_private_data {
+  inline task_private_data() noexcept : data_(nullptr) {}
+  inline task_private_data(PrivateDataType* input) noexcept : data_(input) {}
+  inline task_private_data(task_private_data&& other) noexcept = default;
+  inline task_private_data& operator=(task_private_data&&) noexcept = default;
+  inline task_private_data(const task_private_data&) = delete;
+  inline task_private_data& operator=(const task_private_data&) = delete;
+  inline ~task_private_data() {}
+
+  inline bool await_ready() const noexcept { return true; }
+  inline PrivateDataType* await_resume() const noexcept { return data_; }
+  inline void await_suspend(std::coroutine_handle<>) noexcept {}
+
+  PrivateDataType* data_;
+};
+
+template
+struct task_context {
+PrivateDataType data_;
+};
+
+template
+struct task {
+using self_type = task;
+std::shared_ptr> context_;
+
+task(const std::shared_ptr>& input): 
context_(input) {}
+
+static auto yield_private_data() noexcept { return 
task_private_data{}; }
+
+struct promise_type {
+  std::shared_ptr> context_;
+
+  template
+  promise_type(Input&& input, Rest&&...) {
+context_ = std::make_shared>();
+context_->data_ = std::forward(input);
+  }
+
+  auto get_return_object() noexcept { return self_type{context_}; }
+  std::suspend_never initial_suspend() noexcept { return {}; }
+  std::suspend_never final_suspend() noexcept { return {}; }
+  void unhandled_exception() { throw; }
+
+  template
+  void return_value(ReturnType&&) {}
+
+  template 
+  inline task_private_data yield_value(
+  task_private_data&& input) noexcept {
+input.data_ = &context_->data_;
+return task_private_data(input.data_);
+  }
+};
+};
+
+template
+task call1(TArg&& arg, OutputType& output) {
+OutputType* ptr = co_yield task::yield_private_data();
+output = *ptr;
+co_return 0;
+}
+
+
+struct container {
+std::string* ptr;
+};
+
+template
+task call2(TArg&& arg, container& output) {
+output.ptr = co_yield task::yield_private_data();
+co_return 0;
+}
+
+int main() {
+  // success
+  std::string output1;
+  call1(std::string("hello1"), output1);
+  std::cout<< "output1: "<< output1<< std::endl;
+
+  // crash
+  container output2;
+  auto task2 = call2(std::string("hello2"), output2);
+  std::cout<< "output2: "<< *output2.ptr<< std::endl;
+  return 0;
+}


[gcc r15-2457] dir-locals: apply our C settings in C++ also

2024-07-31 Thread Arsen Arsenovic via Gcc-cvs
https://gcc.gnu.org/g:ebf4f095568e8e4af042f3e5a8cb655d9172

commit r15-2457-gebf4f095568e8e4af042f3e5a8cb655d9172
Author: Arsen Arsenović 
Date:   Wed Jul 31 16:53:35 2024 +0200

dir-locals: apply our C settings in C++ also

This also works with Emacs 30 Tree-Sitter C and C++ modes, as they are
submodes.

ChangeLog:

* .dir-locals.el: Change c-mode to a list of C, C++ and ObjC
modes that Emacs currently provides.

Diff:
---
 .dir-locals.el | 6 +-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/.dir-locals.el b/.dir-locals.el
index fa031cbded99..2c12b3866633 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -18,6 +18,10 @@
  (tcl-continued-indent-level . 4)
  (indent-tabs-mode . t)))
  (nil . ((bug-reference-url-format . "https://gcc.gnu.org/PR%s";)))
+ ;; Please keep C and C++ in sync.
  (c-mode . ((c-file-style . "GNU")
(indent-tabs-mode . t)
-   (fill-column . 79
+   (fill-column . 79)))
+ (c++-mode . ((c-file-style . "GNU")
+ (indent-tabs-mode . t)
+ (fill-column . 79


[gcc r15-2540] c++/coroutines: only defer expanding co_{await, return, yield} if dependent [PR112341]

2024-08-01 Thread Arsen Arsenovic via Gcc-cvs
https://gcc.gnu.org/g:32e678b2ed752154b2f96719e33f11a7c6417f20

commit r15-2540-g32e678b2ed752154b2f96719e33f11a7c6417f20
Author: Arsen Arsenović 
Date:   Tue Jul 30 23:36:24 2024 +0200

c++/coroutines: only defer expanding co_{await,return,yield} if dependent 
[PR112341]

By doing so, we can get diagnostics in template decls when we know we
can.  For instance, in the following:

  awaitable g();
  template
  task f()
  {
co_await g();
co_yield 1;
co_return "foo";
  }

... the coroutine promise type in each statement is always
std::coroutine_handle::promise_type, and all of the operands are
not type-dependent, so we can always compute the resulting types (and
expected types) of these expressions and statements.

Also, when we do not know the type of the CO_AWAIT_EXPR or
CO_YIELD_EXPR, we now return NULL_TREE as the type rather than
unknown_type_node.  This is more correct, since the type is not unknown,
it just isn't determined yet.  This also means we can remove the
CO_AWAIT_EXPR and CO_YIELD_EXPR special-cases from
type_dependent_expression_p.

PR c++/112341 - error: insufficient contextual information to determine 
type on co_await result in function template

gcc/cp/ChangeLog:

PR c++/112341
* coroutines.cc (struct coroutine_info): Also cache the
traits type.
(ensure_coro_initialized): New function.  Makes sure we have
initialized the coroutine state successfully, or informs the
caller should it fail to do so.  Extracted from
coro_promise_type_found_p.
(coro_get_traits_class): New function.  Gets the (cached)
coroutine traits type for a given coroutine.  Extracted from
coro_promise_type_found_p and refactored to cache the result.
(coro_promise_type_found_p): Use the two functions above.
(build_template_co_await_expr): New function.  Builds a
CO_AWAIT_EXPR representing a CO_AWAIT_EXPR in a template
declaration.
(build_co_await): Use the above if processing_template_decl, and
give it a proper type.
(coro_dependent_p): New function.  Returns true iff its
argument is a type-dependent expression OR the current functions
traits class is type dependent.
(finish_co_await_expr): Defer expansion only in the case
coro_dependent_p returns true.
(finish_co_yield_expr): Ditto.
(finish_co_return_stmt): Ditto.
* pt.cc (type_dependent_expression_p): Do not treat
CO_AWAIT/CO_YIELD specially.

gcc/testsuite/ChangeLog:

PR c++/112341
* g++.dg/coroutines/pr112341-2.C: New test.
* g++.dg/coroutines/pr112341-3.C: New test.
* g++.dg/coroutines/torture/co-yield-03-tmpl-nondependent.C: New
test.
* g++.dg/coroutines/pr112341.C: New test.

Diff:
---
 gcc/cp/coroutines.cc   | 157 -
 gcc/cp/pt.cc   |   5 -
 gcc/testsuite/g++.dg/coroutines/pr112341-2.C   |  25 
 gcc/testsuite/g++.dg/coroutines/pr112341-3.C   |  65 +
 gcc/testsuite/g++.dg/coroutines/pr112341.C |  21 +++
 .../torture/co-yield-03-tmpl-nondependent.C| 140 ++
 6 files changed, 376 insertions(+), 37 deletions(-)

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index 127a1c06b56e..91bbe6b0a0eb 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -85,6 +85,7 @@ struct GTY((for_user)) coroutine_info
   tree actor_decl;/* The synthesized actor function.  */
   tree destroy_decl;  /* The synthesized destroy function.  */
   tree promise_type;  /* The cached promise type for this function.  */
+  tree traits_type;   /* The cached traits type for this function.  */
   tree handle_type;   /* The cached coroutine handle for this function.  */
   tree self_h_proxy;  /* A handle instance that is used as the proxy for the
 one that will eventually be allocated in the coroutine
@@ -429,11 +430,12 @@ find_promise_type (tree traits_class)
   return promise_type;
 }
 
+/* Perform initialization of the coroutine processor state, if not done
+   before.  */
+
 static bool
-coro_promise_type_found_p (tree fndecl, location_t loc)
+ensure_coro_initialized (location_t loc)
 {
-  gcc_assert (fndecl != NULL_TREE);
-
   if (!coro_initialized)
 {
   /* Trees we only need to create once.
@@ -466,6 +468,30 @@ coro_promise_type_found_p (tree fndecl, location_t loc)
 
   coro_initialized = true;
 }
+  return true;
+}
+
+/* Try to get the coroutine traits class.  */
+static tree
+coro_get_traits_class (tree fndecl, location_t loc)
+{
+  gcc_assert (fndecl != NULL_TREE);
+  gcc_assert (coro_initialize

[gcc r15-2702] c++/coroutines: check for members we use in handle_types [PR105475]

2024-08-02 Thread Arsen Arsenovic via Gcc-cvs
https://gcc.gnu.org/g:5b4476a165565cb20729c0a97a3f43b060595209

commit r15-2702-g5b4476a165565cb20729c0a97a3f43b060595209
Author: Arsen Arsenović 
Date:   Thu Jul 25 22:41:34 2024 +0200

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

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.
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::address() instance method.
(get_handle_type_address): New function.  Looks up and validates
handle_type::address in a given handle_type.
(get_handle_type_from_address): New function.  Looks up and
validates handle_type::from_address in a given handle_type.
(coro_promise_type_found_p): Remove reliance on
coroutine_handle<> defaulting the promise type to void.  Store
get_handle_type_* results where appropriate.
(get_coroutine_from_address): New helper.  Gets the
handle_type::from_address BASELINK from a coroutine_info.
(build_actor_fn): Use the get_coroutine_from_address 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-5.C: New test.
* g++.dg/coroutines/pr105475-6.C: New test.
* g++.dg/coroutines/pr105475-broken-spec.C: New test.
* g++.dg/coroutines/pr105475-broken-spec-2.C: New test.

Diff:
---
 gcc/cp/coroutines.cc   | 138 ++---
 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   |  41 ++
 gcc/testsuite/g++.dg/coroutines/pr105475-5.C   |  29 +
 gcc/testsuite/g++.dg/coroutines/pr105475-6.C   |  29 +
 .../g++.dg/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 +-
 gcc/testsuite/g++.dg/coroutines/pr94879-folly-1.C  |  10 +-
 gcc/testsuite/g++.dg/coroutines/pr94883-folly-2.C  |  10 +-
 gcc/testsuite/g++.dg/coroutines/pr98118.C  |  10 +-
 17 files changed, 439 insertions(+), 23 deletions(-)

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index af03f5e0f744..742f0e505976 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -91,6 +91,7 @@ struct GTY((for_user)) coroutine_info
 one that will eventually be allocated in the coroutine
 frame.  */
   tree promise_proxy; /* Likewise, a proxy promise instance.  */
+  tree from_address;  /* handle_type from_address function.  */
   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
function into a coroutine.  */
@@ -203,7 +204,6 @@ static GTY(()) tree coro_final_suspend_identifier;
 static GTY(()) tree coro_return_void_identifier;
 static GTY(()) tree coro_retu

[gcc r15-3922] c++: simplify handling implicit INDIRECT_REF and co_await in convert_to_void

2024-09-27 Thread Arsen Arsenovic via Gcc-cvs
https://gcc.gnu.org/g:de03ef6337b0a368d61c74b790313b4216c7ed6e

commit r15-3922-gde03ef6337b0a368d61c74b790313b4216c7ed6e
Author: Arsen Arsenović 
Date:   Fri Sep 20 13:13:02 2024 +0200

c++: simplify handling implicit INDIRECT_REF and co_await in convert_to_void

convert_to_void has, so far, when converting a co_await expression to
void altered the await_resume expression of a co_await so that it is
also converted to void.  This meant that the type of the await_resume
expression, which is also supposed to be the type of the whole co_await
expression, was not the same as the type of the CO_AWAIT_EXPR tree.

While this has not caused problems so far, it is unexpected, I think.

Also, convert_to_void had a special case when an INDIRECT_REF wrapped a
CALL_EXPR.  In this case, we also diagnosed maybe_warn_nodiscard.  This
was a duplication of logic related to converting call expressions to
void.

Instead, we can generalize a bit, and rather discard the expression that
was implicitly dereferenced instead.

This patch changes the diagnostic of:

  void f(struct S* x) { static_cast(*x); }

... from:

  warning: indirection will not access object of incomplete type
   'volatile S' in statement

... to:

  warning: implicit dereference will not access object of type
   ‘volatile S’ in statement

... but should have no impact in other cases.

gcc/cp/ChangeLog:

* coroutines.cc (co_await_get_resume_call): Return a tree
directly, rather than a tree pointer.
* cp-tree.h (co_await_get_resume_call): Adjust signature
accordingly.
* cvt.cc (convert_to_void): Do not alter CO_AWAIT_EXPRs when
discarding them.  Simplify handling implicit INDIRECT_REFs.

gcc/testsuite/ChangeLog:

* g++.dg/coroutines/nodiscard-1.C: New test.

Diff:
---
 gcc/cp/coroutines.cc  |   4 +-
 gcc/cp/cp-tree.h  |   2 +-
 gcc/cp/cvt.cc | 102 +-
 gcc/testsuite/g++.dg/coroutines/nodiscard-1.C |  77 +++
 4 files changed, 132 insertions(+), 53 deletions(-)

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index 50904e0d004e..8e4c55a800e4 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -730,14 +730,14 @@ coro_get_destroy_function (tree decl)
 
 /* Given a CO_AWAIT_EXPR AWAIT_EXPR, return its resume call.  */
 
-tree*
+tree
 co_await_get_resume_call (tree await_expr)
 {
   gcc_checking_assert (TREE_CODE (await_expr) == CO_AWAIT_EXPR);
   tree vec = TREE_OPERAND (await_expr, 3);
   if (!vec)
 return nullptr;
-  return &TREE_VEC_ELT (vec, 2);
+  return TREE_VEC_ELT (vec, 2);
 }
 
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 7c438eca16d6..39c065eecf6d 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -8782,7 +8782,7 @@ extern tree coro_get_actor_function   (tree);
 extern tree coro_get_destroy_function  (tree);
 extern tree coro_get_ramp_function (tree);
 
-extern tree* co_await_get_resume_call  (tree await_expr);
+extern tree co_await_get_resume_call   (tree await_expr);
 
 
 /* contracts.cc */
diff --git a/gcc/cp/cvt.cc b/gcc/cp/cvt.cc
index df02b8faaf51..526937d36181 100644
--- a/gcc/cp/cvt.cc
+++ b/gcc/cp/cvt.cc
@@ -1272,88 +1272,96 @@ convert_to_void (tree expr, impl_conv_void implicit, 
tsubst_flags_t complain)
  complete_type (type);
int is_complete = COMPLETE_TYPE_P (type);
 
-   /* Can't load the value if we don't know the type.  */
-   if (is_volatile && !is_complete)
+   /* Don't load the value if this is an implicit dereference, or if
+  the type needs to be handled by ctors/dtors.  */
+   if (is_reference)
   {
-if (complain & tf_warning)
+if (is_volatile && (complain & tf_warning)
+   /* A co_await expression, in its await_resume expression, also
+  contains an implicit dereference.  As a result, we don't
+  need to warn about them here.  */
+   && TREE_CODE (TREE_OPERAND (expr, 0)) != CO_AWAIT_EXPR)
  switch (implicit)
{
  case ICV_CAST:
warning_at (loc, 0, "conversion to void will not access "
-   "object of incomplete type %qT", type);
+   "object of type %qT", type);
break;
  case ICV_SECOND_OF_COND:
-   warning_at (loc, 0, "indirection will not access object of "
-   "incomplete type %qT in second operand "
-   "of conditional expression", type);
+   warning_at (loc, 0, "implicit dereference will not access "
+

[gcc r15-3921] c++/coro: prevent ICV_STATEMENT diagnostics in temp promotion [PR116502]

2024-09-27 Thread Arsen Arsenovic via Gcc-cvs
https://gcc.gnu.org/g:05e4f07cad1eacf869c10622cae2a9cdee3b6a7a

commit r15-3921-g05e4f07cad1eacf869c10622cae2a9cdee3b6a7a
Author: Arsen Arsenović 
Date:   Wed Aug 28 21:59:18 2024 +0200

c++/coro: prevent ICV_STATEMENT diagnostics in temp promotion [PR116502]

If such a diagnostic is necessary, it has already been emitted,
otherwise, it is not correct and emitting it here is inactionable by the
user, and bogus.

PR c++/116502

gcc/cp/ChangeLog:

* coroutines.cc (maybe_promote_temps): Convert temporary
initializers to void without complaining.

gcc/testsuite/ChangeLog:

* g++.dg/coroutines/maybe-unused-1.C: New test.
* g++.dg/coroutines/pr116502.C: New test.

Diff:
---
 gcc/cp/coroutines.cc | 12 ++---
 gcc/testsuite/g++.dg/coroutines/maybe-unused-1.C | 33 
 gcc/testsuite/g++.dg/coroutines/pr116502.C   | 33 
 3 files changed, 75 insertions(+), 3 deletions(-)

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index 4c7ea1dd3216..50904e0d004e 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -3203,7 +3203,13 @@ maybe_promote_temps (tree *stmt, void *d)
 to run the initializer.
 If the initializer is a conditional expression, we need to collect
 and declare any promoted variables nested within it.  DTORs for such
-variables must be run conditionally too.  */
+variables must be run conditionally too.
+
+Since here we're synthetically processing code here, we've already
+emitted any Wunused-result warnings.  Below, however, we call
+finish_expr_stmt, which will convert its operand to void, and could
+result in such a diagnostic being emitted.  To avoid that, convert to
+void ahead of time.  */
   if (t->var)
{
  tree var = t->var;
@@ -3213,7 +3219,7 @@ maybe_promote_temps (tree *stmt, void *d)
  if (TREE_CODE (t->init) == COND_EXPR)
process_conditional (t, vlist);
  else
-   finish_expr_stmt (t->init);
+   finish_expr_stmt (convert_to_void (t->init, ICV_STATEMENT, 
tf_none));
  if (tree cleanup = cxx_maybe_build_cleanup (var, tf_warning_or_error))
{
  tree cl = build_stmt (sloc, CLEANUP_STMT, expr_list, cleanup, 
var);
@@ -3232,7 +3238,7 @@ maybe_promote_temps (tree *stmt, void *d)
  if (TREE_CODE (t->init) == COND_EXPR)
process_conditional (t, vlist);
  else
-   finish_expr_stmt (t->init);
+   finish_expr_stmt (convert_to_void (t->init, ICV_STATEMENT, 
tf_none));
  if (expr_list)
{
  if (TREE_CODE (expr_list) != STATEMENT_LIST)
diff --git a/gcc/testsuite/g++.dg/coroutines/maybe-unused-1.C 
b/gcc/testsuite/g++.dg/coroutines/maybe-unused-1.C
new file mode 100644
index ..68d59d83e8eb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/maybe-unused-1.C
@@ -0,0 +1,33 @@
+// https://gcc.gnu.org/PR116502
+#include 
+
+struct SuspendNever {
+  bool await_ready() noexcept;
+  void await_suspend(std::coroutine_handle<>) noexcept;
+  void await_resume() noexcept;
+};
+
+struct Coroutine;
+
+struct PromiseType {
+  Coroutine get_return_object();
+  SuspendNever initial_suspend();
+  SuspendNever final_suspend() noexcept;
+  void return_void();
+  void unhandled_exception();
+};
+
+struct Coroutine {
+  using promise_type = PromiseType;
+};
+
+struct Awaiter {
+  bool await_ready();
+  void await_suspend(std::coroutine_handle<>);
+  [[nodiscard]] int& await_resume();
+};
+
+Coroutine foo()
+{
+  co_await Awaiter {}; // { dg-warning "Wunused-result" }
+}
diff --git a/gcc/testsuite/g++.dg/coroutines/pr116502.C 
b/gcc/testsuite/g++.dg/coroutines/pr116502.C
new file mode 100644
index ..95cc0bc8a983
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/pr116502.C
@@ -0,0 +1,33 @@
+// https://gcc.gnu.org/PR116502
+#include 
+
+struct SuspendNever {
+  bool await_ready() noexcept;
+  void await_suspend(std::coroutine_handle<>) noexcept;
+  void await_resume() noexcept;
+};
+
+struct Coroutine;
+
+struct PromiseType {
+  Coroutine get_return_object();
+  SuspendNever initial_suspend();
+  SuspendNever final_suspend() noexcept;
+  void return_void();
+  void unhandled_exception();
+};
+
+struct Coroutine {
+  using promise_type = PromiseType;
+};
+
+struct Awaiter {
+  bool await_ready();
+  void await_suspend(std::coroutine_handle<>);
+  [[nodiscard]] int& await_resume();
+};
+
+Coroutine foo()
+{
+  (void)co_await Awaiter {};
+}


[gcc r15-3923] c++/coro: ignore cleanup_point_exprs while expanding awaits [PR116793]

2024-09-27 Thread Arsen Arsenovic via Gcc-cvs
https://gcc.gnu.org/g:d888a8a8dcf391197ae82e2bbf99507effc27950

commit r15-3923-gd888a8a8dcf391197ae82e2bbf99507effc27950
Author: Arsen Arsenović 
Date:   Tue Sep 24 18:16:01 2024 +0200

c++/coro: ignore cleanup_point_exprs while expanding awaits [PR116793]

If we reach a CLEANUP_POINT_EXPR while trying to walk statements, we
actually care about the statement or statement list contained within it.

Indeed, such a construction started happening with
r15-3513-g964577c31df206, after temporary promotion.  In the test case
presented in PR116793, the compiler generated:

  T002_2_3];

  int T002 [value-expr: frame_ptr->T002_2_3];
<) >;
  struct _cleanup_task Aw0 [value-expr: frame_ptr->Aw0_2_3];
<) >;
<
Aw0

{_cleanup_task::await_ready (&Aw0), 
_cleanup_task::await_suspend<_task1::promise_type> (&Aw0, TARGET_EXPR ), <<< Unknown tree: aggr_init_expr
  4
  await_resume
  D.22443
  &Aw0 >>>}
0 >>>) >;
<;
  }
  D.22467 = 1;
  int & i [value-expr: frame_ptr->i_1_2];
  < (NON_LVALUE_EXPR )) >;>>;

... i.e. a statement list within a cleanup point.  In such a case, we
don't actually care about the cleanup point, but we do care about the
statement inside, so, we can just walk down into the CLEANUP_POINT_EXPR.

PR c++/116793

gcc/cp/ChangeLog:

* coroutines.cc (await_statement_expander): Just process
subtrees if encountering a CLEANUP_POINT_EXPR.

gcc/testsuite/ChangeLog:

* g++.dg/coroutines/pr116793-1.C: New test.

Diff:
---
 gcc/cp/coroutines.cc |  4 +++-
 gcc/testsuite/g++.dg/coroutines/pr116793-1.C | 26 ++
 2 files changed, 29 insertions(+), 1 deletion(-)

diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index 8e4c55a800e4..86a5ac8999ac 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -2063,7 +2063,9 @@ await_statement_expander (tree *stmt, int *do_subtree, 
void *d)
   tree res = NULL_TREE;
 
   /* Process a statement at a time.  */
-  if (STATEMENT_CLASS_P (*stmt) || TREE_CODE (*stmt) == BIND_EXPR)
+  if (STATEMENT_CLASS_P (*stmt)
+  || TREE_CODE (*stmt) == BIND_EXPR
+  || TREE_CODE (*stmt) == CLEANUP_POINT_EXPR)
 return NULL_TREE; /* Just process the sub-trees.  */
   else if (TREE_CODE (*stmt) == STATEMENT_LIST)
 {
diff --git a/gcc/testsuite/g++.dg/coroutines/pr116793-1.C 
b/gcc/testsuite/g++.dg/coroutines/pr116793-1.C
new file mode 100644
index ..ed2bdd26996a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/coroutines/pr116793-1.C
@@ -0,0 +1,26 @@
+// https://gcc.gnu.org/PR116793
+#include 
+#include 
+struct _cleanup_task {
+  bool await_ready() const noexcept;
+  template 
+  bool await_suspend(std::coroutine_handle parent) noexcept;
+  std::tuple await_resume() noexcept;
+};
+struct _task1 {
+  struct promise_type final {
+std::suspend_always initial_suspend() noexcept;
+_task1 get_return_object() noexcept;
+void unhandled_exception() noexcept;
+struct awaiter final {
+  bool await_ready() noexcept;
+  void await_resume() noexcept;
+  void await_suspend(std::coroutine_handle h) noexcept;
+};
+awaiter final_suspend() noexcept;
+  };
+};
+_cleanup_task func(int &&);
+_task1 g() {
+  auto &&[i] = co_await func(3);
+}


[gcc r15-6447] libstdc++: add missing return in generator assignment operator [PR118196]

2024-12-27 Thread Arsen Arsenovic via Gcc-cvs
https://gcc.gnu.org/g:9a1cb52cae2d48d2fc18d01b534bf4e3203f0cc1

commit r15-6447-g9a1cb52cae2d48d2fc18d01b534bf4e3203f0cc1
Author: Arsen Arsenović 
Date:   Tue Dec 24 12:50:28 2024 +0100

libstdc++: add missing return in generator assignment operator [PR118196]

libstdc++-v3/ChangeLog:

PR libstdc++/118196
* include/std/generator (generator::operator=(generator)): Add
missing 'return *this;'.
* testsuite/24_iterators/range_generators/pr118196.cc: New test.

Diff:
---
 libstdc++-v3/include/std/generator  |  1 +
 .../testsuite/24_iterators/range_generators/pr118196.cc | 13 +
 2 files changed, 14 insertions(+)

diff --git a/libstdc++-v3/include/std/generator 
b/libstdc++-v3/include/std/generator
index 3a19d535ef86..7d7e9b708657 100644
--- a/libstdc++-v3/include/std/generator
+++ b/libstdc++-v3/include/std/generator
@@ -717,6 +717,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   {
swap(__other._M_coro, this->_M_coro);
swap(__other._M_began, this->_M_began);
+   return *this;
   }
 
   _Iterator
diff --git a/libstdc++-v3/testsuite/24_iterators/range_generators/pr118196.cc 
b/libstdc++-v3/testsuite/24_iterators/range_generators/pr118196.cc
new file mode 100644
index ..ae161df2a4a6
--- /dev/null
+++ b/libstdc++-v3/testsuite/24_iterators/range_generators/pr118196.cc
@@ -0,0 +1,13 @@
+// { dg-do compile { target c++23 } }
+#include ""
+
+std::generator
+generator();
+
+void
+try_reassigning()
+{
+  auto gen = generator();
+  auto gen2 = generator();
+  gen = std::move(gen2);
+}


[gcc r15-6084] c++: Implement a coroutine language debug dump

2024-12-10 Thread Arsen Arsenovic via Gcc-cvs
https://gcc.gnu.org/g:fcdc0d8963adfa99315a7895b5685ac102182cdf

commit r15-6084-gfcdc0d8963adfa99315a7895b5685ac102182cdf
Author: Arsen Arsenović 
Date:   Thu Sep 5 19:53:07 2024 +0200

c++: Implement a coroutine language debug dump

This provides to people working on coroutines, as well as writing tests
for coroutines, a way to have insight into the results and inputs of the
coroutine transformation passes, which is quite essential to
understanding what happens in the coroutine transformation.  Currently,
the information dumped is the pre-transform function (which is not
otherwise available), the generated ramp function, the generated frame
type, the transformed actor/resumer, and the destroyer stub.

While debugging this, I've also encountered a minor bug in
c-pretty-print.cc, where it tried to check DECL_REGISTER of DECLs that
did not support it.  I've added a check for that.

Similary, I found one in pp_cxx_template_parameter, where TREE_TYPE was
called on the list cell the template parameter was in rather than on the
parameter itself.  I've fixed that.

And, lastly, there appeared to be no way to pretty-print a FIELD_DECL,
so I added support to cxx_pretty_printer::declaration for it (by reusing
the VAR_DECL path).

Co-authored-by: Iain Sandoe 

gcc/c-family/ChangeLog:

* c-pretty-print.cc (c_pretty_printer::storage_class_specifier):
Check that we're looking at a PARM_DECL or VAR_DECL before
looking at DECL_REGISTER.

gcc/cp/ChangeLog:

* coroutines.cc (dump_record_fields): New helper.  Iterates a
RECORD_TYPEs TYPE_FIELDS and pretty-prints them.
(dmp_str): New.  The lang-coro dump stream.
(coro_dump_id): New.  ID of the lang-coro dump.
(coro_dump_flags): New.  Flags passed to the lang-coro dump.
(coro_maybe_dump_initial_function): New helper.  Prints, if
dumping is enabled, the fndecl passed to it as the original
function.
(coro_maybe_dump_ramp): New.  Prints the ramp function passed to
it, if dumping is enabled.
(coro_maybe_dump_transformed_functions): New.
(cp_coroutine_transform::apply_transforms): Initialize the
lang-coro dump.  Call coro_maybe_dump_initial_function on the
original function, as well as coro_maybe_dump_ramp, after the
transformation into the ramp is finished.
(cp_coroutine_transform::finish_transforms): Call
coro_maybe_dump_transformed_functions on the built actor and
destroy.
* cp-objcp-common.cc (cp_register_dumps): Register the coroutine
dump.
* cp-tree.h (coro_dump_id): Declare as extern.
* cxx-pretty-print.cc (pp_cxx_template_parameter): Don't call
TREE_TYPE on a TREE_LIST cell.
(cxx_pretty_printer::declaration): Handle FIELD_DECL similar to
VAR_DECL.

gcc/ChangeLog:

* dumpfile.cc (FIRST_ME_AUTO_NUMBERED_DUMP): Bump to 6 for sake
of the coroutine dump.

Diff:
---
 gcc/c-family/c-pretty-print.cc |   3 +-
 gcc/cp/coroutines.cc   | 124 +
 gcc/cp/cp-objcp-common.cc  |   2 +
 gcc/cp/cp-tree.h   |   1 +
 gcc/cp/cxx-pretty-print.cc |   3 +-
 gcc/dumpfile.cc|   2 +-
 6 files changed, 132 insertions(+), 3 deletions(-)

diff --git a/gcc/c-family/c-pretty-print.cc b/gcc/c-family/c-pretty-print.cc
index b772d8d8ae2b..c48336f88295 100644
--- a/gcc/c-family/c-pretty-print.cc
+++ b/gcc/c-family/c-pretty-print.cc
@@ -753,7 +753,8 @@ c_pretty_printer::storage_class_specifier (tree t)
 pp_c_ws_string (this, "typedef");
   else if (DECL_P (t))
 {
-  if (DECL_REGISTER (t))
+  if ((TREE_CODE (t) == PARM_DECL || VAR_P (t))
+ && DECL_REGISTER (t))
pp_c_ws_string (this, "register");
   else if (TREE_STATIC (t) && VAR_P (t))
pp_c_ws_string (this, "static");
diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index 7585229d94ab..242afbe9a251 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -34,6 +34,123 @@ along with GCC; see the file COPYING3.  If not see
 #include "hash-map.h"
 #include "coroutines.h"
 
+/* = Debug. = */
+
+#include "langhooks.h"
+#include "cxx-pretty-print.h"
+
+/* Walk through the fields of the type TYP and print them to the pretty printer
+   PP.  */
+
+static void
+dump_record_fields (cxx_pretty_printer *pp, tree typ)
+{
+  pp->type_id (typ);
+  pp_newline_and_indent (pp, 2);
+  pp_left_brace (pp);
+  pp_indentation (pp) += 2;
+
+  /* We'll be on a new line, we don't need padding.  */
+  pp->set_padding (pp_none);
+
+  for (tree tmp = TYPE_FIELDS (typ); tmp; tmp = DECL_CHAIN (tmp))
+{
+  pp_newline_and_indent (pp, 0)

[gcc r15-5577] doc/cpp: Document __has_include_next

2024-11-21 Thread Arsen Arsenovic via Gcc-cvs
https://gcc.gnu.org/g:ffeee625c53d882171af436222a7b18ed9ed89e1

commit r15-5577-gffeee625c53d882171af436222a7b18ed9ed89e1
Author: Arsen Arsenović 
Date:   Fri Oct 18 23:14:58 2024 +0200

doc/cpp: Document __has_include_next

While hacking on an unrelated change, I noticed that __has_include_next
hasn't been documented at all.  This patch adds it to the __has_include
manual node.

gcc/ChangeLog:

* doc/cpp.texi (__has_include): Document __has_include_next
also.
(Conditional Syntax): Mention __has_include_next in the
description for the __has_include menu entry.

Diff:
---
 gcc/doc/cpp.texi | 40 
 1 file changed, 24 insertions(+), 16 deletions(-)

diff --git a/gcc/doc/cpp.texi b/gcc/doc/cpp.texi
index a83aa263df0f..970c0a393971 100644
--- a/gcc/doc/cpp.texi
+++ b/gcc/doc/cpp.texi
@@ -3204,7 +3204,8 @@ directive}: @samp{#if}, @samp{#ifdef} or @samp{#ifndef}.
 * @code{__has_builtin}::
 * @code{__has_feature}::
 * @code{__has_extension}::
-* @code{__has_include}::
+* @code{__has_include}::@code{__has_include} and
+ @code{__has_include_next}
 * @code{__has_embed}::
 @end menu
 
@@ -3607,22 +3608,27 @@ details of which identifiers are accepted by these 
function-like macros, see
 the Clang documentation}}.
 
 @node @code{__has_include}
-@subsection @code{__has_include}
+@subsection @code{__has_include}, @code{__has_include_next}
 @cindex @code{__has_include}
-
-The special operator @code{__has_include (@var{operand})} may be used in
-@samp{#if} and @samp{#elif} expressions to test whether the header referenced
-by its @var{operand} can be included using the @samp{#include} directive.  
Using
-the operator in other contexts is not valid.  The @var{operand} takes
-the same form as the file in the @samp{#include} directive (@pxref{Include
-Syntax}) and evaluates to a nonzero value if the header can be included and
-to zero otherwise.  Note that that the ability to include a header doesn't
-imply that the header doesn't contain invalid constructs or @samp{#error}
-directives that would cause the preprocessor to fail.
-
-The @code{__has_include} operator by itself, without any @var{operand} or
-parentheses, acts as a predefined macro so that support for it can be tested
-in portable code.  Thus, the recommended use of the operator is as follows:
+@cindex @code{__has_include_next}
+
+The special operators @code{__has_include (@var{operand})} and
+@code{__has_include_next (@var{operand})} may be used in @samp{#if} and
+@samp{#elif} expressions to test whether the header referenced by their
+@var{operand} can be included using the @samp{#include} and
+@samp{#include_next} directive, respectively.  Using the operators in
+other contexts is not valid.  The @var{operand} takes the same form as
+the file in the @samp{#include} and @samp{#include_next} directives
+respectively (@pxref{Include Syntax}) and the operators evaluate to a
+nonzero value if the header can be included and to zero otherwise.  Note
+that that the ability to include a header doesn't imply that the header
+doesn't contain invalid constructs or @samp{#error} directives that
+would cause the preprocessor to fail.
+
+The @code{__has_include} and @code{__has_include_next} operators by
+themselves, without any @var{operand} or parentheses, act as
+predefined macros so that support for them can be tested in portable
+code.  Thus, the recommended use of the operators is as follows:
 
 @smallexample
 #if defined __has_include
@@ -3645,6 +3651,8 @@ but not with others that don't.
 #endif
 @end smallexample
 
+The same holds for @code{__has_include_next}.
+
 @node @code{__has_embed}
 @subsection @code{__has_embed}
 @cindex @code{__has_embed}


[gcc r15-6409] warn-access: ignore template parameters when matching operator new/delete [PR109224]

2024-12-21 Thread Arsen Arsenovic via Gcc-cvs
https://gcc.gnu.org/g:7d83a32aacd6005c0c038c74562e35d70f6a77a8

commit r15-6409-g7d83a32aacd6005c0c038c74562e35d70f6a77a8
Author: Arsen Arsenović 
Date:   Thu Aug 1 17:38:15 2024 +0200

warn-access: ignore template parameters when matching operator new/delete 
[PR109224]

Template parameters on a member operator new cannot affect its member
status nor whether it is a singleton or array operator new, hence, we
can ignore it for purposes of matching.  Similar logic applies to the
placement operator delete.

In the PR (and a lot of idiomatic coroutine code generally), operator
new is templated in order to be able to inspect (some of) the arguments
passed to the coroutine, to make allocation-related decisions.  However,
the coroutine implementation will not call a placement delete form, so
it cannot get templated.  As a result, when demangling, we have an extra
template DEMANGLE_COMPONENT_TEMPLATE around the actual operator new, but
not operator delete.  This terminates new_delete_mismatch_p early.

PR middle-end/109224 - Wmismatched-new-delete false positive with a 
templated operator new (common with coroutines)

gcc/ChangeLog:

PR middle-end/109224
* gimple-ssa-warn-access.cc (new_delete_mismatch_p): Strip
DEMANGLE_COMPONENT_TEMPLATE from the operator new and operator
after demangling.

gcc/testsuite/ChangeLog:

PR middle-end/109224
* g++.dg/warn/Wmismatched-new-delete-9.C: New test.

Diff:
---
 gcc/gimple-ssa-warn-access.cc  | 18 -
 .../g++.dg/warn/Wmismatched-new-delete-9.C | 47 ++
 gcc/testsuite/g++.dg/warn/pr109224.C   | 25 
 3 files changed, 89 insertions(+), 1 deletion(-)

diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc
index 820f23825254..1eb9d2ee4bac 100644
--- a/gcc/gimple-ssa-warn-access.cc
+++ b/gcc/gimple-ssa-warn-access.cc
@@ -1765,7 +1765,23 @@ new_delete_mismatch_p (tree new_decl, tree delete_decl)
   void *np = NULL, *dp = NULL;
   demangle_component *ndc = cplus_demangle_v3_components (new_str, 0, &np);
   demangle_component *ddc = cplus_demangle_v3_components (del_str, 0, &dp);
-  bool mismatch = ndc && ddc && new_delete_mismatch_p (*ndc, *ddc);
+
+  /* Sometimes, notably quite often with coroutines, 'operator new' is
+ templated.  However, template arguments can't change whether a given
+ new/delete is a singleton or array one, nor what it is a member of, so
+ the template arguments can be safely ignored for the purposes of checking
+ for mismatches.   */
+
+  auto strip_dc_template = [] (demangle_component* dc)
+  {
+if (dc->type == DEMANGLE_COMPONENT_TEMPLATE)
+  dc = dc->u.s_binary.left;
+return dc;
+  };
+
+  bool mismatch = (ndc && ddc
+  && new_delete_mismatch_p (*strip_dc_template (ndc),
+*strip_dc_template (ddc)));
   free (np);
   free (dp);
   return mismatch;
diff --git a/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-9.C 
b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-9.C
new file mode 100644
index ..d431f4049e87
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wmismatched-new-delete-9.C
@@ -0,0 +1,47 @@
+/* { dg-do compile { target c++11 } } */
+/* { dg-additional-options "-Wmismatched-new-delete" } */
+/* PR middle-end/109224 */
+/* Verify that we consider templated operator new matching with its operator
+   delete.  */
+
+#include 
+
+struct foo
+{
+  template
+  void* operator new (std::size_t sz, Args&&...);
+  template
+  void* operator new[] (std::size_t sz, Args&&...);
+
+  void operator delete (void* x);
+  void operator delete[] (void* x);
+
+  template
+  void operator delete (void* x, Args&&...);
+  template
+  void operator delete[] (void* x, Args&&...);
+};
+
+void
+f ()
+{
+  delete (new (123, true) foo);
+  delete[] (new (123, true) foo[123]);
+
+  delete (new (123, true) foo[123]);
+  // { dg-warning "Wmismatched-new-delete" "" { target *-*-* } {.-1} }
+  // { dg-note "returned from" "" { target *-*-* } {.-2} }
+  delete[] (new (123, true) foo);
+  // { dg-warning "Wmismatched-new-delete" "" { target *-*-* } {.-1} }
+  // { dg-note "returned from" "" { target *-*-* } {.-2} }
+
+  foo::operator delete (foo::operator new (1, 123, true), 123, true);
+  foo::operator delete[] (foo::operator new[] (123, 123, true), 123, true);
+
+  foo::operator delete (foo::operator new[] (123, 123, true), 123, true);
+  // { dg-warning "Wmismatched-new-delete" "" { target *-*-* } {.-1} }
+  // { dg-note "returned from" "" { target *-*-* } {.-2} }
+  foo::operator delete[] (foo::operator new (1, 123, true), 123, true);
+  // { dg-warning "Wmismatched-new-delete" "" { target *-*-* } {.-1} }
+  // { dg-note "returned from" "" { target *-*-* } {.-2} }
+}
diff --git a/gcc/testsuite/g++.dg/warn/pr109224.C 
b/gcc/t

[gcc r15-7129] d, ada/spec: only sub nostd{inc, lib} rather than nostd{inc, lib}*

2025-01-22 Thread Arsen Arsenovic via Gcc-cvs
https://gcc.gnu.org/g:fbc94ff6e7615c7e5aeff8f3e0a2f894889e1b4f

commit r15-7129-gfbc94ff6e7615c7e5aeff8f3e0a2f894889e1b4f
Author: Arsen Arsenović 
Date:   Mon Sep 2 21:29:53 2024 +0200

d,ada/spec: only sub nostd{inc,lib} rather than nostd{inc,lib}*

This prevents the gcc driver erroneously accepting -nostdlib++ when it
should not when Ada was enabled.

Also, similarly, -nostdinc* (where * is nonempty) is unhandled by either
the Ada or D compiler, so the spec should not substitute those
either (thanks for pointing that out, Jakub).

Brought to my attention by Michał Górny .

gcc/ada/ChangeLog:

* gcc-interface/lang-specs.h: Replace %{nostdinc*} %{nostdlib*}
with %{nostdinc} %{nostdlib}.

gcc/d/ChangeLog:

* lang-specs.h: Replace %{nostdinc*} with %{nostdinc}.

gcc/testsuite/ChangeLog:

* gcc.dg/driver-nostdlibstar.c: New test.

Diff:
---
 gcc/ada/gcc-interface/lang-specs.h | 6 +++---
 gcc/d/lang-specs.h | 2 +-
 gcc/testsuite/gcc.dg/driver-nostdlibstar.c | 4 
 3 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/gcc/ada/gcc-interface/lang-specs.h 
b/gcc/ada/gcc-interface/lang-specs.h
index 2ba0f1d20a74..bfa41afe621b 100644
--- a/gcc/ada/gcc-interface/lang-specs.h
+++ b/gcc/ada/gcc-interface/lang-specs.h
@@ -37,7 +37,7 @@
  %{pg:%{fomit-frame-pointer:%e-pg and -fomit-frame-pointer are incompatible}}\
  %{!S:%{!c:%e-c or -S required for Ada}}\
  gnat1 %{I*} %{k8:-gnatk8} %{!Q:-quiet}\
-%{nostdinc*} %{nostdlib*}\
+%{nostdinc} %{nostdlib}\
 %{fcompare-debug-second:-gnatd_A} \
 %{O*} %{W*} %{w} %{p} %{pg:-p} " ADA_DUMPS_OPTIONS " \
 %{coverage:-fprofile-arcs -ftest-coverage} "
@@ -55,7 +55,7 @@
"\
  %{!c:%e-c required for gnat2why}\
  gnat1why %{I*} %{k8:-gnatk8} %{!Q:-quiet}\
-%{nostdinc*} %{nostdlib*}\
+%{nostdinc} %{nostdlib}\
 %{a} " ADA_DUMPS_OPTIONS " \
 %{gnatea:-gnatez} %{g*&m*&f*} \
 %1 %{o*:%w%*-gnatO} \
@@ -66,7 +66,7 @@
"\
  %{!c:%e-c required for gnat2scil}\
  gnat1scil %{I*} %{k8:-gnatk8} %{!Q:-quiet}\
-%{nostdinc*} %{nostdlib*}\
+%{nostdinc} %{nostdlib}\
 %{a} " ADA_DUMPS_OPTIONS " \
 %{gnatea:-gnatez} %{g*&m*&f*} \
 %1 %{o*:%w%*-gnatO} \
diff --git a/gcc/d/lang-specs.h b/gcc/d/lang-specs.h
index 2292e7eac9fa..64e7a5f25988 100644
--- a/gcc/d/lang-specs.h
+++ b/gcc/d/lang-specs.h
@@ -22,7 +22,7 @@ along with GCC; see the file COPYING3.  If not see
 {".dd", "@d", 0, 1, 0 },
 {".di", "@d", 0, 1, 0 },
 {"@d",
-  "%{!E:d21 %i %(cc1_options) %I %{nostdinc*} %{i*} %{I*} %{J*} \
+  "%{!E:d21 %i %(cc1_options) %I %{nostdinc} %{i*} %{I*} %{J*} \
 %{H} %{Hd*} %{Hf*} %{MD:-MD %b.deps} %{MMD:-MMD %b.deps} \
 %{M} %{MM} %{MF*} %{MG} %{MP} %{MQ*} %{MT*} \
 %{X:-Xf %b.json} %{Xf*} \
diff --git a/gcc/testsuite/gcc.dg/driver-nostdlibstar.c 
b/gcc/testsuite/gcc.dg/driver-nostdlibstar.c
new file mode 100644
index ..b3b208248abe
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/driver-nostdlibstar.c
@@ -0,0 +1,4 @@
+// Test that the GCC driver (which has no concept of libstdc++) rejects 
-nostdlib++
+// { dg-additional-options "-nostdlib++" }
+// { dg-prune-output "compilation terminated" }
+// { dg-error "unrecognized command-line option '-nostdlib\\+\\+'" "" { target 
*-*-* } 0 }