mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
cgen: implement overriding of !=
and ==
(#7837)
This commit is contained in:
parent
b7f83e2f50
commit
9033099676
@ -2,6 +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`.
|
||||||
|
- Allow overloading of `>`, `<`, `!=` and `==` operators.
|
||||||
|
|
||||||
## V 0.2.1
|
## V 0.2.1
|
||||||
- Hashmap bootstrapping fixes.
|
- 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:
|
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.
|
- `==` 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 `>`, 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 {
|
||||||
|
@ -4980,7 +4980,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 {
|
||||||
@ -4992,7 +4992,7 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2868,6 +2868,8 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
right_sym := g.table.get_type_symbol(node.right_type)
|
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 {
|
unaliased_right := if right_sym.kind == .alias {
|
||||||
(right_sym.info as table.Alias).parent_type
|
(right_sym.info as table.Alias).parent_type
|
||||||
} else {
|
} else {
|
||||||
@ -3006,7 +3008,8 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
|
|||||||
}
|
}
|
||||||
g.expr(node.right)
|
g.expr(node.right)
|
||||||
g.write(')')
|
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)
|
ptr_typ := g.gen_struct_equality_fn(left_type)
|
||||||
if node.op == .eq {
|
if node.op == .eq {
|
||||||
g.write('${ptr_typ}_struct_eq(')
|
g.write('${ptr_typ}_struct_eq(')
|
||||||
@ -3154,13 +3157,16 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) {
|
|||||||
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_
|
left_sym.kind !in [.enum_, .function, .interface_, .sum_type]
|
||||||
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, .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
|
// Overloaded operators
|
||||||
g.write(g.typ(if !d {
|
g.write(g.typ(if !d {
|
||||||
left_type
|
left_type
|
||||||
|
@ -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 in ['+', '-', '*', '/', '%', '<', '>', '==', '!='] {
|
||||||
name = util.replace_op(name)
|
name = util.replace_op(name)
|
||||||
}
|
}
|
||||||
if it.is_method {
|
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()
|
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',
|
||||||
|
@ -35,6 +35,14 @@ fn (a Vec) < (b Vec) bool {
|
|||||||
return a.x < b.x && a.y < b.y
|
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() {
|
fn test_operator_overloading_with_string_interpolation() {
|
||||||
a := Vec{2, 3}
|
a := Vec{2, 3}
|
||||||
b := Vec{4, 5}
|
b := Vec{4, 5}
|
||||||
@ -60,6 +68,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 (Vec{2, 3} == Vec{3, 2}) == true
|
||||||
|
assert (Vec{2, 3} != Vec{3, 2}) == false
|
||||||
////// /////
|
////// /////
|
||||||
assert c.str() == '{6, 8}'
|
assert c.str() == '{6, 8}'
|
||||||
assert d.str() == '{-2, -2}'
|
assert d.str() == '{-2, -2}'
|
||||||
|
@ -278,18 +278,27 @@ pub fn imax(a int, b int) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn replace_op(s string) string {
|
pub fn replace_op(s string) string {
|
||||||
last_char := s[s.len - 1]
|
if s.len == 1 {
|
||||||
suffix := match last_char {
|
last_char := s[s.len - 1]
|
||||||
`+` { '_plus' }
|
suffix := match last_char {
|
||||||
`-` { '_minus' }
|
`+` { '_plus' }
|
||||||
`*` { '_mult' }
|
`-` { '_minus' }
|
||||||
`/` { '_div' }
|
`*` { '_mult' }
|
||||||
`%` { '_mod' }
|
`/` { '_div' }
|
||||||
`<` { '_lt' }
|
`%` { '_mod' }
|
||||||
`>` { '_gt' }
|
`<` { '_lt' }
|
||||||
else { '' }
|
`>` { '_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 {
|
pub fn join_env_vflags_and_os_args() []string {
|
||||||
|
Loading…
Reference in New Issue
Block a user