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

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

This commit is contained in:
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:
- 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.
- 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).
## 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')
if name in ['+', '-', '*', '/', '%'] {
if name in ['+', '-', '*', '/', '%', '<', '>'] {
f.write(' ')
}
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)
}
}
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 {
c.error('operator methods should have exactly 1 argument', node.pos)
} else {
@ -4961,6 +4961,8 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
} else {
if node.receiver.typ != node.params[1].typ {
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}
16 | }
vlib/v/checker/tests/method_op_err.vv:20:24: error: infix expr: cannot use `Foo` (right expression) as `User`
18 | fn main() {
19 | println(User{3, 4})
20 | println(User{3, 4} - Foo{3, 3})
vlib/v/checker/tests/method_op_err.vv:18:1: error: operator comparison methods should return `bool`
16 | }
17 |
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}
}
fn (u User) > (u1 User) User {
return User{}
}
fn main() {
println(User{3, 4})
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.write(')')
} 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
c := left_sym.kind == .alias && (left_sym.info as table.Alias).language == .c
// Check if aliased type is a struct
d := !b &&
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
g.write(g.typ(if !d {
left_type
@ -5006,6 +5007,8 @@ fn op_to_fn_name(name string) string {
'*' { '_op_mul' }
'/' { '_op_div' }
'%' { '_op_mod' }
'<' { '_op_lt' }
'>' { '_op_gt' }
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
if name[0] in [`+`, `-`, `*`, `/`, `%`] {
if name[0] in [`+`, `-`, `*`, `/`, `%`, `<`, `>`] {
name = util.replace_op(name)
}
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()
if rec_type == table.void_type {
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}'
}
fn (a Vec) +(b Vec) Vec {
fn (a Vec) + (b Vec) Vec {
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}
}
fn (a Vec) *(b Vec) Vec {
fn (a Vec) * (b Vec) Vec {
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}
}
fn (a Vec) %(b Vec) Vec {
fn (a Vec) % (b Vec) Vec {
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() {
a := Vec{2, 3}
b := Vec{4, 5}
c := a + b
assert a.x + b.x == c.x
assert a.y + b.y == c.y
////// /////
d := a - b
assert a.x - b.x == d.x
assert a.y - b.y == d.y
////// /////
e := a * b
assert a.x * b.x == e.x
assert a.y * b.y == e.y
////// /////
f := a / b
assert a.x / b.x == f.x
assert a.y / b.y == f.y
////// /////
g := a % b
assert a.x % b.x == g.x
assert a.y % b.y == g.y
////// /////
assert b > a == true
assert a < b == true
////// /////
assert c.str() == '{6, 8}'
assert d.str() == '{-2, -2}'
assert e.str() == '{8, 15}'

View File

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