Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
136 changes: 87 additions & 49 deletions vlib/v2/transformer/struct.v
Original file line number Diff line number Diff line change
Expand Up @@ -1206,51 +1206,60 @@ fn (t &Transformer) get_struct_field_type_name(struct_name string, field_name st
// Look up the struct type in scopes.
// When the struct name is not module-qualified, prefer the current module scope
// to avoid collisions (e.g., ast.FnType vs types.FnType both named "FnType").
// Extract the type under lock, then call get_field_type_name outside to avoid deadlock.
mut found_type := types.Type(types.void_)
mut found := false
lock t.env.scopes {
// First: try the current module scope for non-qualified names.
if !struct_name.contains('__') && t.cur_module != '' && t.cur_module != 'main'
&& t.cur_module != 'builtin' {
if cur_scope := t.env.scopes[t.cur_module] {
if obj := cur_scope.objects[struct_name] {
if obj is types.Type {
result := t.get_field_type_name(obj, field_name)
if result != '' {
return result
}
found_type = obj
found = true
}
}
}
}
scope_names := t.env.scopes.keys()
for scope_name in scope_names {
scope := t.env.scopes[scope_name] or { continue }
// Try the struct name directly
if obj := scope.objects[struct_name] {
if obj is types.Type {
return t.get_field_type_name(obj, field_name)
}
}
// Try with current module prefix
if t.cur_module != '' && t.cur_module != 'main' && t.cur_module != 'builtin' {
mangled := '${t.cur_module}__${struct_name}'
if obj := scope.objects[mangled] {
if !found {
scope_names := t.env.scopes.keys()
for scope_name in scope_names {
scope := t.env.scopes[scope_name] or { continue }
if obj := scope.objects[struct_name] {
if obj is types.Type {
return t.get_field_type_name(obj, field_name)
found_type = obj
found = true
break
}
}
}
// Try stripping module prefix for cross-module types (e.g., "ast__CallExpr" -> "CallExpr")
if struct_name.contains('__') {
short_name := struct_name.all_after_last('__')
if obj := scope.objects[short_name] {
if obj is types.Type {
return t.get_field_type_name(obj, field_name)
if t.cur_module != '' && t.cur_module != 'main' && t.cur_module != 'builtin' {
mangled := '${t.cur_module}__${struct_name}'
if obj := scope.objects[mangled] {
if obj is types.Type {
found_type = obj
found = true
break
}
}
}
if struct_name.contains('__') {
short_name := struct_name.all_after_last('__')
if obj := scope.objects[short_name] {
if obj is types.Type {
found_type = obj
found = true
break
}
}
}
}
}
}
return ''
if !found {
return ''
}
return t.get_field_type_name(found_type, field_name)
}

// wrap_sumtype_value wraps a value in sum type initialization if needed
Expand Down Expand Up @@ -1303,51 +1312,59 @@ fn (t &Transformer) resolve_struct_field_type(struct_name string, field_name str
lookup_name = parts[parts.len - 1]
}
}

// Extract the type under lock, call get_field_type_name outside to avoid deadlock.
mut found_type := types.Type(types.void_)
mut found := false
lock t.env.scopes {
scope_names := t.env.scopes.keys()
for scope_name in scope_names {
scope := t.env.scopes[scope_name] or { continue }
// Try the lookup name directly in the appropriate module scope
if lookup_module != '' && scope_name == lookup_module {
// Look in the module scope
if obj := scope.objects[lookup_name] {
if obj is types.Type {
return t.get_field_type_name(obj, field_name)
found_type = obj
found = true
break
}
}
// Also try fully qualified name
if obj := scope.objects[struct_name] {
if obj is types.Type {
return t.get_field_type_name(obj, field_name)
found_type = obj
found = true
break
}
}
}
// Try the struct name directly
if obj := scope.objects[struct_name] {
if obj is types.Type {
result := t.get_field_type_name(obj, field_name)
return result
found_type = obj
found = true
break
}
}
// Try just the short name
if obj := scope.objects[lookup_name] {
if obj is types.Type {
return t.get_field_type_name(obj, field_name)
found_type = obj
found = true
break
}
}
// Try with current module prefix
if t.cur_module != '' && t.cur_module != 'main' && t.cur_module != 'builtin' {
mangled := '${t.cur_module}__${struct_name}'
if obj := scope.objects[mangled] {
if obj is types.Type {
return t.get_field_type_name(obj, field_name)
found_type = obj
found = true
break
}
}
}
}
}
return ''
if !found {
return ''
}
return t.get_field_type_name(found_type, field_name)
}

// get_field_type_name gets the type name of a field from a Type
Expand All @@ -1369,11 +1386,14 @@ fn (t &Transformer) get_field_type_name(typ types.Type, field_name string) strin
// get_field_array_elem_sumtype_name returns the sum type name of the array element type
// for a struct field, if the field is an array of sum types. Returns '' otherwise.
fn (t &Transformer) get_field_array_elem_sumtype_name(struct_name string, field_name string) string {
// Extract array element type from scope under lock, then call type_to_name/is_sum_type
// outside the lock to avoid deadlock (those functions also acquire t.env.scopes).
mut found_elem_type := types.Type(types.void_)
mut found := false
lock t.env.scopes {
scope_names := t.env.scopes.keys()
for scope_name in scope_names {
scope := t.env.scopes[scope_name] or { continue }
// Try direct name and short name (for cross-module types like ast__CallExpr)
names := if struct_name.contains('__') {
[struct_name, struct_name.all_after_last('__')]
} else {
Expand All @@ -1387,25 +1407,36 @@ fn (t &Transformer) get_field_array_elem_sumtype_name(struct_name string, field_
if field.name == field_name {
if field.typ is types.Array {
field_arr := field.typ as types.Array
elem_name := t.type_to_name(field_arr.elem_type)
if t.is_sum_type(elem_name) {
return elem_name
}
found_elem_type = field_arr.elem_type
found = true
}
Comment on lines 1408 to 1412
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stop searching after first matching field in sumtype helper

get_field_array_elem_sumtype_name now keeps iterating scopes when it finds field_name but that field is not an array, because found is only set inside the types.Array branch. In projects with homonymous structs across modules, this can skip the intended struct and later pick an array field from another scope, so the transformer wraps elements as a sumtype when the target field is actually non-array. Before this refactor, the function returned immediately after the first matching field, so this introduces incorrect cross-scope resolution.

Useful? React with 👍 / 👎.

return ''
break
}
}
}
}
}
}
if found {
break
}
}
}
if !found {
return ''
}
elem_name := t.type_to_name(found_elem_type)
if t.is_sum_type(elem_name) {
return elem_name
}
return ''
}

// get_field_array_elem_c_name returns the C type name for the element type of an array field
fn (t &Transformer) get_field_array_elem_c_name(struct_name string, field_name string) string {
// Extract element type under lock, call type_to_c_name outside to avoid deadlock.
mut found_elem_type := types.Type(types.void_)
mut found := false
lock t.env.scopes {
scope_names := t.env.scopes.keys()
for scope_name in scope_names {
Expand All @@ -1417,17 +1448,24 @@ fn (t &Transformer) get_field_array_elem_c_name(struct_name string, field_name s
if field.name == field_name {
if field.typ is types.Array {
field_arr := field.typ as types.Array
return t.type_to_c_name(field_arr.elem_type)
found_elem_type = field_arr.elem_type
found = true
}
Comment on lines 1449 to 1453
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Stop searching after first matching field in C elem helper

get_field_array_elem_c_name has the same regression pattern: when field_name matches but the field is not an array, the function no longer returns and continues scanning other scopes. If another module has a same-named struct with an array field of that name, this can return the wrong element C type and stamp an incorrect ArrayInitExpr.typ, producing invalid transformed code for the actual target struct.

Useful? React with 👍 / 👎.

return ''
break
}
}
}
}
}
if found {
break
}
}
}
return ''
if !found {
return ''
}
return t.type_to_c_name(found_elem_type)
}

// type_to_name converts a Type to its name string
Expand Down
Loading