Skip to content

Commit cecec2c

Browse files
timothy-kingGo LUCI
authored andcommitted
go/ssa: add typeset iteration helper
Adds a typeset function for iterating over the type set of a type. This is effectively copying the internal function (go/types).typeset. Renames typeSetOf to termListOf. This additionally shifts usage away from termListOf to typeset where convenient. Change-Id: Ia51d2f9ef648b616646b063ee1adb178863485cf Reviewed-on: https://go-review.googlesource.com/c/tools/+/641839 Reviewed-by: Alan Donovan <[email protected]> Reviewed-by: Robert Findley <[email protected]> Commit-Queue: Tim King <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent 1501321 commit cecec2c

File tree

5 files changed

+105
-71
lines changed

5 files changed

+105
-71
lines changed

go/ssa/builder.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -856,7 +856,7 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value {
856856
if recv, ok := types.Unalias(sel.recv).(*types.TypeParam); ok {
857857
// Emit a nil check if any possible instantiation of the
858858
// type parameter is an interface type.
859-
if len(typeSetOf(recv)) > 0 {
859+
if !typeSetIsEmpty(recv) {
860860
// recv has a concrete term its typeset.
861861
// So it cannot be instantiated as an interface.
862862
//

go/ssa/emit.go

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -257,13 +257,6 @@ func emitConv(f *Function, val Value, typ types.Type) Value {
257257
return f.emit(mi)
258258
}
259259

260-
// In the common case, the typesets of src and dst are singletons
261-
// and we emit an appropriate conversion. But if either contains
262-
// a type parameter, the conversion may represent a cross product,
263-
// in which case which we emit a MultiConvert.
264-
dst_terms := typeSetOf(ut_dst)
265-
src_terms := typeSetOf(ut_src)
266-
267260
// conversionCase describes an instruction pattern that maybe emitted to
268261
// model d <- s for d in dst_terms and s in src_terms.
269262
// Multiple conversions can match the same pattern.
@@ -321,13 +314,14 @@ func emitConv(f *Function, val Value, typ types.Type) Value {
321314
}
322315

323316
var classifications conversionCase
324-
for _, s := range src_terms {
325-
us := s.Type().Underlying()
326-
for _, d := range dst_terms {
327-
ud := d.Type().Underlying()
328-
classifications |= classify(us, ud)
329-
}
330-
}
317+
underIs(ut_src, func(us types.Type) bool {
318+
return underIs(ut_dst, func(ud types.Type) bool {
319+
if us != nil && ud != nil {
320+
classifications |= classify(us, ud)
321+
}
322+
return classifications != 0
323+
})
324+
})
331325
if classifications == 0 {
332326
panic(fmt.Sprintf("in %s: cannot convert %s (%s) to %s", f, val, val.Type(), typ))
333327
}
@@ -381,8 +375,8 @@ func emitConv(f *Function, val Value, typ types.Type) Value {
381375
c.setType(typ)
382376
return f.emit(c)
383377

384-
default: // multiple conversion
385-
c := &MultiConvert{X: val, from: src_terms, to: dst_terms}
378+
default: // The conversion represents a cross product.
379+
c := &MultiConvert{X: val, from: t_src, to: typ}
386380
c.setType(typ)
387381
return f.emit(c)
388382
}

go/ssa/print.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,8 @@ func (v *MultiConvert) String() string {
180180
var b strings.Builder
181181
b.WriteString(printConv("multiconvert", v, v.X))
182182
b.WriteString(" [")
183-
for i, s := range v.from {
184-
for j, d := range v.to {
183+
for i, s := range termListOf(v.from) {
184+
for j, d := range termListOf(v.to) {
185185
if i != 0 || j != 0 {
186186
b.WriteString(" | ")
187187
}

go/ssa/ssa.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -719,9 +719,8 @@ type Convert struct {
719719
// t1 = multiconvert D <- S (t0) [*[2]rune <- []rune | string <- []rune]
720720
type MultiConvert struct {
721721
register
722-
X Value
723-
from []*types.Term
724-
to []*types.Term
722+
X Value
723+
from, to types.Type
725724
}
726725

727726
// ChangeInterface constructs a value of one interface type from a

go/ssa/coretype.go renamed to go/ssa/typeset.go

Lines changed: 90 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -10,36 +10,44 @@ import (
1010
"golang.org/x/tools/internal/typeparams"
1111
)
1212

13-
// Utilities for dealing with core types.
13+
// Utilities for dealing with type sets.
1414

15-
// isBytestring returns true if T has the same terms as interface{[]byte | string}.
16-
// These act like a core type for some operations: slice expressions, append and copy.
17-
//
18-
// See https://go.dev/ref/spec#Core_types for the details on bytestring.
19-
func isBytestring(T types.Type) bool {
20-
U := T.Underlying()
21-
if _, ok := U.(*types.Interface); !ok {
22-
return false
23-
}
15+
const debug = false
2416

25-
hasBytes, hasString := false, false
26-
ok := underIs(U, func(t types.Type) bool {
27-
switch {
28-
case isString(t):
29-
hasString = true
30-
return true
31-
case isByteSlice(t):
32-
hasBytes = true
33-
return true
34-
default:
35-
return false
17+
// typeset is an iterator over the (type/underlying type) pairs of the
18+
// specific type terms of the type set implied by t.
19+
// If t is a type parameter, the implied type set is the type set of t's constraint.
20+
// In that case, if there are no specific terms, typeset calls yield with (nil, nil).
21+
// If t is not a type parameter, the implied type set consists of just t.
22+
// In any case, typeset is guaranteed to call yield at least once.
23+
func typeset(typ types.Type, yield func(t, u types.Type) bool) {
24+
switch typ := types.Unalias(typ).(type) {
25+
case *types.TypeParam, *types.Interface:
26+
terms := termListOf(typ)
27+
if len(terms) == 0 {
28+
yield(nil, nil)
29+
return
3630
}
37-
})
38-
return ok && hasBytes && hasString
31+
for _, term := range terms {
32+
u := types.Unalias(term.Type())
33+
if !term.Tilde() {
34+
u = u.Underlying()
35+
}
36+
if debug {
37+
assert(types.Identical(u, u.Underlying()), "Unalias(x) == under(x) for ~x terms")
38+
}
39+
if !yield(term.Type(), u) {
40+
break
41+
}
42+
}
43+
return
44+
default:
45+
yield(typ, typ.Underlying())
46+
}
3947
}
4048

41-
// typeSetOf returns the type set of typ as a normalized term set. Returns an empty set on an error.
42-
func typeSetOf(typ types.Type) []*types.Term {
49+
// termListOf returns the type set of typ as a normalized term set. Returns an empty set on an error.
50+
func termListOf(typ types.Type) []*types.Term {
4351
// This is a adaptation of x/exp/typeparams.NormalTerms which x/tools cannot depend on.
4452
var terms []*types.Term
4553
var err error
@@ -64,21 +72,52 @@ func typeSetOf(typ types.Type) []*types.Term {
6472
return terms
6573
}
6674

75+
// typeSetIsEmpty returns true if a typeset is empty.
76+
func typeSetIsEmpty(typ types.Type) bool {
77+
var empty bool
78+
typeset(typ, func(t, _ types.Type) bool {
79+
empty = t == nil
80+
return false
81+
})
82+
return empty
83+
}
84+
85+
// isBytestring returns true if T has the same terms as interface{[]byte | string}.
86+
// These act like a core type for some operations: slice expressions, append and copy.
87+
//
88+
// See https://go.dev/ref/spec#Core_types for the details on bytestring.
89+
func isBytestring(T types.Type) bool {
90+
U := T.Underlying()
91+
if _, ok := U.(*types.Interface); !ok {
92+
return false
93+
}
94+
95+
hasBytes, hasString := false, false
96+
ok := underIs(U, func(t types.Type) bool {
97+
switch {
98+
case isString(t):
99+
hasString = true
100+
return true
101+
case isByteSlice(t):
102+
hasBytes = true
103+
return true
104+
default:
105+
return false
106+
}
107+
})
108+
return ok && hasBytes && hasString
109+
}
110+
67111
// underIs calls f with the underlying types of the type terms
68112
// of the type set of typ and reports whether all calls to f returned true.
69113
// If there are no specific terms, underIs returns the result of f(nil).
70114
func underIs(typ types.Type, f func(types.Type) bool) bool {
71-
s := typeSetOf(typ)
72-
if len(s) == 0 {
73-
return f(nil)
74-
}
75-
for _, t := range s {
76-
u := t.Type().Underlying()
77-
if !f(u) {
78-
return false
79-
}
80-
}
81-
return true
115+
var ok bool
116+
typeset(typ, func(t, u types.Type) bool {
117+
ok = f(u)
118+
return ok
119+
})
120+
return ok
82121
}
83122

84123
// indexType returns the element type and index mode of a IndexExpr over a type.
@@ -98,22 +137,24 @@ func indexType(typ types.Type) (types.Type, indexMode) {
98137
case *types.Basic:
99138
return tByte, ixValue // must be a string
100139
case *types.Interface:
101-
tset := typeSetOf(U)
102-
if len(tset) == 0 {
103-
return nil, ixInvalid // no underlying terms or error is empty.
104-
}
105-
elem, mode := indexType(tset[0].Type())
106-
for _, t := range tset[1:] {
107-
e, m := indexType(t.Type())
108-
if !types.Identical(elem, e) { // if type checked, just a sanity check
109-
return nil, ixInvalid
140+
var elem types.Type
141+
mode := ixInvalid
142+
typeset(typ, func(t, _ types.Type) bool {
143+
if t == nil {
144+
return false // empty set
145+
}
146+
e, m := indexType(t)
147+
if elem == nil {
148+
elem, mode = e, m
149+
}
150+
if debug && !types.Identical(elem, e) { // if type checked, just a sanity check
151+
mode = ixInvalid
152+
return false
110153
}
111154
// Update the mode to the most constrained address type.
112155
mode = mode.meet(m)
113-
if mode == ixInvalid {
114-
return nil, ixInvalid // fast exit
115-
}
116-
}
156+
return mode != ixInvalid
157+
})
117158
return elem, mode
118159
}
119160
return nil, ixInvalid

0 commit comments

Comments
 (0)