cgen: allow assignment operators for type aliases (#8086)
parent
aeddd5b559
commit
0ca36aafe2
|
@ -24,26 +24,30 @@ fn test_from_u64() {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_plus() {
|
fn test_plus() {
|
||||||
a := big.from_u64(2)
|
mut a := big.from_u64(2)
|
||||||
b := big.from_u64(3)
|
b := big.from_u64(3)
|
||||||
c := a + b
|
c := a + b
|
||||||
assert c.hexstr() == '5'
|
assert c.hexstr() == '5'
|
||||||
assert (big.from_u64(1024) + big.from_u64(1024)).hexstr() == '800'
|
assert (big.from_u64(1024) + big.from_u64(1024)).hexstr() == '800'
|
||||||
|
a += b
|
||||||
|
assert a.hexstr() == '5'
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_minus() {
|
fn test_minus() {
|
||||||
a := big.from_u64(2)
|
a := big.from_u64(2)
|
||||||
b := big.from_u64(3)
|
mut b := big.from_u64(3)
|
||||||
c := b - a
|
c := b - a
|
||||||
assert c.hexstr() == '1'
|
assert c.hexstr() == '1'
|
||||||
e := big.from_u64(1024)
|
e := big.from_u64(1024)
|
||||||
ee := e - e
|
ee := e - e
|
||||||
assert ee.hexstr() == '0'
|
assert ee.hexstr() == '0'
|
||||||
|
b -= a
|
||||||
|
assert b.hexstr() == '1'
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_divide() {
|
fn test_divide() {
|
||||||
a := big.from_u64(2)
|
a := big.from_u64(2)
|
||||||
b := big.from_u64(3)
|
mut b := big.from_u64(3)
|
||||||
c := b / a
|
c := b / a
|
||||||
assert c.hexstr() == '1'
|
assert c.hexstr() == '1'
|
||||||
assert (b % a).hexstr() == '1'
|
assert (b % a).hexstr() == '1'
|
||||||
|
@ -52,10 +56,12 @@ fn test_divide() {
|
||||||
assert ee.hexstr() == '1'
|
assert ee.hexstr() == '1'
|
||||||
assert (e / a).hexstr() == '200'
|
assert (e / a).hexstr() == '200'
|
||||||
assert (e / (a * a)).hexstr() == '100'
|
assert (e / (a * a)).hexstr() == '100'
|
||||||
|
b /= a
|
||||||
|
assert b.hexstr() == '1'
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_multiply() {
|
fn test_multiply() {
|
||||||
a := big.from_u64(2)
|
mut a := big.from_u64(2)
|
||||||
b := big.from_u64(3)
|
b := big.from_u64(3)
|
||||||
c := b * a
|
c := b * a
|
||||||
assert c.hexstr() == '6'
|
assert c.hexstr() == '6'
|
||||||
|
@ -69,6 +75,8 @@ fn test_multiply() {
|
||||||
assert e8.hexstr() == '100000000000000000000'
|
assert e8.hexstr() == '100000000000000000000'
|
||||||
assert e9.hexstr() == '100000000000000000001'
|
assert e9.hexstr() == '100000000000000000001'
|
||||||
assert d.hexstr() == '60000000000000000000c00000000000000000018'
|
assert d.hexstr() == '60000000000000000000c00000000000000000018'
|
||||||
|
a *= b
|
||||||
|
assert a.hexstr() == '6'
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_mod() {
|
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',
|
c.error('invalid right operand: $left_sym.name $assign_stmt.op $right_sym.name',
|
||||||
right.position())
|
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`',
|
c.error('operator `$assign_stmt.op` not defined on left operand type `$left_sym.name`',
|
||||||
left.position())
|
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',
|
c.error('invalid right operand: $left_sym.name $assign_stmt.op $right_sym.name',
|
||||||
right.position())
|
right.position())
|
||||||
} else if right is ast.IntegerLiteral {
|
} 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 {
|
.mult_assign, .div_assign {
|
||||||
if !left_sym.is_number() &&
|
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`',
|
c.error('operator $assign_stmt.op.str() not defined on left operand type `$left_sym.name`',
|
||||||
left.position())
|
left.position())
|
||||||
} else if !right_sym.is_number() &&
|
} 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`',
|
c.error('operator $assign_stmt.op.str() not defined on right operand type `$right_sym.name`',
|
||||||
right.position())
|
right.position())
|
||||||
}
|
}
|
||||||
|
@ -2539,7 +2539,13 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
|
||||||
}
|
}
|
||||||
if assign_stmt.op in
|
if assign_stmt.op in
|
||||||
[.plus_assign, .minus_assign, .mod_assign, .mult_assign, .div_assign] &&
|
[.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 {
|
extracted_op := match assign_stmt.op {
|
||||||
.plus_assign { '+' }
|
.plus_assign { '+' }
|
||||||
.minus_assign { '-' }
|
.minus_assign { '-' }
|
||||||
|
@ -2548,14 +2554,16 @@ pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
|
||||||
.mult_assign { '*' }
|
.mult_assign { '*' }
|
||||||
else { 'unknown op' }
|
else { 'unknown op' }
|
||||||
}
|
}
|
||||||
left_name := c.table.type_to_str(left_type)
|
|
||||||
if method := left_sym.find_method(extracted_op) {
|
if method := left_sym.find_method(extracted_op) {
|
||||||
if method.return_type != left_type {
|
if method.return_type != left_type {
|
||||||
c.error('operator `$extracted_op` must return `$left_name` to be used as an assignment operator',
|
c.error('operator `$extracted_op` must return `$left_name` to be used as an assignment operator',
|
||||||
assign_stmt.pos)
|
assign_stmt.pos)
|
||||||
}
|
}
|
||||||
} else {
|
} 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 {
|
if left_name == right_name {
|
||||||
c.error('operation `$left_name` $extracted_op `$right_name` does not exist, please define it',
|
c.error('operation `$left_name` $extracted_op `$right_name` does not exist, please define it',
|
||||||
assign_stmt.pos)
|
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',
|
c.error('operator methods are only allowed for struct and type alias',
|
||||||
node.pos)
|
node.pos)
|
||||||
} else {
|
} else {
|
||||||
|
parent_sym := c.table.get_final_type_symbol(node.receiver.typ)
|
||||||
if node.rec_mut {
|
if node.rec_mut {
|
||||||
c.error('receiver cannot be `mut` for operator overloading', node.receiver_pos)
|
c.error('receiver cannot be `mut` for operator overloading', node.receiver_pos)
|
||||||
} else if node.params[1].is_mut {
|
} 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 ['<', '>', '==', '!=', '>=', '<='] &&
|
} else if node.name in ['<', '>', '==', '!=', '>=', '<='] &&
|
||||||
node.return_type != table.bool_type {
|
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)
|
||||||
|
} 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
|
str_add = true
|
||||||
}
|
}
|
||||||
// Assignment Operator Overloading
|
// Assignment Operator Overloading
|
||||||
if left_sym.kind == .struct_ &&
|
if ((left_sym.kind == .struct_ &&
|
||||||
right_sym.kind == .struct_ && assign_stmt.op in
|
right_sym.kind == .struct_) || (left_sym.kind == .alias &&
|
||||||
[.plus_assign, .minus_assign, .div_assign, .mult_assign, .mod_assign] {
|
right_sym.kind == .alias)) &&
|
||||||
g.expr(left)
|
assign_stmt.op in [.plus_assign, .minus_assign, .div_assign, .mult_assign, .mod_assign] {
|
||||||
extracted_op := match assign_stmt.op {
|
extracted_op := match assign_stmt.op {
|
||||||
.plus_assign { '+' }
|
.plus_assign { '+' }
|
||||||
.minus_assign { '-' }
|
.minus_assign { '-' }
|
||||||
|
@ -1965,6 +1965,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
|
||||||
.mult_assign { '*' }
|
.mult_assign { '*' }
|
||||||
else { 'unknown op' }
|
else { 'unknown op' }
|
||||||
}
|
}
|
||||||
|
g.expr(left)
|
||||||
g.write(' = ${styp}_${util.replace_op(extracted_op)}(')
|
g.write(' = ${styp}_${util.replace_op(extracted_op)}(')
|
||||||
op_overloaded = true
|
op_overloaded = true
|
||||||
}
|
}
|
||||||
|
@ -2514,7 +2515,9 @@ fn (mut g Gen) expr(node ast.Expr) {
|
||||||
} else {
|
} else {
|
||||||
styp := g.typ(node.typ)
|
styp := g.typ(node.typ)
|
||||||
mut cast_label := ''
|
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)'
|
cast_label = '($styp)'
|
||||||
}
|
}
|
||||||
g.write('(${cast_label}(')
|
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)
|
right_sym := g.table.get_type_symbol(node.right_type)
|
||||||
has_eq_overloaded := !left_sym.has_method('==')
|
has_eq_overloaded := !left_sym.has_method('==')
|
||||||
has_ne_overloaded := !left_sym.has_method('!=')
|
has_ne_overloaded := !left_sym.has_method('!=')
|
||||||
unaliased_right := if right_sym.kind == .alias {
|
unaliased_right := if right_sym.info is table.Alias {
|
||||||
(right_sym.info as table.Alias).parent_type
|
right_sym.info.parent_type
|
||||||
} else {
|
} else {
|
||||||
node.right_type
|
node.right_type
|
||||||
}
|
}
|
||||||
|
|
|
@ -546,11 +546,21 @@ pub fn (t &TypeSymbol) is_float() bool {
|
||||||
return t.kind in [.f32, .f64, .float_literal]
|
return t.kind in [.f32, .f64, .float_literal]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[inline]
|
||||||
|
pub fn (t &TypeSymbol) is_string() bool {
|
||||||
|
return t.kind in [.string, .ustring]
|
||||||
|
}
|
||||||
|
|
||||||
[inline]
|
[inline]
|
||||||
pub fn (t &TypeSymbol) is_number() bool {
|
pub fn (t &TypeSymbol) is_number() bool {
|
||||||
return t.is_int() || t.is_float()
|
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
|
// for debugging/errors only, perf is not an issue
|
||||||
pub fn (k Kind) str() string {
|
pub fn (k Kind) str() string {
|
||||||
k_str := match k {
|
k_str := match k {
|
||||||
|
|
Loading…
Reference in New Issue