checker, cgen: fix generic operator overload of 'cmp' (#11489)

pull/11491/head
yuyi 2021-09-14 21:20:02 +08:00 committed by GitHub
parent 8862c3af0f
commit 9554470985
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 100 additions and 16 deletions

View File

@ -1528,6 +1528,9 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
.gt, .lt, .ge, .le {
if left_sym.kind in [.array, .array_fixed] && right_sym.kind in [.array, .array_fixed] {
c.error('only `==` and `!=` are defined on arrays', node.pos)
} else if left_sym.kind == .struct_
&& (left_sym.info as ast.Struct).generic_types.len > 0 {
return ast.bool_type
} else if left_sym.kind == .struct_ && right_sym.kind == .struct_
&& node.op in [.eq, .lt] {
if !(left_sym.has_method(node.op.str()) && right_sym.has_method(node.op.str())) {
@ -4286,6 +4289,9 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
.mult_assign { '*' }
else { 'unknown op' }
}
if left_sym.kind == .struct_ && (left_sym.info as ast.Struct).generic_types.len > 0 {
continue
}
if method := left_sym.find_method(extracted_op) {
if method.return_type != left_type {
c.error('operator `$extracted_op` must return `$left_name` to be used as an assignment operator',

View File

@ -2812,16 +2812,28 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
else { 'unknown op' }
}
g.expr(left)
g.write(' = ${styp}_${util.replace_op(extracted_op)}(')
method := g.table.type_find_method(left_sym, extracted_op) or {
// the checker will most likely have found this, already...
g.error('assignemnt operator `$extracted_op=` used but no `$extracted_op` method defined',
assign_stmt.pos)
ast.Fn{}
if left_sym.kind == .struct_ && (left_sym.info as ast.Struct).generic_types.len > 0 {
concrete_types := (left_sym.info as ast.Struct).concrete_types
mut method_name := left_sym.cname + '_' + util.replace_op(extracted_op)
method_name = g.generic_fn_name(concrete_types, method_name, true)
g.write(' = ${method_name}(')
g.expr(left)
g.write(', ')
g.expr(val)
g.writeln(');')
return
} else {
g.write(' = ${styp}_${util.replace_op(extracted_op)}(')
method := g.table.type_find_method(left_sym, extracted_op) or {
// the checker will most likely have found this, already...
g.error('assignemnt operator `$extracted_op=` used but no `$extracted_op` method defined',
assign_stmt.pos)
ast.Fn{}
}
op_expected_left = method.params[0].typ
op_expected_right = method.params[1].typ
op_overloaded = true
}
op_expected_left = method.params[0].typ
op_expected_right = method.params[1].typ
op_overloaded = true
}
if right_sym.kind == .function && is_decl {
if is_inside_ternary && is_decl {

View File

@ -266,7 +266,32 @@ 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.type_has_method(left.sym, '<')
if left.sym.kind == right.sym.kind && has_operator_overloading {
if left.sym.kind == .struct_ && (left.sym.info as ast.Struct).generic_types.len > 0 {
if node.op in [.le, .ge] {
g.write('!')
}
concrete_types := (left.sym.info as ast.Struct).concrete_types
mut method_name := left.sym.cname + '__lt'
method_name = g.generic_fn_name(concrete_types, method_name, true)
g.write(method_name)
if node.op in [.lt, .ge] {
g.write('(')
g.write('*'.repeat(left.typ.nr_muls()))
g.expr(node.left)
g.write(', ')
g.write('*'.repeat(right.typ.nr_muls()))
g.expr(node.right)
g.write(')')
} else {
g.write('(')
g.write('*'.repeat(right.typ.nr_muls()))
g.expr(node.right)
g.write(', ')
g.write('*'.repeat(left.typ.nr_muls()))
g.expr(node.left)
g.write(')')
}
} else if left.sym.kind == right.sym.kind && has_operator_overloading {
if node.op in [.le, .ge] {
g.write('!')
}

View File

@ -26,10 +26,51 @@ fn (m1 Matrix<T>) + (m2 Matrix<T>) Matrix<T> {
return res
}
fn test_generic_operator_overload() {
result := from_array([[1, 2, 3], [4, 5, 6]]) + from_array([[7, 8, 9], [10, 11, 12]])
println(result)
assert result.row == 2
assert result.col == 3
assert result.data == [[8, 10, 12], [14, 16, 18]]
fn (m1 Matrix<T>) == (m2 Matrix<T>) bool {
return m1.row == m2.row && m1.col == m2.col && m1.data == m2.data
}
fn (m1 Matrix<T>) < (m2 Matrix<T>) bool {
return m1.row < m2.row && m1.col < m2.col
}
fn test_generic_operator_overload() {
mut a1 := from_array([[1, 2, 3], [4, 5, 6]])
a2 := from_array([[7, 8, 9], [10, 11, 12]])
plus_ret := a1 + a2
println(plus_ret)
assert plus_ret.row == 2
assert plus_ret.col == 3
assert plus_ret.data == [[8, 10, 12], [14, 16, 18]]
a1 += a2
println(a1)
assert a1.row == 2
assert a1.col == 3
assert a1.data == [[15, 18, 21], [24, 27, 30]]
eq_ret := a1 == a2
println(eq_ret)
assert !eq_ret
ne_ret := a1 != a2
println(ne_ret)
assert ne_ret
lt_ret := a1 < a2
println(lt_ret)
assert !lt_ret
le_ret := a1 <= a2
println(le_ret)
assert le_ret
gt_ret := a1 > a2
println(gt_ret)
assert !gt_ret
ge_ret := a1 >= a2
println(ge_ret)
assert ge_ret
}