Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
267 changes: 267 additions & 0 deletions vlib/v/checker/checker.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Loading