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 {}
|
||||
}
|
||||
}
|
||||
|
||||
// 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