Skip to content

Commit

Permalink
checker: fix struct array generic field (fix #22406, second try) (#22443
Browse files Browse the repository at this point in the history
)
  • Loading branch information
felipensp authored Oct 8, 2024
1 parent 4d74d72 commit e14bc60
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 19 deletions.
35 changes: 28 additions & 7 deletions vlib/v/ast/table.v
Original file line number Diff line number Diff line change
Expand Up @@ -1841,6 +1841,10 @@ pub fn (mut t Table) generic_type_names(generic_type Type) []string {
}

pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concrete_types []Type) Type {
return t.unwrap_generic_type_ex(typ, generic_names, concrete_types, false)
}

pub fn (mut t Table) unwrap_generic_type_ex(typ Type, generic_names []string, concrete_types []Type, recheck_concrete_types bool) Type {
mut final_concrete_types := []Type{}
mut fields := []StructField{}
mut needs_unwrap_types := []Type{}
Expand All @@ -1850,12 +1854,14 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr
match ts.info {
Array {
dims, elem_type := t.get_array_dims(ts.info)
unwrap_typ := t.unwrap_generic_type(elem_type, generic_names, concrete_types)
unwrap_typ := t.unwrap_generic_type_ex(elem_type, generic_names, concrete_types,
recheck_concrete_types)
idx := t.find_or_register_array_with_dims(unwrap_typ, dims)
return new_type(idx).derive_add_muls(typ).clear_flag(.generic)
}
ArrayFixed {
unwrap_typ := t.unwrap_generic_type(ts.info.elem_type, generic_names, concrete_types)
unwrap_typ := t.unwrap_generic_type_ex(ts.info.elem_type, generic_names, concrete_types,
recheck_concrete_types)
idx := t.find_or_register_array_fixed(unwrap_typ, ts.info.size, None{}, false)
return new_type(idx).derive_add_muls(typ).clear_flag(.generic)
}
Expand All @@ -1865,15 +1871,16 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr
return new_type(idx).derive_add_muls(typ).clear_flag(.generic)
}
Thread {
unwrap_typ := t.unwrap_generic_type(ts.info.return_type, generic_names, concrete_types)
unwrap_typ := t.unwrap_generic_type_ex(ts.info.return_type, generic_names,
concrete_types, recheck_concrete_types)
idx := t.find_or_register_thread(unwrap_typ)
return new_type(idx).derive_add_muls(typ).clear_flag(.generic)
}
Map {
unwrap_key_type := t.unwrap_generic_type(ts.info.key_type, generic_names,
concrete_types)
unwrap_value_type := t.unwrap_generic_type(ts.info.value_type, generic_names,
concrete_types)
unwrap_key_type := t.unwrap_generic_type_ex(ts.info.key_type, generic_names,
concrete_types, recheck_concrete_types)
unwrap_value_type := t.unwrap_generic_type_ex(ts.info.value_type, generic_names,
concrete_types, recheck_concrete_types)
idx := t.find_or_register_map(unwrap_key_type, unwrap_value_type)
return new_type(idx).derive_add_muls(typ).clear_flag(.generic)
}
Expand Down Expand Up @@ -1925,6 +1932,20 @@ pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concr
nrt += ']'
idx := t.type_idxs[nrt]
if idx != 0 && t.type_symbols[idx].kind != .placeholder {
if recheck_concrete_types {
fields = ts.info.fields.clone()
for i in 0 .. fields.len {
if !fields[i].typ.has_flag(.generic) {
continue
}
// Map[T], []Type[T]
if t.type_kind(fields[i].typ) in [.array, .array_fixed, .map]
&& t.check_if_elements_need_unwrap(typ, fields[i].typ) {
t.unwrap_generic_type_ex(fields[i].typ, t_generic_names, t_concrete_types,
recheck_concrete_types)
}
}
}
return new_type(idx).derive(typ).clear_flag(.generic)
} else {
// fields type translate to concrete type
Expand Down
21 changes: 11 additions & 10 deletions vlib/v/checker/checker.v
Original file line number Diff line number Diff line change
Expand Up @@ -4988,7 +4988,7 @@ fn (mut c Checker) fetch_field_name(field ast.StructField) string {
return name
}

fn (mut c Checker) ensure_generic_type_specify_type_names(typ ast.Type, pos token.Pos, is_container bool) bool {
fn (mut c Checker) ensure_generic_type_specify_type_names(typ ast.Type, pos token.Pos, is_container_typ bool, is_generic_container bool) bool {
if typ == 0 {
c.error('unknown type', pos)
return false
Expand All @@ -5015,12 +5015,12 @@ fn (mut c Checker) ensure_generic_type_specify_type_names(typ ast.Type, pos toke
.function {
fn_info := sym.info as ast.FnType
if !c.ensure_generic_type_specify_type_names(fn_info.func.return_type, fn_info.func.return_type_pos,
is_container) {
is_container_typ, is_generic_container) {
return false
}
for param in fn_info.func.params {
if !c.ensure_generic_type_specify_type_names(param.typ, param.type_pos,
is_container) {
is_container_typ, is_generic_container) {
return false
}
}
Expand All @@ -5032,29 +5032,29 @@ fn (mut c Checker) ensure_generic_type_specify_type_names(typ ast.Type, pos toke
}
.array {
if !c.ensure_generic_type_specify_type_names((sym.info as ast.Array).elem_type,
pos, true) {
pos, true, typ.has_flag(.generic)) {
return false
}
}
.array_fixed {
if !c.ensure_generic_type_specify_type_names((sym.info as ast.ArrayFixed).elem_type,
pos, true) {
pos, true, typ.has_flag(.generic)) {
return false
}
}
.map {
info := sym.info as ast.Map
if !c.ensure_generic_type_specify_type_names(info.key_type, pos, true) {
if !c.ensure_generic_type_specify_type_names(info.key_type, pos, true, typ.has_flag(.generic)) {
return false
}
if !c.ensure_generic_type_specify_type_names(info.value_type, pos, true) {
if !c.ensure_generic_type_specify_type_names(info.value_type, pos, true, typ.has_flag(.generic)) {
return false
}
}
.sum_type {
info := sym.info as ast.SumType
if info.generic_types.len > 0 && (is_container || !typ.has_flag(.generic))
&& info.concrete_types.len == 0 {
if info.generic_types.len > 0 && ((is_container_typ && !is_generic_container)
|| !typ.has_flag(.generic)) && info.concrete_types.len == 0 {
c.error('`${sym.name}` type is generic sumtype, must specify the generic type names, e.g. ${sym.name}[T], ${sym.name}[int]',
pos)
return false
Expand All @@ -5078,7 +5078,8 @@ fn (mut c Checker) ensure_generic_type_specify_type_names(typ ast.Type, pos toke
}
.alias {
info := sym.info as ast.Alias
if !c.ensure_generic_type_specify_type_names(info.parent_type, pos, is_container) {
if !c.ensure_generic_type_specify_type_names(info.parent_type, pos, is_container_typ,
is_generic_container) {
return false
}
}
Expand Down
9 changes: 9 additions & 0 deletions vlib/v/checker/fn.v
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,15 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
node.return_type_pos)
}
}
if gs.kind == .struct_ && c.needs_unwrap_generic_type(node.return_type) {
// resolve generic Array[T], Map[T] generics, avoid recursive generic resolving type
if c.ensure_generic_type_specify_type_names(node.return_type, node.return_type_pos,
false, false)
{
c.table.unwrap_generic_type_ex(node.return_type, c.table.cur_fn.generic_names,
c.table.cur_concrete_types, true)
}
}
}
return_sym := c.table.sym(node.return_type)
if return_sym.info is ast.Alias {
Expand Down
9 changes: 7 additions & 2 deletions vlib/v/checker/struct.v
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,11 @@ fn (mut c Checker) struct_decl(mut node ast.StructDecl) {
if !c.ensure_type_exists(field.typ, field.type_pos) {
continue
}
if !c.ensure_generic_type_specify_type_names(field.typ, field.type_pos, false) {
if !c.ensure_generic_type_specify_type_names(field.typ, field.type_pos, c.table.final_sym(field.typ).kind in [
.array,
.array_fixed,
.map,
], field.typ.has_flag(.generic)) {
continue
}
if field.typ.has_flag(.generic) {
Expand Down Expand Up @@ -506,7 +510,8 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini
}
// register generic struct type when current fn is generic fn
if c.table.cur_fn != unsafe { nil } && c.table.cur_fn.generic_names.len > 0 {
c.table.unwrap_generic_type(node.typ, c.table.cur_fn.generic_names, c.table.cur_concrete_types)
c.table.unwrap_generic_type_ex(node.typ, c.table.cur_fn.generic_names, c.table.cur_concrete_types,
true)
}
if !is_field_zero_struct_init {
c.ensure_type_exists(node.typ, node.pos)
Expand Down
62 changes: 62 additions & 0 deletions vlib/v/tests/structs/struct_array_generic_field_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// callback types
type CBnoret[T] = fn (val T)

type CBnoret2[T] = fn (val T, prev T)

type CBvret[T] = fn (val T) T

type CBvret2[T] = fn (val T, prev T) T

type Callback[T] = CBnoret[T] | CBnoret2[T] | CBvret[T] | CBvret2[T]

interface IObv[T] {
v T
prev T
cb []Callback[T]
}

struct Obv[T] {
mut:
v T
prev T
cb []Callback[T]
}

fn (o Obv[T]) get() T {
return o.v
}

fn (o Obv[T]) do_callbacks() T {
return o.v
}

fn (mut o Obv[T]) set(new_value T) T {
prev := o.v
if prev != new_value {
o.v = new_value
o.prev = prev
}

return o.v
}

fn oo2[T](init T) ?Obv[T] {
return none
}

fn oo[T](init T) Obv[T] {
return Obv[T]{
v: init
}
}

fn test_main() {
one := oo(1)
txt := oo('lala')

println('txt: ${txt.get()}')
println('one: ${one.get()}')

assert txt.get() == 'lala'
assert one.get() == 1
}

0 comments on commit e14bc60

Please sign in to comment.