diff --git a/src/compiler/enums.h b/src/compiler/enums.h index af66b4a84..2b6b5f249 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -237,11 +237,12 @@ typedef enum PREC_ASSIGNMENT, // =, *=, /=, %=, +=, etc PREC_TRY_ELSE, // try and else PREC_TERNARY, // ?: - PREC_LOGICAL, // && || + PREC_OR, // || + PREC_AND, // && PREC_RELATIONAL, // < > <= >= == != PREC_ADDITIVE, // + - PREC_BIT, // ^ | & - PREC_SHIFT, // << >> >>> + PREC_SHIFT, // << >> PREC_MULTIPLICATIVE, // * / % PREC_UNARY, // ! - + ~ * & prefix ++/-- PREC_CALL, // . () [] postfix ++/-- diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 14af8bf75..3478902d9 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -5,21 +5,6 @@ #include "compiler_internal.h" #include "parser_internal.h" -#define BINOP_PREC_REQ_LEN 40 -int BINOP_PREC_REQ[BINOP_PREC_REQ_LEN] = { - // bitwise operations - [BINARYOP_BIT_OR] = 1, - [BINARYOP_BIT_XOR] = 1, - [BINARYOP_BIT_AND] = 1, - - // comparison operations - [BINARYOP_GT] = 2, - [BINARYOP_GE] = 2, - [BINARYOP_LT] = 2, - [BINARYOP_LE] = 2, - [BINARYOP_NE] = 2, - [BINARYOP_EQ] = 2 -}; typedef Expr *(*ParseFn)(Context *context, Expr *); @@ -389,21 +374,6 @@ static Expr *parse_failable(Context *context, Expr *left_side) } -bool unclear_op_precedence(Expr *left_side, Expr * main_expr, Expr *right_side) -{ - int precedence_main = BINOP_PREC_REQ[main_expr->binary_expr.operator]; - if (left_side->expr_kind == EXPR_BINARY) - { - int precedence_left = BINOP_PREC_REQ[left_side->binary_expr.operator]; - return precedence_left && (precedence_left == precedence_main); - } - if (right_side->expr_kind == EXPR_BINARY) - { - int precedence_right = BINOP_PREC_REQ[right_side->binary_expr.operator]; - return precedence_right && (precedence_right == precedence_main); - } - return false; -} static Expr *parse_binary(Context *context, Expr *left_side) { @@ -429,12 +399,6 @@ static Expr *parse_binary(Context *context, Expr *left_side) expr->binary_expr.left = left_side; expr->binary_expr.right = right_side; - // check if both sides have a binary operation where the precedence is unclear. Example: a ^ b | c - if (unclear_op_precedence(left_side, expr, right_side)) - { - SEMA_TOKEN_ERROR(context->tok, "You need to add explicit parentheses."); - } - expr->span.end_loc = right_side->span.end_loc; return expr; } @@ -1141,8 +1105,8 @@ ParseRule rules[TOKEN_EOF + 1] = { [TOKEN_AT] = { parse_macro_expansion, NULL, PREC_NONE }, [TOKEN_STRING] = { parse_string_literal, NULL, PREC_NONE }, [TOKEN_REAL] = { parse_double, NULL, PREC_NONE }, - [TOKEN_OR] = { NULL, parse_binary, PREC_LOGICAL }, - [TOKEN_AND] = { parse_unary_expr, parse_binary, PREC_LOGICAL }, + [TOKEN_OR] = { NULL, parse_binary, PREC_OR }, + [TOKEN_AND] = { parse_unary_expr, parse_binary, PREC_AND }, [TOKEN_EQ] = { NULL, parse_binary, PREC_ASSIGNMENT }, [TOKEN_PLUS_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, [TOKEN_MINUS_ASSIGN] = { NULL, parse_binary, PREC_ASSIGNMENT }, diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index c75cf38a3..66964f741 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -11,6 +11,27 @@ static inline bool sema_cast_rvalue(Context *context, Type *to, Expr *expr); +int BINOP_PREC_REQ[BINARYOP_LAST] = +{ + // bitwise operations + [BINARYOP_BIT_OR] = 1, + [BINARYOP_BIT_XOR] = 1, + [BINARYOP_BIT_AND] = 1, + + // comparison operations + [BINARYOP_GT] = 2, + [BINARYOP_GE] = 2, + [BINARYOP_LT] = 2, + [BINARYOP_LE] = 2, + [BINARYOP_NE] = 2, + [BINARYOP_EQ] = 2, + + [BINARYOP_SHR] = 3, + [BINARYOP_SHL] = 3, + + +}; + static Expr *expr_access_inline_member(Expr *parent, Decl *parent_decl) { @@ -4147,7 +4168,7 @@ static bool sema_expr_analyse_shift_assign(Context *context, Expr *expr, Expr *l static bool sema_expr_analyse_and(Context *context, Expr *expr, Expr *left, Expr *right) { - if (!sema_analyse_expr(context, type_bool, left) & sema_analyse_expr(context, type_bool, right)) return false; + if (!sema_analyse_expr(context, type_bool, left) || !sema_analyse_expr(context, type_bool, right)) return false; if (!cast_implicit(left, type_bool) || !cast_implicit(right, type_bool)) return false; expr_set_type(expr, type_bool); @@ -4705,12 +4726,34 @@ static inline bool sema_expr_analyse_taddr(Context *context, Type *to, Expr *exp return true; } +static bool unclear_op_precedence(Expr *left_side, Expr * main_expr, Expr *right_side) +{ + int precedence_main = BINOP_PREC_REQ[main_expr->binary_expr.operator]; + if (left_side->expr_kind == EXPR_BINARY) + { + int precedence_left = BINOP_PREC_REQ[left_side->binary_expr.operator]; + return precedence_left && (precedence_left == precedence_main); + } + if (right_side->expr_kind == EXPR_BINARY) + { + int precedence_right = BINOP_PREC_REQ[right_side->binary_expr.operator]; + return precedence_right && (precedence_right == precedence_main); + } + return false; +} static inline bool sema_expr_analyse_binary(Context *context, Type *to, Expr *expr) { assert(expr->resolve_status == RESOLVE_RUNNING); Expr *left = expr->binary_expr.left; Expr *right = expr->binary_expr.right; + + // check if both sides have a binary operation where the precedence is unclear. Example: a ^ b | c + if (unclear_op_precedence(left, expr, right)) + { + SEMA_ERROR(expr, "You need to add explicit parentheses to clarify precedence."); + return expr_poison(expr); + } // Don't push down bool conversions for example. if (to && !type_is_numeric(to)) to = NULL; switch (expr->binary_expr.operator) diff --git a/test/test_suite/errors/precedence_err.c3 b/test/test_suite/errors/precedence_err.c3 index 3c9053eed..239a9690d 100644 --- a/test/test_suite/errors/precedence_err.c3 +++ b/test/test_suite/errors/precedence_err.c3 @@ -1,5 +1,5 @@ func void test() { - int x = 3 ^ 5 & 6; // #error: You need to add explicit parentheses. + int x = 3 ^ 5 & 6; // #error: You need to add explicit parentheses to clarify precedence } diff --git a/test/test_suite/precedence/required_parens.c3 b/test/test_suite/precedence/required_parens.c3 new file mode 100644 index 000000000..25fb162f4 --- /dev/null +++ b/test/test_suite/precedence/required_parens.c3 @@ -0,0 +1,13 @@ +module foo; +import std::io; + +func void main() +{ + int i = 0; + int j = i ^ i | i; // #error: You need to add explicit parentheses to clarify precedence. + bool x = i == j == i; // #error: You need to add explicit parentheses to clarify precedence. + int k = i >> j << k; // #error: You need to add explicit parentheses to clarify precedence. + bool xk = i && j || i; + int y = i + j + i; + int yy = i * i / i + i - i; +} \ No newline at end of file