Skip to content

Commit 1518b70

Browse files
committed
Fix #3058: Use the proper type for the "thisLocalVar" of tailrec.
In Scala 2.12, if a tail-recursive method is in a class or trait with a self-type, the type of the `_$this` local variable is the self type. We previously assumed it was always the current class type, which produced invalid IR in that case.
1 parent c5bc62e commit 1518b70

File tree

2 files changed

+50
-10
lines changed

2 files changed

+50
-10
lines changed

compiler/src/main/scala/org/scalajs/core/compiler/GenJSCode.scala

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ abstract class GenJSCode extends plugins.PluginComponent
136136

137137
// Per method body
138138
private val currentMethodSym = new ScopedVar[Symbol]
139-
private val thisLocalVarIdent = new ScopedVar[Option[js.Ident]]
139+
private val thisLocalVarInfo = new ScopedVar[Option[(js.Ident, jstpe.Type)]]
140140
private val fakeTailJumpParamRepl = new ScopedVar[(Symbol, Symbol)]
141141
private val enclosingLabelDefParams = new ScopedVar[Map[Symbol, List[Symbol]]]
142142
private val isModuleInitialized = new ScopedVar[VarBox[Boolean]]
@@ -168,7 +168,7 @@ abstract class GenJSCode extends plugins.PluginComponent
168168
unexpectedMutatedFields := mutable.Set.empty,
169169
generatedSAMWrapperCount := null,
170170
currentMethodSym := null,
171-
thisLocalVarIdent := null,
171+
thisLocalVarInfo := null,
172172
fakeTailJumpParamRepl := null,
173173
enclosingLabelDefParams := null,
174174
isModuleInitialized := null,
@@ -1397,7 +1397,7 @@ abstract class GenJSCode extends plugins.PluginComponent
13971397

13981398
withScopedVars(
13991399
currentMethodSym := sym,
1400-
thisLocalVarIdent := None,
1400+
thisLocalVarInfo := None,
14011401
fakeTailJumpParamRepl := (NoSymbol, NoSymbol),
14021402
enclosingLabelDefParams := Map.empty,
14031403
isModuleInitialized := new VarBox(false),
@@ -1661,13 +1661,15 @@ abstract class GenJSCode extends plugins.PluginComponent
16611661
mutableLocalVars += thisSym
16621662

16631663
val thisLocalIdent = encodeLocalSym(thisSym)
1664+
val thisLocalType = toIRType(thisSym.tpe)
1665+
16641666
val genRhs = genExpr(initialThis)
16651667
val thisLocalVarDef = js.VarDef(thisLocalIdent,
1666-
currentClassType, thisSym.isMutable, genRhs)
1668+
thisLocalType, thisSym.isMutable, genRhs)
16671669

16681670
val innerBody = {
16691671
withScopedVars(
1670-
thisLocalVarIdent := Some(thisLocalIdent)
1672+
thisLocalVarInfo := Some((thisLocalIdent, thisLocalType))
16711673
) {
16721674
genInnerBody()
16731675
}
@@ -1687,10 +1689,11 @@ abstract class GenJSCode extends plugins.PluginComponent
16871689
} else {
16881690
assert(!static, tree.pos)
16891691

1692+
val thisLocalIdent = freshLocalIdent("this")
16901693
withScopedVars(
1691-
thisLocalVarIdent := Some(freshLocalIdent("this"))
1694+
thisLocalVarInfo := Some((thisLocalIdent, jstpe.AnyType))
16921695
) {
1693-
val thisParamDef = js.ParamDef(thisLocalVarIdent.get.get,
1696+
val thisParamDef = js.ParamDef(thisLocalIdent,
16941697
jstpe.AnyType, mutable = false, rest = false)
16951698

16961699
js.MethodDef(static = true, methodName, thisParamDef :: jsParams,
@@ -1959,14 +1962,15 @@ abstract class GenJSCode extends plugins.PluginComponent
19591962
* is one.
19601963
*/
19611964
private def genThis()(implicit pos: Position): js.Tree = {
1962-
thisLocalVarIdent.fold[js.Tree] {
1965+
thisLocalVarInfo.fold[js.Tree] {
19631966
if (tryingToGenMethodAsJSFunction) {
19641967
throw new CancelGenMethodAsJSFunction(
19651968
"Trying to generate `this` inside the body")
19661969
}
19671970
js.This()(currentClassType)
1968-
} { thisLocalIdent =>
1969-
js.VarRef(thisLocalIdent)(currentClassType)
1971+
} { case (thisLocalVarIdent, thisLocalVarTpe) =>
1972+
// .copy() to get the correct position
1973+
js.VarRef(thisLocalVarIdent.copy())(thisLocalVarTpe)
19701974
}
19711975
}
19721976

test-suite/shared/src/test/scala/org/scalajs/testsuite/compiler/RegressionTest.scala

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,42 @@ class RegressionTest {
603603
assertEquals("B", c.t3)
604604
}
605605

606+
@Test def tailrec_in_trait_with_self_type_scala_2_12_issue_3058(): Unit = {
607+
trait Parent { this: Child =>
608+
@tailrec final def bar(i: Int, acc: Int): Int = {
609+
println(s"bar($i, $acc)")
610+
if (i <= count)
611+
bar(i + 1, acc + i)
612+
else
613+
acc
614+
}
615+
}
616+
617+
class Child extends Parent {
618+
def count: Int = 5
619+
}
620+
621+
assertEquals(15, new Child().bar(1, 0))
622+
}
623+
624+
@Test def tailrec_in_class_with_self_type_scala_2_12_issue_3058(): Unit = {
625+
class Parent { this: Child =>
626+
@tailrec final def bar(i: Int, acc: Int): Int = {
627+
println(s"bar($i, $acc)")
628+
if (i <= count)
629+
bar(i + 1, acc + i)
630+
else
631+
acc
632+
}
633+
}
634+
635+
class Child extends Parent {
636+
def count: Int = 5
637+
}
638+
639+
assertEquals(15, new Child().bar(1, 0))
640+
}
641+
606642
}
607643

608644
object RegressionTest {

0 commit comments

Comments
 (0)