cgen: implement overriding of `!=` and `==` (#7837)
parent
b7f83e2f50
commit
9033099676
|
@ -2,6 +2,7 @@
|
|||
*Not yet released*
|
||||
- `vweb` now uses struct embedding: `app.vweb.text('hello') => app.text('hello')`.
|
||||
- Consts can now be declared outside of `const()` blocks: `const x = 0`.
|
||||
- Allow overloading of `>`, `<`, `!=` and `==` operators.
|
||||
|
||||
## V 0.2.1
|
||||
- Hashmap bootstrapping fixes.
|
||||
|
|
|
@ -3166,11 +3166,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.
|
||||
- `==` and `!=` are self generated by the compiler.
|
||||
- It's only possible to overload `+, -, *, /, %, <, >, ==, !=` operators.
|
||||
- `==` and `!=` are self generated by the compiler but can be overriden.
|
||||
- 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`.
|
||||
- When using `<`, `>`, `==` and `!=` operators, the return type must be `bool`.
|
||||
- Both arguments must have the same type (just like with all operators in V).
|
||||
|
||||
## 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')
|
||||
if name in ['+', '-', '*', '/', '%', '<', '>'] {
|
||||
if name in ['+', '-', '*', '/', '%', '<', '>', '==', '!='] {
|
||||
f.write(' ')
|
||||
}
|
||||
if node.is_generic {
|
||||
|
|
|
@ -4980,7 +4980,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 {
|
||||
|
@ -4992,7 +4992,7 @@ 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 {
|
||||
} else if node.name in ['<', '>', '==', '!='] && node.return_type != table.bool_type {
|
||||
c.error('operator comparison methods should return `bool`', node.pos)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2868,6 +2868,8 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
|
|||
return
|
||||
}
|
||||
right_sym := g.table.get_type_symbol(node.right_type)
|
||||
has_eq_overloaded := !left_sym.has_method('==')
|
||||
has_ne_overloaded := !left_sym.has_method('!=')
|
||||
unaliased_right := if right_sym.kind == .alias {
|
||||
(right_sym.info as table.Alias).parent_type
|
||||
} else {
|
||||
|
@ -3006,7 +3008,8 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
|
|||
}
|
||||
g.expr(node.right)
|
||||
g.write(')')
|
||||
} else if node.op in [.eq, .ne] && left_sym.kind == .struct_ && right_sym.kind == .struct_ {
|
||||
} else if node.op in [.eq, .ne] &&
|
||||
left_sym.kind == .struct_ && right_sym.kind == .struct_ && has_eq_overloaded && has_ne_overloaded {
|
||||
ptr_typ := g.gen_struct_equality_fn(left_type)
|
||||
if node.op == .eq {
|
||||
g.write('${ptr_typ}_struct_eq(')
|
||||
|
@ -3154,13 +3157,16 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
|
|||
g.write(')')
|
||||
} else {
|
||||
a := (left_sym.name[0].is_capital() || left_sym.name.contains('.')) &&
|
||||
left_sym.kind != .enum_
|
||||
left_sym.kind !in [.enum_, .function, .interface_, .sum_type]
|
||||
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, .lt, .gt] && ((a && b) || c || d) {
|
||||
// Do not generate operator overloading with these `right_sym.kind`.
|
||||
e := right_sym.kind !in [.voidptr, .any_int, .int]
|
||||
if node.op in [.plus, .minus, .mul, .div, .mod, .lt, .gt, .eq, .ne] &&
|
||||
((a && b && e) || c || d) {
|
||||
// Overloaded operators
|
||||
g.write(g.typ(if !d {
|
||||
left_type
|
||||
|
|
|
@ -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 in ['+', '-', '*', '/', '%', '<', '>', '==', '!='] {
|
||||
name = util.replace_op(name)
|
||||
}
|
||||
if it.is_method {
|
||||
|
|
|
@ -270,7 +270,8 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
|||
}
|
||||
}
|
||||
}
|
||||
if p.tok.kind in [.plus, .minus, .mul, .div, .mod, .gt, .lt] && p.peek_tok.kind == .lpar {
|
||||
if p.tok.kind in [.plus, .minus, .mul, .div, .mod, .gt, .lt, .eq, .ne] &&
|
||||
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',
|
||||
|
|
|
@ -35,6 +35,14 @@ 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.y && a.y == b.x
|
||||
}
|
||||
|
||||
fn (a Vec) != (b Vec) bool {
|
||||
return !(a == b)
|
||||
}
|
||||
|
||||
fn test_operator_overloading_with_string_interpolation() {
|
||||
a := Vec{2, 3}
|
||||
b := Vec{4, 5}
|
||||
|
@ -60,6 +68,8 @@ fn test_operator_overloading_with_string_interpolation() {
|
|||
////// /////
|
||||
assert b > a == true
|
||||
assert a < b == true
|
||||
assert (Vec{2, 3} == Vec{3, 2}) == true
|
||||
assert (Vec{2, 3} != Vec{3, 2}) == false
|
||||
////// /////
|
||||
assert c.str() == '{6, 8}'
|
||||
assert d.str() == '{-2, -2}'
|
||||
|
|
|
@ -278,18 +278,27 @@ pub fn imax(a int, b int) int {
|
|||
}
|
||||
|
||||
pub fn replace_op(s string) string {
|
||||
last_char := s[s.len - 1]
|
||||
suffix := match last_char {
|
||||
`+` { '_plus' }
|
||||
`-` { '_minus' }
|
||||
`*` { '_mult' }
|
||||
`/` { '_div' }
|
||||
`%` { '_mod' }
|
||||
`<` { '_lt' }
|
||||
`>` { '_gt' }
|
||||
else { '' }
|
||||
if s.len == 1 {
|
||||
last_char := s[s.len - 1]
|
||||
suffix := match last_char {
|
||||
`+` { '_plus' }
|
||||
`-` { '_minus' }
|
||||
`*` { '_mult' }
|
||||
`/` { '_div' }
|
||||
`%` { '_mod' }
|
||||
`<` { '_lt' }
|
||||
`>` { '_gt' }
|
||||
else { '' }
|
||||
}
|
||||
return s[..s.len - 1] + suffix
|
||||
} else {
|
||||
suffix := match s {
|
||||
'==' { '_eq' }
|
||||
'!=' { '_ne' }
|
||||
else { '' }
|
||||
}
|
||||
return s[..s.len - 2] + suffix
|
||||
}
|
||||
return s[..s.len - 1] + suffix
|
||||
}
|
||||
|
||||
pub fn join_env_vflags_and_os_args() []string {
|
||||
|
|
Loading…
Reference in New Issue