cgen: fix operator overloading for array/map aliases (#9529)

pull/9536/head
Swastik Baranwal 2021-03-30 15:09:54 +05:30 committed by GitHub
parent 9b9ef5fe1b
commit b40d06ec1e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 158 additions and 5 deletions

View File

@ -796,14 +796,54 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type {
return table.bool_type
}
.plus, .minus, .mul, .div, .mod, .xor, .amp, .pipe { // binary operators that expect matching types
if right.info is table.Alias
&& (right.info as table.Alias).language != .c && c.mod == c.table.type_to_str(right_type).split('.')[0] {
if right.info is table.Alias && (right.info as table.Alias).language != .c
&& c.mod == c.table.type_to_str(right_type).split('.')[0]
&& c.table.get_type_symbol((right.info as table.Alias).parent_type).is_primitive() {
right = c.table.get_type_symbol((right.info as table.Alias).parent_type)
}
if left.info is table.Alias
&& (left.info as table.Alias).language != .c && c.mod == c.table.type_to_str(left_type).split('.')[0] {
if left.info is table.Alias && (left.info as table.Alias).language != .c
&& c.mod == c.table.type_to_str(left_type).split('.')[0]
&& c.table.get_type_symbol((left.info as table.Alias).parent_type).is_primitive() {
left = c.table.get_type_symbol((left.info as table.Alias).parent_type)
}
// Check if the alias type is not a primitive then allow using operator overloading for aliased `arrays` and `maps`
if left.kind == .alias && left.info is table.Alias
&& !(c.table.get_type_symbol((left.info as table.Alias).parent_type).is_primitive()) {
if left.has_method(infix_expr.op.str()) {
if method := left.find_method(infix_expr.op.str()) {
return_type = method.return_type
} else {
return_type = left_type
}
} else {
left_name := c.table.type_to_str(left_type)
right_name := c.table.type_to_str(right_type)
if left_name == right_name {
c.error('undefined operation `$left_name` $infix_expr.op.str() `$right_name`',
left_right_pos)
} else {
c.error('mismatched types `$left_name` and `$right_name`', left_right_pos)
}
}
} else if right.kind == .alias && right.info is table.Alias
&& !(c.table.get_type_symbol((right.info as table.Alias).parent_type).is_primitive()) {
if right.has_method(infix_expr.op.str()) {
if method := right.find_method(infix_expr.op.str()) {
return_type = method.return_type
} else {
return_type = right_type
}
} else {
left_name := c.table.type_to_str(left_type)
right_name := c.table.type_to_str(right_type)
if left_name == right_name {
c.error('undefined operation `$left_name` $infix_expr.op.str() `$right_name`',
left_right_pos)
} else {
c.error('mismatched types `$left_name` and `$right_name`', left_right_pos)
}
}
}
if left.kind in [.array, .array_fixed, .map, .struct_] {
if left.has_method(infix_expr.op.str()) {
if method := left.find_method(infix_expr.op.str()) {

View File

@ -0,0 +1,13 @@
vlib/v/checker/tests/alias_array_unknown_op_overloading_err.vv:17:7: error: undefined operation `Tuple` - `Tuple`
15 | mut a := new_tuple(12, 4.5, 6.7, 6)
16 | b := new_tuple(12, 4.5, 6.7, 6)
17 | a -= b
| ~~
18 | println(a - b)
19 | }
vlib/v/checker/tests/alias_array_unknown_op_overloading_err.vv:18:13: error: undefined operation `Tuple` - `Tuple`
16 | b := new_tuple(12, 4.5, 6.7, 6)
17 | a -= b
18 | println(a - b)
| ~~~~~
19 | }

View File

@ -0,0 +1,19 @@
type Tuple = []f64
fn new_tuple(x f64, y f64, z f64, w f64) Tuple {
return Tuple([x, y, z, w])
}
fn (a Tuple) + (b Tuple) Tuple {
mut res := []f64{len: a.len}
for i := 0; i < a.len; i++ {
res[i] = a[i] + b[i]
}
return Tuple(res)
}
fn main() {
mut a := new_tuple(12, 4.5, 6.7, 6)
b := new_tuple(12, 4.5, 6.7, 6)
a -= b
println(a - b)
}

View File

@ -0,0 +1,13 @@
vlib/v/checker/tests/alias_map_unknown_op_overloading_err.vv:19:10: error: undefined operation `Map` - `Map`
17 | mut a := new_map()
18 | b := new_map()
19 | println(a - b)
| ~~~~~
20 | a -= b
21 | }
vlib/v/checker/tests/alias_map_unknown_op_overloading_err.vv:20:7: error: undefined operation `Map` - `Map`
18 | b := new_map()
19 | println(a - b)
20 | a -= b
| ~~
21 | }

View File

@ -0,0 +1,21 @@
type Map = map[string]string
pub fn new_map() Map {
return Map(map{
'23': 'str'
})
}
fn (a Map) + (b Map) Map {
str := b['23']
return Map(map{
'34': str + '12'
})
}
fn main() {
mut a := new_map()
b := new_map()
println(a - b)
a -= b
}

View File

@ -3703,7 +3703,12 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
e := right_sym.kind !in [.voidptr, .int_literal, .int]
if node.op in [.plus, .minus, .mul, .div, .mod, .lt, .eq] && ((a && b && e) || c || d) {
// Overloaded operators
the_left_type := if !d { left_type } else { (left_sym.info as table.Alias).parent_type }
the_left_type := if !d
|| g.table.get_type_symbol((left_sym.info as table.Alias).parent_type).kind in [.array, .array_fixed, .map] {
left_type
} else {
(left_sym.info as table.Alias).parent_type
}
g.write(g.typ(the_left_type))
g.write('_')
g.write(util.replace_op(node.op.str()))

View File

@ -0,0 +1,19 @@
type Tuple = []f64
fn new_tuple(x f64, y f64, z f64, w f64) Tuple {
return Tuple([x, y, z, w])
}
fn (a Tuple) + (b Tuple) Tuple {
mut res := []f64{len: a.len}
for i := 0; i < a.len; i++ {
res[i] = a[i] + b[i]
}
return Tuple(res)
}
fn test_tuple_arithmetic() {
a := new_tuple(3, -2, 5, 1)
b := new_tuple(-2, 3, 1, 0)
assert a + b == new_tuple(1, 1, 6, 1)
}

View File

@ -0,0 +1,23 @@
type Map = map[string]string
pub fn new_map() Map {
return Map(map{
'23': 'str'
})
}
fn (a Map) + (b Map) Map {
str := b['23']
return Map(map{
'34': str + '12'
})
}
fn test_map_alias_op_overloading() {
a := new_map()
b := new_map()
assert a + b == Map(map{
'34': 'str12'
})
assert '${a + b}' == "Map({'34': 'str12'})"
}