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
|
||||
|
|
|
@ -27,67 +27,68 @@ mut:
|
|||
scanner &scanner.Scanner
|
||||
comments_mode scanner.CommentsMode = .skip_comments
|
||||
// see comment in parse_file
|
||||
tok token.Token
|
||||
prev_tok token.Token
|
||||
peek_tok token.Token
|
||||
table &ast.Table
|
||||
language ast.Language
|
||||
fn_language ast.Language // .c for `fn C.abcd()` declarations
|
||||
expr_level int // prevent too deep recursions for pathological programs
|
||||
inside_vlib_file bool // true for all vlib/ files
|
||||
inside_test_file bool // when inside _test.v or _test.vv file
|
||||
inside_if bool
|
||||
inside_if_expr bool
|
||||
inside_ct_if_expr bool
|
||||
inside_or_expr bool
|
||||
inside_for bool
|
||||
inside_fn bool // true even with implicit main
|
||||
inside_unsafe_fn bool
|
||||
inside_str_interp bool
|
||||
inside_array_lit bool
|
||||
inside_in_array bool
|
||||
inside_match bool // to separate `match A { }` from `Struct{}`
|
||||
inside_select bool // to allow `ch <- Struct{} {` inside `select`
|
||||
inside_match_case bool // to separate `match_expr { }` from `Struct{}`
|
||||
inside_match_body bool // to fix eval not used TODO
|
||||
inside_unsafe bool
|
||||
inside_sum_type bool // to prevent parsing inline sum type again
|
||||
inside_asm_template bool
|
||||
inside_asm bool
|
||||
inside_defer bool
|
||||
inside_generic_params bool // indicates if parsing between `<` and `>` of a method/function
|
||||
inside_receiver_param bool // indicates if parsing the receiver parameter inside the first `(` and `)` of a method
|
||||
or_is_handled bool // ignore `or` in this expression
|
||||
builtin_mod bool // are we in the `builtin` module?
|
||||
mod string // current module name
|
||||
is_manualfree bool // true when `[manualfree] module abc`, makes *all* fns in the current .v file, opt out of autofree
|
||||
has_globals bool // `[has_globals] module abc` - allow globals declarations, even without -enable-globals, in that single .v file __only__
|
||||
is_generated bool // `[generated] module abc` - turn off compiler notices for that single .v file __only__.
|
||||
attrs []ast.Attr // attributes before next decl stmt
|
||||
expr_mod string // for constructing full type names in parse_type()
|
||||
scope &ast.Scope
|
||||
imports map[string]string // alias => mod_name
|
||||
ast_imports []ast.Import // mod_names
|
||||
used_imports []string // alias
|
||||
auto_imports []string // imports, the user does not need to specify
|
||||
imported_symbols map[string]string
|
||||
is_amp bool // for generating the right code for `&Foo{}`
|
||||
returns bool
|
||||
is_stmt_ident bool // true while the beginning of a statement is an ident/selector
|
||||
expecting_type bool // `is Type`, expecting type
|
||||
errors []errors.Error
|
||||
warnings []errors.Warning
|
||||
notices []errors.Notice
|
||||
vet_errors []vet.Error
|
||||
cur_fn_name string
|
||||
label_names []string
|
||||
name_error bool // indicates if the token is not a name or the name is on another line
|
||||
n_asm int // controls assembly labels
|
||||
global_labels []string
|
||||
comptime_if_cond bool
|
||||
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
|
||||
tok token.Token
|
||||
prev_tok token.Token
|
||||
peek_tok token.Token
|
||||
table &ast.Table
|
||||
language ast.Language
|
||||
fn_language ast.Language // .c for `fn C.abcd()` declarations
|
||||
expr_level int // prevent too deep recursions for pathological programs
|
||||
inside_vlib_file bool // true for all vlib/ files
|
||||
inside_test_file bool // when inside _test.v or _test.vv file
|
||||
inside_if bool
|
||||
inside_if_expr bool
|
||||
inside_ct_if_expr bool
|
||||
inside_or_expr bool
|
||||
inside_for bool
|
||||
inside_fn bool // true even with implicit main
|
||||
inside_unsafe_fn bool
|
||||
inside_str_interp bool
|
||||
inside_array_lit bool
|
||||
inside_in_array bool
|
||||
inside_match bool // to separate `match A { }` from `Struct{}`
|
||||
inside_select bool // to allow `ch <- Struct{} {` inside `select`
|
||||
inside_match_case bool // to separate `match_expr { }` from `Struct{}`
|
||||
inside_match_body bool // to fix eval not used TODO
|
||||
inside_unsafe bool
|
||||
inside_sum_type bool // to prevent parsing inline sum type again
|
||||
inside_asm_template bool
|
||||
inside_asm bool
|
||||
inside_defer bool
|
||||
inside_generic_params bool // indicates if parsing between `<` and `>` of a method/function
|
||||
inside_receiver_param bool // indicates if parsing the receiver parameter inside the first `(` and `)` of a method
|
||||
or_is_handled bool // ignore `or` in this expression
|
||||
builtin_mod bool // are we in the `builtin` module?
|
||||
mod string // current module name
|
||||
is_manualfree bool // true when `[manualfree] module abc`, makes *all* fns in the current .v file, opt out of autofree
|
||||
has_globals bool // `[has_globals] module abc` - allow globals declarations, even without -enable-globals, in that single .v file __only__
|
||||
is_generated bool // `[generated] module abc` - turn off compiler notices for that single .v file __only__.
|
||||
attrs []ast.Attr // attributes before next decl stmt
|
||||
expr_mod string // for constructing full type names in parse_type()
|
||||
scope &ast.Scope
|
||||
imports map[string]string // alias => mod_name
|
||||
ast_imports []ast.Import // mod_names
|
||||
used_imports []string // alias
|
||||
auto_imports []string // imports, the user does not need to specify
|
||||
imported_symbols map[string]string
|
||||
is_amp bool // for generating the right code for `&Foo{}`
|
||||
returns bool
|
||||
is_stmt_ident bool // true while the beginning of a statement is an ident/selector
|
||||
expecting_type bool // `is Type`, expecting type
|
||||
errors []errors.Error
|
||||
warnings []errors.Warning
|
||||
notices []errors.Notice
|
||||
vet_errors []vet.Error
|
||||
cur_fn_name string
|
||||
label_names []string
|
||||
name_error bool // indicates if the token is not a name or the name is on another line
|
||||
n_asm int // controls assembly labels
|
||||
global_labels []string
|
||||
comptime_if_cond bool
|
||||
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