From 4058fd8c4474558871614b96a0d0b7accfe27d64 Mon Sep 17 00:00:00 2001 From: Nicholas Wiersma Date: Fri, 3 Jul 2020 10:28:51 +0200 Subject: [PATCH] fix: do type check on assignment (#738) * fix: do type check on assignment * fix: check for recursive type rather than field --- _test/assign15.go | 11 +++++++++++ interp/cfg.go | 17 +++++++++++++++++ interp/interp_consistent_test.go | 1 + interp/type.go | 6 +++++- 4 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 _test/assign15.go diff --git a/_test/assign15.go b/_test/assign15.go new file mode 100644 index 00000000..5810c300 --- /dev/null +++ b/_test/assign15.go @@ -0,0 +1,11 @@ +package main + +func main() { + var c chan<- struct{} = make(chan struct{}) + var d <-chan struct{} = c + + _ = d +} + +// Error: +// _test/assign15.go:5:26: cannot use type chan<- struct{} as type <-chan struct{} in assignment diff --git a/interp/cfg.go b/interp/cfg.go index 7a7b34c9..add502be 100644 --- a/interp/cfg.go +++ b/interp/cfg.go @@ -518,6 +518,22 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) { err = src.cfgErrorf("invalid float truncate") return } + + // TODO: Rudimentary type check at this point, + // improvements need to be made to make it better. + switch { + case dest.typ.untyped || src.typ.untyped: + // Both side of the assignment must be typed. + case isRecursiveType(dest.typ, dest.typ.rtype) || isRecursiveType(src.typ, src.typ.rtype): + // Recursive types cannot be type checked. + case t0.Kind() == reflect.Interface || t0.Kind() == reflect.Func: + // We have no way of checking interfaces and functions. + case t1.AssignableTo(t0): + // All is well when they are assignable. + default: + err = src.cfgErrorf("cannot use type %s as type %s in assignment", src.typ.id(), dest.typ.id()) + return + } } n.findex = dest.findex n.level = dest.level @@ -1157,6 +1173,7 @@ func (interp *Interpreter) cfg(root *node, pkgID string) ([]*node, error) { case n.ident == "iota": n.rval = reflect.ValueOf(sc.iota) n.kind = basicLit + n.typ.untyped = true case n.ident == "nil": n.kind = basicLit case sym.kind == binSym: diff --git a/interp/interp_consistent_test.go b/interp/interp_consistent_test.go index 590513fb..a24cc06a 100644 --- a/interp/interp_consistent_test.go +++ b/interp/interp_consistent_test.go @@ -35,6 +35,7 @@ func TestInterpConsistencyBuild(t *testing.T) { if filepath.Ext(file.Name()) != ".go" || file.Name() == "assign11.go" || // expect error file.Name() == "assign12.go" || // expect error + file.Name() == "assign15.go" || // expect error file.Name() == "bad0.go" || // expect error file.Name() == "const9.go" || // expect error file.Name() == "export1.go" || // non-main package diff --git a/interp/type.go b/interp/type.go index af58b9de..f08ac796 100644 --- a/interp/type.go +++ b/interp/type.go @@ -1013,7 +1013,11 @@ func (t *itype) id() (res string) { case arrayT: res = "[" + strconv.Itoa(t.size) + "]" + t.val.id() case chanT: - res = "<-" + t.val.id() + res = "chan " + t.val.id() + case chanSendT: + res = "chan<- " + t.val.id() + case chanRecvT: + res = "<-chan " + t.val.id() case funcT: res = "func(" for _, t := range t.arg {