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
|
has_update_expr bool
|
||||||
fields []StructInitField
|
fields []StructInitField
|
||||||
embeds []StructInitEmbed
|
embeds []StructInitEmbed
|
||||||
|
generic_types []Type
|
||||||
}
|
}
|
||||||
|
|
||||||
// import statement
|
// import statement
|
||||||
|
|
|
@ -1811,6 +1811,49 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr
|
||||||
return typ
|
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
|
// generic struct instantiations to concrete types
|
||||||
pub fn (mut t Table) generic_insts_to_concrete() {
|
pub fn (mut t Table) generic_insts_to_concrete() {
|
||||||
for mut typ in t.type_symbols {
|
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>',
|
c.error('generic struct init must specify type parameter, e.g. Foo<int>',
|
||||||
node.pos)
|
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 {
|
} else if struct_sym.info is ast.Alias {
|
||||||
parent_sym := c.table.sym(struct_sym.info.parent_type)
|
parent_sym := c.table.sym(struct_sym.info.parent_type)
|
||||||
// e.g. ´x := MyMapAlias{}´, should be a cast to alias type ´x := MyMapAlias(map[...]...)´
|
// 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_name += ', '
|
||||||
bs_cname += '_'
|
bs_cname += '_'
|
||||||
}
|
}
|
||||||
|
if !is_instance {
|
||||||
|
p.struct_init_generic_types = concrete_types
|
||||||
|
}
|
||||||
concrete_types_pos := start_pos.extend(p.tok.pos())
|
concrete_types_pos := start_pos.extend(p.tok.pos())
|
||||||
p.check(.gt)
|
p.check(.gt)
|
||||||
p.inside_generic_params = false
|
p.inside_generic_params = false
|
||||||
|
|
|
@ -27,67 +27,68 @@ mut:
|
||||||
scanner &scanner.Scanner
|
scanner &scanner.Scanner
|
||||||
comments_mode scanner.CommentsMode = .skip_comments
|
comments_mode scanner.CommentsMode = .skip_comments
|
||||||
// see comment in parse_file
|
// see comment in parse_file
|
||||||
tok token.Token
|
tok token.Token
|
||||||
prev_tok token.Token
|
prev_tok token.Token
|
||||||
peek_tok token.Token
|
peek_tok token.Token
|
||||||
table &ast.Table
|
table &ast.Table
|
||||||
language ast.Language
|
language ast.Language
|
||||||
fn_language ast.Language // .c for `fn C.abcd()` declarations
|
fn_language ast.Language // .c for `fn C.abcd()` declarations
|
||||||
expr_level int // prevent too deep recursions for pathological programs
|
expr_level int // prevent too deep recursions for pathological programs
|
||||||
inside_vlib_file bool // true for all vlib/ files
|
inside_vlib_file bool // true for all vlib/ files
|
||||||
inside_test_file bool // when inside _test.v or _test.vv file
|
inside_test_file bool // when inside _test.v or _test.vv file
|
||||||
inside_if bool
|
inside_if bool
|
||||||
inside_if_expr bool
|
inside_if_expr bool
|
||||||
inside_ct_if_expr bool
|
inside_ct_if_expr bool
|
||||||
inside_or_expr bool
|
inside_or_expr bool
|
||||||
inside_for bool
|
inside_for bool
|
||||||
inside_fn bool // true even with implicit main
|
inside_fn bool // true even with implicit main
|
||||||
inside_unsafe_fn bool
|
inside_unsafe_fn bool
|
||||||
inside_str_interp bool
|
inside_str_interp bool
|
||||||
inside_array_lit bool
|
inside_array_lit bool
|
||||||
inside_in_array bool
|
inside_in_array bool
|
||||||
inside_match bool // to separate `match A { }` from `Struct{}`
|
inside_match bool // to separate `match A { }` from `Struct{}`
|
||||||
inside_select bool // to allow `ch <- Struct{} {` inside `select`
|
inside_select bool // to allow `ch <- Struct{} {` inside `select`
|
||||||
inside_match_case bool // to separate `match_expr { }` from `Struct{}`
|
inside_match_case bool // to separate `match_expr { }` from `Struct{}`
|
||||||
inside_match_body bool // to fix eval not used TODO
|
inside_match_body bool // to fix eval not used TODO
|
||||||
inside_unsafe bool
|
inside_unsafe bool
|
||||||
inside_sum_type bool // to prevent parsing inline sum type again
|
inside_sum_type bool // to prevent parsing inline sum type again
|
||||||
inside_asm_template bool
|
inside_asm_template bool
|
||||||
inside_asm bool
|
inside_asm bool
|
||||||
inside_defer bool
|
inside_defer bool
|
||||||
inside_generic_params bool // indicates if parsing between `<` and `>` of a method/function
|
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
|
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
|
or_is_handled bool // ignore `or` in this expression
|
||||||
builtin_mod bool // are we in the `builtin` module?
|
builtin_mod bool // are we in the `builtin` module?
|
||||||
mod string // current module name
|
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
|
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__
|
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__.
|
is_generated bool // `[generated] module abc` - turn off compiler notices for that single .v file __only__.
|
||||||
attrs []ast.Attr // attributes before next decl stmt
|
attrs []ast.Attr // attributes before next decl stmt
|
||||||
expr_mod string // for constructing full type names in parse_type()
|
expr_mod string // for constructing full type names in parse_type()
|
||||||
scope &ast.Scope
|
scope &ast.Scope
|
||||||
imports map[string]string // alias => mod_name
|
imports map[string]string // alias => mod_name
|
||||||
ast_imports []ast.Import // mod_names
|
ast_imports []ast.Import // mod_names
|
||||||
used_imports []string // alias
|
used_imports []string // alias
|
||||||
auto_imports []string // imports, the user does not need to specify
|
auto_imports []string // imports, the user does not need to specify
|
||||||
imported_symbols map[string]string
|
imported_symbols map[string]string
|
||||||
is_amp bool // for generating the right code for `&Foo{}`
|
is_amp bool // for generating the right code for `&Foo{}`
|
||||||
returns bool
|
returns bool
|
||||||
is_stmt_ident bool // true while the beginning of a statement is an ident/selector
|
is_stmt_ident bool // true while the beginning of a statement is an ident/selector
|
||||||
expecting_type bool // `is Type`, expecting type
|
expecting_type bool // `is Type`, expecting type
|
||||||
errors []errors.Error
|
errors []errors.Error
|
||||||
warnings []errors.Warning
|
warnings []errors.Warning
|
||||||
notices []errors.Notice
|
notices []errors.Notice
|
||||||
vet_errors []vet.Error
|
vet_errors []vet.Error
|
||||||
cur_fn_name string
|
cur_fn_name string
|
||||||
label_names []string
|
label_names []string
|
||||||
name_error bool // indicates if the token is not a name or the name is on another line
|
name_error bool // indicates if the token is not a name or the name is on another line
|
||||||
n_asm int // controls assembly labels
|
n_asm int // controls assembly labels
|
||||||
global_labels []string
|
global_labels []string
|
||||||
comptime_if_cond bool
|
comptime_if_cond bool
|
||||||
defer_vars []ast.Ident
|
defer_vars []ast.Ident
|
||||||
should_abort bool // when too many errors/warnings/notices are accumulated, should_abort becomes true, and the parser should stop
|
should_abort bool // when too many errors/warnings/notices are accumulated, should_abort becomes true, and the parser should stop
|
||||||
codegen_text string
|
codegen_text string
|
||||||
|
struct_init_generic_types []ast.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
__global codegen_files = []&ast.File{}
|
__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 {
|
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()
|
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() }
|
typ := if short_syntax { ast.void_type } else { p.parse_type() }
|
||||||
p.expr_mod = ''
|
p.expr_mod = ''
|
||||||
// sym := p.table.sym(typ)
|
// 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() })
|
pos: first_pos.extend(if short_syntax { p.tok.pos() } else { p.prev_tok.pos() })
|
||||||
is_short: no_keys
|
is_short: no_keys
|
||||||
pre_comments: pre_comments
|
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