summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormmitchel <mmitchel@138bc75d-0d04-0410-961f-82ee72b054a4>2000-09-10 21:34:41 +0000
committermmitchel <mmitchel@138bc75d-0d04-0410-961f-82ee72b054a4>2000-09-10 21:34:41 +0000
commit225ec6aae7502e0b287baca4fd3d4f6d89ad7400 (patch)
treee6b472ea3504de9f1d42acce2241fe0cbaf55dcf
parent508f18c8cfcf812f41173c35b744d20f6a4fd1e6 (diff)
downloadgcc-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/ChangeLog38
-rw-r--r--gcc/c-common.c12
-rw-r--r--gcc/c-common.def4
-rw-r--r--gcc/c-common.h22
-rw-r--r--gcc/c-parse.in28
-rw-r--r--gcc/c-semantics.c50
-rw-r--r--gcc/c-tree.h1
-rw-r--r--gcc/c-typeck.c11
-rw-r--r--gcc/cp/ChangeLog26
-rw-r--r--gcc/cp/cp-tree.h4
-rw-r--r--gcc/cp/decl.c194
-rw-r--r--gcc/cp/decl2.c29
-rw-r--r--gcc/cp/expr.c79
-rw-r--r--gcc/cp/pt.c3
-rw-r--r--gcc/cp/semantics.c48
-rw-r--r--gcc/cp/typeck.c49
-rw-r--r--gcc/stmt.c48
-rw-r--r--gcc/tree.c15
-rw-r--r--gcc/tree.h3
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 *));