diff --git a/go/types/check_test.go b/go/types/check_test.go index 3f6ed83181..21ad03c58e 100644 --- a/go/types/check_test.go +++ b/go/types/check_test.go @@ -45,6 +45,7 @@ var ( // Each tests entry is list of files belonging to the same package. var tests = [][]string{ + {"testdata/cycles.src"}, {"testdata/decls0.src"}, {"testdata/decls1.src"}, {"testdata/decls2a.src", "testdata/decls2b.src"}, @@ -205,7 +206,7 @@ func checkFiles(t *testing.T, testfiles []string) { ctxt.Check(pkgName, fset, files...) if *listErrors { - t.Errorf("--- %s: %d errors found:", pkgName, len(errlist)) + t.Errorf("--- %s: %d errors found", pkgName, len(errlist)) for _, err := range errlist { t.Error(err) } diff --git a/go/types/expr.go b/go/types/expr.go index c0d8028255..f69b7e4c9f 100644 --- a/go/types/expr.go +++ b/go/types/expr.go @@ -116,13 +116,13 @@ func (check *checker) collectParams(scope *Scope, list *ast.FieldList, variadicO return } -func (check *checker) collectMethods(list *ast.FieldList) (methods []*Func) { +func (check *checker) collectMethods(list *ast.FieldList, cycleOk bool) (methods []*Func) { if list == nil { return nil } scope := NewScope(nil) for _, f := range list.List { - typ := check.typ(f.Type, len(f.Names) > 0) // cycles are not ok for embedded interfaces + typ := check.typ(f.Type, cycleOk) // the parser ensures that f.Tag is nil and we don't // care if a constructed AST contains a non-nil tag if len(f.Names) > 0 { @@ -140,15 +140,23 @@ func (check *checker) collectMethods(list *ast.FieldList) (methods []*Func) { } } else { // embedded interface - utyp := typ.Underlying() - if ityp, ok := utyp.(*Interface); ok { - for _, m := range ityp.methods { + switch t := typ.Underlying().(type) { + case nil: + // The underlying type is in the process of being defined + // but we need it in order to complete this type. For now + // complain with an "unimplemented" error. This requires + // a bit more work. + // TODO(gri) finish this. + check.errorf(f.Type.Pos(), "reference to incomplete type %s - unimplemented", f.Type) + case *Interface: + for _, m := range t.methods { check.declare(scope, nil, m) methods = append(methods, m) } - } else if utyp != Typ[Invalid] { - // if utyp is invalid, don't complain (the root cause was reported before) - check.errorf(f.Type.Pos(), "%s is not an interface type", typ) + default: + if t != Typ[Invalid] { + check.errorf(f.Type.Pos(), "%s is not an interface type", typ) + } } } } @@ -1042,7 +1050,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle check.errorf(obj.pos, "illegal cycle in declaration of %s", obj.name) x.expr = e x.typ = Typ[Invalid] - return // don't goto Error - need x.mode == typexpr + return // don't goto Error - want x.mode == typexpr } case *Var: x.mode = variable @@ -1494,7 +1502,7 @@ func (check *checker) rawExpr(x *operand, e ast.Expr, hint Type, iota int, cycle case *ast.InterfaceType: x.mode = typexpr - x.typ = NewInterface(check.collectMethods(e.Methods)) + x.typ = NewInterface(check.collectMethods(e.Methods, cycleOk)) case *ast.MapType: x.mode = typexpr diff --git a/go/types/testdata/cycles.src b/go/types/testdata/cycles.src new file mode 100644 index 0000000000..3e10873d01 --- /dev/null +++ b/go/types/testdata/cycles.src @@ -0,0 +1,112 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cycles + +type ( + T0 int + T1 /* ERROR "cycle" */ T1 + T2 *T2 + + T3 /* ERROR "cycle" */ T4 + T4 T5 + T5 T3 + + T6 T7 + T7 *T8 + T8 T6 + + // arrays + A0 /* ERROR "cycle" */ [10]A0 + A1 [10]*A1 + + A2 /* ERROR "cycle" */ [10]A3 + A3 [10]A4 + A4 A2 + + A5 [10]A6 + A6 *A5 + + // slices + L0 []L0 + + // structs + S0 /* ERROR "cycle" */ struct{ _ S0 } + S1 /* ERROR "cycle" */ struct{ S1 } + S2 struct{ _ *S2 } + S3 struct{ *S3 } + + S4 /* ERROR "cycle" */ struct{ S5 } + S5 struct{ S6 } + S6 S4 + + // pointers + P0 *P0 + + // functions + F0 func(F0) + F1 func() F1 + F2 func(F2) F2 + + // interfaces + I0 /* ERROR "cycle" */ interface{ I0 } + + I1 /* ERROR "cycle" */ interface{ I2 } + I2 interface{ I3 } + I3 interface{ I1 } + + I4 interface{ f(I4) } + + // testcase for issue 5090 + // TODO(gri) fix this + I5 interface{ f(I6) } + I6 interface{ I5 /* ERROR "unimplemented" */ } + + // maps + M0 map[M0]M0 + + // channels + C0 chan C0 +) + +func _() { + type ( + t1 /* ERROR "cycle" */ t1 + t2 *t2 + + t3 t4 /* ERROR "undeclared" */ + t4 t5 /* ERROR "undeclared" */ + t5 t3 + + // arrays + a0 /* ERROR "cycle" */ [10]a0 + a1 [10]*a1 + + // slices + l0 []l0 + + // structs + s0 /* ERROR "cycle" */ struct{ _ s0 } + s1 /* ERROR "cycle" */ struct{ s1 } + s2 struct{ _ *s2 } + s3 struct{ *s3 } + + // pointers + p0 *p0 + + // functions + f0 func(f0) + f1 func() f1 + f2 func(f2) f2 + + // interfaces + i0 /* ERROR "cycle" */ interface{ i0 } + + // maps + m0 map[m0]m0 + + // channels + c0 chan c0 + ) +} \ No newline at end of file