Repository navigation
Evaluate GNU C typeof operands of variably modified type #457
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Give copied VLA bounds independent evaluations. For
|
||
| set_expr_result_not_used(stmt->expr); | ||
| } /* if */ | ||
| } /* if */ | ||
| } /* if */ | ||
| add_stop_token(tok_comma); | ||
| dps->need_comma_remove_stop_token = TRUE; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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); | ||
|
Comment on lines
+19451
to
+19457
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| 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); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| /* 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; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pass the required third argument.
add_statement_at_stmt_posrequirescompiler_generated; it has no default argument. This call prevents the front end from building. The new call inscan_typeof_operatorinsrc/expr.cneeds the same fix. (raw.githubusercontent.com)