]> Cypherpunks repositories - gostls13.git/commitdiff
go/types: report error when recognizing issue #18395.
authorRobert Griesemer <gri@golang.org>
Wed, 29 Nov 2017 01:17:33 +0000 (17:17 -0800)
committerRobert Griesemer <gri@golang.org>
Wed, 29 Nov 2017 23:01:29 +0000 (23:01 +0000)
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/80455
Reviewed-by: Alan Donovan <adonovan@google.com>
src/go/internal/gccgoimporter/parser_test.go
src/go/types/builtins.go
src/go/types/object_test.go
src/go/types/testdata/cycles4.src
src/go/types/type.go
src/go/types/typestring.go
src/go/types/typestring_test.go
src/go/types/typexpr.go

index b96486f20adce1cf3082ab7cc0fa3eaf24091b8f..4a103dc462af8e830b971bff75caa4b0d8359a7c 100644 (file)
@@ -45,6 +45,11 @@ func TestTypeParser(t *testing.T) {
                        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()
                if got != test.want {
                        t.Errorf("got type %q, expected %q", got, test.want)
index 596a989a2dfa2cbe957b96e1f0010318c592dc79..66548231fe45a8fa08e8984afa3195f00d34761e 100644 (file)
@@ -470,15 +470,14 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
 
        case _Panic:
                // panic(x)
-               T := new(Interface)
-               check.assignment(x, T, "argument to panic")
+               check.assignment(x, &emptyInterface, "argument to panic")
                if x.mode == invalid {
                        return
                }
 
                x.mode = novalue
                if check.Types != nil {
-                       check.recordBuiltinType(call.Fun, makeSig(nil, T))
+                       check.recordBuiltinType(call.Fun, makeSig(nil, &emptyInterface))
                }
 
        case _Print, _Println:
@@ -508,7 +507,7 @@ func (check *Checker) builtin(x *operand, call *ast.CallExpr, id builtinId) (_ b
        case _Recover:
                // recover() interface{}
                x.mode = value
-               x.typ = new(Interface)
+               x.typ = &emptyInterface
                if check.Types != nil {
                        check.recordBuiltinType(call.Fun, makeSig(x.typ))
                }
index b0acdd91bac26b87f867de9aa834923338b39263..9f0739441825f22febc89db8cc370499601688a8 100644 (file)
@@ -32,12 +32,12 @@ func TestIsAlias(t *testing.T) {
                {NewTypeName(0, nil, "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
-               {NewTypeName(0, nil, "t2", new(Interface)), 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, 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, 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, "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, 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, 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
        } {
                check(test.name, test.alias)
        }
index 445babca68bc3db73a1cc192f366480171fbc659..3f6304be6be08dc5a6f82f0b7d03f9590cafad73 100644 (file)
@@ -108,3 +108,15 @@ type Element interface {
 type Event interface {
        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
index 4c681a79c4d6986d554b741f8e5c8034dc7b6ca4..ee7159f2e232498936654a75ddc57515c13c7d28 100644 (file)
@@ -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)
 }
 
-// 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 {
        typ := new(Interface)
 
+       if len(methods) == 0 && len(embeddeds) == 0 {
+               return typ
+       }
+
        var mset objset
        for _, m := range methods {
                if mset.insert(m) != nil {
index 0f8a7adc2470279311079997189268c8c2ad3d2f..a9c0bfde1f918470a253d5dc132e0cdf5ab01630 100644 (file)
@@ -156,6 +156,7 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
                //     }
                //
                buf.WriteString("interface{")
+               empty := true
                if gcCompatibilityMode {
                        // print flattened interface
                        // (useful to compare against gc-generated interfaces)
@@ -165,6 +166,7 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
                                }
                                buf.WriteString(m.name)
                                writeSignature(buf, m.typ.(*Signature), qf, visited)
+                               empty = false
                        }
                } else {
                        // print explicit interface methods and embedded types
@@ -174,14 +176,22 @@ func writeType(buf *bytes.Buffer, typ Type, qf Qualifier, visited []Type) {
                                }
                                buf.WriteString(m.name)
                                writeSignature(buf, m.typ.(*Signature), qf, visited)
+                               empty = false
                        }
                        for i, typ := range t.embeddeds {
                                if i > 0 || len(t.methods) > 0 {
                                        buf.WriteString("; ")
                                }
                                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('}')
 
        case *Map:
index b794ea813d2761dccb200bd1f213dc2bbf5b9072..8d4c9f00b927d6c8a9d72deb6eca85f5fee0a88c 100644 (file)
@@ -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) {
        p, _ := pkgFor("p.go", "package p; type T int", nil)
        q, _ := pkgFor("q.go", "package q", nil)
index d48dcbffc3f8a6b10626325c3201b950b795528f..0ab6dfdb79b37aa8d2b26bc615317d812838cc8f 100644 (file)
@@ -540,9 +540,8 @@ func (check *Checker) interfaceType(iface *Interface, ityp *ast.InterfaceType, d
                }
                iface.embeddeds = append(iface.embeddeds, named)
                // collect embedded methods
-               if debug && embed.allMethods == nil {
-                       check.dump("%s: incomplete embedded interface %s", pos, named)
-                       unreachable()
+               if embed.allMethods == nil {
+                       check.errorf(pos, "internal error: incomplete embedded interface %s (issue #18395)", named)
                }
                for _, m := range embed.allMethods {
                        if check.declareInSet(&mset, pos, m) {