-
-
Notifications
You must be signed in to change notification settings - Fork 418
Expand file tree
/
Copy pathtypeset.go
More file actions
175 lines (160 loc) · 4.74 KB
/
Copy pathtypeset.go
File metadata and controls
175 lines (160 loc) · 4.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package ir
import (
"go/types"
"honnef.co/go/tools/go/types/typeutil"
)
// Utilities for dealing with type sets.
const debug = false
// typeset is an iterator over the (type/underlying type) pairs of the
// specific type terms of the type set implied by t.
// If t is a type parameter, the implied type set is the type set of t's constraint.
// In that case, if there are no specific terms, typeset calls yield with (nil, nil).
// If t is not a type parameter, the implied type set consists of just t.
// In any case, typeset is guaranteed to call yield at least once.
func typeset(typ types.Type, yield func(t, u types.Type) bool) {
switch typ := types.Unalias(typ).(type) {
case *types.TypeParam, *types.Interface:
terms := termListOf(typ)
if len(terms) == 0 {
yield(nil, nil)
return
}
for _, term := range terms {
u := types.Unalias(term.Type())
if !term.Tilde() {
u = u.Underlying()
}
if debug {
assert(types.Identical(u, u.Underlying()), "Unalias(x) == under(x) for ~x terms")
}
if !yield(term.Type(), u) {
break
}
}
return
default:
yield(typ, typ.Underlying())
}
}
// termListOf returns the type set of typ as a normalized term set. Returns an empty set on an error.
func termListOf(typ types.Type) []*types.Term {
return typeutil.NewTypeSet(typ).Terms
}
// typeSetIsEmpty returns true if a typeset is empty.
func typeSetIsEmpty(typ types.Type) bool {
var empty bool
typeset(typ, func(t, _ types.Type) bool {
empty = t == nil
return false
})
return empty
}
// isBytestring returns true if T has the same terms as interface{[]byte | string}.
// These act like a core type for some operations: slice expressions, append and copy.
//
// See https://go.dev/ref/spec#Core_types for the details on bytestring.
func isBytestring(T types.Type) bool {
U := T.Underlying()
if _, ok := U.(*types.Interface); !ok {
return false
}
hasBytes, hasString := false, false
ok := underIs(U, func(t types.Type) bool {
switch {
case isString(t):
hasString = true
return true
case isByteSlice(t):
hasBytes = true
return true
default:
return false
}
})
return ok && hasBytes && hasString
}
// underIs calls f with the underlying types of the type terms
// of the type set of typ and reports whether all calls to f returned true.
// If there are no specific terms, underIs returns the result of f(nil).
func underIs(typ types.Type, f func(types.Type) bool) bool {
var ok bool
typeset(typ, func(t, u types.Type) bool {
ok = f(u)
return ok
})
return ok
}
// indexType returns the element type and index mode of a IndexExpr over a type.
// It returns an invalid mode if the type is not indexable; this should never occur in a well-typed program.
func indexType(typ types.Type) (types.Type, indexMode) {
switch U := typ.Underlying().(type) {
case *types.Array:
return U.Elem(), ixArrVar
case *types.Pointer:
if arr, ok := U.Elem().Underlying().(*types.Array); ok {
return arr.Elem(), ixVar
}
case *types.Slice:
return U.Elem(), ixVar
case *types.Map:
return U.Elem(), ixMap
case *types.Basic:
return tByte, ixValue // must be a string
case *types.Interface:
var elem types.Type
mode := ixInvalid
typeset(typ, func(t, _ types.Type) bool {
if t == nil {
return false // empty set
}
e, m := indexType(t)
if elem == nil {
elem, mode = e, m
}
if debug && !types.Identical(elem, e) { // if type checked, just a sanity check
mode = ixInvalid
return false
}
// Update the mode to the most constrained address type.
mode = mode.meet(m)
return mode != ixInvalid
})
return elem, mode
}
return nil, ixInvalid
}
// An indexMode specifies the (addressing) mode of an index operand.
//
// Addressing mode of an index operation is based on the set of
// underlying types.
// Hasse diagram of the indexMode meet semi-lattice:
//
// ixVar ixMap
// | |
// ixArrVar |
// | |
// ixValue |
// \ /
// ixInvalid
type indexMode byte
const (
ixInvalid indexMode = iota // index is invalid
ixValue // index is a computed value (not addressable)
ixArrVar // like ixVar, but index operand contains an array
ixVar // index is an addressable variable
ixMap // index is a map index expression (acts like a variable on lhs, commaok on rhs of an assignment)
)
// meet is the address type that is constrained by both x and y.
func (x indexMode) meet(y indexMode) indexMode {
if (x == ixMap || y == ixMap) && x != y {
return ixInvalid
}
// Use int representation and return min.
if x < y {
return y
}
return x
}