Skip to content

Commit

Permalink
Disallow extending PolyFunction in user code (#18920)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
smarter authored Nov 23, 2023
2 parents 052bce4 + 63b31ac commit 00e9e6b
Show file tree
Hide file tree
Showing 20 changed files with 99 additions and 17 deletions.
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/transform/PostTyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase =>
val callTrace = ref(call.symbol)(using ctx.withSource(pos.source)).withSpan(pos.span)
cpy.Inlined(tree)(callTrace, transformSub(bindings), transform(expansion)(using inlineContext(tree)))
case templ: Template =>
Checking.checkPolyFunctionExtension(templ)
withNoCheckNews(templ.parents.flatMap(newPart)) {
forwardParamAccessors(templ)
synthMbr.addSyntheticMembers(
Expand Down
8 changes: 8 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -845,6 +845,14 @@ object Checking {
def reportNoRefinements(pos: SrcPos) =
report.error("PolyFunction subtypes must refine the apply method", pos)
}.traverse(tree)

/** Check that users do not extend the `PolyFunction` trait.
* We only allow compiler generated `PolyFunction`s.
*/
def checkPolyFunctionExtension(templ: Template)(using Context): Unit =
templ.parents.find(_.tpe.derivesFrom(defn.PolyFunctionClass)) match
case Some(parent) => report.error(s"`PolyFunction` marker trait is reserved for compiler generated refinements", parent.srcPos)
case None =>
}

trait Checking {
Expand Down
2 changes: 1 addition & 1 deletion tests/run/erased-15.scala → tests/neg/erased-15.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ object Test {
}
}

class Foo extends PolyFunction {
class Foo extends PolyFunction { // error
def apply(erased x: Int): Int = {
println("Foo.apply")
42
Expand Down
32 changes: 32 additions & 0 deletions tests/neg/i10075.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
-- Error: tests/neg/i10075.scala:8:24 ----------------------------------------------------------------------------------
8 |trait PolyTrait extends PolyFunction // error
| ^^^^^^^^^^^^
| `PolyFunction` marker trait is reserved for compiler generated refinements
-- Error: tests/neg/i10075.scala:10:24 ---------------------------------------------------------------------------------
10 |class PolyClass extends PolyTrait { // error
| ^^^^^^^^^
| `PolyFunction` marker trait is reserved for compiler generated refinements
-- Error: tests/neg/i10075.scala:14:26 ---------------------------------------------------------------------------------
14 |object PolyObject extends PolyFunction // error
| ^^^^^^^^^^^^
| `PolyFunction` marker trait is reserved for compiler generated refinements
-- Error: tests/neg/i10075.scala:2:14 ----------------------------------------------------------------------------------
2 |val foo = new PolyFunction { } // error
| ^^^^^^^^^^^^
| `PolyFunction` marker trait is reserved for compiler generated refinements
-- Error: tests/neg/i10075.scala:3:14 ----------------------------------------------------------------------------------
3 |val bar = new PolyFunction { def bar = 23 } // error
| ^^^^^^^^^^^^
| `PolyFunction` marker trait is reserved for compiler generated refinements
-- Error: tests/neg/i10075.scala:4:14 ----------------------------------------------------------------------------------
4 |val baz = new PolyFunction { def apply = 23 } // error
| ^^^^^^^^^^^^
| `PolyFunction` marker trait is reserved for compiler generated refinements
-- Error: tests/neg/i10075.scala:5:14 ----------------------------------------------------------------------------------
5 |val qux = new PolyFunction { def apply[T] = 47 } // error
| ^^^^^^^^^^^^
| `PolyFunction` marker trait is reserved for compiler generated refinements
-- Error: tests/neg/i10075.scala:6:15 ----------------------------------------------------------------------------------
6 |val quxx = new PolyFunction { def apply[T](x: T): T = x } // error
| ^^^^^^^^^^^^
| `PolyFunction` marker trait is reserved for compiler generated refinements
14 changes: 14 additions & 0 deletions tests/neg/i10075.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
val poly = [T] => (x: T) => x
val foo = new PolyFunction { } // error
val bar = new PolyFunction { def bar = 23 } // error
val baz = new PolyFunction { def apply = 23 } // error
val qux = new PolyFunction { def apply[T] = 47 } // error
val quxx = new PolyFunction { def apply[T](x: T): T = x } // error

trait PolyTrait extends PolyFunction // error

class PolyClass extends PolyTrait { // error
def apply[T](x: T): T = x
}

object PolyObject extends PolyFunction // error
10 changes: 10 additions & 0 deletions tests/neg/i10369.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
type Upgrade[T] = T match
case Int => Double
case Char => String
case Boolean => Boolean

val upgrade: [t] => t => Upgrade[t] = new PolyFunction: // error
def apply[T](x: T): Upgrade[T] = x match
case x: Int => x.toDouble
case x: Char => x.toString
case x: Boolean => !x
2 changes: 1 addition & 1 deletion tests/pos/i18302a.scala → tests/neg/i18302a.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
def test = polyFun(1)

def polyFun: PolyFunction { def apply(x: Int): Int } =
new PolyFunction { def apply(x: Int): Int = x + 1 }
new PolyFunction { def apply(x: Int): Int = x + 1 } // error
4 changes: 4 additions & 0 deletions tests/neg/i18302b.check
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
3 |def polyFun: PolyFunction { def apply(x: Int)(y: Int): Int } = // error
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|Implementation restriction: PolyFunction apply must have exactly one parameter list and optionally type arguments. No by-name nor varags are allowed.
-- Error: tests/neg/i18302b.scala:4:6 ----------------------------------------------------------------------------------
4 | new PolyFunction: // error
| ^^^^^^^^^^^^
| `PolyFunction` marker trait is reserved for compiler generated refinements
2 changes: 1 addition & 1 deletion tests/neg/i18302b.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
def test = polyFun(1)(2)

def polyFun: PolyFunction { def apply(x: Int)(y: Int): Int } = // error
new PolyFunction:
new PolyFunction: // error
def apply(x: Int)(y: Int): Int = x + y
4 changes: 4 additions & 0 deletions tests/neg/i18302c.check
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
4 |def polyFun: PolyFunction { def foo(x: Int): Int } = // error
| ^^^^^^^^^^^^^^^^^^^^
| PolyFunction only supports apply method refinements
-- Error: tests/neg/i18302c.scala:5:6 ----------------------------------------------------------------------------------
5 | new PolyFunction { def foo(x: Int): Int = x + 1 } // error
| ^^^^^^^^^^^^
| `PolyFunction` marker trait is reserved for compiler generated refinements
2 changes: 1 addition & 1 deletion tests/neg/i18302c.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ import scala.reflect.Selectable.reflectiveSelectable

def test = polyFun.foo(1)
def polyFun: PolyFunction { def foo(x: Int): Int } = // error
new PolyFunction { def foo(x: Int): Int = x + 1 }
new PolyFunction { def foo(x: Int): Int = x + 1 } // error
4 changes: 4 additions & 0 deletions tests/neg/i18302d.check
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,7 @@
1 |def polyFun: PolyFunction { def apply: Int } = // error
| ^^^^^^^^^^^^^^
|Implementation restriction: PolyFunction apply must have exactly one parameter list and optionally type arguments. No by-name nor varags are allowed.
-- Error: tests/neg/i18302d.scala:2:6 ----------------------------------------------------------------------------------
2 | new PolyFunction { def apply: Int = 1 } // error
| ^^^^^^^^^^^^
| `PolyFunction` marker trait is reserved for compiler generated refinements
2 changes: 1 addition & 1 deletion tests/neg/i18302d.scala
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
def polyFun: PolyFunction { def apply: Int } = // error
new PolyFunction { def apply: Int = 1 }
new PolyFunction { def apply: Int = 1 } // error
4 changes: 4 additions & 0 deletions tests/neg/i18302e.check
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
1 |def polyFun: PolyFunction { } = // error
| ^^^^^^^^^^^^^^^^^
| PolyFunction subtypes must refine the apply method
-- Error: tests/neg/i18302e.scala:2:6 ----------------------------------------------------------------------------------
2 | new PolyFunction { } // error
| ^^^^^^^^^^^^
| `PolyFunction` marker trait is reserved for compiler generated refinements
-- Error: tests/neg/i18302e.scala:4:15 ---------------------------------------------------------------------------------
4 |def polyFun(f: PolyFunction { }) = () // error
| ^^^^^^^^^^^^^^^^^
Expand Down
2 changes: 1 addition & 1 deletion tests/neg/i18302e.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
def polyFun: PolyFunction { } = // error
new PolyFunction { }
new PolyFunction { } // error

def polyFun(f: PolyFunction { }) = () // error
8 changes: 8 additions & 0 deletions tests/neg/i18302f.check
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
1 |def polyFun: PolyFunction = // error
| ^^^^^^^^^^^^
| PolyFunction subtypes must refine the apply method
-- Error: tests/neg/i18302f.scala:2:6 ----------------------------------------------------------------------------------
2 | new PolyFunction { } // error
| ^^^^^^^^^^^^
| `PolyFunction` marker trait is reserved for compiler generated refinements
-- Error: tests/neg/i18302f.scala:4:16 ---------------------------------------------------------------------------------
4 |def polyFun2(a: PolyFunction) = () // error
| ^^^^^^^^^^^^
Expand All @@ -10,3 +14,7 @@
6 |val polyFun3: PolyFunction = // error
| ^^^^^^^^^^^^
| PolyFunction subtypes must refine the apply method
-- Error: tests/neg/i18302f.scala:7:6 ----------------------------------------------------------------------------------
7 | new PolyFunction { } // error
| ^^^^^^^^^^^^
| `PolyFunction` marker trait is reserved for compiler generated refinements
4 changes: 2 additions & 2 deletions tests/neg/i18302f.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
def polyFun: PolyFunction = // error
new PolyFunction { }
new PolyFunction { } // error

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

val polyFun3: PolyFunction = // error
new PolyFunction { }
new PolyFunction { } // error
4 changes: 2 additions & 2 deletions tests/neg/i18302j.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
def polyFunByName: PolyFunction { def apply(thunk: => Int): Int } = // error
new PolyFunction { def apply(thunk: => Int): Int = 1 }
new PolyFunction { def apply(thunk: => Int): Int = 1 } // error

def polyFunVarArgs: PolyFunction { def apply(args: Int*): Int } = // error
new PolyFunction { def apply(thunk: Int*): Int = 1 }
new PolyFunction { def apply(thunk: Int*): Int = 1 } // error
6 changes: 0 additions & 6 deletions tests/pos/i10369.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@ type Upgrade[T] = T match
case Char => String
case Boolean => Boolean

val upgrade: [t] => t => Upgrade[t] = new PolyFunction:
def apply[T](x: T): Upgrade[T] = x match
case x: Int => x.toDouble
case x: Char => x.toString
case x: Boolean => !x

val upgrade2: [t] => t => Upgrade[t] = [t] => (x: t) => x match
case x: Int => x.toDouble
case x: Char => x.toString
Expand Down
1 change: 0 additions & 1 deletion tests/run/erased-15.check

This file was deleted.

0 comments on commit 00e9e6b

Please sign in to comment.