scala / bug

Scala 2 bug reports only. Please, no questions — proper bug reports only.
https://scala-lang.org
232 stars 21 forks source link

Scalacheck Prop.forAll with > 8 arbitraries eta-expands under -Xsource:3 #13055

Open som-snytt opened 1 month ago

som-snytt commented 1 month ago

Questions are not bug reports

Miles had a question:

TIL that if you ask Prop.forAll for more than 8 arbitraries in a test the test will silently succeed no matter what. If you hand Prop.forAll a function with > 8 arguments, then instead of getting back a Prop you get back an ... => Prop which is typically silently discarded in a test. Is this widely known? It really ought not to compile.

and an answer:

This appears to be specific to Scala 2.13.x with -Xsource:3.0.0.

Reproduction steps

Scala version: 2.13.15

import org.scalacheck._, Prop._

object Main extends App {

  def what() = forAll {
    (a1: Int, a2: Int, a3: Int, a4: Int, a5: Int, a6: Int, a7: Int,
     a8: Int,
     a9: Int,
    ) => false
  }

}

Normally, scalac correctly says

[error] Unapplied methods are only converted to functions when a function type is expected.
[error] You can make this conversion explicit by writing `forAll _` or `forAll(_)(_)(_,_,_)` instead of `forAll`.

Problem

Incorrectly fails to fail compilation under -Xsource:3, but instead decides to eta-expand "unconditionally".

Probably that condition should fall under -Xsource-features. Probably it should find a way not to compile.

Dotty chooses the correct overload of forAll due to parameter untupling. Can we have that? It's almost Christmas (say the major retailers who are already discounting Halloween decorations).

via https://discord.com/channels/632277896739946517/841617753513263144/1296796824262283347

som-snytt commented 1 month ago
  class Prop
  class Gen[A]
  object Gen {
    implicit def const[T](x: T): Gen[T] = ???
  }

  def forAll[T1, P](g: Gen[T1])(f: T1 => P)(implicit p: P => Prop): Prop = ???
  def forAll[A1, P](f: A1 => P)(implicit p: P => Prop): Prop = ???

  def what() = forAll {
    (a1: Int, a2: Int, a3: Int, a4: Int, a5: Int, a6: Int, a7: Int,
     a8: Int,
     a9: Int,
    ) => false
  }

The successful typecheck infers P as Nothing:

forAll(Gen.const(f))(_)(conforms)

Dotty infers P as Any, so it rejects this alternative.

If -Xsource-features could support both tupling and flipped bound inference, then it could more safely support "unexpected" eta expansion (that is, without an expected type). Or perhaps improved parameter type inference would also be needed, and extended overload resolution for multiple param lists.