Skip to content

Commit 0124dca

Browse files
committed
Fix #2808: Modify lifting of infix operations
Lift the left hand side of a right-associative infix operation only if the operation takes a by-value argument.
1 parent d33db04 commit 0124dca

File tree

4 files changed

+68
-27
lines changed

4 files changed

+68
-27
lines changed

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

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,31 @@ object desugar {
725725
tree
726726
}
727727

728+
/** Translate infix operation expression
729+
*
730+
* l op r ==> l.op(r) if op is left-associative
731+
* ==> r.op(l) if op is right-associative
732+
*/
733+
def binop(left: Tree, op: Ident, right: Tree)(implicit ctx: Context): Apply = {
734+
def assignToNamedArg(arg: Tree) = arg match {
735+
case Assign(Ident(name), rhs) => cpy.NamedArg(arg)(name, rhs)
736+
case _ => arg
737+
}
738+
def makeOp(fn: Tree, arg: Tree, selectPos: Position) = {
739+
val args: List[Tree] = arg match {
740+
case Parens(arg) => assignToNamedArg(arg) :: Nil
741+
case Tuple(args) => args.mapConserve(assignToNamedArg)
742+
case _ => arg :: Nil
743+
}
744+
Apply(Select(fn, op.name).withPos(selectPos), args)
745+
}
746+
747+
if (isLeftAssoc(op.name))
748+
makeOp(left, right, Position(left.pos.start, op.pos.end, op.pos.start))
749+
else
750+
makeOp(right, left, Position(op.pos.start, right.pos.end))
751+
}
752+
728753
/** Make closure corresponding to function.
729754
* params => body
730755
* ==>
@@ -832,30 +857,6 @@ object desugar {
832857
Block(ldef, call)
833858
}
834859

835-
/** Translate infix operation expression left op right
836-
*/
837-
def makeBinop(left: Tree, op: Ident, right: Tree): Tree = {
838-
def assignToNamedArg(arg: Tree) = arg match {
839-
case Assign(Ident(name), rhs) => cpy.NamedArg(arg)(name, rhs)
840-
case _ => arg
841-
}
842-
if (isLeftAssoc(op.name)) {
843-
val args: List[Tree] = right match {
844-
case Parens(arg) => assignToNamedArg(arg) :: Nil
845-
case Tuple(args) => args mapConserve assignToNamedArg
846-
case _ => right :: Nil
847-
}
848-
val selectPos = Position(left.pos.start, op.pos.end, op.pos.start)
849-
Apply(Select(left, op.name).withPos(selectPos), args)
850-
} else {
851-
val x = UniqueName.fresh()
852-
val selectPos = Position(op.pos.start, right.pos.end, op.pos.start)
853-
new InfixOpBlock(
854-
ValDef(x, TypeTree(), left).withMods(synthetic),
855-
Apply(Select(right, op.name).withPos(selectPos), Ident(x).withPos(left.pos)))
856-
}
857-
}
858-
859860
/** Create tree for for-comprehension `<for (enums) do body>` or
860861
* `<for (enums) yield body>` where mapName and flatMapName are chosen
861862
* corresponding to whether this is a for-do or a for-yield.
@@ -1066,10 +1067,10 @@ object desugar {
10661067
if (!op.isBackquoted && op.name == tpnme.raw.AMP) AndTypeTree(l, r) // l & r
10671068
else if (!op.isBackquoted && op.name == tpnme.raw.BAR) OrTypeTree(l, r) // l | r
10681069
else AppliedTypeTree(op, l :: r :: Nil) // op[l, r]
1069-
else if (ctx.mode is Mode.Pattern)
1070+
else {
1071+
assert(ctx.mode is Mode.Pattern) // expressions are handled separately by `binop`
10701072
Apply(op, l :: r :: Nil) // op(l, r)
1071-
else // l.op(r), or val x = r; l.op(x), plus handle named args specially
1072-
makeBinop(l, op, r)
1073+
}
10731074
case PostfixOp(t, op) =>
10741075
if ((ctx.mode is Mode.Type) && !op.isBackquoted && op.name == tpnme.raw.STAR) {
10751076
val seqType = if (ctx.compilationUnit.isJava) defn.ArrayType else defn.SeqType

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1679,6 +1679,30 @@ class Typer extends Namer
16791679
res
16801680
}
16811681

1682+
/** Translate infix operation expression `l op r` to
1683+
*
1684+
* l.op(r) if `op` is left-associative
1685+
* { val x = l; r.op(l) } if `op` is right-associative call-by-value and `l` is impure
1686+
* r.op(l) if `op` is right-associative call-by-name or `l` is pure
1687+
*/
1688+
def typedInfixOp(tree: untpd.InfixOp, pt: Type)(implicit ctx: Context): Tree = {
1689+
val untpd.InfixOp(l, op, r) = tree
1690+
val app = typedApply(desugar.binop(l, op, r), pt)
1691+
if (untpd.isLeftAssoc(op.name)) app
1692+
else {
1693+
val defs = new mutable.ListBuffer[Tree]
1694+
def lift(app: Tree): Tree = (app: @unchecked) match {
1695+
case Apply(fn, args) =>
1696+
tpd.cpy.Apply(app)(fn, LiftImpure.liftArgs(defs, fn.tpe, args))
1697+
case Assign(lhs, rhs) =>
1698+
tpd.cpy.Assign(app)(lhs, lift(rhs))
1699+
case Block(stats, expr) =>
1700+
tpd.cpy.Block(app)(stats, lift(expr))
1701+
}
1702+
Applications.wrapDefs(defs, lift(app))
1703+
}
1704+
}
1705+
16821706
/** Retrieve symbol attached to given tree */
16831707
protected def retrieveSym(tree: untpd.Tree)(implicit ctx: Context) = tree.removeAttachment(SymOfTree) match {
16841708
case Some(sym) =>
@@ -1765,6 +1789,7 @@ class Typer extends Namer
17651789
case tree: untpd.TypedSplice => typedTypedSplice(tree)
17661790
case tree: untpd.UnApply => typedUnApply(tree, pt)
17671791
case tree: untpd.DependentTypeTree => typed(untpd.TypeTree().withPos(tree.pos), pt)
1792+
case tree: untpd.InfixOp if ctx.mode.isExpr => typedInfixOp(tree, pt)
17681793
case tree @ untpd.PostfixOp(qual, Ident(nme.WILDCARD)) => typedAsFunction(tree, pt)
17691794
case untpd.EmptyTree => tpd.EmptyTree
17701795
case _ => typedUnadapted(desugar(tree), pt)

tests/run/i2808.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
foo

tests/run/i2808.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
class C {
2+
def m1_:(f: Int) = ()
3+
def m2_:(f: => Int) = ()
4+
}
5+
6+
object Test {
7+
def foo() = { println("foo") ; 5 }
8+
9+
def main(args: Array[String]): Unit = {
10+
val c = new C
11+
foo() m1_: c
12+
foo() m2_: c
13+
}
14+
}

0 commit comments

Comments
 (0)