diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 74368d7ee8..8817dc5e32 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -8553,3 +8553,270 @@ fn (mut c Checker) ensure_type_exists(typ ast.Type, pos token.Position) ? { else {} } } + +// comptime const eval +fn eval_comptime_const_expr(expr ast.Expr, nlevel int) ?ast.ComptTimeConstValue { + if nlevel > 100 { + // protect against a too deep comptime eval recursion + return none + } + match expr { + ast.IntegerLiteral { + x := expr.val.u64() + if x > 9223372036854775807 { + return x + } + return expr.val.i64() + } + ast.StringLiteral { + return expr.val + } + ast.CharLiteral { + runes := expr.val.runes() + if runes.len > 0 { + return runes[0] + } + return none + } + ast.Ident { + if expr.obj is ast.ConstField { + // an existing constant? + return eval_comptime_const_expr(expr.obj.expr, nlevel + 1) + } + } + ast.CastExpr { + cast_expr_value := eval_comptime_const_expr(expr.expr, nlevel + 1) or { return none } + if expr.typ == ast.i8_type { + return cast_expr_value.i8() or { return none } + } + if expr.typ == ast.i16_type { + return cast_expr_value.i16() or { return none } + } + if expr.typ == ast.int_type { + return cast_expr_value.int() or { return none } + } + if expr.typ == ast.i64_type { + return cast_expr_value.i64() or { return none } + } + // + if expr.typ == ast.byte_type { + return cast_expr_value.byte() or { return none } + } + if expr.typ == ast.u16_type { + return cast_expr_value.u16() or { return none } + } + if expr.typ == ast.u32_type { + return cast_expr_value.u32() or { return none } + } + if expr.typ == ast.u64_type { + return cast_expr_value.u64() or { return none } + } + // + if expr.typ == ast.f32_type { + return cast_expr_value.f32() or { return none } + } + if expr.typ == ast.f64_type { + return cast_expr_value.f64() or { return none } + } + } + ast.InfixExpr { + left := eval_comptime_const_expr(expr.left, nlevel + 1) ? + right := eval_comptime_const_expr(expr.right, nlevel + 1) ? + if left is string && right is string { + match expr.op { + .plus { + return left + right + } + else { + return none + } + } + } else if left is u64 && right is i64 { + match expr.op { + .plus { return i64(left) + i64(right) } + .minus { return i64(left) - i64(right) } + .mul { return i64(left) * i64(right) } + .div { return i64(left) / i64(right) } + .mod { return i64(left) % i64(right) } + .xor { return i64(left) ^ i64(right) } + .pipe { return i64(left) | i64(right) } + .amp { return i64(left) & i64(right) } + .left_shift { return i64(left) << i64(right) } + .right_shift { return i64(left) >> i64(right) } + else { return none } + } + } else if left is i64 && right is u64 { + match expr.op { + .plus { return i64(left) + i64(right) } + .minus { return i64(left) - i64(right) } + .mul { return i64(left) * i64(right) } + .div { return i64(left) / i64(right) } + .mod { return i64(left) % i64(right) } + .xor { return i64(left) ^ i64(right) } + .pipe { return i64(left) | i64(right) } + .amp { return i64(left) & i64(right) } + .left_shift { return i64(left) << i64(right) } + .right_shift { return i64(left) >> i64(right) } + else { return none } + } + } else if left is u64 && right is u64 { + match expr.op { + .plus { return left + right } + .minus { return left - right } + .mul { return left * right } + .div { return left / right } + .mod { return left % right } + .xor { return left ^ right } + .pipe { return left | right } + .amp { return left & right } + .left_shift { return left << right } + .right_shift { return left >> right } + else { return none } + } + } else if left is i64 && right is i64 { + match expr.op { + .plus { return left + right } + .minus { return left - right } + .mul { return left * right } + .div { return left / right } + .mod { return left % right } + .xor { return left ^ right } + .pipe { return left | right } + .amp { return left & right } + .left_shift { return left << right } + .right_shift { return left >> right } + else { return none } + } + } else if left is byte && right is byte { + match expr.op { + .plus { return left + right } + .minus { return left - right } + .mul { return left * right } + .div { return left / right } + .mod { return left % right } + .xor { return left ^ right } + .pipe { return left | right } + .amp { return left & right } + .left_shift { return left << right } + .right_shift { return left >> right } + else { return none } + } + } + } + else { + // eprintln('>>> nlevel: $nlevel | another $expr.type_name() | $expr ') + return none + } + } + return none +} + +fn (mut c Checker) check_noreturn_fn_decl(mut node ast.FnDecl) { + if !node.is_noreturn { + return + } + if node.no_body { + return + } + if node.return_type != ast.void_type { + c.error('[noreturn] functions cannot have return types', node.pos) + } + if uses_return_stmt(node.stmts) { + c.error('[noreturn] functions cannot use return statements', node.pos) + } + if node.stmts.len != 0 { + mut is_valid_end_of_noreturn_fn := false + last_stmt := node.stmts.last() + match last_stmt { + ast.ExprStmt { + if last_stmt.expr is ast.CallExpr { + if last_stmt.expr.should_be_skipped { + c.error('[noreturn] functions cannot end with a skippable `[if ..]` call', + last_stmt.pos) + return + } + if last_stmt.expr.is_noreturn { + is_valid_end_of_noreturn_fn = true + } + } + } + ast.ForStmt { + if last_stmt.is_inf && last_stmt.stmts.len == 0 { + is_valid_end_of_noreturn_fn = true + } + } + else {} + } + if !is_valid_end_of_noreturn_fn { + c.error('[noreturn] functions should end with a call to another [noreturn] function, or with an infinite `for {}` loop', + last_stmt.pos) + return + } + } +} + +fn uses_return_stmt(stmts []ast.Stmt) bool { + if stmts.len == 0 { + return false + } + for stmt in stmts { + match stmt { + ast.Return { + return true + } + ast.Block { + if uses_return_stmt(stmt.stmts) { + return true + } + } + ast.ExprStmt { + match stmt.expr { + ast.CallExpr { + if uses_return_stmt(stmt.expr.or_block.stmts) { + return true + } + } + ast.MatchExpr { + for b in stmt.expr.branches { + if uses_return_stmt(b.stmts) { + return true + } + } + } + ast.SelectExpr { + for b in stmt.expr.branches { + if uses_return_stmt(b.stmts) { + return true + } + } + } + ast.IfExpr { + for b in stmt.expr.branches { + if uses_return_stmt(b.stmts) { + return true + } + } + } + else {} + } + } + ast.ForStmt { + if uses_return_stmt(stmt.stmts) { + return true + } + } + ast.ForCStmt { + if uses_return_stmt(stmt.stmts) { + return true + } + } + ast.ForInStmt { + if uses_return_stmt(stmt.stmts) { + return true + } + } + else {} + } + } + return false +} diff --git a/vlib/v/checker/comptime_const_eval.v b/vlib/v/checker/comptime_const_eval.v deleted file mode 100644 index 5bb0781558..0000000000 --- a/vlib/v/checker/comptime_const_eval.v +++ /dev/null @@ -1,159 +0,0 @@ -module checker - -import v.ast - -fn eval_comptime_const_expr(expr ast.Expr, nlevel int) ?ast.ComptTimeConstValue { - if nlevel > 100 { - // protect against a too deep comptime eval recursion - return none - } - match expr { - ast.IntegerLiteral { - x := expr.val.u64() - if x > 9223372036854775807 { - return x - } - return expr.val.i64() - } - ast.StringLiteral { - return expr.val - } - ast.CharLiteral { - runes := expr.val.runes() - if runes.len > 0 { - return runes[0] - } - return none - } - ast.Ident { - if expr.obj is ast.ConstField { - // an existing constant? - return eval_comptime_const_expr(expr.obj.expr, nlevel + 1) - } - } - ast.CastExpr { - cast_expr_value := eval_comptime_const_expr(expr.expr, nlevel + 1) or { return none } - if expr.typ == ast.i8_type { - return cast_expr_value.i8() or { return none } - } - if expr.typ == ast.i16_type { - return cast_expr_value.i16() or { return none } - } - if expr.typ == ast.int_type { - return cast_expr_value.int() or { return none } - } - if expr.typ == ast.i64_type { - return cast_expr_value.i64() or { return none } - } - // - if expr.typ == ast.byte_type { - return cast_expr_value.byte() or { return none } - } - if expr.typ == ast.u16_type { - return cast_expr_value.u16() or { return none } - } - if expr.typ == ast.u32_type { - return cast_expr_value.u32() or { return none } - } - if expr.typ == ast.u64_type { - return cast_expr_value.u64() or { return none } - } - // - if expr.typ == ast.f32_type { - return cast_expr_value.f32() or { return none } - } - if expr.typ == ast.f64_type { - return cast_expr_value.f64() or { return none } - } - } - ast.InfixExpr { - left := eval_comptime_const_expr(expr.left, nlevel + 1) ? - right := eval_comptime_const_expr(expr.right, nlevel + 1) ? - if left is string && right is string { - match expr.op { - .plus { - return left + right - } - else { - return none - } - } - } else if left is u64 && right is i64 { - match expr.op { - .plus { return i64(left) + i64(right) } - .minus { return i64(left) - i64(right) } - .mul { return i64(left) * i64(right) } - .div { return i64(left) / i64(right) } - .mod { return i64(left) % i64(right) } - .xor { return i64(left) ^ i64(right) } - .pipe { return i64(left) | i64(right) } - .amp { return i64(left) & i64(right) } - .left_shift { return i64(left) << i64(right) } - .right_shift { return i64(left) >> i64(right) } - else { return none } - } - } else if left is i64 && right is u64 { - match expr.op { - .plus { return i64(left) + i64(right) } - .minus { return i64(left) - i64(right) } - .mul { return i64(left) * i64(right) } - .div { return i64(left) / i64(right) } - .mod { return i64(left) % i64(right) } - .xor { return i64(left) ^ i64(right) } - .pipe { return i64(left) | i64(right) } - .amp { return i64(left) & i64(right) } - .left_shift { return i64(left) << i64(right) } - .right_shift { return i64(left) >> i64(right) } - else { return none } - } - } else if left is u64 && right is u64 { - match expr.op { - .plus { return left + right } - .minus { return left - right } - .mul { return left * right } - .div { return left / right } - .mod { return left % right } - .xor { return left ^ right } - .pipe { return left | right } - .amp { return left & right } - .left_shift { return left << right } - .right_shift { return left >> right } - else { return none } - } - } else if left is i64 && right is i64 { - match expr.op { - .plus { return left + right } - .minus { return left - right } - .mul { return left * right } - .div { return left / right } - .mod { return left % right } - .xor { return left ^ right } - .pipe { return left | right } - .amp { return left & right } - .left_shift { return left << right } - .right_shift { return left >> right } - else { return none } - } - } else if left is byte && right is byte { - match expr.op { - .plus { return left + right } - .minus { return left - right } - .mul { return left * right } - .div { return left / right } - .mod { return left % right } - .xor { return left ^ right } - .pipe { return left | right } - .amp { return left & right } - .left_shift { return left << right } - .right_shift { return left >> right } - else { return none } - } - } - } - else { - // eprintln('>>> nlevel: $nlevel | another $expr.type_name() | $expr ') - return none - } - } - return none -} diff --git a/vlib/v/checker/noreturn.v b/vlib/v/checker/noreturn.v deleted file mode 100644 index aaf0986049..0000000000 --- a/vlib/v/checker/noreturn.v +++ /dev/null @@ -1,113 +0,0 @@ -module checker - -import v.ast - -fn (mut c Checker) check_noreturn_fn_decl(mut node ast.FnDecl) { - if !node.is_noreturn { - return - } - if node.no_body { - return - } - if node.return_type != ast.void_type { - c.error('[noreturn] functions cannot have return types', node.pos) - } - if uses_return_stmt(node.stmts) { - c.error('[noreturn] functions cannot use return statements', node.pos) - } - if node.stmts.len != 0 { - mut is_valid_end_of_noreturn_fn := false - last_stmt := node.stmts.last() - match last_stmt { - ast.ExprStmt { - if last_stmt.expr is ast.CallExpr { - if last_stmt.expr.should_be_skipped { - c.error('[noreturn] functions cannot end with a skippable `[if ..]` call', - last_stmt.pos) - return - } - if last_stmt.expr.is_noreturn { - is_valid_end_of_noreturn_fn = true - } - } - } - ast.ForStmt { - if last_stmt.is_inf && last_stmt.stmts.len == 0 { - is_valid_end_of_noreturn_fn = true - } - } - else {} - } - if !is_valid_end_of_noreturn_fn { - c.error('[noreturn] functions should end with a call to another [noreturn] function, or with an infinite `for {}` loop', - last_stmt.pos) - return - } - } -} - -fn uses_return_stmt(stmts []ast.Stmt) bool { - if stmts.len == 0 { - return false - } - for stmt in stmts { - match stmt { - ast.Return { - return true - } - ast.Block { - if uses_return_stmt(stmt.stmts) { - return true - } - } - ast.ExprStmt { - match stmt.expr { - ast.CallExpr { - if uses_return_stmt(stmt.expr.or_block.stmts) { - return true - } - } - ast.MatchExpr { - for b in stmt.expr.branches { - if uses_return_stmt(b.stmts) { - return true - } - } - } - ast.SelectExpr { - for b in stmt.expr.branches { - if uses_return_stmt(b.stmts) { - return true - } - } - } - ast.IfExpr { - for b in stmt.expr.branches { - if uses_return_stmt(b.stmts) { - return true - } - } - } - else {} - } - } - ast.ForStmt { - if uses_return_stmt(stmt.stmts) { - return true - } - } - ast.ForCStmt { - if uses_return_stmt(stmt.stmts) { - return true - } - } - ast.ForInStmt { - if uses_return_stmt(stmt.stmts) { - return true - } - } - else {} - } - } - return false -}