Required parens code moved to semantic analysis. Make || and && have different levels since parens cannot be enforced.

This commit is contained in:
Christoffer Lerno
2021-07-18 20:26:19 +02:00
parent ac2fd2dd71
commit 8ba2994caa
5 changed files with 63 additions and 42 deletions

View File

@@ -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 ++/--

View File

@@ -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 },

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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;
}