ast, parser, checker: fix generic struct init with inconsistent generic types (#13359)
parent
89d399b035
commit
a61b4809dc
|
@ -411,6 +411,7 @@ pub mut:
|
|||
has_update_expr bool
|
||||
fields []StructInitField
|
||||
embeds []StructInitEmbed
|
||||
generic_types []Type
|
||||
}
|
||||
|
||||
// import statement
|
||||
|
|
|
@ -1811,6 +1811,49 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr
|
|||
return typ
|
||||
}
|
||||
|
||||
// Foo<U>{ bar: U } to Foo<T>{ bar: T }
|
||||
pub fn (mut t Table) replace_generic_type(typ Type, generic_types []Type) {
|
||||
mut ts := t.sym(typ)
|
||||
match mut ts.info {
|
||||
Array {
|
||||
mut elem_type := ts.info.elem_type
|
||||
mut elem_sym := t.sym(elem_type)
|
||||
mut dims := 1
|
||||
for mut elem_sym.info is Array {
|
||||
info := elem_sym.info as Array
|
||||
elem_type = info.elem_type
|
||||
elem_sym = t.sym(elem_type)
|
||||
dims++
|
||||
}
|
||||
t.replace_generic_type(elem_type, generic_types)
|
||||
}
|
||||
ArrayFixed {
|
||||
t.replace_generic_type(ts.info.elem_type, generic_types)
|
||||
}
|
||||
Chan {
|
||||
t.replace_generic_type(ts.info.elem_type, generic_types)
|
||||
}
|
||||
Map {
|
||||
t.replace_generic_type(ts.info.key_type, generic_types)
|
||||
t.replace_generic_type(ts.info.value_type, generic_types)
|
||||
}
|
||||
Struct, Interface, SumType {
|
||||
generic_names := ts.info.generic_types.map(t.sym(it).name)
|
||||
for i in 0 .. ts.info.fields.len {
|
||||
if ts.info.fields[i].typ.has_flag(.generic) {
|
||||
if t_typ := t.resolve_generic_to_concrete(ts.info.fields[i].typ, generic_names,
|
||||
generic_types)
|
||||
{
|
||||
ts.info.fields[i].typ = t_typ
|
||||
}
|
||||
}
|
||||
}
|
||||
ts.info.generic_types = generic_types
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
|
||||
// generic struct instantiations to concrete types
|
||||
pub fn (mut t Table) generic_insts_to_concrete() {
|
||||
for mut typ in t.type_symbols {
|
||||
|
|
|
@ -135,6 +135,9 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
|
|||
c.error('generic struct init must specify type parameter, e.g. Foo<int>',
|
||||
node.pos)
|
||||
}
|
||||
if node.generic_types.len > 0 && struct_sym.info.generic_types != node.generic_types {
|
||||
c.table.replace_generic_type(node.typ, node.generic_types)
|
||||
}
|
||||
} else if struct_sym.info is ast.Alias {
|
||||
parent_sym := c.table.sym(struct_sym.info.parent_type)
|
||||
// e.g. ´x := MyMapAlias{}´, should be a cast to alias type ´x := MyMapAlias(map[...]...)´
|
||||
|
|
|
@ -638,6 +638,9 @@ pub fn (mut p Parser) parse_generic_inst_type(name string) ast.Type {
|
|||
bs_name += ', '
|
||||
bs_cname += '_'
|
||||
}
|
||||
if !is_instance {
|
||||
p.struct_init_generic_types = concrete_types
|
||||
}
|
||||
concrete_types_pos := start_pos.extend(p.tok.pos())
|
||||
p.check(.gt)
|
||||
p.inside_generic_params = false
|
||||
|
|
|
@ -88,6 +88,7 @@ mut:
|
|||
defer_vars []ast.Ident
|
||||
should_abort bool // when too many errors/warnings/notices are accumulated, should_abort becomes true, and the parser should stop
|
||||
codegen_text string
|
||||
struct_init_generic_types []ast.Type
|
||||
}
|
||||
|
||||
__global codegen_files = []&ast.File{}
|
||||
|
|
|
@ -337,6 +337,7 @@ fn (mut p Parser) struct_decl() ast.StructDecl {
|
|||
|
||||
fn (mut p Parser) struct_init(typ_str string, short_syntax bool) ast.StructInit {
|
||||
first_pos := (if short_syntax && p.prev_tok.kind == .lcbr { p.prev_tok } else { p.tok }).pos()
|
||||
p.struct_init_generic_types = []ast.Type{}
|
||||
typ := if short_syntax { ast.void_type } else { p.parse_type() }
|
||||
p.expr_mod = ''
|
||||
// sym := p.table.sym(typ)
|
||||
|
@ -426,6 +427,7 @@ fn (mut p Parser) struct_init(typ_str string, short_syntax bool) ast.StructInit
|
|||
pos: first_pos.extend(if short_syntax { p.tok.pos() } else { p.prev_tok.pos() })
|
||||
is_short: no_keys
|
||||
pre_comments: pre_comments
|
||||
generic_types: p.struct_init_generic_types
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
struct Response<U> {
|
||||
result U
|
||||
}
|
||||
|
||||
pub fn send<T>(res T) string {
|
||||
msg := Response<T>{
|
||||
result: res
|
||||
}
|
||||
return '$msg'
|
||||
}
|
||||
|
||||
fn test_generics_struct_init_with_inconsistent_generic_types() {
|
||||
mut ret := send(123)
|
||||
println(ret)
|
||||
assert ret.contains('Response<int>{')
|
||||
assert ret.contains('result: 123')
|
||||
|
||||
ret = send('abc')
|
||||
println(ret)
|
||||
assert ret.contains('Response<string>{')
|
||||
assert ret.contains("result: 'abc'")
|
||||
}
|
Loading…
Reference in New Issue