diff --git a/src/decls.c b/src/decls.c index 6916c90263..4131160025 100644 --- a/src/decls.c +++ b/src/decls.c @@ -22602,6 +22602,23 @@ parameters are scanned by scan_a_template_parameter_declaration. recent (GNU C/C++ 3.1 and later) behavior. */ scan_gnu_declarator_attributes(dps); #endif /* GNU_EXTENSIONS_ALLOWED */ + if (C_mode() && is_variably_modified_type(dps->specifiers_type) && + is_typeref_kind( + skip_typerefs_not_typedefs_or_type_operators( + dps->specifiers_type), + trk_is_typeof_with_expression)) { + a_type_ptr typeof_type = + skip_typerefs_not_typedefs_or_type_operators( + dps->specifiers_type); + an_expr_node_ptr expr = decltype_arg(typeof_type); + if (expr != NULL) { + a_statement_ptr stmt = add_statement_at_stmt_pos( + stmk_expr, + &pos_curr_token); + stmt->expr = copy_expr_tree(expr, CE_NO_OPTIONS); + set_expr_result_not_used(stmt->expr); + } /* if */ + } /* if */ } /* if */ add_stop_token(tok_comma); dps->need_comma_remove_stop_token = TRUE; diff --git a/src/expr.c b/src/expr.c index 67283dbc3a..22a7753490 100644 --- a/src/expr.c +++ b/src/expr.c @@ -19143,6 +19143,39 @@ called from outside of the expression-processing routines. } /* scan_type_returning_type_trait_operator */ +an_expr_node_ptr take_typeof_operands(a_type_ptr tp) +/* +Extract evaluated typeof operands before enclosing declarator bounds. +Typedef uses do not repeat evaluation of their defining type specifier. +*/ +{ + an_expr_node_ptr inits = NULL; + + while (is_variably_modified_type(tp)) { + if (tp->kind == (a_type_kind)tk_typeref) { + if (typeref_is_typedef(tp)) break; + if (tp->variant.typeref.typeof_operand_pending) { + an_expr_node_ptr expr = decltype_arg(tp); + check_assertion(expr != NULL); + tp->variant.typeref.typeof_operand_pending = FALSE; + if (expr->is_lvalue) expr = add_address_of_to_node(expr); + inits = inits == NULL ? expr : make_comma_node(inits, expr); + } /* if */ + tp = tp->variant.typeref.type; + } else if (tp->kind == (a_type_kind)tk_pointer) { + tp = tp->variant.pointer.type; + } else if (tp->kind == (a_type_kind)tk_array) { + tp = tp->variant.array.element_type; + } else if (tp->kind == (a_type_kind)tk_routine) { + tp = tp->variant.routine.return_type; + } else { + break; + } /* if */ + } /* while */ + return inits; +} /* take_typeof_operands */ + + a_type_ptr scan_typeof_operator(a_rescan_control_block *rcblock, ARG_UNUSED a_decl_pos_block *decl_pos_block) /* @@ -19189,6 +19222,12 @@ the expression-processing routines. an_expr_node_ptr saved_decltype_rescan_operand = NULL; a_boolean is_unqual_variant = curr_token == tok_typeof_unqual; + a_boolean runtime_operand = FALSE; + a_boolean cache_operand = C_mode() && vla_enabled && + rcblock == NULL; + a_token_sequence_number first_operand_token = NO_TOKEN_SEQUENCE_NUMBER; + unsigned long saved_error_count = + diagnostic_counters.total.errors; parens_optional = gpp_mode && gnu_version >= 30400; /* Note that, unlike e.g. sizeof, typeof can appear directly in a declarative @@ -19253,8 +19292,13 @@ the expression-processing routines. } /* if */ } /* if */ } /* if */ +reparse_typeof_operand: if (!is_type) { /* typeof(expression) case. */ + if (cache_operand && !runtime_operand) { + first_operand_token = curr_token_sequence_number; + begin_caching_fetched_tokens(/*include_curr_token=*/TRUE); + } /* if */ /* If we're in the file-scope memory region instead of a function-scope memory region because we're scanning something like a template argument, switch back. If we're in a function, any expression nodes allocated @@ -19264,7 +19308,9 @@ the expression-processing routines. ®ion_to_switch_back_to, &saved_object_lifetime); save_expr_stack(&saved_expr_stack); - push_expr_stack_with_rcblock((an_expression_kind)ek_sizeof, + push_expr_stack_with_rcblock( + runtime_operand ? ek_normal : + ek_sizeof, &expr_stack_entry, /*force_object_lifetime=*/FALSE, /*suppress_object_lifetime=*/ @@ -19348,6 +19394,29 @@ the expression-processing routines. result = make_unqualified_type(result); } /* if */ } /* if */ + if (cache_operand && !runtime_operand) { + end_caching_fetched_tokens(); + if (is_variably_modified_type(result) && + diagnostic_counters.total.errors == saved_error_count) { + a_token_cache operand_tokens; + + clear_token_cache(&operand_tokens, /*reusable=*/FALSE); + copy_tokens_from_cache(curr_lexical_state_cache(), first_operand_token, + curr_token_sequence_number, + /*include_last_token=*/FALSE, &operand_tokens); + expr_stack->unevaluated_expr_will_be_kept_in_il = FALSE; + pop_expr_stack(); + restore_expr_stack(saved_expr_stack); + switch_back_region_and_lifetime(region_to_switch_back_to, + saved_object_lifetime); +#if GENERATE_SOURCE_SEQUENCE_LISTS + ssep = NULL; +#endif /* GENERATE_SOURCE_SEQUENCE_LISTS */ + runtime_operand = TRUE; + rescan_cached_tokens(&operand_tokens); + goto reparse_typeof_operand; + } /* if */ + } /* if */ } /* if */ if (is_error_type(result)) { /* We'll just return the error type. */ @@ -19378,6 +19447,20 @@ the expression-processing routines. prep_generic_operand(&operand); } /* if */ expr = make_node_from_operand(&operand); + if (runtime_operand) { + if (saved_expr_stack == NULL) { + /* Declarative contexts evaluate the type specifier before any + following declarator bounds or initializers. Expression + contexts defer evaluation to VLA lowering instead. */ + a_statement_ptr stmt = add_statement_at_stmt_pos( + stmk_expr, + &operand.position); + stmt->expr = expr; + set_expr_result_not_used(expr); + } else { + typeof_type->variant.typeref.typeof_operand_pending = TRUE; + } /* if */ + } /* if */ if (!dependent_arg) { /* Check for cases where the result type is not dependent but the expression is instantiation-dependent. */ @@ -30469,6 +30552,16 @@ just an expression in parentheses. Return the scanned expression in &type_position, type_cast_to); } /* if */ + if (C_mode() && !err && is_variably_modified_type(type_cast_to)) { + an_expr_node_ptr type_inits = take_typeof_operands(type_cast_to); + if (type_inits != NULL) { + /* Preserve the execution point before conditional folding. */ + make_expression_operand( + make_comma_node(type_inits, + make_node_from_operand(result)), result); + rule_out_expr_kinds(ROEK_INTEGRAL_CONSTANT, result); + } /* if */ + } /* if */ } /* if */ set_operand_position(result, &start_position, &end_position, &start_position); diff --git a/src/expr.h b/src/expr.h index ab79604b05..decd3324f6 100644 --- a/src/expr.h +++ b/src/expr.h @@ -927,6 +927,7 @@ extern a_type_ptr scan_pack_index_type_specifier( extern a_type_ptr scan_typeof_operator(a_rescan_control_block *rcblock, a_decl_pos_block *decl_pos_block); +extern an_expr_node_ptr take_typeof_operands(a_type_ptr tp); #if GNU_EXTENSIONS_ALLOWED diff --git a/src/il_alloc.c b/src/il_alloc.c index 0d590dfa02..94024114dd 100644 --- a/src/il_alloc.c +++ b/src/il_alloc.c @@ -2157,6 +2157,7 @@ to default values. pte->variant.typeref.explicit_memory_attribute_made_implicit = FALSE; #endif /* NEAR_AND_FAR_ALLOWED */ pte->variant.typeref.has_variably_modified_type = FALSE; + pte->variant.typeref.typeof_operand_pending = FALSE; #if LOWER_VARIABLE_LENGTH_ARRAYS pte->variant.typeref.is_lowered_variably_modified_type = FALSE; #endif /* LOWER_VARIABLE_LENGTH_ARRAYS */ diff --git a/src/il_def.h b/src/il_def.h index 43470cfc6b..b570059fc5 100644 --- a/src/il_def.h +++ b/src/il_def.h @@ -10774,6 +10774,10 @@ typedef struct a_type { has_variably_modified_type:1; /* The type referred to is a variably modified type, i.e., is or contains a VLA type. */ + a_bit_field + typeof_operand_pending:1; + /* A C typeof operand of variably modified type must + be evaluated at this type's point of use. */ #if LOWER_VARIABLE_LENGTH_ARRAYS a_bit_field is_lowered_variably_modified_type:1; diff --git a/src/il_display.c b/src/il_display.c index 9145ed239d..f5bc605151 100644 --- a/src/il_display.c +++ b/src/il_display.c @@ -2738,6 +2738,9 @@ Display the indicated type entry. if (ptr->variant.typeref.has_variably_modified_type) { disp_boolean("has_variably_modified_type", TRUE); } /* if */ + if (ptr->variant.typeref.typeof_operand_pending) { + disp_boolean("typeof_operand_pending", TRUE); + } /* if */ #if BACK_END_IS_CP_GEN_BE if (ptr->variant.typeref.surrounding_name_linkage_state != (a_name_linkage_kind)nlk_none) { diff --git a/src/lower_c99.c b/src/lower_c99.c index a6005bade3..68ddd8de36 100644 --- a/src/lower_c99.c +++ b/src/lower_c99.c @@ -433,6 +433,10 @@ variables also make indexing into the VLA arrays more efficient. an_expr_node_ptr inits = NULL, accums = NULL; a_type_ptr ptrdiff_type = integer_type(targ_ptrdiff_t_int_kind); + inits = take_typeof_operands(tp); + if (inits != NULL) { + lower_any_expr(inits); + } /* if */ /* There may be multiple multilevel VLA components in a type. For example: int (*const (a[n][n]))[m][m]; The outer loop deals with that possibility. */ @@ -699,6 +703,11 @@ memory for it. */ { a_variable_ptr vla_var; + an_expr_node_ptr type_inits; + a_type_ptr declared_type = stmt->variant.vla.is_typedef_decl ? + stmt->variant.vla.variant.typedef_type->variant.typeref.type : + stmt->variant.vla.variant.variable->type; + type_inits = lower_vla_dimensions(declared_type); if (stmt->variant.vla.is_typedef_decl) { vla_var = NULL; @@ -710,6 +719,10 @@ memory for it. if (vla_var != NULL && vla_var->is_vla) { stmt->expr = make_vla_allocation_expr(vla_var); } /* if */ + if (type_inits != NULL) { + stmt->expr = stmt->expr == NULL ? type_inits : + make_comma_node(type_inits, stmt->expr); + } /* if */ if (stmt->expr == NULL) { /* This may happen for simple VLA typedefs. Create a dummy expression. */ stmt->expr = node_for_host_large_integer((a_host_large_integer)0, diff --git a/src/pending_changes/edg-28891-typeof-evaluation.txt b/src/pending_changes/edg-28891-typeof-evaluation.txt new file mode 100644 index 0000000000..34b3e002b7 --- /dev/null +++ b/src/pending_changes/edg-28891-typeof-evaluation.txt @@ -0,0 +1,6 @@ +[GH #457] +Evaluate GNU C typeof operands of variably modified type + +GNU C typeof operands of variably modified type are now evaluated. The +evaluation is retained in declarations and expressions without repeating it +when a typedef naming the resulting type is used. diff --git a/tests/tests/regressions/edg-28891.sft.c b/tests/tests/regressions/edg-28891.sft.c new file mode 100644 index 0000000000..f12c2c3368 --- /dev/null +++ b/tests/tests/regressions/edg-28891.sft.c @@ -0,0 +1,26 @@ +//type:rp +//options_all:--c --c11 --no_strict_gnu --gnu_version=130300 +int main(void) { + int k = 1, a[8], fails = 0; + typeof (++k, (int (*)[k])a) p; + (void)sizeof(p); + fails += k != 2; + // Each declarator evaluates the type specifier once. + typeof (++k, (int (*)[k])a) q, r; + (void)sizeof(q); (void)sizeof(r); + fails += k != 4; + typedef typeof (++k, (int (*)[k])a) T; + fails += k != 5; + T t; + (void)sizeof(t); + fails += k != 5; // A typedef use must not repeat its defining expression. + typeof (++k, (int*)a) ordinary; + (void)sizeof(ordinary); + fails += k != 5; // A non-variably-modified operand is unevaluated. + (void)(typeof (++k, (int (*)[k])a))a; + fails += k != 6; + (void)sizeof(typeof (++k, (int (*)[k])a)); + // sizeof a pointer does not require evaluating its variably modified type. + // GNU C is permitted to omit that evaluation, so do not constrain k here. + return fails; +}