Skip to content

Commit f4e8a71

Browse files
committed
go/ssa: use core type for composite literal addresses
Dereferences using the core type during compLit and when creating addresses for composite literals. Also adds a new utility fieldOf for selecting a field from a type whose core type is a struct. Change-Id: I2fd0a1caf99819d0b9be5f3ba79a00f8053565e3 Reviewed-on: https://go-review.googlesource.com/c/tools/+/494978 TryBot-Result: Gopher Robot <[email protected]> gopls-CI: kokoro <[email protected]> Run-TryBot: Tim King <[email protected]> Reviewed-by: Alan Donovan <[email protected]>
1 parent 3b25dbd commit f4e8a71

File tree

5 files changed

+97
-62
lines changed

5 files changed

+97
-62
lines changed

go/ssa/builder.go

Lines changed: 14 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -429,12 +429,12 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
429429
return &address{addr: v, pos: e.Pos(), expr: e}
430430

431431
case *ast.CompositeLit:
432-
t, _ := deptr(fn.typeOf(e))
432+
typ, _ := deref(fn.typeOf(e))
433433
var v *Alloc
434434
if escaping {
435-
v = emitNew(fn, t, e.Lbrace)
435+
v = emitNew(fn, typ, e.Lbrace)
436436
} else {
437-
v = fn.addLocal(t, e.Lbrace)
437+
v = fn.addLocal(typ, e.Lbrace)
438438
}
439439
v.Comment = "complit"
440440
var sb storebuf
@@ -457,8 +457,7 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
457457
wantAddr := true
458458
v := b.receiver(fn, e.X, wantAddr, escaping, sel)
459459
index := sel.index[len(sel.index)-1]
460-
dt, _ := deptr(v.Type())
461-
fld := typeparams.CoreType(dt).(*types.Struct).Field(index)
460+
fld := fieldOf(mustDeref(v.Type()), index) // v is an addr.
462461

463462
// Due to the two phases of resolving AssignStmt, a panic from x.f = p()
464463
// when x is nil is required to come after the side-effects of
@@ -553,7 +552,7 @@ func (b *builder) assign(fn *Function, loc lvalue, e ast.Expr, isZero bool, sb *
553552
// so if the type of the location is a pointer,
554553
// an &-operation is implied.
555554
if _, ok := loc.(blank); !ok { // avoid calling blank.typ()
556-
if _, ok := deptr(loc.typ()); ok {
555+
if _, ok := deref(loc.typ()); ok {
557556
ptr := b.addr(fn, e, true).address(fn)
558557
// copy address
559558
if sb != nil {
@@ -583,7 +582,7 @@ func (b *builder) assign(fn *Function, loc lvalue, e ast.Expr, isZero bool, sb *
583582

584583
// Subtle: emit debug ref for aggregate types only;
585584
// slice and map are handled by store ops in compLit.
586-
switch loc.typ().Underlying().(type) { // TODO(taking): check if Underlying() appropriate.
585+
switch typeparams.CoreType(loc.typ()).(type) {
587586
case *types.Struct, *types.Array:
588587
emitDebugRef(fn, e, addr, true)
589588
}
@@ -1253,39 +1252,13 @@ func (b *builder) arrayLen(fn *Function, elts []ast.Expr) int64 {
12531252
// literal has type *T behaves like &T{}.
12541253
// In that case, addr must hold a T, not a *T.
12551254
func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero bool, sb *storebuf) {
1256-
typ, _ := deptr(fn.typeOf(e)) // type with name [may be type param]
1257-
t, _ := deptr(typeparams.CoreType(typ)) // core type for comp lit case
1258-
t = t.Underlying()
1259-
1260-
// Computing typ and t is subtle as these handle pointer types.
1261-
// For example, &T{...} is valid even for maps and slices.
1262-
// Also typ should refer to T (not *T) while t should be the core type of T.
1263-
//
1264-
// To show the ordering to take into account, consider the composite literal
1265-
// expressions `&T{f: 1}` and `{f: 1}` within the expression `[]S{{f: 1}}` here:
1266-
// type N struct{f int}
1267-
// func _[T N, S *N]() {
1268-
// _ = &T{f: 1}
1269-
// _ = []S{{f: 1}}
1270-
// }
1271-
// For `&T{f: 1}`, we compute `typ` and `t` as:
1272-
// typeOf(&T{f: 1}) == *T
1273-
// deref(*T) == T (typ)
1274-
// CoreType(T) == N
1275-
// deref(N) == N
1276-
// N.Underlying() == struct{f int} (t)
1277-
// For `{f: 1}` in `[]S{{f: 1}}`, we compute `typ` and `t` as:
1278-
// typeOf({f: 1}) == S
1279-
// deref(S) == S (typ)
1280-
// CoreType(S) == *N
1281-
// deref(*N) == N
1282-
// N.Underlying() == struct{f int} (t)
1283-
switch t := t.(type) {
1255+
typ, _ := deref(fn.typeOf(e)) // type with name [may be type param]
1256+
switch t := typeparams.CoreType(typ).(type) {
12841257
case *types.Struct:
12851258
if !isZero && len(e.Elts) != t.NumFields() {
12861259
// memclear
1287-
dt, _ := deptr(addr.Type())
1288-
sb.store(&address{addr, e.Lbrace, nil}, zeroConst(dt))
1260+
zt, _ := deref(addr.Type())
1261+
sb.store(&address{addr, e.Lbrace, nil}, zeroConst(zt))
12891262
isZero = true
12901263
}
12911264
for i, e := range e.Elts {
@@ -1329,8 +1302,8 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero
13291302

13301303
if !isZero && int64(len(e.Elts)) != at.Len() {
13311304
// memclear
1332-
dt, _ := deptr(array.Type())
1333-
sb.store(&address{array, e.Lbrace, nil}, zeroConst(dt))
1305+
zt, _ := deref(array.Type())
1306+
sb.store(&address{array, e.Lbrace, nil}, zeroConst(zt))
13341307
}
13351308
}
13361309

@@ -1385,7 +1358,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero
13851358
// map[*struct{}]bool{&struct{}{}: true}
13861359
wantAddr := false
13871360
if _, ok := unparen(e.Key).(*ast.CompositeLit); ok {
1388-
_, wantAddr = t.Key().Underlying().(*types.Pointer)
1361+
_, wantAddr = deref(t.Key())
13891362
}
13901363

13911364
var key Value
@@ -1416,7 +1389,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero
14161389
sb.store(&address{addr: addr, pos: e.Lbrace, expr: e}, m)
14171390

14181391
default:
1419-
panic("unexpected CompositeLit type: " + t.String())
1392+
panic("unexpected CompositeLit type: " + typ.String())
14201393
}
14211394
}
14221395

go/ssa/builder_generic_test.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,13 @@ func TestInstructionString(t *testing.T) {
607607
return u
608608
}
609609
610+
//@ instrs("f1b", "*ssa.Alloc", "new T (complit)")
611+
//@ instrs("f1b", "*ssa.FieldAddr", "&t0.x [#0]")
612+
func f1b[T ~struct{ x string }]() *T {
613+
u := &T{"lorem"}
614+
return u
615+
}
616+
610617
//@ instrs("f2", "*ssa.TypeAssert", "typeassert t0.(interface{})")
611618
//@ instrs("f2", "*ssa.Call", "invoke x.foo()")
612619
func f2[T interface{ foo() string }](x T) {
@@ -628,6 +635,61 @@ func TestInstructionString(t *testing.T) {
628635
print(i, v)
629636
}
630637
}
638+
639+
//@ instrs("f5", "*ssa.Call", "nil:func()()")
640+
func f5() {
641+
var f func()
642+
f()
643+
}
644+
645+
type S struct{ f int }
646+
647+
//@ instrs("f6", "*ssa.Alloc", "new [1]P (slicelit)", "new S (complit)")
648+
//@ instrs("f6", "*ssa.IndexAddr", "&t0[0:int]")
649+
//@ instrs("f6", "*ssa.FieldAddr", "&t2.f [#0]")
650+
func f6[P *S]() []P { return []P{{f: 1}} }
651+
652+
//@ instrs("f7", "*ssa.Alloc", "local S (complit)")
653+
//@ instrs("f7", "*ssa.FieldAddr", "&t0.f [#0]")
654+
func f7[T any, S struct{f T}](x T) S { return S{f: x} }
655+
656+
//@ instrs("f8", "*ssa.Alloc", "new [1]P (slicelit)", "new struct{f T} (complit)")
657+
//@ instrs("f8", "*ssa.IndexAddr", "&t0[0:int]")
658+
//@ instrs("f8", "*ssa.FieldAddr", "&t2.f [#0]")
659+
func f8[T any, P *struct{f T}](x T) []P { return []P{{f: x}} }
660+
661+
//@ instrs("f9", "*ssa.Alloc", "new [1]PS (slicelit)", "new S (complit)")
662+
//@ instrs("f9", "*ssa.IndexAddr", "&t0[0:int]")
663+
//@ instrs("f9", "*ssa.FieldAddr", "&t2.f [#0]")
664+
func f9[T any, S struct{f T}, PS *S](x T) {
665+
_ = []PS{{f: x}}
666+
}
667+
668+
//@ instrs("f10", "*ssa.FieldAddr", "&t0.x [#0]")
669+
//@ instrs("f10", "*ssa.Store", "*t0 = *new(T):T", "*t1 = 4:int")
670+
func f10[T ~struct{ x, y int }]() T {
671+
var u T
672+
u = T{x: 4}
673+
return u
674+
}
675+
676+
//@ instrs("f11", "*ssa.FieldAddr", "&t1.y [#1]")
677+
//@ instrs("f11", "*ssa.Store", "*t1 = *new(T):T", "*t2 = 5:int")
678+
func f11[T ~struct{ x, y int }, PT *T]() PT {
679+
var u PT = new(T)
680+
*u = T{y: 5}
681+
return u
682+
}
683+
684+
//@ instrs("f12", "*ssa.Alloc", "new struct{f T} (complit)")
685+
//@ instrs("f12", "*ssa.MakeMap", "make map[P]bool 1:int")
686+
func f12[T any, P *struct{f T}](x T) map[P]bool { return map[P]bool{{}: true} }
687+
688+
//@ instrs("f13", "&v[0:int]")
689+
//@ instrs("f13", "*ssa.Store", "*t0 = 7:int", "*v = *new(A):A")
690+
func f13[A [3]int, PA *A](v PA) {
691+
*v = A{7}
692+
}
631693
`
632694

633695
// Parse

go/ssa/emit.go

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ import (
1111
"go/ast"
1212
"go/token"
1313
"go/types"
14-
15-
"golang.org/x/tools/internal/typeparams"
1614
)
1715

1816
// emitNew emits to f a new (heap Alloc) instruction allocating an
@@ -478,9 +476,8 @@ func emitTailCall(f *Function, call *Call) {
478476
// value of a field.
479477
func emitImplicitSelections(f *Function, v Value, indices []int, pos token.Pos) Value {
480478
for _, index := range indices {
481-
st, vptr := deptr(v.Type())
482-
fld := typeparams.CoreType(st).(*types.Struct).Field(index)
483-
if vptr {
479+
if st, vptr := deptr(v.Type()); vptr {
480+
fld := fieldOf(st, index)
484481
instr := &FieldAddr{
485482
X: v,
486483
Field: index,
@@ -493,6 +490,7 @@ func emitImplicitSelections(f *Function, v Value, indices []int, pos token.Pos)
493490
v = emitLoad(f, v)
494491
}
495492
} else {
493+
fld := fieldOf(v.Type(), index)
496494
instr := &Field{
497495
X: v,
498496
Field: index,
@@ -512,15 +510,8 @@ func emitImplicitSelections(f *Function, v Value, indices []int, pos token.Pos)
512510
// field's value.
513511
// Ident id is used for position and debug info.
514512
func emitFieldSelection(f *Function, v Value, index int, wantAddr bool, id *ast.Ident) Value {
515-
// TODO(taking): Cover the following cases of interest
516-
// func f[T any, S struct{f T}, P *struct{f T}, PS *S](x T) {
517-
// _ := S{f: x}
518-
// _ := P{f: x}
519-
// _ := PS{f: x}
520-
// }
521-
st, vptr := deptr(v.Type())
522-
fld := typeparams.CoreType(st).(*types.Struct).Field(index)
523-
if vptr {
513+
if st, vptr := deptr(v.Type()); vptr {
514+
fld := fieldOf(st, index)
524515
instr := &FieldAddr{
525516
X: v,
526517
Field: index,
@@ -533,6 +524,7 @@ func emitFieldSelection(f *Function, v Value, index int, wantAddr bool, id *ast.
533524
v = emitLoad(f, v)
534525
}
535526
} else {
527+
fld := fieldOf(v.Type(), index)
536528
instr := &Field{
537529
X: v,
538530
Field: index,

go/ssa/print.go

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -259,22 +259,19 @@ func (v *MakeChan) String() string {
259259
}
260260

261261
func (v *FieldAddr) String() string {
262-
dt, _ := deptr(v.X.Type())
263-
st := typeparams.CoreType(dt).(*types.Struct)
264262
// Be robust against a bad index.
265263
name := "?"
266-
if 0 <= v.Field && v.Field < st.NumFields() {
267-
name = st.Field(v.Field).Name()
264+
if fld := fieldOf(mustDeref(v.X.Type()), v.Field); fld != nil {
265+
name = fld.Name()
268266
}
269267
return fmt.Sprintf("&%s.%s [#%d]", relName(v.X, v), name, v.Field)
270268
}
271269

272270
func (v *Field) String() string {
273-
st := typeparams.CoreType(v.X.Type()).(*types.Struct)
274271
// Be robust against a bad index.
275272
name := "?"
276-
if 0 <= v.Field && v.Field < st.NumFields() {
277-
name = st.Field(v.Field).Name()
273+
if fld := fieldOf(v.X.Type(), v.Field); fld != nil {
274+
name = fld.Name()
278275
}
279276
return fmt.Sprintf("%s.%s [#%d]", relName(v.X, v), name, v.Field)
280277
}

go/ssa/util.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,17 @@ func recvType(obj *types.Func) types.Type {
128128
return obj.Type().(*types.Signature).Recv().Type()
129129
}
130130

131+
// fieldOf returns the index'th field of the (core type of) a struct type;
132+
// otherwise returns nil.
133+
func fieldOf(typ types.Type, index int) *types.Var {
134+
if st, ok := typeparams.CoreType(typ).(*types.Struct); ok {
135+
if 0 <= index && index < st.NumFields() {
136+
return st.Field(index)
137+
}
138+
}
139+
return nil
140+
}
141+
131142
// isUntyped returns true for types that are untyped.
132143
func isUntyped(typ types.Type) bool {
133144
b, ok := typ.(*types.Basic)

0 commit comments

Comments
 (0)