On 1/23/25 9:47 AM, Jakub Jelinek wrote:
On Wed, Jan 22, 2025 at 04:19:38PM -0500, Jason Merrill wrote:
@@ -4482,6 +4536,7 @@ emit_partial_init_fini_fn (bool initp, u
         /* Do one initialization or destruction.  */
         one_static_initialization_or_destruction (initp, decl, init);
       }
+  decomp_finalize_var_list (sl, save_stmts_are_full_exprs_p);
     if (omp_target)
       {
@@ -4510,6 +4565,7 @@ prune_vars_needing_no_initialization (tr
   {
     tree *var = vars;
     tree result = NULL_TREE;
+  bool clear_nonbase = false;
     while (*var)
       {
@@ -4517,6 +4573,20 @@ prune_vars_needing_no_initialization (tr
         tree decl = TREE_VALUE (t);
         tree init = TREE_PURPOSE (t);
+      if (STATIC_INIT_DECOMP_BASE_P (t)
+         && result != NULL_TREE
+         && STATIC_INIT_DECOMP_NONBASE_P (result))
+       clear_nonbase = true;
+      else if (clear_nonbase && !STATIC_INIT_DECOMP_BASE_P (t))
+       {

I don't see how we can ever get here...

+         clear_nonbase = false;
+         for (tree r = result; r; r = TREE_CHAIN (r))
+           if (STATIC_INIT_DECOMP_NONBASE_P (r))
+             STATIC_INIT_DECOMP_NONBASE_P (r) = 0;
+           else
+             break;
+       }
+
         /* Deal gracefully with error.  */
         if (error_operand_p (decl))
        {
@@ -4544,6 +4614,28 @@ prune_vars_needing_no_initialization (tr
          continue;
        }
+      clear_nonbase = false;

...if we always clear the flag here?  What situation is the code in this
function trying to correct?  This needs a lot more rationale.  Would it be
simpler to do a second loop over result after the main loop?

The purpose of the function is to prune some vars for whatever reason.
What cp_finish_decl ensures is that if there are namespace scope structured
bindings that need the CWG2867 special handling there is one or more
STATIC_INIT_DECOMP_BASE_P {static,tls}_aggregates TREE_LIST (where those
correspond to either the artificial base variable or some lifetime extended
helpers) followed by (note, here "followed by" really depends on whether the
list is reversed or not, e.g. for this function the argument list is reversed
and the returned one is not) one or more STATIC_INIT_DECOMP_NONBASE_P
TREE_LISTs (these are either the variables corresponding to user identifiers
and/or their lifetime extended helpers).

As there are only 2 single-bit flags, there is no clear marking which of these
base/nonbase VAR_DECLs correspond to structured binding xyz and which
correspond to another one.  Sure, if those are DECL_DECOMPOSITION_P
VAR_DECLs, we can look at base (or self if it is base), but if it is the
life extended VAR_DECLs, maybe we could guess something from the mangling,
but it becomes horrible.

So, all we have is
zero or more non-STATIC_INIT_DECOMP*P entries, followed by one or more
STATIC_INIT_DECOMP_BASE_P entries, followed by one or more
STATIC_INIT_DECOMP_NONBASE_P entries, followed by zero or more
non-STATIC_INIT_DECOMP*P entries, perhaps followed by one or more
STATIC_INIT_DECOMP_BASE_P entries etc.
As there could be zero normal entries in between, the boundary is
when STATIC_INIT_DECOMP_NONBASE_P is followed by normal or
STATIC_INIT_DECOMP_BASE_P entry.

Now if we prune some entries from this list (and sure, reverse it),
we could loose the original properties.
E.g. if the whole series of consecutive STATIC_INIT_DECOMP_BASE_P
entries is pruned and previously it was preceded and followed by
STATIC_INIT_DECOMP_NONBASE_P entries, all of sudden the two
STATIC_INIT_DECOMP_NONBASE_P sequences are indistinguishable from one
and so the cleanups of the STATIC_INIT_DECOMP_BASE_P that are kept
could be extended even over the STATIC_INIT_DECOMP_NONBASE_P initialization
over which it should not be extended.
Similarly, if the whole series of consecutive STATIC_INIT_DECOMP_NONBASE_P
entries is pruned and was in between two STATIC_INIT_DECOMP_BASE_P
sequences, in the pruned lists those might appear as something for a single
structured binding and have all cleanups extended over something it
shouldn't be.
If from entries corresponding to a single structured binding we drop
all STATIC_INIT_DECOMP_BASE_P or all STATIC_INIT_DECOMP_NONBASE_P
(or both entries), we just shouldn't have those flags set in any of the
remaining entries for that structured binding in the pruned list,
because either there are no base VAR_DECLs (so no cleanups to extend
across the non-bases) or there are no nonbase VAR_DECLs (and so the base
cleanups can just end at the end of the bases).
The clear_nonbase flag is part of this pruning.  It is not always cleared,
in particular it is not cleared in any of the cases where we prune some
entries (i.e. if (whatever) { ...; continue; }).

Hypothetically, but those cases are just either error or DECL_EXTERNAL. In the error case we're failing anyway; in the external case all the base/nonbase for a particular structured binding declaration should be consistent.

Note, unfortunately it is hard to come up with a testcase that actually
prunes something on purpose...

Indeed, it shouldn't be possible.

Jason

Reply via email to