Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport "Improve source positions emited for synthetic unit in if-conditions" to LTS #21147

Merged
merged 4 commits into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions compiler/src/dotty/tools/backend/jvm/BCodeBodyBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import dotty.tools.dotc.core.Contexts.*
import dotty.tools.dotc.core.Phases.*
import dotty.tools.dotc.core.Decorators.em
import dotty.tools.dotc.report
import dotty.tools.dotc.ast.Trees.SyntheticUnit

/*
*
Expand Down Expand Up @@ -218,10 +219,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
val success = new asm.Label
val failure = new asm.Label

val hasElse = !elsep.isEmpty && (elsep match {
case Literal(value) if value.tag == UnitTag => false
case _ => true
})
val hasElse = !elsep.hasAttachment(SyntheticUnit)

genCond(condp, success, failure, targetIfNoJump = success)
markProgramPoint(success)
Expand Down Expand Up @@ -250,6 +248,7 @@ trait BCodeBodyBuilder extends BCodeSkelBuilder {
if hasElse then
genLoadTo(elsep, expectedType, dest)
else
lineNumber(tree.cond)
genAdaptAndSendToDest(UNIT, expectedType, dest)
expectedType
end if
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ object desugar {
if isSetterNeeded(vdef) then
val setterParam = makeSyntheticParameter(tpt = SetterParamTree().watching(vdef))
// The rhs gets filled in later, when field is generated and getter has parameters (see Memoize miniphase)
val setterRhs = if (vdef.rhs.isEmpty) EmptyTree else unitLiteral
val setterRhs = if (vdef.rhs.isEmpty) EmptyTree else syntheticUnitLiteral
val setter = cpy.DefDef(vdef)(
name = valName.setterName,
paramss = (setterParam :: Nil) :: Nil,
Expand Down Expand Up @@ -1311,7 +1311,7 @@ object desugar {
def block(tree: Block)(using Context): Block = tree.expr match {
case EmptyTree =>
cpy.Block(tree)(tree.stats,
unitLiteral.withSpan(if (tree.stats.isEmpty) tree.span else tree.span.endPos))
syntheticUnitLiteral.withSpan(if (tree.stats.isEmpty) tree.span else tree.span.endPos))
case _ =>
tree
}
Expand Down Expand Up @@ -1811,7 +1811,7 @@ object desugar {
case ts: Thicket => ts.trees.tail
case t => Nil
} map {
case Block(Nil, EmptyTree) => unitLiteral // for s"... ${} ..."
case Block(Nil, EmptyTree) => syntheticUnitLiteral // for s"... ${} ..."
case Block(Nil, expr) => expr // important for interpolated string as patterns, see i1773.scala
case t => t
}
Expand Down Expand Up @@ -1839,7 +1839,7 @@ object desugar {
val pats1 = if (tpt.isEmpty) pats else pats map (Typed(_, tpt))
flatTree(pats1 map (makePatDef(tree, mods, _, rhs)))
case ext: ExtMethods =>
Block(List(ext), unitLiteral.withSpan(ext.span))
Block(List(ext), syntheticUnitLiteral.withSpan(ext.span))
case f: FunctionWithMods if f.hasErasedParams => makeFunctionWithValDefs(f, pt)
}
desugared.withSpan(tree.span)
Expand Down
6 changes: 5 additions & 1 deletion compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,11 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
def InferredTypeTree(tpe: Type)(using Context): TypedSplice =
TypedSplice(new InferredTypeTree().withTypeUnchecked(tpe))

def unitLiteral(implicit src: SourceFile): Literal = Literal(Constant(())).withAttachment(SyntheticUnit, ())
def unitLiteral(implicit src: SourceFile): Literal =
Literal(Constant(()))

def syntheticUnitLiteral(implicit src: SourceFile): Literal =
unitLiteral.withAttachment(SyntheticUnit, ())

def ref(tp: NamedType)(using Context): Tree =
TypedSplice(tpd.ref(tp))
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2284,7 +2284,7 @@ object Parsers {
in.nextToken();
val expr = subExpr()
if expr.span.exists then expr
else unitLiteral // finally without an expression
else syntheticUnitLiteral // finally without an expression
}
else {
if handler.isEmpty then
Expand Down Expand Up @@ -3768,10 +3768,10 @@ object Parsers {
val stats = selfInvocation() :: (
if (isStatSep) { in.nextToken(); blockStatSeq() }
else Nil)
Block(stats, unitLiteral)
Block(stats, syntheticUnitLiteral)
}
}
else Block(selfInvocation() :: Nil, unitLiteral)
else Block(selfInvocation() :: Nil, syntheticUnitLiteral)

/** SelfInvocation ::= this ArgumentExprs {ArgumentExprs}
*/
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2009,7 +2009,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
// because we do not know the internal type params and method params.
// Hence no adaptation is possible, and we assume WildcardType as prototype.
(from, proto)
val expr1 = typedExpr(tree.expr orElse untpd.unitLiteral.withSpan(tree.span), proto)
val expr1 = typedExpr(tree.expr orElse untpd.syntheticUnitLiteral.withSpan(tree.span), proto)
assignType(cpy.Return(tree)(expr1, from))
end typedReturn

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/repl/ReplCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ class ReplCompiler extends Compiler:
def wrap(trees: List[untpd.Tree]): untpd.PackageDef = {
import untpd.*

val valdef = ValDef("expr".toTermName, TypeTree(), Block(trees, unitLiteral).withSpan(Span(0, expr.length)))
val valdef = ValDef("expr".toTermName, TypeTree(), Block(trees, syntheticUnitLiteral).withSpan(Span(0, expr.length)))
val tmpl = Template(emptyConstructor, Nil, Nil, EmptyValDef, List(valdef))
val wrapper = TypeDef("$wrapper".toTypeName, tmpl)
.withMods(Modifiers(Final))
Expand Down
116 changes: 116 additions & 0 deletions compiler/test/dotty/tools/backend/jvm/SourcePositionsTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package dotty.tools.backend.jvm

import scala.language.unsafeNulls

import org.junit.Assert._
import org.junit.Test

class SourcePositionsTest extends DottyBytecodeTest:
import ASMConverters._

@Test def issue18238_a(): Unit = {
val code =
"""
|class Test {
| def test(): Unit = {
| var x = 3
| var y = 2
| while(true) {
| if (x < y)
| if (x >= y)
| x += 1
| else
| y -= 1
| }
| }
|}""".stripMargin

checkBCode(code) { dir =>
val testClass = loadClassNode(dir.lookupName("Test.class", directory = false).input, skipDebugInfo = false)
val testMethod = getMethod(testClass, "test")
val lineNumbers = instructionsFromMethod(testMethod).collect{case ln: LineNumber => ln}
val expected = List(
LineNumber(4, Label(0)), // var x
LineNumber(5, Label(4)), // var y
LineNumber(6, Label(8)), // while(true)
LineNumber(7, Label(13)), // if (x < y)
LineNumber(8, Label(18)), // if (x >= y)
LineNumber(9, Label(23)), // x += 1
LineNumber(11, Label(27)), // y -= 1
LineNumber(7, Label(32)) // <synthetic unit> point back to `if (x < y)
)
assertEquals(expected, lineNumbers)
}
}

@Test def issue18238_b(): Unit = {
val code =
"""
|class Test {
| def test(): Unit = {
| var x = 3
| var y = 2
| while(true) {
| if (x < y)
| if (x >= y)
| x += 1
| else
| y -= 1
| else ()
| }
| }
|}""".stripMargin

checkBCode(code) { dir =>
val testClass = loadClassNode(dir.lookupName("Test.class", directory = false).input, skipDebugInfo = false)
val testMethod = getMethod(testClass, "test")
val lineNumbers = instructionsFromMethod(testMethod).collect{case ln: LineNumber => ln}
val expected = List(
LineNumber(4, Label(0)), // var x
LineNumber(5, Label(4)), // var y
LineNumber(6, Label(8)), // while(true)
LineNumber(7, Label(13)), // if (x < y)
LineNumber(8, Label(18)), // if (x >= y)
LineNumber(9, Label(23)), // x += 1
LineNumber(11, Label(27)), // y -= 1
LineNumber(12, Label(32)) // else ()
)
assertEquals(expected, lineNumbers)
}
}

@Test def issue18238_c(): Unit = {
val code =
"""
|class Test {
| def test(): Unit = {
| var x = 3
| var y = 2
| while(true) {
| if (x < y)
| if (x >= y)
| x += 1
| else
| y -= 1
| println()
| }
| }
|}""".stripMargin

checkBCode(code) { dir =>
val testClass = loadClassNode(dir.lookupName("Test.class", directory = false).input, skipDebugInfo = false)
val testMethod = getMethod(testClass, "test")
val lineNumbers = instructionsFromMethod(testMethod).collect{case ln: LineNumber => ln}
val expected = List(
LineNumber(4, Label(0)), // var x
LineNumber(5, Label(4)), // var y
LineNumber(6, Label(8)), // while(true)
LineNumber(7, Label(13)), // if (x < y)
LineNumber(8, Label(18)), // if (x >= y)
LineNumber(9, Label(23)), // x += 1
LineNumber(11, Label(27)), // y -= 1
LineNumber(12, Label(31)) // println()
)
assertEquals(expected, lineNumbers)
}
}