Skip to content

Commit 75408c9

Browse files
committed
Reject trailing _ syntax under -strict mode.
The reason to do this only under -strict mode for now is so that we can still compile the same code under both Scala 2 and Dotty. If we rejected trailing `_` outright, Scala 2 code would need to get either explicitly eta-expanded or get an expected type to still work. But in Dotty, we can simply drop `_` for functions with arity >= 1. So to keep code dual compilable, we'd need to add boilerplate which under Dotty would be no longer needed.
1 parent 5714438 commit 75408c9

File tree

3 files changed

+32
-4
lines changed

3 files changed

+32
-4
lines changed

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

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,15 +1542,31 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
15421542
val pt1 = if (defn.isFunctionType(pt)) pt else AnyFunctionProto
15431543
var res = typed(qual, pt1)
15441544
if (pt1.eq(AnyFunctionProto) && !defn.isFunctionClass(res.tpe.classSymbol)) {
1545-
def msg = i"not a function: ${res.tpe}; cannot be followed by `_'"
1545+
ctx.errorOrMigrationWarning(i"not a function: ${res.tpe}; cannot be followed by `_'", tree.pos)
15461546
if (ctx.scala2Mode) {
15471547
// Under -rewrite, patch `x _` to `(() => x)`
1548-
ctx.migrationWarning(msg, tree.pos)
15491548
patch(Position(tree.pos.start), "(() => ")
15501549
patch(Position(qual.pos.end, tree.pos.end), ")")
15511550
res = typed(untpd.Function(Nil, untpd.TypedSplice(res)))
15521551
}
1553-
else ctx.error(msg, tree.pos)
1552+
}
1553+
else if (ctx.settings.strict.value) {
1554+
lazy val (prefix, suffix) = res match {
1555+
case Block(mdef @ DefDef(_, _, vparams :: Nil, _, _) :: Nil, _: Closure) =>
1556+
val arity = vparams.length
1557+
if (arity > 0) ("", "") else ("(() => ", "())")
1558+
case _ =>
1559+
("(() => ", ")")
1560+
}
1561+
def remedy =
1562+
if ((prefix ++ suffix).isEmpty) "simply leave out the trailing ` _`"
1563+
else s"use `$prefix<function>$suffix` instead"
1564+
ctx.errorOrMigrationWarning(i"""The syntax `<function> _` is no longer supported;
1565+
|you can $remedy""", tree.pos)
1566+
if (ctx.scala2Mode) {
1567+
patch(Position(tree.pos.start), prefix)
1568+
patch(Position(qual.pos.end, tree.pos.end), suffix)
1569+
}
15541570
}
15551571
res
15561572
}
@@ -2057,7 +2073,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
20572073

20582074
// Reasons NOT to eta expand:
20592075
// - we reference a constructor
2060-
// - we are in a patterm
2076+
// - we are in a pattern
20612077
// - the current tree is a synthetic apply which is not expandable (eta-expasion would simply undo that)
20622078
if (arity >= 0 &&
20632079
!tree.symbol.isConstructor &&

compiler/test/dotc/tests.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ class tests extends CompilerTest {
196196
@Test def neg_valueclasses_doubledefs = compileFile(negCustomArgs, "valueclasses-doubledefs")(allowDoubleBindings)
197197
@Test def neg_valueclasses_doubledefs2 = compileFile(negCustomArgs, "valueclasses-doubledefs2")(allowDoubleBindings)
198198
@Test def neg_valueclasses_pavlov = compileFile(negCustomArgs, "valueclasses-pavlov")(allowDoubleBindings)
199+
@Test def neg_trailingUnderscore = compileFile(negCustomArgs, "trailingUnderscore")(List("-strict"))
199200

200201
val negTailcallDir = negDir + "tailcall/"
201202
@Test def neg_tailcall_t1672b = compileFile(negTailcallDir, "t1672b")
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// to be compiled with -strict
2+
object trailingUnderscore {
3+
4+
def f(x: Int) = x
5+
def g() = f(2)
6+
def h = g()
7+
8+
val x1 = f _ // error
9+
val x2 = g _ // error
10+
val x3 = h _ // error
11+
}

0 commit comments

Comments
 (0)