* 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.
From-SVN: r36305
+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.
/* 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)
/* 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. */
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));
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. */
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. */
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,
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));
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));
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);
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++;
/* T is a statement. Add it to the statement-tree. */
-void
+tree
add_stmt (t)
tree 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
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. */
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. */
switch (TREE_CODE (t))
{
case RETURN_STMT:
- genrtl_return_stmt (RETURN_EXPR (t));
+ genrtl_return_stmt (t);
break;
case EXPR_STMT:
break;
case CASE_LABEL:
- genrtl_case_label (CASE_LOW (t), CASE_HIGH (t));
+ genrtl_case_label (t);
break;
case LABEL_STMT:
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. */
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));
/* Return either DECL or its known constant value (if it has one). */
-static tree
+tree
decl_constant_value (decl)
tree decl;
{
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
{
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));
}
\f
/* Start a C switch statement, testing expression EXP.
+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.
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));
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));
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));
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));
{
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;
}
struct cp_switch *cs;
cs = switch_stack;
+ splay_tree_delete (cs->cases);
switch_stack = switch_stack->next;
free (cs);
}
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;
/* 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);
}
\f
/* Return the list of declarations of the current level.
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)
}
}
-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
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;
-}
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:
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) \
void
finish_break_stmt ()
{
- add_stmt (build_stmt (BREAK_STMT));
+ add_stmt (build_break_stmt ());
}
/* Finish a continue-statement. */
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
{
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)
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
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
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 ();
- }
-}
\f
-/* 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
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.
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 *));
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;
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 ();
|| ! 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
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;
{
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;
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);
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. */
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));
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 *));