Skip to content

Commit 00e9e6b

Browse files
authored
Disallow extending PolyFunction in user code (#18920)
The `PolyFunction` trait should only be used for compiler generated encoded lambdas. Any other use case that was allowed before was accidental. In the future, we might consider supporting these if there is a good use case. This would probably require a SIP. Fixes #10075
2 parents 052bce4 + 63b31ac commit 00e9e6b

20 files changed

+99
-17
lines changed

Diff for: compiler/src/dotty/tools/dotc/transform/PostTyper.scala

+1
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
370370
val callTrace = ref(call.symbol)(using ctx.withSource(pos.source)).withSpan(pos.span)
371371
cpy.Inlined(tree)(callTrace, transformSub(bindings), transform(expansion)(using inlineContext(tree)))
372372
case templ: Template =>
373+
Checking.checkPolyFunctionExtension(templ)
373374
withNoCheckNews(templ.parents.flatMap(newPart)) {
374375
forwardParamAccessors(templ)
375376
synthMbr.addSyntheticMembers(

Diff for: compiler/src/dotty/tools/dotc/typer/Checking.scala

+8
Original file line numberDiff line numberDiff line change
@@ -845,6 +845,14 @@ object Checking {
845845
def reportNoRefinements(pos: SrcPos) =
846846
report.error("PolyFunction subtypes must refine the apply method", pos)
847847
}.traverse(tree)
848+
849+
/** Check that users do not extend the `PolyFunction` trait.
850+
* We only allow compiler generated `PolyFunction`s.
851+
*/
852+
def checkPolyFunctionExtension(templ: Template)(using Context): Unit =
853+
templ.parents.find(_.tpe.derivesFrom(defn.PolyFunctionClass)) match
854+
case Some(parent) => report.error(s"`PolyFunction` marker trait is reserved for compiler generated refinements", parent.srcPos)
855+
case None =>
848856
}
849857

850858
trait Checking {

Diff for: tests/run/erased-15.scala renamed to tests/neg/erased-15.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ object Test {
1212
}
1313
}
1414

15-
class Foo extends PolyFunction {
15+
class Foo extends PolyFunction { // error
1616
def apply(erased x: Int): Int = {
1717
println("Foo.apply")
1818
42

Diff for: tests/neg/i10075.check

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
-- Error: tests/neg/i10075.scala:8:24 ----------------------------------------------------------------------------------
2+
8 |trait PolyTrait extends PolyFunction // error
3+
| ^^^^^^^^^^^^
4+
| `PolyFunction` marker trait is reserved for compiler generated refinements
5+
-- Error: tests/neg/i10075.scala:10:24 ---------------------------------------------------------------------------------
6+
10 |class PolyClass extends PolyTrait { // error
7+
| ^^^^^^^^^
8+
| `PolyFunction` marker trait is reserved for compiler generated refinements
9+
-- Error: tests/neg/i10075.scala:14:26 ---------------------------------------------------------------------------------
10+
14 |object PolyObject extends PolyFunction // error
11+
| ^^^^^^^^^^^^
12+
| `PolyFunction` marker trait is reserved for compiler generated refinements
13+
-- Error: tests/neg/i10075.scala:2:14 ----------------------------------------------------------------------------------
14+
2 |val foo = new PolyFunction { } // error
15+
| ^^^^^^^^^^^^
16+
| `PolyFunction` marker trait is reserved for compiler generated refinements
17+
-- Error: tests/neg/i10075.scala:3:14 ----------------------------------------------------------------------------------
18+
3 |val bar = new PolyFunction { def bar = 23 } // error
19+
| ^^^^^^^^^^^^
20+
| `PolyFunction` marker trait is reserved for compiler generated refinements
21+
-- Error: tests/neg/i10075.scala:4:14 ----------------------------------------------------------------------------------
22+
4 |val baz = new PolyFunction { def apply = 23 } // error
23+
| ^^^^^^^^^^^^
24+
| `PolyFunction` marker trait is reserved for compiler generated refinements
25+
-- Error: tests/neg/i10075.scala:5:14 ----------------------------------------------------------------------------------
26+
5 |val qux = new PolyFunction { def apply[T] = 47 } // error
27+
| ^^^^^^^^^^^^
28+
| `PolyFunction` marker trait is reserved for compiler generated refinements
29+
-- Error: tests/neg/i10075.scala:6:15 ----------------------------------------------------------------------------------
30+
6 |val quxx = new PolyFunction { def apply[T](x: T): T = x } // error
31+
| ^^^^^^^^^^^^
32+
| `PolyFunction` marker trait is reserved for compiler generated refinements

Diff for: tests/neg/i10075.scala

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
val poly = [T] => (x: T) => x
2+
val foo = new PolyFunction { } // error
3+
val bar = new PolyFunction { def bar = 23 } // error
4+
val baz = new PolyFunction { def apply = 23 } // error
5+
val qux = new PolyFunction { def apply[T] = 47 } // error
6+
val quxx = new PolyFunction { def apply[T](x: T): T = x } // error
7+
8+
trait PolyTrait extends PolyFunction // error
9+
10+
class PolyClass extends PolyTrait { // error
11+
def apply[T](x: T): T = x
12+
}
13+
14+
object PolyObject extends PolyFunction // error

Diff for: tests/neg/i10369.scala

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
type Upgrade[T] = T match
2+
case Int => Double
3+
case Char => String
4+
case Boolean => Boolean
5+
6+
val upgrade: [t] => t => Upgrade[t] = new PolyFunction: // error
7+
def apply[T](x: T): Upgrade[T] = x match
8+
case x: Int => x.toDouble
9+
case x: Char => x.toString
10+
case x: Boolean => !x
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
def test = polyFun(1)
22

33
def polyFun: PolyFunction { def apply(x: Int): Int } =
4-
new PolyFunction { def apply(x: Int): Int = x + 1 }
4+
new PolyFunction { def apply(x: Int): Int = x + 1 } // error

Diff for: tests/neg/i18302b.check

+4
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@
22
3 |def polyFun: PolyFunction { def apply(x: Int)(y: Int): Int } = // error
33
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
44
|Implementation restriction: PolyFunction apply must have exactly one parameter list and optionally type arguments. No by-name nor varags are allowed.
5+
-- Error: tests/neg/i18302b.scala:4:6 ----------------------------------------------------------------------------------
6+
4 | new PolyFunction: // error
7+
| ^^^^^^^^^^^^
8+
| `PolyFunction` marker trait is reserved for compiler generated refinements

Diff for: tests/neg/i18302b.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
def test = polyFun(1)(2)
22

33
def polyFun: PolyFunction { def apply(x: Int)(y: Int): Int } = // error
4-
new PolyFunction:
4+
new PolyFunction: // error
55
def apply(x: Int)(y: Int): Int = x + y

Diff for: tests/neg/i18302c.check

+4
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@
22
4 |def polyFun: PolyFunction { def foo(x: Int): Int } = // error
33
| ^^^^^^^^^^^^^^^^^^^^
44
| PolyFunction only supports apply method refinements
5+
-- Error: tests/neg/i18302c.scala:5:6 ----------------------------------------------------------------------------------
6+
5 | new PolyFunction { def foo(x: Int): Int = x + 1 } // error
7+
| ^^^^^^^^^^^^
8+
| `PolyFunction` marker trait is reserved for compiler generated refinements

Diff for: tests/neg/i18302c.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ import scala.reflect.Selectable.reflectiveSelectable
22

33
def test = polyFun.foo(1)
44
def polyFun: PolyFunction { def foo(x: Int): Int } = // error
5-
new PolyFunction { def foo(x: Int): Int = x + 1 }
5+
new PolyFunction { def foo(x: Int): Int = x + 1 } // error

Diff for: tests/neg/i18302d.check

+4
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,7 @@
22
1 |def polyFun: PolyFunction { def apply: Int } = // error
33
| ^^^^^^^^^^^^^^
44
|Implementation restriction: PolyFunction apply must have exactly one parameter list and optionally type arguments. No by-name nor varags are allowed.
5+
-- Error: tests/neg/i18302d.scala:2:6 ----------------------------------------------------------------------------------
6+
2 | new PolyFunction { def apply: Int = 1 } // error
7+
| ^^^^^^^^^^^^
8+
| `PolyFunction` marker trait is reserved for compiler generated refinements

Diff for: tests/neg/i18302d.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
def polyFun: PolyFunction { def apply: Int } = // error
2-
new PolyFunction { def apply: Int = 1 }
2+
new PolyFunction { def apply: Int = 1 } // error

Diff for: tests/neg/i18302e.check

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
1 |def polyFun: PolyFunction { } = // error
33
| ^^^^^^^^^^^^^^^^^
44
| PolyFunction subtypes must refine the apply method
5+
-- Error: tests/neg/i18302e.scala:2:6 ----------------------------------------------------------------------------------
6+
2 | new PolyFunction { } // error
7+
| ^^^^^^^^^^^^
8+
| `PolyFunction` marker trait is reserved for compiler generated refinements
59
-- Error: tests/neg/i18302e.scala:4:15 ---------------------------------------------------------------------------------
610
4 |def polyFun(f: PolyFunction { }) = () // error
711
| ^^^^^^^^^^^^^^^^^

Diff for: tests/neg/i18302e.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
def polyFun: PolyFunction { } = // error
2-
new PolyFunction { }
2+
new PolyFunction { } // error
33

44
def polyFun(f: PolyFunction { }) = () // error

Diff for: tests/neg/i18302f.check

+8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
1 |def polyFun: PolyFunction = // error
33
| ^^^^^^^^^^^^
44
| PolyFunction subtypes must refine the apply method
5+
-- Error: tests/neg/i18302f.scala:2:6 ----------------------------------------------------------------------------------
6+
2 | new PolyFunction { } // error
7+
| ^^^^^^^^^^^^
8+
| `PolyFunction` marker trait is reserved for compiler generated refinements
59
-- Error: tests/neg/i18302f.scala:4:16 ---------------------------------------------------------------------------------
610
4 |def polyFun2(a: PolyFunction) = () // error
711
| ^^^^^^^^^^^^
@@ -10,3 +14,7 @@
1014
6 |val polyFun3: PolyFunction = // error
1115
| ^^^^^^^^^^^^
1216
| PolyFunction subtypes must refine the apply method
17+
-- Error: tests/neg/i18302f.scala:7:6 ----------------------------------------------------------------------------------
18+
7 | new PolyFunction { } // error
19+
| ^^^^^^^^^^^^
20+
| `PolyFunction` marker trait is reserved for compiler generated refinements

Diff for: tests/neg/i18302f.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
def polyFun: PolyFunction = // error
2-
new PolyFunction { }
2+
new PolyFunction { } // error
33

44
def polyFun2(a: PolyFunction) = () // error
55

66
val polyFun3: PolyFunction = // error
7-
new PolyFunction { }
7+
new PolyFunction { } // error

Diff for: tests/neg/i18302j.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
def polyFunByName: PolyFunction { def apply(thunk: => Int): Int } = // error
2-
new PolyFunction { def apply(thunk: => Int): Int = 1 }
2+
new PolyFunction { def apply(thunk: => Int): Int = 1 } // error
33

44
def polyFunVarArgs: PolyFunction { def apply(args: Int*): Int } = // error
5-
new PolyFunction { def apply(thunk: Int*): Int = 1 }
5+
new PolyFunction { def apply(thunk: Int*): Int = 1 } // error

Diff for: tests/pos/i10369.scala

-6
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,6 @@ type Upgrade[T] = T match
33
case Char => String
44
case Boolean => Boolean
55

6-
val upgrade: [t] => t => Upgrade[t] = new PolyFunction:
7-
def apply[T](x: T): Upgrade[T] = x match
8-
case x: Int => x.toDouble
9-
case x: Char => x.toString
10-
case x: Boolean => !x
11-
126
val upgrade2: [t] => t => Upgrade[t] = [t] => (x: t) => x match
137
case x: Int => x.toDouble
148
case x: Char => x.toString

Diff for: tests/run/erased-15.check

-1
This file was deleted.

0 commit comments

Comments
 (0)