Skip to content
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

checker, cgen: fix interface deref from generic #20675

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions vlib/v/ast/table.v
Original file line number Diff line number Diff line change
Expand Up @@ -1371,6 +1371,11 @@ pub fn (t &Table) is_interface_var(var ScopeObject) bool {
&& t.sym(var.smartcasts.last()).kind != .interface_
}

@[inline]
pub fn (t &Table) is_generic_smartcast(var ScopeObject) bool {
return var is Var && var.smartcasts.len > 0 && t.sym(var.smartcasts.last()).kind == .any
}

// only used for debugging V compiler type bugs
pub fn (t &Table) known_type_names() []string {
mut res := []string{cap: t.type_idxs.len}
Expand Down
10 changes: 5 additions & 5 deletions vlib/v/checker/checker.v
Original file line number Diff line number Diff line change
Expand Up @@ -1020,11 +1020,11 @@ fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos to
// `none` "implements" the Error interface
return true
}
if typ_sym.kind == .interface_ && inter_sym.kind == .interface_ && !styp.starts_with('JS.')
&& !inter_sym.name.starts_with('JS.') {
c.error('cannot implement interface `${inter_sym.name}` with a different interface `${styp}`',
pos)
}
// if typ_sym.kind == .interface_ && inter_sym.kind == .interface_ && !styp.starts_with('JS.')
// && !inter_sym.name.starts_with('JS.') {
// c.error('cannot implement interface `${inter_sym.name}` with a different interface `${styp}`',
// pos)
// }
imethods := if inter_sym.kind == .interface_ {
(inter_sym.info as ast.Interface).methods
} else {
Expand Down
2 changes: 1 addition & 1 deletion vlib/v/checker/if.v
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,7 @@ fn (mut c Checker) smartcast_if_conds(mut node ast.Expr, mut scope ast.Scope) {
}
}
if right_type != ast.Type(0) {
right_sym := c.table.sym(right_type)
right_sym := c.table.sym(c.unwrap_generic(right_type))
mut expr_type := c.unwrap_generic(node.left_type)
left_sym := c.table.sym(expr_type)
if left_sym.kind == .aggregate {
Expand Down
2 changes: 1 addition & 1 deletion vlib/v/checker/infix.v
Original file line number Diff line number Diff line change
Expand Up @@ -666,7 +666,7 @@ fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
}
}
if typ != ast.Type(0) {
typ_sym := c.table.sym(typ)
typ_sym := c.table.sym(c.unwrap_generic(typ))
op := node.op.str()
if typ_sym.kind == .placeholder {
c.error('${op}: type `${typ_sym.name}` does not exist', right_expr.pos())
Expand Down
6 changes: 0 additions & 6 deletions vlib/v/checker/tests/interface_implementing_interface.out
Original file line number Diff line number Diff line change
@@ -1,6 +0,0 @@
vlib/v/checker/tests/interface_implementing_interface.vv:15:10: error: cannot implement interface `Thing` with a different interface `Animal`
13 | dog := Dog{}
14 | animal := Animal(dog)
15 | thing := Thing(animal)
| ~~~~~~~~~~~~~
16 | println(thing)
25 changes: 21 additions & 4 deletions vlib/v/gen/c/cgen.v
Original file line number Diff line number Diff line change
Expand Up @@ -2461,6 +2461,20 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
false, got_styp)
}
return
} else if got_sym.info is ast.Interface && exp_sym.info is ast.Interface && expr is ast.Ident
&& got_type.idx() != expected_type.idx() && !got_type.is_ptr()
&& !expected_type.has_flag(.result) && !g.inside_struct_init {
g.inside_cast_in_heap++
got_styp := g.cc_type(got_type.ref(), true)
exp_styp := exp_sym.cname
mut fname := 'I_${got_styp}_to_Interface_${exp_styp}'
if exp_sym.info.is_generic {
fname = g.generic_fn_name(exp_sym.info.concrete_types, fname)
}
g.call_cfn_for_casting_expr(fname, expr, expected_is_ptr, exp_styp, true, false,
got_styp)
g.inside_cast_in_heap--
return
}
// cast to sum type
exp_styp := g.typ(expected_type)
Expand Down Expand Up @@ -2565,10 +2579,13 @@ fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_typ
&& expr !is ast.InfixExpr {
got_deref_type := got_type.deref()
deref_sym := g.table.sym(got_deref_type)
deref_will_match := expected_type in [got_type, got_deref_type, deref_sym.parent_idx]
got_is_opt_or_res := got_type.has_option_or_result()
if deref_will_match || got_is_opt_or_res || expr.is_auto_deref_var() {
g.write('*')
if !(expr is ast.Ident && (expr as ast.Ident).obj is ast.Var
&& g.table.is_generic_smartcast((expr as ast.Ident).obj) && exp_sym.kind == .interface_) {
deref_will_match := expected_type in [got_type, got_deref_type, deref_sym.parent_idx]
got_is_opt_or_res := got_type.has_option_or_result()
if deref_will_match || got_is_opt_or_res || expr.is_auto_deref_var() {
g.write('*')
}
}
}
if expr is ast.IntegerLiteral {
Expand Down
7 changes: 4 additions & 3 deletions vlib/v/gen/c/infix.v
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,7 @@ fn (mut g Gen) infix_expr_is_op(node ast.InfixExpr) {
parent_left_type := (left_sym.info as ast.Aggregate).sum_type
left_sym = g.table.sym(parent_left_type)
}
right_sym := g.table.sym(node.right_type)
right_sym := g.table.sym(g.unwrap_generic(node.right_type))
if left_sym.kind == .interface_ && right_sym.kind == .interface_ {
g.gen_interface_is_op(node)
return
Expand Down Expand Up @@ -736,15 +736,16 @@ fn (mut g Gen) infix_expr_is_op(node ast.InfixExpr) {

fn (mut g Gen) gen_interface_is_op(node ast.InfixExpr) {
mut left_sym := g.table.sym(node.left_type)
right_sym := g.table.sym(node.right_type)
right_type := g.unwrap_generic(node.right_type)
right_sym := g.table.sym(right_type)

mut info := left_sym.info as ast.Interface

common_variants := info.conversions[node.right_type] or {
left_variants := g.table.iface_types[left_sym.name]
right_variants := g.table.iface_types[right_sym.name]
c := left_variants.filter(it in right_variants)
info.conversions[node.right_type] = c
info.conversions[right_type] = c
c
}
left_sym.info = info
Expand Down
43 changes: 43 additions & 0 deletions vlib/v/tests/smartcast_interface_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
interface IModel {
model string
}

interface ICar {
name string
}

pub struct Car {
name string
model string
}

fn test_main() {
mut cars := []ICar{}
cars << Car{
model: 'Tesla'
}
cars << Car{
model: 'Toyota'
}

mut cc := []IModel{}
for mut c in cars {
if mut c is IModel {
cc << c
}
}
assert cc.len == 2

a := get_all[IModel](mut cars)
assert a.len == 2
}

pub fn get_all[T](mut cars []ICar) []T {
mut cc := []T{}
for mut c in cars {
if mut c is T {
cc << c
}
}
return cc
}
Loading