Commit 49dad0f5 authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile: support arbitrarily deep embedded fields

Fixes #13337.

Change-Id: Ie74d00390111796619150287d3f7a147750ab456
Reviewed-on: https://go-review.googlesource.com/19932Reviewed-by: default avatarIan Lance Taylor <iant@golang.org>
parent f91b832a
...@@ -192,10 +192,6 @@ type Sig struct { ...@@ -192,10 +192,6 @@ type Sig struct {
offset int32 offset int32
} }
type Dlist struct {
field *Type
}
// argument passing to/from // argument passing to/from
// smagic and umagic // smagic and umagic
type Magic struct { type Magic struct {
...@@ -240,8 +236,6 @@ var sizeof_Array int // runtime sizeof(Array) ...@@ -240,8 +236,6 @@ var sizeof_Array int // runtime sizeof(Array)
// } String; // } String;
var sizeof_String int // runtime sizeof(String) var sizeof_String int // runtime sizeof(String)
var dotlist [10]Dlist // size is max depth of embeddeds
// lexlineno is the line number _after_ the most recently read rune. // lexlineno is the line number _after_ the most recently read rune.
// In particular, it's advanced (or rewound) as newlines are read (or unread). // In particular, it's advanced (or rewound) as newlines are read (or unread).
var lexlineno int32 var lexlineno int32
......
...@@ -1564,14 +1564,22 @@ func Setmaxarg(t *Type, extra int32) { ...@@ -1564,14 +1564,22 @@ func Setmaxarg(t *Type, extra int32) {
} }
} }
// unicode-aware case-insensitive strcmp // Code to resolve elided DOTs in embedded types.
// code to resolve elided DOTs // A Dlist stores a pointer to a TFIELD Type embedded within
// in embedded types // a TSTRUCT or TINTER Type.
type Dlist struct {
field *Type
}
// dotlist is used by adddot1 to record the path of embedded fields
// used to access a target field or method.
// Must be non-nil so that dotpath returns a non-nil slice even if d is zero.
var dotlist = make([]Dlist, 10)
// search depth 0 -- // lookdot0 returns the number of fields or methods named s associated
// return count of fields+methods // with Type t. If exactly one exists, it will be returned in *save
// found with a given name // (if save is not nil).
func lookdot0(s *Sym, t *Type, save **Type, ignorecase int) int { func lookdot0(s *Sym, t *Type, save **Type, ignorecase int) int {
u := t u := t
if Isptr[u.Etype] { if Isptr[u.Etype] {
...@@ -1605,26 +1613,30 @@ func lookdot0(s *Sym, t *Type, save **Type, ignorecase int) int { ...@@ -1605,26 +1613,30 @@ func lookdot0(s *Sym, t *Type, save **Type, ignorecase int) int {
return c return c
} }
// search depth d for field/method s -- // adddot1 returns the number of fields or methods named s at depth d in Type t.
// return count of fields+methods // If exactly one exists, it will be returned in *save (if save is not nil),
// found at search depth. // and dotlist will contain the path of embedded fields traversed to find it,
// answer is in dotlist array and // in reverse order. If none exist, more will indicate whether t contains any
// count of number of ways is returned. // embedded fields at depth d, so callers can decide whether to retry at
func adddot1(s *Sym, t *Type, d int, save **Type, ignorecase int) int { // a greater depth.
func adddot1(s *Sym, t *Type, d int, save **Type, ignorecase int) (c int, more bool) {
if t.Trecur != 0 { if t.Trecur != 0 {
return 0 return
} }
t.Trecur = 1 t.Trecur = 1
var c int
var u *Type var u *Type
var a int d--
if d == 0 { if d < 0 {
// We've reached our target depth. If t has any fields/methods
// named s, then we're done. Otherwise, we still need to check
// below for embedded fields.
c = lookdot0(s, t, save, ignorecase) c = lookdot0(s, t, save, ignorecase)
if c != 0 {
goto out goto out
} }
}
c = 0
u = t u = t
if Isptr[u.Etype] { if Isptr[u.Etype] {
u = u.Type u = u.Type
...@@ -1633,24 +1645,53 @@ func adddot1(s *Sym, t *Type, d int, save **Type, ignorecase int) int { ...@@ -1633,24 +1645,53 @@ func adddot1(s *Sym, t *Type, d int, save **Type, ignorecase int) int {
goto out goto out
} }
d--
for f := u.Type; f != nil; f = f.Down { for f := u.Type; f != nil; f = f.Down {
if f.Embedded == 0 { if f.Embedded == 0 || f.Sym == nil {
continue continue
} }
if f.Sym == nil { if d < 0 {
continue // Found an embedded field at target depth.
more = true
goto out
} }
a = adddot1(s, f.Type, d, save, ignorecase) a, more1 := adddot1(s, f.Type, d, save, ignorecase)
if a != 0 && c == 0 { if a != 0 && c == 0 {
dotlist[d].field = f dotlist[d].field = f
} }
c += a c += a
if more1 {
more = true
}
} }
out: out:
t.Trecur = 0 t.Trecur = 0
return c return c, more
}
// dotpath computes the unique shortest explicit selector path to fully qualify
// a selection expression x.f, where x is of type t and f is the symbol s.
// If no such path exists, dotpath returns nil.
// If there are multiple shortest paths to the same depth, ambig is true.
func dotpath(s *Sym, t *Type, save **Type, ignorecase int) (path []Dlist, ambig bool) {
// The embedding of types within structs imposes a tree structure onto
// types: structs parent the types they embed, and types parent their
// fields or methods. Our goal here is to find the shortest path to
// a field or method named s in the subtree rooted at t. To accomplish
// that, we iteratively perform depth-first searches of increasing depth
// until we either find the named field/method or exhaust the tree.
for d := 0; ; d++ {
if d > len(dotlist) {
dotlist = append(dotlist, Dlist{})
}
if c, more := adddot1(s, t, d, save, ignorecase); c == 1 {
return dotlist[:d], false
} else if c > 1 {
return nil, true
} else if !more {
return nil, false
}
}
} }
// in T.field // in T.field
...@@ -1677,24 +1718,16 @@ func adddot(n *Node) *Node { ...@@ -1677,24 +1718,16 @@ func adddot(n *Node) *Node {
return n return n
} }
var c int switch path, ambig := dotpath(s, t, nil, 0); {
for d := 0; d < len(dotlist); d++ { case path != nil:
c = adddot1(s, t, d, nil, 0)
if c > 0 {
if c > 1 {
Yyerror("ambiguous selector %v", n)
n.Left = nil
return n
}
// rebuild elided dots // rebuild elided dots
for c := d - 1; c >= 0; c-- { for c := len(path) - 1; c >= 0; c-- {
n.Left = Nod(ODOT, n.Left, newname(dotlist[c].field.Sym)) n.Left = Nod(ODOT, n.Left, newname(path[c].field.Sym))
n.Left.Implicit = true n.Left.Implicit = true
} }
case ambig:
return n Yyerror("ambiguous selector %v", n)
} n.Left = nil
} }
return n return n
...@@ -1758,16 +1791,13 @@ func expand0(t *Type, followptr bool) { ...@@ -1758,16 +1791,13 @@ func expand0(t *Type, followptr bool) {
} }
} }
func expand1(t *Type, d int, followptr bool) { func expand1(t *Type, top, followptr bool) {
if t.Trecur != 0 { if t.Trecur != 0 {
return return
} }
if d == 0 {
return
}
t.Trecur = 1 t.Trecur = 1
if d != len(dotlist)-1 { if !top {
expand0(t, followptr) expand0(t, followptr)
} }
...@@ -1788,7 +1818,7 @@ func expand1(t *Type, d int, followptr bool) { ...@@ -1788,7 +1818,7 @@ func expand1(t *Type, d int, followptr bool) {
if f.Sym == nil { if f.Sym == nil {
continue continue
} }
expand1(f.Type, d-1, followptr) expand1(f.Type, false, followptr)
} }
out: out:
...@@ -1810,30 +1840,21 @@ func expandmeth(t *Type) { ...@@ -1810,30 +1840,21 @@ func expandmeth(t *Type) {
// generate all reachable methods // generate all reachable methods
slist = nil slist = nil
expand1(t, len(dotlist)-1, false) expand1(t, true, false)
// check each method to be uniquely reachable // check each method to be uniquely reachable
var c int
var d int
for sl := slist; sl != nil; sl = sl.link { for sl := slist; sl != nil; sl = sl.link {
sl.field.Sym.Flags &^= SymUniq sl.field.Sym.Flags &^= SymUniq
for d = 0; d < len(dotlist); d++ { if path, _ := dotpath(sl.field.Sym, t, &f, 0); path == nil {
c = adddot1(sl.field.Sym, t, d, &f, 0)
if c == 0 {
continue continue
} }
if c == 1 { // dotpath may have dug out arbitrary fields, we only want methods.
// addot1 may have dug out arbitrary fields, we only want methods.
if f.Type.Etype == TFUNC && f.Type.Thistuple > 0 { if f.Type.Etype == TFUNC && f.Type.Thistuple > 0 {
sl.good = true sl.good = true
sl.field = f sl.field = f
} }
} }
break
}
}
for f = t.Method; f != nil; f = f.Down { for f = t.Method; f != nil; f = f.Down {
f.Sym.Flags &^= SymUniq f.Sym.Flags &^= SymUniq
} }
...@@ -1991,6 +2012,7 @@ func genwrapper(rcvr *Type, method *Type, newnam *Sym, iface int) { ...@@ -1991,6 +2012,7 @@ func genwrapper(rcvr *Type, method *Type, newnam *Sym, iface int) {
if !instrumenting && Isptr[rcvr.Etype] && Isptr[methodrcvr.Etype] && method.Embedded != 0 && !isifacemethod(method.Type) { if !instrumenting && Isptr[rcvr.Etype] && Isptr[methodrcvr.Etype] && method.Embedded != 0 && !isifacemethod(method.Type) {
// generate tail call: adjust pointer receiver and jump to embedded method. // generate tail call: adjust pointer receiver and jump to embedded method.
dot = dot.Left // skip final .M dot = dot.Left // skip final .M
// TODO(mdempsky): Remove dependency on dotlist.
if !Isptr[dotlist[0].field.Type.Etype] { if !Isptr[dotlist[0].field.Type.Etype] {
dot = Nod(OADDR, dot, nil) dot = Nod(OADDR, dot, nil)
} }
...@@ -2058,18 +2080,16 @@ func ifacelookdot(s *Sym, t *Type, followptr *bool, ignorecase int) *Type { ...@@ -2058,18 +2080,16 @@ func ifacelookdot(s *Sym, t *Type, followptr *bool, ignorecase int) *Type {
} }
var m *Type var m *Type
var i int path, ambig := dotpath(s, t, &m, ignorecase)
var c int if path == nil {
for d := 0; d < len(dotlist); d++ { if ambig {
c = adddot1(s, t, d, &m, ignorecase)
if c > 1 {
Yyerror("%v.%v is ambiguous", t, s) Yyerror("%v.%v is ambiguous", t, s)
}
return nil return nil
} }
if c == 1 { for _, d := range path {
for i = 0; i < d; i++ { if Isptr[d.field.Type.Etype] {
if Isptr[dotlist[i].field.Type.Etype] {
*followptr = true *followptr = true
break break
} }
...@@ -2081,10 +2101,6 @@ func ifacelookdot(s *Sym, t *Type, followptr *bool, ignorecase int) *Type { ...@@ -2081,10 +2101,6 @@ func ifacelookdot(s *Sym, t *Type, followptr *bool, ignorecase int) *Type {
} }
return m return m
}
}
return nil
} }
func implements(t *Type, iface *Type, m **Type, samename **Type, ptr *int) bool { func implements(t *Type, iface *Type, m **Type, samename **Type, ptr *int) bool {
......
// compile
// Copyright 2016 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.
// Issue 13337: The Go compiler limited how deeply embedded types
// were searched for promoted fields and methods.
package s
type S0 struct{ f int }
func (S0) m() {}
type S1 struct{ S0 }
type S2 struct{ S1 }
type S3 struct{ S2 }
type S4 struct{ S3 }
type S5 struct{ S4 }
type S6 struct{ S5 }
type S7 struct{ S6 }
type S8 struct{ S7 }
type S9 struct{ S8 }
type S10 struct{ S9 }
type S11 struct{ S10 }
type S12 struct{ S11 }
type S13 struct{ S12 }
var _ = S13{}.f
var _ = S13.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