checker: merge comptime_const_eval.v and noreturn.v into checker.v (#12573)
parent
9a2c563735
commit
ac3910b8c2
|
@ -8553,3 +8553,270 @@ fn (mut c Checker) ensure_type_exists(typ ast.Type, pos token.Position) ? {
|
||||||
else {}
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
Loading…
Reference in New Issue