cgen: implement `>=` and `<=` (#7991)
parent
73bd63d69d
commit
1f5255c2bb
|
@ -2,7 +2,7 @@
|
||||||
*Not yet released*
|
*Not yet released*
|
||||||
- `vweb` now uses struct embedding: `app.vweb.text('hello') => app.text('hello')`.
|
- `vweb` now uses struct embedding: `app.vweb.text('hello') => app.text('hello')`.
|
||||||
- Consts can now be declared outside of `const()` blocks: `const x = 0`.
|
- Consts can now be declared outside of `const()` blocks: `const x = 0`.
|
||||||
- Overloading of `>`, `<`, `!=`, and `==` operators.
|
- Overloading of `>`, `<`, `!=`, `==`, `<=` and `>=` operators.
|
||||||
- New struct updating syntax: `User{ ...u, name: 'new' }` to replace `{ u | name: 'new' }`.
|
- New struct updating syntax: `User{ ...u, name: 'new' }` to replace `{ u | name: 'new' }`.
|
||||||
- `byte.str()` has been fixed and works like with all other numbers. `byte.ascii_str()` has been added.
|
- `byte.str()` has been fixed and works like with all other numbers. `byte.ascii_str()` has been added.
|
||||||
- Smart cast in for loops: `for mut x is string {}`.
|
- Smart cast in for loops: `for mut x is string {}`.
|
||||||
|
|
|
@ -3217,11 +3217,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 but can be overriden.
|
- `==` and `!=` are self generated by the compiler but can be overriden.
|
||||||
- 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 `!=` operators, the return type must be `bool`.
|
- When using `<`, `>`, `>=`, `<=`, `==` and `!=` operators, 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 {
|
||||||
|
|
|
@ -840,6 +840,17 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type {
|
||||||
.gt, .lt, .ge, .le {
|
.gt, .lt, .ge, .le {
|
||||||
if left.kind in [.array, .array_fixed] && right.kind in [.array, .array_fixed] {
|
if left.kind in [.array, .array_fixed] && right.kind in [.array, .array_fixed] {
|
||||||
c.error('only `==` and `!=` are defined on arrays', infix_expr.pos)
|
c.error('only `==` and `!=` are defined on arrays', infix_expr.pos)
|
||||||
|
} else if left.kind == .struct_ && right.kind == .struct_ {
|
||||||
|
if !(left.has_method(infix_expr.op.str()) && right.has_method(infix_expr.op.str())) {
|
||||||
|
left_name := c.table.type_to_str(left_type)
|
||||||
|
right_name := c.table.type_to_str(right_type)
|
||||||
|
if left_name == right_name {
|
||||||
|
c.error('operation `$left_name` $infix_expr.op.str() `$right_name` does not exist, please define it',
|
||||||
|
infix_expr.pos)
|
||||||
|
} else {
|
||||||
|
c.error('mismatched types `$left_name` and `$right_name`', infix_expr.pos)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.left_shift {
|
.left_shift {
|
||||||
|
@ -5103,7 +5114,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 {
|
} else if node.name in ['<', '>', '==', '!=', '>=', '<='] &&
|
||||||
|
node.return_type != table.bool_type {
|
||||||
c.error('operator comparison methods should return `bool`', node.pos)
|
c.error('operator comparison methods should return `bool`', node.pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,4 +24,18 @@ vlib/v/checker/tests/method_op_err.vv:24:24: error: infix expr: cannot use `Foo`
|
||||||
23 | println(User{3, 4})
|
23 | println(User{3, 4})
|
||||||
24 | println(User{3, 4} - Foo{3, 3})
|
24 | println(User{3, 4} - Foo{3, 3})
|
||||||
| ^
|
| ^
|
||||||
25 | }
|
25 | println(User{3, 2} < User{2, 4})
|
||||||
|
26 | println(User{3, 4} < Foo{3, 4})
|
||||||
|
vlib/v/checker/tests/method_op_err.vv:25:24: error: operation `User` < `User` does not exist, please define it
|
||||||
|
23 | println(User{3, 4})
|
||||||
|
24 | println(User{3, 4} - Foo{3, 3})
|
||||||
|
25 | println(User{3, 2} < User{2, 4})
|
||||||
|
| ^
|
||||||
|
26 | println(User{3, 4} < Foo{3, 4})
|
||||||
|
27 | }
|
||||||
|
vlib/v/checker/tests/method_op_err.vv:26:24: error: mismatched types `User` and `Foo`
|
||||||
|
24 | println(User{3, 4} - Foo{3, 3})
|
||||||
|
25 | println(User{3, 2} < User{2, 4})
|
||||||
|
26 | println(User{3, 4} < Foo{3, 4})
|
||||||
|
| ^
|
||||||
|
27 | }
|
||||||
|
|
|
@ -22,4 +22,6 @@ fn (u User) > (u1 User) 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})
|
||||||
|
println(User{3, 2} < User{2, 4})
|
||||||
|
println(User{3, 4} < Foo{3, 4})
|
||||||
}
|
}
|
||||||
|
|
|
@ -3230,7 +3230,7 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
|
||||||
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()
|
||||||
// Do not generate operator overloading with these `right_sym.kind`.
|
// Do not generate operator overloading with these `right_sym.kind`.
|
||||||
e := right_sym.kind !in [.voidptr, .any_int, .int]
|
e := right_sym.kind !in [.voidptr, .any_int, .int]
|
||||||
if node.op in [.plus, .minus, .mul, .div, .mod, .lt, .gt, .eq, .ne] &&
|
if node.op in [.plus, .minus, .mul, .div, .mod, .lt, .gt, .eq, .ne, .le, .ge] &&
|
||||||
((a && b && e) || c || d) {
|
((a && b && e) || c || d) {
|
||||||
// Overloaded operators
|
// Overloaded operators
|
||||||
g.write(g.typ(if !d {
|
g.write(g.typ(if !d {
|
||||||
|
|
|
@ -56,7 +56,7 @@ fn (mut g Gen) gen_fn_decl(it ast.FnDecl, skip bool) {
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
mut name := it.name
|
mut name := it.name
|
||||||
if name in ['+', '-', '*', '/', '%', '<', '>', '==', '!='] {
|
if name in ['+', '-', '*', '/', '%', '<', '>', '==', '!=', '<=', '>='] {
|
||||||
name = util.replace_op(name)
|
name = util.replace_op(name)
|
||||||
}
|
}
|
||||||
if it.is_method {
|
if it.is_method {
|
||||||
|
|
|
@ -277,7 +277,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if p.tok.kind in [.plus, .minus, .mul, .div, .mod, .gt, .lt, .eq, .ne] &&
|
if p.tok.kind in [.plus, .minus, .mul, .div, .mod, .gt, .lt, .eq, .ne, .le, .ge] &&
|
||||||
p.peek_tok.kind == .lpar {
|
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 {
|
||||||
|
|
|
@ -43,6 +43,14 @@ fn (a Vec) != (b Vec) bool {
|
||||||
return !(a == b)
|
return !(a == b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (a Vec) >= (b Vec) bool {
|
||||||
|
return a > b || a == b
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (a Vec) <= (b Vec) bool {
|
||||||
|
return a < b || a == b
|
||||||
|
}
|
||||||
|
|
||||||
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}
|
||||||
|
@ -68,6 +76,8 @@ fn test_operator_overloading_with_string_interpolation() {
|
||||||
////// /////
|
////// /////
|
||||||
assert b > a == true
|
assert b > a == true
|
||||||
assert a < b == true
|
assert a < b == true
|
||||||
|
assert b >= a == true
|
||||||
|
assert a <= b == true
|
||||||
assert (Vec{2, 3} == Vec{3, 2}) == true
|
assert (Vec{2, 3} == Vec{3, 2}) == true
|
||||||
assert (Vec{2, 3} != Vec{3, 2}) == false
|
assert (Vec{2, 3} != Vec{3, 2}) == false
|
||||||
////// /////
|
////// /////
|
||||||
|
|
|
@ -316,6 +316,8 @@ pub fn replace_op(s string) string {
|
||||||
suffix := match s {
|
suffix := match s {
|
||||||
'==' { '_eq' }
|
'==' { '_eq' }
|
||||||
'!=' { '_ne' }
|
'!=' { '_ne' }
|
||||||
|
'<=' { '_le' }
|
||||||
|
'>=' { '_ge' }
|
||||||
else { '' }
|
else { '' }
|
||||||
}
|
}
|
||||||
return s[..s.len - 2] + suffix
|
return s[..s.len - 2] + suffix
|
||||||
|
|
Loading…
Reference in New Issue