1
0
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:
Swastik Baranwal 2021-01-03 20:49:02 +05:30 committed by GitHub
parent b7f83e2f50
commit 9033099676
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 49 additions and 22 deletions

View File

@ -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.

View File

@ -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

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') f.write('fn $receiver$name')
if name in ['+', '-', '*', '/', '%', '<', '>'] { if name in ['+', '-', '*', '/', '%', '<', '>', '==', '!='] {
f.write(' ') f.write(' ')
} }
if node.is_generic { if node.is_generic {

View File

@ -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)
} }
} }

View File

@ -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

View File

@ -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 {

View File

@ -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',

View File

@ -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}'

View File

@ -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 {