summaryrefslogtreecommitdiff
path: root/switch.c
diff options
context:
space:
mode:
authorSøren Sandmann Pedersen <ssp@l3000.localdomain>2011-03-23 09:39:11 -0400
committerSøren Sandmann Pedersen <ssp@l3000.localdomain>2011-03-23 09:39:38 -0400
commit7a5f615e0c8e191b2ed1e0de40c11b9486848c69 (patch)
tree0f3000cb7181e7a225307d13038a4ab330de8bd1 /switch.c
parent6adbade35aa76b19074f16e5d30c177e68385f0f (diff)
Move constant-expression marking to a separate phase.
Diffstat (limited to 'switch.c')
-rw-r--r--switch.c170
1 files changed, 25 insertions, 145 deletions
diff --git a/switch.c b/switch.c
index 5d5df4c..7368885 100644
--- a/switch.c
+++ b/switch.c
@@ -22,140 +22,7 @@
/* Check switch statements */
static gboolean
-constant_expression (ast_expression_t *expr,
- value_t *value)
-{
- value_t left;
- value_t right;
- value_t result;
-
- g_assert (((ast_t *)expr)->common.type == AST_EXPRESSION);
-
- switch (expr->common.type)
- {
- case AST_INT_LITERAL_EXPRESSION:
- value->int32_val = expr->int_literal.value;
- break;
-
- case AST_BOOL_LITERAL_EXPRESSION:
- value->bool_val = expr->bool_literal.value;
- break;
-
- case AST_STRING_LITERAL_EXPRESSION:
- value->pointer_val = expr->string_literal.value;
- break;
-
- case AST_NULL_EXPRESSION:
- value->pointer_val = NULL;
- break;
-
- case AST_IDENTIFIER_EXPRESSION:
- /* FIXME: maybe check if it's a constant, particularly
- * when we add enums
- */
- return FALSE;
- break;
-
- case AST_LAMBDA_EXPRESSION:
- case AST_CALL_EXPRESSION:
- case AST_STATEMENT_EXPRESSION:
- case AST_NEW_EXPRESSION:
- case AST_DOT_EXPRESSION:
- case AST_THIS_EXPRESSION:
- case AST_VOID_EXPRESSION:
- case AST_INDEX_EXPRESSION:
- return FALSE;
- break;
-
- case AST_DEFINITION_EXPRESSION:
- /* FIXME: It might actually be useful to allow this */
- return FALSE;
- break;
-
- case AST_BLOCK_EXPRESSION:
- if (!constant_expression (expr->block.body, value))
- return FALSE;
- break;
-
- case AST_UNARY_EXPRESSION:
- if (!constant_expression (expr->unary.expr, &result))
- return FALSE;
-
- evaluate_unary_operator (expr->unary.operator,
- expr->unary.expr->common.type_spec, &result,
- expr->common.type_spec, value);
- break;
-
- case AST_BINARY_EXPRESSION:
- if (!constant_expression (expr->binary.left, &left))
- return FALSE;
-
- if (!constant_expression (expr->binary.right, &right))
- return FALSE;
-
- evaluate_binary_operator (expr->binary.operator,
- expr->binary.left->common.type_spec, &left,
- expr->binary.right->common.type_spec, &right,
- expr->common.type_spec, value);
- break;
-
- case AST_TERNARY_EXPRESSION:
- if (!constant_expression (expr->ternary.cond, &result))
- return FALSE;
-
- /* FIXME: we could require only the taken branch to be constant */
-
- if (!constant_expression (expr->ternary.then_expr, &left))
- return FALSE;
-
- if (!constant_expression (expr->ternary.else_expr, &right))
- return FALSE;
-
- if (result.bool_val)
- *value = left;
- else
- *value = right;
- break;
- }
-
- return TRUE;
-}
-
-static gboolean
-check_constant_case_expressions (ast_t *ast)
-{
- GList *list;
-
- for (list = ast->common.children->head; list; list = list->next)
- {
- if (!check_constant_case_expressions (list->data))
- return FALSE;
- }
-
- if (ast_is (ast, AST_CASE, AST_EXPRESSION_CASE))
- {
- value_t value;
-
- if (!constant_expression (ast->case_.expression.expression, &value))
- {
- return report_error (
- "Expression in case clause must be constant\n");
- }
-
-#if 0
- g_print ("constant expression evaluates to ");
- print_value (
- &value, ast->case_.expression.expression->common.type_spec);
-#endif
-
- ast->case_.expression.value = value;
- }
-
- return TRUE;
-}
-
-static gboolean
-check_unique_cases (ast_t *ast)
+real_switch_check (ast_t *ast)
{
int n_cases;
ast_case_t **cases;
@@ -168,13 +35,31 @@ check_unique_cases (ast_t *ast)
for (list = ast->common.children->head; list; list = list->next)
{
- if (!check_unique_cases (list->data))
+ if (!real_switch_check (list->data))
return FALSE;
}
return TRUE;
}
+ n_cases = array_length ((gpointer *)ast->statement.switch_.cases);
+ cases = ast->statement.switch_.cases;
+
+ /* Check that all case expressions are constant */
+ for (i = 0; i < n_cases; ++i)
+ {
+ const ast_case_t *case_ = cases[i];
+
+ if (case_->common.type == AST_EXPRESSION_CASE)
+ {
+ if (!case_->expression.expr->common.constant)
+ {
+ return report_error (
+ "Expression in case clause must be constant\n");
+ }
+ }
+ }
+
/* This algorithm is O(n^2) in the number of cases, but it's noise
* on the profile even with thousands of cases.
*
@@ -184,8 +69,6 @@ check_unique_cases (ast_t *ast)
* robust to rely on the EQUAL operator, which is what the interpreter
* is using anyway.
*/
- n_cases = array_length ((gpointer *)ast->statement.switch_.cases);
- cases = ast->statement.switch_.cases;
bool = ast_type_spec_new (AST_BOOL_TYPE);
for (i = 0; i < n_cases; ++i)
@@ -209,11 +92,11 @@ check_unique_cases (ast_t *ast)
const value_t *value_b;
value_t result;
- type_a = case_a->expression.expression->common.type_spec;
- value_a = &case_a->expression.value;
+ type_a = case_a->expression.expr->common.type_spec;
+ value_a = &case_a->expression.expr->common.constant_value;
- type_b = case_b->expression.expression->common.type_spec;
- value_b = &case_b->expression.value;
+ type_b = case_b->expression.expr->common.type_spec;
+ value_b = &case_b->expression.expr->common.constant_value;
evaluate_binary_operator (AST_EQUAL,
type_a, value_a,
@@ -232,10 +115,7 @@ check_unique_cases (ast_t *ast)
gboolean
switch_check (ast_program_t *program)
{
- if (!check_constant_case_expressions ((ast_t *)program))
- return FALSE;
-
- if (!check_unique_cases ((ast_t *)program))
+ if (!real_switch_check ((ast_t *)program))
return FALSE;
return TRUE;