Commit 07de3465 authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile/internal/gc: handle recursive interfaces better

Previously, we handled recursive interfaces by deferring typechecking
of interface methods, while eagerly expanding interface embeddings.

This CL switches to eagerly evaluating interface methods, and
deferring expanding interface embeddings to dowidth. This allows us to
detect recursive interface embeddings with the same mechanism used for
detecting recursive struct embeddings.

Updates #16369.

Change-Id: If4c0320058047f8a2d9b52b9a79de47eb9887f95
Reviewed-on: https://go-review.googlesource.com/38391
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarRobert Griesemer <gri@golang.org>
parent 4e35e5fc
...@@ -4,6 +4,10 @@ ...@@ -4,6 +4,10 @@
package gc package gc
import (
"sort"
)
// machine size and rounding alignment is dictated around // machine size and rounding alignment is dictated around
// the size of a pointer, set in betypeinit (see ../amd64/galign.go). // the size of a pointer, set in betypeinit (see ../amd64/galign.go).
var defercalc int var defercalc int
...@@ -15,6 +19,47 @@ func Rnd(o int64, r int64) int64 { ...@@ -15,6 +19,47 @@ func Rnd(o int64, r int64) int64 {
return (o + r - 1) &^ (r - 1) return (o + r - 1) &^ (r - 1)
} }
// expandiface computes the method set for interface type t by
// expanding embedded interfaces.
func expandiface(t *Type) {
var fields []*Field
for _, m := range t.Methods().Slice() {
if m.Sym != nil {
fields = append(fields, m)
continue
}
if !m.Type.IsInterface() {
yyerrorl(m.Nname.Pos, "interface contains embedded non-interface %v", m.Type)
m.SetBroke(true)
t.SetBroke(true)
// Add to fields so that error messages
// include the broken embedded type when
// printing t.
// TODO(mdempsky): Revisit this.
fields = append(fields, m)
continue
}
// Embedded interface: duplicate all methods
// (including broken ones, if any) and add to t's
// method set.
for _, t1 := range m.Type.Fields().Slice() {
f := newField()
f.Type = t1.Type
f.SetBroke(t1.Broke())
f.Sym = t1.Sym
f.Nname = m.Nname // preserve embedding position
fields = append(fields, f)
}
}
sort.Sort(methcmp(fields))
// Access fields directly to avoid recursively calling dowidth
// within Type.Fields().
t.Extra.(*InterType).fields.Set(fields)
}
func offmod(t *Type) { func offmod(t *Type) {
o := int32(0) o := int32(0)
for _, f := range t.Fields().Slice() { for _, f := range t.Fields().Slice() {
...@@ -203,9 +248,8 @@ func dowidth(t *Type) { ...@@ -203,9 +248,8 @@ func dowidth(t *Type) {
case TINTER: // implemented as 2 pointers case TINTER: // implemented as 2 pointers
w = 2 * int64(Widthptr) w = 2 * int64(Widthptr)
t.Align = uint8(Widthptr) t.Align = uint8(Widthptr)
offmod(t) expandiface(t)
case TCHAN: // implemented as pointer case TCHAN: // implemented as pointer
w = int64(Widthptr) w = int64(Widthptr)
...@@ -316,6 +360,14 @@ func dowidth(t *Type) { ...@@ -316,6 +360,14 @@ func dowidth(t *Type) {
t.Align = uint8(w) t.Align = uint8(w)
} }
if t.Etype == TINTER {
// We defer calling these functions until after
// setting t.Width and t.Align so the recursive calls
// to dowidth within t.Fields() will succeed.
checkdupfields("method", t)
offmod(t)
}
lineno = lno lineno = lno
if defercalc == 1 { if defercalc == 1 {
......
...@@ -535,7 +535,6 @@ func (p *importer) typ() *Type { ...@@ -535,7 +535,6 @@ func (p *importer) typ() *Type {
t = p.newtyp(TINTER) t = p.newtyp(TINTER)
t.SetInterface(ml) t.SetInterface(ml)
} }
checkwidth(t)
case mapTag: case mapTag:
t = p.newtyp(TMAP) t = p.newtyp(TMAP)
......
...@@ -7,7 +7,6 @@ package gc ...@@ -7,7 +7,6 @@ package gc
import ( import (
"cmd/internal/src" "cmd/internal/src"
"fmt" "fmt"
"sort"
"strings" "strings"
) )
...@@ -705,24 +704,19 @@ func structfield(n *Node) *Field { ...@@ -705,24 +704,19 @@ func structfield(n *Node) *Field {
// checkdupfields emits errors for duplicately named fields or methods in // checkdupfields emits errors for duplicately named fields or methods in
// a list of struct or interface types. // a list of struct or interface types.
func checkdupfields(what string, ts ...*Type) { func checkdupfields(what string, ts ...*Type) {
lno := lineno
seen := make(map[*Sym]bool) seen := make(map[*Sym]bool)
for _, t := range ts { for _, t := range ts {
for _, f := range t.Fields().Slice() { for _, f := range t.Fields().Slice() {
if f.Sym == nil || f.Nname == nil || isblank(f.Nname) { if f.Sym == nil || isblanksym(f.Sym) || f.Nname == nil {
continue continue
} }
if seen[f.Sym] { if seen[f.Sym] {
lineno = f.Nname.Pos yyerrorl(f.Nname.Pos, "duplicate %s %s", what, f.Sym.Name)
yyerror("duplicate %s %s", what, f.Sym.Name)
continue continue
} }
seen[f.Sym] = true seen[f.Sym] = true
} }
} }
lineno = lno
} }
// convert a parsed id/type list into // convert a parsed id/type list into
...@@ -805,51 +799,27 @@ func interfacefield(n *Node) *Field { ...@@ -805,51 +799,27 @@ func interfacefield(n *Node) *Field {
yyerror("interface method cannot have annotation") yyerror("interface method cannot have annotation")
} }
f := newField() // MethodSpec = MethodName Signature | InterfaceTypeName .
f.SetIsddd(n.Isddd()) //
// If Left != nil, then Left is MethodName and Right is Signature.
// Otherwise, Right is InterfaceTypeName.
if n.Right != nil { if n.Right != nil {
if n.Left != nil {
// queue resolution of method type for later.
// right now all we need is the name list.
// avoids cycles for recursive interface types.
n.Type = typ(TINTERMETH)
n.Type.SetNname(n.Right)
n.Left.Type = n.Type
queuemethod(n)
if n.Left.Op == ONAME {
f.Nname = n.Left
f.Embedded = n.Embedded
f.Sym = f.Nname.Sym
}
} else {
n.Right = typecheck(n.Right, Etype) n.Right = typecheck(n.Right, Etype)
n.Type = n.Right.Type n.Type = n.Right.Type
n.Right = nil
if n.Embedded != 0 {
checkembeddedtype(n.Type)
} }
if n.Type != nil { f := newField()
switch n.Type.Etype { if n.Left != nil {
case TINTER: f.Nname = n.Left
break f.Sym = f.Nname.Sym
} else {
case TFORW: // Placeholder ONAME just to hold Pos.
yyerror("interface type loop involving %v", n.Type) // TODO(mdempsky): Add Pos directly to Field instead.
f.SetBroke(true) f.Nname = newname(nblank.Sym)
default:
yyerror("interface contains embedded non-interface %v", n.Type)
f.SetBroke(true)
}
}
}
} }
n.Right = nil
f.Type = n.Type f.Type = n.Type
if f.Type == nil { if f.Type == nil {
f.SetBroke(true) f.SetBroke(true)
...@@ -876,32 +846,13 @@ func tointerface0(t *Type, l []*Node) *Type { ...@@ -876,32 +846,13 @@ func tointerface0(t *Type, l []*Node) *Type {
var fields []*Field var fields []*Field
for _, n := range l { for _, n := range l {
f := interfacefield(n) f := interfacefield(n)
if n.Left == nil && f.Type.IsInterface() {
// embedded interface, inline methods
for _, t1 := range f.Type.Fields().Slice() {
f = newField()
f.Type = t1.Type
f.SetBroke(t1.Broke())
f.Sym = t1.Sym
if f.Sym != nil {
f.Nname = newname(f.Sym)
}
fields = append(fields, f)
}
} else {
fields = append(fields, f)
}
if f.Broke() { if f.Broke() {
t.SetBroke(true) t.SetBroke(true)
} }
fields = append(fields, f)
} }
sort.Sort(methcmp(fields))
t.SetInterface(fields) t.SetInterface(fields)
checkdupfields("method", t)
checkwidth(t)
return t return t
} }
......
...@@ -642,7 +642,6 @@ var etnames = []string{ ...@@ -642,7 +642,6 @@ var etnames = []string{
TBLANK: "TBLANK", TBLANK: "TBLANK",
TFUNCARGS: "TFUNCARGS", TFUNCARGS: "TFUNCARGS",
TCHANARGS: "TCHANARGS", TCHANARGS: "TCHANARGS",
TINTERMETH: "TINTERMETH",
TDDDFIELD: "TDDDFIELD", TDDDFIELD: "TDDDFIELD",
} }
......
...@@ -65,7 +65,6 @@ const ( ...@@ -65,7 +65,6 @@ const (
// pseudo-types for frame layout // pseudo-types for frame layout
TFUNCARGS TFUNCARGS
TCHANARGS TCHANARGS
TINTERMETH
// pseudo-types for import/export // pseudo-types for import/export
TDDDFIELD // wrapper: contained type is a ... field TDDDFIELD // wrapper: contained type is a ... field
...@@ -420,8 +419,6 @@ func typ(et EType) *Type { ...@@ -420,8 +419,6 @@ func typ(et EType) *Type {
t.Extra = new(ForwardType) t.Extra = new(ForwardType)
case TFUNC: case TFUNC:
t.Extra = new(FuncType) t.Extra = new(FuncType)
case TINTERMETH:
t.Extra = InterMethType{}
case TSTRUCT: case TSTRUCT:
t.Extra = new(StructType) t.Extra = new(StructType)
case TINTER: case TINTER:
...@@ -807,8 +804,6 @@ func (t *Type) Nname() *Node { ...@@ -807,8 +804,6 @@ func (t *Type) Nname() *Node {
switch t.Etype { switch t.Etype {
case TFUNC: case TFUNC:
return t.Extra.(*FuncType).Nname return t.Extra.(*FuncType).Nname
case TINTERMETH:
return t.Extra.(InterMethType).Nname
} }
Fatalf("Type.Nname %v %v", t.Etype, t) Fatalf("Type.Nname %v %v", t.Etype, t)
return nil return nil
...@@ -819,8 +814,6 @@ func (t *Type) SetNname(n *Node) { ...@@ -819,8 +814,6 @@ func (t *Type) SetNname(n *Node) {
switch t.Etype { switch t.Etype {
case TFUNC: case TFUNC:
t.Extra.(*FuncType).Nname = n t.Extra.(*FuncType).Nname = n
case TINTERMETH:
t.Extra = InterMethType{Nname: n}
default: default:
Fatalf("Type.SetNname %v %v", t.Etype, t) Fatalf("Type.SetNname %v %v", t.Etype, t)
} }
...@@ -846,6 +839,7 @@ func (t *Type) Fields() *Fields { ...@@ -846,6 +839,7 @@ func (t *Type) Fields() *Fields {
case TSTRUCT: case TSTRUCT:
return &t.Extra.(*StructType).fields return &t.Extra.(*StructType).fields
case TINTER: case TINTER:
dowidth(t)
return &t.Extra.(*InterType).fields return &t.Extra.(*InterType).fields
} }
Fatalf("Fields: type %v does not have fields", t) Fatalf("Fields: type %v does not have fields", t)
...@@ -882,7 +876,7 @@ func (t *Type) SetFields(fields []*Field) { ...@@ -882,7 +876,7 @@ func (t *Type) SetFields(fields []*Field) {
func (t *Type) SetInterface(methods []*Field) { func (t *Type) SetInterface(methods []*Field) {
t.wantEtype(TINTER) t.wantEtype(TINTER)
t.Fields().Set(methods) t.Methods().Set(methods)
} }
func (t *Type) isDDDArray() bool { func (t *Type) isDDDArray() bool {
......
...@@ -3571,8 +3571,15 @@ func copytype(n *Node, t *Type) { ...@@ -3571,8 +3571,15 @@ func copytype(n *Node, t *Type) {
if n.Name != nil { if n.Name != nil {
t.Vargen = n.Name.Vargen t.Vargen = n.Name.Vargen
} }
// spec: "The declared type does not inherit any methods bound
// to the existing type, but the method set of an interface
// type [...] remains unchanged."
if !t.IsInterface() {
t.methods = Fields{} t.methods = Fields{}
t.allMethods = Fields{} t.allMethods = Fields{}
}
t.nod = n t.nod = n
t.SetDeferwidth(false) t.SetDeferwidth(false)
t.ptrTo = ptrTo t.ptrTo = ptrTo
......
...@@ -14,14 +14,14 @@ type I3 interface { int } // ERROR "interface" ...@@ -14,14 +14,14 @@ type I3 interface { int } // ERROR "interface"
type S struct { type S struct {
x interface{ S } // ERROR "interface" x interface{ S } // ERROR "interface"
} }
type I4 interface { type I4 interface { // GC_ERROR "invalid recursive type"
I4 // ERROR "interface" I4 // GCCGO_ERROR "interface"
} }
type I5 interface { type I5 interface {
I6 // GCCGO_ERROR "interface" I6 // GCCGO_ERROR "interface"
} }
type I6 interface { type I6 interface { // GC_ERROR "invalid recursive type"
I5 // ERROR "interface" I5 // GCCGO_ERROR "interface"
} }
...@@ -6,13 +6,17 @@ ...@@ -6,13 +6,17 @@
package main package main
type I1 interface { type I1 interface { // GC_ERROR "invalid recursive type"
m() I2 m() I2
I2 // GCCGO_ERROR "loop|interface" // TODO(mdempsky): The duplicate method error is silly
// and redundant, but tricky to prevent as it's actually
// being emitted against the underlying interface type
// literal, not I1 itself.
I2 // ERROR "loop|interface|duplicate method m"
} }
type I2 interface { type I2 interface {
I1 // ERROR "loop|interface" I1 // GCCGO_ERROR "loop|interface"
} }
......
...@@ -7,5 +7,8 @@ ...@@ -7,5 +7,8 @@
package p package p
type A interface { type A interface {
Fn(A.Fn) // ERROR "type A has no method A.Fn" // TODO(mdempsky): This should be an error, but this error is
// nonsense. The error should actually mention that there's a
// type loop.
Fn(A.Fn) // ERROR "type A has no method Fn"
} }
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