go/ssa: deprecate coreType and _NormalTerms
Removes coreType and _NormalTerms. Uses typeparams.CoreType instead.
Change-Id: Ie7b82142c9ec5a0799784edc0917a2dd511658eb
Reviewed-on: https://go-review.googlesource.com/c/tools/+/452156
Run-TryBot: Tim King <taking@google.com>
Reviewed-by: Zvonimir Pavlinovic <zpavlinovic@google.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
gopls-CI: kokoro <noreply+kokoro@google.com>
diff --git a/go/ssa/builder.go b/go/ssa/builder.go
index 04deb7b..be8d36a 100644
--- a/go/ssa/builder.go
+++ b/go/ssa/builder.go
@@ -275,7 +275,7 @@
return fn.emit(&c)
case *ast.IndexExpr:
- mapt := coreType(fn.typeOf(e.X)).(*types.Map) // ,ok must be a map.
+ mapt := typeparams.CoreType(fn.typeOf(e.X)).(*types.Map) // ,ok must be a map.
lookup := &Lookup{
X: b.expr(fn, e.X),
Index: emitConv(fn, b.expr(fn, e.Index), mapt.Key()),
@@ -312,7 +312,7 @@
typ = fn.typ(typ)
switch obj.Name() {
case "make":
- switch ct := coreType(typ).(type) {
+ switch ct := typeparams.CoreType(typ).(type) {
case *types.Slice:
n := b.expr(fn, args[1])
m := n
@@ -482,7 +482,7 @@
x = b.expr(fn, e.X)
et = types.NewPointer(elem)
case ixMap:
- mt := coreType(xt).(*types.Map)
+ mt := typeparams.CoreType(xt).(*types.Map)
return &element{
m: b.expr(fn, e.X),
k: emitConv(fn, b.expr(fn, e.Index), mt.Key()),
@@ -752,14 +752,14 @@
var low, high, max Value
var x Value
xtyp := fn.typeOf(e.X)
- switch coreType(xtyp).(type) {
+ switch typeparams.CoreType(xtyp).(type) {
case *types.Array:
// Potentially escaping.
x = b.addr(fn, e.X, true).address(fn)
case *types.Basic, *types.Slice, *types.Pointer: // *array
x = b.expr(fn, e.X)
default:
- // coreType exception?
+ // core type exception?
if isBytestring(xtyp) {
x = b.expr(fn, e.X) // bytestring is handled as string and []byte.
} else {
@@ -841,7 +841,7 @@
if recv, ok := sel.recv.(*typeparams.TypeParam); ok {
// Emit a nil check if any possible instantiation of the
// type parameter is an interface type.
- if len(typeSetOf(recv)) > 0 {
+ if typeSetOf(recv).Len() > 0 {
// recv has a concrete term its typeset.
// So it cannot be instantiated as an interface.
//
@@ -906,7 +906,7 @@
// An array in a register, a string or a combined type that contains
// either an [_]array (ixArrVar) or string (ixValue).
- // Note: for ixArrVar and coreType(xt)==nil can be IndexAddr and Load.
+ // Note: for ixArrVar and CoreType(xt)==nil can be IndexAddr and Load.
index := b.expr(fn, e.Index)
if isUntyped(index.Type()) {
index = emitConv(fn, index, tInt)
@@ -920,7 +920,7 @@
return fn.emit(v)
case ixMap:
- ct := coreType(xt).(*types.Map)
+ ct := typeparams.CoreType(xt).(*types.Map)
v := &Lookup{
X: b.expr(fn, e.X),
Index: emitConv(fn, b.expr(fn, e.Index), ct.Key()),
@@ -1120,7 +1120,7 @@
b.setCallFunc(fn, e, c)
// Then append the other actual parameters.
- sig, _ := coreType(fn.typeOf(e.Fun)).(*types.Signature)
+ sig, _ := typeparams.CoreType(fn.typeOf(e.Fun)).(*types.Signature)
if sig == nil {
panic(fmt.Sprintf("no signature for call of %s", e.Fun))
}
@@ -1253,8 +1253,8 @@
// literal has type *T behaves like &T{}.
// In that case, addr must hold a T, not a *T.
func (b *builder) compLit(fn *Function, addr Value, e *ast.CompositeLit, isZero bool, sb *storebuf) {
- typ := deref(fn.typeOf(e)) // type with name [may be type param]
- t := deref(coreType(typ)).Underlying() // core type for comp lit case
+ typ := deref(fn.typeOf(e)) // type with name [may be type param]
+ t := deref(typeparams.CoreType(typ)).Underlying() // core type for comp lit case
// Computing typ and t is subtle as these handle pointer types.
// For example, &T{...} is valid even for maps and slices.
// Also typ should refer to T (not *T) while t should be the core type of T.
@@ -1269,13 +1269,13 @@
// For `&T{f: 1}`, we compute `typ` and `t` as:
// typeOf(&T{f: 1}) == *T
// deref(*T) == T (typ)
- // coreType(T) == N
+ // CoreType(T) == N
// deref(N) == N
// N.Underlying() == struct{f int} (t)
// For `{f: 1}` in `[]S{{f: 1}}`, we compute `typ` and `t` as:
// typeOf({f: 1}) == S
// deref(S) == S (typ)
- // coreType(S) == *N
+ // CoreType(S) == *N
// deref(*N) == N
// N.Underlying() == struct{f int} (t)
switch t := t.(type) {
@@ -1660,7 +1660,7 @@
case *ast.SendStmt: // ch<- i
ch := b.expr(fn, comm.Chan)
- chtyp := coreType(fn.typ(ch.Type())).(*types.Chan)
+ chtyp := typeparams.CoreType(fn.typ(ch.Type())).(*types.Chan)
st = &SelectState{
Dir: types.SendOnly,
Chan: ch,
@@ -1717,7 +1717,7 @@
vars = append(vars, varIndex, varOk)
for _, st := range states {
if st.Dir == types.RecvOnly {
- chtyp := coreType(fn.typ(st.Chan.Type())).(*types.Chan)
+ chtyp := typeparams.CoreType(fn.typ(st.Chan.Type())).(*types.Chan)
vars = append(vars, anonVar(chtyp.Elem()))
}
}
@@ -1916,7 +1916,7 @@
k = emitLoad(fn, index)
if tv != nil {
- switch t := coreType(x.Type()).(type) {
+ switch t := typeparams.CoreType(x.Type()).(type) {
case *types.Array:
instr := &Index{
X: x,
@@ -1988,7 +1988,7 @@
okv := &Next{
Iter: it,
- IsString: isBasic(coreType(x.Type())),
+ IsString: isBasic(typeparams.CoreType(x.Type())),
}
okv.setType(types.NewTuple(
varOk,
@@ -2038,7 +2038,7 @@
}
recv.setPos(pos)
recv.setType(types.NewTuple(
- newVar("k", coreType(x.Type()).(*types.Chan).Elem()),
+ newVar("k", typeparams.CoreType(x.Type()).(*types.Chan).Elem()),
varOk,
))
ko := fn.emit(recv)
@@ -2082,7 +2082,7 @@
var k, v Value
var loop, done *BasicBlock
- switch rt := coreType(x.Type()).(type) {
+ switch rt := typeparams.CoreType(x.Type()).(type) {
case *types.Slice, *types.Array, *types.Pointer: // *array
k, v, loop, done = b.rangeIndexed(fn, x, tv, s.For)
@@ -2160,7 +2160,7 @@
b.expr(fn, s.X)
case *ast.SendStmt:
- chtyp := coreType(fn.typeOf(s.Chan)).(*types.Chan)
+ chtyp := typeparams.CoreType(fn.typeOf(s.Chan)).(*types.Chan)
fn.emit(&Send{
Chan: b.expr(fn, s.Chan),
X: emitConv(fn, b.expr(fn, s.Value), chtyp.Elem()),
diff --git a/go/ssa/const.go b/go/ssa/const.go
index 3468eac..a1bac7b 100644
--- a/go/ssa/const.go
+++ b/go/ssa/const.go
@@ -45,7 +45,7 @@
// Candidates (perhaps all) are eliminated during the type-set
// iteration, which executes at least once.
state := types.IsBoolean | types.IsInteger | types.IsString
- typeSetOf(typ).underIs(func(t types.Type) bool {
+ underIs(typeSetOf(typ), func(t types.Type) bool {
var c types.BasicInfo
if t, ok := t.(*types.Basic); ok {
c = t.Info()
@@ -169,7 +169,7 @@
case *types.Pointer, *types.Slice, *types.Chan, *types.Map, *types.Signature:
return true
case *types.Interface:
- return len(typeSetOf(t)) == 0 // basic interface.
+ return typeSetOf(t).Len() == 0 // basic interface.
default:
return false
}
diff --git a/go/ssa/coretype.go b/go/ssa/coretype.go
index 54bc4a8..128d61e 100644
--- a/go/ssa/coretype.go
+++ b/go/ssa/coretype.go
@@ -12,63 +12,8 @@
// Utilities for dealing with core types.
-// coreType returns the core type of T or nil if T does not have a core type.
-//
-// See https://go.dev/ref/spec#Core_types for the definition of a core type.
-func coreType(T types.Type) types.Type {
- U := T.Underlying()
- if _, ok := U.(*types.Interface); !ok {
- return U // for non-interface types,
- }
-
- terms, err := _NormalTerms(U)
- if len(terms) == 0 || err != nil {
- // len(terms) -> empty type set of interface.
- // err != nil => U is invalid, exceeds complexity bounds, or has an empty type set.
- return nil // no core type.
- }
-
- U = terms[0].Type().Underlying()
- var identical int // i in [0,identical) => Identical(U, terms[i].Type().Underlying())
- for identical = 1; identical < len(terms); identical++ {
- if !types.Identical(U, terms[identical].Type().Underlying()) {
- break
- }
- }
-
- if identical == len(terms) {
- // https://go.dev/ref/spec#Core_types
- // "There is a single type U which is the underlying type of all types in the type set of T"
- return U
- }
- ch, ok := U.(*types.Chan)
- if !ok {
- return nil // no core type as identical < len(terms) and U is not a channel.
- }
- // https://go.dev/ref/spec#Core_types
- // "the type chan E if T contains only bidirectional channels, or the type chan<- E or
- // <-chan E depending on the direction of the directional channels present."
- for chans := identical; chans < len(terms); chans++ {
- curr, ok := terms[chans].Type().Underlying().(*types.Chan)
- if !ok {
- return nil
- }
- if !types.Identical(ch.Elem(), curr.Elem()) {
- return nil // channel elements are not identical.
- }
- if ch.Dir() == types.SendRecv {
- // ch is bidirectional. We can safely always use curr's direction.
- ch = curr
- } else if curr.Dir() != types.SendRecv && ch.Dir() != curr.Dir() {
- // ch and curr are not bidirectional and not the same direction.
- return nil
- }
- }
- return ch
-}
-
// isBytestring returns true if T has the same terms as interface{[]byte | string}.
-// These act like a coreType for some operations: slice expressions, append and copy.
+// 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 {
@@ -78,11 +23,11 @@
}
tset := typeSetOf(U)
- if len(tset) != 2 {
+ if tset.Len() != 2 {
return false
}
hasBytes, hasString := false, false
- tset.underIs(func(t types.Type) bool {
+ underIs(tset, func(t types.Type) bool {
switch {
case isString(t):
hasString = true
@@ -94,87 +39,45 @@
return hasBytes && hasString
}
-// _NormalTerms returns a slice of terms representing the normalized structural
-// type restrictions of a type, if any.
-//
-// For all types other than *types.TypeParam, *types.Interface, and
-// *types.Union, this is just a single term with Tilde() == false and
-// Type() == typ. For *types.TypeParam, *types.Interface, and *types.Union, see
-// below.
-//
-// Structural type restrictions of a type parameter are created via
-// non-interface types embedded in its constraint interface (directly, or via a
-// chain of interface embeddings). For example, in the declaration type
-// T[P interface{~int; m()}] int the structural restriction of the type
-// parameter P is ~int.
-//
-// With interface embedding and unions, the specification of structural type
-// restrictions may be arbitrarily complex. For example, consider the
-// following:
-//
-// type A interface{ ~string|~[]byte }
-//
-// type B interface{ int|string }
-//
-// type C interface { ~string|~int }
-//
-// type T[P interface{ A|B; C }] int
-//
-// In this example, the structural type restriction of P is ~string|int: A|B
-// expands to ~string|~[]byte|int|string, which reduces to ~string|~[]byte|int,
-// which when intersected with C (~string|~int) yields ~string|int.
-//
-// _NormalTerms computes these expansions and reductions, producing a
-// "normalized" form of the embeddings. A structural restriction is normalized
-// if it is a single union containing no interface terms, and is minimal in the
-// sense that removing any term changes the set of types satisfying the
-// constraint. It is left as a proof for the reader that, modulo sorting, there
-// is exactly one such normalized form.
-//
-// Because the minimal representation always takes this form, _NormalTerms
-// returns a slice of tilde terms corresponding to the terms of the union in
-// the normalized structural restriction. An error is returned if the type is
-// invalid, exceeds complexity bounds, or has an empty type set. In the latter
-// case, _NormalTerms returns ErrEmptyTypeSet.
-//
-// _NormalTerms makes no guarantees about the order of terms, except that it
-// is deterministic.
-//
-// This is a copy of x/exp/typeparams.NormalTerms which x/tools cannot depend on.
-// TODO(taking): Remove this copy when possible.
-func _NormalTerms(typ types.Type) ([]*typeparams.Term, error) {
- switch typ := typ.(type) {
- case *typeparams.TypeParam:
- return typeparams.StructuralTerms(typ)
- case *typeparams.Union:
- return typeparams.UnionTermSet(typ)
- case *types.Interface:
- return typeparams.InterfaceTermSet(typ)
- default:
- return []*typeparams.Term{typeparams.NewTerm(false, typ)}, nil
- }
-}
+// 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) typeSet {
- terms, err := _NormalTerms(typ)
- if err != nil {
- return nil
+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)}
}
- return terms
-}
-type typeSet []*typeparams.Term // type terms of the type set
+ 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 (s typeSet) underIs(f func(types.Type) bool) bool {
- if len(s) == 0 {
+func underIs(s termList, f func(types.Type) bool) bool {
+ if s.Len() == 0 {
return f(nil)
}
- for _, t := range s {
- u := t.Type().Underlying()
+ for i := 0; i < s.Len(); i++ {
+ u := s.At(i).Underlying()
if !f(u) {
return false
}
@@ -199,14 +102,14 @@
case *types.Basic:
return tByte, ixValue // must be a string
case *types.Interface:
- terms, err := _NormalTerms(U)
- if len(terms) == 0 || err != nil {
+ tset := typeSetOf(U)
+ if tset.Len() == 0 {
return nil, ixInvalid // no underlying terms or error is empty.
}
- elem, mode := indexType(terms[0].Type())
- for i := 1; i < len(terms) && mode != ixInvalid; i++ {
- e, m := indexType(terms[i].Type())
+ 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
}
diff --git a/go/ssa/coretype_test.go b/go/ssa/coretype_test.go
index c4ed290..74fe4db 100644
--- a/go/ssa/coretype_test.go
+++ b/go/ssa/coretype_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package ssa
+package ssa_test
import (
"go/ast"
@@ -91,7 +91,7 @@
t.Fatalf("Eval(%s) failed: %v", test.expr, err)
}
- ct := coreType(tv.Type)
+ ct := typeparams.CoreType(tv.Type)
var got string
if ct == nil {
got = "<nil>"
@@ -99,7 +99,7 @@
got = ct.String()
}
if got != test.want {
- t.Errorf("coreType(%s) = %v, want %v", test.expr, got, test.want)
+ t.Errorf("CoreType(%s) = %v, want %v", test.expr, got, test.want)
}
}
}
diff --git a/go/ssa/emit.go b/go/ssa/emit.go
index b041491..3754325 100644
--- a/go/ssa/emit.go
+++ b/go/ssa/emit.go
@@ -11,6 +11,8 @@
"go/ast"
"go/token"
"go/types"
+
+ "golang.org/x/tools/internal/typeparams"
)
// emitNew emits to f a new (heap Alloc) instruction allocating an
@@ -210,8 +212,8 @@
src_types := typeSetOf(ut_src)
// Just a change of type, but not value or representation?
- preserving := src_types.underIs(func(s types.Type) bool {
- return dst_types.underIs(func(d types.Type) bool {
+ preserving := underIs(src_types, func(s types.Type) bool {
+ return underIs(dst_types, func(d types.Type) bool {
return s != nil && d != nil && isValuePreserving(s, d) // all (s -> d) are value preserving.
})
})
@@ -261,8 +263,8 @@
}
// Conversion from slice to array pointer?
- slice2ptr := src_types.underIs(func(s types.Type) bool {
- return dst_types.underIs(func(d types.Type) bool {
+ slice2ptr := underIs(src_types, func(s types.Type) bool {
+ return underIs(dst_types, func(d types.Type) bool {
return s != nil && d != nil && isSliceToArrayPointer(s, d) // all (s->d) are slice to array pointer conversion.
})
})
@@ -273,8 +275,8 @@
}
// Conversion from slice to array?
- slice2array := src_types.underIs(func(s types.Type) bool {
- return dst_types.underIs(func(d types.Type) bool {
+ slice2array := underIs(src_types, func(s types.Type) bool {
+ return underIs(dst_types, func(d types.Type) bool {
return s != nil && d != nil && isSliceToArray(s, d) // all (s->d) are slice to array conversion.
})
})
@@ -428,7 +430,7 @@
// value of a field.
func emitImplicitSelections(f *Function, v Value, indices []int, pos token.Pos) Value {
for _, index := range indices {
- fld := coreType(deref(v.Type())).(*types.Struct).Field(index)
+ fld := typeparams.CoreType(deref(v.Type())).(*types.Struct).Field(index)
if isPointer(v.Type()) {
instr := &FieldAddr{
@@ -462,7 +464,7 @@
// field's value.
// Ident id is used for position and debug info.
func emitFieldSelection(f *Function, v Value, index int, wantAddr bool, id *ast.Ident) Value {
- fld := coreType(deref(v.Type())).(*types.Struct).Field(index)
+ fld := typeparams.CoreType(deref(v.Type())).(*types.Struct).Field(index)
if isPointer(v.Type()) {
instr := &FieldAddr{
X: v,
diff --git a/go/ssa/print.go b/go/ssa/print.go
index 9aa6809..e40bbfa 100644
--- a/go/ssa/print.go
+++ b/go/ssa/print.go
@@ -17,6 +17,7 @@
"strings"
"golang.org/x/tools/go/types/typeutil"
+ "golang.org/x/tools/internal/typeparams"
)
// relName returns the name of v relative to i.
@@ -232,7 +233,7 @@
}
func (v *FieldAddr) String() string {
- st := coreType(deref(v.X.Type())).(*types.Struct)
+ st := typeparams.CoreType(deref(v.X.Type())).(*types.Struct)
// Be robust against a bad index.
name := "?"
if 0 <= v.Field && v.Field < st.NumFields() {
@@ -242,7 +243,7 @@
}
func (v *Field) String() string {
- st := coreType(v.X.Type()).(*types.Struct)
+ st := typeparams.CoreType(v.X.Type()).(*types.Struct)
// Be robust against a bad index.
name := "?"
if 0 <= v.Field && v.Field < st.NumFields() {
diff --git a/go/ssa/ssa.go b/go/ssa/ssa.go
index 698cb16..3108de2 100644
--- a/go/ssa/ssa.go
+++ b/go/ssa/ssa.go
@@ -1401,7 +1401,7 @@
if c.Method != nil {
return c.Method.Type().(*types.Signature)
}
- return coreType(c.Value.Type()).(*types.Signature)
+ return typeparams.CoreType(c.Value.Type()).(*types.Signature)
}
// StaticCallee returns the callee if this is a trivially static
diff --git a/go/ssa/util.go b/go/ssa/util.go
index c30e74c..6b0aada 100644
--- a/go/ssa/util.go
+++ b/go/ssa/util.go
@@ -88,16 +88,16 @@
// - All are basic, []byte, or []rune.
// - At least 1 is basic.
// - At most 1 is []byte or []rune.
-func isBasicConvTypes(tset typeSet) bool {
+func isBasicConvTypes(tset termList) bool {
basics := 0
- all := tset.underIs(func(t types.Type) bool {
+ all := underIs(tset, func(t types.Type) bool {
if isBasic(t) {
basics++
return true
}
return isByteSlice(t) || isRuneSlice(t)
})
- return all && basics >= 1 && len(tset)-basics <= 1
+ return all && basics >= 1 && tset.Len()-basics <= 1
}
// deref returns a pointer's element type; otherwise it returns typ.