Skip to content

Commit 528f6b0

Browse files
committed
Fix #1503 - be careful where to insert an apply.
`apply` nodes should not be inserted in the result parts of a block, if-then-else, match, or try. Instead they should be added to the surrounding statement.
1 parent c420b4c commit 528f6b0

File tree

9 files changed

+86
-12
lines changed

9 files changed

+86
-12
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,7 +699,7 @@ object desugar {
699699
Apply(Select(left, op), args)
700700
} else {
701701
val x = ctx.freshName().toTermName
702-
Block(
702+
new InfixOpBlock(
703703
ValDef(x, TypeTree(), left).withMods(synthetic),
704704
Apply(Select(right, op), Ident(x)))
705705
}

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,16 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
6363
class PolyTypeDef(name: TypeName, override val tparams: List[TypeDef], rhs: Tree)
6464
extends TypeDef(name, rhs)
6565

66+
/** A block arising from a right-associative infix operation, where, e.g.
67+
*
68+
* a +: b
69+
*
70+
* is expanded to
71+
*
72+
* { val x = a; b.+:(x) }
73+
*/
74+
class InfixOpBlock(leftOperand: Tree, rightOp: Tree) extends Block(leftOperand :: Nil, rightOp)
75+
6676
// ----- TypeTrees that refer to other tree's symbols -------------------
6777

6878
/** A type tree that gets its type from some other tree's symbol. Enters the

src/dotty/tools/dotc/core/Types.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -927,15 +927,18 @@ object Types {
927927
def narrow(implicit ctx: Context): TermRef =
928928
TermRef(NoPrefix, ctx.newSkolem(this))
929929

930-
/** Useful for diagnsotics: The underlying type if this type is a type proxy,
930+
/** Useful for diagnostics: The underlying type if this type is a type proxy,
931931
* otherwise NoType
932932
*/
933933
def underlyingIfProxy(implicit ctx: Context) = this match {
934934
case this1: TypeProxy => this1.underlying
935935
case _ => NoType
936936
}
937937

938-
// ----- Normalizing typerefs over refined types ----------------------------
938+
/** If this is a FunProto or PolyProto, WildcardType, otherwise this */
939+
def notApplied: Type = this
940+
941+
// ----- Normalizing typerefs over refined types ----------------------------
939942

940943
/** If this normalizes* to a refinement type that has a refinement for `name` (which might be followed
941944
* by other refinements), and the refined info is a type alias, return the alias,

src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -652,7 +652,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
652652

653653
/** Overridden in ReTyper to handle primitive operations that can be generated after erasure */
654654
protected def handleUnexpectedFunType(tree: untpd.Apply, fun: Tree)(implicit ctx: Context): Tree =
655-
throw new Error(s"unexpected type.\n fun = $fun,\n methPart(fun) = ${methPart(fun)},\n methPart(fun).tpe = ${methPart(fun).tpe},\n tpe = ${fun.tpe}")
655+
throw new Error(i"unexpected type.\n fun = $fun,\n methPart(fun) = ${methPart(fun)},\n methPart(fun).tpe = ${methPart(fun).tpe},\n tpe = ${fun.tpe}")
656656

657657
def typedNamedArgs(args: List[untpd.Tree])(implicit ctx: Context) =
658658
for (arg @ NamedArg(id, argtpt) <- args) yield {

src/dotty/tools/dotc/typer/ProtoTypes.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,8 @@ object ProtoTypes {
182182
if ((args eq this.args) && (resultType eq this.resultType) && (typer eq this.typer)) this
183183
else new FunProto(args, resultType, typer)
184184

185+
override def notApplied = WildcardType
186+
185187
/** Forget the types of any arguments that have been typed producing a constraint in a
186188
* typer state that is not yet committed into the one of the current context `ctx`.
187189
* This is necessary to avoid "orphan" PolyParams that are referred to from
@@ -319,6 +321,8 @@ object ProtoTypes {
319321
if ((targs eq this.targs) && (resType eq this.resType)) this
320322
else PolyProto(targs, resType)
321323

324+
override def notApplied = WildcardType
325+
322326
def map(tm: TypeMap)(implicit ctx: Context): PolyProto =
323327
derivedPolyProto(targs mapConserve tm, tm(resultType))
324328

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
572572
def typedBlock(tree: untpd.Block, pt: Type)(implicit ctx: Context) = track("typedBlock") {
573573
val exprCtx = index(tree.stats)
574574
val stats1 = typedStats(tree.stats, ctx.owner)
575-
val expr1 = typedExpr(tree.expr, pt)(exprCtx)
575+
val ept = if (tree.isInstanceOf[untpd.InfixOpBlock]) pt else pt.notApplied
576+
val expr1 = typedExpr(tree.expr, ept)(exprCtx)
576577
ensureNoLocalRefs(
577578
assignType(cpy.Block(tree)(stats1, expr1), stats1, expr1), pt, localSyms(stats1))
578579
}
@@ -619,8 +620,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
619620

620621
def typedIf(tree: untpd.If, pt: Type)(implicit ctx: Context) = track("typedIf") {
621622
val cond1 = typed(tree.cond, defn.BooleanType)
622-
val thenp1 = typed(tree.thenp, pt)
623-
val elsep1 = typed(tree.elsep orElse (untpd.unitLiteral withPos tree.pos), pt)
623+
val thenp1 = typed(tree.thenp, pt.notApplied)
624+
val elsep1 = typed(tree.elsep orElse (untpd.unitLiteral withPos tree.pos), pt.notApplied)
624625
val thenp2 :: elsep2 :: Nil = harmonize(thenp1 :: elsep1 :: Nil)
625626
assignType(cpy.If(tree)(cond1, thenp2, elsep2), thenp2, elsep2)
626627
}
@@ -793,7 +794,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
793794
val selType = widenForMatchSelector(
794795
fullyDefinedType(sel1.tpe, "pattern selector", tree.pos))
795796

796-
val cases1 = typedCases(tree.cases, selType, pt)
797+
val cases1 = typedCases(tree.cases, selType, pt.notApplied)
797798
val cases2 = harmonize(cases1).asInstanceOf[List[CaseDef]]
798799
assignType(cpy.Match(tree)(sel1, cases2), cases2)
799800
}
@@ -920,8 +921,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
920921
}
921922

922923
def typedTry(tree: untpd.Try, pt: Type)(implicit ctx: Context): Try = track("typedTry") {
923-
val expr1 = typed(tree.expr, pt)
924-
val cases1 = typedCases(tree.cases, defn.ThrowableType, pt)
924+
val expr1 = typed(tree.expr, pt.notApplied)
925+
val cases1 = typedCases(tree.cases, defn.ThrowableType, pt.notApplied)
925926
val finalizer1 = typed(tree.finalizer, defn.UnitType)
926927
val expr2 :: cases2x = harmonize(expr1 :: cases1)
927928
val cases2 = cases2x.asInstanceOf[List[CaseDef]]
@@ -1535,8 +1536,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
15351536
}
15361537

15371538
/** If this tree is a select node `qual.name`, try to insert an implicit conversion
1538-
* `c` around `qual` so that `c(qual).name` conforms to `pt`. If that fails
1539-
* return `tree` itself.
1539+
* `c` around `qual` so that `c(qual).name` conforms to `pt`.
15401540
*/
15411541
def tryInsertImplicitOnQualifier(tree: Tree, pt: Type)(implicit ctx: Context): Option[Tree] = ctx.traceIndented(i"try insert impl on qualifier $tree $pt") {
15421542
tree match {

tests/neg/i1503.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
object Test {
2+
3+
val cond = true
4+
def foo1() = println("hi")
5+
def bar1() = println("there")
6+
7+
def foo2(x: Int) = println("hi")
8+
def bar2(x: Int) = println("there")
9+
10+
def main(args: Array[String]) = {
11+
(if (cond) foo1 else bar1)() // error: Unit does not take parameters
12+
(if (cond) foo2 else bar2)(22) // error: missing arguments // error: missing arguments
13+
}
14+
}

tests/run/i1503.check

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
hello
2+
hi
3+
33
4+
hi
5+
hi

tests/run/i1503.scala

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
object Test {
2+
3+
def test1() =
4+
(new Function0[Unit] {
5+
def apply() = println("hello")
6+
})()
7+
8+
val cond = true
9+
val foo = () => println("hi")
10+
val bar = () => println("there")
11+
12+
val baz = (x: Int) => println(x)
13+
14+
def test2() =
15+
(if (cond) foo else bar)()
16+
17+
def test2a() =
18+
(if (cond) baz else baz)(33)
19+
20+
def test3() =
21+
(try foo
22+
catch { case ex: Exception => bar }
23+
finally ())()
24+
25+
def test4() =
26+
(cond match {
27+
case true => foo
28+
case false => bar
29+
})()
30+
31+
def main(args: Array[String]) = {
32+
test1()
33+
test2()
34+
test2a()
35+
test3()
36+
test4()
37+
}
38+
}

0 commit comments

Comments
 (0)