diff options
author | mmitchel <mmitchel@138bc75d-0d04-0410-961f-82ee72b054a4> | 2000-09-10 21:34:41 +0000 |
---|---|---|
committer | mmitchel <mmitchel@138bc75d-0d04-0410-961f-82ee72b054a4> | 2000-09-10 21:34:41 +0000 |
commit | 225ec6aae7502e0b287baca4fd3d4f6d89ad7400 (patch) | |
tree | e6b472ea3504de9f1d42acce2241fe0cbaf55dcf | |
parent | 508f18c8cfcf812f41173c35b744d20f6a4fd1e6 (diff) | |
download | gcc-225ec6aae7502e0b287baca4fd3d4f6d89ad7400.tar.gz |
* c-common.h (add_stmt): Change prototype.
(RECHAIN_STMTS): New macro.
(CASE_LABEL_DECL): Likewise.
(genrtl_case_label): Change prototype.
(c_expand_start_case): Remove prototype.
(build_case_label): Change prototype.
(decl_constant_value): Declare.
* c-common.c (check_case_value): Handle C++'s extensions to C
semantics.
* c-commnon.def (CASE_LABEL): Add room for the CASE_LABEL_DECL
field.
* c-parse.in (stmt): Adjust handling of return statements and case
laels.
* c-semantics.c (add_stmt): Return the new statement.
(genrtl_return_stmt): Take the RETURN_STMT as input, not the
returned expression. Directly generate RTL, rather than calling
c_expand_return.
(genrtl_switch_stmt): Don't call c_expand_start_case.
(build_case_label): Take the LABEL_DECL as input, too.
(genrtl_case_label): Just call add_case_node.
(expand_stmt): Adjust calls to genrtl_return_stmt and
genrtl_case_label.
* c-tree.h (c_expand_start_case): Declare.
* c-typeck.c (decl_constant_value): Give it external linkage.
(c_expand_return): Don't call expand_return or expand_null_return;
use genrtl_return_stmt instead.
* stmt.c (struct nesting): Remove num_ranges field.
(add_case_node): Give it external linkage.
(expand_start_case): Don't set num_ranges.
(expand_start_case_dummy): Don't clear it.
(pushcase): Rely on add_case_node to handle `default' labels.
(add_case_node): Handle `default' labels.
* tree.c (tree_int_cst_compare): New function.
* tree.h (tree_int_cst_compare): Declare.
(add_case_node): Likewise.
* cp-tree.h (push_switch): Change prototype.
(check_cp_case_value): Remove declaration.
(decl_constant_value): Likewise.
* decl.c (struct cp_switch): Add switch_stmt and cases.
(case_compare): New function.
(push_switch): Set switch_stmt. Initialize cases.
(pop_switch): Clean up cases.
(define_case_label): Rename to ...
(finish_case_label): ... this. Do semantic analysis for case
labels here.
(start_function): Correct comment.
* decl2.c (check_cp_case_value): Remove.
* expr.c (do_case): Remove.
* pt.c (tsubst_expr): Adjust call to finish_case_label.
* semantics.c (genrtl_do_poplevel): Remove declaration.
(RECHAIN_STMTS): Remove.
(finish_break_stmt): Use build_break_stmt.
(finish_continue_stmt): Use build_continue_stmt.
(finish_switch_cond): Adjust condition here, rater than in
c_expand_start_case.
(finish_case_label): Remove.
* typeck.c (c_expand_return): Remove.
(c_expand_start_case): Likewise.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@36305 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r-- | gcc/ChangeLog | 38 | ||||
-rw-r--r-- | gcc/c-common.c | 12 | ||||
-rw-r--r-- | gcc/c-common.def | 4 | ||||
-rw-r--r-- | gcc/c-common.h | 22 | ||||
-rw-r--r-- | gcc/c-parse.in | 28 | ||||
-rw-r--r-- | gcc/c-semantics.c | 50 | ||||
-rw-r--r-- | gcc/c-tree.h | 1 | ||||
-rw-r--r-- | gcc/c-typeck.c | 11 | ||||
-rw-r--r-- | gcc/cp/ChangeLog | 26 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 4 | ||||
-rw-r--r-- | gcc/cp/decl.c | 194 | ||||
-rw-r--r-- | gcc/cp/decl2.c | 29 | ||||
-rw-r--r-- | gcc/cp/expr.c | 79 | ||||
-rw-r--r-- | gcc/cp/pt.c | 3 | ||||
-rw-r--r-- | gcc/cp/semantics.c | 48 | ||||
-rw-r--r-- | gcc/cp/typeck.c | 49 | ||||
-rw-r--r-- | gcc/stmt.c | 48 | ||||
-rw-r--r-- | gcc/tree.c | 15 | ||||
-rw-r--r-- | gcc/tree.h | 3 |
19 files changed, 387 insertions, 277 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 65a02e32b1b..e03db7dfa85 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,41 @@ +2000-09-10 Mark Mitchell <mark@codesourcery.com> + + * c-common.h (add_stmt): Change prototype. + (RECHAIN_STMTS): New macro. + (CASE_LABEL_DECL): Likewise. + (genrtl_case_label): Change prototype. + (c_expand_start_case): Remove prototype. + (build_case_label): Change prototype. + (decl_constant_value): Declare. + * c-common.c (check_case_value): Handle C++'s extensions to C + semantics. + * c-commnon.def (CASE_LABEL): Add room for the CASE_LABEL_DECL + field. + * c-parse.in (stmt): Adjust handling of return statements and case + laels. + * c-semantics.c (add_stmt): Return the new statement. + (genrtl_return_stmt): Take the RETURN_STMT as input, not the + returned expression. Directly generate RTL, rather than calling + c_expand_return. + (genrtl_switch_stmt): Don't call c_expand_start_case. + (build_case_label): Take the LABEL_DECL as input, too. + (genrtl_case_label): Just call add_case_node. + (expand_stmt): Adjust calls to genrtl_return_stmt and + genrtl_case_label. + * c-tree.h (c_expand_start_case): Declare. + * c-typeck.c (decl_constant_value): Give it external linkage. + (c_expand_return): Don't call expand_return or expand_null_return; + use genrtl_return_stmt instead. + * stmt.c (struct nesting): Remove num_ranges field. + (add_case_node): Give it external linkage. + (expand_start_case): Don't set num_ranges. + (expand_start_case_dummy): Don't clear it. + (pushcase): Rely on add_case_node to handle `default' labels. + (add_case_node): Handle `default' labels. + * tree.c (tree_int_cst_compare): New function. + * tree.h (tree_int_cst_compare): Declare. + (add_case_node): Likewise. + 2000-09-10 Richard Henderson <rth@cygnus.com> * c-parse.in: Revert last change. diff --git a/gcc/c-common.c b/gcc/c-common.c index c53ee1ae7dd..5e093372121 100644 --- a/gcc/c-common.c +++ b/gcc/c-common.c @@ -2678,6 +2678,18 @@ check_case_value (value) /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */ STRIP_TYPE_NOPS (value); + /* In C++, the following is allowed: + + const int i = 3; + switch (...) { case i: ... } + + So, we try to reduce the VALUE to a constant that way. */ + if (c_language == clk_cplusplus) + { + value = decl_constant_value (value); + STRIP_TYPE_NOPS (value); + value = fold (value); + } if (TREE_CODE (value) != INTEGER_CST && value != error_mark_node) diff --git a/gcc/c-common.def b/gcc/c-common.def index 3486a4c1d88..b262dd76385 100644 --- a/gcc/c-common.def +++ b/gcc/c-common.def @@ -95,8 +95,8 @@ DEFTREECODE (SCOPE_STMT, "scope_stmt", 'e', 1) /* Used to represent a CASE_LABEL. The operands are CASE_LOW and CASE_HIGH, respectively. If CASE_LOW is NULL_TREE, the label is a 'default' label. If CASE_HIGH is NULL_TREE, the label is a normal case - label. */ -DEFTREECODE (CASE_LABEL, "case_label", 'e', 2) + label. The CASE_LABEL_DECL is a LABEL_DECL for this node. */ +DEFTREECODE (CASE_LABEL, "case_label", 'e', 3) /* A STMT_EXPR represents a statement-expression. The STMT_EXPR_STMT is the statement given by the expression. */ diff --git a/gcc/c-common.h b/gcc/c-common.h index e995b9978e0..4fb65608b7e 100644 --- a/gcc/c-common.h +++ b/gcc/c-common.h @@ -256,7 +256,7 @@ typedef tree (*walk_tree_fn) PARAMS ((tree *, extern stmt_tree current_stmt_tree PARAMS ((void)); extern void begin_stmt_tree PARAMS ((tree *)); -extern void add_stmt PARAMS ((tree)); +extern tree add_stmt PARAMS ((tree)); extern void finish_stmt_tree PARAMS ((tree *)); extern int statement_code_p PARAMS ((enum tree_code)); @@ -268,6 +268,18 @@ extern void prep_stmt PARAMS ((tree)); extern void (*lang_expand_stmt) PARAMS ((tree)); extern void expand_stmt PARAMS ((tree)); +/* LAST_TREE contains the last statement parsed. These are chained + together through the TREE_CHAIN field, but often need to be + re-organized since the parse is performed bottom-up. This macro + makes LAST_TREE the indicated SUBSTMT of STMT. */ + +#define RECHAIN_STMTS(stmt, substmt) \ + do { \ + substmt = TREE_CHAIN (stmt); \ + TREE_CHAIN (stmt) = NULL_TREE; \ + last_tree = stmt; \ + } while (0) + /* The variant of the C language being processed. Each C language front-end defines this variable. */ @@ -457,6 +469,7 @@ extern tree simple_type_promotes_to PARAMS ((tree)); of a case label, respectively. */ #define CASE_LOW(NODE) TREE_OPERAND (CASE_LABEL_CHECK (NODE), 0) #define CASE_HIGH(NODE) TREE_OPERAND (CASE_LABEL_CHECK (NODE), 1) +#define CASE_LABEL_DECL(NODE) TREE_OPERAND (CASE_LABEL_CHECK (NODE), 2) /* GOTO_STMT accessor. This gives access to the label associated with a goto statement. */ @@ -570,7 +583,7 @@ extern void genrtl_break_stmt PARAMS ((void)); extern void genrtl_continue_stmt PARAMS ((void)); extern void genrtl_scope_stmt PARAMS ((tree)); extern void genrtl_switch_stmt PARAMS ((tree)); -extern void genrtl_case_label PARAMS ((tree, tree)); +extern void genrtl_case_label PARAMS ((tree)); extern void genrtl_compound_stmt PARAMS ((tree)); extern void genrtl_asm_stmt PARAMS ((tree, tree, tree, tree, @@ -589,10 +602,9 @@ extern void emit_local_var PARAMS ((tree)); extern void make_rtl_for_local_static PARAMS ((tree)); extern tree expand_cond PARAMS ((tree)); extern void c_expand_return PARAMS ((tree)); -extern tree c_expand_start_case PARAMS ((tree)); extern void do_case PARAMS ((tree, tree)); extern tree build_stmt PARAMS ((enum tree_code, ...)); -extern tree build_case_label PARAMS ((tree, tree)); +extern tree build_case_label PARAMS ((tree, tree, tree)); extern tree build_continue_stmt PARAMS ((void)); extern tree build_break_stmt PARAMS ((void)); extern tree build_return_stmt PARAMS ((tree)); @@ -620,6 +632,8 @@ extern tree common_type PARAMS ((tree, tree)); extern tree expand_tree_builtin PARAMS ((tree, tree, tree)); +extern tree decl_constant_value PARAMS ((tree)); + /* Hook currently used only by the C++ front end to reset internal state after entering or leaving a header file. */ extern void extract_interface_info PARAMS ((void)); diff --git a/gcc/c-parse.in b/gcc/c-parse.in index 5825da4c7da..bd9e652de89 100644 --- a/gcc/c-parse.in +++ b/gcc/c-parse.in @@ -1879,13 +1879,11 @@ stmt: stmt_count++; genrtl_continue_stmt (); } | RETURN ';' - { tree return_stmt = build_return_stmt (NULL_TREE); - stmt_count++; - genrtl_return_stmt (RETURN_EXPR(return_stmt)); } + { stmt_count++; + c_expand_return (NULL_TREE); } | RETURN expr ';' - { tree return_stmt = build_return_stmt ($2); - stmt_count++; - genrtl_return_stmt (RETURN_EXPR(return_stmt)); } + { stmt_count++; + c_expand_return ($2); } | ASM_KEYWORD maybe_type_qual '(' expr ')' ';' { stmt_count++; emit_line_note ($<filename>-1, $<lineno>0); @@ -1943,20 +1941,14 @@ stmt: also at the end of a compound statement. */ label: CASE expr_no_commas ':' - { tree case_label_tree = build_case_label ($2, NULL_TREE); - stmt_count++; - genrtl_case_label(CASE_LOW(case_label_tree), CASE_HIGH(case_label_tree)); - } + { stmt_count++; + do_case ($2, NULL_TREE); } | CASE expr_no_commas ELLIPSIS expr_no_commas ':' - { tree case_label_tree = build_case_label ($2, $4); - stmt_count++; - genrtl_case_label(CASE_LOW(case_label_tree), CASE_HIGH(case_label_tree)); - } + { stmt_count++; + do_case ($2, $4); } | DEFAULT ':' - { tree case_label_tree = build_case_label (NULL_TREE, NULL_TREE); - stmt_count++; - genrtl_case_label(CASE_LOW(case_label_tree), CASE_HIGH(case_label_tree)); - } + { stmt_count++; + do_case (NULL_TREE, NULL_TREE); } | identifier save_filename save_lineno ':' maybe_attribute { tree label = define_label ($2, $3, $1); stmt_count++; diff --git a/gcc/c-semantics.c b/gcc/c-semantics.c index 8a4cf91c6c4..991311ef117 100644 --- a/gcc/c-semantics.c +++ b/gcc/c-semantics.c @@ -58,7 +58,7 @@ begin_stmt_tree (t) /* T is a statement. Add it to the statement-tree. */ -void +tree add_stmt (t) tree t; { @@ -68,6 +68,7 @@ add_stmt (t) /* When we expand a statement-tree, we must know whether or not the statements are full-expresions. We record that fact here. */ STMT_IS_FULL_EXPR_P (last_tree) = stmts_are_full_exprs_p (); + return t; } /* Remove declarations of internal variables that are not used from a @@ -434,14 +435,23 @@ build_return_stmt (expr) return (build_stmt (RETURN_STMT, expr)); } -/* Generate the RTL for EXPR, which is a RETURN_STMT. */ +/* Generate the RTL for STMT, which is a RETURN_STMT. */ void -genrtl_return_stmt (expr) - tree expr; +genrtl_return_stmt (stmt) + tree stmt; { + tree expr = RETURN_EXPR (stmt); + emit_line_note (input_filename, lineno); - c_expand_return (expr); + if (!expr) + expand_null_return (); + else + { + expand_start_target_temps (); + expand_return (expr); + expand_end_target_temps (); + } } /* Generate the RTL for T, which is a FOR_STMT. */ @@ -547,40 +557,38 @@ genrtl_switch_stmt (t) genrtl_do_pushlevel (); cond = expand_cond (SWITCH_COND (t)); - if (cond != error_mark_node) - { - emit_line_note (input_filename, lineno); - c_expand_start_case (cond); - } - else + if (cond == error_mark_node) /* The code is in error, but we don't want expand_end_case to crash. */ - c_expand_start_case (boolean_false_node); + cond = boolean_false_node; + emit_line_note (input_filename, lineno); + expand_start_case (1, cond, TREE_TYPE (cond), "switch statement"); expand_stmt (SWITCH_BODY (t)); - expand_end_case (cond); } /* Create a CASE_LABEL tree node and return it. */ tree -build_case_label (low_value, high_value) +build_case_label (low_value, high_value, label_decl) tree low_value; tree high_value; + tree label_decl; { - return build_stmt (CASE_LABEL, low_value, high_value); + return build_stmt (CASE_LABEL, low_value, high_value, label_decl); } /* Generate the RTL for a CASE_LABEL. */ void -genrtl_case_label (low_value, high_value) - tree low_value; - tree high_value; +genrtl_case_label (case_label) + tree case_label; { - do_case (low_value, high_value); + tree duplicate; + add_case_node (CASE_LOW (case_label), CASE_HIGH (case_label), + CASE_LABEL_DECL (case_label), &duplicate); } /* Generate the RTL for T, which is a COMPOUND_STMT. */ @@ -677,7 +685,7 @@ expand_stmt (t) switch (TREE_CODE (t)) { case RETURN_STMT: - genrtl_return_stmt (RETURN_EXPR (t)); + genrtl_return_stmt (t); break; case EXPR_STMT: @@ -721,7 +729,7 @@ expand_stmt (t) break; case CASE_LABEL: - genrtl_case_label (CASE_LOW (t), CASE_HIGH (t)); + genrtl_case_label (t); break; case LABEL_STMT: diff --git a/gcc/c-tree.h b/gcc/c-tree.h index 290ebe9daa8..92f0b453329 100644 --- a/gcc/c-tree.h +++ b/gcc/c-tree.h @@ -268,6 +268,7 @@ extern void set_init_label PARAMS ((tree)); extern void process_init_element PARAMS ((tree)); extern void pedwarn_c99 PARAMS ((const char *, ...)) ATTRIBUTE_PRINTF_1; +extern tree c_expand_start_case PARAMS ((tree)); /* Set to 0 at beginning of a function definition, set to 1 if a return statement that specifies a return value is seen. */ diff --git a/gcc/c-typeck.c b/gcc/c-typeck.c index cd787d97295..49227ff82c5 100644 --- a/gcc/c-typeck.c +++ b/gcc/c-typeck.c @@ -54,7 +54,6 @@ static tree qualify_type PARAMS ((tree, tree)); static int comp_target_types PARAMS ((tree, tree)); static int function_types_compatible_p PARAMS ((tree, tree)); static int type_lists_compatible_p PARAMS ((tree, tree)); -static tree decl_constant_value PARAMS ((tree)); static tree lookup_field PARAMS ((tree, tree, tree *)); static tree convert_arguments PARAMS ((tree, tree, tree, tree)); static tree pointer_int_sum PARAMS ((enum tree_code, tree, tree)); @@ -838,7 +837,7 @@ c_alignof_expr (expr) /* Return either DECL or its known constant value (if it has one). */ -static tree +tree decl_constant_value (decl) tree decl; { @@ -6629,14 +6628,12 @@ c_expand_return (retval) if ((warn_return_type || flag_isoc99) && valtype != 0 && TREE_CODE (valtype) != VOID_TYPE) pedwarn_c99 ("`return' with no value, in function returning non-void"); - expand_null_return (); } else if (valtype == 0 || TREE_CODE (valtype) == VOID_TYPE) { current_function_returns_null = 1; if (pedantic || TREE_CODE (TREE_TYPE (retval)) != VOID_TYPE) pedwarn ("`return' with a value, in function returning void"); - expand_return (retval); } else { @@ -6701,11 +6698,11 @@ c_expand_return (retval) break; } - t = build (MODIFY_EXPR, TREE_TYPE (res), res, t); - TREE_SIDE_EFFECTS (t) = 1; - expand_return (t); + retval = build (MODIFY_EXPR, TREE_TYPE (res), res, t); current_function_returns_value = 1; } + + genrtl_return_stmt (build_return_stmt (retval)); } /* Start a C switch statement, testing expression EXP. diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 537e57ad048..1ba64f02bac 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,29 @@ +2000-09-09 Mark Mitchell <mark@codesourcery.com> + + * cp-tree.h (push_switch): Change prototype. + (check_cp_case_value): Remove declaration. + (decl_constant_value): Likewise. + * decl.c (struct cp_switch): Add switch_stmt and cases. + (case_compare): New function. + (push_switch): Set switch_stmt. Initialize cases. + (pop_switch): Clean up cases. + (define_case_label): Rename to ... + (finish_case_label): ... this. Do semantic analysis for case + labels here. + (start_function): Correct comment. + * decl2.c (check_cp_case_value): Remove. + * expr.c (do_case): Remove. + * pt.c (tsubst_expr): Adjust call to finish_case_label. + * semantics.c (genrtl_do_poplevel): Remove declaration. + (RECHAIN_STMTS): Remove. + (finish_break_stmt): Use build_break_stmt. + (finish_continue_stmt): Use build_continue_stmt. + (finish_switch_cond): Adjust condition here, rater than in + c_expand_start_case. + (finish_case_label): Remove. + * typeck.c (c_expand_return): Remove. + (c_expand_start_case): Likewise. + 2000-09-07 Gabriel Dos Reis <gdr@codesourcery.com> * ir.texi: Document type nodes. diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index f29b04b35e6..8dbb82e29ad 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -3829,7 +3829,7 @@ extern void pop_nested_namespace PARAMS ((tree)); extern void maybe_push_to_top_level PARAMS ((int)); extern void push_to_top_level PARAMS ((void)); extern void pop_from_top_level PARAMS ((void)); -extern void push_switch PARAMS ((void)); +extern void push_switch PARAMS ((tree)); extern void pop_switch PARAMS ((void)); extern tree identifier_type_value PARAMS ((tree)); extern void set_identifier_type_value PARAMS ((tree, tree)); @@ -3990,7 +3990,6 @@ extern tree reparse_absdcl_as_casts PARAMS ((tree, tree)); extern tree build_expr_from_tree PARAMS ((tree)); extern tree reparse_decl_as_expr PARAMS ((tree, tree)); extern tree finish_decl_parsing PARAMS ((tree)); -extern tree check_cp_case_value PARAMS ((tree)); extern void set_decl_namespace PARAMS ((tree, tree, int)); extern tree current_decl_namespace PARAMS ((void)); extern void push_decl_namespace PARAMS ((tree)); @@ -4075,7 +4074,6 @@ extern tree get_type_value PARAMS ((tree)); extern tree build_member_call PARAMS ((tree, tree, tree)); extern tree build_offset_ref PARAMS ((tree, tree)); extern tree resolve_offset_ref PARAMS ((tree)); -extern tree decl_constant_value PARAMS ((tree)); extern tree build_new PARAMS ((tree, tree, tree, int)); extern tree build_vec_init PARAMS ((tree, tree, tree, tree, int)); extern tree build_x_delete PARAMS ((tree, int, tree)); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 640deb2208c..9ac30e40832 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -187,6 +187,7 @@ static tree check_special_function_return_type PARAMS ((special_function_kind, tree, tree, tree)); static tree push_cp_library_fn PARAMS ((enum tree_code, tree)); static tree build_cp_library_fn PARAMS ((tree, enum tree_code, tree)); +static int case_compare PARAMS ((splay_tree_key, splay_tree_key)); #if defined (DEBUG_CP_BINDING_LEVELS) static void indent PARAMS ((void)); @@ -5176,17 +5177,52 @@ struct cp_switch { struct binding_level *level; struct cp_switch *next; + /* The SWITCH_STMT being built. */ + tree switch_stmt; + /* A splay-tree mapping the low element of a case range to the high + element, or NULL_TREE if there is no high element. Used to + determine whether or not a new case label duplicates an old case + label. We need a tree, rather than simply a hash table, because + of the GNU case range extension. */ + splay_tree cases; }; +/* A stack of the currently active switch statements. The innermost + switch statement is on the top of the stack. There is no need to + mark the stack for garbage collection because it is only active + during the processing of the body of a function, and we never + collect at that point. */ + static struct cp_switch *switch_stack; +static int +case_compare (k1, k2) + splay_tree_key k1; + splay_tree_key k2; +{ + /* Consider a NULL key (such as arises with a `default' label) to be + smaller than anything else. */ + if (!k1) + return k2 ? -1 : 0; + else if (!k2) + return k1 ? 1 : 0; + + return tree_int_cst_compare ((tree) k1, (tree) k2); +} + +/* Called right after a switch-statement condition is parsed. + SWITCH_STMT is the switch statement being parsed. */ + void -push_switch () +push_switch (switch_stmt) + tree switch_stmt; { struct cp_switch *p = (struct cp_switch *) xmalloc (sizeof (struct cp_switch)); p->level = current_binding_level; p->next = switch_stack; + p->switch_stmt = switch_stmt; + p->cases = splay_tree_new (case_compare, NULL, NULL); switch_stack = p; } @@ -5196,6 +5232,7 @@ pop_switch () struct cp_switch *cs; cs = switch_stack; + splay_tree_delete (cs->cases); switch_stack = switch_stack->next; free (cs); } @@ -5204,14 +5241,150 @@ pop_switch () is a bad place for one. */ void -define_case_label () +finish_case_label (low_value, high_value) + tree low_value; + tree high_value; { - tree cleanup = last_cleanup_this_contour (); + tree label; + tree cleanup; + tree type; + tree cond; + tree case_label; + splay_tree_node node; if (! switch_stack) - /* Don't crash; we'll complain in do_case. */ + { + if (high_value) + error ("case label not within a switch statement"); + else if (low_value) + cp_error ("case label `%E' not within a switch statement", + low_value); + else + error ("`default' label not within a switch statement"); + return; + } + + label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); + + if (processing_template_decl) + { + /* For templates, just add the case label; we'll do semantic + analysis at instantiation-time. */ + add_stmt (build_case_label (low_value, high_value, label)); + return; + } + + /* Find the condition on which this switch statement depends. */ + cond = SWITCH_COND (switch_stack->switch_stmt); + if (cond && TREE_CODE (cond) == TREE_LIST) + cond = TREE_VALUE (cond); + /* If there was an error processing the switch condition, bail now + before we get more confused. */ + if (!cond || cond == error_mark_node) return; + type = TREE_TYPE (cond); + if ((low_value && TREE_TYPE (low_value) + && POINTER_TYPE_P (TREE_TYPE (low_value))) + || (high_value && TREE_TYPE (high_value) + && POINTER_TYPE_P (TREE_TYPE (high_value)))) + error ("pointers are not permitted as case values"); + + /* Case ranges are a GNU extension. */ + if (high_value && pedantic) + pedwarn ("ISO C++ forbids range expressions in switch statement"); + + if (low_value) + { + low_value = check_case_value (low_value); + low_value = convert_and_check (type, low_value); + } + if (high_value) + { + high_value = check_case_value (high_value); + high_value = convert_and_check (type, high_value); + } + + /* If an error has occurred, bail out now. */ + if (low_value == error_mark_node || high_value == error_mark_node) + return; + + /* If the LOW_VALUE and HIGH_VALUE are the same, then this isn't + really a case range, even though it was written that way. Remove + the HIGH_VALUE to simplify later processing. */ + if (tree_int_cst_equal (low_value, high_value)) + high_value = NULL_TREE; + if (low_value && high_value + && !tree_int_cst_lt (low_value, high_value)) + warning ("empty range specified"); + + /* Look up the LOW_VALUE in the table of case labels we already + have. */ + node = splay_tree_lookup (switch_stack->cases, (splay_tree_key) low_value); + /* If there was not an exact match, check for overlapping ranges. + There's no need to do this if there's no LOW_VALUE or HIGH_VALUE; + that's a `default' label and the only overlap is an exact match. */ + if (!node && (low_value || high_value)) + { + splay_tree_node low_bound; + splay_tree_node high_bound; + + /* Even though there wasn't an exact match, there might be an + overlap between this case range and another case range. + Since we've (inductively) not allowed any overlapping case + ranges, we simply need to find the greatest low case label + that is smaller that LOW_VALUE, and the smallest low case + label that is greater than LOW_VALUE. If there is an overlap + it will occur in one of these two ranges. */ + low_bound = splay_tree_predecessor (switch_stack->cases, + (splay_tree_key) low_value); + high_bound = splay_tree_successor (switch_stack->cases, + (splay_tree_key) low_value); + + /* Check to see if the LOW_BOUND overlaps. It is smaller than + the LOW_VALUE, so there is no need to check unless the + LOW_BOUND is in fact itself a case range. */ + if (low_bound + && CASE_HIGH ((tree) low_bound->value) + && tree_int_cst_compare (CASE_HIGH ((tree) low_bound->value), + low_value) >= 0) + node = low_bound; + /* Check to see if the HIGH_BOUND overlaps. The low end of that + range is bigger than the low end of the current range, so we + are only interested if the current range is a real range, and + not an ordinary case label. */ + else if (high_bound + && high_value + && (tree_int_cst_compare ((tree) high_bound->key, + high_value) + <= 0)) + node = high_bound; + } + /* If there was an overlap, issue an error. */ + if (node) + { + tree duplicate = CASE_LABEL_DECL ((tree) node->value); + + if (high_value) + { + error ("duplicate (or overlapping) case value"); + cp_error_at ("this is the first entry overlapping that value", + duplicate); + } + else if (low_value) + { + cp_error ("duplicate case value `%E'", low_value) ; + cp_error_at ("previously used here", duplicate); + } + else + { + error ("multiple default labels in one switch"); + cp_error_at ("this is the first default label", duplicate); + } + return; + } + + cleanup = last_cleanup_this_contour (); if (cleanup) { static int explained = 0; @@ -5228,9 +5401,18 @@ define_case_label () /* After labels, make any new cleanups go into their own new (temporary) binding contour. */ - current_binding_level->more_cleanups_ok = 0; current_function_return_value = NULL_TREE; + + /* Add a representation for the case label to the statement + tree. */ + case_label = build_case_label (low_value, high_value, label); + add_stmt (case_label); + + /* Register this case label in the splay tree. */ + splay_tree_insert (switch_stack->cases, + (splay_tree_key) low_value, + (splay_tree_value) case_label); } /* Return the list of declarations of the current level. @@ -13601,7 +13783,7 @@ start_function (declspecs, declarator, attrs, flags) if (flags & SF_INCLASS_INLINE) maybe_begin_member_template_processing (decl1); - /* Effective C++ rule 15. See also c_expand_return. */ + /* Effective C++ rule 15. */ if (warn_ecpp && DECL_OVERLOADED_OPERATOR_P (decl1) == NOP_EXPR && TREE_CODE (TREE_TYPE (fntype)) == VOID_TYPE) diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index 7bbd998df45..e97d52593ab 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -4329,35 +4329,6 @@ finish_decl_parsing (decl) } } -tree -check_cp_case_value (value) - tree value; -{ - if (value == NULL_TREE) - return value; - - /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue. */ - STRIP_TYPE_NOPS (value); - value = decl_constant_value (value); - STRIP_TYPE_NOPS (value); - value = fold (value); - - if (TREE_CODE (value) != INTEGER_CST - && value != error_mark_node) - { - cp_error ("case label `%E' does not reduce to an integer constant", - value); - value = error_mark_node; - } - else - /* Promote char or short to int. */ - value = default_conversion (value); - - constant_expression_warning (value); - - return value; -} - /* Return 1 if root encloses child. */ static int diff --git a/gcc/cp/expr.c b/gcc/cp/expr.c index 42a53903ee3..39095ec17db 100644 --- a/gcc/cp/expr.c +++ b/gcc/cp/expr.c @@ -205,82 +205,3 @@ extract_init (decl, init) return 0; } -void -do_case (start, end) - tree start, end; -{ - tree value1 = NULL_TREE, value2 = NULL_TREE, label; - - if (start != NULL_TREE && TREE_TYPE (start) != NULL_TREE - && POINTER_TYPE_P (TREE_TYPE (start))) - error ("pointers are not permitted as case values"); - - if (end && pedantic) - pedwarn ("ISO C++ forbids range expressions in switch statement"); - - if (start) - value1 = check_cp_case_value (start); - if (end) - value2 = check_cp_case_value (end); - - label = build_decl (LABEL_DECL, NULL_TREE, NULL_TREE); - - if (value1 != error_mark_node - && value2 != error_mark_node) - { - tree duplicate; - int success; - - if (end) - success = pushcase_range (value1, value2, convert_and_check, - label, &duplicate); - else if (start) - success = pushcase (value1, convert_and_check, label, &duplicate); - else - success = pushcase (NULL_TREE, 0, label, &duplicate); - - if (success == 1) - { - if (end) - error ("case label not within a switch statement"); - else if (start) - cp_error ("case label `%E' not within a switch statement", start); - else - error ("default label not within a switch statement"); - } - else if (success == 2) - { - if (end) - { - error ("duplicate (or overlapping) case value"); - cp_error_at ("this is the first entry overlapping that value", - duplicate); - } - else if (start) - { - cp_error ("duplicate case value `%E'", start); - cp_error_at ("previously used here", duplicate); - } - else - { - error ("multiple default labels in one switch"); - cp_error_at ("this is the first default label", duplicate); - } - } - else if (success == 3) - warning ("case value out of range"); - else if (success == 4) - warning ("empty range specified"); - else if (success == 5) - { - if (end) - error ("case label within scope of cleanup or variable array"); - else if (! start) - error ("`default' label within scope of cleanup or variable array"); - else - cp_error ("case label `%E' within scope of cleanup or variable array", start); - } - } - - current_function_return_value = NULL_TREE; -} diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 51421fd9a17..dc6f00f931c 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -7269,7 +7269,8 @@ tsubst_expr (t, args, complain, in_decl) case CASE_LABEL: prep_stmt (t); finish_case_label (tsubst_expr (CASE_LOW (t), args, complain, in_decl), - tsubst_expr (CASE_HIGH (t), args, complain, in_decl)); + tsubst_expr (CASE_HIGH (t), args, complain, + in_decl)); break; case LABEL_STMT: diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index e1a73cee6bc..b7abff29dec 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -55,23 +55,9 @@ static void genrtl_handler PARAMS ((tree)); static void genrtl_catch_block PARAMS ((tree)); static void genrtl_ctor_stmt PARAMS ((tree)); static void genrtl_subobject PARAMS ((tree)); -static tree genrtl_do_poplevel PARAMS ((void)); static void genrtl_named_return_value PARAMS ((void)); static void cp_expand_stmt PARAMS ((tree)); -/* When parsing a template, LAST_TREE contains the last statement - parsed. These are chained together through the TREE_CHAIN field, - but often need to be re-organized since the parse is performed - bottom-up. This macro makes LAST_TREE the indicated SUBSTMT of - STMT. */ - -#define RECHAIN_STMTS(stmt, substmt) \ - do { \ - substmt = TREE_CHAIN (stmt); \ - TREE_CHAIN (stmt) = NULL_TREE; \ - last_tree = stmt; \ - } while (0) - /* Finish processing the COND, the SUBSTMT condition for STMT. */ #define FINISH_COND(cond, stmt, substmt) \ @@ -520,7 +506,7 @@ finish_for_stmt (for_stmt) void finish_break_stmt () { - add_stmt (build_stmt (BREAK_STMT)); + add_stmt (build_break_stmt ()); } /* Finish a continue-statement. */ @@ -528,7 +514,7 @@ finish_break_stmt () void finish_continue_stmt () { - add_stmt (build_stmt (CONTINUE_STMT)); + add_stmt (build_continue_stmt ()); } /* Begin a switch-statement. Returns a new SWITCH_STMT if @@ -553,6 +539,9 @@ finish_switch_cond (cond, switch_stmt) { if (!processing_template_decl) { + tree type; + tree index; + /* Convert the condition to an integer or enumeration type. */ cond = build_expr_type_conversion (WANT_INT | WANT_ENUM, cond, 1); if (cond == NULL_TREE) @@ -565,9 +554,19 @@ finish_switch_cond (cond, switch_stmt) cond = default_conversion (cond); cond = fold (build1 (CLEANUP_POINT_EXPR, TREE_TYPE (cond), cond)); } + + type = TREE_TYPE (cond); + index = get_unwidened (cond, NULL_TREE); + /* We can't strip a conversion from a signed type to an unsigned, + because if we did, int_fits_type_p would do the wrong thing + when checking case values for being in range, + and it's too hard to do the right thing. */ + if (TREE_UNSIGNED (TREE_TYPE (cond)) + == TREE_UNSIGNED (TREE_TYPE (index))) + cond = index; } FINISH_COND (cond, switch_stmt, SWITCH_COND (switch_stmt)); - push_switch (); + push_switch (switch_stmt); } /* Finish the body of a switch-statement, which may be given by @@ -583,21 +582,6 @@ finish_switch_stmt (switch_stmt) finish_stmt (); } -/* Finish a case-label. */ - -void -finish_case_label (low_value, high_value) - tree low_value; - tree high_value; -{ - /* Add a representation for the case label to the statement - tree. */ - add_stmt (build_stmt (CASE_LABEL, low_value, high_value)); - /* And warn about crossing initializations, etc. */ - if (!processing_template_decl) - define_case_label (); -} - /* Generate the RTL for T, which is a TRY_BLOCK. */ static void diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index bff019d36c9..e15a7248930 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -6839,56 +6839,7 @@ check_return_expr (retval) return retval; } -/* Expand a C `return' statement. - RETVAL is the expression for what to return, - or a null pointer for `return;' with no value. - - C++: upon seeing a `return', we must call destructors on all - variables in scope which had constructors called on them. - This means that if in a destructor, the base class destructors - must be called before returning. - - The RETURN statement in C++ has initialization semantics. */ - -void -c_expand_return (retval) - tree retval; -{ - if (!retval) - expand_null_return (); - else - { - expand_start_target_temps (); - expand_return (retval); - expand_end_target_temps (); - } -} -/* Start a C switch statement, testing expression EXP. - Return EXP if it is valid, an error node otherwise. */ - -tree -c_expand_start_case (exp) - tree exp; -{ - tree type; - tree index; - - type = TREE_TYPE (exp); - index = get_unwidened (exp, NULL_TREE); - /* We can't strip a conversion from a signed type to an unsigned, - because if we did, int_fits_type_p would do the wrong thing - when checking case values for being in range, - and it's too hard to do the right thing. */ - if (TREE_UNSIGNED (TREE_TYPE (exp)) - == TREE_UNSIGNED (TREE_TYPE (index))) - exp = index; - - expand_start_case (1, exp, type, "switch statement"); - - return exp; -} - /* Returns non-zero if the pointer-type FROM can be converted to the pointer-type TO via a qualification conversion. If CONSTP is -1, then we return non-zero if the pointers are similar, and the diff --git a/gcc/stmt.c b/gcc/stmt.c index 46b2317149f..6adee8159d7 100644 --- a/gcc/stmt.c +++ b/gcc/stmt.c @@ -240,8 +240,6 @@ struct nesting tree index_expr; /* Type that INDEX_EXPR should be converted to. */ tree nominal_type; - /* Number of range exprs in case statement. */ - int num_ranges; /* Name of this kind of statement, for warnings. */ const char *printname; /* Used to save no_line_numbers till we see the first case label. @@ -421,7 +419,6 @@ static int node_has_high_bound PARAMS ((case_node_ptr, tree)); static int node_is_bounded PARAMS ((case_node_ptr, tree)); static void emit_jump_if_reachable PARAMS ((rtx)); static void emit_case_nodes PARAMS ((rtx, case_node_ptr, rtx, tree)); -static int add_case_node PARAMS ((tree, tree, tree, tree *)); static struct case_node *case_tree2list PARAMS ((case_node *, case_node *)); static void mark_cond_nesting PARAMS ((struct nesting *)); static void mark_loop_nesting PARAMS ((struct nesting *)); @@ -4426,7 +4423,6 @@ expand_start_case (exit_flag, expr, type, printname) thiscase->data.case_stmt.index_expr = expr; thiscase->data.case_stmt.nominal_type = type; thiscase->data.case_stmt.default_label = 0; - thiscase->data.case_stmt.num_ranges = 0; thiscase->data.case_stmt.printname = printname; thiscase->data.case_stmt.line_number_status = force_line_numbers (); case_stack = thiscase; @@ -4464,7 +4460,6 @@ expand_start_case_dummy () thiscase->data.case_stmt.start = 0; thiscase->data.case_stmt.nominal_type = 0; thiscase->data.case_stmt.default_label = 0; - thiscase->data.case_stmt.num_ranges = 0; case_stack = thiscase; nesting_stack = thiscase; start_cleanup_deferral (); @@ -4580,21 +4575,7 @@ pushcase (value, converter, label, duplicate) || ! int_fits_type_p (value, index_type))) return 3; - /* Fail if this is a duplicate or overlaps another entry. */ - if (value == 0) - { - if (case_stack->data.case_stmt.default_label != 0) - { - *duplicate = case_stack->data.case_stmt.default_label; - return 2; - } - case_stack->data.case_stmt.default_label = label; - } - else - return add_case_node (value, value, label, duplicate); - - expand_label (label); - return 0; + return add_case_node (value, value, label, duplicate); } /* Like pushcase but this case applies to all values between VALUE1 and @@ -4670,7 +4651,7 @@ pushcase_range (value1, value2, converter, label, duplicate) into case_stack->data.case_stmt.case_list. Use an AVL tree to avoid slowdown for large switch statements. */ -static int +int add_case_node (low, high, label, duplicate) tree low, high; tree label; @@ -4678,6 +4659,25 @@ add_case_node (low, high, label, duplicate) { struct case_node *p, **q, *r; + /* If there's no HIGH value, then this is not a case range; it's + just a simple case label. But that's just a degenerate case + range. */ + if (!high) + high = low; + + /* Handle default labels specially. */ + if (!high && !low) + { + if (case_stack->data.case_stmt.default_label != 0) + { + *duplicate = case_stack->data.case_stmt.default_label; + return 2; + } + case_stack->data.case_stmt.default_label = label; + expand_label (label); + return 0; + } + q = &case_stack->data.case_stmt.case_list; p = *q; @@ -4709,14 +4709,10 @@ add_case_node (low, high, label, duplicate) r->low = copy_node (low); /* If the bounds are equal, turn this into the one-value case. */ - if (tree_int_cst_equal (low, high)) r->high = r->low; else - { - r->high = copy_node (high); - case_stack->data.case_stmt.num_ranges++; - } + r->high = copy_node (high); r->code_label = label; expand_label (label); diff --git a/gcc/tree.c b/gcc/tree.c index e1a4a7d0eb6..d396cc11ca8 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -4293,6 +4293,21 @@ tree_int_cst_lt (t1, t2) return INT_CST_LT_UNSIGNED (t1, t2); } +/* Returns -1 if T1 < T2, 0 if T1 == T2, and 1 if T1 > T2. */ + +int +tree_int_cst_compare (t1, t2) + tree t1; + tree t2; +{ + if (tree_int_cst_lt (t1, t2)) + return -1; + else if (tree_int_cst_lt (t2, t1)) + return 1; + else + return 0; +} + /* Return 1 if T is an INTEGER_CST that can be represented in a single HOST_WIDE_INT value. If POS is nonzero, the result must be positive. */ diff --git a/gcc/tree.h b/gcc/tree.h index 245f413c77b..baf32266661 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -1943,6 +1943,7 @@ extern int attribute_list_equal PARAMS ((tree, tree)); extern int attribute_list_contained PARAMS ((tree, tree)); extern int tree_int_cst_equal PARAMS ((tree, tree)); extern int tree_int_cst_lt PARAMS ((tree, tree)); +extern int tree_int_cst_compare PARAMS ((tree, tree)); extern int host_integerp PARAMS ((tree, int)); extern HOST_WIDE_INT tree_low_cst PARAMS ((tree, int)); extern int tree_int_cst_msb PARAMS ((tree)); @@ -2497,6 +2498,8 @@ extern int expand_dcc_cleanup PARAMS ((tree)); extern void expand_start_case PARAMS ((int, tree, tree, const char *)); extern void expand_end_case PARAMS ((tree)); +extern int add_case_node PARAMS ((tree, tree, + tree, tree *)); extern int pushcase PARAMS ((tree, tree (*) (tree, tree), tree, tree *)); |