Skip to content

Commit 09062b4

Browse files
authored
checker, cgen: fix three interface regressions (#27960)
1 parent d8abccb commit 09062b4

15 files changed

Lines changed: 610 additions & 6 deletions

vlib/v/ast/table.v

Lines changed: 158 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,165 @@ pub fn (t &Table) fn_type_source_signature(f &Fn) string {
268268
return sig
269269
}
270270

271+
fn fn_type_calling_convention(f &Fn) string {
272+
for attr in f.attrs {
273+
if attr.name == 'callconv' {
274+
return attr.arg
275+
}
276+
}
277+
return 'cdecl'
278+
}
279+
280+
fn (t &Table) fn_types_are_compatible(left &Fn, right &Fn, depth int) bool {
281+
if left.params.len != right.params.len || left.is_variadic != right.is_variadic
282+
|| left.is_c_variadic != right.is_c_variadic
283+
|| fn_type_calling_convention(left) != fn_type_calling_convention(right) {
284+
return false
285+
}
286+
if depth >= max_alias_chain_depth {
287+
return false
288+
}
289+
for i, param in left.params {
290+
right_param := right.params[i]
291+
if param.is_mut != right_param.is_mut || param.is_shared != right_param.is_shared
292+
|| param.is_atomic != right_param.is_atomic
293+
|| !t.fn_type_components_are_compatible(param.typ, right_param.typ, depth + 1) {
294+
return false
295+
}
296+
}
297+
return t.fn_type_components_are_compatible(left.return_type, right.return_type, depth + 1)
298+
}
299+
300+
fn (t &Table) fn_type_components_are_compatible(left_type Type, right_type Type, depth int) bool {
301+
if left_type == right_type {
302+
return true
303+
}
304+
left := t.fully_unaliased_type(left_type)
305+
right := t.fully_unaliased_type(right_type)
306+
if left.has_option_or_result() || right.has_option_or_result() {
307+
return false
308+
}
309+
if left == right {
310+
return true
311+
}
312+
if depth >= max_alias_chain_depth {
313+
return false
314+
}
315+
if left.flags() != right.flags() {
316+
return false
317+
}
318+
left_sym := t.sym(left)
319+
right_sym := t.sym(right)
320+
if left_sym.kind != right_sym.kind {
321+
return false
322+
}
323+
if left_sym.info is FnType {
324+
if right_sym.info !is FnType {
325+
return false
326+
}
327+
left_info := left_sym.info as FnType
328+
right_info := right_sym.info as FnType
329+
return t.fn_types_are_compatible(left_info.func, right_info.func, depth)
330+
}
331+
if left_sym.info is Array {
332+
if right_sym.info !is Array {
333+
return false
334+
}
335+
left_info := left_sym.info as Array
336+
right_info := right_sym.info as Array
337+
return left_info.nr_dims == right_info.nr_dims
338+
&& t.fn_type_components_are_compatible(left_info.elem_type, right_info.elem_type, depth + 1)
339+
}
340+
if left_sym.info is ArrayFixed {
341+
if right_sym.info !is ArrayFixed {
342+
return false
343+
}
344+
left_info := left_sym.info as ArrayFixed
345+
right_info := right_sym.info as ArrayFixed
346+
return left_info.size == right_info.size
347+
&& t.fn_type_components_are_compatible(left_info.elem_type, right_info.elem_type, depth + 1)
348+
}
349+
if left_sym.info is Map {
350+
if right_sym.info !is Map {
351+
return false
352+
}
353+
left_info := left_sym.info as Map
354+
right_info := right_sym.info as Map
355+
return
356+
t.fn_type_components_are_compatible(left_info.key_type, right_info.key_type, depth + 1)
357+
&& t.fn_type_components_are_compatible(left_info.value_type, right_info.value_type, depth + 1)
358+
}
359+
if left_sym.info is Chan {
360+
if right_sym.info !is Chan {
361+
return false
362+
}
363+
left_info := left_sym.info as Chan
364+
right_info := right_sym.info as Chan
365+
return left_info.is_mut == right_info.is_mut
366+
&& t.fn_type_components_are_compatible(left_info.elem_type, right_info.elem_type, depth + 1)
367+
}
368+
if left_sym.info is Thread {
369+
if right_sym.info !is Thread {
370+
return false
371+
}
372+
left_info := left_sym.info as Thread
373+
right_info := right_sym.info as Thread
374+
return_type_matches := t.fn_type_components_are_compatible(left_info.return_type,
375+
right_info.return_type, depth + 1)
376+
return return_type_matches
377+
}
378+
if left_sym.info is MultiReturn {
379+
if right_sym.info !is MultiReturn {
380+
return false
381+
}
382+
left_info := left_sym.info as MultiReturn
383+
right_info := right_sym.info as MultiReturn
384+
if left_info.types.len != right_info.types.len {
385+
return false
386+
}
387+
for i, typ in left_info.types {
388+
if !t.fn_type_components_are_compatible(typ, right_info.types[i], depth + 1) {
389+
return false
390+
}
391+
}
392+
return true
393+
}
394+
if left_sym.info is GenericInst {
395+
if right_sym.info !is GenericInst {
396+
return false
397+
}
398+
left_info := left_sym.info as GenericInst
399+
right_info := right_sym.info as GenericInst
400+
if left_info.parent_idx != right_info.parent_idx
401+
|| left_info.concrete_types.len != right_info.concrete_types.len {
402+
return false
403+
}
404+
for i, typ in left_info.concrete_types {
405+
if !t.fn_type_components_are_compatible(typ, right_info.concrete_types[i], depth + 1) {
406+
return false
407+
}
408+
}
409+
return true
410+
}
411+
return false
412+
}
413+
271414
pub fn (t &Table) is_same_method(f &Fn, func &Fn) string {
272-
if f.return_type != func.return_type {
415+
mut same_return_type := f.return_type == func.return_type
416+
if !same_return_type {
417+
f_return_type := t.fully_unaliased_type(f.return_type)
418+
func_return_type := t.fully_unaliased_type(func.return_type)
419+
if !f_return_type.has_option_or_result() && !func_return_type.has_option_or_result()
420+
&& f_return_type.nr_muls() == func_return_type.nr_muls() {
421+
f_return_sym := t.sym(f_return_type)
422+
func_return_sym := t.sym(func_return_type)
423+
if f_return_sym.info is FnType && func_return_sym.info is FnType {
424+
same_return_type = t.fn_types_are_compatible(f_return_sym.info.func,
425+
func_return_sym.info.func, 0)
426+
}
427+
}
428+
}
429+
if !same_return_type {
273430
s := t.type_to_str(f.return_type)
274431
return 'expected return type `${s}`'
275432
}

vlib/v/checker/containers.v

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1169,6 +1169,8 @@ fn (mut c Checker) check_append(mut node ast.InfixExpr, left_type ast.Type, righ
11691169
}
11701170
}
11711171
if left_value_sym.kind == .interface {
1172+
is_empty_interface := left_value_sym.info is ast.Interface
1173+
&& left_value_sym.info.methods.len == 0 && left_value_sym.info.fields.len == 0
11721174
if right is ast.ArrayInit && right.is_fixed {
11731175
c.error('cannot append `${right_sym.name}` to `${left_sym.name}`', right_pos)
11741176
return ast.void_type
@@ -1177,7 +1179,7 @@ fn (mut c Checker) check_append(mut node ast.InfixExpr, left_type ast.Type, righ
11771179
left_value_type)
11781180
if right_is_interface_value {
11791181
if !right_type.is_any_kind_of_pointer() && !c.inside_unsafe
1180-
&& right_sym.kind != .interface {
1182+
&& right_sym.kind != .interface && !is_empty_interface {
11811183
c.mark_as_referenced(mut &node.right, true)
11821184
}
11831185
} else if right_final_sym.kind == .array {
@@ -1187,7 +1189,7 @@ fn (mut c Checker) check_append(mut node ast.InfixExpr, left_type ast.Type, righ
11871189
// []Animal << Cat
11881190
if c.type_implements(right_type, left_value_type, right_pos) {
11891191
if !right_type.is_any_kind_of_pointer() && !c.inside_unsafe
1190-
&& right_sym.kind != .interface {
1192+
&& right_sym.kind != .interface && !is_empty_interface {
11911193
c.mark_as_referenced(mut &node.right, true)
11921194
}
11931195
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
vlib/v/checker/tests/interface_method_fn_type_alias_callconv_mismatch_err.vv:20:7: error: `CdeclProvider` incorrectly implements method `callback` of interface `CallbackProvider`: expected return type `fn (int) int`
2+
18 |
3+
19 | fn main() {
4+
20 | _ := CallbackProvider(CdeclProvider{})
5+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6+
21 | }
7+
Details: main.CallbackProvider has `fn callback(x main.CallbackProvider) fn (int) int`
8+
main.CdeclProvider has `fn callback(_ main.CdeclProvider) fn (int) int`
9+
vlib/v/checker/tests/interface_method_fn_type_alias_callconv_mismatch_err.vv:20:7: error: `CdeclProvider` does not implement interface `CallbackProvider`, cannot cast `CdeclProvider` to interface `CallbackProvider`
10+
18 |
11+
19 | fn main() {
12+
20 | _ := CallbackProvider(CdeclProvider{})
13+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14+
21 | }
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
@[callconv: stdcall]
2+
type StdcallCallback = fn (int) int
3+
4+
@[callconv: cdecl]
5+
type CdeclCallback = fn (int) int
6+
7+
interface CallbackProvider {
8+
callback() StdcallCallback
9+
}
10+
11+
struct CdeclProvider {}
12+
13+
fn (_ CdeclProvider) callback() CdeclCallback {
14+
return fn (value int) int {
15+
return value
16+
}
17+
}
18+
19+
fn main() {
20+
_ := CallbackProvider(CdeclProvider{})
21+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
vlib/v/checker/tests/interface_method_fn_type_nested_option_result_alias_mismatch_err.vv:36:7: error: `OptionalIntCallbackProvider` incorrectly implements method `callback` of interface `OptionalCallbackProvider`: expected return type `fn () ?MyInt`
2+
34 |
3+
35 | fn main() {
4+
36 | _ := OptionalCallbackProvider(OptionalIntCallbackProvider{})
5+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6+
37 | _ := ResultCallbackProvider(ResultIntCallbackProvider{})
7+
38 | }
8+
Details: main.OptionalCallbackProvider has `fn callback(x main.OptionalCallbackProvider) fn () ?main.MyInt`
9+
main.OptionalIntCallbackProvider has `fn callback(_ main.OptionalIntCallbackProvider) fn () ?int`
10+
vlib/v/checker/tests/interface_method_fn_type_nested_option_result_alias_mismatch_err.vv:36:7: error: `OptionalIntCallbackProvider` does not implement interface `OptionalCallbackProvider`, cannot cast `OptionalIntCallbackProvider` to interface `OptionalCallbackProvider`
11+
34 |
12+
35 | fn main() {
13+
36 | _ := OptionalCallbackProvider(OptionalIntCallbackProvider{})
14+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
15+
37 | _ := ResultCallbackProvider(ResultIntCallbackProvider{})
16+
38 | }
17+
vlib/v/checker/tests/interface_method_fn_type_nested_option_result_alias_mismatch_err.vv:37:7: error: `ResultIntCallbackProvider` incorrectly implements method `callback` of interface `ResultCallbackProvider`: expected return type `fn () !MyInt`
18+
35 | fn main() {
19+
36 | _ := OptionalCallbackProvider(OptionalIntCallbackProvider{})
20+
37 | _ := ResultCallbackProvider(ResultIntCallbackProvider{})
21+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
22+
38 | }
23+
Details: main.ResultCallbackProvider has `fn callback(x main.ResultCallbackProvider) fn () !main.MyInt`
24+
main.ResultIntCallbackProvider has `fn callback(_ main.ResultIntCallbackProvider) fn () !int`
25+
vlib/v/checker/tests/interface_method_fn_type_nested_option_result_alias_mismatch_err.vv:37:7: error: `ResultIntCallbackProvider` does not implement interface `ResultCallbackProvider`, cannot cast `ResultIntCallbackProvider` to interface `ResultCallbackProvider`
26+
35 | fn main() {
27+
36 | _ := OptionalCallbackProvider(OptionalIntCallbackProvider{})
28+
37 | _ := ResultCallbackProvider(ResultIntCallbackProvider{})
29+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
30+
38 | }
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
type MyInt = int
2+
3+
type OptionalAliasCallback = fn () ?MyInt
4+
5+
type OptionalIntCallback = fn () ?int
6+
7+
interface OptionalCallbackProvider {
8+
callback() OptionalAliasCallback
9+
}
10+
11+
struct OptionalIntCallbackProvider {}
12+
13+
fn (_ OptionalIntCallbackProvider) callback() OptionalIntCallback {
14+
return fn () ?int {
15+
return 1
16+
}
17+
}
18+
19+
type ResultAliasCallback = fn () !MyInt
20+
21+
type ResultIntCallback = fn () !int
22+
23+
interface ResultCallbackProvider {
24+
callback() ResultAliasCallback
25+
}
26+
27+
struct ResultIntCallbackProvider {}
28+
29+
fn (_ ResultIntCallbackProvider) callback() ResultIntCallback {
30+
return fn () !int {
31+
return 2
32+
}
33+
}
34+
35+
fn main() {
36+
_ := OptionalCallbackProvider(OptionalIntCallbackProvider{})
37+
_ := ResultCallbackProvider(ResultIntCallbackProvider{})
38+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
vlib/v/checker/tests/interface_method_nested_fn_type_alias_callconv_mismatch_err.vv:28:7: error: `CdeclFactoryProvider` incorrectly implements method `factory` of interface `FactoryProvider`: expected return type `fn () fn (int) int`
2+
26 |
3+
27 | fn main() {
4+
28 | _ := FactoryProvider(CdeclFactoryProvider{})
5+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6+
29 | }
7+
Details: main.FactoryProvider has `fn factory(x main.FactoryProvider) fn () fn (int) int`
8+
main.CdeclFactoryProvider has `fn factory(_ main.CdeclFactoryProvider) fn () fn (int) int`
9+
vlib/v/checker/tests/interface_method_nested_fn_type_alias_callconv_mismatch_err.vv:28:7: error: `CdeclFactoryProvider` does not implement interface `FactoryProvider`, cannot cast `CdeclFactoryProvider` to interface `FactoryProvider`
10+
26 |
11+
27 | fn main() {
12+
28 | _ := FactoryProvider(CdeclFactoryProvider{})
13+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14+
29 | }
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
@[callconv: stdcall]
2+
type StdcallNestedCallback = fn (int) int
3+
4+
@[callconv: cdecl]
5+
type CdeclNestedCallback = fn (int) int
6+
7+
@[callconv: cdecl]
8+
type StdcallFactory = fn () StdcallNestedCallback
9+
10+
@[callconv: cdecl]
11+
type CdeclFactory = fn () CdeclNestedCallback
12+
13+
interface FactoryProvider {
14+
factory() StdcallFactory
15+
}
16+
17+
struct CdeclFactoryProvider {}
18+
19+
fn (_ CdeclFactoryProvider) factory() CdeclFactory {
20+
return fn () CdeclNestedCallback {
21+
return fn (value int) int {
22+
return value
23+
}
24+
}
25+
}
26+
27+
fn main() {
28+
_ := FactoryProvider(CdeclFactoryProvider{})
29+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
vlib/v/checker/tests/interface_method_option_result_alias_return_mismatch_err.vv:24:7: error: `OptionalIntProvider` incorrectly implements method `value` of interface `OptionalProvider`: expected return type `?MyInt`
2+
22 |
3+
23 | fn main() {
4+
24 | _ := OptionalProvider(OptionalIntProvider{})
5+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
6+
25 | _ := ResultProvider(ResultIntProvider{})
7+
26 | }
8+
Details: main.OptionalProvider has `fn value(x main.OptionalProvider) ?main.MyInt`
9+
main.OptionalIntProvider has `fn value(_ main.OptionalIntProvider) ?int`
10+
vlib/v/checker/tests/interface_method_option_result_alias_return_mismatch_err.vv:24:7: error: `OptionalIntProvider` does not implement interface `OptionalProvider`, cannot cast `OptionalIntProvider` to interface `OptionalProvider`
11+
22 |
12+
23 | fn main() {
13+
24 | _ := OptionalProvider(OptionalIntProvider{})
14+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
15+
25 | _ := ResultProvider(ResultIntProvider{})
16+
26 | }
17+
vlib/v/checker/tests/interface_method_option_result_alias_return_mismatch_err.vv:25:7: error: `ResultIntProvider` incorrectly implements method `value` of interface `ResultProvider`: expected return type `!MyInt`
18+
23 | fn main() {
19+
24 | _ := OptionalProvider(OptionalIntProvider{})
20+
25 | _ := ResultProvider(ResultIntProvider{})
21+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
22+
26 | }
23+
Details: main.ResultProvider has `fn value(x main.ResultProvider) !main.MyInt`
24+
main.ResultIntProvider has `fn value(_ main.ResultIntProvider) !int`
25+
vlib/v/checker/tests/interface_method_option_result_alias_return_mismatch_err.vv:25:7: error: `ResultIntProvider` does not implement interface `ResultProvider`, cannot cast `ResultIntProvider` to interface `ResultProvider`
26+
23 | fn main() {
27+
24 | _ := OptionalProvider(OptionalIntProvider{})
28+
25 | _ := ResultProvider(ResultIntProvider{})
29+
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
30+
26 | }

0 commit comments

Comments
 (0)