Skip to content

Commit

Permalink
Disallow use of PolyFunction trait in class parents
Browse files Browse the repository at this point in the history
Users should only write lambdas.

Fixes #10075
  • Loading branch information
nicolasstucki committed Nov 20, 2023
1 parent 5bb6f0d commit 63b31ac
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 63b31ac

Please sign in to comment.