cgen: allow assignment operators for type aliases (#8086)
							parent
							
								
									aeddd5b559
								
							
						
					
					
						commit
						0ca36aafe2
					
				|  | @ -24,26 +24,30 @@ fn test_from_u64() { | |||
| } | ||||
| 
 | ||||
| fn test_plus() { | ||||
| 	a := big.from_u64(2) | ||||
| 	mut a := big.from_u64(2) | ||||
| 	b := big.from_u64(3) | ||||
| 	c := a + b | ||||
| 	assert c.hexstr() == '5' | ||||
| 	assert (big.from_u64(1024) + big.from_u64(1024)).hexstr() == '800' | ||||
| 	a += b | ||||
| 	assert a.hexstr() == '5' | ||||
| } | ||||
| 
 | ||||
| fn test_minus() { | ||||
| 	a := big.from_u64(2) | ||||
| 	b := big.from_u64(3) | ||||
| 	mut b := big.from_u64(3) | ||||
| 	c := b - a | ||||
| 	assert c.hexstr() == '1' | ||||
| 	e := big.from_u64(1024) | ||||
| 	ee := e - e | ||||
| 	assert ee.hexstr() == '0' | ||||
| 	b -= a | ||||
| 	assert b.hexstr() == '1' | ||||
| } | ||||
| 
 | ||||
| fn test_divide() { | ||||
| 	a := big.from_u64(2) | ||||
| 	b := big.from_u64(3) | ||||
| 	mut b := big.from_u64(3) | ||||
| 	c := b / a | ||||
| 	assert c.hexstr() == '1' | ||||
| 	assert (b % a).hexstr() == '1' | ||||
|  | @ -52,10 +56,12 @@ fn test_divide() { | |||
| 	assert ee.hexstr() == '1' | ||||
| 	assert (e / a).hexstr() == '200' | ||||
| 	assert (e / (a * a)).hexstr() == '100' | ||||
| 	b /= a | ||||
| 	assert b.hexstr() == '1' | ||||
| } | ||||
| 
 | ||||
| fn test_multiply() { | ||||
| 	a := big.from_u64(2) | ||||
| 	mut a := big.from_u64(2) | ||||
| 	b := big.from_u64(3) | ||||
| 	c := b * a | ||||
| 	assert c.hexstr() == '6' | ||||
|  | @ -69,6 +75,8 @@ fn test_multiply() { | |||
| 	assert e8.hexstr() == '100000000000000000000' | ||||
| 	assert e9.hexstr() == '100000000000000000001' | ||||
| 	assert d.hexstr() == '60000000000000000000c00000000000000000018' | ||||
| 	a *= b | ||||
| 	assert a.hexstr() == '6' | ||||
| } | ||||
| 
 | ||||
| fn test_mod() { | ||||
|  |  | |||
|  | @ -2496,10 +2496,10 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) { | |||
| 						c.error('invalid right operand: $left_sym.name $assign_stmt.op $right_sym.name', | ||||
| 							right.position()) | ||||
| 					} | ||||
| 				} else if !left_sym.is_number() && left_sym.kind !in [.byteptr, .charptr, .struct_] { | ||||
| 				} else if !left_sym.is_number() && left_sym.kind !in [.byteptr, .charptr, .struct_, .alias] { | ||||
| 					c.error('operator `$assign_stmt.op` not defined on left operand type `$left_sym.name`', | ||||
| 						left.position()) | ||||
| 				} else if !right_sym.is_number() && left_sym.kind !in [.byteptr, .charptr, .struct_] { | ||||
| 				} else if !right_sym.is_number() && left_sym.kind !in [.byteptr, .charptr, .struct_, .alias] { | ||||
| 					c.error('invalid right operand: $left_sym.name $assign_stmt.op $right_sym.name', | ||||
| 						right.position()) | ||||
| 				} else if right is ast.IntegerLiteral { | ||||
|  | @ -2515,11 +2515,11 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) { | |||
| 			} | ||||
| 			.mult_assign, .div_assign { | ||||
| 				if !left_sym.is_number() && | ||||
| 					!c.table.get_final_type_symbol(left_type_unwrapped).is_int() && left_sym.kind != .struct_ { | ||||
| 					!c.table.get_final_type_symbol(left_type_unwrapped).is_int() && left_sym.kind !in [.struct_, .alias] { | ||||
| 					c.error('operator $assign_stmt.op.str() not defined on left operand type `$left_sym.name`', | ||||
| 						left.position()) | ||||
| 				} else if !right_sym.is_number() && | ||||
| 					!c.table.get_final_type_symbol(left_type_unwrapped).is_int() && left_sym.kind != .struct_ { | ||||
| 					!c.table.get_final_type_symbol(left_type_unwrapped).is_int() && left_sym.kind !in [.struct_, .alias] { | ||||
| 					c.error('operator $assign_stmt.op.str() not defined on right operand type `$right_sym.name`', | ||||
| 						right.position()) | ||||
| 				} | ||||
|  | @ -2539,7 +2539,13 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) { | |||
| 		} | ||||
| 		if assign_stmt.op in | ||||
| 			[.plus_assign, .minus_assign, .mod_assign, .mult_assign, .div_assign] && | ||||
| 			left_sym.kind == .struct_ && right_sym.kind == .struct_ { | ||||
| 			((left_sym.kind == .struct_ && right_sym.kind == .struct_) || left_sym.kind == .alias) { | ||||
| 			left_name := c.table.type_to_str(left_type) | ||||
| 			right_name := c.table.type_to_str(right_type) | ||||
| 			parent_sym := c.table.get_final_type_symbol(left_type) | ||||
| 			if left_sym.kind == .alias && right_sym.kind != .alias { | ||||
| 				c.error('mismatched types `$left_name` and `$right_name`', assign_stmt.pos) | ||||
| 			} | ||||
| 			extracted_op := match assign_stmt.op { | ||||
| 				.plus_assign { '+' } | ||||
| 				.minus_assign { '-' } | ||||
|  | @ -2548,14 +2554,16 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) { | |||
| 				.mult_assign { '*' } | ||||
| 				else { 'unknown op' } | ||||
| 			} | ||||
| 			left_name := c.table.type_to_str(left_type) | ||||
| 			if method := left_sym.find_method(extracted_op) { | ||||
| 				if method.return_type != left_type { | ||||
| 					c.error('operator `$extracted_op` must return `$left_name` to be used as an assignment operator', | ||||
| 						assign_stmt.pos) | ||||
| 				} | ||||
| 			} else { | ||||
| 				right_name := c.table.type_to_str(right_type) | ||||
| 				if parent_sym.is_primitive() { | ||||
| 					c.error('cannot use operator methods on type alias for `$parent_sym.name`', | ||||
| 						assign_stmt.pos) | ||||
| 				} | ||||
| 				if left_name == right_name { | ||||
| 					c.error('operation `$left_name` $extracted_op `$right_name` does not exist, please define it', | ||||
| 						assign_stmt.pos) | ||||
|  | @ -5166,6 +5174,7 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) { | |||
| 				c.error('operator methods are only allowed for struct and type alias', | ||||
| 					node.pos) | ||||
| 			} else { | ||||
| 				parent_sym := c.table.get_final_type_symbol(node.receiver.typ) | ||||
| 				if node.rec_mut { | ||||
| 					c.error('receiver cannot be `mut` for operator overloading', node.receiver_pos) | ||||
| 				} else if node.params[1].is_mut { | ||||
|  | @ -5175,6 +5184,9 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) { | |||
| 				} else if node.name in ['<', '>', '==', '!=', '>=', '<='] && | ||||
| 					node.return_type != table.bool_type { | ||||
| 					c.error('operator comparison methods should return `bool`', node.pos) | ||||
| 				} else if parent_sym.is_primitive() { | ||||
| 					c.error('cannot define operator methods on type alias for `$parent_sym.name`', | ||||
| 						node.pos) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  |  | |||
|  | @ -0,0 +1,42 @@ | |||
| vlib/v/checker/tests/method_op_alias_err.vv:4:1: error: both sides of an operator must be the same type | ||||
|     2 | type Foo2 = string | ||||
|     3 |  | ||||
|     4 | fn (f Foo) + (f1 Foo2) Foo2 { | ||||
|       | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|     5 |     return Foo2(f + f1) | ||||
|     6 | } | ||||
| vlib/v/checker/tests/method_op_alias_err.vv:5:19: error: infix expr: cannot use `string` (right expression) as `string` | ||||
|     3 |  | ||||
|     4 | fn (f Foo) + (f1 Foo2) Foo2 { | ||||
|     5 |     return Foo2(f + f1) | ||||
|       |                   ^ | ||||
|     6 | } | ||||
|     7 | | ||||
| vlib/v/checker/tests/method_op_alias_err.vv:8:1: error: cannot define operator methods on type alias for `string` | ||||
|     6 | } | ||||
|     7 |  | ||||
|     8 | fn (f Foo) * (f1 Foo) Foo { | ||||
|       | ~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||
|     9 |     return Foo(f + f1) | ||||
|    10 | } | ||||
| vlib/v/checker/tests/method_op_alias_err.vv:14:6: error: mismatched types `Foo` and `string` | ||||
|    12 | fn main() { | ||||
|    13 |    mut f := Foo('fg') | ||||
|    14 |    f += 'fg' | ||||
|       |      ~~ | ||||
|    15 |    f *= Foo2('2') | ||||
|    16 |    f -= Foo('fo') | ||||
| vlib/v/checker/tests/method_op_alias_err.vv:15:9: error: cannot assign to `f`: expected `Foo`, not `Foo2` | ||||
|    13 |    mut f := Foo('fg') | ||||
|    14 |    f += 'fg' | ||||
|    15 |    f *= Foo2('2') | ||||
|       |         ~~~~~~~~~ | ||||
|    16 |    f -= Foo('fo')  | ||||
|    17 |     println(f) | ||||
| vlib/v/checker/tests/method_op_alias_err.vv:16:6: error: cannot use operator methods on type alias for `string` | ||||
|    14 |    f += 'fg' | ||||
|    15 |    f *= Foo2('2') | ||||
|    16 |    f -= Foo('fo')  | ||||
|       |      ~~ | ||||
|    17 |     println(f) | ||||
|    18 | } | ||||
|  | @ -0,0 +1,18 @@ | |||
| type Foo = string  | ||||
| type Foo2 = string | ||||
| 
 | ||||
| fn (f Foo) + (f1 Foo2) Foo2 { | ||||
|     return Foo2(f + f1) | ||||
| } | ||||
| 
 | ||||
| fn (f Foo) * (f1 Foo) Foo { | ||||
|     return Foo(f + f1) | ||||
| } | ||||
| 
 | ||||
| fn main() { | ||||
|    mut f := Foo('fg') | ||||
|    f += 'fg' | ||||
|    f *= Foo2('2') | ||||
|    f -= Foo('fo')  | ||||
|     println(f) | ||||
| } | ||||
|  | @ -1953,10 +1953,10 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { | |||
| 				str_add = true | ||||
| 			} | ||||
| 			// Assignment Operator Overloading
 | ||||
| 			if left_sym.kind == .struct_ && | ||||
| 				right_sym.kind == .struct_ && assign_stmt.op in | ||||
| 				[.plus_assign, .minus_assign, .div_assign, .mult_assign, .mod_assign] { | ||||
| 				g.expr(left) | ||||
| 			if ((left_sym.kind == .struct_ && | ||||
| 				right_sym.kind == .struct_) || (left_sym.kind == .alias && | ||||
| 				right_sym.kind == .alias)) && | ||||
| 				assign_stmt.op in [.plus_assign, .minus_assign, .div_assign, .mult_assign, .mod_assign] { | ||||
| 				extracted_op := match assign_stmt.op { | ||||
| 					.plus_assign { '+' } | ||||
| 					.minus_assign { '-' } | ||||
|  | @ -1965,6 +1965,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { | |||
| 					.mult_assign { '*' } | ||||
| 					else { 'unknown op' } | ||||
| 				} | ||||
| 				g.expr(left) | ||||
| 				g.write(' = ${styp}_${util.replace_op(extracted_op)}(') | ||||
| 				op_overloaded = true | ||||
| 			} | ||||
|  | @ -2514,7 +2515,9 @@ fn (mut g Gen) expr(node ast.Expr) { | |||
| 			} else { | ||||
| 				styp := g.typ(node.typ) | ||||
| 				mut cast_label := '' | ||||
| 				if sym.kind != .alias || (sym.info as table.Alias).parent_type != node.expr_type { | ||||
| 				// `table.string_type` is done for MSVC's bug
 | ||||
| 				if sym.kind != .alias || | ||||
| 					(sym.info as table.Alias).parent_type !in [node.expr_type, table.string_type] { | ||||
| 					cast_label = '($styp)' | ||||
| 				} | ||||
| 				g.write('(${cast_label}(') | ||||
|  | @ -2943,8 +2946,8 @@ fn (mut g Gen) infix_expr(node ast.InfixExpr) { | |||
| 	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 | ||||
| 	unaliased_right := if right_sym.info is table.Alias { | ||||
| 		right_sym.info.parent_type | ||||
| 	} else { | ||||
| 		node.right_type | ||||
| 	} | ||||
|  |  | |||
|  | @ -546,11 +546,21 @@ pub fn (t &TypeSymbol) is_float() bool { | |||
| 	return t.kind in [.f32, .f64, .float_literal] | ||||
| } | ||||
| 
 | ||||
| [inline] | ||||
| pub fn (t &TypeSymbol) is_string() bool { | ||||
| 	return t.kind in [.string, .ustring] | ||||
| } | ||||
| 
 | ||||
| [inline] | ||||
| pub fn (t &TypeSymbol) is_number() bool { | ||||
| 	return t.is_int() || t.is_float() | ||||
| } | ||||
| 
 | ||||
| [inline] | ||||
| pub fn (t &TypeSymbol) is_primitive() bool { | ||||
| 	return t.is_number() || t.is_pointer() || t.is_string() | ||||
| } | ||||
| 
 | ||||
| // for debugging/errors only, perf is not an issue
 | ||||
| pub fn (k Kind) str() string { | ||||
| 	k_str := match k { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue