Skip to content

Commit 7f203f0

Browse files
committed
go/ssa: consolidate use of underlying pointer
Consolidates the use of typ.Underlying().(*types.Pointer) to the deptr function. This function replace isPointer and the old deref function. Both have been replaced. This allows for tracking where underlying pointers are used. Follow up CLs will try to move away from using underlying pointers when necessary. Switches deptr for mustDeref in obviously safe locations (alloc.Type()). Adds a new deref function that uses the core type instead of the underlying type. Change-Id: Id51f95e87cb40a13d43fd595d1f20b21b8325eeb Reviewed-on: https://go-review.googlesource.com/c/tools/+/494976 TryBot-Result: Gopher Robot <[email protected]> Run-TryBot: Tim King <[email protected]> gopls-CI: kokoro <[email protected]> Reviewed-by: Alan Donovan <[email protected]>
1 parent a13793e commit 7f203f0

10 files changed

+108
-69
lines changed

go/ssa/builder.go

+31-18
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ func (b *builder) builtin(fn *Function, obj *types.Builtin, args []ast.Expr, typ
363363
}
364364

365365
case "new":
366-
alloc := emitNew(fn, deref(typ), pos)
366+
alloc := emitNew(fn, mustDeref(typ), pos)
367367
alloc.Comment = "new"
368368
return alloc
369369

