improve constexpr handling
v2 cover improve constexpr handling
This is the second attempt to clean up and improve sparse's handling of constant expressions. After I got some helpful reviews from Josh Triplett and Luc Van Oostenryck on my initial RFC series, I feel quite comfortable with this now and dropped the 'RFC' tag in favour of 'v2'.
Quote from my initial 'RFC' cover letter regarding the structure of this series:
This patch series is split into four parts:
- The first part deals with the refactorization of the current integer constant expression handling and introduces some support for recognizing arithmetic expressions. [1-5/13]
- The second part introduces support for recognizing address constants.
[6/13]
- The third part introduces a check for the constness of static storage duration objects' initializers. [7/13]
- The last part stems from my tests with the kernel. It contains things I missed in the first [9-10/13] and second [8,12/13] parts and relaxes some of the constraints on constant expressions [11/13]. For the last patch [13/13], please see below.
[...]
Although the patches of the fourth part, the fixup part, fit very well into the first two categories, their associated testcases, if any, depend on [7/13]. Thus, I dediced to keep the order as is.
Quote end.
The question from the initial 'RFC' series whether or not to relax the constexpr constraints, meaning that
a difference of address constants may yield an integer constant
in order to make the kernel's ACPI_OFFSET macro happy, is still unaddressed. However, if it turns out that we actually want to do so, this single issue can be easily handled by some follow up patch.
Changes made between v2 and the initial, 'RFC' tagged series:
- As suggested by Josh Triplett,
[7/13] ("check static storage duration objects' intializers' constness") only warns now if -Wstatic-initializer-not-const is given. This option is not enabled by default and thus, the two testsuite failers reported by Luc Van Oostenryck cease to fail.
- Luc Van Oostenryck didn't like the way the setting and clearing of flags is handled. I did not completely follow his advice of introducing predefined sets of masks to or in/and out resp., because setting and clearing is kind of different. However, I tried to address his concerns by changing expr_{set,clear}_flag_mask(...) into expr_{set,clear}_flag(...) whose interfaces are (hopefully) much saner.
Nicolai Stange (13):
expression: introduce additional expression constness tracking flags
expression: examine constness of casts at evaluation only
expression: examine constness of binops and alike at evaluation only
expression: examine constness of preops at evaluation only
expression: examine constness of conditionals at evaluation only expression, evaluate: add support for recognizing address constants
evaluate: check static storage duration objects' intializers'
constness
expression: recognize references to labels as address constants
expression: examine constness of __builtin_offsetof at evaluation only
symbol: flag builtins constant_p, safe_p and warning as constexprs
evaluate: relax some constant expression rules for pointer expressions expression, evaluate: support compound literals as address constants
symbol: do not inherit storage modifiers from base types at
examination
evaluate.c | 188 ++++++++++++++++++++++++-------- expand.c | 2 +- expression.c | 52 ++++----- expression.h | 123 ++++++++++++++++++++- lib.c | 2 + lib.h | 2 +- sparse.1 | 7 ++ symbol.c | 12 +- validation/constexpr-binop.c | 33 ++++++ validation/constexpr-cast.c | 25 +++++ validation/constexpr-compound-literal.c | 19 ++++ validation/constexpr-conditional.c | 34 ++++++ validation/constexpr-init.c | 110 +++++++++++++++++++ validation/constexpr-offsetof.c | 21 ++++ validation/constexpr-preop.c | 29 +++++ 15 files changed, 575 insertions(+), 84 deletions(-) create mode 100644 validation/constexpr-binop.c create mode 100644 validation/constexpr-cast.c create mode 100644 validation/constexpr-compound-literal.c create mode 100644 validation/constexpr-conditional.c create mode 100644 validation/constexpr-init.c create mode 100644 validation/constexpr-offsetof.c create mode 100644 validation/constexpr-preop.c
Signature · 1 line
2.7.0
PATCH v2 1/13 expression: introduce additional expression constness tracking flags
Prepare for a more fine-grained tracking of expression constness in the sense of C99 [6.4.4, 6.6].
User-visible behaviour remains unchanged.
The current implementation tags an expression with either combination of the flags Int_const_expr and Float_literal, the latter being only used to tell that
(int).0
is indeed an integer constant expression.
Even if sparse attempted to verify that initializers for static storage duration objects are constant expressions [6.7.8(4)] (which it currently does not), it could not tell reliably.
Examples:
1.)
static float a = { (float)0 }; /* allowed by C99 */ '(float)0' is not an integer constant expression, but an arithmetic one.
2.)
enum { b = 0 };
static void *c = { (void*)b }; /* disallowed by C99 */ References to enum members are not allowed in address constants [6.6(9)] and thus, the initializer is not a constant expression at all.
Introduce a broader set of constness tracking flags, resembling the four types of constants [6.4.4] (integer, floating, enumeration, character) and the three types of constant expressions [6.6] (integer, arithmetic, address). Use helper functions to consistently set and clear these flags as they are not completely independent. Finally, make alloc_expression() and alloc_const_expression() initialize the new expression's flags to zero.
--- evaluate.c | 72 ++++++++++++++++++++-------------- expand.c | 2 +- expression.c | 67 ++++++++++++++++++++------------ expression.h | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 206 insertions(+), 58 deletions(-)
evaluate.c +44 -28
@@ -400,7 +400,7 @@ static struct symbol *bad_expr_type(struct expression *expr)break ; } - expr->flags =0 ; + expr->flags =EXPR_FLAG_NONE ;return expr->ctype = &bad_ctype; }
@@ -880,8 +880,9 @@ static struct symbol *evaluate_logical(struct expression *expr)/* the result is int [6.5.13(3), 6.5.14(3)] */ expr->ctype = &int_ctype;if (expr->flags) { -if (!(expr->left->flags & expr->right->flags & Int_const_expr)) - expr->flags =0 ; +if (!(expr->left->flags & expr->right->flags & +EXPR_FLAG_INT_CONST_EXPR )) + expr->flags =EXPR_FLAG_NONE ; }return &int_ctype; }
@@ -894,8 +895,9 @@ static struct symbol *evaluate_binop(struct expression *expr)int op = expr->op;if (expr->flags) { -if (!(expr->left->flags & expr->right->flags & Int_const_expr)) - expr->flags =0 ; +if (!(expr->left->flags & expr->right->flags & +EXPR_FLAG_INT_CONST_EXPR )) + expr->flags =EXPR_FLAG_NONE ; }/* number op number */
@@ -986,7 +988,7 @@ static inline int is_null_pointer_constant(struct expression *e) {if (e->ctype == &null_ctype)return 1 ; -if (!(e->flags & Int_const_expr)) +if (!(e->flags &EXPR_FLAG_INT_CONST_EXPR ))return 0 ;return is_zero_constant (e) ?2 :0 ; }
@@ -1001,8 +1003,9 @@ static struct symbol *evaluate_compare(struct expression *expr)const char *typediff;if (expr->flags) { -if (!(expr->left->flags & expr->right->flags & Int_const_expr)) - expr->flags =0 ; +if (!(expr->left->flags & expr->right->flags & +EXPR_FLAG_INT_CONST_EXPR )) + expr->flags =EXPR_FLAG_NONE ; }/* Type types? */
@@ -1120,10 +1123,14 @@ static struct symbol *evaluate_conditional_expression(struct expression *expr) }if (expr->flags) { -int flags = expr->conditional->flags & Int_const_expr; +unsigned char flags_mask =0 ; +unsigned char flags; + +expr_set_flag (&flags_mask,EXPR_FLAG_INT_CONST_EXPR ); + flags = (expr->conditional->flags & flags_mask); flags &= (*true)->flags & expr->cond_false->flags;if (!flags) - expr->flags =0 ; + expr->flags =EXPR_FLAG_NONE ; } lclass =classify_type (ltype, <ype);
@@ -1681,7 +1688,7 @@ static struct symbol *evaluate_addressof(struct expression *expr) } ctype = op->ctype; *expr = *op->unop; - expr->flags =0 ; + expr->flags =EXPR_FLAG_NONE ;if (expr->type ==EXPR_SYMBOL ) {struct symbol *sym = expr->symbol;
@@ -1709,7 +1716,7 @@ static struct symbol *evaluate_dereference(struct expression *expr)/* Simplify: *&(expr) => (expr) */ if (op->type ==EXPR_PREOP && op->op =='&' ) { *expr = *op->unop; - expr->flags =0 ; + expr->flags =EXPR_FLAG_NONE ;return expr->ctype; }
@@ -1799,8 +1806,8 @@ static struct symbol *evaluate_sign(struct expression *expr) {struct symbol *ctype = expr->unop->ctype;int class =classify_type (ctype, &ctype); -if (expr->flags && !(expr->unop->flags & Int_const_expr)) - expr->flags =0 ; +if (expr->flags && !(expr->unop->flags &EXPR_FLAG_INT_CONST_EXPR )) + expr->flags =EXPR_FLAG_NONE ;/* should be an arithmetic type */ if (!(class &TYPE_NUM ))return bad_expr_type (expr);
@@ -1854,8 +1861,9 @@ static struct symbol *evaluate_preop(struct expression *expr)return evaluate_postop (expr);case '!' : -if (expr->flags && !(expr->unop->flags & Int_const_expr)) - expr->flags =0 ; +if (expr->flags && !(expr->unop->flags & +EXPR_FLAG_INT_CONST_EXPR )) + expr->flags =EXPR_FLAG_NONE ;if (is_safe_type (ctype))warning (expr->pos,"testing a 'safe expression'" );if (is_float_type (ctype)) {
@@ -2734,14 +2742,18 @@ static struct symbol *evaluate_cast(struct expression *expr) class1 =classify_type (ctype, &t1); -/* cast to non-integer type -> not an integer constant expression */ -if (!is_int (class1)) - expr->flags =0 ; +/* cast to non-numeric type -> not an arithmetic expression */ +if (!(class1 &TYPE_NUM )) +expr_clear_flag (&expr->flags,EXPR_FLAG_ARITH_CONST_EXPR ); +/* cast to float type -> not an integer constant expression */ +else if (class1 &TYPE_FLOAT ) +expr_clear_flag (&expr->flags,EXPR_FLAG_INT_CONST_EXPR );/* if argument turns out to be not an integer constant expression *and* it was not a floating literal to start with -> too bad */ -else if (expr->flags == Int_const_expr && - !(target->flags & Int_const_expr)) - expr->flags =0 ; +else if (!(target->flags & + (EXPR_FLAG_INT_CONST_EXPR |EXPR_FLAG_FP_CONST ))) +expr_clear_flag (&expr->flags,EXPR_FLAG_INT_CONST_EXPR ); +/* * You can always throw a value away by casting to * "void" - that's an implicit "force". Note that
@@ -2803,7 +2815,7 @@ static struct symbol *evaluate_cast(struct expression *expr) "cast adds address space to expression (<asn:%d>)", as1);if (!(t1->ctype.modifiers &MOD_PTRINHERIT ) && class1 ==TYPE_PTR && - !as1 && (target->flags & Int_const_expr)) { + !as1 && (target->flags &EXPR_FLAG_INT_CONST_EXPR )) {if (t1->ctype.base_type == &void_ctype) {if (is_zero_constant (target)) {/* NULL */
@@ -2933,7 +2945,8 @@ static struct symbol *evaluate_offsetof(struct expression *expr) } ctype = field; expr->type =EXPR_VALUE ; - expr->flags = Int_const_expr; + expr->flags =EXPR_FLAG_NONE ; +expr_set_flag (&expr->flags,EXPR_FLAG_INT_CONST_EXPR ); expr->value = offset; expr->taint =0 ; expr->ctype = size_t_ctype;
@@ -2951,7 +2964,8 @@ static struct symbol *evaluate_offsetof(struct expression *expr) ctype = ctype->ctype.base_type;if (!expr->index) { expr->type =EXPR_VALUE ; - expr->flags = Int_const_expr; + expr->flags =EXPR_FLAG_NONE ; +expr_set_flag (&expr->flags,EXPR_FLAG_INT_CONST_EXPR ); expr->value =0 ; expr->taint =0 ; expr->ctype = size_t_ctype;
@@ -2968,24 +2982,26 @@ static struct symbol *evaluate_offsetof(struct expression *expr) m =alloc_const_expression (expr->pos,bits_to_bytes (ctype->bit_size)); m->ctype = size_t_ctype; - m->flags = Int_const_expr; +expr_set_flag (&m->flags,EXPR_FLAG_INT_CONST_EXPR ); expr->type =EXPR_BINOP ; expr->left = idx; expr->right = m; expr->op ='*' ; expr->ctype = size_t_ctype; - expr->flags = m->flags & idx->flags & Int_const_expr; + expr->flags = m->flags & idx->flags; } }if (e) {struct expression *copy =__alloc_expression (0 ); +unsigned char flags_mask =EXPR_FLAG_NONE ; *copy = *expr;if (e->type ==EXPR_OFFSETOF ) e->in = ctype;if (!evaluate_expression (e))return NULL ; expr->type =EXPR_BINOP ; - expr->flags = e->flags & copy->flags & Int_const_expr; +expr_set_flag (&flags_mask,EXPR_FLAG_INT_CONST_EXPR ); + expr->flags = e->flags & copy->flags & flags_mask; expr->op ='+' ; expr->ctype = size_t_ctype; expr->left = copy;
expand.c +1 -1
@@ -1212,7 +1212,7 @@ static int expand_statement(struct statement *stmt)static inline int bad_integer_constant_expression (struct expression *expr) { -if (!(expr->flags & Int_const_expr)) +if (!(expr->flags &EXPR_FLAG_INT_CONST_EXPR ))return 1 ;if (expr->taint & Taint_comma)return 1 ;
expression.c +43 -24
164 lines not on this page · raw message
expression.h +118 -5
147 lines not on this page · raw message
Signature · 1 line
2.7.0
Re: [PATCH v2 01/13] expression: introduce additional expression constness tracking flags
Quoted 2 lines · Nicolai Stange
Prepare for a more fine-grained tracking of expression constness in the sense of C99 [6.4.4, 6.6].
This part could be moved at the end of the patch description
Quoted 7 lines
User-visible behaviour remains unchanged.
The current implementation tags an expression with either combination of the flags
Int_const_exprandFloat_literal, the latter being only used to tell that
(int).0
is indeed an integer constant expression.
This part should be dropped, I think, and maybe moved to the cover letter.
Quoted 6 lines
Even if sparse attempted to verify that initializers for static storage duration objects are constant expressions [6.7.8(4)] (which it currently does not), it could not tell reliably.
Examples:
1.)
static float a = { (float)0 }; /* allowed by C99 */
Quoted 8 lines
'(float)0' is not an integer constant expression, but an arithmetic one.
2.)
enum { b = 0 };
static void *c = { (void*)b }; /* disallowed by C99 */ References to enum members are not allowed in address constants [6.6(9)] and thus, the initializer is not a constant expression at all.
This could be moved to the top of the patch description
Quoted 7 lines
Introduce a broader set of constness tracking flags, resembling the four types of constants [6.4.4] (integer, floating, enumeration, character) and the three types of constant expressions [6.6] (integer, arithmetic, address). Use helper functions to consistently set and clear these flags as they are not completely independent. Finally, make
alloc_expression()andalloc_const_expression()initialize the new expression's flags to zero.
I think this patch should be split into 2-4 smaller patches:
0) introduce the new flags, maybe first only NONE, INT_EXPR & FP_CONST
1) replace the old use of 0, Int_const_expr, ... (can be folded with 0)
2) initialize the flag in alloc_expression() (can be folded with 1)..
3) introduce the the new flags if needed, the expr_..._flag() helpers and begin them
expression.c static struct token *parse_type(struct token *token, struct expression **tree) · line 134
Quoted 8 lines
expression.c +1
-1@@ -131,7 +131,7 @@ static struct token *parse_type(struct token *token, struct expression **tree) {struct symbol *sym; *tree =alloc_expression (token->pos,EXPR_TYPE ); - (*tree)->flags = Int_const_expr;/* sic */ +expr_set_flag (&(*tree)->flags,EXPR_FLAG_INT_CONST_EXPR );/* sic */
Better to remove those 'sic'. To which text do they refer?
expression.c struct token *primary_expression(struct token *token, struct expression **tree) · line 463
Quoted 7 lines
expression.c +2
-1@@ -457,7 +459,8 @@ struct token *primary_expression(struct token *token, struct expression **tree) }if (token->special =='[' &&lookup_type (token->next)) { expr =alloc_expression (token->pos,EXPR_TYPE ); - expr->flags = Int_const_expr;/* sic */ +/* sic */ +expr_set_flag (&expr->flags,EXPR_FLAG_INT_CONST_EXPR );
Ditto for the 'sic'
Quoted 69 lines
expression.c +69
-0+ +/* +* Set a particular flag among an expression's ones. +* +* The set of flags defined is not completely independent. Take care +* that implications keep obeyed. +* +* Only one single flag from enum expression_flags is allowed for +* the flag argument at a time. +*/ +static inline void expr_set_flag (unsigned char *const flags, +enum expression_flags flag) +{ +/* obey the implications */ +switch (flag) { +case EXPR_FLAG_INT_CONST : +case EXPR_FLAG_ENUM_CONST : +case EXPR_FLAG_CHAR_CONST : + flag |=EXPR_FLAG_INT_CONST_EXPR ; +/* fallthrough */ +case EXPR_FLAG_FP_CONST : +case EXPR_FLAG_INT_CONST_EXPR : + flag |=EXPR_FLAG_ARITH_CONST_EXPR ; +/* fallthrough */ +case EXPR_FLAG_ARITH_CONST_EXPR : +case EXPR_FLAG_ADDR_CONST_EXPR : +case EXPR_FLAG_NONE : +break ; + } + + *flags |= flag; +} + +/* +* Clear a particular flag among an expression's ones. +* +* The set of flags defined is not completely independent. Take care +* that implications keep obeyed. +* +* Only one single flag from enum expression_flags is allowed for +* the flag argument at a time. +*/ +static inline void expr_clear_flag (unsigned char *const flags, +enum expression_flags flag) +{ +/* obey the implications */ +switch (flag) { +case EXPR_FLAG_ARITH_CONST_EXPR : + flag |=EXPR_FLAG_INT_CONST_EXPR ; + flag |=EXPR_FLAG_FP_CONST ; +/* fallthrough */ +case EXPR_FLAG_INT_CONST_EXPR : + flag |=EXPR_FLAG_INT_CONST ; + flag |=EXPR_FLAG_ENUM_CONST ; + flag |=EXPR_FLAG_CHAR_CONST ; +/* fallthrough */ +case EXPR_FLAG_ADDR_CONST_EXPR : +case EXPR_FLAG_INT_CONST : +case EXPR_FLAG_FP_CONST : +case EXPR_FLAG_ENUM_CONST : +case EXPR_FLAG_CHAR_CONST : +case EXPR_FLAG_NONE : +break ; + } + + *flags &= ~flag; +} + +/*
expression.c struct token *primary_expression(struct token *token, struct expression **tree)
Quoted 10 lines
expression.c +10
-0+* Remove any "Constant" [6.4.4] flag, but retain the "constant +* expression" [6.6] flags. +* Used to merge the constantness flags of primary subexpressions +* into their parent expressions' ones. +*/ +static inline void expr_flags_decay_consts (unsigned char *const flags) +{ + *flags &= ~(EXPR_FLAG_INT_CONST |EXPR_FLAG_FP_CONST | +EXPR_FLAG_ENUM_CONST |EXPR_FLAG_CHAR_CONST ); +}
I think it's already much better so!
But honestly, I still think that it could be improved:
- the fact that it need a pointer for the update, means that it can only be used with a specific type, here unsigned char, and not arbitrary expressions
- expr_flags_decay_consts(flags) could be simply be replaced by something like:
#define EXPR_FLAG_CONSTANTS (EXPR_FLAG_INT_CONST|EXPR_FLAG_FP_CONST|...) flags &= ~EXPR_FLAG_CONSTANTS;
- expr_clear_flag() is only used 5 times:
-) 4 times with ADDR_CONST_EXPR where it can be replaced by a simple &= ~ -) once with INT_CONST_EXPR
So, with only a few defines for the set/union, one for the 'clear' and one one for the decay you can get rid of these helpers wich IMO improve readability and is consistent that elsewhere in the code there is anyway manipulations of expr->flags done simply with '|' and '&'.
expression.c enum { · line 191
Quoted 8 lines
enum {
expression.c +1
-1Taint_comma = 1,@@ -77,7 +188,7 @@ enum {struct expression {enum expression_type type:8 ; -unsigned flags:8 ; +unsigned char flags;
I suppose that initialy this 'flags' was foreseen for other things than 'Int_const_expr' & 'Float_literal' but now I think it should be better to rename it with something more explicit, like 'constness' or something (same then for the EXPR_FLAG_... of course).
Luc
Re: [PATCH v2 01/13] expression: introduce additional expression constness tracking flags
Quoted 5 lines · Luc Van Oostenryck
Quoted 2 lines · Nicolai Stange
Prepare for a more fine-grained tracking of expression constness in the sense of C99 [6.4.4, 6.6].
This part could be moved at the end of the patch description
Yes.
Quoted 10 lines
Quoted 7 lines
User-visible behaviour remains unchanged.
The current implementation tags an expression with either combination of the flags
Int_const_exprandFloat_literal, the latter being only used to tell that
(int).0
is indeed an integer constant expression.This part should be dropped, I think, and maybe moved to the cover letter.
I included this description of the current state of the art in order to make the problem addressed by this patch very clear.
Maybe you're right and my problem statement is too much blah blah, i.e. the paragraph immediately following would suffice.
Quoted 15 lines
Quoted 6 lines
Even if sparse attempted to verify that initializers for static storage duration objects are constant expressions [6.7.8(4)] (which it currently does not), it could not tell reliably.
Examples:
1.)
static float a = { (float)0 }; /* allowed by C99 */Quoted 8 lines
'(float)0' is not an integer constant expression, but an arithmetic one.
2.)
enum { b = 0 };
static void *c = { (void*)b }; /* disallowed by C99 */ References to enum members are not allowed in address constants [6.6(9)] and thus, the initializer is not a constant expression at all.
Quoted 1 line
This could be moved to the top of the patch description
Yes.
Quoted 8 lines
Quoted 7 lines
Introduce a broader set of constness tracking flags, resembling the four types of constants [6.4.4] (integer, floating, enumeration, character) and the three types of constant expressions [6.6] (integer, arithmetic, address). Use helper functions to consistently set and clear these flags as they are not completely independent. Finally, make
alloc_expression()andalloc_const_expression()initialize the new expression's flags to zero.
Quoted 6 lines
I think this patch should be split into 2-4 smaller patches:
0) introduce the new flags, maybe first only NONE,INT_EXPR&FP_CONST
1) replace the old use of 0,Int_const_expr, ... (can be folded with 0)
2) initialize the flag inalloc_expression()(can be folded with 1)..
3) introduce the the new flags if needed, the expr_..._flag() helpers and begin them
I'll drop the expr_..._flag() helpers as recommended by you (see below) and introduce the additional flags (arithm. constness, addr constants, ?) as needed in later patches.
Thus, no split of this patch since 3.) would be addressed by deferring the introduction of additional flags and 1)-3) "can be folded" anyway.
Quoted 12 lines
expression.c static struct token *parse_type(struct token *token, struct expression **tree) · line 134
Quoted 9 lines
expression.c +1
-1@@ -131,7 +131,7 @@ static struct token *parse_type(struct token *token, struct expression **tree) {struct symbol *sym; *tree =alloc_expression (token->pos,EXPR_TYPE ); - (*tree)->flags = Int_const_expr;/* sic */ +expr_set_flag (&(*tree)->flags,EXPR_FLAG_INT_CONST_EXPR );/* sic */ Better to remove those 'sic'. To which text do they refer?
Quoted 9 lines
expression.c struct token *primary_expression(struct token *token, struct expression **tree) · line 463
Quoted 7 lines
expression.c +2
-1@@ -457,7 +459,8 @@ struct token *primary_expression(struct token *token, struct expression **tree) }if (token->special =='[' &&lookup_type (token->next)) { expr =alloc_expression (token->pos,EXPR_TYPE ); - expr->flags = Int_const_expr;/* sic */ +/* sic */ +expr_set_flag (&expr->flags,EXPR_FLAG_INT_CONST_EXPR );Ditto for the 'sic'
That 'sic' had been introduced in
a24a3adf3f0e ("fix handling of integer constant expressions")
Quote from the commit message:
EXPR_TYPE also gets marked int_const_expr (to make it DTRT on the builtin_same_type_p() et.al.)
Quote end.
It is a hack to turn __builtin_types_compatible_p() into an integer constant expression and works this way:builtin_types_compatible_p_expr() builds an EXPR_COMPARE expression node with its two leafs being the two EXPR_TYPE arguments.
Now, at the evaluation of the EXPR_COMPARE expression, the two leafs should better be flagged constant in order for the EXPR_COMPARE expression to remain constant.
Remember: before this series, EXPR_COMPARE *removes* constness flags. After this series, it would *add* them, provided the subexpressions, i.e. the EXPR_TYPE leafs are flagged constant.
So yes, since one of the goals of this series is to promote expression constness only in one direction, namely from subexpressions to a parent expression, this gives the opportunity to clean up that sic-thing: don't flag the EXPR_TYPE expressions individually, but the parent __builtin_types_compatible_p() expression.
Nice catch! I'll see where it fits.
Quoted 88 lines
Quoted 69 lines
expression.c +69
-0+ +/* +* Set a particular flag among an expression's ones. +* +* The set of flags defined is not completely independent. Take care +* that implications keep obeyed. +* +* Only one single flag from enum expression_flags is allowed for +* the flag argument at a time. +*/ +static inline void expr_set_flag (unsigned char *const flags, +enum expression_flags flag) +{ +/* obey the implications */ +switch (flag) { +case EXPR_FLAG_INT_CONST : +case EXPR_FLAG_ENUM_CONST : +case EXPR_FLAG_CHAR_CONST : + flag |=EXPR_FLAG_INT_CONST_EXPR ; +/* fallthrough */ +case EXPR_FLAG_FP_CONST : +case EXPR_FLAG_INT_CONST_EXPR : + flag |=EXPR_FLAG_ARITH_CONST_EXPR ; +/* fallthrough */ +case EXPR_FLAG_ARITH_CONST_EXPR : +case EXPR_FLAG_ADDR_CONST_EXPR : +case EXPR_FLAG_NONE : +break ; + } + + *flags |= flag; +} + +/* +* Clear a particular flag among an expression's ones. +* +* The set of flags defined is not completely independent. Take care +* that implications keep obeyed. +* +* Only one single flag from enum expression_flags is allowed for +* the flag argument at a time. +*/ +static inline void expr_clear_flag (unsigned char *const flags, +enum expression_flags flag) +{ +/* obey the implications */ +switch (flag) { +case EXPR_FLAG_ARITH_CONST_EXPR : + flag |=EXPR_FLAG_INT_CONST_EXPR ; + flag |=EXPR_FLAG_FP_CONST ; +/* fallthrough */ +case EXPR_FLAG_INT_CONST_EXPR : + flag |=EXPR_FLAG_INT_CONST ; + flag |=EXPR_FLAG_ENUM_CONST ; + flag |=EXPR_FLAG_CHAR_CONST ; +/* fallthrough */ +case EXPR_FLAG_ADDR_CONST_EXPR : +case EXPR_FLAG_INT_CONST : +case EXPR_FLAG_FP_CONST : +case EXPR_FLAG_ENUM_CONST : +case EXPR_FLAG_CHAR_CONST : +case EXPR_FLAG_NONE : +break ; + } + + *flags &= ~flag; +} + +/* expression.c struct token *primary_expression(struct token *token, struct expression **tree)
Quoted 10 lines
expression.c +10
-0+* Remove any "Constant" [6.4.4] flag, but retain the "constant +* expression" [6.6] flags. +* Used to merge the constantness flags of primary subexpressions +* into their parent expressions' ones. +*/ +static inline void expr_flags_decay_consts (unsigned char *const flags) +{ + *flags &= ~(EXPR_FLAG_INT_CONST |EXPR_FLAG_FP_CONST | +EXPR_FLAG_ENUM_CONST |EXPR_FLAG_CHAR_CONST ); +}I think it's already much better so!
But honestly, I still think that it could be improved:
- the fact that it need a pointer for the update, means that it can only be used with a specific type, here unsigned char, and not arbitrary expressions
-expr_flags_decay_consts(flags)could be simply be replaced by something like:#define EXPR_FLAG_CONSTANTS (EXPR_FLAG_INT_CONST|EXPR_FLAG_FP_CONST|...) flags &= ~EXPR_FLAG_CONSTANTS;
Quoted 8 lines
-
expr_clear_flag()is only used 5 times:
-) 4 times withADDR_CONST_EXPRwhere it can be replaced by a simple &= ~ -) once withINT_CONST_EXPRSo, with only a few defines for the set/union, one for the 'clear' and one one for the decay you can get rid of these helpers wich IMO improve readability and is consistent that elsewhere in the code there is anyway manipulations of
expr->flagsdone simply with '|' and '&'.
To be honest, I don't know exactly why I'm clinging so much to the getters/setters. Certainly because they're pretty much self-documenting while the EXPR_CONSTEXPR_FLAG_INT_CONST #define obviously deserves a detailed comment. That's not really an argument though while your point about the pointer to flags requirement is.
So it would be certainly better to stick to your recommendation.
Your enumeration of the actual expr_{set,clear}_flag() usages made me recognize another point: the current EXPR_FLAG_ARITH_CONST_EXPR is redundant in that it being set is equivalent to either of EXPR_FLAG_FP_CONST and EXPR_FLAG_INT_CONST_EXPR being set. Wouldn't it be better to drop that EXPR_FLAG_ARITH_CONST_EXPR and introduce an #define EXPR_CONSTEXPR_FLAG_ARITH_CONST_MASK \ (EXPR_FLAG_FP_CONST | EXPR_FLAG_INT_CONST_EXPR) ?
Quoted 4 lines
Quoted 4 lines
enum {
expression.c +0
-0Taint_comma = 1,@@ -77,7 +188,7 @@ enum {
Quoted 9 lines
expression.c enum { · line 191
Quoted 4 lines
expression.c +1
-1struct expression {enum expression_type type:8 ; -unsigned flags:8 ; +unsigned char flags;I suppose that initialy this 'flags' was foreseen for other things than '
Int_const_expr' & 'Float_literal' but now I think it should be better to rename it with something more explicit, like 'constness' or something (same then for theEXPR_FLAG_... of course).
Oh thank you! Actually I've thought about this since the beginning. Only I haven't been brave enough...
Will be part of the next iteration of this patch [00/13].
Quoted 3 lines
Luc
Re: [PATCH v2 01/13] expression: introduce additional expression constness tracking flags
Quoted 226 lines · Nicolai Stange
Quoted 5 lines · Luc Van Oostenryck
Quoted 2 lines · Nicolai Stange
Prepare for a more fine-grained tracking of expression constness in the sense of C99 [6.4.4, 6.6].
This part could be moved at the end of the patch description
Yes.
Quoted 10 lines
Quoted 7 lines
User-visible behaviour remains unchanged.
The current implementation tags an expression with either combination of the flags
Int_const_exprandFloat_literal, the latter being only used to tell that
(int).0
is indeed an integer constant expression.This part should be dropped, I think, and maybe moved to the cover letter.
I included this description of the current state of the art in order to make the problem addressed by this patch very clear.
Maybe you're right and my problem statement is too much blah blah, i.e. the paragraph immediately following would suffice.
Quoted 15 lines
Quoted 6 lines
Even if sparse attempted to verify that initializers for static storage duration objects are constant expressions [6.7.8(4)] (which it currently does not), it could not tell reliably.
Examples:
1.)
static float a = { (float)0 }; /* allowed by C99 */Quoted 8 lines
'(float)0' is not an integer constant expression, but an arithmetic one.
2.)
enum { b = 0 };
static void *c = { (void*)b }; /* disallowed by C99 */ References to enum members are not allowed in address constants [6.6(9)] and thus, the initializer is not a constant expression at all.Quoted 1 line
This could be moved to the top of the patch description
Yes.
Quoted 8 lines
Quoted 7 lines
Introduce a broader set of constness tracking flags, resembling the four types of constants [6.4.4] (integer, floating, enumeration, character) and the three types of constant expressions [6.6] (integer, arithmetic, address). Use helper functions to consistently set and clear these flags as they are not completely independent. Finally, make
alloc_expression()andalloc_const_expression()initialize the new expression's flags to zero.Quoted 6 lines
I think this patch should be split into 2-4 smaller patches:
0) introduce the new flags, maybe first only NONE,INT_EXPR&FP_CONST
1) replace the old use of 0,Int_const_expr, ... (can be folded with 0)
2) initialize the flag inalloc_expression()(can be folded with 1)..
3) introduce the the new flags if needed, the expr_..._flag() helpers and begin themI'll drop the expr_..._flag() helpers as recommended by you (see below) and introduce the additional flags (arithm. constness, addr constants, ?) as needed in later patches.
Thus, no split of this patch since 3.) would be addressed by deferring the introduction of additional flags and 1)-3) "can be folded" anyway.
Quoted 12 lines
expression.c static struct token *parse_type(struct token *token, struct expression **tree) · line 134
Quoted 9 lines
expression.c +1
-1@@ -131,7 +131,7 @@ static struct token *parse_type(struct token *token, struct expression **tree) {struct symbol *sym; *tree =alloc_expression (token->pos,EXPR_TYPE ); - (*tree)->flags = Int_const_expr;/* sic */ +expr_set_flag (&(*tree)->flags,EXPR_FLAG_INT_CONST_EXPR );/* sic */ Better to remove those 'sic'. To which text do they refer?
Quoted 9 lines
expression.c struct token *primary_expression(struct token *token, struct expression **tree) · line 463
Quoted 7 lines
expression.c +2
-1@@ -457,7 +459,8 @@ struct token *primary_expression(struct token *token, struct expression **tree) }if (token->special =='[' &&lookup_type (token->next)) { expr =alloc_expression (token->pos,EXPR_TYPE ); - expr->flags = Int_const_expr;/* sic */ +/* sic */ +expr_set_flag (&expr->flags,EXPR_FLAG_INT_CONST_EXPR );Ditto for the 'sic'
That 'sic' had been introduced in
a24a3adf3f0e("fix handling of integer constant expressions")Quote from the commit message:
EXPR_TYPEalso gets markedint_const_expr(to make it DTRT on thebuiltin_same_type_p()et.al.)Quote end.
It is a hack to turn
__builtin_types_compatible_p()into an integer constant expression and works this way:builtin_types_compatible_p_expr()builds anEXPR_COMPAREexpression node with its two leafs being the twoEXPR_TYPEarguments.Now, at the evaluation of the
EXPR_COMPAREexpression, the two leafs should better be flagged constant in order for theEXPR_COMPAREexpression to remain constant.
Remember: before this series,EXPR_COMPARE*removes* constness flags. After this series, it would *add* them, provided the subexpressions, i.e. theEXPR_TYPEleafs are flagged constant.So yes, since one of the goals of this series is to promote expression constness only in one direction, namely from subexpressions to a parent expression, this gives the opportunity to clean up that sic-thing: don't flag the
EXPR_TYPEexpressions individually, but the parent__builtin_types_compatible_p()expression.Nice catch! I'll see where it fits.
Quoted 88 lines
Quoted 69 lines
expression.c +69
-0+ +/* +* Set a particular flag among an expression's ones. +* +* The set of flags defined is not completely independent. Take care +* that implications keep obeyed. +* +* Only one single flag from enum expression_flags is allowed for +* the flag argument at a time. +*/ +static inline void expr_set_flag (unsigned char *const flags, +enum expression_flags flag) +{ +/* obey the implications */ +switch (flag) { +case EXPR_FLAG_INT_CONST : +case EXPR_FLAG_ENUM_CONST : +case EXPR_FLAG_CHAR_CONST : + flag |=EXPR_FLAG_INT_CONST_EXPR ; +/* fallthrough */ +case EXPR_FLAG_FP_CONST : +case EXPR_FLAG_INT_CONST_EXPR : + flag |=EXPR_FLAG_ARITH_CONST_EXPR ; +/* fallthrough */ +case EXPR_FLAG_ARITH_CONST_EXPR : +case EXPR_FLAG_ADDR_CONST_EXPR : +case EXPR_FLAG_NONE : +break ; + } + + *flags |= flag; +} + +/* +* Clear a particular flag among an expression's ones. +* +* The set of flags defined is not completely independent. Take care +* that implications keep obeyed. +* +* Only one single flag from enum expression_flags is allowed for +* the flag argument at a time. +*/ +static inline void expr_clear_flag (unsigned char *const flags, +enum expression_flags flag) +{ +/* obey the implications */ +switch (flag) { +case EXPR_FLAG_ARITH_CONST_EXPR : + flag |=EXPR_FLAG_INT_CONST_EXPR ; + flag |=EXPR_FLAG_FP_CONST ; +/* fallthrough */ +case EXPR_FLAG_INT_CONST_EXPR : + flag |=EXPR_FLAG_INT_CONST ; + flag |=EXPR_FLAG_ENUM_CONST ; + flag |=EXPR_FLAG_CHAR_CONST ; +/* fallthrough */ +case EXPR_FLAG_ADDR_CONST_EXPR : +case EXPR_FLAG_INT_CONST : +case EXPR_FLAG_FP_CONST : +case EXPR_FLAG_ENUM_CONST : +case EXPR_FLAG_CHAR_CONST : +case EXPR_FLAG_NONE : +break ; + } + + *flags &= ~flag; +} + +/* expression.c struct token *primary_expression(struct token *token, struct expression **tree)
Quoted 10 lines
expression.c +10
-0+* Remove any "Constant" [6.4.4] flag, but retain the "constant +* expression" [6.6] flags. +* Used to merge the constantness flags of primary subexpressions +* into their parent expressions' ones. +*/ +static inline void expr_flags_decay_consts (unsigned char *const flags) +{ + *flags &= ~(EXPR_FLAG_INT_CONST |EXPR_FLAG_FP_CONST | +EXPR_FLAG_ENUM_CONST |EXPR_FLAG_CHAR_CONST ); +}I think it's already much better so!
But honestly, I still think that it could be improved:
- the fact that it need a pointer for the update, means that it can only be used with a specific type, here unsigned char, and not arbitrary expressions
-expr_flags_decay_consts(flags)could be simply be replaced by something like:#define EXPR_FLAG_CONSTANTS (EXPR_FLAG_INT_CONST|EXPR_FLAG_FP_CONST|...) flags &= ~EXPR_FLAG_CONSTANTS;Quoted 8 lines
-
expr_clear_flag()is only used 5 times:
-) 4 times withADDR_CONST_EXPRwhere it can be replaced by a simple &= ~ -) once withINT_CONST_EXPRSo, with only a few defines for the set/union, one for the 'clear' and one one for the decay you can get rid of these helpers wich IMO improve readability and is consistent that elsewhere in the code there is anyway manipulations of
expr->flagsdone simply with '|' and '&'.To be honest, I don't know exactly why I'm clinging so much to the getters/setters. Certainly because they're pretty much self-documenting while the
EXPR_CONSTEXPR_FLAG_INT_CONST#define obviously deserves a detailed comment. That's not really an argument though while your point about the pointer to flags requirement is.So it would be certainly better to stick to your recommendation.
Quoted 7 lines
Your enumeration of the actual expr_{set,clear}_flag() usages made me recognize another point: the current
EXPR_FLAG_ARITH_CONST_EXPRis redundant in that it being set is equivalent to either ofEXPR_FLAG_FP_CONSTandEXPR_FLAG_INT_CONST_EXPRbeing set. Wouldn't it be better to drop thatEXPR_FLAG_ARITH_CONST_EXPRand introduce an #defineEXPR_CONSTEXPR_FLAG_ARITH_CONST_MASK\ (EXPR_FLAG_FP_CONST|EXPR_FLAG_INT_CONST_EXPR) ?
Sorry, complete nonsense, forget about that last paragraph.
Counterexample: 0. + 0.
Arithmetic constant expression, but neither a fp literal nor an integer constant expression.
Quoted 23 lines
Quoted 5 lines
Quoted 5 lines
enum {
expression.c +0
-0Taint_comma = 1,@@ -77,7 +188,7 @@ enum {Quoted 9 lines
expression.c enum { · line 191
Quoted 4 lines
expression.c +1
-1struct expression {enum expression_type type:8 ; -unsigned flags:8 ; +unsigned char flags;I suppose that initialy this 'flags' was foreseen for other things than '
Int_const_expr' & 'Float_literal' but now I think it should be better to rename it with something more explicit, like 'constness' or something (same then for theEXPR_FLAG_... of course).Oh thank you! Actually I've thought about this since the beginning. Only I haven't been brave enough...
Will be part of the next iteration of this patch [00/13].
Quoted 3 lines
Luc
PATCH v2 2/13 expression: examine constness of casts at evaluation only
In reply to Nicolai StangeCurrently, the propagation of expressions' constness flags through cast expressions is done in two steps:
- Several flags are speculatively set at cast expression parsing time
- and possibly cleared again at evaluation time.
Set aside this unfortunate split of code, the early propagation of constness flags is not able to recognize constant expressions such as
(int)__builtin_choose_expr(0, 0, 0)
since the final expression to be thrown into the cast is known only after evaluation.
Move the whole calculation of cast expressions' constness flags to the evaluation phase.
Introduce support for tracking arithmetic constness propagation through cast expressions.
Introduce support for recognizing address constants created by casting an integer constant to pointer type.
--- evaluate.c | 42 ++++++++++++++++++++++++++++++------------ expression.c | 19 ------------------- validation/constexpr-cast.c | 25 +++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 31 deletions(-) create mode 100644 validation/constexpr-cast.c
evaluate.c +30 -12
@@ -323,7 +323,6 @@ static struct expression * cast_to(struct expression *old, struct symbol *type) } expr =alloc_expression (old->pos,EXPR_IMPLIED_CAST ); - expr->flags = old->flags; expr->ctype = type; expr->cast_type = type; expr->cast_expression = old;
@@ -2742,17 +2741,36 @@ static struct symbol *evaluate_cast(struct expression *expr) class1 =classify_type (ctype, &t1); -/* cast to non-numeric type -> not an arithmetic expression */ -if (!(class1 &TYPE_NUM )) -expr_clear_flag (&expr->flags,EXPR_FLAG_ARITH_CONST_EXPR ); -/* cast to float type -> not an integer constant expression */ -else if (class1 &TYPE_FLOAT ) -expr_clear_flag (&expr->flags,EXPR_FLAG_INT_CONST_EXPR ); -/* if argument turns out to be not an integer constant expression *and* -it was not a floating literal to start with -> too bad */ -else if (!(target->flags & - (EXPR_FLAG_INT_CONST_EXPR |EXPR_FLAG_FP_CONST ))) -expr_clear_flag (&expr->flags,EXPR_FLAG_INT_CONST_EXPR ); +if (!(class1 &TYPE_NUM )) { +/* +* Casts of integer literals to pointer type yield +* address constants [6.6(9)]. +*/ +if (class1 &TYPE_PTR && + (target->flags &EXPR_FLAG_INT_CONST )) { +expr_set_flag (&expr->flags,EXPR_FLAG_ADDR_CONST_EXPR ); + } + }else { + expr->flags = target->flags; +expr_flags_decay_consts (&expr->flags); +/* +* Casts to numeric types never result in address +* constants [6.6(9)]. +*/ +expr_clear_flag (&expr->flags,EXPR_FLAG_ADDR_CONST_EXPR ); +/* +* Cast to float type -> not an integer constant +* expression [6.6(6)]. +*/ +if (class1 &TYPE_FLOAT ) +expr_clear_flag (&expr->flags,EXPR_FLAG_INT_CONST_EXPR ); +/* +* Casts of float literals to integer type results in +* a constant integer expression [6.6(6)]. +*/ +else if (target->flags &EXPR_FLAG_FP_CONST ) +expr_set_flag (&expr->flags,EXPR_FLAG_INT_CONST_EXPR ); + }/* * You can always throw a value away by casting to
expression.c +0 -19
@@ -725,25 +725,6 @@ static struct token *cast_expression(struct token *token, struct expression **trif (!v)return token; cast->cast_expression = v; - - cast->flags = v->flags; -expr_flags_decay_consts (&cast->flags); -/* -* Up to now, we missed the (int).0 case here -* which should really get a -* EXPR_FLAG_INT_CONST_EXPR marker. Also, -* conversion to non-numeric types is not -* properly reflected up to this point. -* However, we do not know until evaluation. -* For the moment, in order to preserve -* semantics, speculatively set -* EXPR_FLAG_INT_CONST_EXPR if -* EXPR_FLAG_FP_CONST is set. evaluate_cast() -* will unset inappropriate flags again after -* examining type information. -*/ -if (v->flags &EXPR_FLAG_FP_CONST ) - cast->flags |=EXPR_FLAG_INT_CONST_EXPR ;return token; } }
validation/constexpr-cast.c new file +25 -0
@@ -0,0 +1,25 @@ +static int a[] = { + [(int )0 ] =0 ,// OK + [(int )(int )0 ] =0 ,// OK + [(int )0. ] =0 ,// OK + [(int )(int )0. ] =0 ,// OK + [(int )__builtin_choose_expr (0 ,0 ,0 )] =0 ,// OK + [(int )__builtin_choose_expr (0 ,0 ,0. )] =0 ,// OK + + [(int )(float )0 ] =0 ,// KO + [(int )(float )0. ] =0 ,// KO + + [(int )(void *)0 ] =0 ,// KO + [(int )(void *)0. ] =0 ,// KO + +}; +/* +* check-name: Expression constness propagation in casts +* +* check-error-start +constexpr-cast.c:9:11: error: bad integer constant expression +constexpr-cast.c:10:11: error: bad integer constant expression +constexpr-cast.c:12:11: error: bad integer constant expression +constexpr-cast.c:13:11: error: bad integer constant expression +* check-error-end +*/
Signature · 1 line
2.7.0
Re: [PATCH v2 02/13] expression: examine constness of casts at evaluation only
Quoted 11 lines · Nicolai Stange
Currently, the propagation of expressions' constness flags through cast expressions is done in two steps:
- Several flags are speculatively set at cast expression parsing time
- and possibly cleared again at evaluation time.Set aside this unfortunate split of code, the early propagation of constness flags is not able to recognize constant expressions such as
(int)__builtin_choose_expr(0, 0, 0)
since the final expression to be thrown into the cast is known only after evaluation.
Quoted 8 lines
Move the whole calculation of cast expressions' constness flags to the evaluation phase.
Introduce support for tracking arithmetic constness propagation through cast expressions.
Introduce support for recognizing address constants created by casting an integer constant to pointer type.
The description show that 3 things are done in the patch. Can it be splitted in 3?
Also, it describes the current situation and what is changed but the 'why' part is not so clear.
The changes themselves are very fine.
And the comments help :)
Luc
Quoted 136 lines
Signed-off-by Nicolai Stange <[email protected]>--- evaluate.c | 42 ++++++++++++++++++++++++++++++------------ expression.c | 19 ------------------- validation/constexpr-cast.c | 25 +++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 31 deletions(-) create mode 100644 validation/constexpr-cast.cevaluate.c +30
-12@@ -323,7 +323,6 @@ static struct expression * cast_to(struct expression *old, struct symbol *type) } expr =alloc_expression (old->pos,EXPR_IMPLIED_CAST ); - expr->flags = old->flags; expr->ctype = type; expr->cast_type = type; expr->cast_expression = old;@@ -2742,17 +2741,36 @@ static struct symbol *evaluate_cast(struct expression *expr) class1 =classify_type (ctype, &t1); -/* cast to non-numeric type -> not an arithmetic expression */ -if (!(class1 &TYPE_NUM )) -expr_clear_flag (&expr->flags,EXPR_FLAG_ARITH_CONST_EXPR ); -/* cast to float type -> not an integer constant expression */ -else if (class1 &TYPE_FLOAT ) -expr_clear_flag (&expr->flags,EXPR_FLAG_INT_CONST_EXPR ); -/* if argument turns out to be not an integer constant expression *and* -it was not a floating literal to start with -> too bad */ -else if (!(target->flags & - (EXPR_FLAG_INT_CONST_EXPR |EXPR_FLAG_FP_CONST ))) -expr_clear_flag (&expr->flags,EXPR_FLAG_INT_CONST_EXPR ); +if (!(class1 &TYPE_NUM )) { +/* +* Casts of integer literals to pointer type yield +* address constants [6.6(9)]. +*/ +if (class1 &TYPE_PTR && + (target->flags &EXPR_FLAG_INT_CONST )) { +expr_set_flag (&expr->flags,EXPR_FLAG_ADDR_CONST_EXPR ); + } + }else { + expr->flags = target->flags; +expr_flags_decay_consts (&expr->flags); +/* +* Casts to numeric types never result in address +* constants [6.6(9)]. +*/ +expr_clear_flag (&expr->flags,EXPR_FLAG_ADDR_CONST_EXPR ); +/* +* Cast to float type -> not an integer constant +* expression [6.6(6)]. +*/ +if (class1 &TYPE_FLOAT ) +expr_clear_flag (&expr->flags,EXPR_FLAG_INT_CONST_EXPR ); +/* +* Casts of float literals to integer type results in +* a constant integer expression [6.6(6)]. +*/ +else if (target->flags &EXPR_FLAG_FP_CONST ) +expr_set_flag (&expr->flags,EXPR_FLAG_INT_CONST_EXPR ); + }/* * You can always throw a value away by casting to expression.c +0
-19@@ -725,25 +725,6 @@ static struct token *cast_expression(struct token *token, struct expression **trif (!v)return token; cast->cast_expression = v; - - cast->flags = v->flags; -expr_flags_decay_consts (&cast->flags); -/* -* Up to now, we missed the (int).0 case here -* which should really get a -* EXPR_FLAG_INT_CONST_EXPR marker. Also, -* conversion to non-numeric types is not -* properly reflected up to this point. -* However, we do not know until evaluation. -* For the moment, in order to preserve -* semantics, speculatively set -* EXPR_FLAG_INT_CONST_EXPR if -* EXPR_FLAG_FP_CONST is set. evaluate_cast() -* will unset inappropriate flags again after -* examining type information. -*/ -if (v->flags &EXPR_FLAG_FP_CONST ) - cast->flags |=EXPR_FLAG_INT_CONST_EXPR ;return token; } }validation/constexpr-cast.c new file +25
-0@@ -0,0 +1,25 @@ +static int a[] = { + [(int )0 ] =0 ,// OK + [(int )(int )0 ] =0 ,// OK + [(int )0. ] =0 ,// OK + [(int )(int )0. ] =0 ,// OK + [(int )__builtin_choose_expr (0 ,0 ,0 )] =0 ,// OK + [(int )__builtin_choose_expr (0 ,0 ,0. )] =0 ,// OK + + [(int )(float )0 ] =0 ,// KO + [(int )(float )0. ] =0 ,// KO + + [(int )(void *)0 ] =0 ,// KO + [(int )(void *)0. ] =0 ,// KO + +}; +/* +* check-name: Expression constness propagation in casts +* +* check-error-start +constexpr-cast.c:9:11: error: bad integer constant expression +constexpr-cast.c:10:11: error: bad integer constant expression +constexpr-cast.c:12:11: error: bad integer constant expression +constexpr-cast.c:13:11: error: bad integer constant expression +* check-error-end +*/ Signature · 6 lines
2.7.0 -- To unsubscribe from this list: send the line "unsubscribe linux-sparse" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v2 02/13] expression: examine constness of casts at evaluation only
Quoted 18 lines · Luc Van Oostenryck
Quoted 17 lines · Nicolai Stange
Currently, the propagation of expressions' constness flags through cast expressions is done in two steps:
- Several flags are speculatively set at cast expression parsing time
- and possibly cleared again at evaluation time.Set aside this unfortunate split of code, the early propagation of constness flags is not able to recognize constant expressions such as
(int)__builtin_choose_expr(0, 0, 0)
since the final expression to be thrown into the cast is known only after evaluation.Move the whole calculation of cast expressions' constness flags to the evaluation phase.
Introduce support for tracking arithmetic constness propagation through cast expressions.
Quoted 6 lines
Quoted 2 lines
Introduce support for recognizing address constants created by casting an integer constant to pointer type.
The description show that 3 things are done in the patch. Can it be splitted in 3?
That tracking "arithmetic constness propagation" is a stupid assertion anyway since it is an implication of the change listed before ("calculate constness at evaluation"). I'll drop that or rewrite it.
And yes, the third thing should be a separate patch.
Quoted 4 lines
Also, it describes the current situation and what is changed but the 'why' part is not so clear.
Well, because I want (int)__builtin_choose_expr(0, 0, 0) to being recognized as being a constant expression?
Quoted 141 lines
The changes themselves are very fine.
And the comments help :)Luc
Quoted 136 lines
Signed-off-by Nicolai Stange <[email protected]>--- evaluate.c | 42 ++++++++++++++++++++++++++++++------------ expression.c | 19 ------------------- validation/constexpr-cast.c | 25 +++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 31 deletions(-) create mode 100644 validation/constexpr-cast.cevaluate.c +30
-12@@ -323,7 +323,6 @@ static struct expression * cast_to(struct expression *old, struct symbol *type) } expr =alloc_expression (old->pos,EXPR_IMPLIED_CAST ); - expr->flags = old->flags; expr->ctype = type; expr->cast_type = type; expr->cast_expression = old;@@ -2742,17 +2741,36 @@ static struct symbol *evaluate_cast(struct expression *expr) class1 =classify_type (ctype, &t1); -/* cast to non-numeric type -> not an arithmetic expression */ -if (!(class1 &TYPE_NUM )) -expr_clear_flag (&expr->flags,EXPR_FLAG_ARITH_CONST_EXPR ); -/* cast to float type -> not an integer constant expression */ -else if (class1 &TYPE_FLOAT ) -expr_clear_flag (&expr->flags,EXPR_FLAG_INT_CONST_EXPR ); -/* if argument turns out to be not an integer constant expression *and* -it was not a floating literal to start with -> too bad */ -else if (!(target->flags & - (EXPR_FLAG_INT_CONST_EXPR |EXPR_FLAG_FP_CONST ))) -expr_clear_flag (&expr->flags,EXPR_FLAG_INT_CONST_EXPR ); +if (!(class1 &TYPE_NUM )) { +/* +* Casts of integer literals to pointer type yield +* address constants [6.6(9)]. +*/ +if (class1 &TYPE_PTR && + (target->flags &EXPR_FLAG_INT_CONST )) { +expr_set_flag (&expr->flags,EXPR_FLAG_ADDR_CONST_EXPR ); + } + }else { + expr->flags = target->flags; +expr_flags_decay_consts (&expr->flags); +/* +* Casts to numeric types never result in address +* constants [6.6(9)]. +*/ +expr_clear_flag (&expr->flags,EXPR_FLAG_ADDR_CONST_EXPR ); +/* +* Cast to float type -> not an integer constant +* expression [6.6(6)]. +*/ +if (class1 &TYPE_FLOAT ) +expr_clear_flag (&expr->flags,EXPR_FLAG_INT_CONST_EXPR ); +/* +* Casts of float literals to integer type results in +* a constant integer expression [6.6(6)]. +*/ +else if (target->flags &EXPR_FLAG_FP_CONST ) +expr_set_flag (&expr->flags,EXPR_FLAG_INT_CONST_EXPR ); + }/* * You can always throw a value away by casting to expression.c +0
-19@@ -725,25 +725,6 @@ static struct token *cast_expression(struct token *token, struct expression **trif (!v)return token; cast->cast_expression = v; - - cast->flags = v->flags; -expr_flags_decay_consts (&cast->flags); -/* -* Up to now, we missed the (int).0 case here -* which should really get a -* EXPR_FLAG_INT_CONST_EXPR marker. Also, -* conversion to non-numeric types is not -* properly reflected up to this point. -* However, we do not know until evaluation. -* For the moment, in order to preserve -* semantics, speculatively set -* EXPR_FLAG_INT_CONST_EXPR if -* EXPR_FLAG_FP_CONST is set. evaluate_cast() -* will unset inappropriate flags again after -* examining type information. -*/ -if (v->flags &EXPR_FLAG_FP_CONST ) - cast->flags |=EXPR_FLAG_INT_CONST_EXPR ;return token; } }validation/constexpr-cast.c new file +25
-0@@ -0,0 +1,25 @@ +static int a[] = { + [(int )0 ] =0 ,// OK + [(int )(int )0 ] =0 ,// OK + [(int )0. ] =0 ,// OK + [(int )(int )0. ] =0 ,// OK + [(int )__builtin_choose_expr (0 ,0 ,0 )] =0 ,// OK + [(int )__builtin_choose_expr (0 ,0 ,0. )] =0 ,// OK + + [(int )(float )0 ] =0 ,// KO + [(int )(float )0. ] =0 ,// KO + + [(int )(void *)0 ] =0 ,// KO + [(int )(void *)0. ] =0 ,// KO + +}; +/* +* check-name: Expression constness propagation in casts +* +* check-error-start +constexpr-cast.c:9:11: error: bad integer constant expression +constexpr-cast.c:10:11: error: bad integer constant expression +constexpr-cast.c:12:11: error: bad integer constant expression +constexpr-cast.c:13:11: error: bad integer constant expression +* check-error-end +*/ Signature · 6 lines
2.7.0 -- To unsubscribe from this list: send the line "unsubscribe linux-sparse" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v2 3/13 expression: examine constness of binops and alike at evaluation only
In reply to Nicolai StangeCurrently, the propagation of expressions' constness flags through binary operations, compare and logical expressions is done in two steps:
- Several flags are speculatively set at expression parsing time
- and possibly cleared again at evaluation time.
Set aside this unfortunate split of code, the early propagation of constness flags is not able to recognize constant expressions such as
0 + __builtin_choose_expr(0, 0, 0)
0 < __builtin_choose_expr(0, 0, 0)
0 && __builtin_choose_expr(0, 0, 0)
since the final expression to be thrown into the binop-like expression is known only after evaluation.
Move the whole calculation of binary operations', compare and logical expressions' constness flags to the evaluation phase.
Introduce support for tracking arithmetic constness propagation through binop-like expressions.
--- evaluate.c | 25 +++++++++---------------- expression.c | 3 --- validation/constexpr-binop.c | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 19 deletions(-) create mode 100644 validation/constexpr-binop.c
evaluate.c +9 -16
@@ -878,11 +878,9 @@ static struct symbol *evaluate_logical(struct expression *expr)/* the result is int [6.5.13(3), 6.5.14(3)] */ expr->ctype = &int_ctype; -if (expr->flags) { -if (!(expr->left->flags & expr->right->flags & -EXPR_FLAG_INT_CONST_EXPR )) - expr->flags =EXPR_FLAG_NONE ; - } + expr->flags = expr->left->flags & expr->right->flags; +expr_flags_decay_consts (&expr->flags); +expr_clear_flag (&expr->flags,EXPR_FLAG_ADDR_CONST_EXPR );return &int_ctype; }
@@ -893,14 +891,11 @@ static struct symbol *evaluate_binop(struct expression *expr)int rclass =classify_type (expr->right->ctype, &rtype);int op = expr->op; -if (expr->flags) { -if (!(expr->left->flags & expr->right->flags & -EXPR_FLAG_INT_CONST_EXPR )) - expr->flags =EXPR_FLAG_NONE ; - } -/* number op number */ if (lclass & rclass &TYPE_NUM ) { + expr->flags = expr->left->flags & expr->right->flags; +expr_flags_decay_consts (&expr->flags); +if ((lclass | rclass) &TYPE_FLOAT ) {switch (op) {case '+' :case '-' :case '*' :case '/' :
@@ -1001,11 +996,9 @@ static struct symbol *evaluate_compare(struct expression *expr)struct symbol *ctype;const char *typediff; -if (expr->flags) { -if (!(expr->left->flags & expr->right->flags & -EXPR_FLAG_INT_CONST_EXPR )) - expr->flags =EXPR_FLAG_NONE ; - } + expr->flags = left->flags & right->flags; +expr_flags_decay_consts (&expr->flags); +expr_clear_flag (&expr->flags,EXPR_FLAG_ADDR_CONST_EXPR );/* Type types? */ if (is_type_type (ltype) &&is_type_type (rtype))
expression.c +0 -3
@@ -146,7 +146,6 @@ static struct token *builtin_types_compatible_p_expr(struct token *token, {struct expression *expr =alloc_expression ( token->pos,EXPR_COMPARE ); -expr_set_flag (&expr->flags,EXPR_FLAG_INT_CONST_EXPR ); expr->op =SPECIAL_EQUAL ; token = token->next;if (!match_op (token,'(' ))
@@ -761,8 +760,6 @@ static struct token *cast_expression(struct token *token, struct expression **trsparse_error (next->pos,"No right hand side of '%s'-expression" ,show_special (op)); \break ; \ } \ - top->flags = left->flags & right->flags; \ -expr_flags_decay_consts (&top->flags); \ top->op = op; \ top->left = left; \ top->right = right; \
validation/constexpr-binop.c new file +33 -0
@@ -0,0 +1,33 @@ +static int a[] = { + [0 +0 ] =0 ,// OK + [0 +0. ] =0 ,// KO + [(void *)0 +0 ] =0 ,// KO + [0 +__builtin_choose_expr (0 ,0 ,0 )] =0 ,// OK + [0 +__builtin_choose_expr (0 ,0. ,0 )] =0 ,// OK + [0 +__builtin_choose_expr (0 ,0 ,0. )] =0 ,// KO + [0 <0 ] =0 ,// OK + [0 <0. ] =0 ,// KO + [0 <__builtin_choose_expr (0 ,0 ,0 )] =0 ,// OK + [0 <__builtin_choose_expr (0 ,0. ,0 )] =0 ,// OK + [0 <__builtin_choose_expr (0 ,0 ,0. )] =0 ,// KO + [0 &&0 ] =0 ,// OK + [0 &&0. ] =0 ,// KO + [0 &&__builtin_choose_expr (0 ,0 ,0 )] =0 ,// OK + [0 &&__builtin_choose_expr (0 ,0. ,0 )] =0 ,// OK + [0 &&__builtin_choose_expr (0 ,0 ,0. )] =0 ,// KO + [0 +__builtin_types_compatible_p (int, float)] =0 ,// OK +}; + +/* +* check-name: Expression constness propagation in binops and alike +* +* check-error-start +constexpr-binop.c:3:12: error: bad constant expression +constexpr-binop.c:4:19: error: bad integer constant expression +constexpr-binop.c:7:12: error: bad constant expression +constexpr-binop.c:9:12: error: bad integer constant expression +constexpr-binop.c:12:12: error: bad integer constant expression +constexpr-binop.c:14:12: error: bad integer constant expression +constexpr-binop.c:17:12: error: bad integer constant expression +* check-error-end +*/
Signature · 1 line
2.7.0
Re: [PATCH v2 03/13] expression: examine constness of binops and alike at evaluation only
Quoted 14 lines · Nicolai Stange
Currently, the propagation of expressions' constness flags through binary operations, compare and logical expressions is done in two steps:
- Several flags are speculatively set at expression parsing time
- and possibly cleared again at evaluation time.Set aside this unfortunate split of code, the early propagation of constness flags is not able to recognize constant expressions such as
0 +__builtin_choose_expr(0, 0, 0)
0 <__builtin_choose_expr(0, 0, 0)
0 &&__builtin_choose_expr(0, 0, 0)
since the final expression to be thrown into the binop-like expression is known only after evaluation.
Quoted 5 lines
Move the whole calculation of binary operations', compare and logical expressions' constness flags to the evaluation phase.
Introduce support for tracking arithmetic constness propagation through binop-like expressions.
Same as previous patch regarding the description and splitness of the patch.
Quoted 5 lines
evaluate.c +0
-1@@ -893,14 +891,11 @@ static struct symbol *evaluate_binop(struct expression *expr)int rclass =classify_type (expr->right->ctype, &rtype);int op = expr->op; -if (expr->flags) {
evaluate.c static struct symbol *evaluate_binop(struct expression *expr) · line 898
Quoted 10 lines
evaluate.c +3
-5-if (!(expr->left->flags & expr->right->flags & -EXPR_FLAG_INT_CONST_EXPR )) - expr->flags =EXPR_FLAG_NONE ; - } -/* number op number */ if (lclass & rclass &TYPE_NUM ) { + expr->flags = expr->left->flags & expr->right->flags; +expr_flags_decay_consts (&expr->flags); +
What about expr->flags now if not TYPE_NUM?
Are we sure it's always initialized to NONE by the alloctor?
validation/constexpr-binop.c line 9
Quoted 11 lines
validation/constexpr-binop.c +9
-0@@ -0,0 +1,33 @@ +static int a[] = { + [0 +0 ] =0 ,// OK + [0 +0. ] =0 ,// KO + [(void *)0 +0 ] =0 ,// KO + [0 +__builtin_choose_expr (0 ,0 ,0 )] =0 ,// OK + [0 +__builtin_choose_expr (0 ,0. ,0 )] =0 ,// OK + [0 +__builtin_choose_expr (0 ,0 ,0. )] =0 ,// KO + [0 <0 ] =0 ,// OK + [0 <0. ] =0 ,// KO
It's not clear to me what the standrad says about this case. What about the constness of 'usual artihmetic conversions' ? Also GCC don't complain on this one.
validation/constexpr-binop.c line 14
Quoted 1 line
validation/constexpr-binop.c +1
-0+ [0 && 0.] = 0, // KO
Same here.
Luc
Re: [PATCH v2 03/13] expression: examine constness of binops and alike at evaluation only
Quoted 15 lines · Luc Van Oostenryck
Quoted 14 lines · Nicolai Stange
Currently, the propagation of expressions' constness flags through binary operations, compare and logical expressions is done in two steps:
- Several flags are speculatively set at expression parsing time
- and possibly cleared again at evaluation time.Set aside this unfortunate split of code, the early propagation of constness flags is not able to recognize constant expressions such as
0 +__builtin_choose_expr(0, 0, 0)
0 <__builtin_choose_expr(0, 0, 0)
0 &&__builtin_choose_expr(0, 0, 0)
since the final expression to be thrown into the binop-like expression is known only after evaluation.
Quoted 8 lines
Quoted 5 lines
Move the whole calculation of binary operations', compare and logical expressions' constness flags to the evaluation phase.
Introduce support for tracking arithmetic constness propagation through binop-like expressions.
Same as previous patch regarding the description and splitness of the patch.
And again, tracking arithmetic constness is more or less an implication rather than an explicit change.
I will reformulate.
Quoted 20 lines
Quoted 6 lines
evaluate.c +0
-1@@ -893,14 +891,11 @@ static struct symbol *evaluate_binop(struct expression *expr)int rclass =classify_type (expr->right->ctype, &rtype);int op = expr->op; -if (expr->flags) {evaluate.c static struct symbol *evaluate_binop(struct expression *expr) · line 898
Quoted 10 lines
evaluate.c +3
-5-if (!(expr->left->flags & expr->right->flags & -EXPR_FLAG_INT_CONST_EXPR )) - expr->flags =EXPR_FLAG_NONE ; - } -/* number op number */ if (lclass & rclass &TYPE_NUM ) { + expr->flags = expr->left->flags & expr->right->flags; +expr_flags_decay_consts (&expr->flags); +What about
expr->flagsnow if notTYPE_NUM?
Are we sure it's always initialized to NONE by the alloctor?
Quoted 15 lines
validation/constexpr-binop.c line 9
Quoted 11 lines
validation/constexpr-binop.c +9
-0@@ -0,0 +1,33 @@ +static int a[] = { + [0 +0 ] =0 ,// OK + [0 +0. ] =0 ,// KO + [(void *)0 +0 ] =0 ,// KO + [0 +__builtin_choose_expr (0 ,0 ,0 )] =0 ,// OK + [0 +__builtin_choose_expr (0 ,0. ,0 )] =0 ,// OK + [0 +__builtin_choose_expr (0 ,0 ,0. )] =0 ,// KO + [0 <0 ] =0 ,// OK + [0 <0. ] =0 ,// KO It's not clear to me what the standrad says about this case. What about the constness of 'usual artihmetic conversions' ? Also GCC don't complain on this one.
Within the square brackets, an integer constant expression is needed.
That's 6.6(6). "Floating constants that are immediate operands of casts" are allowed. Implicitly promoted types are not, at least to my interpretation.
Quoted 7 lines
validation/constexpr-binop.c line 14
Quoted 2 lines
validation/constexpr-binop.c +1
-0+ [0 && 0.] = 0, // KOSame here.
Luc
Re: [PATCH v2 03/13] expression: examine constness of binops and alike at evaluation only
Quoted 9 lines · Nicolai Stange
Quoted 6 lines · Luc Van Oostenryck
validation/constexpr-binop.c line 9
Quoted 1 line · Nicolai Stange
validation/constexpr-binop.c new file +1
-0+ [0 < 0.] = 0, // KOIt's not clear to me what the standrad says about this case. What about the constness of 'usual artihmetic conversions' ? Also GCC don't complain on this one.
Quoted 5 lines
Within the square brackets, an integer constant expression is needed.
That's 6.6(6). "Floating constants that are immediate operands of casts" are allowed. Implicitly promoted types are not, at least to my interpretation.
Yes, I saw that the standard isn't explicit about it. The way I see things is:
- I don't see any reason why an explicit conversion would preserve constness while an implicit one would not.
- intuitively, when I read the code I see that the result of this expression is can be known at compile time.
But well ... I have the same issue with [(int) (0 + 0.0)] which is clearly not allowed by the standard while [(int) 0.0] is.
Maybe those should be relaxed latter and we can invoke 6.6(10):
An implementation may accept other forms of constant expressions
OTOH, who cares about floats ;)
Reading a bit more about it ...
For the designator in the array initializer (but also probably elsewhere) 6.7.8(6) first uses
[ <i>constant-expression<\i> ]
and then
and the expression shall be an integer constant expression.
Can this last 'integer constant expression' be interpreted as 'constant expression of integer type'?
This could be considered to be coherent with the footnote 99) in 6.6(6) followed by 6.6(7).
I don't know, it's something for language lawyers.
Luc
Re: [PATCH v2 03/13] expression: examine constness of binops and alike at evaluation only
Quoted 12 lines · Luc Van Oostenryck
Quoted 11 lines · Nicolai Stange
Quoted 6 lines · Luc Van Oostenryck
validation/constexpr-binop.c line 9
Quoted 1 line · Nicolai Stange
validation/constexpr-binop.c new file +1
-0+ [0 < 0.] = 0, // KOIt's not clear to me what the standrad says about this case. What about the constness of 'usual artihmetic conversions' ? Also GCC don't complain on this one.
Within the square brackets, an integer constant expression is needed.
Quoted 8 lines
Quoted 3 lines
That's 6.6(6). "Floating constants that are immediate operands of casts" are allowed. Implicitly promoted types are not, at least to my interpretation.
Yes, I saw that the standard isn't explicit about it. The way I see things is:
- I don't see any reason why an explicit conversion would preserve constness while an implicit one would not.
Just to make it explicit here, we're not talking about "arithmetic constant expressions", but "integer constant expressions".
I think the standard designers made this distinction in order to differentiate between something so const that it can be used in static initializers (arith. constexpr.) vs. sth. so const and _free of surprises_ (integer constexpr.) that it can safely be used in various sensitive places.
For example:
#define PI 3.14
switch(foo) {
case PI:
...
};should certainly not be allowed, while a
#define PI 3.14
switch(foo) {
case (int)PI:
...
};signals the compiler that the programmer knows (or pretends to know) what he's doing, so it should be allowed.
OTOH,
0 < 0.
is clearly an arithmetic constant expression and can be used in static initializers or wherever.
This is *my* interpretation of why the standard designers did it that way. Of course I might be wrong though.
Quoted 2 lines
- intuitively, when I read the code I see that the result of this expression is can be known at compile time.
Yes, the compiler knows that it's an _arithmetic_ constant expression.
Quoted 2 lines
But well ... I have the same issue with [(int) (0 + 0.0)] which
Again, programmers writing code like this don't even pretend that they know what they're doing. Why should a compiler or even sparse trust them?
Quoted 8 lines
is clearly not allowed by the standard while [(int) 0.0] is.
Maybe those should be relaxed latter and we can invoke 6.6(10):
An implementation may accept other forms of constant expressionsOTOH, who cares about floats ;)
A true word. Thus, I suggest not to introduce any additional form of constness at this moment. In the end, we wanted to be stricter than gcc.
If real world problems arise, we can easily return to that question.
But as you said, certainly nobody cares.
Quoted 4 lines
Reading a bit more about it ...
Quoted 8 lines
For the designator in the array initializer (but also probably elsewhere) 6.7.8(6) first uses
[ <i>constant-expression<\i> ]and then
and the expression shall be an integer constant expression.Can this last 'integer constant expression' be interpreted as 'constant expression of integer type'?
I'm sure that if the standard authors' real intents had been to allow arithmetic constant expressions of integer types at this place, they would have said so and not used the well defined term "integer constant expression" at this point.
Quoted 4 lines
This could be considered to be coherent with the footnote 99) in 6.6(6) followed by 6.6(7).
I don't know, it's something for language lawyers.
If you don't agree with my interpretation, we could very well try to get some language layer into our boat.
OTOH, if you agree that we could safely leave the semantics as they currently are, I could just go on and prepare v3...
Nicolai
Re: [PATCH v2 03/13] expression: examine constness of binops and alike at evaluation only
Quoted 1 line · Nicolai Stange
Luc Van Oostenryck <[email protected]> writes:
...
Quoted 9 lines
Quoted 4 lines
Maybe those should be relaxed latter and we can invoke 6.6(10):
An implementation may accept other forms of constant expressionsOTOH, who cares about floats ;)
A true word. Thus, I suggest not to introduce any additional form of constness at this moment. In the end, we wanted to be stricter than gcc.
If real world problems arise, we can easily return to that question.
It's exactly what I meant.
...
Quoted 4 lines
I'm sure that if the standard authors' real intents had been to allow arithmetic constant expressions of integer types at this place, they would have said so and not used the well defined term "integer constant expression" at this point.
I would definitively appreciate the standard being less ambiguous. And I find the footnote 99) in 6.6 really confusing.
Quoted 10 lines
Quoted 4 lines
This could be considered to be coherent with the footnote 99) in 6.6(6) followed by 6.6(7).
I don't know, it's something for language lawyers.
If you don't agree with my interpretation, we could very well try to get some language layer into our boat.
OTOH, if you agree that we could safely leave the semantics as they currently are, I could just go on and prepare v3...
It's not that I really disagree with your interpretation, nor that I totally agree with it. It's more me playing devil's advocate, insuring that the reasonable cases are well covered.
It's the fact that GCC and your code differ on this point that made me stop at it because it could make your changes less valuable which we don't want.
My remark about a language lawyer was not a serious one, it was more a way to say
"let's not spend more time on it for now".
Please go on.
And with what you have put in place for the expression constness it will be very easy to relax some of the rules when there will be some needs for it.
Luc
Re: [PATCH v2 03/13] expression: examine constness of binops and alike at evaluation only
In reply to Nicolai Stangeevaluate.c static struct symbol *evaluate_compare(struct expression *expr) · line 1001
Quoted 12 lines · Nicolai Stange
evaluate.c +3
-5@@ -1001,11 +996,9 @@ static struct symbol *evaluate_compare(struct expression *expr)struct symbol *ctype;const char *typediff; -if (expr->flags) { -if (!(expr->left->flags & expr->right->flags & -EXPR_FLAG_INT_CONST_EXPR )) - expr->flags =EXPR_FLAG_NONE ; - } + expr->flags = left->flags & right->flags; +expr_flags_decay_consts (&expr->flags); +expr_clear_flag (&expr->flags,EXPR_FLAG_ADDR_CONST_EXPR );
Related to the [0 < 0.0] test case, shouldn't this be (re) done after the call to usual_conversions() ?
Luc
PATCH v2 4/13 expression: examine constness of preops at evaluation only
In reply to Nicolai StangeCurrently, the propagation of expressions' constness flags through prefix expressions is done in two steps:
- Several flags are speculatively set at expression parsing time
- and possibly cleared again at evaluation time.
Set aside this unfortunate split of code, the early propagation of constness flags is not able to recognize constant expressions such as
-__builtin_choose_expr(0, 0, 0) ~__builtin_choose_expr(0, 0, 0) !__builtin_choose_expr(0, 0, 0)
since the final expression to be thrown into the prefix expression is known only after evaluation.
Move the whole calculation of prefix expressions' constness flags to the evaluation phase.
Introduce support for tracking arithmetic constness propagation through prefix expressions.
--- evaluate.c | 17 ++++++++++++----- expression.c | 4 ---- validation/constexpr-preop.c | 29 +++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 9 deletions(-) create mode 100644 validation/constexpr-preop.c
evaluate.c +12 -5
@@ -1798,8 +1798,10 @@ static struct symbol *evaluate_sign(struct expression *expr) {struct symbol *ctype = expr->unop->ctype;int class =classify_type (ctype, &ctype); -if (expr->flags && !(expr->unop->flags &EXPR_FLAG_INT_CONST_EXPR )) - expr->flags =EXPR_FLAG_NONE ; +unsigned char flags; + + flags = expr->flags | expr->unop->flags; +expr_flags_decay_consts (&flags);/* should be an arithmetic type */ if (!(class &TYPE_NUM ))return bad_expr_type (expr);
@@ -1816,6 +1818,7 @@ Normal: }if (expr->op =='+' ) *expr = *expr->unop; + expr->flags = flags; expr->ctype = ctype;return ctype; Restr:
@@ -1853,9 +1856,13 @@ static struct symbol *evaluate_preop(struct expression *expr)return evaluate_postop (expr);case '!' : -if (expr->flags && !(expr->unop->flags & -EXPR_FLAG_INT_CONST_EXPR )) - expr->flags =EXPR_FLAG_NONE ; + expr->flags = expr->unop->flags; +expr_flags_decay_consts (&expr->flags); +/* +* A logical negation never yields an address constant +* [6.6(9)]. +*/ +expr_clear_flag (&expr->flags,EXPR_FLAG_ADDR_CONST_EXPR );if (is_safe_type (ctype))warning (expr->pos,"testing a 'safe expression'" );if (is_float_type (ctype)) {
expression.c +0 -4
@@ -452,8 +452,6 @@ struct token *primary_expression(struct token *token, struct expression **tree) expr =alloc_expression (token->pos,EXPR_PREOP ); expr->op ='(' ; token =parens_expression (token, &expr->unop,"in expression" ); -if (expr->unop) - expr->flags = expr->unop->flags;break ; }if (token->special =='[' &&lookup_type (token->next)) {
@@ -665,8 +663,6 @@ static struct token *unary_expression(struct token *token, struct expression **t unary =alloc_expression (token->pos,EXPR_PREOP ); unary->op = token->special; unary->unop = unop; - unary->flags = unop->flags; -expr_flags_decay_consts (&unary->flags); *tree = unary;return next; }
validation/constexpr-preop.c new file +29 -0
@@ -0,0 +1,29 @@ +static int a[] = { + [+0 ] =0 ,// OK + [+__builtin_choose_expr (0 ,0 ,0 )] =0 ,// OK + [+0. ] =0 ,// KO + [+__builtin_choose_expr (0 ,0 ,0. )] =0 ,// KO + [-0 ] =0 ,// OK + [-__builtin_choose_expr (0 ,0 ,0 )] =0 ,// OK + [-0. ] =0 ,// KO + [-__builtin_choose_expr (0 ,0 ,0. )] =0 ,// KO + [~0 ] =0 ,// OK + [~__builtin_choose_expr (0 ,0 ,0 )] =0 ,// OK + [!0 ] =0 ,// OK + [!__builtin_choose_expr (0 ,0 ,0 )] =0 ,// OK + [!0. ] =0 ,// KO + [!__builtin_choose_expr (0 ,0 ,0. )] =0 ,// KO +}; + +/* +* check-name: Expression constness propagation in preops +* +* check-error-start +constexpr-preop.c:4:5: error: bad constant expression +constexpr-preop.c:5:33: error: bad constant expression +constexpr-preop.c:8:4: error: bad constant expression +constexpr-preop.c:9:4: error: bad constant expression +constexpr-preop.c:14:4: error: bad integer constant expression +constexpr-preop.c:15:4: error: bad integer constant expression +* check-error-end +*/
Signature · 1 line
2.7.0
Re: [PATCH v2 04/13] expression: examine constness of preops at evaluation only
Quoted 13 lines · Nicolai Stange
Currently, the propagation of expressions' constness flags through prefix expressions is done in two steps:
- Several flags are speculatively set at expression parsing time
- and possibly cleared again at evaluation time.Set aside this unfortunate split of code, the early propagation of constness flags is not able to recognize constant expressions such as
-__builtin_choose_expr(0, 0, 0) ~__builtin_choose_expr(0, 0, 0) !__builtin_choose_expr(0, 0, 0)
since the final expression to be thrown into the prefix expression is known only after evaluation.
Quoted 5 lines
Move the whole calculation of prefix expressions' constness flags to the evaluation phase.
Introduce support for tracking arithmetic constness propagation through prefix expressions.
What about replacing the whole description with something::
Move the whole calculation of prefix expressions' constness flags to the evaluation phase so that we can now recognize constant expressions such as -__builtin_choose_expr(0, 0, 0) ~__builtin_choose_expr(0, 0, 0) !__builtin_choose_expr(0, 0, 0) and adding further explanation after this, if really needed?
Luc
PATCH v2 5/13 expression: examine constness of conditionals at evaluation only
In reply to Nicolai StangeCurrently, the propagation of expressions' constness flags through conditional expressions is done in two steps:
- Several flags are speculatively set at expression parsing time
- and possibly cleared again at evaluation time.
Set aside this unfortunate split of code, the early propagation of constness flags is not able to recognize constant expressions such as
0 ? __builtin_choose_expr(0, 0, 0) : 0
0 ? 0 : __builtin_choose_expr(0, 0, 0)
since the final expression to be thrown into the conditional expression is known only after evaluation.
Move the whole calculation of conditional expressions' constness flags to the evaluation phase.
Introduce support for tracking arithmetic constness propagation through conditional expressions.
--- evaluate.c | 18 ++++++++---------- expression.c | 7 ------- validation/constexpr-conditional.c | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 17 deletions(-) create mode 100644 validation/constexpr-conditional.c
evaluate.c +8 -10
@@ -1114,16 +1114,14 @@ static struct symbol *evaluate_conditional_expression(struct expression *expr) true = &expr->cond_true; } -if (expr->flags) { -unsigned char flags_mask =0 ; -unsigned char flags; - -expr_set_flag (&flags_mask,EXPR_FLAG_INT_CONST_EXPR ); - flags = (expr->conditional->flags & flags_mask); - flags &= (*true)->flags & expr->cond_false->flags; -if (!flags) - expr->flags =EXPR_FLAG_NONE ; - } + expr->flags = (expr->conditional->flags & (*true)->flags & + expr->cond_false->flags); +expr_flags_decay_consts (&expr->flags); +/* +* A conditional operator never yields an address constant +* [6.6(9)]. +*/ +expr_clear_flag (&expr->flags,EXPR_FLAG_ADDR_CONST_EXPR ); lclass =classify_type (ltype, <ype); rclass =classify_type (rtype, &rtype);
expression.c +0 -7
@@ -858,13 +858,6 @@ struct token *conditional_expression(struct token *token, struct expression **tr token =parse_expression (token->next, &expr->cond_true); token =expect (token,':' ,"in conditional expression" ); token =conditional_expression (token, &expr->cond_false); -if (expr->left && expr->cond_false) { - expr->flags = expr->left->flags & - expr->cond_false->flags; -if (expr->cond_true) - expr->flags &= expr->cond_true->flags; -expr_flags_decay_consts (&expr->flags); - } }return token; }
validation/constexpr-conditional.c new file +34 -0
@@ -0,0 +1,34 @@ +static int a[] = { + [0 ? :0 ] =0 ,// OK + [1 ? :0 ] =0 ,// OK + [0 ?0 :0 ] =0 ,// OK + [1 ?0 :0 ] =0 ,// OK + [0 ?0 :__builtin_choose_expr (0 ,0 ,0 )] =0 ,// OK + [1 ?__builtin_choose_expr (0 ,0 ,0 ) :0 ] =0 ,// OK + [0 ?__builtin_choose_expr (0 ,0 ,0 ) :0 ] =0 ,// OK + [1 ?1 :__builtin_choose_expr (0 ,0 ,0 )] =0 ,// OK + [__builtin_choose_expr (0 ,0 ,0 ) ? :0 ] =0 ,// OK + [__builtin_choose_expr (0 ,0 ,1 ) ? :0 ] =0 ,// OK + [0. ? :0 ] =0 ,// KO + [0 ?0. :0 ] =0 ,// KO + [1 ? :0. ] =0 ,// KO + [__builtin_choose_expr (0 ,0. ,0 ) ? :0 ] =0 ,// OK + [__builtin_choose_expr (0 ,0 ,0. ) ? :0 ] =0 ,// KO + [0 ?__builtin_choose_expr (0 ,0. ,0 ) :0 ] =0 ,// OK + [0 ?__builtin_choose_expr (0 ,0 ,0. ) :0 ] =0 ,// KO + [1 ?0 :__builtin_choose_expr (0 ,0. ,0 )] =0 ,// OK + [1 ?0 :__builtin_choose_expr (0 ,0 ,0. )] =0 ,// KO +}; + +/* +* check-name: Expression constness propagation in conditional expressions +* +* check-error-start +constexpr-conditional.c:12:13: error: bad constant expression +constexpr-conditional.c:13:19: error: bad constant expression +constexpr-conditional.c:14:12: error: bad constant expression +constexpr-conditional.c:16:42: error: bad constant expression +constexpr-conditional.c:18:48: error: bad constant expression +constexpr-conditional.c:20:14: error: bad constant expression +* check-error-end +*/
Signature · 1 line
2.7.0
Re: [PATCH v2 05/13] expression: examine constness of conditionals at evaluation only
Quoted 12 lines · Nicolai Stange
Currently, the propagation of expressions' constness flags through conditional expressions is done in two steps:
- Several flags are speculatively set at expression parsing time
- and possibly cleared again at evaluation time.Set aside this unfortunate split of code, the early propagation of constness flags is not able to recognize constant expressions such as
0 ?__builtin_choose_expr(0, 0, 0): 0
0 ? 0 :__builtin_choose_expr(0, 0, 0)
since the final expression to be thrown into the conditional expression is known only after evaluation.
Quoted 5 lines
Move the whole calculation of conditional expressions' constness flags to the evaluation phase.
Introduce support for tracking arithmetic constness propagation through conditional expressions.
Same comments about the description as for the previous patches.
Good otherwise.
Luc
PATCH v2 6/13 expression, evaluate: add support for recognizing address constants
In reply to Nicolai StangeAddress constants [6.6(9)] constitute one of the types of constant expressions allowed in initializers [6.6(7)] for static storage duration objects [6.7.8(4)].
Introduce support for recognizing address constants created either
- explicitly by referencing a static storage duration object by means of the unary & operator
- or implicitly by the use of an expression of array or function type.
Treat string literals as address constants.
Initially tag an expression as being an address constant at the primary expression level, i.e. upon encountering a symbol designating an object of static storage duration in primary_expression().
Carry these flags over to the *-preop wrapped expression created by evaluate_symbol_expression().
For the special case of string literals, tag them as address constants in evaluate_string().
Take care in evaluate_ptr_add() and evaluate_offset() to properly propagate the address constness flags from subexpressions to their parent expressions, namely the array ([]) or structure member dereference (->, .) expressions.
Finally, do not strip away an *-preop wrapped expression's constness flags in evaluate_addressof().
--- evaluate.c | 18 +++++++++++++++++- expression.c | 8 ++++++++ 2 files changed, 25 insertions(+), 1 deletion(-)
evaluate.c +17 -1
@@ -70,9 +70,11 @@ static struct symbol *evaluate_symbol_expression(struct expression *expr) addr->symbol = sym; addr->symbol_name = expr->symbol_name; addr->ctype = &lazy_ptr_ctype;/* Lazy evaluation: we need to do a proper job if somebody does &sym */ + addr->flags = expr->flags; expr->type =EXPR_PREOP ; expr->op ='*' ; expr->unop = addr; + expr->flags =EXPR_FLAG_NONE ;/* The type of a symbol is the symbol itself! */ expr->ctype = sym;
@@ -106,6 +108,7 @@ static struct symbol *evaluate_string(struct expression *expr) addr->symbol = sym; addr->ctype = &lazy_ptr_ctype; +expr_set_flag (&addr->flags,EXPR_FLAG_ADDR_CONST_EXPR ); expr->type =EXPR_PREOP ; expr->op ='*' ;
@@ -563,6 +566,14 @@ static struct symbol *evaluate_ptr_add(struct expression *expr, struct symbol *iclassify_type (degenerate (expr->left), &ctype); base =examine_pointer_target (ctype); +/* +* An address constant +/- an integer constant expression +* yields an address constant again [6.6(7)]. +*/ +if ((expr->left->flags &EXPR_FLAG_ADDR_CONST_EXPR ) && + (expr->right->flags &EXPR_FLAG_INT_CONST_EXPR )) +expr_set_flag (&expr->flags,EXPR_FLAG_ADDR_CONST_EXPR ); +if (!base) {expression_error (expr,"missing type information" );return NULL ;
@@ -1678,7 +1689,6 @@ static struct symbol *evaluate_addressof(struct expression *expr) } ctype = op->ctype; *expr = *op->unop; - expr->flags =EXPR_FLAG_NONE ;if (expr->type ==EXPR_SYMBOL ) {struct symbol *sym = expr->symbol;
@@ -1945,6 +1955,12 @@ static struct expression *evaluate_offset(struct expression *expr, unsigned long* we ever take the address of this member dereference.. */ add->ctype = &lazy_ptr_ctype; +/* +* An address constant +/- an integer constant expression +* yields an address constant again [6.6(7)]. +*/ + add->flags |= expr->flags; +return add; }
expression.c +8 -0
@@ -437,6 +437,14 @@ struct token *primary_expression(struct token *token, struct expression **tree) } expr->symbol_name = token->ident; expr->symbol = sym; + +/* +* A pointer to an lvalue designating a static storage +* duration object is an address constant [6.6(9)]. +*/ +if (sym && (sym->ctype.modifiers & (MOD_TOPLEVEL |MOD_STATIC ))) +expr_set_flag (&expr->flags,EXPR_FLAG_ADDR_CONST_EXPR ); + token = next;break ; }
Signature · 1 line
2.7.0
Re: [PATCH v2 06/13] expression, evaluate: add support for recognizing address constants
Quoted 21 lines · Nicolai Stange
Address constants [6.6(9)] constitute one of the types of constant expressions allowed in initializers [6.6(7)] for static storage duration objects [6.7.8(4)].
Introduce support for recognizing address constants created either
- explicitly by referencing a static storage duration object by means of the unary & operator
- or implicitly by the use of an expression of array or function type.Treat string literals as address constants.
Initially tag an expression as being an address constant at the primary expression level, i.e. upon encountering a symbol designating an object of static storage duration in
primary_expression().Carry these flags over to the *-preop wrapped expression created by
evaluate_symbol_expression().For the special case of string literals, tag them as address constants in
evaluate_string().
Quoted 7 lines
Take care in
evaluate_ptr_add()andevaluate_offset()to properly propagate the address constness flags from subexpressions to their parent expressions, namely the array ([]) or structure member dereference (->, .) expressions.Finally, do not strip away an *-preop wrapped expression's constness flags in
evaluate_addressof().
There is two much thing in the description, this is a sure sign that the patch should be splitted, like putting the part concerning strings in a separate one.
evaluate.c static struct symbol *evaluate_addressof(struct expression *expr) · line 1681
Quoted 5 lines
evaluate.c +0
-1@@ -1678,7 +1689,6 @@ static struct symbol *evaluate_addressof(struct expression *expr) } ctype = op->ctype; *expr = *op->unop; - expr->flags =EXPR_FLAG_NONE ;
Is this because it's already initialized so by the allocator? if so, it can be moved to the patch that add this initialization (because it's unrelated to the current change).
Good otherwise.
Luc
Re: [PATCH v2 06/13] expression, evaluate: add support for recognizing address constants
In reply to Nicolai Stangeevaluate.c static struct symbol *evaluate_ptr_add(struct expression *expr, struct symbol *i · line 573
Quoted 9 lines · Nicolai Stange
evaluate.c +5
-0@@ -563,6 +566,14 @@ static struct symbol *evaluate_ptr_add(struct expression *expr, struct symbol *iclassify_type (degenerate (expr->left), &ctype); base =examine_pointer_target (ctype); +/* +* An address constant +/- an integer constant expression +* yields an address constant again [6.6(7)]. +*/ + if((expr->left->flags & EXPR_FLAG_ADDR_CONST_EXPR) &&
Missing ' ' in 'if('
PATCH v2 7/13 evaluate: check static storage duration objects' intializers' constness
In reply to Nicolai StangeInitializers of static storage duration objects shall be constant expressions [6.7.8(4)].
Warn if that requirement is not met and the -Wstatic-initializer-not-const flag has been given on sparse's command line.
Identify static storage duration objects by having either of MOD_TOPLEVEL or MOD_STATIC set.
Check an initializer's constness at the lowest possible subobject level, i.e. at the level of the "assignment-expression" production in [6.7.8].
For compound objects, make handle_list_initializer() pass the surrounding object's storage duration modifiers down to handle_simple_initializer() at subobject initializer evaluation.
--- evaluate.c | 26 ++++++++++- lib.c | 2 + lib.h | 2 +- sparse.1 | 7 +++ validation/constexpr-init.c | 110 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 validation/constexpr-init.c
evaluate.c +25 -1
@@ -2468,6 +2468,7 @@ static void handle_list_initializer(struct expression *expr, {struct expression *e, *last =NULL , *top =NULL , *next;int jumped =0 ; +unsigned long old_modifiers; FOR_EACH_PTR(expr->expr_list, e) {struct expression **v;
@@ -2522,8 +2523,21 @@ found:else v = &top->ident_expression; -if (handle_simple_initializer (v,1 , lclass, top->ctype)) +/* +* Temporarily copy storage modifiers down from +* surrounding type such that +* handle_simple_initializer() can check +* initializations of subobjects with static storage +* duration. +*/ + old_modifiers = top->ctype->ctype.modifiers; + top->ctype->ctype.modifiers = + old_modifiers | (ctype->ctype.modifiers &MOD_STORAGE ); +if (handle_simple_initializer (v,1 , lclass, top->ctype)) { + top->ctype->ctype.modifiers = old_modifiers;continue ; + } + top->ctype->ctype.modifiers = old_modifiers;if (!(lclass &TYPE_COMPOUND )) {warning (e->pos,"bogus scalar initializer" );
@@ -2633,6 +2647,16 @@ static int handle_simple_initializer(struct expression **ep, int nested,if (!evaluate_expression (e))return 1 ;compatible_assignment_types (e, ctype, ep,"initializer" ); +/* +* Initializers for static storage duration objects +* shall be constant expressions or a string literal [6.7.8(4)]. +*/ +if ((ctype->ctype.modifiers & (MOD_TOPLEVEL |MOD_STATIC )) && + !(e->flags & (EXPR_FLAG_ARITH_CONST_EXPR + |EXPR_FLAG_ADDR_CONST_EXPR )) && + Wstatic_initializer_not_const) +warning (e->pos,"initializer for static storage duration object is not a constant expression" ); +return 1 ; }
lib.c +2 -0
@@ -241,6 +241,7 @@ int Wtypesign = 0;int Wundef =0 ;int Wuninitialized =1 ;int Wvla =1 ; +int Wstatic_initializer_not_const =0 ;int dbg_entry =0 ;int dbg_dead =0 ;
@@ -464,6 +465,7 @@ static const struct warning { { "undef", &Wundef }, { "uninitialized", &Wuninitialized }, { "vla", &Wvla }, + { "static-initializer-not-const", &Wstatic_initializer_not_const}, }; enum {
lib.h +1 -1
@@ -127,7 +127,7 @@ extern int Wtypesign;extern int Wundef;extern int Wuninitialized;extern int Wvla; - +extern int Wstatic_initializer_not_const;extern int dbg_entry;extern int dbg_dead;
sparse.1 +7 -0
@@ -308,6 +308,13 @@ C99 does not specify the sizeof a _Bool. gcc uses 1. Sparse does not issue these warnings by default. . .TP +.B \-Wstatic-initializer-not-const +Warn when initializing an object of static storage duration with an initializer +which is not a constant expression. + +Sparse does not issue these warnings by default. +. +.TP .B \-Wtransparent\-union Warn about any declaration using the GCC extension \fB__attribute__((transparent_union))\fR.
validation/constexpr-init.c new file +110 -0
110 lines not on this page · raw message
Signature · 1 line
2.7.0
Re: [PATCH v2 07/13] evaluate: check static storage duration objects' intializers' constness
Quoted 9 lines · Nicolai Stange
Initializers of static storage duration objects shall be constant expressions [6.7.8(4)].
Warn if that requirement is not met and the -Wstatic-initializer-not-const flag has been given on sparse's command line.
Identify static storage duration objects by having either of
MOD_TOPLEVELorMOD_STATICset.
Quoted 7 lines
Check an initializer's constness at the lowest possible subobject level, i.e. at the level of the "assignment-expression" production in [6.7.8].
For compound objects, make
handle_list_initializer()pass the surrounding object's storage duration modifiers down tohandle_simple_initializer()at subobject initializer evaluation.
Better here also to split the patch in two:
one add the -W flag flag and another one which will use it.
ch > Signed-off-by: Nicolai Stange <[email protected]>
Quoted 31 lines
--- evaluate.c | 26 ++++++++++- lib.c | 2 + lib.h | 2 +- sparse.1 | 7 +++ validation/constexpr-init.c | 110 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 validation/constexpr-init.cevaluate.c +6
-1@@ -2468,6 +2468,7 @@ static void handle_list_initializer(struct expression *expr, {struct expression *e, *last =NULL , *top =NULL , *next;int jumped =0 ; +unsigned long old_modifiers; FOR_EACH_PTR(expr->expr_list, e) {struct expression **v;@@ -2522,8 +2523,21 @@ found:else v = &top->ident_expression; -if (handle_simple_initializer (v,1 , lclass, top->ctype)) +/* +* Temporarily copy storage modifiers down from +* surrounding type such that +* handle_simple_initializer() can check +* initializations of subobjects with static storage
evaluate.c found: · line 2540
Quoted 10 lines
evaluate.c +9
-0+* duration. +*/ + old_modifiers = top->ctype->ctype.modifiers; + top->ctype->ctype.modifiers = + old_modifiers | (ctype->ctype.modifiers &MOD_STORAGE ); +if (handle_simple_initializer (v,1 , lclass, top->ctype)) { + top->ctype->ctype.modifiers = old_modifiers;continue ; + } + top->ctype->ctype.modifiers = old_modifiers;
I don't understand why saving the mods is needed.
It feels hackish to me. Isn't it because something is done wrongly at another level or maybe handle_simple_initializer() need an additional arg or so?
Quoted 1 line
evaluate.c +0
-0@@ -2633,6 +2647,16 @@ static int handle_simple_initializer(struct expression **ep, int nested,
...
evaluate.c static int handle_simple_initializer(struct expression **ep, int nested, · line 2658
Quoted 1 line
evaluate.c +1
-0+warning (e->pos,"initializer for static storage duration object is not a constant expression" );
This is quite longish message.
What about something like "non-constant initializer"?
lib.c int Wtypesign = 0; · line 244
Quoted 8 lines
lib.c +1
-0@@ -241,6 +241,7 @@ int Wtypesign = 0;int Wundef =0 ;int Wuninitialized =1 ;int Wvla =1 ; +int Wstatic_initializer_not_const =0 ;
Here also it's quite longish. Yes I'm a lazy typer :) What about simply -Wconst-initializer ?
One thing that could be added (later and in another patch) is to set it by default when the C99 variant is selected.
lib.h extern int Wtypesign; · line 130
Quoted 11 lines
lib.c +0
-0enum {lib.h +1
-1@@ -127,7 +127,7 @@ extern int Wtypesign;extern int Wundef;extern int Wuninitialized;extern int Wvla; - +extern int Wstatic_initializer_not_const;
Better to leave the blank line where it was.
Luc
Re: [PATCH v2 07/13] evaluate: check static storage duration objects' intializers' constness
Quoted 14 lines · Luc Van Oostenryck
Quoted 13 lines · Nicolai Stange
Initializers of static storage duration objects shall be constant expressions [6.7.8(4)].
Warn if that requirement is not met and the -Wstatic-initializer-not-const flag has been given on sparse's command line.
Identify static storage duration objects by having either of
MOD_TOPLEVELorMOD_STATICset.Check an initializer's constness at the lowest possible subobject level, i.e. at the level of the "assignment-expression" production in [6.7.8].
Quoted 6 lines
Quoted 3 lines
For compound objects, make
handle_list_initializer()pass the surrounding object's storage duration modifiers down tohandle_simple_initializer()at subobject initializer evaluation.Better here also to split the patch in two:
one add the -W flag flag and another one which will use it.
Introducing a flag without any functionality attached to it feels wrong for me. For example, where to update the manpage? Before or after actual functionality is introduced?
Quoted 39 lines
ch > Signed-off-by: Nicolai Stange <[email protected]>
Quoted 37 lines
--- evaluate.c | 26 ++++++++++- lib.c | 2 + lib.h | 2 +- sparse.1 | 7 +++ validation/constexpr-init.c | 110 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 validation/constexpr-init.cevaluate.c +12
-1@@ -2468,6 +2468,7 @@ static void handle_list_initializer(struct expression *expr, {struct expression *e, *last =NULL , *top =NULL , *next;int jumped =0 ; +unsigned long old_modifiers; FOR_EACH_PTR(expr->expr_list, e) {struct expression **v;@@ -2522,8 +2523,21 @@ found:else v = &top->ident_expression; -if (handle_simple_initializer (v,1 , lclass, top->ctype)) +/* +* Temporarily copy storage modifiers down from +* surrounding type such that +* handle_simple_initializer() can check +* initializations of subobjects with static storage +* duration. +*/ + old_modifiers = top->ctype->ctype.modifiers; + top->ctype->ctype.modifiers = + old_modifiers | (ctype->ctype.modifiers &MOD_STORAGE ); +if (handle_simple_initializer (v,1 , lclass, top->ctype)) {
Quoted 8 lines
evaluate.c found: · line 2540
Quoted 4 lines
evaluate.c +3
-0+ top->ctype->ctype.modifiers = old_modifiers;continue ; + } + top->ctype->ctype.modifiers = old_modifiers;I don't understand why saving the mods is needed.
It feels hackish to me. Isn't it because something is done wrongly at another level or maybehandle_simple_initializer()need an additional arg or so?
It _is_ hackish and I will change to add an additional argument to handle_simple_initializer().
Quoted 6 lines
Quoted 1 line
evaluate.c +0
-0@@ -2633,6 +2647,16 @@ static int handle_simple_initializer(struct expression **ep, int nested,...
evaluate.c static int handle_simple_initializer(struct expression **ep, int nested, · line 2658
Quoted 1 line
evaluate.c +1
-0+warning (e->pos,"initializer for static storage duration object is not a constant expression" );This is quite longish message.
What about something like "non-constant initializer"?
That could be misleading:
static const int a = 1;
static const int b = a;
is forbidden, but obiously, 'a' is constant.
I'd like to keep the C99 term "constant expression", as well as "initializer" and "static".
I could s/storage duration//.
Quoted 11 lines
lib.c int Wtypesign = 0; · line 244
Quoted 8 lines
lib.c +1
-0@@ -241,6 +241,7 @@ int Wtypesign = 0;int Wundef =0 ;int Wuninitialized =1 ;int Wvla =1 ; +int Wstatic_initializer_not_const =0 ;Here also it's quite longish. Yes I'm a lazy typer :) What about simply -Wconst-initializer ?
Josh Triplett wrote in his replies to my RFC series:
Shouldn't it be something like -Wnon-constant-initializer, since that's what it checks for?
I conclude that we generally want to have -Wwhat-is-checked. Now, it is the *non*-constant initializers that are being checked for.
Unfortunately, -WnoXXXXXXX seems to get misinterpreted as "switch XXXXXXX" off by sparse's command line parsing.
In this case "switch n-constant-initializer off".
(I did not verify that by reading code, just by trying it out and failing, so just a guess).
The -Wstatic-initializer-not-const choice made in the current series is simply a workaround, any better suggestions welcome!
I'm also fine with -Wstatic-initializer.
Comments?
Quoted 4 lines
One thing that could be added (later and in another patch) is to set it by default when the C99 variant is selected.
Quoted 13 lines
lib.h extern int Wtypesign; · line 130
Quoted 11 lines
lib.c +0
-0enum {lib.h +1
-1@@ -127,7 +127,7 @@ extern int Wtypesign;extern int Wundef;extern int Wuninitialized;extern int Wvla; - +extern int Wstatic_initializer_not_const;Better to leave the blank line where it was.
Yes, indeed :P.
Quoted 2 lines
Luc
Re: [PATCH v2 07/13] evaluate: check static storage duration objects' intializers' constness
Quoted 7 lines · Nicolai Stange
Quoted 2 lines · Luc Van Oostenryck
Better here also to split the patch in two:
one add the -W flag flag and another one which will use it.Introducing a flag without any functionality attached to it feels wrong for me. For example, where to update the manpage? Before or after actual functionality is introduced?
For me it's fine when it's all part of the same serie. The update to the manpage should be done when adding the flag, before using it.
Quoted 12 lines
Quoted 6 lines
Quoted 1 line
(quoted diff) +0
-0@@ -2633,6 +2647,16 @@ static int handle_simple_initializer(struct expression **ep, int nested,...
evaluate.c static int handle_simple_initializer(struct expression **ep, int nested, · line 2658
Quoted 1 line
evaluate.c +1
-0+warning (e->pos,"initializer for static storage duration object is not a constant expression" );This is quite longish message.
What about something like "non-constant initializer"?That could be misleading:
static const int a = 1;
static const int b = a;
Quoted 6 lines
is forbidden, but obiously, 'a' is constant.
I'd like to keep the C99 term "constant expression", as well as "initializer" and "static".
I could s/storage duration//.
Or "non-constant initializer for static object"?
Quoted 9 lines
Quoted 4 lines
Quoted 1 line
+int
Wstatic_initializer_not_const= 0;Here also it's quite longish. Yes I'm a lazy typer :) What about simply -Wconst-initializer ?
Josh Triplett wrote in his replies to my RFC series:
Shouldn't it be something like -Wnon-constant-initializer, since that's what it checks for?
Quoted 8 lines
I conclude that we generally want to have -Wwhat-is-checked. Now, it is the *non*-constant initializers that are being checked for.
Unfortunately, -WnoXXXXXXX seems to get misinterpreted as "switch XXXXXXX" off by sparse's command line parsing.
In this case "switch n-constant-initializer off".
(I did not verify that by reading code, just by trying it out and failing, so just a guess).
Yes the code disable warning flags that begin by 'no' or 'no-' .
I see things a bit more loosely: -Wfoobar could means that we will check and warn something related to 'foobar' but yes it's certainly better to keep the logic in the direction "warn if 'foobar' is encountered".
Quoted 6 lines
The -Wstatic-initializer-not-const choice made in the current series is simply a workaround, any better suggestions welcome!
I'm also fine with -Wstatic-initializer.
Comments?
I dunno.
Better to leave it so for now, I think.
Luc
Re: [PATCH v2 07/13] evaluate: check static storage duration objects' intializers' constness
Quoted 11 lines · Luc Van Oostenryck
Quoted 6 lines · Nicolai Stange
The -Wstatic-initializer-not-const choice made in the current series is simply a workaround, any better suggestions welcome!
I'm also fine with -Wstatic-initializer.
Comments?
I dunno.
Better to leave it so for now, I think.
I see that clang has -Wconstexpr-not-const, which is not too long and still quite explicit and can be used for others things than initializers.
Luc
Re: [PATCH v2 07/13] evaluate: check static storage duration objects' intializers' constness
In reply to Luc Van OostenryckQuoted 59 lines · Luc Van Oostenryck
Quoted 9 lines · Nicolai Stange
Initializers of static storage duration objects shall be constant expressions [6.7.8(4)].
Warn if that requirement is not met and the -Wstatic-initializer-not-const flag has been given on sparse's command line.
Identify static storage duration objects by having either of
MOD_TOPLEVELorMOD_STATICset.Quoted 7 lines
Check an initializer's constness at the lowest possible subobject level, i.e. at the level of the "assignment-expression" production in [6.7.8].
For compound objects, make
handle_list_initializer()pass the surrounding object's storage duration modifiers down tohandle_simple_initializer()at subobject initializer evaluation.Better here also to split the patch in two:
one add the -W flag flag and another one which will use it.ch > Signed-off-by: Nicolai Stange <[email protected]>
Quoted 37 lines
--- evaluate.c | 26 ++++++++++- lib.c | 2 + lib.h | 2 +- sparse.1 | 7 +++ validation/constexpr-init.c | 110 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 validation/constexpr-init.cevaluate.c +12
-1@@ -2468,6 +2468,7 @@ static void handle_list_initializer(struct expression *expr, {struct expression *e, *last =NULL , *top =NULL , *next;int jumped =0 ; +unsigned long old_modifiers; FOR_EACH_PTR(expr->expr_list, e) {struct expression **v;@@ -2522,8 +2523,21 @@ found:else v = &top->ident_expression; -if (handle_simple_initializer (v,1 , lclass, top->ctype)) +/* +* Temporarily copy storage modifiers down from +* surrounding type such that +* handle_simple_initializer() can check +* initializations of subobjects with static storage +* duration. +*/ + old_modifiers = top->ctype->ctype.modifiers; + top->ctype->ctype.modifiers = + old_modifiers | (ctype->ctype.modifiers &MOD_STORAGE ); +if (handle_simple_initializer (v,1 , lclass, top->ctype)) {
Quoted 8 lines
evaluate.c found: · line 2540
Quoted 4 lines
evaluate.c +3
-0+ top->ctype->ctype.modifiers = old_modifiers;continue ; + } + top->ctype->ctype.modifiers = old_modifiers;I don't understand why saving the mods is needed.
It feels hackish to me. Isn't it because something is done wrongly at another level or maybehandle_simple_initializer()need an additional arg or so?
I haven't addressed this one yet, so: giving handle_simple_initializer() an additional flag won't suffice since it can recursively call into handle_list_initializer() again. Thus, handle_simple_initializer() would need an additional argument, too. Plus we'd need a enw entry point examining the top level modifiers for staticness.
I thought it would be much easier and straight forward to temporarily flag the elements of an initializer list as static if the parent is in global context.
The "temporarily" in there is of course argueable...
Quoted 39 lines
Quoted 1 line
evaluate.c +0
-0@@ -2633,6 +2647,16 @@ static int handle_simple_initializer(struct expression **ep, int nested,...
evaluate.c static int handle_simple_initializer(struct expression **ep, int nested, · line 2658
Quoted 1 line
evaluate.c +1
-0+warning (e->pos,"initializer for static storage duration object is not a constant expression" );This is quite longish message.
What about something like "non-constant initializer"?lib.c int Wtypesign = 0; · line 244
Quoted 8 lines
lib.c +1
-0@@ -241,6 +241,7 @@ int Wtypesign = 0;int Wundef =0 ;int Wuninitialized =1 ;int Wvla =1 ; +int Wstatic_initializer_not_const =0 ;Here also it's quite longish. Yes I'm a lazy typer :) What about simply -Wconst-initializer ?
One thing that could be added (later and in another patch) is to set it by default when the C99 variant is selected.
lib.h extern int Wtypesign; · line 130
Quoted 11 lines
lib.c +0
-0enum {lib.h +1
-1@@ -127,7 +127,7 @@ extern int Wtypesign;extern int Wundef;extern int Wuninitialized;extern int Wvla; - +extern int Wstatic_initializer_not_const;Better to leave the blank line where it was.
Luc
PATCH v2 8/13 expression: recognize references to labels as address constants
In reply to Nicolai StangeAs an extension, GCC allows labels to be referenced a la
label1:
...
void *ptr = &&label1;
Tag these label references as being address constants allowing them to be used as initializers for objects of static storage duration.
--- expression.c | 1 + 1 file changed, 1 insertion(+)
expression.c +1 -0
@@ -683,6 +683,7 @@ static struct token *unary_expression(struct token *token, struct expression **t sym->ctype.modifiers |=MOD_ADDRESSABLE ;add_symbol (&function_computed_target_list, sym); } +expr_set_flag (&label->flags,EXPR_FLAG_ADDR_CONST_EXPR ); label->label_symbol = sym; *tree = label;return token->next->next;
Signature · 1 line
2.7.0
Re: [PATCH v2 08/13] expression: recognize references to labels as address constants
Quoted 8 lines · Nicolai Stange
As an extension, GCC allows labels to be referenced a la
label1:
...void *ptr = &&label1;Tag these label references as being address constants allowing them to be used as initializers for objects of static storage duration.
Would be good to add afew test case to show that.
Luc
PATCH v2 9/13 expression: examine constness of __builtin_offsetof at evaluation only
In reply to Nicolai StangeCurrently, the determination of a __builtin_offsetof() expressions' constness flags is done in two steps:
- Several flags are speculatively set at expression parsing time
- and possibly cleared again at evaluation if the member expression includes a non-const array index like in __builtin_offsetof(struct A, a.b[non_const_foo])
For consistency with other expression types' evaluation, defer the determination of a __builtin_offsetof() expression's constness to evaluation time, too.
Furthermore, carry an array index expression's constness flags through the implicit cast to size_t type.
--- evaluate.c | 13 ++++++++----- expression.c | 3 --- validation/constexpr-offsetof.c | 21 +++++++++++++++++++++ 3 files changed, 29 insertions(+), 8 deletions(-) create mode 100644 validation/constexpr-offsetof.c
evaluate.c +8 -5
@@ -3001,7 +3001,6 @@ static struct symbol *evaluate_offsetof(struct expression *expr) } ctype = field; expr->type =EXPR_VALUE ; - expr->flags =EXPR_FLAG_NONE ;expr_set_flag (&expr->flags,EXPR_FLAG_INT_CONST_EXPR ); expr->value = offset; expr->taint =0 ;
@@ -3020,7 +3019,6 @@ static struct symbol *evaluate_offsetof(struct expression *expr) ctype = ctype->ctype.base_type;if (!expr->index) { expr->type =EXPR_VALUE ; - expr->flags =EXPR_FLAG_NONE ;expr_set_flag (&expr->flags,EXPR_FLAG_INT_CONST_EXPR ); expr->value =0 ; expr->taint =0 ;
@@ -3028,13 +3026,18 @@ static struct symbol *evaluate_offsetof(struct expression *expr) }else {struct expression *idx = expr->index, *m;struct symbol *i_type =evaluate_expression (idx); +unsigned old_idx_flags;int i_class =classify_type (i_type, &i_type); +if (!is_int (i_class)) {expression_error (expr,"non-integer index" );return NULL ; }unrestrict (idx, i_class, &i_type); + old_idx_flags = idx->flags; idx =cast_to (idx, size_t_ctype); + idx->flags |= old_idx_flags; +expr_flags_decay_consts (&idx->flags); m =alloc_const_expression (expr->pos,bits_to_bytes (ctype->bit_size)); m->ctype = size_t_ctype;
@@ -3045,19 +3048,19 @@ static struct symbol *evaluate_offsetof(struct expression *expr) expr->op ='*' ; expr->ctype = size_t_ctype; expr->flags = m->flags & idx->flags; +expr_flags_decay_consts (&expr->flags); } }if (e) {struct expression *copy =__alloc_expression (0 ); -unsigned char flags_mask =EXPR_FLAG_NONE ; *copy = *expr;if (e->type ==EXPR_OFFSETOF ) e->in = ctype;if (!evaluate_expression (e))return NULL ; expr->type =EXPR_BINOP ; -expr_set_flag (&flags_mask,EXPR_FLAG_INT_CONST_EXPR ); - expr->flags = e->flags & copy->flags & flags_mask; + expr->flags = e->flags & copy->flags; +expr_flags_decay_consts (&expr->flags); expr->op ='+' ; expr->ctype = size_t_ctype; expr->left = copy;
expression.c +0 -3
@@ -199,7 +199,6 @@ static struct token *builtin_offsetof_expr(struct token *token,return expect (token,')' ,"at end of __builtin_offset" );case SPECIAL_DEREFERENCE : e =alloc_expression (token->pos,EXPR_OFFSETOF ); -expr_set_flag (&e->flags,EXPR_FLAG_INT_CONST_EXPR ); e->op ='[' ; *p = e; p = &e->down;
@@ -207,7 +206,6 @@ static struct token *builtin_offsetof_expr(struct token *token,case '.' : token = token->next; e =alloc_expression (token->pos,EXPR_OFFSETOF ); -expr_set_flag (&e->flags,EXPR_FLAG_INT_CONST_EXPR ); e->op ='.' ;if (token_type (token) !=TOKEN_IDENT ) {sparse_error (token->pos,"Expected member name" );
@@ -219,7 +217,6 @@ static struct token *builtin_offsetof_expr(struct token *token,case '[' : token = token->next; e =alloc_expression (token->pos,EXPR_OFFSETOF ); -expr_set_flag (&e->flags,EXPR_FLAG_INT_CONST_EXPR ); e->op ='[' ; token =parse_expression (token, &e->index); token = expect(token, ']',
validation/constexpr-offsetof.c new file +21 -0
@@ -0,0 +1,21 @@ +struct A { +int a[1 ]; +int b; +}; + +extern int c; + +static int o[] = { + [__builtin_offsetof(struct A, b)] = 0, // OK + [__builtin_offsetof(struct A, a[0])] = 0, // OK + [__builtin_offsetof(struct A, a[0*0])] = 0, // OK + [__builtin_offsetof(struct A, a[c])] = 0 // KO +}; + +/* +* check-name: __builtin_offsetof() constness verification. +* +* check-error-start +constexpr-offsetof.c:12:39: error: bad constant expression +* check-error-end +*/
Signature · 1 line
2.7.0
Re: [PATCH v2 09/13] expression: examine constness of __builtin_offsetof at evaluation only
Quoted 7 lines · Nicolai Stange
Currently, the determination of a
__builtin_offsetof()expressions' constness flags is done in two steps:
- Several flags are speculatively set at expression parsing time
- and possibly cleared again at evaluation if the member expression includes a non-const array index like in__builtin_offsetof(struct A, a.b[non_const_foo])
Quoted 6 lines
For consistency with other expression types' evaluation, defer the determination of a
__builtin_offsetof()expression's constness to evaluation time, too.Furthermore, carry an array index expression's constness flags through the implicit cast to
size_ttype.
Better to split this into two patches.
Quoted 9 lines
evaluate.c +2
-0@@ -3028,13 +3026,18 @@ static struct symbol *evaluate_offsetof(struct expression *expr) }else {struct expression *idx = expr->index, *m;struct symbol *i_type =evaluate_expression (idx); +unsigned old_idx_flags;int i_class =classify_type (i_type, &i_type); +if (!is_int (i_class)) {expression_error (expr,"non-integer index" );
evaluate.c static struct symbol *evaluate_offsetof(struct expression *expr) · line 3043
Quoted 10 lines
evaluate.c +3
-0return NULL ; }unrestrict (idx, i_class, &i_type); + old_idx_flags = idx->flags; idx =cast_to (idx, size_t_ctype); + idx->flags |= old_idx_flags; +expr_flags_decay_consts (&idx->flags); m =alloc_const_expression (expr->pos,bits_to_bytes (ctype->bit_size)); m->ctype = size_t_ctype;
It's not clear at all to me why this is needed.
Why cast_to() can't set itself the right value for idx->flags?
Luc
Re: [PATCH v2 09/13] expression: examine constness of __builtin_offsetof at evaluation only
Quoted 31 lines · Luc Van Oostenryck
Quoted 7 lines · Nicolai Stange
Currently, the determination of a
__builtin_offsetof()expressions' constness flags is done in two steps:
- Several flags are speculatively set at expression parsing time
- and possibly cleared again at evaluation if the member expression includes a non-const array index like in__builtin_offsetof(struct A, a.b[non_const_foo])Quoted 6 lines
For consistency with other expression types' evaluation, defer the determination of a
__builtin_offsetof()expression's constness to evaluation time, too.Furthermore, carry an array index expression's constness flags through the implicit cast to
size_ttype.Better to split this into two patches.
Quoted 14 lines
evaluate.c +3
-0@@ -3028,13 +3026,18 @@ static struct symbol *evaluate_offsetof(struct expression *expr) }else {struct expression *idx = expr->index, *m;struct symbol *i_type =evaluate_expression (idx); +unsigned old_idx_flags;int i_class =classify_type (i_type, &i_type); +if (!is_int (i_class)) {expression_error (expr,"non-integer index" );return NULL ; }unrestrict (idx, i_class, &i_type); + old_idx_flags = idx->flags; idx =cast_to (idx, size_t_ctype);
Quoted 8 lines
evaluate.c static struct symbol *evaluate_offsetof(struct expression *expr) · line 3043
Quoted 5 lines
evaluate.c +2
-0+ idx->flags |= old_idx_flags; +expr_flags_decay_consts (&idx->flags); m =alloc_const_expression (expr->pos,bits_to_bytes (ctype->bit_size)); m->ctype = size_t_ctype;It's not clear at all to me why this is needed.
Whycast_to()can't set itself the right value foridx->flags?
cast_to() is for implicit casts and to be honest, I don't know what the ->flags should be set to.
Also, this is the only place in the whole code where preserving the flags over a cast_to() call is needed.
Furthermore, we could get rid of the flags' save and restore by moving the calculation of expr->flags in front of the cast_to() invocation -- all other cast_to() actually do it this way.
However, I think, it's better to have the assignment to expr->flags grouped with the rest of the assignments to expr's members.
PATCH v2 10/13 symbol: flag builtins constant_p, safe_p and warning as constexprs
In reply to Nicolai StangeUnconditionally flag the expressions
__builtin_constant_p(),
__builtin_safe_p(),
__builtin_warning()
as being integer constant expressions.
--- symbol.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-)
symbol.c +5 -4
@@ -642,9 +642,10 @@ struct symbol *create_symbol(int stream, const char *name, int type, int namespareturn sym; } -static int evaluate_to_integer (struct expression *expr) +static int evaluate_to_int_const_expr (struct expression *expr) { expr->ctype = &int_ctype; +expr_set_flag (&expr->flags,EXPR_FLAG_INT_CONST_EXPR );return 1 ; }
@@ -749,17 +750,17 @@ out: }static struct symbol_op constant_p_op = { - .evaluate = evaluate_to_integer, + .evaluate = evaluate_to_int_const_expr, .expand = expand_constant_p };static struct symbol_op safe_p_op = { - .evaluate = evaluate_to_integer, + .evaluate = evaluate_to_int_const_expr, .expand = expand_safe_p };static struct symbol_op warning_op = { - .evaluate = evaluate_to_integer, + .evaluate = evaluate_to_int_const_expr, .expand = expand_warning };
Signature · 1 line
2.7.0
Re: [PATCH v2 10/13] symbol: flag builtins constant_p, safe_p and warning as constexprs
Quoted 5 lines · Nicolai Stange
Unconditionally flag the expressions
__builtin_constant_p(),
__builtin_safe_p(),
__builtin_warning()
as being integer constant expressions.
Good!
Luc
PATCH v2 11/13 evaluate: relax some constant expression rules for pointer expressions
In reply to Nicolai StangeThe official constraints on constant expressions [6.6] are insanely strict in that they do not allow some constructs commonly used in the wild.
Relax them by treating
- address constants cast to different pointer type as address constants again,
- address constants cast to integer type as integer constant expressions
- conditional expressions whose true and false branches both yield address constants as address constants,
- and conditional expressions whose condition is an address constant as an constant expression to the extent their true and false branches allow.
--- evaluate.c | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-)
evaluate.c +30 -5
@@ -1125,14 +1125,23 @@ static struct symbol *evaluate_conditional_expression(struct expression *expr) true = &expr->cond_true; } - expr->flags = (expr->conditional->flags & (*true)->flags & - expr->cond_false->flags); -expr_flags_decay_consts (&expr->flags);/* +* A conditional operator yields a particular constant +* expression type only if all of its three subexpressions are +* of that type [6.6(6), 6.6(8)]. +* As an extension, relax this restriction by allowing any +* constant expression type for the condition expression. +* * A conditional operator never yields an address constant * [6.6(9)]. +* However, as an extension, if the condition is any constant +* expression, and the true and false expressions are both +* address constants, mark the result as an address constant. */ -expr_clear_flag (&expr->flags,EXPR_FLAG_ADDR_CONST_EXPR ); +if (expr->conditional->flags & (EXPR_FLAG_ARITH_CONST_EXPR | +EXPR_FLAG_ADDR_CONST_EXPR )) + expr->flags = (*true)->flags &expr->cond_false->flags; +expr_flags_decay_consts (&expr->flags); lclass =classify_type (ltype, <ype); rclass =classify_type (rtype, &rtype);
@@ -2783,14 +2792,30 @@ static struct symbol *evaluate_cast(struct expression *expr)/* * Casts of integer literals to pointer type yield * address constants [6.6(9)]. +* +* As an extension, treat address constants cast to a +* different pointer type as adress constants again. +* +* As another extension, treat integer constant +* expressions (in contrast to literals) cast to +* pointer type as address constants. */ if (class1 &TYPE_PTR && - (target->flags &EXPR_FLAG_INT_CONST )) { + ((target->flags &EXPR_FLAG_INT_CONST_EXPR ) || + (target->flags &EXPR_FLAG_ADDR_CONST_EXPR ))) {expr_set_flag (&expr->flags,EXPR_FLAG_ADDR_CONST_EXPR ); + } }else { expr->flags = target->flags;expr_flags_decay_consts (&expr->flags); + +/* +* As an extension, treat address constants cast to +* integer type as an arithmetic constant. +*/ +if (expr->flags &EXPR_FLAG_ADDR_CONST_EXPR ) +expr_set_flag (&expr->flags,EXPR_FLAG_ARITH_CONST_EXPR );/* * Casts to numeric types never result in address * constants [6.6(9)].
Signature · 1 line
2.7.0
Re: [PATCH v2 11/13] evaluate: relax some constant expression rules for pointer expressions
Quoted 17 lines · Nicolai Stange
The official constraints on constant expressions [6.6] are insanely strict in that they do not allow some constructs commonly used in the wild.
Relax them by treating
- address constants cast to different pointer type as address constants again,
- address constants cast to integer type as integer constant expressions
- conditional expressions whose true and false branches both yield address constants as address constants,
- and conditional expressions whose condition is an address constant as an constant expression to the extent their true and false branches allow.
evaluate.c static struct symbol *evaluate_cast(struct expression *expr) · line 2797
Quoted 6 lines
evaluate.c +3
-0@@ -2783,14 +2792,30 @@ static struct symbol *evaluate_cast(struct expression *expr)/* * Casts of integer literals to pointer type yield * address constants [6.6(9)]. +* +* As an extension, treat address constants cast to a +* different pointer type as adress constants again.
Typo: s/adress/address/
Good otherwise.
Luc
PATCH v2 12/13 expression, evaluate: support compound literals as address constants
In reply to Nicolai StangeToplevel compound literals have got static storage duration [6.5.2.5(6)].
This implies that
1. their addresses are address constants [6.6(9)] and
2. their initializers must contain constant expressions only [6.5.2.5(3), 6.7.8(4)] .
Flag the anonymous symbol created at expression parsing time as having static storage duration if the compound literal occurs at top level scope.
Flag the whole expression as being an address constant at evaluation time if its corresponding anonymous symbol had been previously marked as having static storage duration.
--- evaluate.c | 2 ++ expression.c | 2 ++ validation/constexpr-compound-literal.c | 19 +++++++++++++++++++ 3 files changed, 23 insertions(+) create mode 100644 validation/constexpr-compound-literal.c
evaluate.c +2 -0
@@ -2770,6 +2770,8 @@ static struct symbol *evaluate_cast(struct expression *expr) addr->ctype = &lazy_ptr_ctype;/* Lazy eval */ addr->symbol = sym; +if (sym->ctype.modifiers &MOD_TOPLEVEL ) +expr_set_flag (&addr->flags,EXPR_FLAG_ADDR_CONST_EXPR ); expr->type =EXPR_PREOP ; expr->op ='*' ;
expression.c +2 -0
@@ -713,6 +713,8 @@ static struct token *cast_expression(struct token *token, struct expression **tr cast->cast_type = sym; token =expect (token,')' ,"at end of cast operator" );if (match_op (token,'{' )) { +if (toplevel (block_scope)) + sym->ctype.modifiers |=MOD_TOPLEVEL ;if (is_force)warning (sym->pos,"[force] in compound literal" );
validation/constexpr-compound-literal.c new file +19 -0
@@ -0,0 +1,19 @@ +static int *a = &(int ){1 };// OK +static int *b = &(int ){ *a };// KO + +static void foo (void ) +{ +int *b = &(int ){1 };// OK +int *c = &(int ){ *a };// OK +static int *d = &(int ){1 };// KO +} + +/* +* check-name: compound literal address constness verification +* check-command: sparse -Wstatic-initializer-not-const $file +* +* check-error-start +constexpr-compound-literal.c:2:25: warning: initializer for static storage duration object is not a constant expression +constexpr-compound-literal.c:8:27: warning: initializer for static storage duration object is not a constant expression +* check-error-end +*/
Signature · 1 line
2.7.0
Re: [PATCH v2 12/13] expression, evaluate: support compound literals as address constants
Quoted 8 lines · Nicolai Stange
Toplevel compound literals have got static storage duration [6.5.2.5(6)].
This implies that
1. their addresses are address constants [6.6(9)] and
2. their initializers must contain constant expressions only [6.5.2.5(3), 6.7.8(4)] .
Quoted 7 lines
Flag the anonymous symbol created at expression parsing time as having static storage duration if the compound literal occurs at top level scope.
Flag the whole expression as being an address constant at evaluation time if its corresponding anonymous symbol had been previously marked as having static storage duration.
Good.
Luc
PATCH v2 13/13 symbol: do not inherit storage modifiers from base types at examination
In reply to Nicolai StangeConsider the following code snippet:
static inline foo(int dummy, ...) {}
static int a = 0;
static void bar(void)
{
foo(0, a);
}
Sparse moans:
test.c:5:9: warning: initializer for static storage duration object
is not a constant expression
The cause can be tracked down as follows:
The anonymous node created by inline_function() for the variadic argument will get assigned to its base_type whatever the passed expression's ctype is. For the special case of a primary expression referencing a symbol, this ctype is the referenced symbol itself. Furthermore, inline_function() sets that symbol node's initializer to this expression.
Now, when the anonymous symbol node is evaluated, its base_type is handled in examine_base_type(). This applies the base_type's modifiers, i.e. the referenced symbol's MOD_STATIC in this case, to the inheriting ctype, that of the anonymous node, itself.
This in turn instructs the evaluation of the symbol's initializer to allow constant expressions only.
Do not inherit a base_type's storage related modifiers in examine_base_type().
--- symbol.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
symbol.c +2 -1
@@ -214,7 +214,8 @@ static struct symbol *examine_base_type(struct symbol *sym)if (!base_type || base_type->type ==SYM_PTR )return base_type; sym->ctype.as |= base_type->ctype.as; - sym->ctype.modifiers |= base_type->ctype.modifiers &MOD_PTRINHERIT ; + sym->ctype.modifiers |= base_type->ctype.modifiers &MOD_PTRINHERIT & + ~MOD_STORAGE ;concat_ptr_list ((struct ptr_list *)base_type->ctype.contexts, (struct ptr_list **)&sym->ctype.contexts);if (base_type->type ==SYM_NODE ) {
Signature · 1 line
2.7.0
Re: [PATCH v2 13/13] symbol: do not inherit storage modifiers from base types at examination
Quoted 27 lines · Nicolai Stange
Consider the following code snippet:
static inlinefoo(int dummy, ...){}
static int a = 0;
static void bar(void)
{
foo(0, a);
}Sparse moans:
test.c:5:9: warning: initializer for static storage duration object
is not a constant expressionThe cause can be tracked down as follows:
The anonymous node created byinline_function()for the variadic argument will get assigned to itsbase_typewhatever the passed expression's ctype is. For the special case of a primary expression referencing a symbol, this ctype is the referenced symbol itself. Furthermore,inline_function()sets that symbol node's initializer to this expression.Now, when the anonymous symbol node is evaluated, its
base_typeis handled inexamine_base_type(). This applies the base_type's modifiers, i.e. the referenced symbol'sMOD_STATICin this case, to the inheriting ctype, that of the anonymous node, itself.
This in turn instructs the evaluation of the symbol's initializer to allow constant expressions only.
Quoted 2 lines
Do not inherit a base_type's storage related modifiers in
examine_base_type().
For this one ...
there is a lot to say about, not about your patch but about what should be done.
It should be noted that the warning only occurs because:
1) the function is inlined.
2) the function is variadic and the symbol is given in the variable part.
3) the symbol doesn't need integer promotion.
4) MOD_STORAGE is part of MOD_PTRINHERIT.
It would be good to show these conditions in the test case.
What I see in the code about calls is that when conditions 1, 2 & 3 are met the argument to this variadic inline function is sorta used as-it-is while, I think, a sort of l-value to r-value conversion should be done. This conversion would essentially just adapting some of the modifiers:
clearing MOD_STORAGE (and maybe MOD_ADDRESSABLE).
For the condition 4), while digging in the code history (commit 822d3b1a222b), it can be seen that MOD_STORAGE was added to MOD_PTRINHERIT without a real need; quoting Linus:
This still keeps other attributes, like "pointer to 'static'". Useful? Probably not. I'll have to think about it.
Normally it's not a problem to have it because unneeded modifiers are generally filtered out or simply ignored.
So there is several ways to "fix" (or at least hide) this problem but it's not clear to me what really should be done.
I added Linus in CC, his opinion on the subject would be precious.
NB. While looking at this one I've noted several other problems with modifier
inheritance. I'll send an RFC in the coming hours or days.
Cheers,
Luc
Quoted 19 lines
Signed-off-by Nicolai Stange <[email protected]>--- symbol.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)symbol.c +2
-1@@ -214,7 +214,8 @@ static struct symbol *examine_base_type(struct symbol *sym)if (!base_type || base_type->type ==SYM_PTR )return base_type; sym->ctype.as |= base_type->ctype.as; - sym->ctype.modifiers |= base_type->ctype.modifiers &MOD_PTRINHERIT ; + sym->ctype.modifiers |= base_type->ctype.modifiers &MOD_PTRINHERIT & + ~MOD_STORAGE ;concat_ptr_list ((struct ptr_list *)base_type->ctype.contexts, (struct ptr_list **)&sym->ctype.contexts);if (base_type->type ==SYM_NODE ) {
Quoted 5 lines · Nicolai Stange
This is the second attempt to clean up and improve sparse's handling of constant expressions. After I got some helpful reviews from Josh Triplett and Luc Van Oostenryck on my initial RFC series, I feel quite comfortable with this now and dropped the 'RFC' tag in favour of 'v2'.
Great.
Quoted 9 lines
Quote from my initial 'RFC' cover letter regarding the structure of this series:
This patch series is split into four parts:
- The first part deals with the refactorization of the current integer constant expression handling and introduces some support for recognizing arithmetic expressions. [1-5/13]
- The second part introduces support for recognizing address constants.[6/13]
Quoted 10 lines
- The third part introduces a check for the constness of static storage duration objects' initializers. [7/13]
- The last part stems from my tests with the kernel. It contains things I missed in the first [9-10/13] and second [8,12/13] parts and relaxes some of the constraints on constant expressions [11/13]. For the last patch [13/13], please see below.
[...]
Although the patches of the fourth part, the fixup part, fit very well into the first two categories, their associated testcases, if any, depend on [7/13]. Thus, I dediced to keep the order as is.
Yes, it's fine. certainly so since you now added the -W flag. I really consider your [13/13] as a totally separate patch but it needs this series to see its effect.
Quoted 11 lines
Quote end.
The question from the initial 'RFC' series whether or not to relax the constexpr constraints, meaning that
a difference of address constants may yield an integer constant
in order to make the kernel's
ACPI_OFFSETmacro happy, is still unaddressed. However, if it turns out that we actually want to do so, this single issue can be easily handled by some follow up patch.
Yes, indeed.
Luc
Hi Luc,
thank you once again!
Quoted 29 lines · Luc Van Oostenryck
Quoted 5 lines · Nicolai Stange
This is the second attempt to clean up and improve sparse's handling of constant expressions. After I got some helpful reviews from Josh Triplett and Luc Van Oostenryck on my initial RFC series, I feel quite comfortable with this now and dropped the 'RFC' tag in favour of 'v2'.
Great.
Quoted 9 lines
Quote from my initial 'RFC' cover letter regarding the structure of this series:
This patch series is split into four parts:
- The first part deals with the refactorization of the current integer constant expression handling and introduces some support for recognizing arithmetic expressions. [1-5/13]
- The second part introduces support for recognizing address constants.[6/13]Quoted 10 lines
- The third part introduces a check for the constness of static storage duration objects' initializers. [7/13]
- The last part stems from my tests with the kernel. It contains things I missed in the first [9-10/13] and second [8,12/13] parts and relaxes some of the constraints on constant expressions [11/13]. For the last patch [13/13], please see below.
[...]
Although the patches of the fourth part, the fixup part, fit very well into the first two categories, their associated testcases, if any, depend on [7/13]. Thus, I dediced to keep the order as is.
Quoted 3 lines
Yes, it's fine. certainly so since you now added the -W flag. I really consider your [13/13] as a totally separate patch but it needs this series to see its effect.
You're right, it somehow feels like it could go separately. However, it is strictly needed in this form or another in order to avoid false positives with -Wstatic-initializer-not-const (see the commit message of [13/13] for an example). Because of that, I'd prefer to keep it with this series.
Quoted 16 lines
Quoted 11 lines
Quote end.
The question from the initial 'RFC' series whether or not to relax the constexpr constraints, meaning that
a difference of address constants may yield an integer constant
in order to make the kernel's
ACPI_OFFSETmacro happy, is still unaddressed. However, if it turns out that we actually want to do so, this single issue can be easily handled by some follow up patch.Yes, indeed.
Luc