checker: move arr << logic to check_append()

This commit is contained in:
Alexander Medvednikov 2025-06-04 02:39:03 +03:00
parent 262a72ea96
commit 03e93b35e3
4 changed files with 81 additions and 63 deletions

View File

@ -775,3 +775,67 @@ fn (mut c Checker) check_elements_initialized(typ ast.Type) ! {
else {}
}
}
fn (mut c Checker) check_append(mut node ast.InfixExpr, left_type ast.Type, right_type ast.Type,
right_final_sym ast.TypeSymbol) ast.Type {
if !node.is_stmt {
c.error('array append cannot be used in an expression', node.pos)
}
if left_type.has_flag(.option) && node.left is ast.Ident && node.left.or_expr.kind == .absent {
c.error('unwrapped Option cannot be used in an infix expression', node.pos)
}
right_pos := node.right.pos()
mut right_sym := c.table.sym(right_type)
mut left_sym := c.table.sym(left_type)
// `array << elm`
c.check_expr_option_or_result_call(node.right, right_type)
node.auto_locked, _ = c.fail_if_immutable(mut node.left)
left_value_type := c.table.value_type(c.unwrap_generic(left_type))
left_value_sym := c.table.sym(c.unwrap_generic(left_value_type))
if !left_value_type.has_flag(.option) && right_type.has_flag(.option) {
c.error('unwrapped Option cannot be used in an infix expression', node.pos)
}
if left_value_sym.kind == .interface {
if right_final_sym.kind != .array {
// []Animal << Cat
if c.type_implements(right_type, left_value_type, right_pos) {
if !right_type.is_any_kind_of_pointer() && !c.inside_unsafe
&& right_sym.kind != .interface {
c.mark_as_referenced(mut &node.right, true)
}
}
} else {
// []Animal << []Cat
c.type_implements(c.table.value_type(right_type), left_value_type, right_pos)
}
return ast.void_type
} else if left_value_sym.kind == .sum_type {
if right_sym.kind != .array {
if !c.table.is_sumtype_or_in_variant(left_value_type, ast.mktyp(c.unwrap_generic(right_type))) {
c.error('cannot append `${right_sym.name}` to `${left_sym.name}`', right_pos)
}
} else {
right_value_type := c.table.value_type(c.unwrap_generic(right_type))
if !c.table.is_sumtype_or_in_variant(left_value_type, ast.mktyp(right_value_type)) {
c.error('cannot append `${right_sym.name}` to `${left_sym.name}`', right_pos)
}
}
return ast.void_type
}
// []T << T or []T << []T
unwrapped_right_type := c.unwrap_generic(right_type)
if c.check_types(unwrapped_right_type, left_value_type) {
// []&T << T is wrong: we check for that, !(T.is_ptr()) && ?(&T).is_ptr()
if !(!unwrapped_right_type.is_ptr() && left_value_type.is_ptr()
&& left_value_type.share() == .mut_t) {
return ast.void_type
}
} else if c.check_types(unwrapped_right_type, c.unwrap_generic(left_type)) {
return ast.void_type
}
if left_value_type.has_flag(.option) && right_type == ast.none_type {
return ast.void_type
}
c.error('cannot append `${right_sym.name}` to `${left_sym.name}`', right_pos)
return ast.void_type
}

View File

@ -631,69 +631,7 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
.left_shift {
if left_final_sym.kind == .array
|| c.table.sym(c.unwrap_generic(left_type)).kind == .array {
if !node.is_stmt {
c.error('array append cannot be used in an expression', node.pos)
}
if left_type.has_flag(.option) && node.left is ast.Ident
&& node.left.or_expr.kind == .absent {
c.error('unwrapped Option cannot be used in an infix expression',
node.pos)
}
// `array << elm`
c.check_expr_option_or_result_call(node.right, right_type)
node.auto_locked, _ = c.fail_if_immutable(mut node.left)
left_value_type := c.table.value_type(c.unwrap_generic(left_type))
left_value_sym := c.table.sym(c.unwrap_generic(left_value_type))
if !left_value_type.has_flag(.option) && right_type.has_flag(.option) {
c.error('unwrapped Option cannot be used in an infix expression',
node.pos)
}
if left_value_sym.kind == .interface {
if right_final_sym.kind != .array {
// []Animal << Cat
if c.type_implements(right_type, left_value_type, right_pos) {
if !right_type.is_any_kind_of_pointer() && !c.inside_unsafe
&& right_sym.kind != .interface {
c.mark_as_referenced(mut &node.right, true)
}
}
} else {
// []Animal << []Cat
c.type_implements(c.table.value_type(right_type), left_value_type,
right_pos)
}
return ast.void_type
} else if left_value_sym.kind == .sum_type {
if right_sym.kind != .array {
if !c.table.is_sumtype_or_in_variant(left_value_type, ast.mktyp(c.unwrap_generic(right_type))) {
c.error('cannot append `${right_sym.name}` to `${left_sym.name}`',
right_pos)
}
} else {
right_value_type := c.table.value_type(c.unwrap_generic(right_type))
if !c.table.is_sumtype_or_in_variant(left_value_type, ast.mktyp(right_value_type)) {
c.error('cannot append `${right_sym.name}` to `${left_sym.name}`',
right_pos)
}
}
return ast.void_type
}
// []T << T or []T << []T
unwrapped_right_type := c.unwrap_generic(right_type)
if c.check_types(unwrapped_right_type, left_value_type) {
// []&T << T is wrong: we check for that, !(T.is_ptr()) && ?(&T).is_ptr()
if !(!unwrapped_right_type.is_ptr() && left_value_type.is_ptr()
&& left_value_type.share() == .mut_t) {
return ast.void_type
}
} else if c.check_types(unwrapped_right_type, c.unwrap_generic(left_type)) {
return ast.void_type
}
if left_value_type.has_flag(.option) && right_type == ast.none_type {
return ast.void_type
}
c.error('cannot append `${right_sym.name}` to `${left_sym.name}`', right_pos)
return ast.void_type
return c.check_append(mut node, left_type, right_type, right_final_sym)
} else {
node.promoted_type = c.check_shift(mut node, left_type, right_type)
return node.promoted_type

View File

@ -0,0 +1,16 @@
/*
struct Foobar {
mut:
foo int
}
const foobar_const = Foobar{123}
fn main() {
println("foobar_const.foo: ${foobar_const.foo}") // foobar_const.foo: 123
mut foobars := []&Foobar{}
foobars << &foobar_const
foobars[0].foo = 456
println("foobar_const.foo: ${foobar_const.foo}") // foobar_const.foo: 456
}
*/