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
