forked from golang/tools
/
coretype.go
159 lines (144 loc) · 4.4 KB
/
coretype.go
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
// 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 ssa
import (
"go/types"
"golang.org/x/tools/internal/typeparams"
)
// Utilities for dealing with core types.
// 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
}
tset := typeSetOf(U)
if tset.Len() != 2 {
return false
}
hasBytes, hasString := false, false
underIs(tset, func(t types.Type) bool {
switch {
case isString(t):
hasString = true
case isByteSlice(t):
hasBytes = true
}
return hasBytes || hasString
})
return hasBytes && hasString
}
// termList is a list of types.
type termList []*typeparams.Term // type terms of the type set
func (s termList) Len() int { return len(s) }
func (s termList) At(i int) types.Type { return s[i].Type() }
// typeSetOf returns the type set of typ. Returns an empty typeset on an error.
func typeSetOf(typ types.Type) termList {
// This is a adaptation of x/exp/typeparams.NormalTerms which x/tools cannot depend on.
var terms []*typeparams.Term
var err error
switch typ := typ.(type) {
case *typeparams.TypeParam:
terms, err = typeparams.StructuralTerms(typ)
case *typeparams.Union:
terms, err = typeparams.UnionTermSet(typ)
case *types.Interface:
terms, err = typeparams.InterfaceTermSet(typ)
default:
// Common case.
// Specializing the len=1 case to avoid a slice
// had no measurable space/time benefit.
terms = []*typeparams.Term{typeparams.NewTerm(false, typ)}
}
if err != nil {
return termList(nil)
}
return termList(terms)
}
// underIs calls f with the underlying types of the specific type terms
// of s and reports whether all calls to f returned true. If there are
// no specific terms, underIs returns the result of f(nil).
func underIs(s termList, f func(types.Type) bool) bool {
if s.Len() == 0 {
return f(nil)
}
for i := 0; i < s.Len(); i++ {
u := s.At(i).Underlying()
if !f(u) {
return false
}
}
return true
}
// indexType returns the element type and index mode of a IndexExpr over a type.
// It returns (nil, invalid) 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:
tset := typeSetOf(U)
if tset.Len() == 0 {
return nil, ixInvalid // no underlying terms or error is empty.
}
elem, mode := indexType(tset.At(0))
for i := 1; i < tset.Len() && mode != ixInvalid; i++ {
e, m := indexType(tset.At(i))
if !types.Identical(elem, e) { // if type checked, just a sanity check
return nil, ixInvalid
}
// Update the mode to the most constrained address type.
mode = mode.meet(m)
}
if 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
}