M pkg/semantic/semantic.go => pkg/semantic/semantic.go +3 -6
@@ 55,6 55,7 @@ type Typed interface {
Node
Type() Type
TypeContext() TypeContext
+ setType(T Type)
}
type Expr interface {
@@ 71,6 72,7 @@ type commonExpr struct {
func (c commonExpr) Type() Type { return c.typ }
func (c commonExpr) TypeContext() TypeContext { return c.ctx }
func (c commonExpr) expr() {}
+func (c *commonExpr) setType(T Type) { c.typ = T }
type Decl interface {
Typed
@@ 110,6 112,7 @@ func (c commonDecl) Ident() string { return c.ident }
func (c commonDecl) Type() Type { return c.typ }
func (c commonDecl) IsGeneric() bool { return false }
func (c commonDecl) decl() {}
+func (c *commonDecl) setType(T Type) { c.typ = T }
func AsFnDecl(n Node) *Fn {
if fn, ok := n.(*Fn); ok {
@@ 396,12 399,6 @@ 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/testdata/check/interface_satisfied.snow.err => pkg/semantic/testdata/check/interface_satisfied.snow.err +7 -5
@@ 1,5 1,7 @@
-testdata/interface_satisfied.snow:35:11: cannot assign type int to variable of type interface Empty
-testdata/interface_satisfied.snow:38:7: cannot assign type struct S2 to variable of type interface Foo
-testdata/interface_satisfied.snow:39:7: cannot assign type struct S3 to variable of type interface Foo
-testdata/interface_satisfied.snow:47:7: type struct S4 does not satisfy type interface Foo: missing [foo] (ref methods can only be used in a mutable context)
-testdata/interface_satisfied.snow:49:7: type struct S4 does not satisfy type interface Foo: missing [foo] (ref methods can only be used in a mutable context)
+testdata/interface_satisfied.snow:39:11: cannot assign type int to variable of type interface Empty
+testdata/interface_satisfied.snow:42:7: cannot assign type struct S2 to variable of type interface Foo
+testdata/interface_satisfied.snow:43:7: cannot assign type struct S3 to variable of type interface Foo
+testdata/interface_satisfied.snow:51:7: cannot assign type struct S4 to variable of type interface Foo
+testdata/interface_satisfied.snow:53:7: cannot assign type struct S4 to variable of type interface Foo
+testdata/interface_satisfied.snow:59:9: cannot assign type (int, struct S4) to variable of type (int, interface Foo)
+testdata/interface_satisfied.snow:63:10: cannot assign type (int, struct S4, (bool, struct S4)) to variable of type (int, interface Foo, (bool, interface Foo))
M pkg/semantic/testdata/check/interface_satisfied.snow.want => pkg/semantic/testdata/check/interface_satisfied.snow.want +77 -7
@@ 1,6 1,12 @@
-file testdata/interface_satisfied.snow [0, 1, 4, 2]
+file testdata/interface_satisfied.snow [0, 2, 4, 2]
+ fn get_s4 [let: () -> struct S4]
+ ident S4 [type: struct S4]
+ block [1]
+ return
+ call [0] [value: struct S4]
+ ident S4 [type: struct S4]
fn main [let: () -> void]
- block [16]
+ block [25]
var: empty [var: interface Empty]
ident Empty [type: interface Empty]
var: f [var: interface Foo]
@@ 48,13 54,77 @@ file testdata/interface_satisfied.snow [0, 1, 4, 2]
ident vs4 [var: struct S4]
assign
ident f [var: interface Foo]
- implicit conv [value: interface Foo]
- ident ls4 [let: struct S4]
+ ident ls4 [let: struct S4]
assign
ident f [var: interface Foo]
- implicit conv [value: interface Foo]
- call [0] [value: struct S4]
- ident S4 [type: struct S4]
+ call [0] [value: struct S4]
+ ident S4 [type: struct S4]
+ var: tup [var: (int, interface Foo)]
+ tuple type [2] [type: (int, interface Foo)]
+ ident int [type: int]
+ ident Foo [type: interface Foo]
+ assign
+ ident tup [var: (int, interface Foo)]
+ implicit conv [value: (int, interface Foo)]
+ tuple value [2] [value: (int, struct S4)]
+ int [1] [const: int]
+ ident vs4 [var: struct S4]
+ assign
+ ident tup [var: (int, interface Foo)]
+ tuple value [2] [value: (int, struct S4)]
+ int [1] [const: int]
+ ident ls4 [let: struct S4]
+ var: tup2 [var: (int, interface Foo, (bool, interface Foo))]
+ tuple type [3] [type: (int, interface Foo, (bool, interface Foo))]
+ ident int [type: int]
+ ident Foo [type: interface Foo]
+ tuple type [2] [type: (bool, interface Foo)]
+ ident bool [type: bool]
+ ident Foo [type: interface Foo]
+ assign
+ ident tup2 [var: (int, interface Foo, (bool, interface Foo))]
+ implicit conv [value: (int, interface Foo, (bool, interface Foo))]
+ tuple value [3] [value: (int, struct S4, (bool, struct S4))]
+ int [1] [const: int]
+ ident vs4 [var: struct S4]
+ tuple value [2] [value: (bool, struct S4)]
+ ident true [let: bool]
+ ident vs4 [var: struct S4]
+ assign
+ ident tup2 [var: (int, interface Foo, (bool, interface Foo))]
+ tuple value [3] [value: (int, struct S4, (bool, struct S4))]
+ int [1] [const: int]
+ ident ls4 [let: struct S4]
+ tuple value [2] [value: (bool, struct S4)]
+ ident true [let: bool]
+ call [0] [value: struct S4]
+ ident get_s4 [let: () -> struct S4]
+ var: tup3 [var: (int, (interface Foo, interface Empty))]
+ tuple type [2] [type: (int, (interface Foo, interface Empty))]
+ ident int [type: int]
+ tuple type [2] [type: (interface Foo, interface Empty)]
+ ident Foo [type: interface Foo]
+ paren [type: interface Empty]
+ ident Empty [type: interface Empty]
+ assign
+ ident tup3 [var: (int, (interface Foo, interface Empty))]
+ implicit conv [value: (int, (interface Foo, interface Empty))]
+ tuple value [2] [value: (int, (struct S4, struct S4))]
+ int [3] [const: int]
+ tuple value [2] [value: (struct S4, struct S4)]
+ ident vs4 [var: struct S4]
+ paren [let: struct S4]
+ ident ls4 [let: struct S4]
+ assign
+ ident tup3 [var: (int, (interface Foo, interface Empty))]
+ implicit conv [value: (int, (interface Foo, interface Empty))]
+ tuple value [2] [value: (int, (struct S4, struct S4))]
+ int [3] [const: int]
+ tuple value [2] [value: (struct S4, struct S4)]
+ ident vs4 [var: struct S4]
+ paren [value: struct S4]
+ call [0] [value: struct S4]
+ ident get_s4 [let: () -> struct S4]
struct S1 [0, 2, 0, 0] [type: struct S1]
fn foo [let: (int) -> int]
let: x [let: int]
M pkg/semantic/testdata/interface_satisfied.snow => pkg/semantic/testdata/interface_satisfied.snow +3 -2
@@ 59,9 59,10 @@ fn main() {
tup = (1, ls4)
var tup2: (int, Foo, (bool, Foo))
- tup2 = (1, vs4, (true, vs4))
- tup2 = (1, ls4, (true, get_s4()))
+ tup2 = (1, vs4, (true, vs4)) # works, var s4 in both Foo places
+ tup2 = (1, ls4, (true, get_s4())) # fails, immutable S4 and value S4
+ # those all work (var S4 for Foo, and whatever for Empty)
var tup3: (int, (Foo, (Empty)))
tup3 = (3, (vs4, (ls4)))
tup3 = (3, (vs4, (get_s4())))
M pkg/semantic/testdata/scopes/interface_satisfied.snow.want => pkg/semantic/testdata/scopes/interface_satisfied.snow.want +1 -0
@@ 88,6 88,7 @@
. . . s1
. . . tup
. . . tup2
+. . . tup3
. . . vs4
. . }
. }
M pkg/semantic/testdata/static/interface_satisfied.snow.err => pkg/semantic/testdata/static/interface_satisfied.snow.err +7 -5
@@ 1,5 1,7 @@
-testdata/interface_satisfied.snow:35:11: cannot assign type int to variable of type interface Empty
-testdata/interface_satisfied.snow:38:7: cannot assign type struct S2 to variable of type interface Foo
-testdata/interface_satisfied.snow:39:7: cannot assign type struct S3 to variable of type interface Foo
-testdata/interface_satisfied.snow:47:7: type struct S4 does not satisfy type interface Foo: missing [foo] (ref methods can only be used in a mutable context)
-testdata/interface_satisfied.snow:49:7: type struct S4 does not satisfy type interface Foo: missing [foo] (ref methods can only be used in a mutable context)
+testdata/interface_satisfied.snow:39:11: cannot assign type int to variable of type interface Empty
+testdata/interface_satisfied.snow:42:7: cannot assign type struct S2 to variable of type interface Foo
+testdata/interface_satisfied.snow:43:7: cannot assign type struct S3 to variable of type interface Foo
+testdata/interface_satisfied.snow:51:7: cannot assign type struct S4 to variable of type interface Foo
+testdata/interface_satisfied.snow:53:7: cannot assign type struct S4 to variable of type interface Foo
+testdata/interface_satisfied.snow:59:9: cannot assign type (int, struct S4) to variable of type (int, interface Foo)
+testdata/interface_satisfied.snow:63:10: cannot assign type (int, struct S4, (bool, struct S4)) to variable of type (int, interface Foo, (bool, interface Foo))
M pkg/semantic/testdata/static/interface_satisfied.snow.want => pkg/semantic/testdata/static/interface_satisfied.snow.want +25 -12
@@ 1,12 1,25 @@
-testdata/interface_satisfied.snow:32:11: empty
-testdata/interface_satisfied.snow:33:11: f
-testdata/interface_satisfied.snow:34:11: s1
-testdata/interface_satisfied.snow:35:11: integer
-testdata/interface_satisfied.snow:37:7: S1
-testdata/interface_satisfied.snow:38:7: S2
-testdata/interface_satisfied.snow:39:7: S3
-testdata/interface_satisfied.snow:41:13: S4
-testdata/interface_satisfied.snow:42:13: S4
-testdata/interface_satisfied.snow:45:7: vs4
-testdata/interface_satisfied.snow:47:7: ls4
-testdata/interface_satisfied.snow:49:7: S4
+testdata/interface_satisfied.snow:27:10: S4
+testdata/interface_satisfied.snow:36:11: empty
+testdata/interface_satisfied.snow:37:11: f
+testdata/interface_satisfied.snow:38:11: s1
+testdata/interface_satisfied.snow:39:11: integer
+testdata/interface_satisfied.snow:41:7: S1
+testdata/interface_satisfied.snow:42:7: S2
+testdata/interface_satisfied.snow:43:7: S3
+testdata/interface_satisfied.snow:45:13: S4
+testdata/interface_satisfied.snow:46:13: S4
+testdata/interface_satisfied.snow:49:7: vs4
+testdata/interface_satisfied.snow:51:7: ls4
+testdata/interface_satisfied.snow:53:7: S4
+testdata/interface_satisfied.snow:57:13: vs4
+testdata/interface_satisfied.snow:59:13: ls4
+testdata/interface_satisfied.snow:62:14: vs4
+testdata/interface_satisfied.snow:62:20: true
+testdata/interface_satisfied.snow:62:26: vs4
+testdata/interface_satisfied.snow:63:14: ls4
+testdata/interface_satisfied.snow:63:20: true
+testdata/interface_satisfied.snow:63:26: get_s4
+testdata/interface_satisfied.snow:67:15: vs4
+testdata/interface_satisfied.snow:67:21: ls4
+testdata/interface_satisfied.snow:68:15: vs4
+testdata/interface_satisfied.snow:68:21: get_s4
M pkg/semantic/testdata/types/interface_satisfied.snow.want => pkg/semantic/testdata/types/interface_satisfied.snow.want +70 -2
@@ 1,6 1,12 @@
-file testdata/interface_satisfied.snow [0, 1, 4, 2]
+file testdata/interface_satisfied.snow [0, 2, 4, 2]
+ fn get_s4 [let: () -> struct S4]
+ ident S4 [type: struct S4]
+ block [1]
+ return
+ call [0] [value: struct S4]
+ ident S4 [type: struct S4]
fn main [let: () -> void]
- block [16]
+ block [25]
var: empty [var: interface Empty]
ident Empty [type: interface Empty]
var: f [var: interface Foo]
@@ 49,6 55,68 @@ file testdata/interface_satisfied.snow [0, 1, 4, 2]
ident f [var: interface Foo]
call [0] [value: struct S4]
ident S4 [type: struct S4]
+ var: tup [var: (int, interface Foo)]
+ tuple type [2] [type: (int, interface Foo)]
+ ident int [type: int]
+ ident Foo [type: interface Foo]
+ assign
+ ident tup [var: (int, interface Foo)]
+ tuple value [2] [value: (int, struct S4)]
+ int [1] [const: int]
+ ident vs4 [var: struct S4]
+ assign
+ ident tup [var: (int, interface Foo)]
+ tuple value [2] [value: (int, struct S4)]
+ int [1] [const: int]
+ ident ls4 [let: struct S4]
+ var: tup2 [var: (int, interface Foo, (bool, interface Foo))]
+ tuple type [3] [type: (int, interface Foo, (bool, interface Foo))]
+ ident int [type: int]
+ ident Foo [type: interface Foo]
+ tuple type [2] [type: (bool, interface Foo)]
+ ident bool [type: bool]
+ ident Foo [type: interface Foo]
+ assign
+ ident tup2 [var: (int, interface Foo, (bool, interface Foo))]
+ tuple value [3] [value: (int, struct S4, (bool, struct S4))]
+ int [1] [const: int]
+ ident vs4 [var: struct S4]
+ tuple value [2] [value: (bool, struct S4)]
+ ident true [let: bool]
+ ident vs4 [var: struct S4]
+ assign
+ ident tup2 [var: (int, interface Foo, (bool, interface Foo))]
+ tuple value [3] [value: (int, struct S4, (bool, struct S4))]
+ int [1] [const: int]
+ ident ls4 [let: struct S4]
+ tuple value [2] [value: (bool, struct S4)]
+ ident true [let: bool]
+ call [0] [value: struct S4]
+ ident get_s4 [let: () -> struct S4]
+ var: tup3 [var: (int, (interface Foo, interface Empty))]
+ tuple type [2] [type: (int, (interface Foo, interface Empty))]
+ ident int [type: int]
+ tuple type [2] [type: (interface Foo, interface Empty)]
+ ident Foo [type: interface Foo]
+ paren [type: interface Empty]
+ ident Empty [type: interface Empty]
+ assign
+ ident tup3 [var: (int, (interface Foo, interface Empty))]
+ tuple value [2] [value: (int, (struct S4, struct S4))]
+ int [3] [const: int]
+ tuple value [2] [value: (struct S4, struct S4)]
+ ident vs4 [var: struct S4]
+ paren [let: struct S4]
+ ident ls4 [let: struct S4]
+ assign
+ ident tup3 [var: (int, (interface Foo, interface Empty))]
+ tuple value [2] [value: (int, (struct S4, struct S4))]
+ int [3] [const: int]
+ tuple value [2] [value: (struct S4, struct S4)]
+ ident vs4 [var: struct S4]
+ paren [value: struct S4]
+ call [0] [value: struct S4]
+ ident get_s4 [let: () -> struct S4]
struct S1 [0, 2, 0, 0] [type: struct S1]
fn foo [let: (int) -> int]
let: x [let: int]
M pkg/semantic/type.go => pkg/semantic/type.go +17 -54
@@ 59,6 59,11 @@ type (
// Inst is the list of types used in a generic instantiation, if the
// struct declaration is generic.
Inst []Type
+ // Ctx is the type context associated with the value that has that type.
+ // It only plays a role in one specific case: when the value associated
+ // with that type is assigned to an interface type, to check if ref
+ // functions can be used to satisfy that type (if Ctx is mutable).
+ Ctx TypeContext
// lookup cache of GenericType to instantiated type, nil until the first
// call to typeOfSel.
@@ 395,7 400,7 @@ func (s *StructType) AssignableTo(T Type) bool {
case *StructType:
return s.IdenticalTo(T)
case *InterfaceType:
- return T.mightBeSatisfiedBy(s)
+ return T.isSatisfiedBy(s)
}
return false
}
@@ 501,7 506,7 @@ func makeGenericResolveMap(gc *GenericClause, types []Type) map[*GenericType]Typ
// identical).
func (i *InterfaceType) AssignableTo(T Type) bool {
if i2 := AsInterfaceType(T); i2 != nil {
- return i2.mightBeSatisfiedBy(i)
+ return i2.isSatisfiedBy(i)
}
return false
}
@@ 599,22 604,22 @@ func (i *InterfaceType) typeOfSel(T Type) Type {
return T.ResolveGeneric(i.lookup)
}
-// mightBeSatisfiedBy makes incomplete checks that type T satisfies interface
-// i. If false, T definitely does not satisfy i, but if true, it might still
-// not satisfy it. Further checks are done afterwards in the type-check pass.
-func (i *InterfaceType) mightBeSatisfiedBy(T Type) bool {
+func (i *InterfaceType) isSatisfiedBy(T Type) bool {
var (
- tfns []*Fn
- resolve func(Type) Type
+ tfns []*Fn
+ resolve func(Type) Type
+ allowRef bool
)
switch T := T.(type) {
case *StructType:
tfns = T.Decl.Fns
resolve = T.typeOfSel
+ allowRef = T.Ctx == Mutable
case *InterfaceType:
tfns = T.Decl.Methods
resolve = T.typeOfSel
+ allowRef = false // cannot have ref methods on interface anyway
default:
return false
}
@@ 628,6 633,10 @@ func (i *InterfaceType) mightBeSatisfiedBy(T Type) bool {
for _, fn := range tfns {
// if this fn is part of the required methods set, check if the types match
if reqt := req[fn.Ident()]; reqt != nil {
+ if fn.IsRef && !allowRef {
+ // won't be able to satisfy this method
+ return false
+ }
if !reqt.IdenticalTo(resolve(fn.Type())) {
return false
}
@@ 637,52 646,6 @@ func (i *InterfaceType) mightBeSatisfiedBy(T Type) bool {
return len(req) == 0
}
-// isSatisfiedBy is the complete, thorough interface satisfiability check version of
-// mightBeSatisfiedBy. It is based on both the type T and the type context Tctx, so that
-// ref functions can only be used to satisfy an interface's method if the context allows
-// mutating the corresponding value.
-//
-// It returns true if the interface is satisfied by T and false otherwise. In any case,
-// the mappings map will be filled with the interface's methods and the matching T's
-// method that satisfies this method. If it returns false, then mappings has some
-// nil values.
-func (i *InterfaceType) isSatisfiedBy(T Type, Tctx TypeContext, mappings map[*Fn]*Fn) bool {
- var (
- tfns []*Fn
- resolve func(Type) Type
- )
-
- req := make(map[string]*Fn, len(i.Decl.Methods))
- for _, m := range i.Decl.Methods {
- if mappings != nil {
- mappings[m] = nil
- }
- req[m.Ident()] = m
- }
-
- switch T := T.(type) {
- case *StructType:
- tfns = T.Decl.Fns
- resolve = T.typeOfSel
- default:
- return false
- }
-
- for _, fn := range tfns {
- if ifaceFn := req[fn.Ident()]; ifaceFn != nil {
- if fn.IsRef && Tctx != Mutable {
- continue
- }
- reqt := i.typeOfSel(ifaceFn.Type())
- if reqt.IdenticalTo(resolve(fn.Type())) {
- delete(req, fn.Ident())
- mappings[ifaceFn] = fn
- }
- }
- }
- return len(req) == 0
-}
-
// ========> implement Type for GenericType
func (g *GenericType) String() string { return g.Name }
M pkg/semantic/typeassign_pass.go => pkg/semantic/typeassign_pass.go +6 -0
@@ 318,6 318,12 @@ func (t *typeassignVisitor) Visit(n Node) Visitor {
if typed, ok := n.(Typed); ok {
if !typed.Type().Valid() {
t.addDeferred(n)
+ return nil
+ }
+
+ if st := AsStructType(typed.Type()); st != nil {
+ cloned := &StructType{Decl: st.Decl, Inst: st.Inst, Ctx: typed.TypeContext()}
+ typed.setType(cloned)
}
}
M pkg/semantic/typecheck_pass.go => pkg/semantic/typecheck_pass.go +0 -21
@@ 781,32 781,11 @@ func asIdentDeclRef(expr Expr) Decl {
}
func (t *typecheckVisitor) createImplicitConv(expr Expr, toType Type) *ImplicitConv {
- var ms map[*Fn]*Fn
- // if the expression to convert from is an interface, no need to do the more thorough
- // check if it satisfies the target interface - that's only required for non-interface
- // to interface.
- if it := AsInterfaceType(toType); it != nil && AsInterfaceType(expr.Type()) == nil {
- ms = make(map[*Fn]*Fn, len(it.Decl.Methods))
- if !it.isSatisfiedBy(expr.Type(), expr.TypeContext(), ms) {
- // due to the weaker mightBeSatisfiedBy check done earlier, the only unsatisfied methods
- // will be because the corresponding method is a ref and the type context is not mutable.
- var missing []string
- for k, v := range ms {
- if v == nil {
- missing = append(missing, k.Ident())
- }
- }
- sort.Strings(missing)
- t.errh(expr.Pos(), fmt.Sprintf("type %s does not satisfy type %s: missing %v (ref methods can only be used in a mutable context)", expr.Type(), toType, missing))
- }
- }
-
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
}