vxx / vlib / v / tests / interfaces / interface_smartcast_field_access_test.v
116 lines · 98 sloc · 2.94 KB · 493890af91c07e6190f5cc63ab8a05c7bb528000
Raw
1// Tests for interface smart-cast cgen bugs in field access and variable
2// declaration paths.
3//
4// 1. Interface→interface smart-cast: `parent.id` where parent is smart-cast
5// from one interface to another. Cgen used to emit
6// `(I_X_as_I_Y(parent)->id)` (`->` on a struct value) instead of
7// `(I_X_as_I_Y(parent).id)`.
8//
9// 2. Variable declared from a smart-cast selector should take the smart-cast
10// type, not the original interface. `mut dd := w.face` inside
11// `if mut w.face is Concrete` was being typed as the original interface,
12// so embedded-field access (e.g. `dd.context.x`) crashed in cgen.
13//
14// 3. Auto-deref of a `mut` method receiver passed as `voidptr` to a generic
15// method was emitting `*f` instead of `f` because the voidptr parameter
16// type carried the `.generic` flag during cgen, defeating the
17// `voidptr_type !in [...]` early-return.
18import eventbus
19
20// ── (1) interface→interface smart-cast field access ────────────────────────
21
22interface Foo {
23 id string
24}
25
26interface Bar {
27 id string
28}
29
30struct ImplFB {
31 id string
32}
33
34fn check_iface_to_iface(parent Foo) string {
35 if parent is Bar {
36 return parent.id
37 }
38 return ''
39}
40
41fn test_interface_to_interface_smartcast_field_access() {
42 i := &ImplFB{
43 id: 'iface_to_iface'
44 }
45 assert check_iface_to_iface(i) == 'iface_to_iface'
46}
47
48// ── (2) selector smart-cast var declaration ────────────────────────────────
49
50struct Inner {
51 value string
52}
53
54struct ConcreteWithEmbed {
55 Inner
56 tag string
57}
58
59interface IFace {}
60
61struct WrapIF {
62mut:
63 face IFace
64}
65
66fn check_selector_smartcast_var(mut w WrapIF) string {
67 if mut w.face is ConcreteWithEmbed {
68 // `dd` should be ConcreteWithEmbed, not IFace, so accessing the
69 // embedded `Inner.value` field works.
70 dd := w.face
71 return '${dd.tag}:${dd.value}'
72 }
73 return ''
74}
75
76fn test_selector_smartcast_var_declaration() {
77 mut c := ConcreteWithEmbed{
78 Inner: Inner{
79 value: 'inner_v'
80 }
81 tag: 'tag_v'
82 }
83 mut w := WrapIF{
84 face: &c
85 }
86 assert check_selector_smartcast_var(mut w) == 'tag_v:inner_v'
87}
88
89// ── (3) auto-deref of mut receiver passed as voidptr to a generic method ───
90
91struct Counter {
92mut:
93 got_self int
94 hits int
95}
96
97fn counter_handler(receiver voidptr, args voidptr, sender voidptr) {
98 mut c := unsafe { &Counter(receiver) }
99 c.hits++
100}
101
102fn (mut c Counter) wire(mut bus eventbus.EventBus[string]) {
103 // Bare `c` here. The receiver is a `voidptr` parameter on a generic
104 // method, and the bug emitted `*c` (struct value) for the C call.
105 bus.subscriber.subscribe_method('hit', counter_handler, c)
106 c.got_self = unsafe { int(i64(voidptr(c)) & 0xffffffff) }
107}
108
109fn test_voidptr_mut_receiver_in_generic_method() {
110 mut c := &Counter{}
111 mut bus := eventbus.new[string]()
112 c.wire(mut bus)
113 bus.publish('hit', unsafe { nil }, unsafe { nil })
114 bus.publish('hit', unsafe { nil }, unsafe { nil })
115 assert c.hits == 2
116}
117