go/ssa: types.Unalias() cleanup.
Removes a few instances of unneeded Unalias calls, and
adds additional documentation about aliases.
Change-Id: I02045ef74d6d9b80189caf6a9831bded2bae5ad4
Reviewed-on: https://go-review.googlesource.com/c/tools/+/568176
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Tim King <taking@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Alan Donovan <adonovan@google.com>
diff --git a/go/ssa/builder.go b/go/ssa/builder.go
index 79b9edd..9b3753c 100644
--- a/go/ssa/builder.go
+++ b/go/ssa/builder.go
@@ -1253,7 +1253,7 @@
case *types.Array, *types.Slice:
var at *types.Array
var array Value
- switch t := aliases.Unalias(t).(type) {
+ switch t := t.(type) {
case *types.Slice:
at = types.NewArray(t.Elem(), b.arrayLen(fn, e.Elts))
array = emitNew(fn, at, e.Lbrace, "slicelit")
diff --git a/go/ssa/const.go b/go/ssa/const.go
index e0d79f5..2a4e0dd 100644
--- a/go/ssa/const.go
+++ b/go/ssa/const.go
@@ -46,9 +46,9 @@
// Candidates (perhaps all) are eliminated during the type-set
// iteration, which executes at least once.
state := types.IsBoolean | types.IsInteger | types.IsString
- underIs(typeSetOf(typ), func(t types.Type) bool {
+ underIs(typeSetOf(typ), func(ut types.Type) bool {
var c types.BasicInfo
- if t, ok := aliases.Unalias(t).(*types.Basic); ok {
+ if t, ok := ut.(*types.Basic); ok {
c = t.Info()
}
if c&types.IsNumeric != 0 { // int/float/complex
diff --git a/go/ssa/coretype.go b/go/ssa/coretype.go
index 3a51283..8c218f9 100644
--- a/go/ssa/coretype.go
+++ b/go/ssa/coretype.go
@@ -50,6 +50,7 @@
// This is a adaptation of x/exp/typeparams.NormalTerms which x/tools cannot depend on.
var terms []*types.Term
var err error
+ // typeSetOf(t) == typeSetOf(Unalias(t))
switch typ := aliases.Unalias(typ).(type) {
case *types.TypeParam:
terms, err = typeparams.StructuralTerms(typ)
diff --git a/go/ssa/emit.go b/go/ssa/emit.go
index 9d2c18a..97c0b59 100644
--- a/go/ssa/emit.go
+++ b/go/ssa/emit.go
@@ -12,7 +12,6 @@
"go/token"
"go/types"
- "golang.org/x/tools/internal/aliases"
"golang.org/x/tools/internal/typeparams"
)
@@ -277,18 +276,20 @@
sliceTo0ArrayPtr
convert
)
- classify := func(s, d types.Type) conversionCase {
+ // classify the conversion case of a source type us to a destination type ud.
+ // us and ud are underlying types (not *Named or *Alias)
+ classify := func(us, ud types.Type) conversionCase {
// Just a change of type, but not value or representation?
- if isValuePreserving(s, d) {
+ if isValuePreserving(us, ud) {
return changeType
}
// Conversion from slice to array or slice to array pointer?
- if slice, ok := aliases.Unalias(s).(*types.Slice); ok {
+ if slice, ok := us.(*types.Slice); ok {
var arr *types.Array
var ptr bool
// Conversion from slice to array pointer?
- switch d := aliases.Unalias(d).(type) {
+ switch d := ud.(type) {
case *types.Array:
arr = d
case *types.Pointer:
@@ -313,8 +314,8 @@
// The only remaining case in well-typed code is a representation-
// changing conversion of basic types (possibly with []byte/[]rune).
- if !isBasic(s) && !isBasic(d) {
- panic(fmt.Sprintf("in %s: cannot convert term %s (%s [within %s]) to type %s [within %s]", f, val, val.Type(), s, typ, d))
+ if !isBasic(us) && !isBasic(ud) {
+ panic(fmt.Sprintf("in %s: cannot convert term %s (%s [within %s]) to type %s [within %s]", f, val, val.Type(), us, typ, ud))
}
return convert
}
diff --git a/go/ssa/func.go b/go/ssa/func.go
index d4a21c7..f645fa1 100644
--- a/go/ssa/func.go
+++ b/go/ssa/func.go
@@ -37,7 +37,8 @@
panic(fmt.Sprintf("no type for %T @ %s", e, f.Prog.Fset.Position(e.Pos())))
}
-// typ is the locally instantiated type of T. T==typ(T) if f is not an instantiation.
+// typ is the locally instantiated type of T.
+// If f is not an instantiation, then f.typ(T)==T.
func (f *Function) typ(T types.Type) types.Type {
return f.subst.typ(T)
}
diff --git a/go/ssa/interp/ops.go b/go/ssa/interp/ops.go
index 62b635c..668fcae 100644
--- a/go/ssa/interp/ops.go
+++ b/go/ssa/interp/ops.go
@@ -173,7 +173,7 @@
// zero returns a new "zero" value of the specified type.
func zero(t types.Type) value {
- switch t := aliases.Unalias(t).(type) {
+ switch t := t.(type) {
case *types.Basic:
if t.Kind() == types.UntypedNil {
panic("untyped nil has no zero value")
@@ -235,6 +235,8 @@
return a
case *types.Named:
return zero(t.Underlying())
+ case *aliases.Alias:
+ return zero(aliases.Unalias(t))
case *types.Interface:
return iface{} // nil type, methodset and value
case *types.Slice:
diff --git a/go/ssa/interp/reflect.go b/go/ssa/interp/reflect.go
index 7df3ea2..d713256 100644
--- a/go/ssa/interp/reflect.go
+++ b/go/ssa/interp/reflect.go
@@ -179,8 +179,8 @@
}
func reflectKind(t types.Type) reflect.Kind {
- switch t := aliases.Unalias(t).(type) {
- case *types.Named:
+ switch t := t.(type) {
+ case *types.Named, *aliases.Alias:
return reflectKind(t.Underlying())
case *types.Basic:
switch t.Kind() {
diff --git a/go/ssa/methods.go b/go/ssa/methods.go
index 60711dd..197b9e7 100644
--- a/go/ssa/methods.go
+++ b/go/ssa/methods.go
@@ -210,7 +210,7 @@
switch T := T.(type) {
case *aliases.Alias:
- visit(aliases.Unalias(T), false)
+ visit(aliases.Unalias(T), skip) // emulates the pre-Alias behavior
case *types.Basic:
// nop
diff --git a/go/ssa/subst.go b/go/ssa/subst.go
index 9f2f2f3..e1b8e19 100644
--- a/go/ssa/subst.go
+++ b/go/ssa/subst.go
@@ -80,11 +80,7 @@
subst.cache[t] = res
}()
- // fall through if result r will be identical to t, types.Identical(r, t).
switch t := t.(type) {
- case *aliases.Alias:
- return subst.typ(aliases.Unalias(t))
-
case *types.TypeParam:
r := subst.replacements[t]
assert(r != nil, "type param without replacement encountered")
@@ -140,6 +136,9 @@
case *types.Interface:
return subst.interface_(t)
+ case *aliases.Alias:
+ return subst.alias(t)
+
case *types.Named:
return subst.named(t)
@@ -307,6 +306,18 @@
return types.NewInterfaceType(methods, embeds).Complete()
}
+func (subst *subster) alias(t *aliases.Alias) types.Type {
+ // TODO(go.dev/issues/46477): support TypeParameters once these are available from go/types.
+ u := aliases.Unalias(t)
+ if s := subst.typ(u); s != u {
+ // If there is any change, do not create a new alias.
+ return s
+ }
+ // If there is no change, t did not reach any type parameter.
+ // Keep the Alias.
+ return t
+}
+
func (subst *subster) named(t *types.Named) types.Type {
// A named type may be:
// (1) ordinary named type (non-local scope, no type parameters, no type arguments),
diff --git a/go/ssa/util.go b/go/ssa/util.go
index 4d65259..462e275 100644
--- a/go/ssa/util.go
+++ b/go/ssa/util.go
@@ -51,8 +51,9 @@
}
// isBasic reports whether t is a basic type.
+// t is assumed to be an Underlying type (not Named or Alias).
func isBasic(t types.Type) bool {
- _, ok := aliases.Unalias(t).(*types.Basic)
+ _, ok := t.(*types.Basic)
return ok
}
@@ -263,13 +264,40 @@
return nil
}
+ unaliasAll := func(ts []types.Type) []types.Type {
+ // Is there some top level alias?
+ var found bool
+ for _, t := range ts {
+ if _, ok := t.(*aliases.Alias); ok {
+ found = true
+ break
+ }
+ }
+ if !found {
+ return ts // no top level alias
+ }
+
+ cp := make([]types.Type, len(ts)) // copy with top level aliases removed.
+ for i, t := range ts {
+ cp[i] = aliases.Unalias(t)
+ }
+ return cp
+ }
+ l := unaliasAll(ts)
+
c.mu.Lock()
defer c.mu.Unlock()
- return c.lists.rep(ts)
+ return c.lists.rep(l)
}
// Type returns a canonical representative of type T.
+// Removes top-level aliases.
+//
+// For performance, reasons the canonical instance is order-dependent,
+// and may contain deeply nested aliases.
func (c *canonizer) Type(T types.Type) types.Type {
+ T = aliases.Unalias(T) // remove the top level alias.
+
c.mu.Lock()
defer c.mu.Unlock()