Fixed handling of case labels:
authorMichael Beck <beck@ipd.info.uni-karlsruhe.de>
Thu, 28 Aug 2008 12:20:29 +0000 (12:20 +0000)
committerMichael Beck <beck@ipd.info.uni-karlsruhe.de>
Thu, 28 Aug 2008 12:20:29 +0000 (12:20 +0000)
- correctly check gnu extensions
- handle GNU empty ranges
- fold constants only once

[r21542]

ast2firm.c
ast_t.h
parser.c

index 03f2ed8..e370061 100644 (file)
@@ -4443,6 +4443,9 @@ static void switch_statement_to_firm(const switch_statement_t *statement)
 
 static void case_label_to_firm(const case_label_statement_t *statement)
 {
+       if (statement->is_empty)
+               return;
+
        dbg_info *dbgi = get_dbg_info(&statement->base.source_position);
 
        ir_node *const fallthrough = (get_cur_block() == NULL ? NULL : new_Jmp());
@@ -4455,11 +4458,8 @@ static void case_label_to_firm(const case_label_statement_t *statement)
 
        set_cur_block(old_block);
        if (statement->expression != NULL) {
-               long start_pn = fold_constant(statement->expression);
-               long end_pn = start_pn;
-               if (statement->end_range != NULL) {
-                       end_pn = fold_constant(statement->end_range);
-               }
+               long start_pn = statement->first_case;
+               long end_pn   = statement->last_case;
                assert(start_pn <= end_pn);
                /* create jumps for all cases in the given range */
                for (long pn = start_pn; pn <= end_pn; ++pn) {
diff --git a/ast_t.h b/ast_t.h
index 0230f18..52b6dc8 100644 (file)
--- a/ast_t.h
+++ b/ast_t.h
@@ -662,8 +662,12 @@ struct case_label_statement_t {
        statement_base_t        base;
        expression_t           *expression;  /**< The case label expression, NULL for default label. */
        expression_t           *end_range;   /**< For GNUC case a .. b: the end range expression, NULL else. */
+       case_label_statement_t *next;        /**< link to the next case label in switch */
        statement_t            *statement;
-       case_label_statement_t *next; /**< link to the next case label in switch */
+       long                   first_case;   /**< The folded value of expression. */
+       long                   last_case;    /**< The folded value of end_range. */
+       bool                   is_bad;       /**< If set marked as bad to supress warnings. */
+       bool                   is_empty;     /**< If set marked this is a empty range. */
 };
 
 struct label_statement_t {
index c9df2f3..760a6df 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -8151,34 +8151,54 @@ static statement_t *parse_case_statement(void)
 
        *pos                             = token.source_position;
        statement->case_label.expression = parse_expression();
-
-       PUSH_PARENT(statement);
+       if (! is_constant_expression(statement->case_label.expression)) {
+               errorf(pos, "case label does not reduce to an integer constant");
+               statement->case_label.is_bad = true;
+       } else {
+               long const val = fold_constant(statement->case_label.expression);
+               statement->case_label.first_case = val;
+               statement->case_label.last_case  = val;
+       }
 
        if (c_mode & _GNUC) {
                if (token.type == T_DOTDOTDOT) {
                        next_token();
                        statement->case_label.end_range = parse_expression();
+                       if (! is_constant_expression(statement->case_label.end_range)) {
+                               errorf(pos, "case range does not reduce to an integer constant");
+                               statement->case_label.is_bad = true;
+                       } else {
+                               long const val = fold_constant(statement->case_label.end_range);
+                               statement->case_label.last_case = val;
+
+                               if (val < statement->case_label.first_case) {
+                                       statement->case_label.is_empty = true;
+                                       warningf(pos, "empty range specified");
+                               }
+                       }
                }
        }
 
+       PUSH_PARENT(statement);
+
        expect(':');
 
-       if (! is_constant_expression(statement->case_label.expression)) {
-               errorf(pos, "case label does not reduce to an integer constant");
-       } else if (current_switch != NULL) {
-               /* Check for duplicate case values */
-               /* FIXME slow */
-               long const val = fold_constant(statement->case_label.expression);
-               for (case_label_statement_t *l = current_switch->first_case; l != NULL; l = l->next) {
-                       expression_t const* const e = l->expression;
-                       if (e == NULL || !is_constant_expression(e) || fold_constant(e) != val)
-                               continue;
+       if (current_switch != NULL) {
+               if (! statement->case_label.is_bad) {
+                       /* Check for duplicate case values */
+                       case_label_statement_t *c = &statement->case_label;
+                       for (case_label_statement_t *l = current_switch->first_case; l != NULL; l = l->next) {
+                               if (l->is_bad || l->is_empty)
+                                       continue;
 
-                       errorf(pos, "duplicate case value (previously used %P)",
-                              &l->base.source_position);
-                       break;
-               }
+                               if (c->last_case < l->first_case || c->first_case > l->last_case)
+                                       continue;
 
+                               errorf(pos, "duplicate case value (previously used %P)",
+                                      &l->base.source_position);
+                               break;
+                       }
+               }
                /* link all cases into the switch statement */
                if (current_switch->last_case == NULL) {
                        current_switch->first_case      = &statement->case_label;