Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
struct Model {}

struct Process {
mut:
on_done ?fn ()
}

fn (m Model) run(mut p Process) bool {
p.on_done = m.on_done
return p.do_work()
}

fn (m Model) on_done() {}

fn (p Process) do_work() bool {
if f := p.on_done {
f()
return true
}
return false
}

fn test_optional_void_callback_with_same_named_method() {
m := Model{}
mut p := Process{}
assert !p.do_work()
assert m.run(mut p)
assert p.do_work()
p.on_done = none
assert !p.do_work()
}

struct ValueModel {
value int
}

struct ValueProcess {
mut:
on_value ?fn () int
}

fn (m ValueModel) run(mut p ValueProcess) int {
p.on_value = m.on_value
return p.do_work()
}

fn (m ValueModel) on_value() int {
return m.value
}

fn (p ValueProcess) do_work() int {
if f := p.on_value {
return f()
}
return -1
}

fn test_optional_callback_preserves_receiver_with_same_named_method() {
first := ValueModel{
value: 42
}
second := ValueModel{
value: 17
}
mut p := ValueProcess{}
assert p.do_work() == -1
assert first.run(mut p) == 42
assert p.do_work() == 42
assert second.run(mut p) == 17
assert p.do_work() == 17
p.on_value = none
assert p.do_work() == -1
}
39 changes: 39 additions & 0 deletions vlib/v/transform/selector_method_field_collision_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module transform

import v.flat

fn test_known_struct_selector_does_not_inherit_unrelated_field_type() {
for receiver_type in ['Model', '&Model', 'Unregistered'] {
mut a := flat.FlatAst.new()
receiver := a.add_val(.ident, 'm')
start := a.children.len
a.children << receiver
selector := a.add_node(flat.Node{
kind: .selector
value: 'on_done'
children_start: start
children_count: 1
})
t := Transformer{
a: &a
cur_module: 'main'
var_types: [
VarTypeBinding{
name: 'm'
typ: receiver_type
},
]
structs: {
'Model': StructInfo{}
}
unique_fields: {
'on_done': '?fn ()'
}
}
// Model has no on_done field: a method selector must not acquire the
// optional callback type belonging to another struct. Keep the fallback
// for receivers whose struct information is unavailable.
expected := if receiver_type == 'Unregistered' { '?fn ()' } else { '' }
assert t.resolve_selector_type(a.nodes[int(selector)]) == expected
}
}
4 changes: 4 additions & 0 deletions vlib/v/transform/type_propagation.v
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,10 @@ fn (t &Transformer) resolve_selector_type_uncached(node flat.Node) string {
}
}
}
// A selector on a known struct may be a bound method. Do not infer its
// type from a same-named field on an unrelated struct; let the checker
// supply the method's function type instead.
return ''
Comment on lines +777 to +780

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 Preserve type resolution through nested embeddings

When a field is promoted through two or more embedded structs, this return incorrectly classifies it as a possible method and produces no type. embedded_field_for_promoted_field in vlib/v/transform/struct.v checks only whether the immediate embedded struct directly declares the field, so a valid selector such as Baz.x in vlib/v/tests/structs/nested_struct_embed_selector_test.v (Baz -> Bar -> Foo.x) reaches this branch; before this change, the unique-field fallback returned int. Direct callers of resolve_selector_type, including specialized array/loop inference paths, can consequently lose the field type when a checker annotation is unavailable, so the known-struct early return should occur only after a recursive promoted-field lookup.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Addressed in ff12b02.

Replaced the one-level promoted-field lookup with the existing recursive struct_field_path_for_field helper, then resolve the field type from the final owning struct. Pointer embeddings are unwrapped for the owner lookup, and the known-struct guard still prevents borrowing an unrelated same-named field's type.

Added a checker-free regression covering eight combinations of value/pointer receivers, value/pointer embeddings, and scalar/array fields, plus nearer-field and receiver-field shadowing. The original method/field collision regression remains in place.

Verified that the committed diff matches the prepared two-file patch. V compilation, formatting, and runtime tests remain unrun locally: the sandbox has no V compiler, and bootstrap downloads fail DNS resolution. Leaving the thread open for re-review.

}
if ftyp := t.lookup_unique_field_type(field_name) {
return ftyp
Expand Down
Loading