~mna/snow unlisted

155472820e1c8e96487b7258d3320fcccae2b2a7 — Martin Angers 9 months ago af9a050
pkg/semantic: notes on how to address ref methods in interfaces
A pkg/codegen/testdata/fn_interface_with_ref_method.snow => pkg/codegen/testdata/fn_interface_with_ref_method.snow +23 -0
@@ 0,0 1,23 @@
interface I {
  fn foo()
}

struct S {
  var i: int 
  ref fn foo() {
    i = i + 1
  }
}

fn main() {
  let s = S(i: 0)
  # this shouldn't even work as s is immutable, can't get its ref method
  let iface: I = s
  iface.foo()
  println(s.i)
}

@extern(import: "fmt", symbol: "Println")
fn println(v: int)

#=1

M pkg/semantic/semantic.go => pkg/semantic/semantic.go +6 -0
@@ 396,6 396,12 @@ type Ident struct {
// type.
type ImplicitConv struct {
	Value Expr
	// MethodMapping is set when the ImplicitConv targets an interface. It maps
	// the methods of the target interface as keys to the methods of the Value
	// expression (which has necessarily either a *StructType or an *InterfaceType
	// at the moment) as values.
	MethodMapping map[*Fn]*Fn

	commonExpr
}


M pkg/semantic/typecheck_pass.go => pkg/semantic/typecheck_pass.go +20 -8
@@ 189,7 189,7 @@ func (t *typecheckVisitor) Visit(n Node) Visitor {
				return nil
			}
			if !T.IdenticalTo(n.Type()) {
				n.Value = createImplicitConv(n.Value, n.Type())
				n.Value = t.createImplicitConv(n.Value, n.Type())
			}
		}
		var T Type


@@ 262,7 262,7 @@ func (t *typecheckVisitor) Visit(n Node) Visitor {
			}
			// insert implicit conversion if required
			if !valt.IdenticalTo(retType) {
				n.Value = createImplicitConv(n.Value, retType)
				n.Value = t.createImplicitConv(n.Value, retType)
			}
			return nil
		}


@@ 297,7 297,7 @@ func (t *typecheckVisitor) Visit(n Node) Visitor {
		}
		// insert implicit conversion if required
		if !rt.IdenticalTo(lt) {
			n.Right = createImplicitConv(n.Right, lt)
			n.Right = t.createImplicitConv(n.Right, lt)
		}

	case *ExprStmt:


@@ 373,9 373,9 @@ func (t *typecheckVisitor) Visit(n Node) Visitor {
		// converted operand is always the smaller one
		lsz, rsz := basicKindSizes[lt.Kind], basicKindSizes[rt.Kind]
		if lsz < rsz {
			n.Left = createImplicitConv(n.Left, rt)
			n.Left = t.createImplicitConv(n.Left, rt)
		} else if rsz < lsz {
			n.Right = createImplicitConv(n.Right, lt)
			n.Right = t.createImplicitConv(n.Right, lt)
		}

	case *Unary:


@@ 654,7 654,7 @@ func (t *typecheckVisitor) typecheckStructInit(n *Call) {
		}
		// insert implicit conversion if required
		if !argt.IdenticalTo(typ) {
			n.Args[i] = createImplicitConv(arg, typ)
			n.Args[i] = t.createImplicitConv(arg, typ)
		}
	}



@@ 734,7 734,7 @@ func (t *typecheckVisitor) typecheckFnCall(n *Call) {
		}
		// insert implicit conversion if required
		if !argt.IdenticalTo(st.Params[i]) {
			n.Args[i] = createImplicitConv(arg, st.Params[i])
			n.Args[i] = t.createImplicitConv(arg, st.Params[i])
		}
	}



@@ 780,12 780,24 @@ func asIdentDeclRef(expr Expr) Decl {
	return ref
}

func createImplicitConv(expr Expr, toType Type) *ImplicitConv {
func (t *typecheckVisitor) createImplicitConv(expr Expr, toType Type) *ImplicitConv {
	// TODO: store the mapping of methods when converting to an interface type
	// TODO: this might be a good place too to typecheck e.g. that expr has the right
	// type context to access a ref method.
	// TODO: maybe have `mightBeSatisfiedBy` when checking AssignableTo, and then here
	// use the more thorough isSatisfiedBy, which builds the method mapping, and then here
	// we can check with the type context if it is ok.
	var ms map[*Fn]*Fn
	if it := AsInterfaceType(toType); it != nil {
		ms = make(map[*Fn]*Fn, len(it.Decl.Methods))
	}

	var ic ImplicitConv
	ic.pos = expr.Pos()
	ic.scope = expr.Scope()
	ic.Value = expr
	ic.typ = toType
	ic.ctx = Value
	ic.MethodMapping = ms
	return &ic
}