v.checker,v.gen.c: extend auto heap mechanism to objects used as interfaces (#10529)

pull/10533/head
Uwe Krüger 2021-06-21 06:10:10 +02:00 committed by GitHub
parent 5d138768e9
commit 8f2f377cb1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 162 additions and 37 deletions

View File

@ -179,7 +179,6 @@ pub:
pos token.Position pos token.Position
type_pos token.Position type_pos token.Position
comments []Comment comments []Comment
default_expr Expr
has_default_expr bool has_default_expr bool
attrs []Attr attrs []Attr
is_pub bool is_pub bool
@ -187,6 +186,7 @@ pub:
is_mut bool is_mut bool
is_global bool is_global bool
pub mut: pub mut:
default_expr Expr
default_expr_typ Type default_expr_typ Type
name string name string
typ Type typ Type
@ -283,12 +283,12 @@ pub mut:
pub struct StructInitField { pub struct StructInitField {
pub: pub:
expr Expr
pos token.Position pos token.Position
name_pos token.Position name_pos token.Position
comments []Comment comments []Comment
next_comments []Comment next_comments []Comment
pub mut: pub mut:
expr Expr
name string name string
typ Type typ Type
expected_type Type expected_type Type
@ -449,9 +449,9 @@ pub struct CallArg {
pub: pub:
is_mut bool is_mut bool
share ShareType share ShareType
expr Expr
comments []Comment comments []Comment
pub mut: pub mut:
expr Expr
typ Type typ Type
is_tmp_autofree bool // this tells cgen that a tmp variable has to be used for the arg expression in order to free it after the call is_tmp_autofree bool // this tells cgen that a tmp variable has to be used for the arg expression in order to free it after the call
pos token.Position pos token.Position
@ -462,9 +462,9 @@ pub mut:
pub struct Return { pub struct Return {
pub: pub:
pos token.Position pos token.Position
exprs []Expr
comments []Comment comments []Comment
pub mut: pub mut:
exprs []Expr
types []Type types []Type
} }
@ -769,13 +769,13 @@ pub mut:
pub struct MatchBranch { pub struct MatchBranch {
pub: pub:
exprs []Expr // left side
ecmnts [][]Comment // inline comments for each left side expr ecmnts [][]Comment // inline comments for each left side expr
stmts []Stmt // right side stmts []Stmt // right side
pos token.Position pos token.Position
is_else bool is_else bool
post_comments []Comment // comments below ´... }´ post_comments []Comment // comments below ´... }´
pub mut: pub mut:
exprs []Expr // left side
scope &Scope scope &Scope
} }
@ -1093,11 +1093,11 @@ pub:
// `string(x,y)`, while skipping the real pointer casts like `&string(x)`. // `string(x,y)`, while skipping the real pointer casts like `&string(x)`.
pub struct CastExpr { pub struct CastExpr {
pub: pub:
expr Expr // `buf` in `string(buf, n)` arg Expr // `n` in `string(buf, n)`
arg Expr // `n` in `string(buf, n)` typ Type // `string` TODO rename to `type_to_cast_to`
typ Type // `string` TODO rename to `type_to_cast_to` pos token.Position
pos token.Position
pub mut: pub mut:
expr Expr // `buf` in `string(buf, n)`
typname string // TypeSymbol.name typname string // TypeSymbol.name
expr_type Type // `byteptr` expr_type Type // `byteptr`
has_arg bool has_arg bool

View File

@ -585,8 +585,17 @@ pub fn (mut c Checker) struct_decl(mut decl ast.StructDecl) {
} }
struct_sym.info.fields[i].default_expr_typ = field_expr_type struct_sym.info.fields[i].default_expr_typ = field_expr_type
c.check_expected(field_expr_type, field.typ) or { c.check_expected(field_expr_type, field.typ) or {
if !(sym.kind == .interface_ if sym.kind == .interface_
&& c.type_implements(field_expr_type, field.typ, field.pos)) { && c.type_implements(field_expr_type, field.typ, field.pos) {
if !field_expr_type.is_ptr() && !field_expr_type.is_pointer()
&& !c.inside_unsafe {
field_expr_type_sym := c.table.get_type_symbol(field_expr_type)
if field_expr_type_sym.kind != .interface_ {
c.mark_as_referenced(mut &decl.fields[i].default_expr,
true)
}
}
} else {
c.error('incompatible initializer for field `$field.name`: $err.msg', c.error('incompatible initializer for field `$field.name`: $err.msg',
field.default_expr.position()) field.default_expr.position())
} }
@ -793,7 +802,7 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
} }
} }
mut inited_fields := []string{} mut inited_fields := []string{}
for i, field in node.fields { for i, mut field in node.fields {
mut info_field := ast.StructField{} mut info_field := ast.StructField{}
mut embed_type := ast.Type(0) mut embed_type := ast.Type(0)
mut is_embed := false mut is_embed := false
@ -865,7 +874,12 @@ pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type {
} }
expr_type_sym := c.table.get_type_symbol(expr_type) expr_type_sym := c.table.get_type_symbol(expr_type)
if field_type_sym.kind == .interface_ { if field_type_sym.kind == .interface_ {
c.type_implements(expr_type, info_field.typ, field.pos) if c.type_implements(expr_type, info_field.typ, field.pos) {
if !expr_type.is_ptr() && !expr_type.is_pointer()
&& expr_type_sym.kind != .interface_ && !c.inside_unsafe {
c.mark_as_referenced(mut &field.expr, true)
}
}
} else if expr_type != ast.void_type && expr_type_sym.kind != .placeholder { } else if expr_type != ast.void_type && expr_type_sym.kind != .placeholder {
c.check_expected(expr_type, info_field.typ) or { c.check_expected(expr_type, info_field.typ) or {
c.error('cannot assign to field `$info_field.name`: $err.msg', c.error('cannot assign to field `$info_field.name`: $err.msg',
@ -1255,7 +1269,12 @@ pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type {
if left_value_sym.kind == .interface_ { if left_value_sym.kind == .interface_ {
if right_final.kind != .array { if right_final.kind != .array {
// []Animal << Cat // []Animal << Cat
c.type_implements(right_type, left_value_type, right_pos) if c.type_implements(right_type, left_value_type, right_pos) {
if !right_type.is_ptr() && !right_type.is_pointer() && !c.inside_unsafe
&& right_sym.kind != .interface_ {
c.mark_as_referenced(mut &node.right, true)
}
}
} else { } else {
// []Animal << []Cat // []Animal << []Cat
c.type_implements(c.table.value_type(right_type), left_value_type, c.type_implements(c.table.value_type(right_type), left_value_type,
@ -1903,7 +1922,7 @@ pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type {
// } // }
// call_expr.args << method.args[0].typ // call_expr.args << method.args[0].typ
// call_expr.exp_arg_types << method.args[0].typ // call_expr.exp_arg_types << method.args[0].typ
for i, arg in call_expr.args { for i, mut arg in call_expr.args {
if i > 0 || exp_arg_typ == ast.Type(0) { if i > 0 || exp_arg_typ == ast.Type(0) {
exp_arg_typ = if method.is_variadic && i >= method.params.len - 1 { exp_arg_typ = if method.is_variadic && i >= method.params.len - 1 {
method.params[method.params.len - 1].typ method.params[method.params.len - 1].typ
@ -1933,7 +1952,14 @@ pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type {
} }
// Handle expected interface // Handle expected interface
if final_arg_sym.kind == .interface_ { if final_arg_sym.kind == .interface_ {
c.type_implements(got_arg_typ, exp_arg_typ, arg.expr.position()) if c.type_implements(got_arg_typ, exp_arg_typ, arg.expr.position()) {
if !got_arg_typ.is_ptr() && !got_arg_typ.is_pointer() && !c.inside_unsafe {
got_arg_typ_sym := c.table.get_type_symbol(got_arg_typ)
if got_arg_typ_sym.kind != .interface_ {
c.mark_as_referenced(mut &arg.expr, true)
}
}
}
continue continue
} }
if method.generic_names.len > 0 { if method.generic_names.len > 0 {
@ -2466,7 +2492,7 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
call_expr.expected_arg_types << param.typ call_expr.expected_arg_types << param.typ
} }
} }
for i, call_arg in call_expr.args { for i, mut call_arg in call_expr.args {
param := if func.is_variadic && i >= func.params.len - 1 { param := if func.is_variadic && i >= func.params.len - 1 {
func.params[func.params.len - 1] func.params[func.params.len - 1]
} else { } else {
@ -2521,7 +2547,12 @@ pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type {
} }
// Handle expected interface // Handle expected interface
if final_param_sym.kind == .interface_ { if final_param_sym.kind == .interface_ {
c.type_implements(typ, param.typ, call_arg.expr.position()) if c.type_implements(typ, param.typ, call_arg.expr.position()) {
if !typ.is_ptr() && !typ.is_pointer() && !c.inside_unsafe
&& typ_sym.kind != .interface_ {
c.mark_as_referenced(mut &call_arg.expr, true)
}
}
continue continue
} }
c.check_expected_call_arg(typ, c.unwrap_generic(param.typ), call_expr.language) or { c.check_expected_call_arg(typ, c.unwrap_generic(param.typ), call_expr.language) or {
@ -3086,7 +3117,12 @@ pub fn (mut c Checker) return_stmt(mut node ast.Return) {
continue continue
} }
if exp_typ_sym.kind == .interface_ { if exp_typ_sym.kind == .interface_ {
c.type_implements(got_typ, exp_type, node.pos) if c.type_implements(got_typ, exp_type, node.pos) {
if !got_typ.is_ptr() && !got_typ.is_pointer() && got_typ_sym.kind != .interface_
&& !c.inside_unsafe {
c.mark_as_referenced(mut &node.exprs[i], true)
}
}
continue continue
} }
c.error('cannot use `$got_typ_sym.name` as type `${c.table.type_to_str(exp_type)}` in return argument', c.error('cannot use `$got_typ_sym.name` as type `${c.table.type_to_str(exp_type)}` in return argument',
@ -3653,7 +3689,12 @@ pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
} }
} }
if left_sym.kind == .interface_ { if left_sym.kind == .interface_ {
c.type_implements(right_type, left_type, right.position()) if c.type_implements(right_type, left_type, right.position()) {
if !right_type.is_ptr() && !right_type.is_pointer() && right_sym.kind != .interface_
&& !c.inside_unsafe {
c.mark_as_referenced(mut &node.right[i], true)
}
}
} }
} }
// this needs to run after the assign stmt left exprs have been run through checker // this needs to run after the assign stmt left exprs have been run through checker
@ -3812,7 +3853,7 @@ pub fn (mut c Checker) array_init(mut array_init ast.ArrayInit) ast.Type {
// if expecting_interface_array { // if expecting_interface_array {
// println('ex $c.expected_type') // println('ex $c.expected_type')
// } // }
for i, expr in array_init.exprs { for i, mut expr in array_init.exprs {
typ := c.check_expr_opt_call(expr, c.expr(expr)) typ := c.check_expr_opt_call(expr, c.expr(expr))
array_init.expr_types << typ array_init.expr_types << typ
// The first element's type // The first element's type
@ -3822,6 +3863,12 @@ pub fn (mut c Checker) array_init(mut array_init ast.ArrayInit) ast.Type {
c.expected_type = elem_type c.expected_type = elem_type
c.type_implements(typ, elem_type, expr.position()) c.type_implements(typ, elem_type, expr.position())
} }
if !typ.is_ptr() && !typ.is_pointer() && !c.inside_unsafe {
typ_sym := c.table.get_type_symbol(typ)
if typ_sym.kind != .interface_ {
c.mark_as_referenced(mut &expr, true)
}
}
continue continue
} }
// The first element's type // The first element's type
@ -5014,7 +5061,12 @@ pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type {
c.error('cannot cast `$type_name` to struct', node.pos) c.error('cannot cast `$type_name` to struct', node.pos)
} }
} else if to_type_sym.kind == .interface_ { } else if to_type_sym.kind == .interface_ {
c.type_implements(node.expr_type, node.typ, node.pos) if c.type_implements(node.expr_type, node.typ, node.pos) {
if !node.expr_type.is_ptr() && !node.expr_type.is_pointer()
&& from_type_sym.kind != .interface_ && !c.inside_unsafe {
c.mark_as_referenced(mut &node.expr, true)
}
}
} else if node.typ == ast.bool_type && !c.inside_unsafe { } else if node.typ == ast.bool_type && !c.inside_unsafe {
c.error('cannot cast to bool - use e.g. `some_int != 0` instead', node.pos) c.error('cannot cast to bool - use e.g. `some_int != 0` instead', node.pos)
} else if node.expr_type == ast.none_type && !node.typ.has_flag(.optional) { } else if node.expr_type == ast.none_type && !node.typ.has_flag(.optional) {
@ -5529,7 +5581,7 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym
for branch_i, _ in node.branches { for branch_i, _ in node.branches {
mut branch := node.branches[branch_i] mut branch := node.branches[branch_i]
mut expr_types := []ast.TypeNode{} mut expr_types := []ast.TypeNode{}
for expr in branch.exprs { for k, expr in branch.exprs {
mut key := '' mut key := ''
if expr is ast.RangeExpr { if expr is ast.RangeExpr {
mut low := i64(0) mut low := i64(0)
@ -5599,7 +5651,14 @@ fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSym
// Current solution is to move expr.position() to its own statement // Current solution is to move expr.position() to its own statement
// c.type_implements(expr_type, c.expected_type, expr.position()) // c.type_implements(expr_type, c.expected_type, expr.position())
expr_pos := expr.position() expr_pos := expr.position()
c.type_implements(expr_type, c.expected_type, expr_pos) if c.type_implements(expr_type, c.expected_type, expr_pos) {
if !expr_type.is_ptr() && !expr_type.is_pointer() && !c.inside_unsafe {
expr_type_sym := c.table.get_type_symbol(expr_type)
if expr_type_sym.kind != .interface_ {
c.mark_as_referenced(mut &branch.exprs[k], true)
}
}
}
} else if mut cond_type_sym.info is ast.SumType { } else if mut cond_type_sym.info is ast.SumType {
if expr_type !in cond_type_sym.info.variants { if expr_type !in cond_type_sym.info.variants {
expr_str := c.table.type_to_str(expr_type) expr_str := c.table.type_to_str(expr_type)
@ -6388,7 +6447,7 @@ pub fn (mut c Checker) postfix_expr(mut node ast.PostfixExpr) ast.Type {
return typ return typ
} }
pub fn (mut c Checker) mark_as_referenced(mut node ast.Expr) { pub fn (mut c Checker) mark_as_referenced(mut node ast.Expr, as_interface bool) {
match mut node { match mut node {
ast.Ident { ast.Ident {
if mut node.obj is ast.Var { if mut node.obj is ast.Var {
@ -6404,7 +6463,12 @@ pub fn (mut c Checker) mark_as_referenced(mut node ast.Expr) {
'wrapping the `$type_sym.name` object in a `struct` declared as `[heap]`' 'wrapping the `$type_sym.name` object in a `struct` declared as `[heap]`'
} }
if !c.pref.translated { if !c.pref.translated {
c.error('`$node.name` cannot be referenced outside `unsafe` blocks as it might be stored on stack. Consider ${suggestion}.', mischief := if as_interface {
'used as interface object'
} else {
'referenced'
}
c.error('`$node.name` cannot be $mischief outside `unsafe` blocks as it might be stored on stack. Consider ${suggestion}.',
node.pos) node.pos)
} }
} else if type_sym.kind == .array_fixed { } else if type_sym.kind == .array_fixed {
@ -6427,11 +6491,11 @@ pub fn (mut c Checker) mark_as_referenced(mut node ast.Expr) {
} }
ast.SelectorExpr { ast.SelectorExpr {
if !node.expr_type.is_ptr() { if !node.expr_type.is_ptr() {
c.mark_as_referenced(mut &node.expr) c.mark_as_referenced(mut &node.expr, as_interface)
} }
} }
ast.IndexExpr { ast.IndexExpr {
c.mark_as_referenced(mut &node.left) c.mark_as_referenced(mut &node.left, as_interface)
} }
else {} else {}
} }
@ -6496,12 +6560,12 @@ pub fn (mut c Checker) prefix_expr(mut node ast.PrefixExpr) ast.Type {
} }
} }
if !c.inside_fn_arg && !c.inside_unsafe { if !c.inside_fn_arg && !c.inside_unsafe {
c.mark_as_referenced(mut &node.right) c.mark_as_referenced(mut &node.right, false)
} }
return right_type.to_ptr() return right_type.to_ptr()
} else if node.op == .amp && node.right !is ast.CastExpr { } else if node.op == .amp && node.right !is ast.CastExpr {
if !c.inside_fn_arg && !c.inside_unsafe { if !c.inside_fn_arg && !c.inside_unsafe {
c.mark_as_referenced(mut &node.right) c.mark_as_referenced(mut &node.right, false)
} }
if node.right.is_auto_deref_var() { if node.right.is_auto_deref_var() {
return right_type return right_type

View File

@ -2422,7 +2422,7 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
is_auto_heap = left.obj.is_auto_heap is_auto_heap = left.obj.is_auto_heap
} }
} }
styp := if ident.name in g.defer_vars { '' } else { g.typ(var_type) } styp := g.typ(var_type)
mut is_fixed_array_init := false mut is_fixed_array_init := false
mut has_val := false mut has_val := false
match val { match val {
@ -2580,9 +2580,11 @@ fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
if is_inside_ternary { if is_inside_ternary {
g.out.write_string(util.tabs(g.indent - g.inside_ternary)) g.out.write_string(util.tabs(g.indent - g.inside_ternary))
} }
g.write('$styp ') if ident.name !in g.defer_vars {
if is_auto_heap { g.write('$styp ')
g.write('*') if is_auto_heap {
g.write('*')
}
} }
} }
if left is ast.Ident || left is ast.SelectorExpr { if left is ast.Ident || left is ast.SelectorExpr {

View File

@ -286,11 +286,17 @@ fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
if var.name in fargs || var.kind == .constant { if var.name in fargs || var.kind == .constant {
continue continue
} }
if var.info is ast.IdentVar { if var.kind == .variable {
info := var.info
if var.name !in g.defer_vars { if var.name !in g.defer_vars {
g.defer_vars << var.name g.defer_vars << var.name
g.writeln('${g.typ(info.typ)} $var.name;') mut deref := ''
if v := var.scope.find_var(var.name) {
if v.is_auto_heap {
deref = '*'
}
}
info := var.obj as ast.Var
g.writeln('${g.typ(info.typ)}$deref $var.name;')
} }
} }
} }

View File

@ -0,0 +1,53 @@
// declare interface
interface MyInterface {
val() int
}
// define struct type
struct St {
mut:
n int
}
// make the struct type implement the interface
fn (x St) val() int {
return x.n
}
fn owerwrite_stack() f64 {
a := 12.5
b := 3.5
c := a + b
return c
}
// nothing special so far, but now some functions that return an interfaces
// these used to cause memory corruptions, but work with this PR:
fn gen_interface() MyInterface {
x := St{
n: -123
} // `x`will be allocated on heap
return x // because an interface object is returned here that contains the address of x
}
fn return_interface(x St) MyInterface {
// x will be copied to stack (requires #10528)
return x // because it's address is returned inside the interface
}
fn test_gen_interface() {
i1 := gen_interface()
d := owerwrite_stack()
assert i1.val() == -123
assert d == 16.0
}
fn test_convert_to_interface() {
x := St{
n: 5
}
i2 := return_interface(x)
d := owerwrite_stack()
assert i2.val() == 5
assert d == 16.0
}