Types returned by the go/types API must be immutable (or at least
concurrency safe), but NewAlias returned an alias without actual set.
Ensure that actual is set by unaliasing. Also make some superficial
simplifications to unalias, and avoid indirection where unnecessary.
Fixes golang/go#65728
Change-Id: Ic9a020da5accf9032056a924b65c9e9e08cb2e0a
Reviewed-on: https://go-review.googlesource.com/c/go/+/560915
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
Reviewed-by: Robert Griesemer <gri@google.com>
(cherry picked from commit
10a65649a3b2e34ffe8e4202bfa3df851cea0fb4)
Reviewed-on: https://go-review.googlesource.com/c/go/+/564356
// NewAlias creates a new Alias type with the given type name and rhs.
// rhs must not be nil.
func NewAlias(obj *TypeName, rhs Type) *Alias {
- return (*Checker)(nil).newAlias(obj, rhs)
+ alias := (*Checker)(nil).newAlias(obj, rhs)
+ // Ensure that alias.actual is set (#65455).
+ unalias(alias)
+ return alias
}
func (a *Alias) Obj() *TypeName { return a.obj }
-func (a *Alias) Underlying() Type { return a.actual.Underlying() }
+func (a *Alias) Underlying() Type { return unalias(a).Underlying() }
func (a *Alias) String() string { return TypeString(a, nil) }
// Type accessors
// Consequently, the result is never an alias type.
func Unalias(t Type) Type {
if a0, _ := t.(*Alias); a0 != nil {
- if a0.actual != nil {
- return a0.actual
- }
- for a := a0; ; {
- t = a.fromRHS
- a, _ = t.(*Alias)
- if a == nil {
- break
- }
- }
- if t == nil {
- panic(fmt.Sprintf("non-terminated alias %s", a0.obj.name))
- }
- a0.actual = t
+ return unalias(a0)
}
return t
}
+func unalias(a0 *Alias) Type {
+ if a0.actual != nil {
+ return a0.actual
+ }
+ var t Type
+ for a := a0; a != nil; a, _ = t.(*Alias) {
+ t = a.fromRHS
+ }
+ if t == nil {
+ panic(fmt.Sprintf("non-terminated alias %s", a0.obj.name))
+ }
+ a0.actual = t
+ return t
+}
+
// asNamed returns t as *Named if that is t's
// actual type. It returns nil otherwise.
func asNamed(t Type) *Named {
iface.NumMethods() // unlike go/types, there is no Complete() method, so we complete implicitly
}
+func TestNewAlias_Issue65455(t *testing.T) {
+ obj := NewTypeName(nopos, nil, "A", nil)
+ alias := NewAlias(obj, Typ[Int])
+ alias.Underlying() // must not panic
+}
+
func TestIssue15305(t *testing.T) {
const src = "package p; func f() int16; var _ = f(undef)"
f := mustParse(src)
// NewAlias creates a new Alias type with the given type name and rhs.
// rhs must not be nil.
func NewAlias(obj *TypeName, rhs Type) *Alias {
- return (*Checker)(nil).newAlias(obj, rhs)
+ alias := (*Checker)(nil).newAlias(obj, rhs)
+ // Ensure that alias.actual is set (#65455).
+ unalias(alias)
+ return alias
}
func (a *Alias) Obj() *TypeName { return a.obj }
-func (a *Alias) Underlying() Type { return a.actual.Underlying() }
+func (a *Alias) Underlying() Type { return unalias(a).Underlying() }
func (a *Alias) String() string { return TypeString(a, nil) }
// Type accessors
// Consequently, the result is never an alias type.
func Unalias(t Type) Type {
if a0, _ := t.(*Alias); a0 != nil {
- if a0.actual != nil {
- return a0.actual
- }
- for a := a0; ; {
- t = a.fromRHS
- a, _ = t.(*Alias)
- if a == nil {
- break
- }
- }
- if t == nil {
- panic(fmt.Sprintf("non-terminated alias %s", a0.obj.name))
- }
- a0.actual = t
+ return unalias(a0)
}
return t
}
+func unalias(a0 *Alias) Type {
+ if a0.actual != nil {
+ return a0.actual
+ }
+ var t Type
+ for a := a0; a != nil; a, _ = t.(*Alias) {
+ t = a.fromRHS
+ }
+ if t == nil {
+ panic(fmt.Sprintf("non-terminated alias %s", a0.obj.name))
+ }
+ a0.actual = t
+ return t
+}
+
// asNamed returns t as *Named if that is t's
// actual type. It returns nil otherwise.
func asNamed(t Type) *Named {
iface.Complete()
}
+func TestNewAlias_Issue65455(t *testing.T) {
+ obj := NewTypeName(nopos, nil, "A", nil)
+ alias := NewAlias(obj, Typ[Int])
+ alias.Underlying() // must not panic
+}
+
func TestIssue15305(t *testing.T) {
const src = "package p; func f() int16; var _ = f(undef)"
fset := token.NewFileSet()