@@ -375,8 +375,8 @@ func (b *builder) builtin(fn *Function, obj *types.Builtin, args []ast.Expr, typ
375375
// been constant-folded.)
376376
//
377377
// Type parameters are always non-constant so use Underlying.
378-
t := deref(fn.typeOf(args[0])).Underlying()
379-
if at, ok := t.(*types.Array); ok {
378+
t, _ := deptr(fn.typeOf(args[0]))
379+
if at, ok := t.Underlying().(*types.Array); ok {
380380
b.expr(fn, args[0]) // for effects only
381381
return intConst(at.Len())
382382
}
@@ -431,7 +431,7 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
431431
return &address{addr: v, pos: e.Pos(), expr: e}
432432

433433
case *ast.CompositeLit:
434-
t := deref(fn.typeOf(e))
434+
t, _ := deptr(fn.typeOf(e))
435435
var v *Alloc
436436
if escaping {
437437
v = emitNew(fn, t, e.Lbrace)
@@ -459,7 +459,8 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
459459
wantAddr := true
460460
v := b.receiver(fn, e.X, wantAddr, escaping, sel)
461461
index := sel.index[len(sel.index)-1]
462-
fld := typeparams.CoreType(deref(v.Type())).(*types.Struct).Field(index)
462+
dt, _ := deptr(v.Type())
463+
fld := typeparams.CoreType(dt).(*types.Struct).Field(index)
463464

464465
// Due to the two phases of resolving AssignStmt, a panic from x.f = p()
465466
// when x is nil is required to come after the side-effects of
@@ -508,7 +509,7 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
508509
v.setType(et)
509510
return fn.emit(v)
510511
}
511-
return &lazyAddress{addr: emit, t: deref(et), pos: e.Lbrack, expr: e}
512+
return &lazyAddress{addr: emit, t: mustDeref(et), pos: e.Lbrack, expr: e}
512513

513514
case *ast.StarExpr:
514515
return &address{addr: b.expr(fn, e.X), pos: e.Star, expr: e}
@@ -554,7 +555,7 @@ func (b *builder) assign(fn *Function, loc lvalue, e ast.Expr, isZero bool, sb *
554555
// so if the type of the location is a pointer,
555556
// an &-operation is implied.
556557
if _, ok := loc.(blank); !ok { // avoid calling blank.typ()
557-
if isPointer(loc.typ()) {
558+
if _, ok := deptr(loc.typ()); ok {
558559
ptr := b.addr(fn, e, true).address(fn)
559560
// copy address
560561
if sb != nil {
@@ -584,7 +585,7 @@ func (b *builder) assign(fn *Function, loc lvalue, e ast.Expr, isZero bool, sb *
584585

585586
// Subtle: emit debug ref for aggregate types only;
586587
// slice and map are handled by store ops in compLit.
587-
switch loc.typ().Underlying().(type) {
588+
switch loc.typ().Underlying().(type) { // TODO(taking): check if Underlying() appropriate.
588589
case *types.Struct, *types.Array:
589590
emitDebugRef(fn, e, addr, true)
590591
}
@@ -831,7 +832,7 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value {
831832
// The result is a "bound".
832833
obj := sel.obj.(*types.Func)
833834
rt := fn.typ(recvType(obj))
834-
wantAddr := isPointer(rt)
835+
_, wantAddr := deptr(rt)
835836
escaping := true
836837
v := b.receiver(fn, e.X, wantAddr, escaping, sel)
837838

@@ -958,8 +959,9 @@ func (b *builder) stmtList(fn *Function, list []ast.Stmt) {
958959
//
959960
// escaping is defined as per builder.addr().
960961
func (b *builder) receiver(fn *Function, e ast.Expr, wantAddr, escaping bool, sel *selection) Value {
962+
961963
var v Value
962-
if wantAddr && !sel.indirect && !isPointer(fn.typeOf(e)) {
964+
if _, eptr := deptr(fn.typeOf(e)); wantAddr && !sel.indirect && !eptr {
963965
v = b.addr(fn, e, escaping).address(fn)
964966
} else {
965967
v = b.expr(fn, e)
@@ -968,7 +970,7 @@ func (b *builder) receiver(fn *Function, e ast.Expr, wantAddr, escaping bool, se
968970
last := len(sel.index) - 1
969971
// The position of implicit selection is the position of the inducing receiver expression.
970972
v = emitImplicitSelections(fn, v, sel.index[:last], e.Pos())
971-
if !wantAddr && isPointer(v.Type()) {
973+
if _, vptr := deptr(v.Type()); !wantAddr && vptr {
972974
v = emitLoad(fn, v)
973975
}
974976
return v
@@ -987,7 +989,7 @@ func (b *builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) {
987989
obj := sel.obj.(*types.Func)
988990
recv := recvType(obj)
989991

990-
wantAddr := isPointer(recv)
992+
_, wantAddr := deptr(recv)
991993
escaping := true
992994
v := b.receiver(fn, selector.X, wantAddr, escaping, sel)
993995
if types.IsInterface(recv) {
@@ -1253,8 +1255,10 @@ func (b *builder) arrayLen(fn *Function, elts []ast.Expr) int64 {
12531255
// literal has type *T behaves like &T{}.
12541256
// In that case, addr must hold a T, not a *T.
12551257
func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero bool, sb *storebuf) {
1256-
typ := deref(fn.typeOf(e)) // type with name [may be type param]
1257-
t := deref(typeparams.CoreType(typ)).Underlying() // core type for comp lit case
1258+
typ, _ := deptr(fn.typeOf(e)) // type with name [may be type param]
1259+
t, _ := deptr(typeparams.CoreType(typ)) // core type for comp lit case
1260+
t = t.Underlying()
1261+
12581262
// Computing typ and t is subtle as these handle pointer types.
12591263
// For example, &T{...} is valid even for maps and slices.
12601264
// Also typ should refer to T (not *T) while t should be the core type of T.
@@ -1282,7 +1286,8 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero
12821286
case *types.Struct:
12831287
if !isZero && len(e.Elts) != t.NumFields() {
12841288
// memclear
1285-
sb.store(&address{addr, e.Lbrace, nil}, zeroConst(deref(addr.Type())))
1289+
dt, _ := deptr(addr.Type())
1290+
sb.store(&address{addr, e.Lbrace, nil}, zeroConst(dt))
12861291
isZero = true
12871292
}
12881293
for i, e := range e.Elts {
@@ -1326,7 +1331,8 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero
13261331

13271332
if !isZero && int64(len(e.Elts)) != at.Len() {
13281333
// memclear
1329-
sb.store(&address{array, e.Lbrace, nil}, zeroConst(deref(array.Type())))
1334+
dt, _ := deptr(array.Type())
1335+
sb.store(&address{array, e.Lbrace, nil}, zeroConst(dt))
13301336
}
13311337
}
13321338

@@ -1379,8 +1385,13 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero
13791385
// map[*struct{}]bool{{}: true}
13801386
// An &-operation may be implied:
13811387
// map[*struct{}]bool{&struct{}{}: true}
1388+
wantAddr := false
1389+
if _, ok := unparen(e.Key).(*ast.CompositeLit); ok {
1390+
_, wantAddr = t.Key().Underlying().(*types.Pointer)
1391+
}
1392+
13821393
var key Value
1383-
if _, ok := unparen(e.Key).(*ast.CompositeLit); ok && isPointer(t.Key()) {
1394+
if wantAddr {
13841395
// A CompositeLit never evaluates to a pointer,
13851396
// so if the type of the location is a pointer,
13861397
// an &-operation is implied.
@@ -1873,7 +1884,8 @@ func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type, pos token.P
18731884

18741885
// Determine number of iterations.
18751886
var length Value
1876-
if arr, ok := deref(x.Type()).Underlying().(*types.Array); ok {
1887+
dt, _ := deptr(x.Type())
1888+
if arr, ok := dt.Underlying().(*types.Array); ok {
18771889
// For array or *array, the number of iterations is
18781890
// known statically thanks to the type. We avoid a
18791891
// data dependence upon x, permitting later dead-code
@@ -1882,6 +1894,7 @@ func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type, pos token.P
18821894
// We still generate code for x, in case it has effects.
18831895
//
18841896
// TypeParams do not have constant length. Use underlying instead of core type.
1897+
// TODO: check if needed.
18851898
length = intConst(arr.Len())
18861899
} else {
18871900
// length = len(x).

go/ssa/builder_generic_test.go

+23-13
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package ssa_test
66

77
import (
8+
"bytes"
89
"fmt"
910
"go/parser"
1011
"go/token"
@@ -540,30 +541,30 @@ func TestInstructionString(t *testing.T) {
540541
const contents = `
541542
package p
542543
543-
//@ instrs("f", "*ssa.TypeAssert")
544-
//@ instrs("f", "*ssa.Call", "print(nil:interface{}, 0:int)")
545-
func f(x int) { // non-generic smoke test.
544+
//@ instrs("f0", "*ssa.TypeAssert")
545+
//@ instrs("f0", "*ssa.Call", "print(nil:interface{}, 0:int)")
546+
func f0(x int) { // non-generic smoke test.
546547
var i interface{}
547548
print(i, 0)
548549
}
549550
550-
//@ instrs("h", "*ssa.Alloc", "local T (u)")
551-
//@ instrs("h", "*ssa.FieldAddr", "&t0.x [#0]")
552-
func h[T ~struct{ x string }]() T {
551+
//@ instrs("f1", "*ssa.Alloc", "local T (u)")
552+
//@ instrs("f1", "*ssa.FieldAddr", "&t0.x [#0]")
553+
func f1[T ~struct{ x string }]() T {
553554
u := T{"lorem"}
554555
return u
555556
}
556557
557-
//@ instrs("c", "*ssa.TypeAssert", "typeassert t0.(interface{})")
558-
//@ instrs("c", "*ssa.Call", "invoke x.foo()")
559-
func c[T interface{ foo() string }](x T) {
558+
//@ instrs("f2", "*ssa.TypeAssert", "typeassert t0.(interface{})")
559+
//@ instrs("f2", "*ssa.Call", "invoke x.foo()")
560+
func f2[T interface{ foo() string }](x T) {
560561
_ = x.foo
561562
_ = x.foo()
562563
}
563564
564-
//@ instrs("d", "*ssa.TypeAssert", "typeassert t0.(interface{})")
565-
//@ instrs("d", "*ssa.Call", "invoke x.foo()")
566-
func d[T interface{ foo() string; comparable }](x T) {
565+
//@ instrs("f3", "*ssa.TypeAssert", "typeassert t0.(interface{})")
566+
//@ instrs("f3", "*ssa.Call", "invoke x.foo()")
567+
func f3[T interface{ foo() string; comparable }](x T) {
567568
_ = x.foo
568569
_ = x.foo()
569570
}
@@ -643,14 +644,16 @@ func TestInstructionString(t *testing.T) {
643644

644645
// Check each expectation.
645646
for key, value := range expectations {
646-
if _, ok := p.Members[key.function]; !ok {
647+
fn, ok := p.Members[key.function].(*ssa.Function)
648+
if !ok {
647649
t.Errorf("Expectation on %s does not match a member in %s", key.function, p.Pkg.Name())
648650
}
649651
got, want := value.matches, value.wants
650652
sort.Strings(got)
651653
sort.Strings(want)
652654
if !reflect.DeepEqual(want, got) {
653655
t.Errorf("Within %s wanted instructions of kind %s: %q. got %q", key.function, key.kind, want, got)
656+
logFunction(t, fn)
654657
}
655658
}
656659
}
@@ -664,3 +667,10 @@ func packageName(t testing.TB, content string) string {
664667
}
665668
return f.Name.Name
666669
}
670+
671+
func logFunction(t testing.TB, fn *ssa.Function) {
672+
// TODO: Consider adding a ssa.Function.GoString() so this can be logged to t via '%#v'.
673+
var buf bytes.Buffer
674+
ssa.WriteFunction(&buf, fn)
675+
t.Log(buf.String())
676+
}

go/ssa/emit.go

+14-7
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func emitNew(f *Function, typ types.Type, pos token.Pos) *Alloc {
2929
// new temporary, and returns the value so defined.
3030
func emitLoad(f *Function, addr Value) *UnOp {
3131
v := &UnOp{Op: token.MUL, X: addr}
32-
v.setType(deref(typeparams.CoreType(addr.Type())))
32+
v.setType(mustDeref(addr.Type()))
3333
f.emit(v)
3434
return v
3535
}
@@ -478,9 +478,9 @@ func emitTailCall(f *Function, call *Call) {
478478
// value of a field.
479479
func emitImplicitSelections(f *Function, v Value, indices []int, pos token.Pos) Value {
480480
for _, index := range indices {
481-
fld := typeparams.CoreType(deref(v.Type())).(*types.Struct).Field(index)
482-
483-
if isPointer(v.Type()) {
481+
st, vptr := deptr(v.Type())
482+
fld := typeparams.CoreType(st).(*types.Struct).Field(index)
483+
if vptr {
484484
instr := &FieldAddr{
485485
X: v,
486486
Field: index,
@@ -489,7 +489,7 @@ func emitImplicitSelections(f *Function, v Value, indices []int, pos token.Pos)
489489
instr.setType(types.NewPointer(fld.Type()))
490490
v = f.emit(instr)
491491
// Load the field's value iff indirectly embedded.
492-
if isPointer(fld.Type()) {
492+
if _, fldptr := deptr(fld.Type()); fldptr {
493493
v = emitLoad(f, v)
494494
}
495495
} else {
@@ -512,8 +512,15 @@ func emitImplicitSelections(f *Function, v Value, indices []int, pos token.Pos)
512512
// field's value.
513513
// Ident id is used for position and debug info.
514514
func emitFieldSelection(f *Function, v Value, index int, wantAddr bool, id *ast.Ident) Value {
515-
fld := typeparams.CoreType(deref(v.Type())).(*types.Struct).Field(index)
516-
if isPointer(v.Type()) {
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 {
517524
instr := &FieldAddr{
518525
X: v,
519526
Field: index,

go/ssa/func.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,7 @@ func WriteFunction(buf *bytes.Buffer, f *Function) {
596596
if len(f.Locals) > 0 {
597597
buf.WriteString("# Locals:\n")
598598
for i, l := range f.Locals {
599-
fmt.Fprintf(buf, "# % 3d:\t%s %s\n", i, l.Name(), relType(deref(l.Type()), from))
599+
fmt.Fprintf(buf, "# % 3d:\t%s %s\n", i, l.Name(), relType(mustDeref(l.Type()), from))
600600
}
601601
}
602602
writeSignature(buf, from, f.Name(), f.Signature, f.Params)

go/ssa/lift.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,7 @@ func liftAlloc(df domFrontier, alloc *Alloc, newPhis newPhiMap, fresh *int) bool
460460
*fresh++
461461

462462
phi.pos = alloc.Pos()
463-
phi.setType(deref(alloc.Type()))
463+
phi.setType(mustDeref(alloc.Type()))
464464
phi.block = v
465465
if debugLifting {
466466
fmt.Fprintf(os.Stderr, "\tplace %s = %s at block %s\n", phi.Name(), phi, v)
@@ -505,7 +505,7 @@ func replaceAll(x, y Value) {
505505
func renamed(renaming []Value, alloc *Alloc) Value {
506506
v := renaming[alloc.index]
507507
if v == nil {
508-
v = zeroConst(deref(alloc.Type()))
508+
v = zeroConst(mustDeref(alloc.Type()))
509509
renaming[alloc.index] = v
510510
}
511511
return v

go/ssa/methods.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,11 @@ func (prog *Program) addMethod(mset *methodSet, sel *types.Selection, cr *creato
101101
sel := toSelection(sel)
102102
obj := sel.obj.(*types.Func)
103103

104+
_, ptrObj := deptr(recvType(obj))
105+
_, ptrRecv := deptr(sel.recv)
106+
104107
needsPromotion := len(sel.index) > 1
105-
needsIndirection := !isPointer(recvType(obj)) && isPointer(sel.recv)
108+
needsIndirection := !ptrObj && ptrRecv
106109
if needsPromotion || needsIndirection {
107110
fn = makeWrapper(prog, sel, cr)
108111
} else {

go/ssa/print.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ func (v *Alloc) String() string {
9595
op = "new"
9696
}
9797
from := v.Parent().relPkg()
98-
return fmt.Sprintf("%s %s (%s)", op, relType(deref(v.Type()), from), v.Comment)
98+
return fmt.Sprintf("%s %s (%s)", op, relType(mustDeref(v.Type()), from), v.Comment)
9999
}
100100

101101
func (v *Phi) String() string {
@@ -259,7 +259,8 @@ func (v *MakeChan) String() string {
259259
}
260260

261261
func (v *FieldAddr) String() string {
262-
st := typeparams.CoreType(deref(v.X.Type())).(*types.Struct)
262+
dt, _ := deptr(v.X.Type())
263+
st := typeparams.CoreType(dt).(*types.Struct)
263264
// Be robust against a bad index.
264265
name := "?"
265266
if 0 <= v.Field && v.Field < st.NumFields() {
@@ -452,7 +453,7 @@ func WritePackage(buf *bytes.Buffer, p *Package) {
452453

453454
case *Global:
454455
fmt.Fprintf(buf, " var %-*s %s\n",
455-
maxname, name, relType(mem.Type().(*types.Pointer).Elem(), from))
456+
maxname, name, relType(mustDeref(mem.Type()), from))
456457
}
457458
}
458459

go/ssa/subst.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ func (subst *subster) interface_(iface *types.Interface) *types.Interface {
249249
}
250250

251251
// methods for the interface. Initially nil if there is no known change needed.
252-
// Signatures for the method where recv is nil. NewInterfaceType fills in the recievers.
252+
// Signatures for the method where recv is nil. NewInterfaceType fills in the receivers.
253253
var methods []*types.Func
254254
initMethods := func(n int) { // copy first n explicit methods
255255
methods = make([]*types.Func, iface.NumExplicitMethods())
@@ -262,7 +262,7 @@ func (subst *subster) interface_(iface *types.Interface) *types.Interface {
262262
for i := 0; i < iface.NumExplicitMethods(); i++ {
263263
f := iface.ExplicitMethod(i)
264264
// On interfaces, we need to cycle break on anonymous interface types
265-
// being in a cycle with their signatures being in cycles with their recievers
265+
// being in a cycle with their signatures being in cycles with their receivers
266266
// that do not go through a Named.
267267
norecv := changeRecv(f.Type().(*types.Signature), nil)
268268
sig := subst.typ(norecv)

0 commit comments

Comments
 (0)