Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FBounded Polymorphism and shouldNot compile giving a false positive #2252

Open
dhinojosa opened this issue Jun 25, 2023 · 6 comments
Open

FBounded Polymorphism and shouldNot compile giving a false positive #2252

dhinojosa opened this issue Jun 25, 2023 · 6 comments

Comments

@dhinojosa
Copy link

Given the following production code:

package com.evolutionnext.fboundedpolymorphism

trait RecursiveSelfTypeEntity[E <: RecursiveSelfTypeEntity[E]]:
  self: E =>
  def create(): E

  def read(id: Long): Option[E]

  def update(f: E => E): E

  def delete(id: Long): Unit

Given the test:

package com.evolutionnext.fboundedpolymorphism

import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers

class FBoundedPolymorphismSpec extends AnyFunSuite with Matchers:

  test("""We need to then ensure that Orange extends Entity[Orange],
         |  we then use self types""".stripMargin) {

    class Apple extends RecursiveSelfTypeEntity[Apple]:
      override def create(): Apple = ???

      override def read(id: Long): Option[Apple] = ???

      override def update(f: Apple => Apple): Apple = ???

      override def delete(id: Long): Unit = ???

    class Orange extends RecursiveSelfTypeEntity[Orange]:
      override def create(): Orange = ???

      override def read(id: Long): Option[Orange] = ???

      override def update(f: Orange => Orange): Orange = ???

      override def delete(id: Long): Unit = ???

    info("This is great, now this will not work, as expected and should not compile, uncomment it and view the compile error")

   //    class Banana extends RecursiveSelfTypeEntity[Apple]:
   //        override def create(): Apple = ???
   //
   //        override def read(id: Long): Option[Apple] = ???
   //
   //        override def update(f: Apple => Apple): Apple = ???
   //
   //        override def delete(id: Long): Unit = ???
   
    info("The following test succeeds, but it should fail. False positive")
    """class Banana extends RecursiveSelfTypeEntity[Apple] {
      |  override def create(): Apple = ???
      |  override def read(id: Long): Option[Apple] = ???
      |  override def update(f: Apple => Apple): Apple = ???
      |  override def delete(id: Long): Unit = ???
      |}""".stripMargin shouldNot compile
  }

In the above, if you uncomment the class Banana block you will find that the code block will not compile which is correct. But, when the shouldNot compile code is run with the same block it errors with the following error message

Expected a compiler error, but got none for code: class Banana extends RecursiveSelfTypeEntity[Apple] {
  override def create(): Apple = ???
  override def read(id: Long): Option[Apple] = ???
  override def update(f: Apple => Apple): Apple = ???
  override def delete(id: Long): Unit = ???
}  
ScalaTestFailureLocation: com.evolutionnext.fboundedpolymorphism.FBoundedPolymorphismSpec at (FBoundedPolymorphismSpec.scala:112)
org.scalatest.exceptions.TestFailedException: Expected a compiler error, but got none for code: class Banana extends RecursiveSelfTypeEntity[Apple] {
  override def create(): Apple = ???
  override def read(id: Long): Option[Apple] = ???
  override def update(f: Apple => Apple): Apple = ???
  override def delete(id: Long): Unit = ???
}  
	at com.evolutionnext.fboundedpolymorphism.FBoundedPolymorphismSpec.testFun$proxy4$1(FBoundedPolymorphismSpec.scala:112)
	at com.evolutionnext.fboundedpolymorphism.FBoundedPolymorphismSpec.$init$$$anonfun$4(FBoundedPolymorphismSpec.scala:76)
	at org.scalatest.Transformer.apply$$anonfun$1(Transformer.scala:22)
	at org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85)
	at org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:31)
	at org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104)
	at org.scalatest.Transformer.apply(Transformer.scala:22)
	at org.scalatest.Transformer.apply(Transformer.scala:21)
	at org.scalatest.funsuite.AnyFunSuiteLike$$anon$1.apply(AnyFunSuiteLike.scala:206)
	at org.scalatest.TestSuite.withFixture(TestSuite.scala:196)
	at org.scalatest.TestSuite.withFixture$(TestSuite.scala:138)
	at org.scalatest.funsuite.AnyFunSuite.withFixture(AnyFunSuite.scala:1563)
	at org.scalatest.funsuite.AnyFunSuiteLike.invokeWithFixture$1(AnyFunSuiteLike.scala:212)
	at org.scalatest.funsuite.AnyFunSuiteLike.runTest$$anonfun$1(AnyFunSuiteLike.scala:216)
	at org.scalatest.SuperEngine.runTestImpl(Engine.scala:306)
	at org.scalatest.funsuite.AnyFunSuiteLike.runTest(AnyFunSuiteLike.scala:216)
	at org.scalatest.funsuite.AnyFunSuiteLike.runTest$(AnyFunSuiteLike.scala:47)
	at org.scalatest.funsuite.AnyFunSuite.runTest(AnyFunSuite.scala:1563)
	at org.scalatest.funsuite.AnyFunSuiteLike.runTests$$anonfun$1(AnyFunSuiteLike.scala:249)
	at org.scalatest.SuperEngine.traverseSubNodes$1$$anonfun$1(Engine.scala:413)
	at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
	at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
	at scala.collection.immutable.List.foreach(List.scala:333)
	at org.scalatest.SuperEngine.traverseSubNodes$1(Engine.scala:429)
	at org.scalatest.SuperEngine.runTestsInBranch(Engine.scala:396)
	at org.scalatest.SuperEngine.runTestsImpl(Engine.scala:475)
	at org.scalatest.funsuite.AnyFunSuiteLike.runTests(AnyFunSuiteLike.scala:249)
	at org.scalatest.funsuite.AnyFunSuiteLike.runTests$(AnyFunSuiteLike.scala:47)
	at org.scalatest.funsuite.AnyFunSuite.runTests(AnyFunSuite.scala:1563)
	at org.scalatest.Suite.run(Suite.scala:1114)
	at org.scalatest.Suite.run$(Suite.scala:564)
	at org.scalatest.funsuite.AnyFunSuite.org$scalatest$funsuite$AnyFunSuiteLike$$super$run(AnyFunSuite.scala:1563)
	at org.scalatest.funsuite.AnyFunSuiteLike.run$$anonfun$1(AnyFunSuiteLike.scala:253)
	at org.scalatest.SuperEngine.runImpl(Engine.scala:535)
	at org.scalatest.funsuite.AnyFunSuiteLike.run(AnyFunSuiteLike.scala:253)
	at org.scalatest.funsuite.AnyFunSuiteLike.run$(AnyFunSuiteLike.scala:47)
	at org.scalatest.funsuite.AnyFunSuite.run(AnyFunSuite.scala:1563)
	at org.scalatest.tools.SuiteRunner.run(SuiteRunner.scala:47)
	at org.scalatest.tools.Runner$.doRunRunRunDaDoRunRun$$anonfun$1(Runner.scala:1321)
	at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
	at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
	at scala.collection.immutable.List.foreach(List.scala:333)
	at org.scalatest.tools.Runner$.doRunRunRunDaDoRunRun(Runner.scala:1323)
	at org.scalatest.tools.Runner$.runOptionallyWithPassFailReporter$$anonfun$2(Runner.scala:992)
	at scala.runtime.function.JProcedure2.apply(JProcedure2.java:15)
	at scala.runtime.function.JProcedure2.apply(JProcedure2.java:10)
	at org.scalatest.tools.Runner$.withClassLoaderAndDispatchReporter(Runner.scala:1481)
	at org.scalatest.tools.Runner$.runOptionallyWithPassFailReporter(Runner.scala:993)
	at org.scalatest.tools.Runner$.run(Runner.scala:798)
	at org.scalatest.tools.Runner.run(Runner.scala)
	at org.jetbrains.plugins.scala.testingSupport.scalaTest.ScalaTestRunner.runScalaTest2or3(ScalaTestRunner.java:43)
	at org.jetbrains.plugins.scala.testingSupport.scalaTest.ScalaTestRunner.main(ScalaTestRunner.java:26)
