-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
v2: fix transformer deadlock on Linux caused by nested lock on shared scopes #26655
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
|
|
@@ -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 | ||
|
|
@@ -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 { | ||
|
|
@@ -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 | ||
| } | ||
| 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 { | ||
|
|
@@ -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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
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 | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
get_field_array_elem_sumtype_namenow keeps iterating scopes when it findsfield_namebut that field is not an array, becausefoundis only set inside thetypes.Arraybranch. 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 👍 / 👎.