gwt /
gwt /
0787e5e3ea53929a53f6f648a9c4e2ca7f10b9b6 Fix for issue #497. Reviewed by gwt.team.mmendez
http://code.google.com/p/google-web-toolkit/issues/detail?id=497
This is one of the most subtle compiler issues I've ever debugged. The
following sequence of events triggers the problem described in the issue.
Assume Foo is a class that implements IFoo.
1) MakeCallsStatic creates a static implementation "Foo.$foo()" of
instance method "Foo.foo()"; tthis moves the body of foo() into $foo(),
and replaces the body of foo() with a call to $foo()
2) MethodInliner inlines calls to Foo.$foo() at all call sites, including
the call from Foo.foo()
3) Pruner prunes Foo.$foo(), because it's been inlined everywhere, there
are no outstanding calls to it
4) More optimizations occur which allow an IFoo type variable to be
tightened to a Foo type variables.
5) MakeCallsStatic sees the tightened variable and statically resolves an
IFoo.foo() call to a Foo.$foo(); unfortunately, $foo() has already been
pruned and there is no process for unpruning
6) For various reasons, the new call to Foo.$foo() does not get inlined at
the new call site (this is a rare case, that it would get inlined
everywhere else, but not here).
7) Foo.$foo() does not get visited during GenerateJavaScriptAST (because
it's pruned); the outstanding non-inlined call to it causes a compiler
error.
My solution addresses step #3 in the problem chain. I modified Pruner so
that Foo.$foo() will not pruned as long as Foo.foo() is live, even if
Foo.$foo() has been inlined into Foo.foo(). On the final pruning pass, we
will go ahead and prune Foo.$foo() if it's unreferenced. By that time,
MakeCallsStatic will never run again because all optimizations will
be done.
However, having $foo() in limbo for some time raised a couple of issues.
I discovered that all of its parameters were being tightening to null,
which led me to two observations.
1) It doesn't make sense to nullflow an unreferenced parameter. Period.
Unlike fields & locals, there is never an implicit null initialization;
the value has to come from SOMEWHERE.
2) It doesn't make sense to make Foo.$foo()'s params tighter than
foo()'s params as long as foo() is still live. This is because
some call site currently pointing to foo() may change and point at
$foo() later, meaning $foo()'s params are now invalidly too tight.
So I made a special case upref from $foo() to foo(), which makes sense
because the normal case is explicit type flow from foo() to $foo() when
inlining does not occur.
I fixed both of the above issues in TypeTightener.
I also changed the semantics of the return value of
tryInlineSimpleMethodCall to differentiate between methods that cannot be
inlined (even in theory) and methods that cannot be inlined at this
particular call site. This prevents a caching bug where a failure to
inline at one site would prevent you from even trying at other sites.
There are also a couple of field sorts mixed into this patch.
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@220 8db76d5a-ed1c-0410-87a9-c151d255dfc7
4 files changed