@@ -10,36 +10,44 @@ import (
10
10
"golang.org/x/tools/internal/typeparams"
11
11
)
12
12
13
- // Utilities for dealing with core types .
13
+ // Utilities for dealing with type sets .
14
14
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
24
16
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
36
30
}
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
+ }
39
47
}
40
48
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 {
43
51
// This is a adaptation of x/exp/typeparams.NormalTerms which x/tools cannot depend on.
44
52
var terms []* types.Term
45
53
var err error
@@ -64,21 +72,52 @@ func typeSetOf(typ types.Type) []*types.Term {
64
72
return terms
65
73
}
66
74
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
+
67
111
// underIs calls f with the underlying types of the type terms
68
112
// of the type set of typ and reports whether all calls to f returned true.
69
113
// If there are no specific terms, underIs returns the result of f(nil).
70
114
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
82
121
}
83
122
84
123
// 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) {
98
137
case * types.Basic :
99
138
return tByte , ixValue // must be a string
100
139
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
110
153
}
111
154
// Update the mode to the most constrained address type.
112
155
mode = mode .meet (m )
113
- if mode == ixInvalid {
114
- return nil , ixInvalid // fast exit
115
- }
116
- }
156
+ return mode != ixInvalid
157
+ })
117
158
return elem , mode
118
159
}
119
160
return nil , ixInvalid
0 commit comments