Skip to content

Commit ded535d

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 1f37b0c commit ded535d

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
@@ -1538,15 +1538,31 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
15381538
val pt1 = if (defn.isFunctionType(pt)) pt else AnyFunctionProto
15391539
var res = typed(qual, pt1)
15401540
if (pt1.eq(AnyFunctionProto) && !defn.isFunctionClass(res.tpe.classSymbol)) {
1541-
def msg = i"not a function: ${res.tpe}; cannot be followed by `_'"
1541+
ctx.errorOrMigrationWarning(i"not a function: ${res.tpe}; cannot be followed by `_'", tree.pos)
15421542
if (ctx.scala2Mode) {
15431543
// Under -rewrite, patch `x _` to `(() => x)`
1544-
ctx.migrationWarning(msg, tree.pos)
15451544
patch(Position(tree.pos.start), "(() => ")
15461545
patch(Position(qual.pos.end, tree.pos.end), ")")
15471546
res = typed(untpd.Function(Nil, untpd.TypedSplice(res)))
15481547
}
1549-
else ctx.error(msg, tree.pos)
1548+
}
1549+
else if (ctx.settings.strict.value) {
1550+
lazy val (prefix, suffix) = res match {
1551+
case Block(mdef @ DefDef(_, _, vparams :: Nil, _, _) :: Nil, _: Closure) =>
1552+
val arity = vparams.length
1553+
if (arity > 0) ("", "") else ("(() => ", "())")
1554+
case _ =>
1555+
("(() => ", ")")
1556+
}
1557+
def remedy =
1558+
if ((prefix ++ suffix).isEmpty) "simply leave out the trailing ` _`"
1559+
else s"use `$prefix<function>$suffix` instead"
1560+
ctx.errorOrMigrationWarning(i"""The syntax `<function> _` is no longer supported;
1561+
|you can $remedy""", tree.pos)
1562+
if (ctx.scala2Mode) {
1563+
patch(Position(tree.pos.start), prefix)
1564+
patch(Position(qual.pos.end, tree.pos.end), suffix)
1565+
}
15501566
}
15511567
res
15521568
}
@@ -2053,7 +2069,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
20532069

20542070
// Reasons NOT to eta expand:
20552071
// - we reference a constructor
2056-
// - we are in a patterm
2072+
// - we are in a pattern
20572073
// - the current tree is a synthetic apply which is not expandable (eta-expasion would simply undo that)
20582074
if (arity >= 0 &&
20592075
!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)