Skip to content

Commit 1c9287f

Browse files
Backport "Improve source positions emited for synthetic unit in if-conditions" to LTS (#21147)
Backports #20431 to the LTS branch. PR submitted by the release tooling. [skip ci]
2 parents f28d68c + e680362 commit 1c9287f

File tree

7 files changed

+133
-14
lines changed

7 files changed

+133
-14
lines changed

compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala

+3-4
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import dotty.tools.dotc.core.Contexts.*
2424
import dotty.tools.dotc.core.Phases.*
2525
import dotty.tools.dotc.core.Decorators.em
2626
import dotty.tools.dotc.report
27+
import dotty.tools.dotc.ast.Trees.SyntheticUnit
2728

2829
/*
2930
*
@@ -218,10 +219,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
218219
val success = new asm.Label
219220
val failure = new asm.Label
220221

221-
val hasElse = !elsep.isEmpty && (elsep match {
222-
case Literal(value) if value.tag == UnitTag => false
223-
case _ => true
224-
})
222+
val hasElse = !elsep.hasAttachment(SyntheticUnit)
225223

226224
genCond(condp, success, failure, targetIfNoJump = success)
227225
markProgramPoint(success)
@@ -250,6 +248,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
250248
if hasElse then
251249
genLoadTo(elsep, expectedType, dest)
252250
else
251+
lineNumber(tree.cond)
253252
genAdaptAndSendToDest(UNIT, expectedType, dest)
254253
expectedType
255254
end if

compiler/src/dotty/tools/dotc/ast/Desugar.scala

+4-4
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ object desugar {
190190
if isSetterNeeded(vdef) then
191191
val setterParam = makeSyntheticParameter(tpt = SetterParamTree().watching(vdef))
192192
// The rhs gets filled in later, when field is generated and getter has parameters (see Memoize miniphase)
193-
val setterRhs = if (vdef.rhs.isEmpty) EmptyTree else unitLiteral
193+
val setterRhs = if (vdef.rhs.isEmpty) EmptyTree else syntheticUnitLiteral
194194
val setter = cpy.DefDef(vdef)(
195195
name = valName.setterName,
196196
paramss = (setterParam :: Nil) :: Nil,
@@ -1311,7 +1311,7 @@ object desugar {
13111311
def block(tree: Block)(using Context): Block = tree.expr match {
13121312
case EmptyTree =>
13131313
cpy.Block(tree)(tree.stats,
1314-
unitLiteral.withSpan(if (tree.stats.isEmpty) tree.span else tree.span.endPos))
1314+
syntheticUnitLiteral.withSpan(if (tree.stats.isEmpty) tree.span else tree.span.endPos))
13151315
case _ =>
13161316
tree
13171317
}
@@ -1811,7 +1811,7 @@ object desugar {
18111811
case ts: Thicket => ts.trees.tail
18121812
case t => Nil
18131813
} map {
1814-
case Block(Nil, EmptyTree) => unitLiteral // for s"... ${} ..."
1814+
case Block(Nil, EmptyTree) => syntheticUnitLiteral // for s"... ${} ..."
18151815
case Block(Nil, expr) => expr // important for interpolated string as patterns, see i1773.scala
18161816
case t => t
18171817
}
@@ -1839,7 +1839,7 @@ object desugar {
18391839
val pats1 = if (tpt.isEmpty) pats else pats map (Typed(_, tpt))
18401840
flatTree(pats1 map (makePatDef(tree, mods, _, rhs)))
18411841
case ext: ExtMethods =>
1842-
Block(List(ext), unitLiteral.withSpan(ext.span))
1842+
Block(List(ext), syntheticUnitLiteral.withSpan(ext.span))
18431843
case f: FunctionWithMods if f.hasErasedParams => makeFunctionWithValDefs(f, pt)
18441844
}
18451845
desugared.withSpan(tree.span)

compiler/src/dotty/tools/dotc/ast/untpd.scala

+5-1
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,11 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
481481
def InferredTypeTree(tpe: Type)(using Context): TypedSplice =
482482
TypedSplice(new InferredTypeTree().withTypeUnchecked(tpe))
483483

484-
def unitLiteral(implicit src: SourceFile): Literal = Literal(Constant(())).withAttachment(SyntheticUnit, ())
484+
def unitLiteral(implicit src: SourceFile): Literal =
485+
Literal(Constant(()))
486+
487+
def syntheticUnitLiteral(implicit src: SourceFile): Literal =
488+
unitLiteral.withAttachment(SyntheticUnit, ())
485489

486490
def ref(tp: NamedType)(using Context): Tree =
487491
TypedSplice(tpd.ref(tp))

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

+3-3
Original file line numberDiff line numberDiff line change
@@ -2284,7 +2284,7 @@ object Parsers {
22842284
in.nextToken();
22852285
val expr = subExpr()
22862286
if expr.span.exists then expr
2287-
else unitLiteral // finally without an expression
2287+
else syntheticUnitLiteral // finally without an expression
22882288
}
22892289
else {
22902290
if handler.isEmpty then
@@ -3768,10 +3768,10 @@ object Parsers {
37683768
val stats = selfInvocation() :: (
37693769
if (isStatSep) { in.nextToken(); blockStatSeq() }
37703770
else Nil)
3771-
Block(stats, unitLiteral)
3771+
Block(stats, syntheticUnitLiteral)
37723772
}
37733773
}
3774-
else Block(selfInvocation() :: Nil, unitLiteral)
3774+
else Block(selfInvocation() :: Nil, syntheticUnitLiteral)
37753775

37763776
/** SelfInvocation ::= this ArgumentExprs {ArgumentExprs}
37773777
*/

compiler/src/dotty/tools/dotc/typer/Typer.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -2009,7 +2009,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
20092009
// because we do not know the internal type params and method params.
20102010
// Hence no adaptation is possible, and we assume WildcardType as prototype.
20112011
(from, proto)
2012-
val expr1 = typedExpr(tree.expr orElse untpd.unitLiteral.withSpan(tree.span), proto)
2012+
val expr1 = typedExpr(tree.expr orElse untpd.syntheticUnitLiteral.withSpan(tree.span), proto)
20132013
assignType(cpy.Return(tree)(expr1, from))
20142014
end typedReturn
20152015

compiler/src/dotty/tools/repl/ReplCompiler.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ class ReplCompiler extends Compiler:
158158
def wrap(trees: List[untpd.Tree]): untpd.PackageDef = {
159159
import untpd.*
160160

161-
val valdef = ValDef("expr".toTermName, TypeTree(), Block(trees, unitLiteral).withSpan(Span(0, expr.length)))
161+
val valdef = ValDef("expr".toTermName, TypeTree(), Block(trees, syntheticUnitLiteral).withSpan(Span(0, expr.length)))
162162
val tmpl = Template(emptyConstructor, Nil, Nil, EmptyValDef, List(valdef))
163163
val wrapper = TypeDef("$wrapper".toTypeName, tmpl)
164164
.withMods(Modifiers(Final))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
package dotty.tools.backend.jvm
2+
3+
import scala.language.unsafeNulls
4+
5+
import org.junit.Assert._
6+
import org.junit.Test
7+
8+
class SourcePositionsTest extends DottyBytecodeTest:
9+
import ASMConverters._
10+
11+
@Test def issue18238_a(): Unit = {
12+
val code =
13+
"""
14+
|class Test {
15+
| def test(): Unit = {
16+
| var x = 3
17+
| var y = 2
18+
| while(true) {
19+
| if (x < y)
20+
| if (x >= y)
21+
| x += 1
22+
| else
23+
| y -= 1
24+
| }
25+
| }
26+
|}""".stripMargin
27+
28+
checkBCode(code) { dir =>
29+
val testClass = loadClassNode(dir.lookupName("Test.class", directory = false).input, skipDebugInfo = false)
30+
val testMethod = getMethod(testClass, "test")
31+
val lineNumbers = instructionsFromMethod(testMethod).collect{case ln: LineNumber => ln}
32+
val expected = List(
33+
LineNumber(4, Label(0)), // var x
34+
LineNumber(5, Label(4)), // var y
35+
LineNumber(6, Label(8)), // while(true)
36+
LineNumber(7, Label(13)), // if (x < y)
37+
LineNumber(8, Label(18)), // if (x >= y)
38+
LineNumber(9, Label(23)), // x += 1
39+
LineNumber(11, Label(27)), // y -= 1
40+
LineNumber(7, Label(32)) // <synthetic unit> point back to `if (x < y)
41+
)
42+
assertEquals(expected, lineNumbers)
43+
}
44+
}
45+
46+
@Test def issue18238_b(): Unit = {
47+
val code =
48+
"""
49+
|class Test {
50+
| def test(): Unit = {
51+
| var x = 3
52+
| var y = 2
53+
| while(true) {
54+
| if (x < y)
55+
| if (x >= y)
56+
| x += 1
57+
| else
58+
| y -= 1
59+
| else ()
60+
| }
61+
| }
62+
|}""".stripMargin
63+
64+
checkBCode(code) { dir =>
65+
val testClass = loadClassNode(dir.lookupName("Test.class", directory = false).input, skipDebugInfo = false)
66+
val testMethod = getMethod(testClass, "test")
67+
val lineNumbers = instructionsFromMethod(testMethod).collect{case ln: LineNumber => ln}
68+
val expected = List(
69+
LineNumber(4, Label(0)), // var x
70+
LineNumber(5, Label(4)), // var y
71+
LineNumber(6, Label(8)), // while(true)
72+
LineNumber(7, Label(13)), // if (x < y)
73+
LineNumber(8, Label(18)), // if (x >= y)
74+
LineNumber(9, Label(23)), // x += 1
75+
LineNumber(11, Label(27)), // y -= 1
76+
LineNumber(12, Label(32)) // else ()
77+
)
78+
assertEquals(expected, lineNumbers)
79+
}
80+
}
81+
82+
@Test def issue18238_c(): Unit = {
83+
val code =
84+
"""
85+
|class Test {
86+
| def test(): Unit = {
87+
| var x = 3
88+
| var y = 2
89+
| while(true) {
90+
| if (x < y)
91+
| if (x >= y)
92+
| x += 1
93+
| else
94+
| y -= 1
95+
| println()
96+
| }
97+
| }
98+
|}""".stripMargin
99+
100+
checkBCode(code) { dir =>
101+
val testClass = loadClassNode(dir.lookupName("Test.class", directory = false).input, skipDebugInfo = false)
102+
val testMethod = getMethod(testClass, "test")
103+
val lineNumbers = instructionsFromMethod(testMethod).collect{case ln: LineNumber => ln}
104+
val expected = List(
105+
LineNumber(4, Label(0)), // var x
106+
LineNumber(5, Label(4)), // var y
107+
LineNumber(6, Label(8)), // while(true)
108+
LineNumber(7, Label(13)), // if (x < y)
109+
LineNumber(8, Label(18)), // if (x >= y)
110+
LineNumber(9, Label(23)), // x += 1
111+
LineNumber(11, Label(27)), // y -= 1
112+
LineNumber(12, Label(31)) // println()
113+
)
114+
assertEquals(expected, lineNumbers)
115+
}
116+
}

0 commit comments

Comments
 (0)