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.