parser: allow multiple types in match branch (#6505)
parent
18be7b115a
commit
324d547cdb
|
@ -1017,6 +1017,7 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
||||||
call_expr.left_type = left_type
|
call_expr.left_type = left_type
|
||||||
left_type_sym := c.table.get_type_symbol(c.unwrap_generic(left_type))
|
left_type_sym := c.table.get_type_symbol(c.unwrap_generic(left_type))
|
||||||
method_name := call_expr.name
|
method_name := call_expr.name
|
||||||
|
mut unknown_method_msg := 'unknown method: `${left_type_sym.source_name}.$method_name`'
|
||||||
if left_type.has_flag(.optional) {
|
if left_type.has_flag(.optional) {
|
||||||
c.error('optional type cannot be called directly', call_expr.left.position())
|
c.error('optional type cannot be called directly', call_expr.left.position())
|
||||||
return table.void_type
|
return table.void_type
|
||||||
|
@ -1213,6 +1214,11 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
||||||
}
|
}
|
||||||
call_expr.return_type = method.return_type
|
call_expr.return_type = method.return_type
|
||||||
return method.return_type
|
return method.return_type
|
||||||
|
} else {
|
||||||
|
if left_type_sym.kind == .aggregate {
|
||||||
|
// the error message contains the problematic type
|
||||||
|
unknown_method_msg = err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// TODO: str methods
|
// TODO: str methods
|
||||||
if method_name == 'str' {
|
if method_name == 'str' {
|
||||||
|
@ -1246,8 +1252,7 @@ pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
||||||
}
|
}
|
||||||
if left_type != table.void_type {
|
if left_type != table.void_type {
|
||||||
suggestion := util.new_suggestion(method_name, left_type_sym.methods.map(it.name))
|
suggestion := util.new_suggestion(method_name, left_type_sym.methods.map(it.name))
|
||||||
c.error(suggestion.say('unknown method: `${left_type_sym.source_name}.$method_name`'),
|
c.error(suggestion.say(unknown_method_msg), call_expr.pos)
|
||||||
call_expr.pos)
|
|
||||||
}
|
}
|
||||||
return table.void_type
|
return table.void_type
|
||||||
}
|
}
|
||||||
|
@ -1651,19 +1656,24 @@ pub fn (mut c Checker) selector_expr(mut selector_expr ast.SelectorExpr) table.T
|
||||||
return table.int_type
|
return table.int_type
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mut unknown_field_msg := 'type `$sym.source_name` has no field or method `$field_name`'
|
||||||
if field := c.table.struct_find_field(sym, field_name) {
|
if field := c.table.struct_find_field(sym, field_name) {
|
||||||
if sym.mod != c.mod && !field.is_pub {
|
if sym.mod != c.mod && !field.is_pub {
|
||||||
c.error('field `${sym.source_name}.$field_name` is not public', selector_expr.pos)
|
c.error('field `${sym.source_name}.$field_name` is not public', selector_expr.pos)
|
||||||
}
|
}
|
||||||
selector_expr.typ = field.typ
|
selector_expr.typ = field.typ
|
||||||
return field.typ
|
return field.typ
|
||||||
|
} else {
|
||||||
|
if sym.kind == .aggregate {
|
||||||
|
unknown_field_msg = err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if sym.kind != .struct_ {
|
if sym.kind !in [.struct_, .aggregate] {
|
||||||
if sym.kind != .placeholder {
|
if sym.kind != .placeholder {
|
||||||
c.error('`$sym.source_name` is not a struct', selector_expr.pos)
|
c.error('`$sym.source_name` is not a struct', selector_expr.pos)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.error('type `$sym.source_name` has no field or method `$field_name`', selector_expr.pos)
|
c.error(unknown_field_msg, selector_expr.pos)
|
||||||
}
|
}
|
||||||
return table.void_type
|
return table.void_type
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
vlib/v/checker/tests/match_sumtype_multiple_types.vv:25:13: error: type `Charlie` has no field or method `char`
|
||||||
|
23 | match NATOAlphabet(a) as l {
|
||||||
|
24 | Alfa, Charlie {
|
||||||
|
25 | assert l.char == `a`
|
||||||
|
| ~~~~
|
||||||
|
26 | assert l.letter() == 'a'
|
||||||
|
27 | }
|
||||||
|
vlib/v/checker/tests/match_sumtype_multiple_types.vv:26:13: error: unknown method: `Charlie.letter`
|
||||||
|
24 | Alfa, Charlie {
|
||||||
|
25 | assert l.char == `a`
|
||||||
|
26 | assert l.letter() == 'a'
|
||||||
|
| ~~~~~~~~~
|
||||||
|
27 | }
|
||||||
|
28 | Bravo {
|
|
@ -0,0 +1,32 @@
|
||||||
|
struct Alfa {
|
||||||
|
char rune
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (a Alfa) letter() rune {
|
||||||
|
return a.char
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Bravo {
|
||||||
|
char rune
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (b Bravo) letter() rune {
|
||||||
|
return b.char
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Charlie {}
|
||||||
|
|
||||||
|
type NATOAlphabet = Alfa | Bravo | Charlie
|
||||||
|
|
||||||
|
fn method_not_exists() {
|
||||||
|
a := Alfa{}
|
||||||
|
match NATOAlphabet(a) as l {
|
||||||
|
Alfa, Charlie {
|
||||||
|
assert l.char == `a`
|
||||||
|
assert l.letter() == 'a'
|
||||||
|
}
|
||||||
|
Bravo {
|
||||||
|
assert false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -283,6 +283,9 @@ pub fn (mut g JsGen) typ(t table.Type) string {
|
||||||
.rune {
|
.rune {
|
||||||
styp = 'any'
|
styp = 'any'
|
||||||
}
|
}
|
||||||
|
.aggregate {
|
||||||
|
panic('TODO: unhandled aggregate in JS')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -6,6 +6,7 @@ module parser
|
||||||
import v.ast
|
import v.ast
|
||||||
import v.table
|
import v.table
|
||||||
import v.token
|
import v.token
|
||||||
|
import strings
|
||||||
|
|
||||||
fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr {
|
fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr {
|
||||||
was_inside_if_expr := p.inside_if_expr
|
was_inside_if_expr := p.inside_if_expr
|
||||||
|
@ -210,14 +211,48 @@ fn (mut p Parser) match_expr() ast.MatchExpr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Sum type match
|
mut types := []table.Type{}
|
||||||
typ := p.parse_type()
|
for {
|
||||||
exprs << ast.Type{
|
// Sum type match
|
||||||
typ: typ
|
parsed_type := p.parse_type()
|
||||||
|
types << parsed_type
|
||||||
|
exprs << ast.Type{
|
||||||
|
typ: parsed_type
|
||||||
|
}
|
||||||
|
if p.tok.kind != .comma {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
p.check(.comma)
|
||||||
|
}
|
||||||
|
mut it_typ := table.void_type
|
||||||
|
if types.len == 1 {
|
||||||
|
it_typ = types[0]
|
||||||
|
} else {
|
||||||
|
// there is more than one types, so we must create a type aggregate
|
||||||
|
mut agg_name := strings.new_builder(20)
|
||||||
|
agg_name.write('(')
|
||||||
|
for i, typ in types {
|
||||||
|
if i > 0 {
|
||||||
|
agg_name.write(' | ')
|
||||||
|
}
|
||||||
|
type_str := p.table.type_to_str(typ)
|
||||||
|
agg_name.write(p.prepend_mod(type_str))
|
||||||
|
}
|
||||||
|
agg_name.write(')')
|
||||||
|
name := agg_name.str()
|
||||||
|
it_typ = p.table.register_type_symbol(table.TypeSymbol{
|
||||||
|
name: name
|
||||||
|
source_name: name
|
||||||
|
kind: .aggregate
|
||||||
|
mod: p.mod
|
||||||
|
info: table.Aggregate{
|
||||||
|
types: types
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
p.scope.register('it', ast.Var{
|
p.scope.register('it', ast.Var{
|
||||||
name: 'it'
|
name: 'it'
|
||||||
typ: typ.to_ptr()
|
typ: it_typ.to_ptr()
|
||||||
pos: cond_pos
|
pos: cond_pos
|
||||||
is_used: true
|
is_used: true
|
||||||
is_mut: is_mut
|
is_mut: is_mut
|
||||||
|
@ -226,18 +261,13 @@ fn (mut p Parser) match_expr() ast.MatchExpr {
|
||||||
// Register shadow variable or `as` variable with actual type
|
// Register shadow variable or `as` variable with actual type
|
||||||
p.scope.register(var_name, ast.Var{
|
p.scope.register(var_name, ast.Var{
|
||||||
name: var_name
|
name: var_name
|
||||||
typ: typ.to_ptr()
|
typ: it_typ.to_ptr()
|
||||||
pos: cond_pos
|
pos: cond_pos
|
||||||
is_used: true
|
is_used: true
|
||||||
is_changed: true // TODO mut unchanged warning hack, remove
|
is_changed: true // TODO mut unchanged warning hack, remove
|
||||||
is_mut: is_mut
|
is_mut: is_mut
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// TODO
|
|
||||||
if p.tok.kind == .comma {
|
|
||||||
p.next()
|
|
||||||
p.parse_type()
|
|
||||||
}
|
|
||||||
is_sum_type = true
|
is_sum_type = true
|
||||||
} else {
|
} else {
|
||||||
// Expression match
|
// Expression match
|
||||||
|
|
|
@ -16,7 +16,7 @@ import strings
|
||||||
pub type Type = int
|
pub type Type = int
|
||||||
|
|
||||||
pub type TypeInfo = Alias | Array | ArrayFixed | Chan | Enum | FnType | GenericStructInst |
|
pub type TypeInfo = Alias | Array | ArrayFixed | Chan | Enum | FnType | GenericStructInst |
|
||||||
Interface | Map | MultiReturn | Struct | SumType
|
Interface | Map | MultiReturn | Struct | SumType | Aggregate
|
||||||
|
|
||||||
pub enum Language {
|
pub enum Language {
|
||||||
v
|
v
|
||||||
|
@ -366,6 +366,7 @@ pub enum Kind {
|
||||||
interface_
|
interface_
|
||||||
any_float
|
any_float
|
||||||
any_int
|
any_int
|
||||||
|
aggregate
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (t &TypeSymbol) str() string {
|
pub fn (t &TypeSymbol) str() string {
|
||||||
|
@ -681,6 +682,7 @@ pub fn (k Kind) str() string {
|
||||||
.ustring { 'ustring' }
|
.ustring { 'ustring' }
|
||||||
.generic_struct_inst { 'generic_struct_inst' }
|
.generic_struct_inst { 'generic_struct_inst' }
|
||||||
.rune { 'rune' }
|
.rune { 'rune' }
|
||||||
|
.aggregate { 'aggregate' }
|
||||||
}
|
}
|
||||||
return k_str
|
return k_str
|
||||||
}
|
}
|
||||||
|
@ -730,6 +732,13 @@ pub:
|
||||||
language Language
|
language Language
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Aggregate {
|
||||||
|
mut:
|
||||||
|
fields []Field // used for faster lookup inside the module
|
||||||
|
pub:
|
||||||
|
types []Type
|
||||||
|
}
|
||||||
|
|
||||||
// NB: FExpr here is a actually an ast.Expr .
|
// NB: FExpr here is a actually an ast.Expr .
|
||||||
// It should always be used by casting to ast.Expr, using ast.fe2ex()/ast.ex2fe()
|
// It should always be used by casting to ast.Expr, using ast.fe2ex()/ast.ex2fe()
|
||||||
// That hack is needed to break an import cycle between v.ast and v.table .
|
// That hack is needed to break an import cycle between v.ast and v.table .
|
||||||
|
@ -749,6 +758,15 @@ pub mut:
|
||||||
is_global bool
|
is_global bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (f &Field) equals(o &Field) bool {
|
||||||
|
return f.name == o.name &&
|
||||||
|
f.typ == o.typ &&
|
||||||
|
// TODO Should those be checked ?
|
||||||
|
f.is_pub == o.is_pub &&
|
||||||
|
f.is_mut == o.is_mut &&
|
||||||
|
f.is_global == o.is_global
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Array {
|
pub struct Array {
|
||||||
pub:
|
pub:
|
||||||
nr_dims int
|
nr_dims int
|
||||||
|
@ -899,6 +917,15 @@ pub fn (t &TypeSymbol) str_method_info() (bool, bool, int) {
|
||||||
return has_str_method, expects_ptr, nr_args
|
return has_str_method, expects_ptr, nr_args
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (a &Aggregate) find_field(name string) ?Field {
|
||||||
|
for field in a.fields {
|
||||||
|
if field.name == name {
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return none
|
||||||
|
}
|
||||||
|
|
||||||
pub fn (s Struct) find_field(name string) ?Field {
|
pub fn (s Struct) find_field(name string) ?Field {
|
||||||
for field in s.fields {
|
for field in s.fields {
|
||||||
if field.name == name {
|
if field.name == name {
|
||||||
|
|
|
@ -39,6 +39,18 @@ pub mut:
|
||||||
name string
|
name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (f &Fn) method_equals(o &Fn) bool {
|
||||||
|
return f.params[1..].equals(o.params[1..]) &&
|
||||||
|
f.return_type == o.return_type &&
|
||||||
|
f.return_type_source_name == o.return_type_source_name &&
|
||||||
|
f.is_variadic == o.is_variadic &&
|
||||||
|
f.language == o.language &&
|
||||||
|
f.is_generic == o.is_generic &&
|
||||||
|
f.is_pub == o.is_pub &&
|
||||||
|
f.mod == o.mod &&
|
||||||
|
f.name == o.name
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Param {
|
pub struct Param {
|
||||||
pub:
|
pub:
|
||||||
pos token.Position
|
pos token.Position
|
||||||
|
@ -49,6 +61,26 @@ pub:
|
||||||
is_hidden bool // interface first arg
|
is_hidden bool // interface first arg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (p &Param) equals(o &Param) bool {
|
||||||
|
return p.name == o.name
|
||||||
|
&& p.is_mut == o.is_mut
|
||||||
|
&& p.typ == o.typ
|
||||||
|
&& p.type_source_name == o.type_source_name
|
||||||
|
&& p.is_hidden == o.is_hidden
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (p []Param) equals(o []Param) bool {
|
||||||
|
if p.len != o.len {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for i in 0..p.len {
|
||||||
|
if !p[i].equals(o[i]) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Var {
|
pub struct Var {
|
||||||
pub:
|
pub:
|
||||||
name string
|
name string
|
||||||
|
@ -139,6 +171,32 @@ pub fn (mut t TypeSymbol) register_method(new_fn Fn) {
|
||||||
t.methods << new_fn
|
t.methods << new_fn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn (t &Table) register_aggregate_method(mut sym TypeSymbol, name string) ?Fn {
|
||||||
|
if sym.kind != .aggregate {
|
||||||
|
panic('Unexpected type symbol: $sym.kind')
|
||||||
|
}
|
||||||
|
agg_info := sym.info as Aggregate
|
||||||
|
// an aggregate always has at least 2 types
|
||||||
|
mut found_once := false
|
||||||
|
mut new_fn := Fn{}
|
||||||
|
for typ in agg_info.types {
|
||||||
|
ts := t.get_type_symbol(typ)
|
||||||
|
if type_method := ts.find_method(name) {
|
||||||
|
if !found_once {
|
||||||
|
found_once = true
|
||||||
|
new_fn = type_method
|
||||||
|
} else if !new_fn.method_equals(type_method) {
|
||||||
|
return error('method `${t.type_to_str(typ)}.$name` signature is different')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return error('unknown method: `${t.type_to_str(typ)}.$name`')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// register the method in the aggregate, so lookup is faster next time
|
||||||
|
sym.register_method(new_fn)
|
||||||
|
return new_fn
|
||||||
|
}
|
||||||
|
|
||||||
pub fn (t &Table) type_has_method(s &TypeSymbol, name string) bool {
|
pub fn (t &Table) type_has_method(s &TypeSymbol, name string) bool {
|
||||||
// println('type_has_method($s.name, $name) types.len=$t.types.len s.parent_idx=$s.parent_idx')
|
// println('type_has_method($s.name, $name) types.len=$t.types.len s.parent_idx=$s.parent_idx')
|
||||||
if _ := t.type_find_method(s, name) {
|
if _ := t.type_find_method(s, name) {
|
||||||
|
@ -155,6 +213,10 @@ pub fn (t &Table) type_find_method(s &TypeSymbol, name string) ?Fn {
|
||||||
if method := ts.find_method(name) {
|
if method := ts.find_method(name) {
|
||||||
return method
|
return method
|
||||||
}
|
}
|
||||||
|
if ts.kind == .aggregate {
|
||||||
|
method := t.register_aggregate_method(mut ts, name) ?
|
||||||
|
return method
|
||||||
|
}
|
||||||
if ts.parent_idx == 0 {
|
if ts.parent_idx == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -163,6 +225,31 @@ pub fn (t &Table) type_find_method(s &TypeSymbol, name string) ?Fn {
|
||||||
return none
|
return none
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (t &Table) register_aggregate_field(mut sym TypeSymbol, name string) ?Field {
|
||||||
|
if sym.kind != .aggregate {
|
||||||
|
panic('Unexpected type symbol: $sym.kind')
|
||||||
|
}
|
||||||
|
mut agg_info := sym.info as Aggregate
|
||||||
|
// an aggregate always has at least 2 types
|
||||||
|
mut found_once := false
|
||||||
|
mut new_field := Field{}
|
||||||
|
for typ in agg_info.types {
|
||||||
|
ts := t.get_type_symbol(typ)
|
||||||
|
if type_field := t.struct_find_field(ts, name) {
|
||||||
|
if !found_once {
|
||||||
|
found_once = true
|
||||||
|
new_field = type_field
|
||||||
|
} else if !new_field.equals(type_field) {
|
||||||
|
return error('field `${t.type_to_str(typ)}.$name` type is different')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return error('type `${t.type_to_str(typ)}` has no field or method `$name`')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
agg_info.fields << new_field
|
||||||
|
return new_field
|
||||||
|
}
|
||||||
|
|
||||||
pub fn (t &Table) struct_has_field(s &TypeSymbol, name string) bool {
|
pub fn (t &Table) struct_has_field(s &TypeSymbol, name string) bool {
|
||||||
// println('struct_has_field($s.name, $name) types.len=$t.types.len s.parent_idx=$s.parent_idx')
|
// println('struct_has_field($s.name, $name) types.len=$t.types.len s.parent_idx=$s.parent_idx')
|
||||||
if _ := t.struct_find_field(s, name) {
|
if _ := t.struct_find_field(s, name) {
|
||||||
|
@ -176,11 +263,18 @@ pub fn (t &Table) struct_find_field(s &TypeSymbol, name string) ?Field {
|
||||||
// println('struct_find_field($s.name, $name) types.len=$t.types.len s.parent_idx=$s.parent_idx')
|
// println('struct_find_field($s.name, $name) types.len=$t.types.len s.parent_idx=$s.parent_idx')
|
||||||
mut ts := s
|
mut ts := s
|
||||||
for {
|
for {
|
||||||
if ts.info is Struct {
|
if ts.info is Struct as struct_info {
|
||||||
struct_info := ts.info as Struct
|
|
||||||
if field := struct_info.find_field(name) {
|
if field := struct_info.find_field(name) {
|
||||||
return field
|
return field
|
||||||
}
|
}
|
||||||
|
} else if ts.info is Aggregate as agg_info {
|
||||||
|
if field := agg_info.find_field(name) {
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
field := t.register_aggregate_field(mut ts, name) or {
|
||||||
|
return error(err)
|
||||||
|
}
|
||||||
|
return field
|
||||||
}
|
}
|
||||||
if ts.parent_idx == 0 {
|
if ts.parent_idx == 0 {
|
||||||
break
|
break
|
||||||
|
@ -396,7 +490,7 @@ pub fn (t &Table) map_source_name(key_type, value_type Type) string {
|
||||||
key_type_sym := t.get_type_symbol(key_type)
|
key_type_sym := t.get_type_symbol(key_type)
|
||||||
value_type_sym := t.get_type_symbol(value_type)
|
value_type_sym := t.get_type_symbol(value_type)
|
||||||
ptr := if value_type.is_ptr() { '&' } else { '' }
|
ptr := if value_type.is_ptr() { '&' } else { '' }
|
||||||
return 'map[${key_type_sym.source_name}]$ptr$value_type_sym.source_name'
|
return 'map[$key_type_sym.source_name]$ptr$value_type_sym.source_name'
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut t Table) find_or_register_chan(elem_type Type, is_mut bool) int {
|
pub fn (mut t Table) find_or_register_chan(elem_type Type, is_mut bool) int {
|
||||||
|
@ -415,7 +509,7 @@ pub fn (mut t Table) find_or_register_chan(elem_type Type, is_mut bool) int {
|
||||||
source_name: source_name
|
source_name: source_name
|
||||||
info: Chan{
|
info: Chan{
|
||||||
elem_type: elem_type
|
elem_type: elem_type
|
||||||
is_mut: is_mut
|
is_mut: is_mut
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return t.register_type_symbol(chan_typ)
|
return t.register_type_symbol(chan_typ)
|
||||||
|
|
|
@ -164,3 +164,40 @@ fn test_sum_type_name() {
|
||||||
}
|
}
|
||||||
assert f(a) == 'A1'
|
assert f(a) == 'A1'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct Alfa {
|
||||||
|
char rune = `a`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (a Alfa) letter() rune {
|
||||||
|
return a.char
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Bravo {
|
||||||
|
// A field so that Alfa and Bravo structures aren't the same
|
||||||
|
dummy_field int
|
||||||
|
char rune = `b`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (b Bravo) letter() rune {
|
||||||
|
return b.char
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Charlie {}
|
||||||
|
|
||||||
|
type NATOAlphabet = Alfa | Bravo | Charlie
|
||||||
|
|
||||||
|
fn test_match_sum_type_multiple_type() {
|
||||||
|
a := Alfa{}
|
||||||
|
// TODO This currently works because cgen takes the first type as the type of `l`
|
||||||
|
// it would fail if we `a` was of type `Bravo`
|
||||||
|
match NATOAlphabet(a) as l {
|
||||||
|
Alfa, Bravo {
|
||||||
|
assert l.char == `a`
|
||||||
|
assert l.letter() == `a`
|
||||||
|
}
|
||||||
|
Charlie {
|
||||||
|
assert false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue