Skip to content

Commit

Permalink
Don't emit line number for synthetic unit value (#18717)
Browse files Browse the repository at this point in the history
Synthetic `()` values are added to blocks without an expression. Don't
emit a line number for them.

Implemented by checking the `SyntheticUnit` attachment. This seems
simpler than trying to control the position assigned to synthetic unit
trees, as they are created in many places.

Fixes #18320. Forward port of
scala/scala#10577
  • Loading branch information
sjrd authored Oct 18, 2023
2 parents f62429b + e204e30 commit b71c2d0
Show file tree
Hide file tree
Showing 16 changed files with 60 additions and 40 deletions.
45 changes: 22 additions & 23 deletions compiler/src/dotty/tools/backend/jvm/BCodeSkelBuilder.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,24 @@ package backend
package jvm

import scala.language.unsafeNulls

import scala.annotation.tailrec

import scala.collection.{ mutable, immutable }

import scala.collection.{immutable, mutable}
import scala.tools.asm
import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.ast.TreeTypeMap
import dotty.tools.dotc.CompilationUnit
import dotty.tools.dotc.core.Decorators._
import dotty.tools.dotc.core.Flags._
import dotty.tools.dotc.core.StdNames._
import dotty.tools.dotc.core.NameKinds._
import dotty.tools.dotc.ast.Trees.SyntheticUnit
import dotty.tools.dotc.core.Decorators.*
import dotty.tools.dotc.core.Flags.*
import dotty.tools.dotc.core.StdNames.*
import dotty.tools.dotc.core.NameKinds.*
import dotty.tools.dotc.core.Names.TermName
import dotty.tools.dotc.core.Symbols._
import dotty.tools.dotc.core.Types._
import dotty.tools.dotc.core.Contexts._
import dotty.tools.dotc.util.Spans._
import dotty.tools.dotc.core.Symbols.*
import dotty.tools.dotc.core.Types.*
import dotty.tools.dotc.core.Contexts.*
import dotty.tools.dotc.util.Spans.*
import dotty.tools.dotc.report
import dotty.tools.dotc.transform.SymUtils._
import dotty.tools.dotc.transform.SymUtils.*

/*
*
Expand Down Expand Up @@ -624,16 +622,17 @@ trait BCodeSkelBuilder extends BCodeHelpers {
case _ => a
}

if (!emitLines || !tree.span.exists) return;
val nr = ctx.source.offsetToLine(tree.span.point) + 1
if (nr != lastEmittedLineNr) {
lastEmittedLineNr = nr
getNonLabelNode(lastInsn) match {
case lnn: asm.tree.LineNumberNode =>
// overwrite previous landmark as no instructions have been emitted for it
lnn.line = nr
case _ =>
mnode.visitLineNumber(nr, currProgramPoint())
if (emitLines && tree.span.exists && !tree.hasAttachment(SyntheticUnit)) {
val nr = ctx.source.offsetToLine(tree.span.point) + 1
if (nr != lastEmittedLineNr) {
lastEmittedLineNr = nr
getNonLabelNode(lastInsn) match {
case lnn: asm.tree.LineNumberNode =>
// overwrite previous landmark as no instructions have been emitted for it
lnn.line = nr
case _ =>
mnode.visitLineNumber(nr, currProgramPoint())
}
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1844,7 +1844,7 @@ object desugar {
case ts: Thicket => ts.trees.tail
case t => Nil
} map {
case Block(Nil, EmptyTree) => Literal(Constant(())) // for s"... ${} ..."
case Block(Nil, EmptyTree) => unitLiteral // for s"... ${} ..."
case Block(Nil, expr) => expr // important for interpolated string as patterns, see i1773.scala
case t => t
}
Expand Down Expand Up @@ -1872,7 +1872,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), Literal(Constant(())).withSpan(ext.span))
Block(List(ext), unitLiteral.withSpan(ext.span))
case f: FunctionWithMods if f.hasErasedParams => makeFunctionWithValDefs(f, pt)
}
desugared.withSpan(tree.span)
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ object Trees {

/** Property key for backquoted identifiers and definitions */
val Backquoted: Property.StickyKey[Unit] = Property.StickyKey()

val SyntheticUnit: Property.StickyKey[Unit] = Property.StickyKey()

