cgen: implement > and < for structs (#7774)

pull/7783/head
Swastik Baranwal 2021-01-01 19:24:32 +05:30 committed by GitHub
parent d15d13674c
commit 24b18f05c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 53 additions and 23 deletions

View File

@ -3148,9 +3148,11 @@ operator overloading is an important feature to have in order to improve readabi
To improve safety and maintainability, operator overloading is limited: To improve safety and maintainability, operator overloading is limited:
- It's only possible to overload `+, -, *, /, %` operators. - It's only possible to overload `+, -, *, /, %, <, >` operators.
- `==` and `!=` are self generated by the compiler.
- Calling other functions inside operator functions is not allowed. - Calling other functions inside operator functions is not allowed.
- Operator functions can't modify their arguments. - Operator functions can't modify their arguments.
- When using `<` and `>`, the return type must be `bool`.
- Both arguments must have the same type (just like with all operators in V). - Both arguments must have the same type (just like with all operators in V).
## Inline assembly ## Inline assembly

View File

@ -52,7 +52,7 @@ pub fn (node &FnDecl) stringify(t &table.Table, cur_mod string, m2a map[string]s
} }
} }
f.write('fn $receiver$name') f.write('fn $receiver$name')
if name in ['+', '-', '*', '/', '%'] { if name in ['+', '-', '*', '/', '%', '<', '>'] {
f.write(' ') f.write(' ')
} }
if node.is_generic { if node.is_generic {

View File

@ -4949,7 +4949,7 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
c.error('.str() methods should have 0 arguments', node.pos) c.error('.str() methods should have 0 arguments', node.pos)
} }
} }
if node.language == .v && node.is_method && node.name in ['+', '-', '*', '%', '/'] { if node.language == .v && node.is_method && node.name in ['+', '-', '*', '%', '/', '<', '>'] {
if node.params.len != 2 { if node.params.len != 2 {
c.error('operator methods should have exactly 1 argument', node.pos) c.error('operator methods should have exactly 1 argument', node.pos)
} else { } else {
@ -4961,6 +4961,8 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
} else { } else {
if node.receiver.typ != node.params[1].typ { if node.receiver.typ != node.params[1].typ {
c.error('both sides of an operator must be the same type', node.pos) c.error('both sides of an operator must be the same type', node.pos)
} else if node.name in ['<', '>'] && node.return_type != table.bool_type {
c.error('operator comparison methods should return `bool`', node.pos)
} }
} }
} }

View File

@ -12,9 +12,16 @@ vlib/v/checker/tests/method_op_err.vv:14:1: error: both sides of an operator mus
| ~~~~~~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~~~~~
15 | return User{u.a - f.a, u.b-f.a} 15 | return User{u.a - f.a, u.b-f.a}
16 | } 16 | }
vlib/v/checker/tests/method_op_err.vv:20:24: error: infix expr: cannot use `Foo` (right expression) as `User` vlib/v/checker/tests/method_op_err.vv:18:1: error: operator comparison methods should return `bool`
18 | fn main() { 16 | }
19 | println(User{3, 4}) 17 |
20 | println(User{3, 4} - Foo{3, 3}) 18 | fn (u User) > (u1 User) User {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
19 | return User{}
20 | }
vlib/v/checker/tests/method_op_err.vv:24:24: error: infix expr: cannot use `Foo` (right expression) as `User`
22 | fn main() {
23 | println(User{3, 4})
24 | println(User{3, 4} - Foo{3, 3})
| ^ | ^
21 | } 25 | }

View File

