Skip to content

Commit 71acab9

Browse files
committed
internal/typesparams: add Deref
This change defines Deref(T), which returns the type of the variable pointed to by T if its core type is a pointer, or T otherwise, and removes all the various other flavors of 'deref' helper that exist across the repo. Also fix and test a generics bug in fillstruct. Updates golang/go#65294 Change-Id: I14f6f35b58eefbad316391745745d662b061c013 Reviewed-on: https://go-review.googlesource.com/c/tools/+/565456 Reviewed-by: Tim King <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Robert Findley <[email protected]>
1 parent 63b3b5a commit 71acab9

File tree

21 files changed

+112
-166
lines changed

21 files changed

+112
-166
lines changed

go/analysis/passes/composite/composite.go

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,9 @@ func run(pass *analysis.Pass) (interface{}, error) {
8484
default:
8585
structuralTypes = append(structuralTypes, typ)
8686
}
87+
8788
for _, typ := range structuralTypes {
88-
// TODO(adonovan): this operation is questionable.
89-
under := aliases.Unalias(deref(typ.Underlying()))
90-
strct, ok := under.(*types.Struct)
89+
strct, ok := typeparams.Deref(typ).Underlying().(*types.Struct)
9190
if !ok {
9291
// skip non-struct composite literals
9392
continue
@@ -144,19 +143,6 @@ func run(pass *analysis.Pass) (interface{}, error) {
144143
return nil, nil
145144
}
146145

147-
// Note: this is not the usual deref operator!
148-
// It strips off all Pointer constructors (and their Aliases).
149-
func deref(typ types.Type) types.Type {
150-
for {
151-
ptr, ok := aliases.Unalias(typ).(*types.Pointer)
152-
if !ok {
153-
break
154-
}
155-
typ = ptr.Elem().Underlying()
156-
}
157-
return typ
158-
}
159-
160146
// isLocalType reports whether typ belongs to the same package as pass.
161147
// TODO(adonovan): local means "internal to a function"; rename to isSamePackageType.
162148
func isLocalType(pass *analysis.Pass, typ types.Type) bool {

go/analysis/passes/unusedwrite/unusedwrite.go

Lines changed: 3 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ package unusedwrite
66

77
import (
88
_ "embed"
9-
"fmt"
109
"go/types"
1110

1211
"golang.org/x/tools/go/analysis"
1312
"golang.org/x/tools/go/analysis/passes/buildssa"
1413
"golang.org/x/tools/go/analysis/passes/internal/analysisutil"
1514
"golang.org/x/tools/go/ssa"
1615
"golang.org/x/tools/internal/aliases"
16+
"golang.org/x/tools/internal/typeparams"
1717
)
1818

1919
//go:embed doc.go
@@ -37,9 +37,9 @@ func run(pass *analysis.Pass) (interface{}, error) {
3737
for _, store := range reports {
3838
switch addr := store.Addr.(type) {
3939
case *ssa.FieldAddr:
40+
field := typeparams.CoreType(typeparams.MustDeref(addr.X.Type())).(*types.Struct).Field(addr.Field)
4041
pass.Reportf(store.Pos(),
41-
"unused write to field %s",
42-
getFieldName(addr.X.Type(), addr.Field))
42+
"unused write to field %s", field.Name())
4343
case *ssa.IndexAddr:
4444
pass.Reportf(store.Pos(),
4545
"unused write to array index %s", addr.Index)
@@ -151,21 +151,3 @@ func hasStructOrArrayType(v ssa.Value) bool {
151151
}
152152
return isStructOrArray(v.Type())
153153
}
154-
155-
// getFieldName returns the name of a field in a struct.
156-
// It the field is not found, then it returns the string format of the index.
157-
//
158-
// For example, for struct T {x int, y int), getFieldName(*T, 1) returns "y".
159-
func getFieldName(tp types.Type, index int) string {
160-
// TODO(adonovan): use
161-
// stp, ok := typeparams.Deref(tp).Underlying().(*types.Struct); ok {
162-
// when Deref is defined. But see CL 565456 for a better fix.
163-
164-
if pt, ok := aliases.Unalias(tp).(*types.Pointer); ok {
165-
tp = pt.Elem()
166-
}
167-
if stp, ok := tp.Underlying().(*types.Struct); ok {
168-
return stp.Field(index).Name()
169-
}
170-
return fmt.Sprintf("%d", index)
171-
}

go/internal/gccgoimporter/parser.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"unicode/utf8"
2323

2424
"golang.org/x/tools/internal/aliases"
25+
"golang.org/x/tools/internal/typesinternal"
2526
)
2627

2728
type parser struct {
@@ -242,13 +243,6 @@ func (p *parser) parseName() string {
242243
return name
243244
}
244245

245-
func deref(typ types.Type) types.Type {
246-
if p, _ := aliases.Unalias(typ).(*types.Pointer); p != nil {
247-
typ = p.Elem()
248-
}
249-
return typ
250-
}
251-
252246
// parseField parses a Field:
253247
//
254248
// Field = Name Type [string] .
@@ -262,7 +256,7 @@ func (p *parser) parseField(pkg *types.Package) (field *types.Var, tag string) {
262256
if aname, ok := p.aliases[n]; ok {
263257
name = aname
264258
} else {
265-
switch typ := aliases.Unalias(deref(typ)).(type) {
259+
switch typ := aliases.Unalias(typesinternal.Unpointer(typ)).(type) {
266260
case *types.Basic:
267261
name = typ.Name()
268262
case *types.Named:

go/ssa/builder.go

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ func (b *builder) builtin(fn *Function, obj *types.Builtin, args []ast.Expr, typ
336336
// We must still evaluate the value, though. (If it
337337
// was side-effect free, the whole call would have
338338
// been constant-folded.)
339-
t, _ := deref(fn.typeOf(args[0]))
339+
t := typeparams.Deref(fn.typeOf(args[0]))
340340
if at, ok := typeparams.CoreType(t).(*types.Array); ok {
341341
b.expr(fn, args[0]) // for effects only
342342
return intConst(at.Len())
@@ -392,7 +392,7 @@ func (b *builder) addr(fn *Function, e ast.Expr, escaping bool) lvalue {
392392
return &address{addr: v, pos: e.Pos(), expr: e}
393393

394394
case *ast.CompositeLit:
395-
typ, _ := deref(fn.typeOf(e))
395+
typ := typeparams.Deref(fn.typeOf(e))
396396
var v *Alloc
397397
if escaping {
398398
v = emitNew(fn, typ, e.Lbrace, "complit")
@@ -513,17 +513,15 @@ func (b *builder) assign(fn *Function, loc lvalue, e ast.Expr, isZero bool, sb *
513513
// A CompositeLit never evaluates to a pointer,
514514
// so if the type of the location is a pointer,
515515
// an &-operation is implied.
516-
if _, ok := loc.(blank); !ok { // avoid calling blank.typ()
517-
if _, ok := deref(loc.typ()); ok {
518-
ptr := b.addr(fn, e, true).address(fn)
519-
// copy address
520-
if sb != nil {
521-
sb.store(loc, ptr)
522-
} else {
523-
loc.store(fn, ptr)
524-
}
525-
return
516+
if !is[blank](loc) && isPointerCore(loc.typ()) { // avoid calling blank.typ()
517+
ptr := b.addr(fn, e, true).address(fn)
518+
// copy address
519+
if sb != nil {
520+
sb.store(loc, ptr)
521+
} else {
522+
loc.store(fn, ptr)
526523
}
524+
return
527525
}
528526

529527
if _, ok := loc.(*address); ok {
@@ -795,7 +793,7 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value {
795793
// The result is a "bound".
796794
obj := sel.obj.(*types.Func)
797795
rt := fn.typ(recvType(obj))
798-
_, wantAddr := deref(rt)
796+
wantAddr := isPointer(rt)
799797
escaping := true
800798
v := b.receiver(fn, e.X, wantAddr, escaping, sel)
801799

@@ -923,7 +921,7 @@ func (b *builder) stmtList(fn *Function, list []ast.Stmt) {
923921
// escaping is defined as per builder.addr().
924922
func (b *builder) receiver(fn *Function, e ast.Expr, wantAddr, escaping bool, sel *selection) Value {
925923
var v Value
926-
if _, eptr := deref(fn.typeOf(e)); wantAddr && !sel.indirect && !eptr {
924+
if wantAddr && !sel.indirect && !isPointerCore(fn.typeOf(e)) {
927925
v = b.addr(fn, e, escaping).address(fn)
928926
} else {
929927
v = b.expr(fn, e)
@@ -935,7 +933,7 @@ func (b *builder) receiver(fn *Function, e ast.Expr, wantAddr, escaping bool, se
935933
if types.IsInterface(v.Type()) {
936934
// When v is an interface, sel.Kind()==MethodValue and v.f is invoked.
937935
// So v is not loaded, even if v has a pointer core type.
938-
} else if _, vptr := deref(v.Type()); !wantAddr && vptr {
936+
} else if !wantAddr && isPointerCore(v.Type()) {
939937
v = emitLoad(fn, v)
940938
}
941939
return v
@@ -954,7 +952,7 @@ func (b *builder) setCallFunc(fn *Function, e *ast.CallExpr, c *CallCommon) {
954952
obj := sel.obj.(*types.Func)
955953
recv := recvType(obj)
956954

957-
_, wantAddr := deref(recv)
955+
wantAddr := isPointer(recv)
958956
escaping := true
959957
v := b.receiver(fn, selector.X, wantAddr, escaping, sel)
960958
if types.IsInterface(recv) {
@@ -1215,12 +1213,12 @@ func (b *builder) arrayLen(fn *Function, elts []ast.Expr) int64 {
12151213
// literal has type *T behaves like &T{}.
12161214
// In that case, addr must hold a T, not a *T.
12171215
func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero bool, sb *storebuf) {
1218-
typ, _ := deref(fn.typeOf(e)) // type with name [may be type param]
1216+
typ := typeparams.Deref(fn.typeOf(e)) // retain the named/alias/param type, if any
12191217
switch t := typeparams.CoreType(typ).(type) {
12201218
case *types.Struct:
12211219
if !isZero && len(e.Elts) != t.NumFields() {
12221220
// memclear
1223-
zt, _ := deref(addr.Type())
1221+
zt := typeparams.MustDeref(addr.Type())
12241222
sb.store(&address{addr, e.Lbrace, nil}, zeroConst(zt))
12251223
isZero = true
12261224
}
@@ -1263,7 +1261,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero
12631261

12641262
if !isZero && int64(len(e.Elts)) != at.Len() {
12651263
// memclear
1266-
zt, _ := deref(array.Type())
1264+
zt := typeparams.MustDeref(array.Type())
12671265
sb.store(&address{array, e.Lbrace, nil}, zeroConst(zt))
12681266
}
12691267
}
@@ -1319,7 +1317,7 @@ func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero
13191317
// map[*struct{}]bool{&struct{}{}: true}
13201318
wantAddr := false
13211319
if _, ok := unparen(e.Key).(*ast.CompositeLit); ok {
1322-
_, wantAddr = deref(t.Key())
1320+
wantAddr = isPointerCore(t.Key())
13231321
}
13241322

13251323
var key Value
@@ -1992,7 +1990,7 @@ func (b *builder) rangeIndexed(fn *Function, x Value, tv types.Type, pos token.P
19921990

19931991
// Determine number of iterations.
19941992
var length Value
1995-
dt, _ := deref(x.Type())
1993+
dt := typeparams.Deref(x.Type())
19961994
if arr, ok := typeparams.CoreType(dt).(*types.Array); ok {
19971995
// For array or *array, the number of iterations is
19981996
// known statically thanks to the type. We avoid a

go/ssa/emit.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -524,8 +524,8 @@ func emitTailCall(f *Function, call *Call) {
524524
// value of a field.
525525
func emitImplicitSelections(f *Function, v Value, indices []int, pos token.Pos) Value {
526526
for _, index := range indices {
527-
if st, vptr := deref(v.Type()); vptr {
528-
fld := fieldOf(st, index)
527+
if isPointerCore(v.Type()) {
528+
fld := fieldOf(typeparams.MustDeref(v.Type()), index)
529529
instr := &FieldAddr{
530530
X: v,
531531
Field: index,
@@ -534,7 +534,7 @@ func emitImplicitSelections(f *Function, v Value, indices []int, pos token.Pos)
534534
instr.setType(types.NewPointer(fld.Type()))
535535
v = f.emit(instr)
536536
// Load the field's value iff indirectly embedded.
537-
if _, fldptr := deref(fld.Type()); fldptr {
537+
if isPointerCore(fld.Type()) {
538538
v = emitLoad(f, v)
539539
}
540540
} else {
@@ -558,8 +558,8 @@ func emitImplicitSelections(f *Function, v Value, indices []int, pos token.Pos)
558558
// field's value.
559559
// Ident id is used for position and debug info.
560560
func emitFieldSelection(f *Function, v Value, index int, wantAddr bool, id *ast.Ident) Value {
561-
if st, vptr := deref(v.Type()); vptr {
562-
fld := fieldOf(st, index)
561+
if isPointerCore(v.Type()) {
562+
fld := fieldOf(typeparams.MustDeref(v.Type()), index)
563563
instr := &FieldAddr{
564564
X: v,
565565
Field: index,

go/ssa/interp/interp.go

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import (
5454
"sync/atomic"
5555

5656
"golang.org/x/tools/go/ssa"
57+
"golang.org/x/tools/internal/typeparams"
5758
)
5859

5960
type continuation int
@@ -245,7 +246,7 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation {
245246
fr.get(instr.Chan).(chan value) <- fr.get(instr.X)
246247

247248
case *ssa.Store:
248-
store(deref(instr.Addr.Type()), fr.get(instr.Addr).(*value), fr.get(instr.Val))
249+
store(typeparams.MustDeref(instr.Addr.Type()), fr.get(instr.Addr).(*value), fr.get(instr.Val))
249250

250251
case *ssa.If:
251252
succ := 1
@@ -289,7 +290,7 @@ func visitInstr(fr *frame, instr ssa.Instruction) continuation {
289290
// local
290291
addr = fr.env[instr].(*value)
291292
}
292-
*addr = zero(deref(instr.Type()))
293+
*addr = zero(typeparams.MustDeref(instr.Type()))
293294

294295
case *ssa.MakeSlice:
295296
slice := make([]value, asInt64(fr.get(instr.Cap)))
@@ -528,7 +529,7 @@ func callSSA(i *interpreter, caller *frame, callpos token.Pos, fn *ssa.Function,
528529
fr.block = fn.Blocks[0]
529530
fr.locals = make([]value, len(fn.Locals))
530531
for i, l := range fn.Locals {
531-
fr.locals[i] = zero(deref(l.Type()))
532+
fr.locals[i] = zero(typeparams.MustDeref(l.Type()))
532533
fr.env[l] = &fr.locals[i]
533534
}
534535
for i, p := range fn.Params {
@@ -673,7 +674,7 @@ func Interpret(mainpkg *ssa.Package, mode Mode, sizes types.Sizes, filename stri
673674
for _, m := range pkg.Members {
674675
switch v := m.(type) {
675676
case *ssa.Global:
676-
cell := zero(deref(v.Type()))
677+
cell := zero(typeparams.MustDeref(v.Type()))
677678
i.globals[v] = &cell
678679
}
679680
}
@@ -717,12 +718,3 @@ func Interpret(mainpkg *ssa.Package, mode Mode, sizes types.Sizes, filename stri
717718
}
718719
return
719720
}
720-
721-
// deref returns a pointer's element type; otherwise it returns typ.
722-
// TODO(adonovan): Import from ssa?
723-
func deref(typ types.Type) types.Type {
724-
if p, ok := typ.Underlying().(*types.Pointer); ok {
725-
return p.Elem()
726-
}
727-
return typ
728-
}

go/ssa/interp/ops.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818

1919
"golang.org/x/tools/go/ssa"
2020
"golang.org/x/tools/internal/aliases"
21+
"golang.org/x/tools/internal/typeparams"
2122
)
2223

2324
// If the target program panics, the interpreter panics with this type.
@@ -884,7 +885,7 @@ func unop(instr *ssa.UnOp, x value) value {
884885
return -x
885886
}
886887
case token.MUL:
887-
return load(deref(instr.X.Type()), x.(*value))
888+
return load(typeparams.MustDeref(instr.X.Type()), x.(*value))
888889
case token.NOT:
889890
return !x.(bool)
890891
case token.XOR:

go/ssa/methods.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,8 @@ func (prog *Program) MethodValue(sel *types.Selection) *Function {
5858
fn, ok := mset.mapping[id]
5959
if !ok {
6060
obj := sel.Obj().(*types.Func)
61-
_, ptrObj := deptr(recvType(obj))
62-
_, ptrRecv := deptr(T)
6361
needsPromotion := len(sel.Index()) > 1
64-
needsIndirection := !ptrObj && ptrRecv
62+
needsIndirection := !isPointer(recvType(obj)) && isPointer(T)
6563
if needsPromotion || needsIndirection {
6664
fn = createWrapper(prog, toSelection(sel), &cr)
6765
} else {

go/ssa/util.go

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -101,24 +101,22 @@ func isBasicConvTypes(tset termList) bool {
101101
return all && basics >= 1 && tset.Len()-basics <= 1
102102
}
103103

104-
// deptr returns a pointer's element type and true; otherwise it returns (typ, false).
105-
// This function is oblivious to core types and is not suitable for generics.
104+
// isPointer reports whether t's underlying type is a pointer.
105+
func isPointer(t types.Type) bool {
106+
return is[*types.Pointer](t.Underlying())
107+
}
108+
109+
// isPointerCore reports whether t's core type is a pointer.
106110
//
107-
// TODO: Deprecate this function once all usages have been audited.
108-
func deptr(typ types.Type) (types.Type, bool) {
109-
if p, ok := typ.Underlying().(*types.Pointer); ok {
110-
return p.Elem(), true
111-
}
112-
return typ, false
111+
// (Most pointer manipulation is related to receivers, in which case
112+
// isPointer is appropriate. tecallers can use isPointer(t).
113+
func isPointerCore(t types.Type) bool {
114+
return is[*types.Pointer](typeparams.CoreType(t))
113115
}
114116

115-
// deref returns the element type of a type with a pointer core type and true;
116-
// otherwise it returns (typ, false).
117-
func deref(typ types.Type) (types.Type, bool) {
118-
if p, ok := typeparams.CoreType(typ).(*types.Pointer); ok {
119-
return p.Elem(), true
120-
}
121-
return typ, false
117+
func is[T any](x any) bool {
118+
_, ok := x.(T)
119+
return ok
122120
}
123121

124122
// recvType returns the receiver type of method obj.

0 commit comments

Comments
 (0)