/** Trees take a parameter indicating what the type of their `tpe` field
* is. Two choices: `Type` or `Untyped`.
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/tpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
ta.assignType(untpd.Literal(const))

def unitLiteral(using Context): Literal =
Literal(Constant(()))
Literal(Constant(())).withAttachment(SyntheticUnit, ())

def nullLiteral(using Context): Literal =
Literal(Constant(null))
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ 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(()))
def unitLiteral(implicit src: SourceFile): Literal = Literal(Constant(())).withAttachment(SyntheticUnit, ())

def ref(tp: NamedType)(using Context): Tree =
TypedSplice(tpd.ref(tp))
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/inlines/Inliner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -793,7 +793,7 @@ class Inliner(val call: tpd.Tree)(using Context):
typed(tree.cond, defn.BooleanType)(using condCtx) match {
case cond1 @ ConstantValue(b: Boolean) =>
val selected0 = if (b) tree.thenp else tree.elsep
val selected = if (selected0.isEmpty) tpd.Literal(Constant(())) else typed(selected0, pt)
val selected = if (selected0.isEmpty) tpd.unitLiteral else typed(selected0, pt)
if (isIdempotentExpr(cond1)) selected
else Block(cond1 :: Nil, selected)
case cond1 =>
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/inlines/Inlines.scala
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ object Inlines:
arg match
case ConstantValue(_) | Inlined(_, Nil, Typed(ConstantValue(_), _)) => // ok
case _ => report.error(em"expected a constant value but found: $arg", arg.srcPos)
return Literal(Constant(())).withSpan(call.span)
return unitLiteral.withSpan(call.span)
else if inlinedMethod == defn.Compiletime_codeOf then
return Intrinsics.codeOf(arg, call.srcPos)
case _ =>
Expand Down
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2211,7 +2211,7 @@ object Parsers {
case _ =>
}
patch(source, cond.span.endPos, "}) ()")
WhileDo(Block(body, cond), Literal(Constant(())))
WhileDo(Block(body, cond), unitLiteral)
}
case TRY =>
val tryOffset = in.offset
Expand Down Expand Up @@ -2241,7 +2241,7 @@ object Parsers {
in.nextToken();
val expr = subExpr()
if expr.span.exists then expr
else Literal(Constant(())) // finally without an expression
else unitLiteral // finally without an expression
}
else {
if handler.isEmpty then
Expand Down Expand Up @@ -3728,10 +3728,10 @@ object Parsers {
val stats = selfInvocation() :: (
if (isStatSep) { in.nextToken(); blockStatSeq() }
else Nil)
Block(stats, Literal(Constant(())))
Block(stats, unitLiteral)
}
}
else Block(selfInvocation() :: Nil, Literal(Constant(())))
else Block(selfInvocation() :: Nil, unitLiteral)

/** SelfInvocation ::= this ArgumentExprs {ArgumentExprs}
*/
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/DropBreaks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ class DropBreaks extends MiniPhase:
case id: Ident =>
val arg = (args: @unchecked) match
case arg :: Nil => arg
case Nil => Literal(Constant(())).withSpan(tree.span)
case Nil => unitLiteral.withSpan(tree.span)
Some((id.symbol, arg))
case _ => None
case _ => None
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/Erasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ object Erasure {
cast(tree1, pt)
case _ =>
val cls = pt.classSymbol
if (cls eq defn.UnitClass) constant(tree, Literal(Constant(())))
if (cls eq defn.UnitClass) constant(tree, unitLiteral)
else {
assert(cls ne defn.ArrayClass)
ref(unboxMethod(cls.asClass)).appliedTo(tree)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/Memoize.scala
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ class Memoize extends MiniPhase with IdentityDenotTransformer { thisPhase =>
myState.classesThatNeedReleaseFence += sym.owner
val initializer =
if isErasableBottomField(field, tree.termParamss.head.head.tpt.tpe.classSymbol)
then Literal(Constant(()))
then unitLiteral
else Assign(ref(field), adaptToField(field, ref(tree.termParamss.head.head.symbol)))
val setterDef = cpy.DefDef(tree)(rhs = transformFollowingDeep(initializer)(using ctx.withOwner(sym)))
sym.keepAnnotationsCarrying(thisPhase, Set(defn.SetterMetaAnnot))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -947,7 +947,7 @@ object PatternMatcher {
case LabeledPlan(label, expr) =>
Labeled(label, emit(expr))
case ReturnPlan(label) =>
Return(Literal(Constant(())), ref(label))
Return(unitLiteral, ref(label))
case plan: SeqPlan =>
def default = seq(emit(plan.head) :: Nil, emit(plan.tail))
def maybeEmitSwitch(scrutinee: Tree): Tree = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ object TypeTestsCasts {
Typed(expr, tree.args.head) // Replace cast by type ascription (which does not generate any bytecode)
else if (testCls eq defn.BoxedUnitClass)
// as a special case, casting to Unit always successfully returns Unit
Block(expr :: Nil, Literal(Constant(()))).withSpan(expr.span)
Block(expr :: Nil, unitLiteral).withSpan(expr.span)
else if (foundClsSymPrimitive)
if (testCls.isPrimitiveValueClass) primitiveConversion(expr, testCls)
else derivedTree(box(expr), defn.Any_asInstanceOf, testType)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/RefChecks.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1578,7 +1578,7 @@ class RefChecks extends MiniPhase { thisPhase =>
private def transformIf(tree: If): Tree = {
val If(cond, thenpart, elsepart) = tree
def unitIfEmpty(t: Tree): Tree =
if (t == EmptyTree) Literal(Constant(())).setPos(tree.pos).setType(UnitTpe) else t
if (t == EmptyTree) unitLiteral.setPos(tree.pos).setType(UnitTpe) else t
cond.tpe match {
case ConstantType(value) =>
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 @@ -4216,7 +4216,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
if (!ctx.isAfterTyper && !tree.isInstanceOf[Inlined] && ctx.settings.WvalueDiscard.value && !isThisTypeResult(tree)) {
report.warning(ValueDiscarding(tree.tpe), tree.srcPos)
}
return tpd.Block(tree1 :: Nil, Literal(Constant(())))
return tpd.Block(tree1 :: Nil, unitLiteral)
}

// convert function literal to SAM closure
Expand Down
19 changes: 19 additions & 0 deletions compiler/test/dotty/tools/backend/jvm/DottyBytecodeTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1682,6 +1682,25 @@ class DottyBytecodeTests extends DottyBytecodeTest {
assertSameCode(instructions, expected)
}
}

@Test def i18320(): Unit = {
val c1 =
"""class C {
| def m: Unit = {
| val x = 1
| }
|}
|""".stripMargin
checkBCode(c1) {dir =>
val clsIn = dir.lookupName("C.class", directory = false).input
val clsNode = loadClassNode(clsIn, skipDebugInfo = false)
val method = getMethod(clsNode, "m")
val instructions = instructionsFromMethod(method).filter(_.isInstanceOf[LineNumber])
val expected = List(LineNumber(3, Label(0)))
assertSameCode(instructions, expected)

}
}
}

object invocationReceiversTestCode {
Expand Down

0 comments on commit b71c2d0

Please sign in to comment.