@ -15,6 +15,10 @@ fn (u User) - (f Foo) User {
return User{u.a - f.a, u.b-f.a} return User{u.a - f.a, u.b-f.a}
} }
fn (u User) > (u1 User) User {
return User{}
}
fn main() { fn main() {
println(User{3, 4}) println(User{3, 4})
println(User{3, 4} - Foo{3, 3}) println(User{3, 4} - Foo{3, 3})

View File

@ -3153,13 +3153,14 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
g.expr(node.left) g.expr(node.left)
g.write(')') g.write(')')
} else { } else {
a := left_sym.name[0].is_capital() || left_sym.name.contains('.') a := (left_sym.name[0].is_capital() || left_sym.name.contains('.')) &&
left_sym.kind != .enum_
b := left_sym.kind != .alias b := left_sym.kind != .alias
c := left_sym.kind == .alias && (left_sym.info as table.Alias).language == .c c := left_sym.kind == .alias && (left_sym.info as table.Alias).language == .c
// Check if aliased type is a struct // Check if aliased type is a struct
d := !b && d := !b &&
g.typ((left_sym.info as table.Alias).parent_type).split('__').last()[0].is_capital() g.typ((left_sym.info as table.Alias).parent_type).split('__').last()[0].is_capital()
if node.op in [.plus, .minus, .mul, .div, .mod] && ((a && b) || c || d) { if node.op in [.plus, .minus, .mul, .div, .mod, .lt, .gt] && ((a && b) || c || d) {
// Overloaded operators // Overloaded operators
g.write(g.typ(if !d { g.write(g.typ(if !d {
left_type left_type
@ -5006,6 +5007,8 @@ fn op_to_fn_name(name string) string {
'*' { '_op_mul' } '*' { '_op_mul' }
'/' { '_op_div' } '/' { '_op_div' }
'%' { '_op_mod' } '%' { '_op_mod' }
'<' { '_op_lt' }
'>' { '_op_gt' }
else { 'bad op $name' } else { 'bad op $name' }
} }
} }

View File

@ -47,7 +47,7 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl, skip bool) {
} }
// //
mut name := it.name mut name := it.name
if name[0] in [`+`, `-`, `*`, `/`, `%`] { if name[0] in [`+`, `-`, `*`, `/`, `%`, `<`, `>`] {
name = util.replace_op(name) name = util.replace_op(name)
} }
if it.is_method { if it.is_method {

View File

@ -270,7 +270,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
} }
} }
} }
if p.tok.kind in [.plus, .minus, .mul, .div, .mod] { if p.tok.kind in [.plus, .minus, .mul, .div, .mod, .gt, .lt] && p.peek_tok.kind == .lpar {
name = p.tok.kind.str() // op_to_fn_name() name = p.tok.kind.str() // op_to_fn_name()
if rec_type == table.void_type { if rec_type == table.void_type {
p.error_with_pos('cannot use operator overloading with normal functions', p.error_with_pos('cannot use operator overloading with normal functions',

View File

@ -7,50 +7,60 @@ pub fn (a Vec) str() string {
return '{$a.x, $a.y}' return '{$a.x, $a.y}'
} }
fn (a Vec) +(b Vec) Vec { fn (a Vec) + (b Vec) Vec {
return Vec{a.x + b.x, a.y + b.y} return Vec{a.x + b.x, a.y + b.y}
} }
fn (a Vec) -(b Vec) Vec { fn (a Vec) - (b Vec) Vec {
return Vec{a.x - b.x, a.y - b.y} return Vec{a.x - b.x, a.y - b.y}
} }
fn (a Vec) *(b Vec) Vec { fn (a Vec) * (b Vec) Vec {
return Vec{a.x * b.x, a.y * b.y} return Vec{a.x * b.x, a.y * b.y}
} }
fn (a Vec) /(b Vec) Vec { fn (a Vec) / (b Vec) Vec {
return Vec{a.x / b.x, a.y / b.y} return Vec{a.x / b.x, a.y / b.y}
} }
fn (a Vec) %(b Vec) Vec { fn (a Vec) % (b Vec) Vec {
return Vec{a.x % b.x, a.y % b.y} return Vec{a.x % b.x, a.y % b.y}
} }
fn (a Vec) > (b Vec) bool {
return a.x > b.x && a.y > b.y
}
fn (a Vec) < (b Vec) bool {
return a.x < b.x && a.y < b.y
}
fn test_operator_overloading_with_string_interpolation() { fn test_operator_overloading_with_string_interpolation() {
a := Vec{2, 3} a := Vec{2, 3}
b := Vec{4, 5} b := Vec{4, 5}
c := a + b c := a + b
assert a.x + b.x == c.x assert a.x + b.x == c.x
assert a.y + b.y == c.y assert a.y + b.y == c.y
////// /////
d := a - b d := a - b
assert a.x - b.x == d.x assert a.x - b.x == d.x
assert a.y - b.y == d.y assert a.y - b.y == d.y
////// /////
e := a * b e := a * b
assert a.x * b.x == e.x assert a.x * b.x == e.x
assert a.y * b.y == e.y assert a.y * b.y == e.y
////// /////
f := a / b f := a / b
assert a.x / b.x == f.x assert a.x / b.x == f.x
assert a.y / b.y == f.y assert a.y / b.y == f.y
////// /////
g := a % b g := a % b
assert a.x % b.x == g.x assert a.x % b.x == g.x
assert a.y % b.y == g.y assert a.y % b.y == g.y
////// /////
assert b > a == true
assert a < b == true
////// /////
assert c.str() == '{6, 8}' assert c.str() == '{6, 8}'
assert d.str() == '{-2, -2}' assert d.str() == '{-2, -2}'
assert e.str() == '{8, 15}' assert e.str() == '{8, 15}'

View File

@ -285,6 +285,8 @@ pub fn replace_op(s string) string {
`*` { '_mult' } `*` { '_mult' }
`/` { '_div' } `/` { '_div' }
`%` { '_mod' } `%` { '_mod' }
`<` { '_lt' }
`>` { '_gt' }
else { '' } else { '' }
} }
return s[..s.len - 1] + suffix return s[..s.len - 1] + suffix