cgen: implement > and < for structs (#7774)
parent
d15d13674c
commit
24b18f05c4
|
@ -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
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 | }
|
||||||
|
|
|
@ -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})
|
||||||
|
|
|
@ -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' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -27,30 +27,40 @@ 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}'
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue