Skip to content

Commit d186dde

Browse files
adonovangopherbot
authored andcommitted
go/types: fix bug in premature Unalias of alias cycle
Unalias memoizes the result of removing Alias constructors. When Unalias is called too soon on a type in a cycle, the initial value of the alias, Invalid, gets latched by the memoization, causing it to appear Invalid forever. This change disables memoization of Invalid, and adds a regression test. Fixes #66704 Updates #65294 Change-Id: I479fe14c88c802504a69f177869f091656489cd4 Reviewed-on: https://go-review.googlesource.com/c/go/+/576975 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Robert Findley <[email protected]> Auto-Submit: Alan Donovan <[email protected]>
1 parent 5ec7395 commit d186dde

File tree

4 files changed

+73
-6
lines changed

4 files changed

+73
-6
lines changed

src/cmd/compile/internal/types2/alias.go

+20-3
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type Alias struct {
2424
func NewAlias(obj *TypeName, rhs Type) *Alias {
2525
alias := (*Checker)(nil).newAlias(obj, rhs)
2626
// Ensure that alias.actual is set (#65455).
27-
unalias(alias)
27+
alias.cleanup()
2828
return alias
2929
}
3030

@@ -60,7 +60,16 @@ func unalias(a0 *Alias) Type {
6060
if t == nil {
6161
panic(fmt.Sprintf("non-terminated alias %s", a0.obj.name))
6262
}
63-
a0.actual = t
63+
64+
// Memoize the type only if valid.
65+
// In the presence of unfinished cyclic declarations, Unalias
66+
// would otherwise latch the invalid value (#66704).
67+
// TODO(adonovan): rethink, along with checker.typeDecl's use
68+
// of Invalid to mark unfinished aliases.
69+
if t != Typ[Invalid] {
70+
a0.actual = t
71+
}
72+
6473
return t
6574
}
6675

@@ -89,5 +98,13 @@ func (check *Checker) newAlias(obj *TypeName, rhs Type) *Alias {
8998
}
9099

91100
func (a *Alias) cleanup() {
92-
Unalias(a)
101+
// Ensure a.actual is set before types are published,
102+
// so Unalias is a pure "getter", not a "setter".
103+
actual := Unalias(a)
104+
105+
if actual == Typ[Invalid] {
106+
// We don't set a.actual to Typ[Invalid] during type checking,
107+
// as it may indicate that the RHS is not fully set up.
108+
a.actual = actual
109+
}
93110
}

src/go/types/alias.go

+20-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/go/types/api_test.go

+28
Original file line numberDiff line numberDiff line change
@@ -2994,3 +2994,31 @@ func TestTooNew(t *testing.T) {
29942994
}
29952995
}
29962996
}
2997+
2998+
// This is a regression test for #66704.
2999+
func TestUnaliasTooSoonInCycle(t *testing.T) {
3000+
t.Setenv("GODEBUG", "gotypesalias=1")
3001+
const src = `package a
3002+
3003+
var x T[B] // this appears to cause Unalias to be called on B while still Invalid
3004+
3005+
type T[_ any] struct{}
3006+
type A T[B]
3007+
type B = T[A]
3008+
`
3009+
fset := token.NewFileSet()
3010+
f, err := parser.ParseFile(fset, "a.go", src, 0)
3011+
if err != nil {
3012+
t.Fatal(err)
3013+
}
3014+
pkg, err := new(Config).Check("a", fset, []*ast.File{f}, nil)
3015+
if err != nil {
3016+
t.Fatal(err)
3017+
}
3018+
3019+
B := pkg.Scope().Lookup("B")
3020+
got, want := Unalias(B.Type()).String(), "a.T[a.A]"
3021+
if got != want {
3022+
t.Errorf("Unalias(type B = T[A]) = %q, want %q", got, want)
3023+
}
3024+
}

src/go/types/decl.go

+5
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,11 @@ func (check *Checker) typeDecl(obj *TypeName, tdecl *ast.TypeSpec, def *TypeName
587587
// TODO(gri) Should be able to use nil instead of Typ[Invalid] to mark
588588
// the alias as incomplete. Currently this causes problems
589589
// with certain cycles. Investigate.
590+
//
591+
// NOTE(adonovan): to avoid the Invalid being prematurely observed
592+
// by (e.g.) a var whose type is an unfinished cycle,
593+
// Unalias does not memoize if Invalid. Perhaps we should use a
594+
// special sentinel distinct from Invalid.
590595
alias := check.newAlias(obj, Typ[Invalid])
591596
setDefType(def, alias)
592597

0 commit comments

Comments
 (0)