Commit 0d2e92c2 authored by Matthew Dempsky's avatar Matthew Dempsky

cmd/compile: add Fields field to Type

Switch TSTRUCT and TINTER to use Fields instead of Type, which wrings
out the remaining few direct uses of the latter.

Preparation for converting fields to use a separate "Field" type.

Passes toolstash/buildall.

Change-Id: I5a2ea7e159d0dde1be2c9afafc10a8f739d95743
Reviewed-on: https://go-review.googlesource.com/20675
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: 's avatarBrad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: 's avatarRobert Griesemer <gri@golang.org>
parent 79718642
...@@ -112,7 +112,7 @@ func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog { ...@@ -112,7 +112,7 @@ func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog {
// A special case to make write barriers more efficient. // A special case to make write barriers more efficient.
// Comparing the first field of a named struct can be done directly. // Comparing the first field of a named struct can be done directly.
base := n1 base := n1
if n1.Op == gc.ODOT && n1.Left.Type.Etype == gc.TSTRUCT && n1.Left.Type.Type.Sym == n1.Right.Sym { if n1.Op == gc.ODOT && n1.Left.Type.Etype == gc.TSTRUCT && n1.Left.Type.Field(0).Sym == n1.Right.Sym {
base = n1.Left base = n1.Left
} }
......
...@@ -652,7 +652,7 @@ func (p *exporter) paramList(params *Type) { ...@@ -652,7 +652,7 @@ func (p *exporter) paramList(params *Type) {
// (look at the first parameter only since either all // (look at the first parameter only since either all
// names are present or all are absent) // names are present or all are absent)
n := countfield(params) n := countfield(params)
if n > 0 && parName(params.Type) == "" { if n > 0 && parName(params.Field(0)) == "" {
n = -n n = -n
} }
p.int(n) p.int(n)
......
...@@ -802,7 +802,7 @@ func cgen_wbptr(n, res *Node) { ...@@ -802,7 +802,7 @@ func cgen_wbptr(n, res *Node) {
} }
wbVar := syslook("writeBarrier") wbVar := syslook("writeBarrier")
wbEnabled := Nod(ODOT, wbVar, newname(wbVar.Type.Type.Sym)) wbEnabled := Nod(ODOT, wbVar, newname(wbVar.Type.Field(0).Sym))
wbEnabled = typecheck(&wbEnabled, Erv) wbEnabled = typecheck(&wbEnabled, Erv)
pbr := Thearch.Ginscmp(ONE, Types[TUINT8], wbEnabled, Nodintconst(0), -1) pbr := Thearch.Ginscmp(ONE, Types[TUINT8], wbEnabled, Nodintconst(0), -1)
Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &src, &dst) Thearch.Gins(Thearch.Optoas(OAS, Types[Tptr]), &src, &dst)
......
...@@ -1133,7 +1133,7 @@ func isifacemethod(f *Type) bool { ...@@ -1133,7 +1133,7 @@ func isifacemethod(f *Type) bool {
return false return false
} }
t = t.Type t = t.Type
if t.Sym != nil || t.Etype != TSTRUCT || t.Type != nil { if t.Sym != nil || t.Etype != TSTRUCT || countfield(t) != 0 {
return false return false
} }
return true return true
......
...@@ -1478,7 +1478,7 @@ func esccall(e *EscState, n *Node, up *Node) { ...@@ -1478,7 +1478,7 @@ func esccall(e *EscState, n *Node, up *Node) {
var src *Node var src *Node
i := 0 i := 0
lls := ll.Slice() lls := ll.Slice()
for t := fntype.Params().Type; i < len(lls); i++ { for t, it := IterFields(fntype.Params()); i < len(lls); i++ {
src = lls[i] src = lls[i]
if t.Isddd && !n.Isddd { if t.Isddd && !n.Isddd {
// Introduce ODDDARG node to represent ... allocation. // Introduce ODDDARG node to represent ... allocation.
...@@ -1523,7 +1523,7 @@ func esccall(e *EscState, n *Node, up *Node) { ...@@ -1523,7 +1523,7 @@ func esccall(e *EscState, n *Node, up *Node) {
// This occurs when function parameter type Isddd and n not Isddd // This occurs when function parameter type Isddd and n not Isddd
break break
} }
t = t.Down t = it.Next()
} }
for ; i < len(lls); i++ { for ; i < len(lls); i++ {
......
...@@ -202,8 +202,11 @@ func reexportdep(n *Node) { ...@@ -202,8 +202,11 @@ func reexportdep(n *Node) {
OMAKECHAN: OMAKECHAN:
t := n.Type t := n.Type
if t.Sym == nil && t.Type != nil { switch t.Etype {
t = t.Type case TARRAY, TCHAN, TPTR32, TPTR64:
if t.Sym == nil {
t = t.Type
}
} }
if t != nil && t.Sym != nil && t.Sym.Def != nil && !exportedsym(t.Sym) { if t != nil && t.Sym != nil && t.Sym.Def != nil && !exportedsym(t.Sym) {
if Debug['E'] != 0 { if Debug['E'] != 0 {
...@@ -280,25 +283,41 @@ func dumpexporttype(t *Type) { ...@@ -280,25 +283,41 @@ func dumpexporttype(t *Type) {
if t == nil { if t == nil {
return return
} }
if t.Etype == TFIELD {
Fatalf("unexpected TFIELD in dumpexporttype")
}
if t.Printed || t == Types[t.Etype] || t == bytetype || t == runetype || t == errortype { if t.Printed || t == Types[t.Etype] || t == bytetype || t == runetype || t == errortype {
return return
} }
t.Printed = true t.Printed = true
if t.Sym != nil && t.Etype != TFIELD { if t.Sym != nil {
dumppkg(t.Sym.Pkg) dumppkg(t.Sym.Pkg)
} }
dumpexporttype(t.Type) switch t.Etype {
dumpexporttype(t.Down) case TSTRUCT, TINTER:
for f, it := IterFields(t); f != nil; f = it.Next() {
if t.Sym == nil || t.Etype == TFIELD { dumpexporttype(f.Type)
}
case TFUNC:
dumpexporttype(t.Recvs())
dumpexporttype(t.Results())
dumpexporttype(t.Params())
case TMAP:
dumpexporttype(t.Type)
dumpexporttype(t.Down) // key
case TARRAY, TCHAN, TPTR32, TPTR64:
dumpexporttype(t.Type)
}
if t.Sym == nil {
return return
} }
var m []*Type var m []*Type
for f, it := IterMethods(t); f != nil; f = it.Next() { for f, it := IterMethods(t); f != nil; f = it.Next() {
dumpexporttype(f) dumpexporttype(f.Type)
m = append(m, f) m = append(m, f)
} }
sort.Sort(methodbyname(m)) sort.Sort(methodbyname(m))
......
...@@ -602,7 +602,7 @@ func typefmt(t *Type, flag int) string { ...@@ -602,7 +602,7 @@ func typefmt(t *Type, flag int) string {
buf.WriteString(";") buf.WriteString(";")
} }
} }
if t.Type != nil { if t.Fields != nil {
buf.WriteString(" ") buf.WriteString(" ")
} }
buf.WriteString("}") buf.WriteString("}")
...@@ -629,7 +629,7 @@ func typefmt(t *Type, flag int) string { ...@@ -629,7 +629,7 @@ func typefmt(t *Type, flag int) string {
case 1: case 1:
if fmtmode != FExp { if fmtmode != FExp {
buf.WriteString(" ") buf.WriteString(" ")
buf.WriteString(Tconv(t.Results().Type.Type, 0)) // struct->field->field's type buf.WriteString(Tconv(t.Results().Field(0).Type, 0)) // struct->field->field's type
break break
} }
fallthrough fallthrough
...@@ -687,7 +687,7 @@ func typefmt(t *Type, flag int) string { ...@@ -687,7 +687,7 @@ func typefmt(t *Type, flag int) string {
buf.WriteString(";") buf.WriteString(";")
} }
} }
if t.Type != nil { if t.Fields != nil {
buf.WriteString(" ") buf.WriteString(" ")
} }
buf.WriteString("}") buf.WriteString("}")
......
...@@ -412,7 +412,7 @@ func Naddr(a *obj.Addr, n *Node) { ...@@ -412,7 +412,7 @@ func Naddr(a *obj.Addr, n *Node) {
// A special case to make write barriers more efficient. // A special case to make write barriers more efficient.
// Taking the address of the first field of a named struct // Taking the address of the first field of a named struct
// is the same as taking the address of the struct. // is the same as taking the address of the struct.
if n.Left.Type.Etype != TSTRUCT || n.Left.Type.Type.Sym != n.Right.Sym { if n.Left.Type.Etype != TSTRUCT || n.Left.Type.Field(0).Sym != n.Right.Sym {
Debug['h'] = 1 Debug['h'] = 1
Dump("naddr", n) Dump("naddr", n)
Fatalf("naddr: bad %v %v", Oconv(n.Op, 0), Ctxt.Dconv(a)) Fatalf("naddr: bad %v %v", Oconv(n.Op, 0), Ctxt.Dconv(a))
......
...@@ -700,8 +700,8 @@ func mkinlcall1(np **Node, fn *Node, isddd bool) { ...@@ -700,8 +700,8 @@ func mkinlcall1(np **Node, fn *Node, isddd bool) {
} }
} else { } else {
// match arguments except final variadic (unless the call is dotted itself) // match arguments except final variadic (unless the call is dotted itself)
var t *Type t, it := IterFields(fn.Type.Params())
for t = fn.Type.Params().Type; t != nil; { for t != nil {
if li >= n.List.Len() { if li >= n.List.Len() {
break break
} }
...@@ -709,7 +709,7 @@ func mkinlcall1(np **Node, fn *Node, isddd bool) { ...@@ -709,7 +709,7 @@ func mkinlcall1(np **Node, fn *Node, isddd bool) {
break break
} }
as.List.Append(tinlvar(t)) as.List.Append(tinlvar(t))
t = t.Down t = it.Next()
li++ li++
} }
...@@ -725,7 +725,7 @@ func mkinlcall1(np **Node, fn *Node, isddd bool) { ...@@ -725,7 +725,7 @@ func mkinlcall1(np **Node, fn *Node, isddd bool) {
} }
if i == varargcount { if i == varargcount {
t = t.Down t = it.Next()
} }
} }
......
...@@ -371,7 +371,7 @@ func ordercall(n *Node, order *Order) { ...@@ -371,7 +371,7 @@ func ordercall(n *Node, order *Order) {
ordercallargs(&n.List, order) ordercallargs(&n.List, order)
if n.Op == OCALLFUNC { if n.Op == OCALLFUNC {
t := n.Left.Type.Params().Type t, it := IterFields(n.Left.Type.Params())
for i := range n.List.Slice() { for i := range n.List.Slice() {
// Check for "unsafe-uintptr" tag provided by escape analysis. // Check for "unsafe-uintptr" tag provided by escape analysis.
// If present and the argument is really a pointer being converted // If present and the argument is really a pointer being converted
...@@ -393,7 +393,7 @@ func ordercall(n *Node, order *Order) { ...@@ -393,7 +393,7 @@ func ordercall(n *Node, order *Order) {
*xp = x *xp = x
} }
} }
t = t.Down t = it.Next()
} }
} }
} }
......
...@@ -883,19 +883,19 @@ func maplit(ctxt int, n *Node, var_ *Node, init *Nodes) { ...@@ -883,19 +883,19 @@ func maplit(ctxt int, n *Node, var_ *Node, init *Nodes) {
tk := t.Down tk := t.Down
tv := t.Type tv := t.Type
syma := Lookup("a")
symb := Lookup("b") symb := Lookup("b")
fieldb := typ(TFIELD)
fieldb.Type = tv
fieldb.Sym = symb
syma := Lookup("a") var fields [2]*Type
fielda := typ(TFIELD) fields[0] = typ(TFIELD)
fielda.Type = tk fields[0].Type = tk
fielda.Sym = syma fields[0].Sym = syma
fielda.Down = fieldb fields[1] = typ(TFIELD)
fields[1].Type = tv
fields[1].Sym = symb
tstruct := typ(TSTRUCT) tstruct := typ(TSTRUCT)
tstruct.Type = fielda tstruct.SetFields(fields[:])
tarr := typ(TARRAY) tarr := typ(TARRAY)
tarr.Bound = int64(b) tarr.Bound = int64(b)
......
...@@ -27,7 +27,7 @@ func TestSizeof(t *testing.T) { ...@@ -27,7 +27,7 @@ func TestSizeof(t *testing.T) {
{Name{}, 52, 80}, {Name{}, 52, 80},
{Node{}, 92, 144}, {Node{}, 92, 144},
{Sym{}, 60, 112}, {Sym{}, 60, 112},
{Type{}, 136, 224}, {Type{}, 140, 232},
} }
for _, tt := range tests { for _, tt := range tests {
......
...@@ -592,13 +592,7 @@ func Isinter(t *Type) bool { ...@@ -592,13 +592,7 @@ func Isinter(t *Type) bool {
} }
func isnilinter(t *Type) bool { func isnilinter(t *Type) bool {
if !Isinter(t) { return Isinter(t) && countfield(t) == 0
return false
}
if t.Type != nil {
return false
}
return true
} }
func isideal(t *Type) bool { func isideal(t *Type) bool {
......
...@@ -140,6 +140,9 @@ type Type struct { ...@@ -140,6 +140,9 @@ type Type struct {
Type *Type // actual type for TFIELD, element type for TARRAY, TCHAN, TMAP, TPTRxx Type *Type // actual type for TFIELD, element type for TARRAY, TCHAN, TMAP, TPTRxx
Width int64 // offset in TFIELD, width in all others Width int64 // offset in TFIELD, width in all others
// TSTRUCT
Fields *Type // first struct field
// TFIELD // TFIELD
Down *Type // next struct field, also key type in TMAP Down *Type // next struct field, also key type in TMAP
Note *string // literal string annotation Note *string // literal string annotation
...@@ -196,7 +199,7 @@ func IterFields(t *Type) (*Type, Iter) { ...@@ -196,7 +199,7 @@ func IterFields(t *Type) (*Type, Iter) {
if t.Etype != TSTRUCT && t.Etype != TINTER { if t.Etype != TSTRUCT && t.Etype != TINTER {
Fatalf("IterFields: type %v does not have fields", t) Fatalf("IterFields: type %v does not have fields", t)
} }
return RawIter(t.Type) return RawIter(t.Fields)
} }
// IterMethods returns the first method in type t's method set // IterMethods returns the first method in type t's method set
...@@ -316,7 +319,7 @@ func (t *Type) SetFields(fields []*Type) { ...@@ -316,7 +319,7 @@ func (t *Type) SetFields(fields []*Type) {
fields[i].Down = next fields[i].Down = next
next = fields[i] next = fields[i]
} }
t.Type = next t.Fields = next
} }
func (t *Type) Size() int64 { func (t *Type) Size() int64 {
......
...@@ -1333,15 +1333,7 @@ OpSwitch: ...@@ -1333,15 +1333,7 @@ OpSwitch:
} }
ok |= Erv ok |= Erv
if t.Outtuple == 1 { if t.Outtuple == 1 {
t := l.Type.Results().Type n.Type = l.Type.Results().Field(0).Type
if t == nil {
n.Type = nil
return
}
if t.Etype == TFIELD {
t = t.Type
}
n.Type = t
if n.Op == OCALLFUNC && n.Left.Op == ONAME && (compiling_runtime != 0 || n.Left.Sym.Pkg == Runtimepkg) && n.Left.Sym.Name == "getg" { if n.Op == OCALLFUNC && n.Left.Op == ONAME && (compiling_runtime != 0 || n.Left.Sym.Pkg == Runtimepkg) && n.Left.Sym.Name == "getg" {
// Emit code for runtime.getg() directly instead of calling function. // Emit code for runtime.getg() directly instead of calling function.
...@@ -1603,7 +1595,7 @@ OpSwitch: ...@@ -1603,7 +1595,7 @@ OpSwitch:
var funarg *Type var funarg *Type
if Istype(t, TSTRUCT) && t.Funarg { if Istype(t, TSTRUCT) && t.Funarg {
funarg = t funarg = t
t = t.Type.Type t = t.Field(0).Type
} }
n.Type = t n.Type = t
...@@ -1642,7 +1634,8 @@ OpSwitch: ...@@ -1642,7 +1634,8 @@ OpSwitch:
} }
if funarg != nil { if funarg != nil {
for t := funarg.Type.Down; t != nil; t = t.Down { _, it := IterFields(funarg) // Skip first field
for t := it.Next(); t != nil; t = it.Next() {
if assignop(t.Type, n.Type.Type, nil) == 0 { if assignop(t.Type, n.Type.Type, nil) == 0 {
Yyerror("cannot append %v value to []%v", t.Type, n.Type.Type) Yyerror("cannot append %v value to []%v", t.Type, n.Type.Type)
} }
...@@ -2403,7 +2396,7 @@ func looktypedot(n *Node, t *Type, dostrcmp int) bool { ...@@ -2403,7 +2396,7 @@ func looktypedot(n *Node, t *Type, dostrcmp int) bool {
s := n.Right.Sym s := n.Right.Sym
if t.Etype == TINTER { if t.Etype == TINTER {
f1 := lookdot1(n, s, t, t.Type, dostrcmp) f1 := lookdot1(n, s, t, t.Fields, dostrcmp)
if f1 == nil { if f1 == nil {
return false return false
} }
...@@ -2464,7 +2457,7 @@ func lookdot(n *Node, t *Type, dostrcmp int) *Type { ...@@ -2464,7 +2457,7 @@ func lookdot(n *Node, t *Type, dostrcmp int) *Type {
dowidth(t) dowidth(t)
var f1 *Type var f1 *Type
if t.Etype == TSTRUCT || t.Etype == TINTER { if t.Etype == TSTRUCT || t.Etype == TINTER {
f1 = lookdot1(n, s, t, t.Type, dostrcmp) f1 = lookdot1(n, s, t, t.Fields, dostrcmp)
} }
var f2 *Type var f2 *Type
...@@ -2627,11 +2620,11 @@ func typecheckaste(op Op, call *Node, isddd bool, tstruct *Type, nl Nodes, desc ...@@ -2627,11 +2620,11 @@ func typecheckaste(op Op, call *Node, isddd bool, tstruct *Type, nl Nodes, desc
} }
} }
tn := n.Type.Type tn, it := IterFields(n.Type)
var why string var why string
for tl, it2 := IterFields(tstruct); tl != nil; tl = it2.Next() { for tl, it2 := IterFields(tstruct); tl != nil; tl = it2.Next() {
if tl.Isddd { if tl.Isddd {
for ; tn != nil; tn = tn.Down { for ; tn != nil; tn = it.Next() {
if assignop(tn.Type, tl.Type.Type, &why) == 0 { if assignop(tn.Type, tl.Type.Type, &why) == 0 {
if call != nil { if call != nil {
Yyerror("cannot use %v as type %v in argument to %v%s", tn.Type, tl.Type.Type, call, why) Yyerror("cannot use %v as type %v in argument to %v%s", tn.Type, tl.Type.Type, call, why)
...@@ -2655,7 +2648,7 @@ func typecheckaste(op Op, call *Node, isddd bool, tstruct *Type, nl Nodes, desc ...@@ -2655,7 +2648,7 @@ func typecheckaste(op Op, call *Node, isddd bool, tstruct *Type, nl Nodes, desc
} }
} }
tn = tn.Down tn = it.Next()
} }
if tn != nil { if tn != nil {
...@@ -3049,7 +3042,7 @@ func typecheckcomplit(np **Node) { ...@@ -3049,7 +3042,7 @@ func typecheckcomplit(np **Node) {
bad := 0 bad := 0
if n.List.Len() != 0 && nokeys(n.List) { if n.List.Len() != 0 && nokeys(n.List) {
// simple list of variables // simple list of variables
f := t.Type f, it := IterFields(t)
var s *Sym var s *Sym
ls := n.List.Slice() ls := n.List.Slice()
...@@ -3075,7 +3068,7 @@ func typecheckcomplit(np **Node) { ...@@ -3075,7 +3068,7 @@ func typecheckcomplit(np **Node) {
n1.Left.Type = f n1.Left.Type = f
n1.Left.Typecheck = 1 n1.Left.Typecheck = 1
ls[i1] = n1 ls[i1] = n1
f = f.Down f = it.Next()
} }
if f != nil { if f != nil {
...@@ -3114,7 +3107,7 @@ func typecheckcomplit(np **Node) { ...@@ -3114,7 +3107,7 @@ func typecheckcomplit(np **Node) {
} }
} }
f := lookdot1(nil, s, t, t.Type, 0) f := lookdot1(nil, s, t, t.Fields, 0)
if f == nil { if f == nil {
Yyerror("unknown %v field '%v' in struct literal", t, s) Yyerror("unknown %v field '%v' in struct literal", t, s)
continue continue
......
...@@ -367,17 +367,22 @@ func typeinit() { ...@@ -367,17 +367,22 @@ func typeinit() {
func lexinit1() { func lexinit1() {
// t = interface { Error() string } // t = interface { Error() string }
rcvr := typ(TSTRUCT)
rcvr.Type = typ(TFIELD) rcvr := typ(TSTRUCT)
rcvr.Type.Type = Ptrto(typ(TSTRUCT))
rcvr.Funarg = true rcvr.Funarg = true
field := typ(TFIELD)
field.Type = Ptrto(typ(TSTRUCT))
rcvr.SetFields([]*Type{field})
in := typ(TSTRUCT) in := typ(TSTRUCT)
in.Funarg = true in.Funarg = true
out := typ(TSTRUCT) out := typ(TSTRUCT)
out.Type = typ(TFIELD)
out.Type.Type = Types[TSTRING]
out.Funarg = true out.Funarg = true
field = typ(TFIELD)
field.Type = Types[TSTRING]
out.SetFields([]*Type{field})
f := typ(TFUNC) f := typ(TFUNC)
*f.RecvsP() = rcvr *f.RecvsP() = rcvr
*f.ResultsP() = out *f.ResultsP() = out
...@@ -386,10 +391,12 @@ func lexinit1() { ...@@ -386,10 +391,12 @@ func lexinit1() {
f.Intuple = 0 f.Intuple = 0
f.Outnamed = false f.Outnamed = false
f.Outtuple = 1 f.Outtuple = 1
t := typ(TINTER) t := typ(TINTER)
t.Type = typ(TFIELD) field = typ(TFIELD)
t.Type.Sym = Lookup("Error") field.Sym = Lookup("Error")
t.Type.Type = f field.Type = f
t.SetFields([]*Type{field})
// error type // error type
s := Pkglookup("error", builtinpkg) s := Pkglookup("error", builtinpkg)
......
...@@ -658,11 +658,7 @@ opswitch: ...@@ -658,11 +658,7 @@ opswitch:
// Update type of OCALLFUNC node. // Update type of OCALLFUNC node.
// Output arguments had not changed, but their offsets could. // Output arguments had not changed, but their offsets could.
if n.Left.Type.Outtuple == 1 { if n.Left.Type.Outtuple == 1 {
t := n.Left.Type.Results().Type n.Type = n.Left.Type.Results().Field(0).Type
if t.Etype == TFIELD {
t = t.Type
}
n.Type = t
} else { } else {
n.Type = n.Left.Type.Results() n.Type = n.Left.Type.Results()
} }
...@@ -2008,13 +2004,7 @@ func walkprint(nn *Node, init *Nodes) *Node { ...@@ -2008,13 +2004,7 @@ func walkprint(nn *Node, init *Nodes) *Node {
continue continue
} }
t = on.Type.Params() t = on.Type.Params().Field(0).Type
if t != nil {
t = t.Type
}
if t != nil {
t = t.Type
}
if !Eqtype(t, n.Type) { if !Eqtype(t, n.Type) {
n = Nod(OCONV, n, nil) n = Nod(OCONV, n, nil)
......
...@@ -639,7 +639,7 @@ func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog { ...@@ -639,7 +639,7 @@ func ginscmp(op gc.Op, t *gc.Type, n1, n2 *gc.Node, likely int) *obj.Prog {
// A special case to make write barriers more efficient. // A special case to make write barriers more efficient.
// Comparing the first field of a named struct can be done directly. // Comparing the first field of a named struct can be done directly.
base := n1 base := n1
if n1.Op == gc.ODOT && n1.Left.Type.Etype == gc.TSTRUCT && n1.Left.Type.Type.Sym == n1.Right.Sym { if n1.Op == gc.ODOT && n1.Left.Type.Etype == gc.TSTRUCT && n1.Left.Type.Field(0).Sym == n1.Right.Sym {
base = n1.Left base = n1.Left
} }
......
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