Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions src/expr.c
Original file line number Diff line number Diff line change
Expand Up @@ -19143,6 +19143,43 @@ 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)
/*
tp is a variably modified type used in an expression (e.g., as the type of a
cast). Return the operands of any C typeof operators in tp whose evaluation
is still pending (see typeof_operand_pending), combined with comma operators,
or NULL if there are none, and mark them as no longer pending. A typedef in
tp stops the search: a use of a typedef does not repeat the evaluation of the
typeof operands in its definition.
*/
{
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)
/*
Expand Down Expand Up @@ -19189,6 +19226,10 @@ the expression-processing routines.
an_expr_node_ptr saved_decltype_rescan_operand = NULL;
a_boolean is_unqual_variant =
curr_token == tok_typeof_unqual;
#if DO_IL_LOWERING
unsigned long saved_error_count =
diagnostic_counters.total.errors;
#endif /* DO_IL_LOWERING */

parens_optional = gpp_mode && gnu_version >= 30400;
/* Note that, unlike e.g. sizeof, typeof can appear directly in a declarative
Expand Down Expand Up @@ -19378,6 +19419,45 @@ the expression-processing routines.
prep_generic_operand(&operand);
} /* if */
expr = make_node_from_operand(&operand);
#if DO_IL_LOWERING
if (C_mode() && vla_enabled && rcblock == NULL &&
is_variably_modified_type(result) &&
diagnostic_counters.total.errors == saved_error_count &&
node_has_side_effects(expr, (a_boolean *)NULL) &&
!type_has_side_effects(result)) {
/* The operand has a variably modified type, so it is evaluated
(C23 6.7.3.6). As for sizeof, the expression scanned above (as
an unevaluated operand) is kept and evaluated at run time. This
is needed only when the IL is lowered: a back end that generates
source (e.g., the C++-generating back end) reproduces the typeof
itself, and the compiler of that source evaluates its operand.
The dimension expressions of the resulting type are evaluated
wherever that type is used, so the operand is evaluated only if
it has side effects of its own and those dimensions have none
(e.g., "typeof(++n, (int (*)[n])p)"); otherwise the evaluation
of a dimension expression would be repeated. */
if (saved_expr_stack != NULL) {
/* Expression contexts defer evaluation to VLA lowering. */
typeof_type->variant.typeref.typeof_operand_pending = TRUE;
} else if (depth_stmt_stack >= 0 &&
!scope_is(&scope_stack_top(), sck_func_prototype)) {
/* A declaration in a block evaluates the type specifier once,
before any declarator bounds or initializers, even if the
declaration has several declarators. */
a_statement_ptr stmt = add_statement_at_stmt_pos(
stmk_expr,
&operand.position,
/*compiler_generated=*/TRUE);
stmt->expr = expr;
set_expr_result_not_used(expr);
} else {
/* A parameter declaration (or a declaration outside of any
function, which is an error for a variably modified type).
No statement can be added here, so the operand is not
evaluated. */
} /* if */
} /* if */
#endif /* DO_IL_LOWERING */
if (!dependent_arg) {
/* Check for cases where the result type is not dependent but the
expression is instantiation-dependent. */
Expand Down Expand Up @@ -30469,6 +30549,18 @@ just an expression in parentheses. Return the scanned expression in
&type_position,
type_cast_to);
} /* if */
#if DO_IL_LOWERING
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 */
#endif /* DO_IL_LOWERING */
} /* if */
set_operand_position(result, &start_position, &end_position,
&start_position);
Expand Down
1 change: 1 addition & 0 deletions src/expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions src/il_alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
6 changes: 6 additions & 0 deletions src/il_def.h
Original file line number Diff line number Diff line change
Expand Up @@ -10774,6 +10774,12 @@ 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;
/* The typeof operand (in C) has a variably modified
type, so it must be evaluated where this type is
used in an expression (e.g., in a cast). Set to
FALSE once that evaluation has been emitted. */
#if LOWER_VARIABLE_LENGTH_ARRAYS
a_bit_field
is_lowered_variably_modified_type:1;
Expand Down
3 changes: 3 additions & 0 deletions src/il_display.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
13 changes: 13 additions & 0 deletions src/lower_c99.c
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down Expand Up @@ -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;
Expand All @@ -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,
Expand Down
13 changes: 13 additions & 0 deletions src/pending_changes/edg-28891-typeof-evaluation.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[GH #457]
GNU C: typeof operands of variably modified type were not evaluated

When the operand of a GNU C typeof (or C23 typeof) operator has a variably
modified type, the operand must be evaluated. The front end treated it as
unevaluated, so its side effects were lost. The operand is now evaluated
once where the declaration or cast appears (not once per declarator, and not
again when a typedef for the type is used). For example, with --gnu_version:

int n = 1, a[8];
void f(void) {
typeof (++n, (int (*)[n])a) p, q; // n is now 2.
}
1 change: 1 addition & 0 deletions tests/expectations/edg_x86_32_cp/regressions.log
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
edg-28891.sft.c:rp:-DTEST_NUMBER=1:REQUIREMENTS NOT MET:
gh-183-redecl.sft.cpp:fp:-DTEST_NUMBER=1 --c++17:REQUIREMENTS NOT MET:
gh-309-diag.sft.cpp:fp:-DTEST_NUMBER=1 --g++:REQUIREMENTS NOT MET:Diagnostic position of implicit destructor calls at scope exit
gh-309-diag.sft.cpp:fp:-DTEST_NUMBER=2 --g++ --no_exceptions:REQUIREMENTS NOT MET:Diagnostic position of implicit destructor calls at scope exit
Expand Down
1 change: 0 additions & 1 deletion tests/expectations/edg_x86_64/imported.log
Original file line number Diff line number Diff line change
Expand Up @@ -3414,7 +3414,6 @@ gnu/c/pr113262.sft.c:fn:-DTEST_NUMBER=1:FAIL:
gnu/c/pr113492.sft.c:fp:-DTEST_NUMBER=1 --c23:FAIL:
gnu/c/pr113596.sft.c:rp:-DTEST_NUMBER=1:FAIL:
gnu/c/pr114090.sft.c:rp:-DTEST_NUMBER=1:FAIL:
gnu/c/pr114831-2.sft.c:rp:-DTEST_NUMBER=1 --c23:FAIL:
gnu/c/pr115109.sft.c:fp:-DTEST_NUMBER=1 --c23:FAIL:
gnu/c/pr115426.sft.c:fp:-DTEST_NUMBER=1 --c11 --strict_gnu:FAIL:
gnu/c/pr116034.sft.c:rp:-DTEST_NUMBER=1:FAIL(COMPILE):
Expand Down
1 change: 1 addition & 0 deletions tests/expectations/edg_x86_64_cp/regressions.log
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
edg-28891.sft.c:rp:-DTEST_NUMBER=1:REQUIREMENTS NOT MET:
gh-183-redecl.sft.cpp:fp:-DTEST_NUMBER=1 --c++17:REQUIREMENTS NOT MET:
gh-309-diag.sft.cpp:fp:-DTEST_NUMBER=1 --g++:REQUIREMENTS NOT MET:Diagnostic position of implicit destructor calls at scope exit
gh-309-diag.sft.cpp:fp:-DTEST_NUMBER=2 --g++ --no_exceptions:REQUIREMENTS NOT MET:Diagnostic position of implicit destructor calls at scope exit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ fe_only -DTEST_NUMBER=1 --gnu_version=999999 -e 200 --no_wrap --no_wrap_diagnost
static int (*a7)[n] = (__typeof__((int (*)[n])sa + m++))sa; /* { dg-error "constant" } */
^

"Test_name.c", line 32: error: expression must have a constant value
static int (*a7)[n] = (__typeof__((int (*)[n])sa + m++))sa; /* { dg-error "constant" } */
^

"Test_name.c", line 26: warning: variable "a1" was declared but never referenced
static int (*a1)[n] = &sa;
^
Expand Down Expand Up @@ -54,4 +58,4 @@ fe_only -DTEST_NUMBER=1 --gnu_version=999999 -e 200 --no_wrap --no_wrap_diagnost
static int (*a7)[n] = (__typeof__((int (*)[n])sa + m++))sa; /* { dg-error "constant" } */
^

6 errors detected in the compilation of "Test_name.c".
7 errors detected in the compilation of "Test_name.c".
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
fe_only -DTEST_NUMBER=1 --gnu_version=999999 -e 200 --no_wrap --no_wrap_diagnostics --gcc --c99 --strict_gnu Test_name.c
"Test_name.c", line 27: error: expression must have a constant value
static int (*a2)[n] = (__typeof__(int (*)[n]))sa;
^
"Test_name.c", line 27: note: the value of parameter "n" (declared at line 24) cannot be used as a constant
static int (*a2)[n] = (__typeof__(int (*)[n]))sa;
^

"Test_name.c", line 28: error: a compound literal is not allowed in an integral constant expression
static int (*a3)[n] = (__typeof__(int (*)[(int){m++}]))sa; /* { dg-error "constant" } */
^

"Test_name.c", line 28: error: this operator is not allowed in a constant expression
static int (*a3)[n] = (__typeof__(int (*)[(int){m++}]))sa; /* { dg-error "constant" } */
^

"Test_name.c", line 29: error: expression must have a constant value
static int (*a4)[n] = (__typeof__((int (*)[n])sa))sa;
^

"Test_name.c", line 30: error: expression must have a constant value
static int (*a5)[n] = (__typeof__((int (*)[m++])sa))sa; /* { dg-error "constant" } */
^

"Test_name.c", line 32: error: expression must have a constant value
static int (*a7)[n] = (__typeof__((int (*)[n])sa + m++))sa; /* { dg-error "constant" } */
^

"Test_name.c", line 26: warning: variable "a1" was declared but never referenced
static int (*a1)[n] = &sa;
^

"Test_name.c", line 27: warning: variable "a2" was declared but never referenced
static int (*a2)[n] = (__typeof__(int (*)[n]))sa;
^

"Test_name.c", line 28: warning: variable "a3" was declared but never referenced
static int (*a3)[n] = (__typeof__(int (*)[(int){m++}]))sa; /* { dg-error "constant" } */
^

"Test_name.c", line 29: warning: variable "a4" was declared but never referenced
static int (*a4)[n] = (__typeof__((int (*)[n])sa))sa;
^

"Test_name.c", line 30: warning: variable "a5" was declared but never referenced
static int (*a5)[n] = (__typeof__((int (*)[m++])sa))sa; /* { dg-error "constant" } */
^

"Test_name.c", line 31: warning: variable "a6" was declared but never referenced
static int (*a6)[n] = (__typeof__((int (*)[100])(int (*)[m++])sa))sa;
^

"Test_name.c", line 32: warning: variable "a7" was declared but never referenced
static int (*a7)[n] = (__typeof__((int (*)[n])sa + m++))sa; /* { dg-error "constant" } */
^

6 errors detected in the compilation of "Test_name.c".
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
fe_only -DTEST_NUMBER=1 --gnu_version=999999 -e 200 --no_wrap --no_wrap_diagnostics --gcc --c99 --strict_gnu Test_name.c
"Test_name.c", line 27: error: expression must have a constant value
static int (*a2)[n] = (__typeof__(int (*)[n]))sa;
^
"Test_name.c", line 27: note: the value of parameter "n" (declared at line 24) cannot be used as a constant
static int (*a2)[n] = (__typeof__(int (*)[n]))sa;
^

"Test_name.c", line 28: error: a compound literal is not allowed in an integral constant expression
static int (*a3)[n] = (__typeof__(int (*)[(int){m++}]))sa; /* { dg-error "constant" } */
^

"Test_name.c", line 28: error: this operator is not allowed in a constant expression
static int (*a3)[n] = (__typeof__(int (*)[(int){m++}]))sa; /* { dg-error "constant" } */
^

"Test_name.c", line 29: error: expression must have a constant value
static int (*a4)[n] = (__typeof__((int (*)[n])sa))sa;
^

"Test_name.c", line 30: error: expression must have a constant value
static int (*a5)[n] = (__typeof__((int (*)[m++])sa))sa; /* { dg-error "constant" } */
^

"Test_name.c", line 32: error: expression must have a constant value
static int (*a7)[n] = (__typeof__((int (*)[n])sa + m++))sa; /* { dg-error "constant" } */
^

"Test_name.c", line 26: warning: variable "a1" was declared but never referenced
static int (*a1)[n] = &sa;
^

"Test_name.c", line 27: warning: variable "a2" was declared but never referenced
static int (*a2)[n] = (__typeof__(int (*)[n]))sa;
^

"Test_name.c", line 28: warning: variable "a3" was declared but never referenced
static int (*a3)[n] = (__typeof__(int (*)[(int){m++}]))sa; /* { dg-error "constant" } */
^

"Test_name.c", line 29: warning: variable "a4" was declared but never referenced
static int (*a4)[n] = (__typeof__((int (*)[n])sa))sa;
^

"Test_name.c", line 30: warning: variable "a5" was declared but never referenced
static int (*a5)[n] = (__typeof__((int (*)[m++])sa))sa; /* { dg-error "constant" } */
^

"Test_name.c", line 31: warning: variable "a6" was declared but never referenced
static int (*a6)[n] = (__typeof__((int (*)[100])(int (*)[m++])sa))sa;
^

"Test_name.c", line 32: warning: variable "a7" was declared but never referenced
static int (*a7)[n] = (__typeof__((int (*)[n])sa + m++))sa; /* { dg-error "constant" } */
^

6 errors detected in the compilation of "Test_name.c".
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
eccp -DTEST_NUMBER=1 --c --c11 --no_strict_gnu --gnu_version=130300 Test_name.c
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./a.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
eccp -DTEST_NUMBER=1 --c --c11 --no_strict_gnu --gnu_version=130300 Test_name.c
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./a.out
1 change: 1 addition & 0 deletions tests/tests/regressions/.edg-28891.rto/default.1.1.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
eccp -DTEST_NUMBER=1 --c --c11 --no_strict_gnu --gnu_version=130300 Test_name.c
1 change: 1 addition & 0 deletions tests/tests/regressions/.edg-28891.rto/default.1.2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
./a.out
11 changes: 11 additions & 0 deletions tests/tests/regressions/edg-28891-params.sft.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//type:rp
//options_all:--c --c11 --no_strict_gnu --gnu_version=130300
// A typeof operand of variably modified type in a block-scope function
// prototype is not evaluated, and must not be inserted as a statement.
int a[8];
int main(void) {
int m = 2;
void local_proto(typeof (++m, (int (*)[m])a) p);
void local_proto2(int n, typeof (++m, (int (*)[m])a) *p, int q[m]);
return m != 2;
}
Loading
Loading