From b40d06ec1e65d74fac27b04798077c2f3111f65a Mon Sep 17 00:00:00 2001 From: Swastik Baranwal Date: Tue, 30 Mar 2021 15:09:54 +0530 Subject: [PATCH] cgen: fix operator overloading for array/map aliases (#9529) --- vlib/v/checker/checker.v | 48 +++++++++++++++++-- ...alias_array_unknown_op_overloading_err.out | 13 +++++ .../alias_array_unknown_op_overloading_err.vv | 19 ++++++++ .../alias_map_unknown_op_overloading_err.out | 13 +++++ .../alias_map_unknown_op_overloading_err.vv | 21 ++++++++ vlib/v/gen/c/cgen.v | 7 ++- .../alias_array_operator_overloading_test.v | 19 ++++++++ .../alias_map_operator_overloading_test.v | 23 +++++++++ 8 files changed, 158 insertions(+), 5 deletions(-) create mode 100644 vlib/v/checker/tests/alias_array_unknown_op_overloading_err.out create mode 100644 vlib/v/checker/tests/alias_array_unknown_op_overloading_err.vv create mode 100644 vlib/v/checker/tests/alias_map_unknown_op_overloading_err.out create mode 100644 vlib/v/checker/tests/alias_map_unknown_op_overloading_err.vv create mode 100644 vlib/v/tests/alias_array_operator_overloading_test.v create mode 100644 vlib/v/tests/alias_map_operator_overloading_test.v diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 7dc4e2b65d..cd1ae0583c 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -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()) { diff --git a/vlib/v/checker/tests/alias_array_unknown_op_overloading_err.out b/vlib/v/checker/tests/alias_array_unknown_op_overloading_err.out new file mode 100644 index 0000000000..34cfbdb82f --- /dev/null +++ b/vlib/v/checker/tests/alias_array_unknown_op_overloading_err.out @@ -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 | } diff --git a/vlib/v/checker/tests/alias_array_unknown_op_overloading_err.vv b/vlib/v/checker/tests/alias_array_unknown_op_overloading_err.vv new file mode 100644 index 0000000000..84d7034f87 --- /dev/null +++ b/vlib/v/checker/tests/alias_array_unknown_op_overloading_err.vv @@ -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) +} diff --git a/vlib/v/checker/tests/alias_map_unknown_op_overloading_err.out b/vlib/v/checker/tests/alias_map_unknown_op_overloading_err.out new file mode 100644 index 0000000000..6a93514b61 --- /dev/null +++ b/vlib/v/checker/tests/alias_map_unknown_op_overloading_err.out @@ -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 | } diff --git a/vlib/v/checker/tests/alias_map_unknown_op_overloading_err.vv b/vlib/v/checker/tests/alias_map_unknown_op_overloading_err.vv new file mode 100644 index 0000000000..bea0261c8b --- /dev/null +++ b/vlib/v/checker/tests/alias_map_unknown_op_overloading_err.vv @@ -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 +} diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 166d3e9529..deded48156 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -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())) diff --git a/vlib/v/tests/alias_array_operator_overloading_test.v b/vlib/v/tests/alias_array_operator_overloading_test.v new file mode 100644 index 0000000000..a53db96c96 --- /dev/null +++ b/vlib/v/tests/alias_array_operator_overloading_test.v @@ -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) +} diff --git a/vlib/v/tests/alias_map_operator_overloading_test.v b/vlib/v/tests/alias_map_operator_overloading_test.v new file mode 100644 index 0000000000..14ad50ffff --- /dev/null +++ b/vlib/v/tests/alias_map_operator_overloading_test.v @@ -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'})" +}