Commit bbd15ff7 authored by Robert Griesemer's avatar Robert Griesemer

go/types: report error when recognizing issue #18395.

The fix (CL 79575) for #18395 is too risky at this stage of the Go 1.10
release process.

Since issue #18395 is easily recognized (but not easily fixed), report
an error instead of silently continuing. This avoids inscrutable follow
on errors.

Also, make sure all empty interfaces are "completed", and adjust
printing code to report incomplete interfaces.

For #18395.

Change-Id: I7fa5f97ff31ac9775c9a6d318fce9f526b0350cd
Reviewed-on: https://go-review.googlesource.com/80455Reviewed-by: default avatarAlan Donovan <adonovan@google.com>
parent 98a8e521
...@@ -45,6 +45,11 @@ func TestTypeParser(t *testing.T) { ...@@ -45,6 +45,11 @@ func TestTypeParser(t *testing.T) {
t.Errorf("expected full parse, stopped at %q", p.lit) t.Errorf("expected full parse, stopped at %q", p.lit)
} }
// interfaces must be explicitly completed
if ityp, _ := typ.(*types.Interface); ityp != nil {
ityp.Complete()
}
got := typ.String() got := typ.String()
if got != test.want { if got != test.want {
t.Errorf("got type %q, expected %q", got, test.want) t.Errorf("got type %q, expected %q", got, test.want)
......
...@@ -470,15 +470,14 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b ...@@ -470,15 +470,14 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
case _Panic: case _Panic:
// panic(x) // panic(x)
T := new(Interface) check.assignment(x, &emptyInterface, "argument to panic")
check.assignment(x, T, "argument to panic")
if x.mode == invalid { if x.mode == invalid {
return return
} }
x.mode = novalue x.mode = novalue
if check.Types != nil { if check.Types != nil {
check.recordBuiltinType(call.Fun, makeSig(nil, T)) check.recordBuiltinType(call.Fun, makeSig(nil, &emptyInterface))
} }
case _Print, _Println: case _Print, _Println:
...@@ -508,7 +507,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b ...@@ -508,7 +507,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
case _Recover: case _Recover:
// recover() interface{} // recover() interface{}
x.mode = value x.mode = value
x.typ = new(Interface) x.typ = &emptyInterface
if check.Types != nil { if check.Types != nil {
check.recordBuiltinType(call.Fun, makeSig(x.typ)) check.recordBuiltinType(call.Fun, makeSig(x.typ))
} }
......
...@@ -32,12 +32,12 @@ func TestIsAlias(t *testing.T) { ...@@ -32,12 +32,12 @@ func TestIsAlias(t *testing.T) {
{NewTypeName(0, nil, "t0", nil), false}, // no type yet {NewTypeName(0, nil, "t0", nil), false}, // no type yet
{NewTypeName(0, pkg, "t0", nil), false}, // no type yet {NewTypeName(0, pkg, "t0", nil), false}, // no type yet
{t1, false}, // type name refers to named type and vice versa {t1, false}, // type name refers to named type and vice versa
{NewTypeName(0, nil, "t2", new(Interface)), true}, // type name refers to unnamed type {NewTypeName(0, nil, "t2", &emptyInterface), true}, // type name refers to unnamed type
{NewTypeName(0, pkg, "t3", n1), true}, // type name refers to named type with different type name {NewTypeName(0, pkg, "t3", n1), true}, // type name refers to named type with different type name
{NewTypeName(0, nil, "t4", Typ[Int32]), true}, // type name refers to basic type with different name {NewTypeName(0, nil, "t4", Typ[Int32]), true}, // type name refers to basic type with different name
{NewTypeName(0, nil, "int32", Typ[Int32]), false}, // type name refers to basic type with same name {NewTypeName(0, nil, "int32", Typ[Int32]), false}, // type name refers to basic type with same name
{NewTypeName(0, pkg, "int32", Typ[Int32]), true}, // type name is declared in user-defined package (outside Universe) {NewTypeName(0, pkg, "int32", Typ[Int32]), true}, // type name is declared in user-defined package (outside Universe)
{NewTypeName(0, nil, "rune", Typ[Rune]), true}, // type name refers to basic type rune which is an alias already {NewTypeName(0, nil, "rune", Typ[Rune]), true}, // type name refers to basic type rune which is an alias already
} { } {
check(test.name, test.alias) check(test.name, test.alias)
} }
......
...@@ -108,3 +108,15 @@ type Element interface { ...@@ -108,3 +108,15 @@ type Element interface {
type Event interface { type Event interface {
Target() Element Target() Element
} }
// Recognize issue #13895.
type (
_ interface{ m(B1) }
A1 interface{ a(D1) }
B1 interface{ A1 }
C1 interface{ B1 /* ERROR issue #18395 */ }
D1 interface{ C1 }
)
var _ A1 = C1 /* ERROR cannot use C1 */ (nil)
\ No newline at end of file
...@@ -246,10 +246,22 @@ type Interface struct { ...@@ -246,10 +246,22 @@ type Interface struct {
allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset) allMethods []*Func // ordered list of methods declared with or embedded in this interface (TODO(gri): replace with mset)
} }
// NewInterface returns a new interface for the given methods and embedded types. // emptyInterface represents the empty (completed) interface
var emptyInterface = Interface{allMethods: markComplete}
// markComplete is used to mark an empty interface as completely
// set up by setting the allMethods field to a non-nil empty slice.
var markComplete = make([]*Func, 0)
// NewInterface returns a new (incomplete) interface for the given methods and embedded types.
// To compute the method set of the interface, Complete must be called.
func NewInterface(methods []*Func, embeddeds []*Named) *Interface { func NewInterface(methods []*Func, embeddeds []*Named) *Interface {
typ := new(Interface) typ := new(Interface)
if len(methods) == 0 && len(embeddeds) == 0 {
return typ
}
var mset objset var mset objset
for _, m := range methods { for _, m := range methods {
if mset.insert(m) != nil { if mset.insert(m) != nil {
......
...@@ -156,6 +156,7 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) { ...@@ -156,6 +156,7 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
// } // }
// //
buf.WriteString("interface{") buf.WriteString("interface{")
empty := true
if gcCompatibilityMode { if gcCompatibilityMode {
// print flattened interface // print flattened interface
// (useful to compare against gc-generated interfaces) // (useful to compare against gc-generated interfaces)
...@@ -165,6 +166,7 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) { ...@@ -165,6 +166,7 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
} }
buf.WriteString(m.name) buf.WriteString(m.name)
writeSignature(buf, m.typ.(*Signature), qf, visited) writeSignature(buf, m.typ.(*Signature), qf, visited)
empty = false
} }
} else { } else {
// print explicit interface methods and embedded types // print explicit interface methods and embedded types
...@@ -174,14 +176,22 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) { ...@@ -174,14 +176,22 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
} }
buf.WriteString(m.name) buf.WriteString(m.name)
writeSignature(buf, m.typ.(*Signature), qf, visited) writeSignature(buf, m.typ.(*Signature), qf, visited)
empty = false
} }
for i, typ := range t.embeddeds { for i, typ := range t.embeddeds {
if i > 0 || len(t.methods) > 0 { if i > 0 || len(t.methods) > 0 {
buf.WriteString("; ") buf.WriteString("; ")
} }
writeType(buf, typ, qf, visited) writeType(buf, typ, qf, visited)
empty = false
} }
} }
if t.allMethods == nil || len(t.methods) > len(t.allMethods) {
if !empty {
buf.WriteByte(' ')
}
buf.WriteString("/* incomplete */")
}
buf.WriteByte('}') buf.WriteByte('}')
case *Map: case *Map:
......
...@@ -138,6 +138,26 @@ func TestTypeString(t *testing.T) { ...@@ -138,6 +138,26 @@ func TestTypeString(t *testing.T) {
} }
} }
func TestIncompleteInterfaces(t *testing.T) {
sig := NewSignature(nil, nil, nil, false)
for _, test := range []struct {
typ *Interface
want string
}{
{new(Interface), "interface{/* incomplete */}"},
{new(Interface).Complete(), "interface{}"},
{NewInterface(nil, nil), "interface{/* incomplete */}"},
{NewInterface(nil, nil).Complete(), "interface{}"},
{NewInterface([]*Func{NewFunc(token.NoPos, nil, "m", sig)}, nil), "interface{m() /* incomplete */}"},
{NewInterface([]*Func{NewFunc(token.NoPos, nil, "m", sig)}, nil).Complete(), "interface{m()}"},
} {
got := test.typ.String()
if got != test.want {
t.Errorf("got: %s, want: %s", got, test.want)
}
}
}
func TestQualifiedTypeString(t *testing.T) { func TestQualifiedTypeString(t *testing.T) {
p, _ := pkgFor("p.go", "package p; type T int", nil) p, _ := pkgFor("p.go", "package p; type T int", nil)
q, _ := pkgFor("q.go", "package q", nil) q, _ := pkgFor("q.go", "package q", nil)
......
...@@ -540,9 +540,8 @@ func (check *Checker) interfaceType(iface *Interface, ityp *ast.InterfaceType, d ...@@ -540,9 +540,8 @@ func (check *Checker) interfaceType(iface *Interface, ityp *ast.InterfaceType, d
} }
iface.embeddeds = append(iface.embeddeds, named) iface.embeddeds = append(iface.embeddeds, named)
// collect embedded methods // collect embedded methods
if debug && embed.allMethods == nil { if embed.allMethods == nil {
check.dump("%s: incomplete embedded interface %s", pos, named) check.errorf(pos, "internal error: incomplete embedded interface %s (issue #18395)", named)
unreachable()
} }
for _, m := range embed.allMethods { for _, m := range embed.allMethods {
if check.declareInSet(&mset, pos, m) { if check.declareInSet(&mset, pos, m) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment