333 lines
11 KiB
V
333 lines
11 KiB
V
// Copyright (c) 2019-2022 Alexander Medvednikov. All rights reserved.
|
|
// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
|
|
module checker
|
|
|
|
import v.ast
|
|
import v.pref
|
|
import v.token
|
|
|
|
pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
|
|
if_kind := if node.is_comptime { '\$if' } else { 'if' }
|
|
mut node_is_expr := false
|
|
if node.branches.len > 0 && node.has_else {
|
|
stmts := node.branches[0].stmts
|
|
if stmts.len > 0 && stmts[stmts.len - 1] is ast.ExprStmt
|
|
&& (stmts[stmts.len - 1] as ast.ExprStmt).typ != ast.void_type {
|
|
node_is_expr = true
|
|
}
|
|
}
|
|
if c.expected_type == ast.void_type && node_is_expr {
|
|
c.expected_type = c.expected_or_type
|
|
}
|
|
expr_required := c.expected_type != ast.void_type
|
|
former_expected_type := c.expected_type
|
|
if node_is_expr {
|
|
c.expected_expr_type = c.expected_type
|
|
defer {
|
|
c.expected_expr_type = ast.void_type
|
|
}
|
|
}
|
|
node.typ = ast.void_type
|
|
mut nbranches_with_return := 0
|
|
mut nbranches_without_return := 0
|
|
mut should_skip := false // Whether the current branch should be skipped
|
|
mut found_branch := false // Whether a matching branch was found- skip the rest
|
|
mut is_comptime_type_is_expr := false // if `$if T is string`
|
|
for i in 0 .. node.branches.len {
|
|
mut branch := node.branches[i]
|
|
if branch.cond is ast.ParExpr && !c.pref.translated && !c.file.is_translated {
|
|
c.error('unnecessary `()` in `$if_kind` condition, use `$if_kind expr {` instead of `$if_kind (expr) {`.',
|
|
branch.pos)
|
|
}
|
|
if !node.has_else || i < node.branches.len - 1 {
|
|
if node.is_comptime {
|
|
should_skip = c.comptime_if_branch(branch.cond, branch.pos)
|
|
node.branches[i].pkg_exist = !should_skip
|
|
} else {
|
|
// check condition type is boolean
|
|
c.expected_type = ast.bool_type
|
|
cond_typ := c.unwrap_generic(c.expr(branch.cond))
|
|
if (cond_typ.idx() != ast.bool_type_idx || cond_typ.has_flag(.optional))
|
|
&& !c.pref.translated && !c.file.is_translated {
|
|
c.error('non-bool type `${c.table.type_to_str(cond_typ)}` used as if condition',
|
|
branch.cond.pos())
|
|
}
|
|
}
|
|
}
|
|
if node.is_comptime { // Skip checking if needed
|
|
// smartcast field type on comptime if
|
|
mut comptime_field_name := ''
|
|
if mut branch.cond is ast.InfixExpr {
|
|
if branch.cond.op == .key_is {
|
|
if branch.cond.right !is ast.TypeNode && branch.cond.right !is ast.ComptimeType {
|
|
c.error('invalid `\$if` condition: expected a type', branch.cond.right.pos())
|
|
return 0
|
|
}
|
|
left := branch.cond.left
|
|
if branch.cond.right is ast.ComptimeType && left is ast.TypeNode {
|
|
is_comptime_type_is_expr = true
|
|
checked_type := c.unwrap_generic(left.typ)
|
|
should_skip = !c.table.is_comptime_type(checked_type, branch.cond.right as ast.ComptimeType)
|
|
} else {
|
|
got_type := c.unwrap_generic((branch.cond.right as ast.TypeNode).typ)
|
|
sym := c.table.sym(got_type)
|
|
if sym.kind == .placeholder || got_type.has_flag(.generic) {
|
|
c.error('unknown type `$sym.name`', branch.cond.right.pos())
|
|
}
|
|
|
|
if left is ast.SelectorExpr {
|
|
comptime_field_name = left.expr.str()
|
|
c.comptime_fields_type[comptime_field_name] = got_type
|
|
is_comptime_type_is_expr = true
|
|
} else if branch.cond.right is ast.TypeNode && left is ast.TypeNode
|
|
&& sym.kind == .interface_ {
|
|
is_comptime_type_is_expr = true
|
|
// is interface
|
|
checked_type := c.unwrap_generic(left.typ)
|
|
should_skip = !c.table.does_type_implement_interface(checked_type,
|
|
got_type)
|
|
} else if left is ast.TypeNode {
|
|
is_comptime_type_is_expr = true
|
|
left_type := c.unwrap_generic(left.typ)
|
|
if left_type != got_type {
|
|
should_skip = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
cur_skip_flags := c.skip_flags
|
|
if found_branch {
|
|
c.skip_flags = true
|
|
} else if should_skip {
|
|
c.skip_flags = true
|
|
should_skip = false // Reset the value of `should_skip` for the next branch
|
|
} else if !is_comptime_type_is_expr {
|
|
found_branch = true // If a branch wasn't skipped, the rest must be
|
|
}
|
|
if c.fn_level == 0 && c.pref.output_cross_c {
|
|
// do not skip any of the branches for top level `$if OS {`
|
|
// statements, in `-os cross` mode
|
|
found_branch = false
|
|
c.skip_flags = false
|
|
c.ct_cond_stack << branch.cond
|
|
}
|
|
if !c.skip_flags {
|
|
if node_is_expr {
|
|
c.stmts_ending_with_expression(branch.stmts)
|
|
} else {
|
|
c.stmts(branch.stmts)
|
|
}
|
|
} else if c.pref.output_cross_c {
|
|
mut is_freestanding_block := false
|
|
if mut branch.cond is ast.Ident {
|
|
if branch.cond.name == 'freestanding' {
|
|
is_freestanding_block = true
|
|
}
|
|
}
|
|
if is_freestanding_block {
|
|
branch.stmts = []
|
|
node.branches[i].stmts = []
|
|
}
|
|
if node_is_expr {
|
|
c.stmts_ending_with_expression(branch.stmts)
|
|
} else {
|
|
c.stmts(branch.stmts)
|
|
}
|
|
} else if !is_comptime_type_is_expr {
|
|
node.branches[i].stmts = []
|
|
}
|
|
if comptime_field_name.len > 0 {
|
|
c.comptime_fields_type[comptime_field_name] = c.comptime_fields_default_type
|
|
}
|
|
c.skip_flags = cur_skip_flags
|
|
if c.fn_level == 0 && c.pref.output_cross_c && c.ct_cond_stack.len > 0 {
|
|
c.ct_cond_stack.delete_last()
|
|
}
|
|
} else {
|
|
// smartcast sumtypes and interfaces when using `is`
|
|
c.smartcast_if_conds(branch.cond, mut branch.scope)
|
|
if node_is_expr {
|
|
c.stmts_ending_with_expression(branch.stmts)
|
|
} else {
|
|
c.stmts(branch.stmts)
|
|
}
|
|
c.smartcast_mut_pos = token.Pos{}
|
|
c.smartcast_cond_pos = token.Pos{}
|
|
}
|
|
if expr_required {
|
|
if branch.stmts.len > 0 && branch.stmts[branch.stmts.len - 1] is ast.ExprStmt {
|
|
mut last_expr := branch.stmts[branch.stmts.len - 1] as ast.ExprStmt
|
|
c.expected_type = former_expected_type
|
|
if c.expected_type.has_flag(.optional) {
|
|
if node.typ == ast.void_type {
|
|
node.is_expr = true
|
|
node.typ = c.expected_type
|
|
}
|
|
}
|
|
if c.expected_type.has_flag(.generic) {
|
|
if node.typ == ast.void_type {
|
|
node.is_expr = true
|
|
node.typ = c.unwrap_generic(c.expected_type)
|
|
}
|
|
continue
|
|
}
|
|
last_expr.typ = c.expr(last_expr.expr)
|
|
if !c.check_types(last_expr.typ, node.typ) {
|
|
if node.typ == ast.void_type {
|
|
// first branch of if expression
|
|
node.is_expr = true
|
|
node.typ = last_expr.typ
|
|
continue
|
|
} else if node.typ in [ast.float_literal_type, ast.int_literal_type] {
|
|
if node.typ == ast.int_literal_type {
|
|
if last_expr.typ.is_int() || last_expr.typ.is_float() {
|
|
node.typ = last_expr.typ
|
|
continue
|
|
}
|
|
} else { // node.typ == float_literal
|
|
if last_expr.typ.is_float() {
|
|
node.typ = last_expr.typ
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
if last_expr.typ in [ast.float_literal_type, ast.int_literal_type] {
|
|
if last_expr.typ == ast.int_literal_type {
|
|
if node.typ.is_int() || node.typ.is_float() {
|
|
continue
|
|
}
|
|
} else { // expr_type == float_literal
|
|
if node.typ.is_float() {
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
if node.is_expr && c.table.sym(former_expected_type).kind == .sum_type {
|
|
continue
|
|
}
|
|
if is_noreturn_callexpr(last_expr.expr) {
|
|
continue
|
|
}
|
|
|
|
c.error('mismatched types `${c.table.type_to_str(node.typ)}` and `${c.table.type_to_str(last_expr.typ)}`',
|
|
node.pos)
|
|
}
|
|
} else {
|
|
c.error('`$if_kind` expression requires an expression as the last statement of every branch',
|
|
branch.pos)
|
|
}
|
|
for st in branch.stmts {
|
|
// must not contain C statements
|
|
st.check_c_expr() or { c.error('`if` expression branch has $err.msg()', st.pos) }
|
|
}
|
|
}
|
|
if mut branch.cond is ast.IfGuardExpr {
|
|
sym := c.table.sym(branch.cond.expr_type)
|
|
if sym.kind == .multi_return {
|
|
mr_info := sym.info as ast.MultiReturn
|
|
if branch.cond.vars.len != mr_info.types.len {
|
|
c.error('if guard expects $mr_info.types.len variables, but got $branch.cond.vars.len',
|
|
branch.pos)
|
|
} else {
|
|
for vi, var in branch.cond.vars {
|
|
branch.scope.update_var_type(var.name, mr_info.types[vi])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Also check for returns inside a comp.if's statements, even if its contents aren't parsed
|
|
if has_return := c.has_return(branch.stmts) {
|
|
if has_return {
|
|
nbranches_with_return++
|
|
} else {
|
|
nbranches_without_return++
|
|
}
|
|
}
|
|
}
|
|
if nbranches_with_return > 0 {
|
|
if nbranches_with_return == node.branches.len {
|
|
// if/else... where all branches returned
|
|
c.returns = true
|
|
}
|
|
if !node.has_else {
|
|
// `if cond { return ... }` means that when cond is false, execution continues
|
|
c.returns = false
|
|
}
|
|
if nbranches_without_return > 0 {
|
|
// some of the branches did not return
|
|
c.returns = false
|
|
}
|
|
}
|
|
// if only untyped literals were given default to int/f64
|
|
if node.typ == ast.int_literal_type {
|
|
node.typ = ast.int_type
|
|
} else if node.typ == ast.float_literal_type {
|
|
node.typ = ast.f64_type
|
|
}
|
|
if expr_required && !node.has_else {
|
|
d := if node.is_comptime { '$' } else { '' }
|
|
c.error('`$if_kind` expression needs `${d}else` clause', node.pos)
|
|
}
|
|
return node.typ
|
|
}
|
|
|
|
fn (mut c Checker) smartcast_if_conds(node ast.Expr, mut scope ast.Scope) {
|
|
if node is ast.InfixExpr {
|
|
if node.op == .and {
|
|
c.smartcast_if_conds(node.left, mut scope)
|
|
c.smartcast_if_conds(node.right, mut scope)
|
|
} else if node.op == .key_is {
|
|
right_expr := node.right
|
|
mut right_type := match right_expr {
|
|
ast.TypeNode {
|
|
right_expr.typ
|
|
}
|
|
ast.None {
|
|
ast.none_type_idx
|
|
}
|
|
else {
|
|
c.error('invalid type `$right_expr`', right_expr.pos())
|
|
ast.Type(0)
|
|
}
|
|
}
|
|
right_type = c.unwrap_generic(right_type)
|
|
if right_type != ast.Type(0) {
|
|
left_sym := c.table.sym(node.left_type)
|
|
right_sym := c.table.sym(right_type)
|
|
expr_type := c.unwrap_generic(c.expr(node.left))
|
|
if left_sym.kind == .interface_ {
|
|
if right_sym.kind != .interface_ {
|
|
c.type_implements(right_type, expr_type, node.pos)
|
|
} else {
|
|
return
|
|
}
|
|
} else if !c.check_types(right_type, expr_type) {
|
|
expect_str := c.table.type_to_str(right_type)
|
|
expr_str := c.table.type_to_str(expr_type)
|
|
c.error('cannot use type `$expect_str` as type `$expr_str`', node.pos)
|
|
}
|
|
if node.left in [ast.Ident, ast.SelectorExpr] && node.right is ast.TypeNode {
|
|
is_variable := if node.left is ast.Ident {
|
|
node.left.kind == .variable
|
|
} else {
|
|
true
|
|
}
|
|
if is_variable {
|
|
if (node.left is ast.Ident && (node.left as ast.Ident).is_mut)
|
|
|| (node.left is ast.SelectorExpr
|
|
&& (node.left as ast.SelectorExpr).is_mut) {
|
|
c.fail_if_immutable(node.left)
|
|
}
|
|
if left_sym.kind in [.interface_, .sum_type] {
|
|
c.smartcast(node.left, node.left_type, right_type, mut scope)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if node is ast.Likely {
|
|
c.smartcast_if_conds(node.expr, mut scope)
|
|
}
|
|
}
|