https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120413
--- Comment #5 from Tobias Burnus <burnus at gcc dot gnu.org> --- ICE FIXED so far for mainline (GCC 16) and GCC 15. * * * Reading: > There is also BIND_EXPR_VARS, dunno if that should be walked instead > or in addition. The current code is about adding map clauses for lambda expressions, excluding locally defined declarations: ————————————————————————————————————————— /* Used to walk OpenMP target directive body. */ struct omp_target_walk_data { ... /* For lambda functions, the __closure object expression of the current function, and the set of captured variables accessed in target body. */ tree current_closure; hash_set<tree> closure_vars_accessed; /* Local variables declared inside a BIND_EXPR, used to filter out such variables when recording lambda_objects_accessed. */ hash_set<tree> local_decls; }; ... finish_omp_target_clauses_r (tree *tp, int *walk_subtrees, void *ptr) { ... if (TREE_CODE (t) == BIND_EXPR) { if (tree block = BIND_EXPR_BLOCK (t)) for (tree var = BLOCK_VARS (block); var; var = DECL_CHAIN (var)) if (!data->local_decls.contains (var)) data->local_decls.add (var); #if 0 // To add? for (tree var = BIND_EXPR_VARS (t); var; var = DECL_CHAIN (var)) if (!data->local_decls.contains (var)) data->local_decls.add (var); #endif return NULL_TREE; } ... if (TREE_TYPE (t) && LAMBDA_TYPE_P (TREE_TYPE (t))) { ... if (!data->lambda_objects_accessed.contains (t) /* Do not prepare to create target maps for locally declared lambdas or anonymous ones. */ && !data->local_decls.contains (t) && TREE_CODE (t) != TARGET_EXPR) data->lambda_objects_accessed.add (t); ... for (hash_set<tree>::iterator i = data.lambda_objects_accessed.begin (); i != data.lambda_objects_accessed.end (); ++i) { ... tree lc = build_omp_clause (loc, OMP_CLAUSE_MAP); OMP_CLAUSE_SET_MAP_KIND (lc, GOMP_MAP_TO); OMP_CLAUSE_DECL (lc) = lobj; ... ————————————————————————————————————————— It seems that the only testcase for the current code is: libgomp.c++/target-lambda-1.C ————————————————————————————————————————— template <typename L> void omp_target_loop (int begin, int end, L loop) { #pragma omp target teams distribute parallel for for (int i = begin; i < end; i++) loop (i); } ... omp_target_loop (0, N, [=](int i) { data1[i] = val; }); omp_target_loop (0, N, [=](int i) { data2[i] = valref + 1; }); ————————————————————————————————————————— Adding some warning diagnostic shows that during initial parsing: 33 | for (int i = 0; i < len; i++) that both BIND_EXPR_BLOCK and BIND_EXPR_VARS contain 'i'. Then for libgomp.c++/target-lambda-1.C: In instantiation of ‘void omp_target_loop(int, int, L) [with L = main()::<lambda(int)>]’: (A) The same for 'i'. (B) 'lambda_objects_accessed.add' for 'loop'. And likewise for the other lambda. => In this case, BIND_EXPR_BLOCK and BIND_EXPR_VARS contain the same variable. For the mapping, we get for the first call: ;; Function void omp_target_loop(int, int, L) [with L = main()::<lambda(int)>] (null) #pragma omp target map(to:loop [len: 16]) map(alloc:*loop.__data1 [len: 0]) \ map(attach_zero_length_array_section:loop.__data1 [bias: 0]) with int * const data1 [value-expr: __closure->__data1]; const int val [value-expr: __closure->__val]; * * * It looks as if BIND_EXPR is primarily created via c-family/c-gimplify.cc's tree decls, bind; if (block == NULL_TREE) decls = NULL_TREE; else if (TREE_CODE (block) == BLOCK) decls = BLOCK_VARS (block); else { decls = block; ... if (decls || block) { bind = build3 (BIND_EXPR, void_type_node, decls, body, block); such that walking BLOCK_VARS shouldn't be required. Only other use with decl != NULL: cp/init.cc:4214: controller = build3 (BIND_EXPR, void_type_node, tbase, NULL_TREE, NULL_TREE); * * * → Hence, IMHO, the current code is fine – as would be using BLOCK_VARS.