cgen: implement > and < for structs (#7774)
							parent
							
								
									d15d13674c
								
							
						
					
					
						commit
						24b18f05c4
					
				|  | @ -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 | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  |  | |||
|  | @ -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 | } | ||||
|  |  | |||
|  | @ -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}) | ||||
|  |  | |||
|  | @ -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' } | ||||
| 	} | ||||
| } | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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', | ||||
|  |  | |||
|  | @ -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}' | ||||
|  |  | |||
|  | @ -285,6 +285,8 @@ pub fn replace_op(s string) string { | |||
| 		`*` { '_mult' } | ||||
| 		`/` { '_div' } | ||||
| 		`%` { '_mod' } | ||||
| 		`<` { '_lt' } | ||||
| 		`>` { '_gt' } | ||||
| 		else { '' } | ||||
| 	} | ||||
| 	return s[..s.len - 1] + suffix | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue