1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

cgen: fix cgen errors for overloaded operators using reference types for the receiver and operand (fix #16725) (fix #15859) (#16726)

This commit is contained in:
Delyan Angelov 2022-12-21 09:10:30 +02:00 committed by GitHub
parent d19c1ef087
commit 1cde55478d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 148 additions and 9 deletions

View File

@ -278,8 +278,12 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
if node.params.len != 2 {
c.error('operator methods should have exactly 1 argument', node.pos)
} else {
receiver_sym := c.table.sym(node.receiver.typ)
param_sym := c.table.sym(node.params[1].typ)
receiver_type := node.receiver.typ
receiver_sym := c.table.sym(receiver_type)
param_type := node.params[1].typ
param_sym := c.table.sym(param_type)
if param_sym.kind == .string && receiver_sym.kind == .string {
// bypass check for strings
// TODO there must be a better way to handle that
@ -302,6 +306,11 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
} else if parent_sym.is_primitive() {
c.error('cannot define operator methods on type alias for `${parent_sym.name}`',
node.pos)
} else if receiver_type != param_type {
srtype := c.table.type_to_str(receiver_type)
sptype := c.table.type_to_str(param_type)
c.error('the receiver type `${srtype}` should be the same type as the operand `${sptype}`',
node.pos)
}
}
}

View File

@ -0,0 +1,14 @@
vlib/v/checker/tests/overload_operator_difference_in_operand_types.vv:9:1: error: the receiver type `Resources` should be the same type as the operand `&Resources`
7 | }
8 |
9 | fn (a Resources) < (b &Resources) bool {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10 | return a.hru < b.hru && a.sru < b.sru && a.cru < b.cru && a.mru < b.mru
11 | }
vlib/v/checker/tests/overload_operator_difference_in_operand_types.vv:13:1: error: the receiver type `&Resources` should be the same type as the operand `Resources`
11 | }
12 |
13 | fn (a &Resources) == (b Resources) bool {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14 | return a.hru == b.hru
15 | }

View File

@ -0,0 +1,26 @@
struct Resources {
mut:
hru u64
sru u64
cru u64
mru u64
}
fn (a Resources) < (b &Resources) bool {
return a.hru < b.hru && a.sru < b.sru && a.cru < b.cru && a.mru < b.mru
}
fn (a &Resources) == (b Resources) bool {
return a.hru == b.hru
}
fn test_struct_with_reference_operands_for_the_overloaded_operators_do_work() {
aa := Resources{}
bb := Resources{}
assert dump(aa < bb) == false
assert dump(aa == bb) == true
assert dump(aa != bb) == false
assert dump(aa > bb) == false
assert dump(aa <= bb) == true
assert dump(aa >= bb) == true
}

View File

@ -85,7 +85,13 @@ fn (mut g Gen) infix_expr_arrow_op(node ast.InfixExpr) {
fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
left := g.unwrap(node.left_type)
right := g.unwrap(node.right_type)
has_defined_eq_operator := g.table.has_method(left.sym, '==')
mut has_defined_eq_operator := false
mut eq_operator_expects_ptr := false
if m := g.table.find_method(left.sym, '==') {
has_defined_eq_operator = true
eq_operator_expects_ptr = m.receiver_type.is_ptr()
}
// TODO: investigate why the following is needed for vlib/v/tests/string_alias_test.v and vlib/v/tests/anon_fn_with_alias_args_test.v
has_alias_eq_op_overload := left.sym.info is ast.Alias && left.sym.has_method('==')
if g.pref.translated && !g.is_builtin_mod {
g.gen_plain_infix_expr(node)
@ -113,9 +119,15 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
}
g.write('__eq(')
g.write('*'.repeat(left.typ.nr_muls()))
if eq_operator_expects_ptr {
g.write('&')
}
g.expr(node.left)
g.write(', ')
g.write('*'.repeat(right.typ.nr_muls()))
if eq_operator_expects_ptr {
g.write('&')
}
g.expr(node.right)
g.write(')')
} else if left.typ.idx() == right.typ.idx()
@ -294,7 +306,14 @@ fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
fn (mut g Gen) infix_expr_cmp_op(node ast.InfixExpr) {
left := g.unwrap(node.left_type)
right := g.unwrap(node.right_type)
has_operator_overloading := g.table.has_method(left.sym, '<')
mut has_operator_overloading := false
mut operator_expects_ptr := false
if m := g.table.find_method(left.sym, '<') {
has_operator_overloading = true
operator_expects_ptr = m.receiver_type.is_ptr()
}
if g.pref.translated && !g.is_builtin_mod {
g.gen_plain_infix_expr(node)
return
@ -310,17 +329,29 @@ fn (mut g Gen) infix_expr_cmp_op(node ast.InfixExpr) {
if node.op in [.lt, .ge] {
g.write('(')
g.write('*'.repeat(left.typ.nr_muls()))
if operator_expects_ptr {
g.write('&')
}
g.expr(node.left)
g.write(', ')
g.write('*'.repeat(right.typ.nr_muls()))
if operator_expects_ptr {
g.write('&')
}
g.expr(node.right)
g.write(')')
} else {
g.write('(')
g.write('*'.repeat(right.typ.nr_muls()))
if operator_expects_ptr {
g.write('&')
}
g.expr(node.right)
g.write(', ')
g.write('*'.repeat(left.typ.nr_muls()))
if operator_expects_ptr {
g.write('&')
}
g.expr(node.left)
g.write(')')
}
@ -333,17 +364,29 @@ fn (mut g Gen) infix_expr_cmp_op(node ast.InfixExpr) {
if node.op in [.lt, .ge] {
g.write('(')
g.write('*'.repeat(left.typ.nr_muls()))
if operator_expects_ptr {
g.write('&')
}
g.expr(node.left)
g.write(', ')
g.write('*'.repeat(right.typ.nr_muls()))
if operator_expects_ptr {
g.write('&')
}
g.expr(node.right)
g.write(')')
} else {
g.write('(')
g.write('*'.repeat(right.typ.nr_muls()))
if operator_expects_ptr {
g.write('&')
}
g.expr(node.right)
g.write(', ')
g.write('*'.repeat(left.typ.nr_muls()))
if operator_expects_ptr {
g.write('&')
}
g.expr(node.left)
g.write(')')
}

View File

@ -0,0 +1,47 @@
struct Resources {
mut:
hru u64
sru u64
cru u64
mru u64
}
fn (a &Resources) < (b &Resources) bool {
return a.hru < b.hru && a.sru < b.sru && a.cru < b.cru && a.mru < b.mru
}
fn (a &Resources) == (b &Resources) bool {
return a.hru == b.hru
}
fn test_struct_with_reference_operands_for_the_overloaded_operators_do_work() {
aa := Resources{}
bb := Resources{}
assert dump(aa < bb) == false
assert dump(aa == bb) == true
assert dump(aa != bb) == false
assert dump(aa > bb) == false
assert dump(aa <= bb) == true
assert dump(aa >= bb) == true
}
// Issue: https://github.com/vlang/v/issues/15859
struct Foo {
id u32
x u32
y u32
}
fn (f &Foo) == (o &Foo) bool {
return f.id == o.id
}
fn test_eq_operator_with_reference_operands() {
a := Foo{1, 4, 5}
b := Foo{1, 9, 10}
if a == b {
assert true
return
}
assert false
}