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