@cheeseng
Copy link
Contributor

@dhinojosa Hmm, fyi ScalaTest macro for Scala 3 just use scala.compiletime.testing.typeChecks to check that the given code does not compile, I wonder if typeChecks does not cover the compiler phase that perform the check for the given expected error above. @liufengyun Do you have any idea if that is the case?

@liufengyun
Copy link
Contributor

I wonder if typeChecks does not cover the compiler phase that perform the check for the given expected error above

Sorry for the late response. I checked the compiler code implementation of typeChecks, it does run PostTyper phase, which checks the type bounds.

I'd suggest create a minimized test without ScalaTest by using scala.compiletime.testing.typeChecks directly. If the error persists, then report a bug to the Scala 3 compiler.

@cheeseng
Copy link
Contributor

cheeseng commented Jul 3, 2023

@dhinojosa @liufengyun This is an example without scalatest:

package com.example

object Test: 
  def main(args: Array[String]): Unit =
    val result = 
      scala.compiletime.testing.typeChecks(
        "trait RecursiveSelfTypeEntity[E <: RecursiveSelfTypeEntity[E]]: \n" +
        "  self: E => \n" +
        "  def create(): E \n" +
        "  def read(id: Long): Option[E] \n" +
        "  def update(f: E => E): E \n" +
        "  def delete(id: Long): Unit \n" +
        "\n" +
        "class Apple extends RecursiveSelfTypeEntity[Apple]: \n" +
        "  override def create(): Apple = ??? \n" +
        "  override def read(id: Long): Option[Apple] = ??? \n" +
        "  override def update(f: Apple => Apple): Apple = ??? \n" +
        "  override def delete(id: Long): Unit = ??? \n" +
        " \n" +
        "class Orange extends RecursiveSelfTypeEntity[Orange]: \n" +
        "  override def create(): Orange = ??? \n" +
        "  override def read(id: Long): Option[Orange] = ??? \n" +
        "  override def update(f: Orange => Orange): Orange = ??? \n" +
        "  override def delete(id: Long): Unit = ??? \n" + 
        " \n" +
        "class Banana extends RecursiveSelfTypeEntity[Apple]: \n" +
        "  override def create(): Apple = ??? \n" +
        "  override def read(id: Long): Option[Apple] = ??? \n" +
        "  override def update(f: Apple => Apple): Apple = ??? \n" +
        "  override def delete(id: Long): Unit = ???\n"
      )
    assert(false, "Should fail type check, but it didn't.")

@dhinojosa Would you like to report it to dotty issues?

@dhinojosa
Copy link
Author

@cheeseng That last line will always fail since it is false, assert(false, "Should fail type check, but it didn't."). When I replaced it with assert(result, "Should fail type check, but it didn't.") it works fine. Let me know if you want to do a follow up test.

@dhinojosa
Copy link
Author

Oh, wait, even if I place the result, this should actually fail. Sorry, brain fail on my part.

@dhinojosa
Copy link
Author

Filed: scala/scala3#18150

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants