cgen: implement `>=` and `<=` (#7991)
							parent
							
								
									73bd63d69d
								
							
						
					
					
						commit
						1f5255c2bb
					
				|  | @ -2,7 +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`. | ||||
| - Overloading of  `>`, `<`, `!=`, and `==` operators. | ||||
| - Overloading of  `>`, `<`, `!=`, `==`, `<=` and `>=` operators. | ||||
| - 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. | ||||
| - 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: | ||||
| 
 | ||||
| - 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. | ||||
| - Calling other functions inside operator functions is not allowed. | ||||
| - 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). | ||||
| 
 | ||||
| ## 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 { | ||||
|  |  | |||
|  | @ -840,6 +840,17 @@ pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type { | |||
| 		.gt, .lt, .ge, .le { | ||||
| 			if left.kind in [.array, .array_fixed] && right.kind in [.array, .array_fixed] { | ||||
| 				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 { | ||||
|  | @ -5103,7 +5114,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 { | ||||
| 				} else if node.name in ['<', '>', '==', '!=', '>=', '<='] && | ||||
| 					node.return_type != table.bool_type { | ||||
| 					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}) | ||||
|    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() { | ||||
|     println(User{3, 4}) | ||||
|     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() | ||||
| 		// 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] && | ||||
| 		if node.op in [.plus, .minus, .mul, .div, .mod, .lt, .gt, .eq, .ne, .le, .ge] && | ||||
| 			((a && b && e) || c || d) { | ||||
| 			// Overloaded operators
 | ||||
| 			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 | ||||
| 	if name in ['+', '-', '*', '/', '%', '<', '>', '==', '!='] { | ||||
| 	if name in ['+', '-', '*', '/', '%', '<', '>', '==', '!=', '<=', '>='] { | ||||
| 		name = util.replace_op(name) | ||||
| 	} | ||||
| 	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 { | ||||
| 		name = p.tok.kind.str() // op_to_fn_name()
 | ||||
| 		if rec_type == table.void_type { | ||||
|  |  | |||
|  | @ -43,6 +43,14 @@ fn (a Vec) != (b Vec) bool { | |||
| 	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() { | ||||
| 	a := Vec{2, 3} | ||||
| 	b := Vec{4, 5} | ||||
|  | @ -68,6 +76,8 @@ fn test_operator_overloading_with_string_interpolation() { | |||
| 	////// /////
 | ||||
| 	assert b > a == 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}) == false | ||||
| 	////// /////
 | ||||
|  |  | |||
|  | @ -316,6 +316,8 @@ pub fn replace_op(s string) string { | |||
| 		suffix := match s { | ||||
| 			'==' { '_eq' } | ||||
| 			'!=' { '_ne' } | ||||
| 			'<=' { '_le' } | ||||
| 			'>=' { '_ge' } | ||||
| 			else { '' } | ||||
| 		} | ||||
| 		return s[..s.len - 2] + suffix | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue