semantic: Extend expression classification to detect integer constant expressions.
authorChristoph Mallon <christoph.mallon@gmx.de>
Thu, 20 Dec 2012 13:11:26 +0000 (14:11 +0100)
committerChristoph Mallon <christoph.mallon@gmx.de>
Thu, 20 Dec 2012 14:31:39 +0000 (15:31 +0100)
This is required for strict detection of null pointer constants and VLAs.

ast.c
ast.h
ast2firm.c
parser.c

diff --git a/ast.c b/ast.c
index f852a9d..fe99462 100644 (file)
--- a/ast.c
+++ b/ast.c
@@ -1630,7 +1630,7 @@ expression_classification_t is_linker_constant(const expression_t *expression)
        case EXPR_CONDITIONAL: {
                expression_t *const c = expression->conditional.condition;
                expression_classification_t const cclass = is_constant_expression(c);
-               if (cclass != EXPR_CLASS_CONSTANT)
+               if (cclass < EXPR_CLASS_CONSTANT)
                        return cclass;
 
                if (fold_constant_to_bool(c)) {
@@ -1720,7 +1720,7 @@ static expression_classification_t is_object_with_constant_address(const express
                array_access_expression_t const* const array_access =
                        &expression->array_access;
                expression_classification_t const idx_class = is_constant_expression(array_access->index);
-               if (idx_class != EXPR_CLASS_CONSTANT)
+               if (idx_class < EXPR_CLASS_CONSTANT)
                        return idx_class;
                expression_classification_t const ref_addr = is_object_with_constant_address(array_access->array_ref);
                expression_classification_t const ref_ptr  = is_constant_pointer(array_access->array_ref);
@@ -1746,7 +1746,7 @@ expression_classification_t is_constant_expression(const expression_t *expressio
        case EXPR_ENUM_CONSTANT:
        case EXPR_LITERAL_BOOLEAN:
        case EXPR_LITERAL_MS_NOOP:
-               return EXPR_CLASS_CONSTANT;
+               return EXPR_CLASS_INTEGER_CONSTANT;
 
        {
                type_t *type;
@@ -1759,7 +1759,6 @@ expression_classification_t is_constant_expression(const expression_t *expressio
                goto check_type;
 
        case EXPR_LITERAL_INTEGER:
-       case EXPR_LITERAL_FLOATINGPOINT:
                type = skip_typeref(expression->base.type);
                goto check_type;
 
@@ -1774,12 +1773,17 @@ expression_classification_t is_constant_expression(const expression_t *expressio
                goto check_type;
 
 check_type:
+               return is_type_valid(type) ? EXPR_CLASS_INTEGER_CONSTANT : EXPR_CLASS_ERROR;
+       }
+
+       case EXPR_LITERAL_FLOATINGPOINT: {
+               type_t *const type = skip_typeref(expression->base.type);
                return is_type_valid(type) ? EXPR_CLASS_CONSTANT : EXPR_CLASS_ERROR;
        }
 
        case EXPR_BUILTIN_CONSTANT_P: {
                expression_classification_t const c = is_constant_expression(expression->builtin_constant.value);
-               return c != EXPR_CLASS_ERROR ? EXPR_CLASS_CONSTANT : EXPR_CLASS_ERROR;
+               return c != EXPR_CLASS_ERROR ? EXPR_CLASS_INTEGER_CONSTANT : EXPR_CLASS_ERROR;
        }
 
        case EXPR_STRING_LITERAL:
@@ -1841,6 +1845,14 @@ check_type:
 
        case EXPR_UNARY_CAST: {
                type_t *const type = skip_typeref(expression->base.type);
+               if (is_type_integer(type)) {
+                       expression_t *const val = expression->unary.value;
+                       if (is_type_arithmetic(skip_typeref(val->base.type))) {
+                               return val->kind == EXPR_LITERAL_FLOATINGPOINT
+                                       ? EXPR_CLASS_INTEGER_CONSTANT
+                                       : is_constant_expression(val);
+                       }
+               }
                if (is_type_scalar(type))
                        return is_constant_expression(expression->unary.value);
                if (!is_type_valid(type))
@@ -1876,40 +1888,48 @@ check_type:
        }
 
        case EXPR_BINARY_LOGICAL_AND: {
-               expression_t const         *const left   = expression->binary.left;
-               expression_classification_t const lclass = is_constant_expression(left);
-               if (lclass != EXPR_CLASS_CONSTANT)
-                       return lclass;
+               expression_t const         *const left = expression->binary.left;
+               expression_classification_t const lcls = is_constant_expression(left);
+               if (lcls < EXPR_CLASS_CONSTANT)
+                       return lcls;
+               expression_classification_t const rcls = is_constant_expression(expression->binary.right);
+               if (lcls == EXPR_CLASS_INTEGER_CONSTANT && rcls == EXPR_CLASS_INTEGER_CONSTANT)
+                       return EXPR_CLASS_INTEGER_CONSTANT;
                if (!fold_constant_to_bool(left))
                        return EXPR_CLASS_CONSTANT;
-               return is_constant_expression(expression->binary.right);
+               return rcls < EXPR_CLASS_CONSTANT ? rcls : EXPR_CLASS_CONSTANT;
        }
 
        case EXPR_BINARY_LOGICAL_OR: {
-               expression_t const         *const left   = expression->binary.left;
-               expression_classification_t const lclass = is_constant_expression(left);
-               if (lclass != EXPR_CLASS_CONSTANT)
-                       return lclass;
+               expression_t const         *const left = expression->binary.left;
+               expression_classification_t const lcls = is_constant_expression(left);
+               if (lcls < EXPR_CLASS_CONSTANT)
+                       return lcls;
+               expression_classification_t const rcls = is_constant_expression(expression->binary.right);
+               if (lcls == EXPR_CLASS_INTEGER_CONSTANT && rcls == EXPR_CLASS_INTEGER_CONSTANT)
+                       return EXPR_CLASS_INTEGER_CONSTANT;
                if (fold_constant_to_bool(left))
                        return EXPR_CLASS_CONSTANT;
-               return is_constant_expression(expression->binary.right);
+               return rcls < EXPR_CLASS_CONSTANT ? rcls : EXPR_CLASS_CONSTANT;
        }
 
        case EXPR_COMPOUND_LITERAL:
                return is_constant_initializer(expression->compound_literal.initializer);
 
        case EXPR_CONDITIONAL: {
-               expression_t               *const condition = expression->conditional.condition;
-               expression_classification_t const cclass    = is_constant_expression(condition);
-               if (cclass != EXPR_CLASS_CONSTANT)
-                       return cclass;
-
-               if (fold_constant_to_bool(condition)) {
-                       expression_t const *const t = expression->conditional.true_expression;
-                       return t == NULL ? EXPR_CLASS_CONSTANT : is_constant_expression(t);
-               } else {
-                       return is_constant_expression(expression->conditional.false_expression);
-               }
+               expression_t               *const cond = expression->conditional.condition;
+               expression_classification_t const ccls = is_constant_expression(cond);
+               if (ccls < EXPR_CLASS_CONSTANT)
+                       return ccls;
+               expression_t         const *const t    = expression->conditional.true_expression;
+               expression_classification_t const tcls = t == NULL ? ccls : is_constant_expression(t);
+               expression_classification_t const fcls = is_constant_expression(expression->conditional.false_expression);
+               if (ccls == EXPR_CLASS_INTEGER_CONSTANT &&
+                   tcls == EXPR_CLASS_INTEGER_CONSTANT &&
+                   fcls == EXPR_CLASS_INTEGER_CONSTANT)
+                       return EXPR_CLASS_INTEGER_CONSTANT;
+               expression_classification_t const cls = fold_constant_to_bool(cond) ? tcls : fcls;
+               return cls < EXPR_CLASS_CONSTANT ? cls : EXPR_CLASS_CONSTANT;
        }
 
        case EXPR_ERROR:
diff --git a/ast.h b/ast.h
index f27f876..3a474e7 100644 (file)
--- a/ast.h
+++ b/ast.h
@@ -85,7 +85,8 @@ void change_indent(int delta);
 typedef enum expression_classification_t {
        EXPR_CLASS_VARIABLE,
        EXPR_CLASS_ERROR,
-       EXPR_CLASS_CONSTANT
+       EXPR_CLASS_CONSTANT,
+       EXPR_CLASS_INTEGER_CONSTANT
 } expression_classification_t;
 
 /**
index c0018ba..9ddd3b2 100644 (file)
@@ -2158,10 +2158,10 @@ static ir_node *handle_assume_compare(dbg_info *dbi,
        }
 
        expression_t *con = NULL;
-       if (is_local_variable(op1) && is_constant_expression(op2) == EXPR_CLASS_CONSTANT) {
+       if (is_local_variable(op1) && is_constant_expression(op2) != EXPR_CLASS_VARIABLE) {
                var = op1->reference.entity;
                con = op2;
-       } else if (is_constant_expression(op1) == EXPR_CLASS_CONSTANT && is_local_variable(op2)) {
+       } else if (is_constant_expression(op1) != EXPR_CLASS_VARIABLE && is_local_variable(op2)) {
                relation = get_inversed_relation(relation);
                var = op2->reference.entity;
                con = op1;
@@ -2444,7 +2444,7 @@ static void compare_to_control_flow(expression_t const *const expr, ir_node *con
                /* set branch prediction info based on __builtin_expect */
                if (is_builtin_expect(expr) && is_Cond(cond)) {
                        call_argument_t *const argument = expr->call.arguments->next;
-                       if (is_constant_expression(argument->expression) == EXPR_CLASS_CONSTANT) {
+                       if (is_constant_expression(argument->expression) != EXPR_CLASS_VARIABLE) {
                                bool               const cnst = fold_constant_to_bool(argument->expression);
                                cond_jmp_predicate const pred = cnst ? COND_JMP_PRED_TRUE : COND_JMP_PRED_FALSE;
                                set_Cond_jmp_pred(cond, pred);
@@ -2644,9 +2644,10 @@ static ir_node *compound_literal_addr(compound_literal_expression_t const *const
        type_t        *type        = expression->type;
        initializer_t *initializer = expression->initializer;
 
-       if (expression->global_scope ||
-               ((type->base.qualifiers & TYPE_QUALIFIER_CONST)
-           && is_constant_initializer(initializer) == EXPR_CLASS_CONSTANT)) {
+       if (expression->global_scope || (
+             type->base.qualifiers & TYPE_QUALIFIER_CONST &&
+             is_constant_initializer(initializer) != EXPR_CLASS_VARIABLE
+           )) {
                ir_entity *entity = create_initializer_entity(dbgi, initializer, type);
                return create_symconst(dbgi, entity);
        } else {
@@ -2745,7 +2746,7 @@ static void init_ir_types(void);
 
 ir_tarval *fold_constant_to_tarval(const expression_t *expression)
 {
-       assert(is_constant_expression(expression) == EXPR_CLASS_CONSTANT);
+       assert(is_constant_expression(expression) >= EXPR_CLASS_CONSTANT);
 
        bool constant_folding_old = constant_folding;
        constant_folding = true;
@@ -2771,7 +2772,7 @@ ir_tarval *fold_constant_to_tarval(const expression_t *expression)
 
 static complex_constant fold_complex_constant(const expression_t *expression)
 {
-       assert(is_constant_expression(expression) == EXPR_CLASS_CONSTANT);
+       assert(is_constant_expression(expression) >= EXPR_CLASS_CONSTANT);
 
        bool constant_folding_old = constant_folding;
        constant_folding = true;
@@ -3145,7 +3146,7 @@ static ir_node *builtin_constant_to_firm(
                const builtin_constant_expression_t *expression)
 {
        ir_mode *const mode = get_ir_mode_storage(expression->base.type);
-       bool     const v    = is_constant_expression(expression->value) == EXPR_CLASS_CONSTANT;
+       bool     const v    = is_constant_expression(expression->value) != EXPR_CLASS_VARIABLE;
        return create_Const_from_bool(mode, v);
 }
 
@@ -5152,7 +5153,7 @@ static ir_node *do_while_statement_to_firm(do_while_statement_t *statement)
 
        expression_t *const cond = statement->condition;
        /* Avoid an explicit body block in case of do ... while (0);. */
-       if (is_constant_expression(cond) == EXPR_CLASS_CONSTANT && !fold_constant_to_bool(cond)) {
+       if (is_constant_expression(cond) != EXPR_CLASS_VARIABLE && !fold_constant_to_bool(cond)) {
                /* do ... while (0);. */
                statement_to_firm(statement->body);
                jump_to_target(&continue_target);
@@ -5208,7 +5209,7 @@ static ir_node *for_statement_to_firm(for_statement_t *statement)
 
        /* Create the condition. */
        expression_t *const cond = statement->condition;
-       if (cond && (is_constant_expression(cond) != EXPR_CLASS_CONSTANT || !fold_constant_to_bool(cond))) {
+       if (cond && (is_constant_expression(cond) == EXPR_CLASS_VARIABLE || !fold_constant_to_bool(cond))) {
                jump_target body_target;
                init_jump_target(&body_target, NULL);
                expression_to_control_flow(cond, &body_target, &break_target);
index 74374c5..d49ec8d 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -860,14 +860,13 @@ static bool is_null_pointer_constant(const expression_t *expression)
                        expression = expression->unary.value;
        }
 
-       type_t *const type = skip_typeref(expression->base.type);
-       if (!is_type_integer(type))
-               return false;
        switch (is_constant_expression(expression)) {
-               case EXPR_CLASS_ERROR:    return true;
-               case EXPR_CLASS_CONSTANT: return !fold_constant_to_bool(expression);
-               default:                  return false;
+               case EXPR_CLASS_VARIABLE:         return false;
+               case EXPR_CLASS_ERROR:            return true;
+               case EXPR_CLASS_CONSTANT:         return false;
+               case EXPR_CLASS_INTEGER_CONSTANT: return !fold_constant_to_bool(expression);
        }
+       panic("invalid expression classification");
 }
 
 /**
@@ -1838,7 +1837,7 @@ static bool walk_designator(type_path_t *path, const designator_t *designator,
                        }
                } else {
                        expression_t *array_index = designator->array_index;
-                       if (is_constant_expression(array_index) != EXPR_CLASS_CONSTANT)
+                       if (is_constant_expression(array_index) < EXPR_CLASS_CONSTANT)
                                return true;
 
                        if (!is_type_array(type)) {
@@ -3558,7 +3557,7 @@ static type_t *construct_declarator_type(construct_type_t *construct_list,
 
                        if (size_expression != NULL) {
                                switch (is_constant_expression(size_expression)) {
-                               case EXPR_CLASS_CONSTANT: {
+                               case EXPR_CLASS_INTEGER_CONSTANT: {
                                        long const size = fold_constant_to_int(size_expression);
                                        array_type->array.size          = size;
                                        array_type->array.size_constant = true;
@@ -3574,6 +3573,7 @@ static type_t *construct_declarator_type(construct_type_t *construct_list,
                                        break;
                                }
 
+                               case EXPR_CLASS_CONSTANT:
                                case EXPR_CLASS_VARIABLE:
                                        array_type->array.is_vla = true;
                                        break;
@@ -4587,8 +4587,8 @@ static void check_declarations(void)
 static int determine_truth(expression_t const* const cond)
 {
        return
-               is_constant_expression(cond) != EXPR_CLASS_CONSTANT ? 0 :
-               fold_constant_to_bool(cond)                         ? 1 :
+               is_constant_expression(cond) < EXPR_CLASS_CONSTANT ? 0 :
+               fold_constant_to_bool(cond)                        ? 1 :
                -1;
 }
 
@@ -4796,7 +4796,7 @@ static void check_reachable(statement_t *const stmt)
                        if (!expression_returns(expr))
                                return;
 
-                       if (is_constant_expression(expr) == EXPR_CLASS_CONSTANT) {
+                       if (is_constant_expression(expr) >= EXPR_CLASS_CONSTANT) {
                                ir_tarval              *const val      = fold_constant_to_tarval(expr);
                                case_label_statement_t *      defaults = NULL;
                                for (case_label_statement_t *i = switchs->first_case; i != NULL; i = i->next) {
@@ -5463,7 +5463,7 @@ static void parse_bitfield_member(entity_t *entity)
                           type);
        }
 
-       if (is_constant_expression(size) != EXPR_CLASS_CONSTANT) {
+       if (is_constant_expression(size) < EXPR_CLASS_CONSTANT) {
                /* error already reported by parse_constant_expression */
                size_long = get_type_size(type) * 8;
        } else {
@@ -7871,7 +7871,7 @@ static void warn_div_by_zero(binary_expression_t const *const expression)
        expression_t const *const right = expression->right;
        /* The type of the right operand can be different for /= */
        if (is_type_integer(skip_typeref(right->base.type))      &&
-           is_constant_expression(right) == EXPR_CLASS_CONSTANT &&
+           is_constant_expression(right) >= EXPR_CLASS_CONSTANT &&
            !fold_constant_to_bool(right)) {
                position_t const *const pos = &expression->base.pos;
                warningf(WARN_DIV_BY_ZERO, pos, "division by zero");
@@ -7931,7 +7931,7 @@ static bool semantic_shift(binary_expression_t *expression)
 
        type_left = promote_integer(type_left);
 
-       if (is_constant_expression(right) == EXPR_CLASS_CONSTANT) {
+       if (is_constant_expression(right) >= EXPR_CLASS_CONSTANT) {
                position_t const *const pos   = &right->base.pos;
                long              const count = fold_constant_to_int(right);
                if (count < 0) {
@@ -8057,10 +8057,12 @@ static void warn_string_literal_address(expression_t const* expr)
 static bool maybe_negative(expression_t const *const expr)
 {
        switch (is_constant_expression(expr)) {
-               case EXPR_CLASS_ERROR:    return false;
-               case EXPR_CLASS_CONSTANT: return constant_is_negative(expr);
-               default:                  return true;
+               case EXPR_CLASS_VARIABLE:         return true;
+               case EXPR_CLASS_ERROR:            return false;
+               case EXPR_CLASS_CONSTANT:
+               case EXPR_CLASS_INTEGER_CONSTANT: return constant_is_negative(expr);
        }
+       panic("invalid expression classification");
 }
 
 static void warn_comparison(position_t const *const pos, expression_t const *const expr, expression_t const *const other)
@@ -8944,7 +8946,7 @@ static statement_t *parse_case_statement(void)
 
        statement->case_label.expression = expression;
        expression_classification_t const expr_class = is_constant_expression(expression);
-       if (expr_class != EXPR_CLASS_CONSTANT) {
+       if (expr_class < EXPR_CLASS_CONSTANT) {
                if (expr_class != EXPR_CLASS_ERROR) {
                        errorf(pos, "case label does not reduce to an integer constant");
                }
@@ -8968,7 +8970,7 @@ static statement_t *parse_case_statement(void)
                        end_range = create_implicit_cast(end_range, type);
                        statement->case_label.end_range = end_range;
                        expression_classification_t const end_class = is_constant_expression(end_range);
-                       if (end_class != EXPR_CLASS_CONSTANT) {
+                       if (end_class < EXPR_CLASS_CONSTANT) {
                                if (end_class != EXPR_CLASS_ERROR) {
                                        errorf(pos, "case range does not reduce to an integer constant");
                                }