Commit 5c84441d authored by Robert Griesemer's avatar Robert Griesemer

go/types: fix computation of initialization order

The old algorithm operated on a dependency graph that included
all objects (including functions) for simplicity: it was based
directly on the dependencies collected for each object during
type checking an object's initialization expression. It also
used that graph to compute the objects involved in an erroneous
initialization cycle.

Cycles that consist only of (mutually recursive) functions are
permitted in initialization code; so those cycles were silently
ignored if encountered. However, such cycles still inflated the
number of dependencies a variable might have (due to the cycle),
which in some cases lead to the wrong variable being scheduled
for initialization before the one with the inflated dependency
count.

Correcting for the cycle when it is found is too late since at
that point another variable may have already been scheduled.

The new algorithm computes the initialization dependency graph as
before but adds an extra pass during which functions are eliminated
from the graph (and their dependencies are "back-propagated").
This eliminates the problem of cycles only involving functions
(there are no functions).

When a cycle is found, the new code computes the cycle path from
the original object dependencies so it can still include functions
on the path as before, for the same detailed error message.

The new code also more clearly distinguishes between objects that
can be in the dependency graph (constants, variables, functions),
and objects that cannot, by introducing the dependency type, a new
subtype of Object. As a consequence, the dependency graph is smaller.

Fixes #10709.

Change-Id: Ib58d6ea65cfb279041a0286a2c8e865f11d244eb
Reviewed-on: https://go-review.googlesource.com/24131Reviewed-by: default avatarAlan Donovan <adonovan@google.com>
parent 66da8855
......@@ -573,26 +573,25 @@ func TestInitOrderInfo(t *testing.T) {
"a = next()", "b = next()", "c = next()", "d = next()", "e = next()", "f = next()", "_ = makeOrder()",
}},
// test case for issue 10709
// TODO(gri) enable once the issue is fixed
// {`package p13
// var (
// v = t.m()
// t = makeT(0)
// )
// type T struct{}
// func (T) m() int { return 0 }
// func makeT(n int) T {
// if n > 0 {
// return makeT(n-1)
// }
// return T{}
// }`, []string{
// "t = makeT(0)", "v = t.m()",
// }},
{`package p13
var (
v = t.m()
t = makeT(0)
)
type T struct{}
func (T) m() int { return 0 }
func makeT(n int) T {
if n > 0 {
return makeT(n-1)
}
return T{}
}`, []string{
"t = makeT(0)", "v = t.m()",
}},
// test case for issue 10709: same as test before, but variable decls swapped
{`package p14
......@@ -613,6 +612,24 @@ func TestInitOrderInfo(t *testing.T) {
}`, []string{
"t = makeT(0)", "v = t.m()",
}},
// another candidate possibly causing problems with issue 10709
{`package p15
var y1 = f1()
func f1() int { return g1() }
func g1() int { f1(); return x1 }
var x1 = 0
var y2 = f2()
func f2() int { return g2() }
func g2() int { return x2 }
var x2 = 0`, []string{
"x1 = 0", "y1 = f1()", "x2 = 0", "y2 = f2()",
}},
}
for _, test := range tests {
......
This diff is collapsed.
......@@ -153,6 +153,8 @@ func NewConst(pos token.Pos, pkg *Package, name string, typ Type, val constant.V
func (obj *Const) Val() constant.Value { return obj.val }
func (*Const) isDependency() {} // a constant may be a dependency of an initialization expression
// A TypeName represents a declared type.
type TypeName struct {
object
......@@ -187,6 +189,8 @@ func (obj *Var) Anonymous() bool { return obj.anonymous }
func (obj *Var) IsField() bool { return obj.isField }
func (*Var) isDependency() {} // a variable may be a dependency of an initialization expression
// A Func represents a declared function, concrete method, or abstract
// (interface) method. Its Type() is always a *Signature.
// An abstract method may belong to many interfaces due to embedding.
......@@ -215,6 +219,8 @@ func (obj *Func) Scope() *Scope {
return obj.typ.(*Signature).scope
}
func (*Func) isDependency() {} // a function may be a dependency of an initialization expression
// A Label represents a declared label.
type Label struct {
object
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment