From d75c62b806bb4542c97ac8a3fc9e32ee80ff58d7 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Sat, 27 Aug 2022 11:54:49 +0300 Subject: [PATCH] checker: allow + operator overrides for type aliases --- vlib/v/checker/check_types.v | 7 ++++ vlib/v/checker/infix.v | 22 +++++++++-- .../alias_array_possible_type_mismatch.out | 8 ++++ .../alias_array_possible_type_mismatch.vv | 13 +++++++ ...ypesymbol_to_a_type_should_not_compile.out | 1 + .../checker/tests/eq_ne_op_wrong_type_err.out | 12 ++++++ vlib/v/tests/alias_array_plus_operator_test.v | 37 +++++++++++++++++++ 7 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 vlib/v/checker/tests/alias_array_possible_type_mismatch.out create mode 100644 vlib/v/checker/tests/alias_array_possible_type_mismatch.vv create mode 100644 vlib/v/tests/alias_array_plus_operator_test.v diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v index 6deafbb03..b74aae40b 100644 --- a/vlib/v/checker/check_types.v +++ b/vlib/v/checker/check_types.v @@ -519,6 +519,13 @@ fn (mut c Checker) check_shift(mut node ast.InfixExpr, left_type_ ast.Type, righ return left_type } +pub fn (mut c Checker) promote_keeping_aliases(left_type ast.Type, right_type ast.Type, left_kind ast.Kind, right_kind ast.Kind) ast.Type { + if left_type == right_type && left_kind == .alias && right_kind == .alias { + return left_type + } + return c.promote(left_type, right_type) +} + pub fn (mut c Checker) promote(left_type ast.Type, right_type ast.Type) ast.Type { if left_type.is_any_kind_of_pointer() { if right_type.is_int() || c.pref.translated { diff --git a/vlib/v/checker/infix.v b/vlib/v/checker/infix.v index cddd41b3a..c4acba2be 100644 --- a/vlib/v/checker/infix.v +++ b/vlib/v/checker/infix.v @@ -45,9 +45,15 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { && node.op in [.plus, .minus, .mul, .div, .mod, .xor, .amp, .pipe] { if !c.pref.translated && ((right_type.is_any_kind_of_pointer() && node.op != .minus) || (!right_type.is_any_kind_of_pointer() && node.op !in [.plus, .minus])) { - left_name := c.table.type_to_str(left_type) - right_name := c.table.type_to_str(right_type) - c.error('invalid operator `$node.op` to `$left_name` and `$right_name`', left_right_pos) + if _ := left_sym.find_method(node.op.str()) { + if left_sym.kind == .alias && right_sym.kind == .alias { + // allow an explicit operator override `fn (x &AliasType) OP (y &AliasType) &AliasType {` + } else { + c.invalid_operator_error(node.op, left_type, right_type, left_right_pos) + } + } else { + c.invalid_operator_error(node.op, left_type, right_type, left_right_pos) + } } else if node.op in [.plus, .minus] { if !c.inside_unsafe && !node.left.is_auto_deref_var() && !node.right.is_auto_deref_var() { c.warn('pointer arithmetic is only allowed in `unsafe` blocks', left_right_pos) @@ -89,6 +95,7 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { (left_sym.kind == .alias && right_sym.kind in [.struct_, .array, .sum_type]) || (right_sym.kind == .alias && left_sym.kind in [.struct_, .array, .sum_type]) if is_mismatch { + c.add_error_detail('left type: `${c.table.type_to_str(left_type)}` vs right type: `${c.table.type_to_str(right_type)}`') c.error('possible type mismatch of compared values of `$node.op` operation', left_right_pos) } else if left_type in ast.integer_type_idxs && right_type in ast.integer_type_idxs { @@ -274,7 +281,8 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { } else { unaliased_left_type := c.table.unalias_num_type(left_type) unalias_right_type := c.table.unalias_num_type(right_type) - mut promoted_type := c.promote(unaliased_left_type, unalias_right_type) + mut promoted_type := c.promote_keeping_aliases(unaliased_left_type, unalias_right_type, + left_sym.kind, right_sym.kind) // substract pointers is allowed in unsafe block is_allowed_pointer_arithmetic := left_type.is_any_kind_of_pointer() && right_type.is_any_kind_of_pointer() && node.op == .minus @@ -657,3 +665,9 @@ fn (mut c Checker) check_div_mod_by_zero(expr ast.Expr, op_kind token.Kind) { else {} } } + +pub fn (mut c Checker) invalid_operator_error(op token.Kind, left_type ast.Type, right_type ast.Type, pos token.Pos) { + left_name := c.table.type_to_str(left_type) + right_name := c.table.type_to_str(right_type) + c.error('invalid operator `$op` to `$left_name` and `$right_name`', pos) +} diff --git a/vlib/v/checker/tests/alias_array_possible_type_mismatch.out b/vlib/v/checker/tests/alias_array_possible_type_mismatch.out new file mode 100644 index 000000000..f1298619d --- /dev/null +++ b/vlib/v/checker/tests/alias_array_possible_type_mismatch.out @@ -0,0 +1,8 @@ +vlib/v/checker/tests/alias_array_possible_type_mismatch.vv:11:7: error: possible type mismatch of compared values of `==` operation + 9 | mut a := new_vector(12, 4.5, 6.7, 6) + 10 | dump(a) + 11 | x := a == []f64{} + | ~~~~~~~~~~~ + 12 | dump(x) + 13 | } +Details: left type: `main.Vector` vs right type: `[]f64` diff --git a/vlib/v/checker/tests/alias_array_possible_type_mismatch.vv b/vlib/v/checker/tests/alias_array_possible_type_mismatch.vv new file mode 100644 index 000000000..5f8e6901f --- /dev/null +++ b/vlib/v/checker/tests/alias_array_possible_type_mismatch.vv @@ -0,0 +1,13 @@ +type Vector = []f64 +type Another = []f64 + +fn new_vector(x f64, y f64, z f64, w f64) Vector { + return Vector([x, y, z, w]) +} + +fn test_alias_array_plus_overload() { + mut a := new_vector(12, 4.5, 6.7, 6) + dump(a) + x := a == []f64{} + dump(x) +} diff --git a/vlib/v/checker/tests/comparing_typesymbol_to_a_type_should_not_compile.out b/vlib/v/checker/tests/comparing_typesymbol_to_a_type_should_not_compile.out index 3b8aae98f..dcdcf068a 100644 --- a/vlib/v/checker/tests/comparing_typesymbol_to_a_type_should_not_compile.out +++ b/vlib/v/checker/tests/comparing_typesymbol_to_a_type_should_not_compile.out @@ -5,3 +5,4 @@ vlib/v/checker/tests/comparing_typesymbol_to_a_type_should_not_compile.vv:12:7: | ~~~~~~~~~~~~~~~~~~~~~~~ 13 | println(typeof(isym).name) 14 | println(typeof(ast.string_type).name) +Details: left type: `&ast.TypeSymbol` vs right type: `ast.Type` diff --git a/vlib/v/checker/tests/eq_ne_op_wrong_type_err.out b/vlib/v/checker/tests/eq_ne_op_wrong_type_err.out index d5f43cf74..c141bf63e 100644 --- a/vlib/v/checker/tests/eq_ne_op_wrong_type_err.out +++ b/vlib/v/checker/tests/eq_ne_op_wrong_type_err.out @@ -33,6 +33,7 @@ vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:17:10: error: possible type mism | ~~~~~~~~~~~~~~~~~ 18 | println(AAaa{0} == Aaa{0}) 19 | println(AAaa{1} != Aaa{1}) +Details: left type: `main.Aaa` vs right type: `main.AAaa` vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:18:10: error: possible type mismatch of compared values of `==` operation 16 | 17 | println(Aaa{0} == AAaa{0}) @@ -40,6 +41,7 @@ vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:18:10: error: possible type mism | ~~~~~~~~~~~~~~~~~ 19 | println(AAaa{1} != Aaa{1}) 20 | println(Aaa{1} != AAaa{1}) +Details: left type: `main.AAaa` vs right type: `main.Aaa` vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:19:10: error: possible type mismatch of compared values of `!=` operation 17 | println(Aaa{0} == AAaa{0}) 18 | println(AAaa{0} == Aaa{0}) @@ -47,6 +49,7 @@ vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:19:10: error: possible type mism | ~~~~~~~~~~~~~~~~~ 20 | println(Aaa{1} != AAaa{1}) 21 | +Details: left type: `main.AAaa` vs right type: `main.Aaa` vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:20:10: error: possible type mismatch of compared values of `!=` operation 18 | println(AAaa{0} == Aaa{0}) 19 | println(AAaa{1} != Aaa{1}) @@ -54,6 +57,7 @@ vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:20:10: error: possible type mism | ~~~~~~~~~~~~~~~~~ 21 | 22 | arr := Arr([0]) +Details: left type: `main.Aaa` vs right type: `main.AAaa` vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:23:10: error: possible type mismatch of compared values of `==` operation 21 | 22 | arr := Arr([0]) @@ -61,6 +65,7 @@ vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:23:10: error: possible type mism | ~~~~~~~~~~ 24 | println([1] == arr) 25 | println(arr != [0]) +Details: left type: `main.Arr` vs right type: `[]int` vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:24:10: error: possible type mismatch of compared values of `==` operation 22 | arr := Arr([0]) 23 | println(arr == [0]) @@ -68,6 +73,7 @@ vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:24:10: error: possible type mism | ~~~~~~~~~~ 25 | println(arr != [0]) 26 | println([1] != arr) +Details: left type: `[]int` vs right type: `main.Arr` vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:25:10: error: possible type mismatch of compared values of `!=` operation 23 | println(arr == [0]) 24 | println([1] == arr) @@ -75,6 +81,7 @@ vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:25:10: error: possible type mism | ~~~~~~~~~~ 26 | println([1] != arr) 27 | +Details: left type: `main.Arr` vs right type: `[]int` vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:26:10: error: possible type mismatch of compared values of `!=` operation 24 | println([1] == arr) 25 | println(arr != [0]) @@ -82,6 +89,7 @@ vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:26:10: error: possible type mism | ~~~~~~~~~~ 27 | 28 | arr_aaa := ArrAaa(arr) +Details: left type: `[]int` vs right type: `main.Arr` vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:29:10: error: possible type mismatch of compared values of `==` operation 27 | 28 | arr_aaa := ArrAaa(arr) @@ -89,6 +97,7 @@ vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:29:10: error: possible type mism | ~~~~~~~~~~~~~~ 30 | println(arr == arr_aaa) 31 | println(arr_aaa != arr) +Details: left type: `main.ArrAaa` vs right type: `main.Arr` vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:30:10: error: possible type mismatch of compared values of `==` operation 28 | arr_aaa := ArrAaa(arr) 29 | println(arr_aaa == arr) @@ -96,6 +105,7 @@ vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:30:10: error: possible type mism | ~~~~~~~~~~~~~~ 31 | println(arr_aaa != arr) 32 | println(arr != arr_aaa) +Details: left type: `main.Arr` vs right type: `main.ArrAaa` vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:31:10: error: possible type mismatch of compared values of `!=` operation 29 | println(arr_aaa == arr) 30 | println(arr == arr_aaa) @@ -103,6 +113,7 @@ vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:31:10: error: possible type mism | ~~~~~~~~~~~~~~ 32 | println(arr != arr_aaa) 33 | +Details: left type: `main.ArrAaa` vs right type: `main.Arr` vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:32:10: error: possible type mismatch of compared values of `!=` operation 30 | println(arr == arr_aaa) 31 | println(arr_aaa != arr) @@ -110,6 +121,7 @@ vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:32:10: error: possible type mism | ~~~~~~~~~~~~~~ 33 | 34 | println(arr_aaa == [0]) +Details: left type: `main.Arr` vs right type: `main.ArrAaa` vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:34:10: error: infix expr: cannot use `[]int` (right expression) as `ArrAaa` 32 | println(arr != arr_aaa) 33 | diff --git a/vlib/v/tests/alias_array_plus_operator_test.v b/vlib/v/tests/alias_array_plus_operator_test.v new file mode 100644 index 000000000..91357727a --- /dev/null +++ b/vlib/v/tests/alias_array_plus_operator_test.v @@ -0,0 +1,37 @@ +type Vector = []f64 + +[heap] +struct HeapArray { +mut: + data []f64 +} + +fn new_vector(x f64, y f64, z f64, w f64) &Vector { + a := HeapArray{ + data: [x, y, z, w] + } + return &Vector(&a.data) +} + +pub fn (a &Vector) + (b &Vector) &Vector { + mut res := HeapArray{ + data: []f64{len: a.len} + } + for i := 0; i < a.len; i++ { + res.data[i] = (*a)[i] + (*b)[i] + } + return &Vector(&res.data) +} + +fn test_alias_array_plus_overload() { + mut a := new_vector(12, 4.5, 6.7, 6) + b := new_vector(12, 4.5, 6.7, 6) + dump(a) + dump(b) + c := a + b + dump(c) + assert *c == Vector([f64(24), 9, 13.4, 12]) + a = a + b + assert a.str() == c.str() + dump(a) +} -- 2.30.2