From 041e90b2e24877cb7e68282d7573b9a571f11d06 Mon Sep 17 00:00:00 2001 From: yuyi Date: Tue, 19 Jul 2022 21:36:16 +0800 Subject: [PATCH] checker, cgen: fix interface embedding smartcast (fix #13296) (#15127) --- examples/js_dom_cube/cube.js.v | 2 +- examples/js_dom_draw/draw.js.v | 2 +- vlib/gg/gg.js.v | 2 +- vlib/v/checker/checker.v | 6 ++- vlib/v/checker/if.v | 2 - vlib/v/gen/c/cgen.v | 52 ++++++++++++------- .../interface_embedding_smartcast_test.v | 47 +++++++++++++++++ ...interface_with_multi_nested_embed_1_test.v | 4 +- 8 files changed, 89 insertions(+), 28 deletions(-) create mode 100644 vlib/v/tests/interface_embedding_smartcast_test.v diff --git a/examples/js_dom_cube/cube.js.v b/examples/js_dom_cube/cube.js.v index 6f1453bd2..f21e7c2b4 100644 --- a/examples/js_dom_cube/cube.js.v +++ b/examples/js_dom_cube/cube.js.v @@ -197,7 +197,7 @@ const ( amortization = 0.95 ) -fn get_webgl() (&JS.HTMLCanvasElement, JS.WebGLRenderingContext) { +fn get_webgl() (JS.HTMLCanvasElement, JS.WebGLRenderingContext) { JS.console.log(dom.document) elem := dom.document.getElementById('myCanvas'.str) or { panic('cannot get canvas') } match elem { diff --git a/examples/js_dom_draw/draw.js.v b/examples/js_dom_draw/draw.js.v index 7259f2214..1b09ef7ce 100644 --- a/examples/js_dom_draw/draw.js.v +++ b/examples/js_dom_draw/draw.js.v @@ -1,6 +1,6 @@ import js.dom -fn get_canvas(elem JS.HTMLElement) &JS.HTMLCanvasElement { +fn get_canvas(elem JS.HTMLElement) JS.HTMLCanvasElement { match elem { JS.HTMLCanvasElement { return elem diff --git a/vlib/gg/gg.js.v b/vlib/gg/gg.js.v index 21e959326..bacccc7a9 100644 --- a/vlib/gg/gg.js.v +++ b/vlib/gg/gg.js.v @@ -294,7 +294,7 @@ pub mut: // *before* the current event was different } -fn get_canvas(elem JS.HTMLElement) &JS.HTMLCanvasElement { +fn get_canvas(elem JS.HTMLElement) JS.HTMLCanvasElement { match elem { JS.HTMLCanvasElement { return elem diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 441e2c36a..6de3e49ec 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2903,7 +2903,11 @@ pub fn (mut c Checker) concat_expr(mut node ast.ConcatExpr) ast.Type { // smartcast takes the expression with the current type which should be smartcasted to the target type in the given scope fn (mut c Checker) smartcast(expr_ ast.Expr, cur_type ast.Type, to_type_ ast.Type, mut scope ast.Scope) { sym := c.table.sym(cur_type) - to_type := if sym.kind == .interface_ { to_type_.ref() } else { to_type_ } + to_type := if sym.kind == .interface_ && c.table.sym(to_type_).kind != .interface_ { + to_type_.ref() + } else { + to_type_ + } mut expr := unsafe { expr_ } match mut expr { ast.SelectorExpr { diff --git a/vlib/v/checker/if.v b/vlib/v/checker/if.v index e24bc32b7..8b4ecb087 100644 --- a/vlib/v/checker/if.v +++ b/vlib/v/checker/if.v @@ -311,8 +311,6 @@ fn (mut c Checker) smartcast_if_conds(node ast.Expr, mut scope ast.Scope) { if left_sym.kind == .interface_ { if right_sym.kind != .interface_ { c.type_implements(right_type, expr_type, node.pos) - } else { - return } } else if !c.check_types(right_type, expr_type) && left_sym.kind != .sum_type { expect_str := c.table.type_to_str(right_type) diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index c8161c21c..c95d8b383 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -3331,15 +3331,22 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { g.write('*') } cast_sym := g.table.sym(g.unwrap_generic(typ)) - if i != 0 { - dot := if field.typ.is_ptr() { '->' } else { '.' } - sum_type_deref_field += ')$dot' - } - if cast_sym.info is ast.Aggregate { - agg_sym := g.table.sym(cast_sym.info.types[g.aggregate_type_idx]) - sum_type_deref_field += '_$agg_sym.cname' + if field_sym.kind == .interface_ && cast_sym.kind == .interface_ { + ptr := '*'.repeat(field.typ.nr_muls()) + dot := if node.expr_type.is_ptr() { '->' } else { '.' } + g.write('I_${field_sym.cname}_as_I_${cast_sym.cname}($ptr$node.expr$dot$node.field_name))') + return } else { - sum_type_deref_field += '_$cast_sym.cname' + if i != 0 { + dot := if field.typ.is_ptr() { '->' } else { '.' } + sum_type_deref_field += ')$dot' + } + if cast_sym.info is ast.Aggregate { + agg_sym := g.table.sym(cast_sym.info.types[g.aggregate_type_idx]) + sum_type_deref_field += '_$agg_sym.cname' + } else { + sum_type_deref_field += '_$cast_sym.cname' + } } } } @@ -3894,19 +3901,24 @@ fn (mut g Gen) ident(node ast.Ident) { } for i, typ in node.obj.smartcasts { cast_sym := g.table.sym(g.unwrap_generic(typ)) - mut is_ptr := false - if i == 0 { - g.write(name) - if node.obj.orig_type.is_ptr() { - is_ptr = true - } - } - dot := if is_ptr || is_auto_heap { '->' } else { '.' } - if cast_sym.info is ast.Aggregate { - sym := g.table.sym(cast_sym.info.types[g.aggregate_type_idx]) - g.write('${dot}_$sym.cname') + if obj_sym.kind == .interface_ && cast_sym.kind == .interface_ { + ptr := '*'.repeat(node.obj.typ.nr_muls()) + g.write('I_${obj_sym.cname}_as_I_${cast_sym.cname}($ptr$node.name)') } else { - g.write('${dot}_$cast_sym.cname') + mut is_ptr := false + if i == 0 { + g.write(name) + if node.obj.orig_type.is_ptr() { + is_ptr = true + } + } + dot := if is_ptr || is_auto_heap { '->' } else { '.' } + if cast_sym.info is ast.Aggregate { + sym := g.table.sym(cast_sym.info.types[g.aggregate_type_idx]) + g.write('${dot}_$sym.cname') + } else { + g.write('${dot}_$cast_sym.cname') + } } g.write(')') } diff --git a/vlib/v/tests/interface_embedding_smartcast_test.v b/vlib/v/tests/interface_embedding_smartcast_test.v new file mode 100644 index 000000000..5b1d87b79 --- /dev/null +++ b/vlib/v/tests/interface_embedding_smartcast_test.v @@ -0,0 +1,47 @@ +// Common interface to all error custom types. +interface ESpeaker { + IError + speak() string +} + +// One custom error implementation. +struct MyError { + // Mandatory fields from IError. + msg string + code int + // Custom field. + blah string +} + +// Interface implementation for this example custom type. +fn (e MyError) speak() string { + return e.blah +} + +fn (e MyError) msg() string { + return e.msg +} + +fn (e MyError) code() int { + return e.code +} + +// An example function that returns a custom error. +fn foo() ?string { + return IError(MyError{ + msg: 'foo' + blah: 'world' + }) +} + +fn test_interface_embedding_smartcast() { + x := foo() or { + if err is ESpeaker { + err.speak() + } else { + 'undefined' + } + } + println(x) + assert x == 'world' +} diff --git a/vlib/v/tests/interface_with_multi_nested_embed_1_test.v b/vlib/v/tests/interface_with_multi_nested_embed_1_test.v index 3f9d9168a..85d50ae1b 100644 --- a/vlib/v/tests/interface_with_multi_nested_embed_1_test.v +++ b/vlib/v/tests/interface_with_multi_nested_embed_1_test.v @@ -34,8 +34,8 @@ mut: fn (mut w Window) init() { for wd in w.initables { - if mut wd is Container { - mut c := wd as Container + if wd is Container { + mut c := unsafe { wd } c.layout() } } -- 2.30.2