Skip to content

Commit 2c770ae

Browse files
committed
SI-8111 Repair symbol owners after abandoned named-/default-args
Names/Defaults eagerly transforms an application with temporaries to maintain evaluation order, and dutifully changes owners of symbols along the way. However, if this approach doesn't work out, we throw away this and try a auto-tupling. However, we an still witness symbols owned by the temporaries. This commit records which symbols are owned by the context.owner before `transformNamedApplication`, and rolls back the changes before `tryTupleApply`. Perhaps a better approach would be to separate the names/defaults applicability checks from the evaluation-order-preserving transform, and only call the latter after we have decided to go that way.
1 parent 370d6d6 commit 2c770ae

File tree

2 files changed

+42
-0
lines changed

2 files changed

+42
-0
lines changed

src/compiler/scala/tools/nsc/typechecker/Typers.scala

+18
Original file line numberDiff line numberDiff line change
@@ -3269,6 +3269,23 @@ trait Typers extends Modes with Adaptations with Tags {
32693269
// calls to the default getters. Example:
32703270
// foo[Int](a)() ==> foo[Int](a)(b = foo$qual.foo$default$2[Int](a))
32713271
checkNotMacro()
3272+
3273+
// SI-8111 transformNamedApplication eagerly shuffles around the application to preserve
3274+
// evaluation order. During this process, it calls `changeOwner` on symbols that
3275+
// are transplanted underneath synthetic temporary vals.
3276+
//
3277+
// Here, we keep track of the symbols owned by `context.owner` to enable us to
3278+
// rollback. Note that duplicating trees would not be enough to fix this problem,
3279+
// we would also need to clone local symbols in the duplicated tree to truly isolate
3280+
// things.
3281+
def ownerOf(sym: Symbol) = if (sym == null || sym == NoSymbol) NoSymbol else sym.owner
3282+
val symsOwnedByContextOwner = tree.collect {
3283+
case t @ (_: DefTree | _: Function) if ownerOf(t.symbol) == context.owner => t.symbol
3284+
}
3285+
def rollbackNamesDefaultsOwnerChanges() {
3286+
symsOwnedByContextOwner foreach (_.owner = context.owner)
3287+
}
3288+
32723289
val fun1 = transformNamedApplication(Typer.this, mode, pt)(fun, x => x)
32733290
if (fun1.isErroneous) duplErrTree
32743291
else {
@@ -3297,6 +3314,7 @@ trait Typers extends Modes with Adaptations with Tags {
32973314
if (!(context.diagnostic contains note)) context.diagnostic = note :: context.diagnostic
32983315
doTypedApply(tree, if (blockIsEmpty) fun else fun1, allArgs, mode, pt)
32993316
} else {
3317+
rollbackNamesDefaultsOwnerChanges()
33003318
tryTupleApply getOrElse duplErrorTree(NotEnoughArgsError(tree, fun, missing))
33013319
}
33023320
}

test/files/pos/t8111.scala

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
trait T {
2+
3+
def crashy(ma: Any) {
4+
// okay
5+
val f1 = (u: Unit) => ma
6+
foo(f1)()
7+
foo((u: Unit) => ma)
8+
foo(0, (u: Any) => ma) apply ()
9+
10+
// crash due to side effects on the onwer of the symbol in the
11+
// qualifier or arguments of the application during an abandoned
12+
// names/defaults transform. The code type checkes because of
13+
// autp-tupling which promotes and empty parmater list to `(): Unit`
14+
foo((u: Any) => ma)()
15+
16+
{{(u: Any) => ma}; this}.foo(0)()
17+
18+
foo({def foo = ma; 0})()
19+
20+
{def foo = ma; this}.foo(0)()
21+
}
22+
23+
def foo(f: Any): Any => Any
24+
}

0 commit comments

Comments
 (0)