Scala Programming Language
  1. Scala Programming Language
  2. SI-5606

report error when using wildcard for top-level type parameter name

    Details

    • Type: Bug Bug
    • Status: Open
    • Priority: Major Major
    • Resolution: Unresolved
    • Affects Version/s: Scala 2.9.1
    • Fix Version/s: Backlog
    • Component/s: Type Checker
    • Labels:

      Description

      I am able to crash the compiler with the following code:

      object TestModel {
      }
      
      case class CaseTest[_](someData:String)
      

      The error is the following:

       java.lang.AssertionError: assertion failed: transformCaseApply: name = _! tree = _ / class scala.reflect.generic.Trees$Ident
      at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer$$anonfun$2.apply$mcV$sp(RefChecks.scala:1493)
      at scala.tools.nsc.typechecker.RefChecks$RefCheckTransformer.transformCaseApply(RefChecks.scala:1309)
      etc..
      

      (I really hope I was supposed to issue this as a bug, first time ever I do this kind of thing. If I shouldnt, I am very sorry for the inconvenience)

        Activity

        Hide
        Adriaan Moors added a comment -

        thanks for reporting! yes, that should give a clean error instead of crashing

        Show
        Adriaan Moors added a comment - thanks for reporting! yes, that should give a clean error instead of crashing
        Hide
        Adriaan Moors added a comment -

        this should give a parse error

        Show
        Adriaan Moors added a comment - this should give a parse error
        Hide
        Paul Phillips added a comment -

        Interesting what happens when one tries to disallow it. (It's clearly a bug it was allowed - try using a second "wildcard" and it fails.)

        [scalacfork] /scratch/trunk1/src/library/scala/collection/immutable/RedBlackTree.scala:88: error: identifier expected but '_' found.
        [scalacfork]   def keysIterator[A, _](tree: Tree[A, _]): Iterator[A] = new KeysIterator(tree)
        [scalacfork]                       ^
        [scalacfork] /scratch/trunk1/src/library/scala/collection/immutable/RedBlackTree.scala:89: error: ']' expected but ';' found.
        [scalacfork]   def valuesIterator[_, B](tree: Tree[_, B]): Iterator[B] = new ValuesIterator(tree)
        [scalacfork] ^
        [scalacfork] /scratch/trunk1/src/library/scala/collection/immutable/RedBlackTree.scala:89: error: identifier expected but '_' found.
        [scalacfork]   def valuesIterator[_, B](tree: Tree[_, B]): Iterator[B] = new ValuesIterator(tree)
        [scalacfork]                      ^
        
        Show
        Paul Phillips added a comment - Interesting what happens when one tries to disallow it. (It's clearly a bug it was allowed - try using a second "wildcard" and it fails.) [scalacfork] /scratch/trunk1/src/library/scala/collection/immutable/RedBlackTree.scala:88: error: identifier expected but '_' found. [scalacfork] def keysIterator[A, _](tree: Tree[A, _]): Iterator[A] = new KeysIterator(tree) [scalacfork] ^ [scalacfork] /scratch/trunk1/src/library/scala/collection/immutable/RedBlackTree.scala:89: error: ']' expected but ';' found. [scalacfork] def valuesIterator[_, B](tree: Tree[_, B]): Iterator[B] = new ValuesIterator(tree) [scalacfork] ^ [scalacfork] /scratch/trunk1/src/library/scala/collection/immutable/RedBlackTree.scala:89: error: identifier expected but '_' found. [scalacfork] def valuesIterator[_, B](tree: Tree[_, B]): Iterator[B] = new ValuesIterator(tree) [scalacfork] ^
        Hide
        A. P. Marki added a comment -

        This is degenerate, but ought it to be illegal?

        In the real world, there are many behaviors that are degenerate but not illegal, or no longer illegal, except in certain states of the Union where they frown on that sort of thing. (For instance, buying beer on Sunday.)

        I took a quick look, and the example CaseTest[_] fails in the "copy" method but not in the companion module factory method, which are not exactly the same for some reason. The error is the wildcard in the new expr. (-Xprint:typer shows new pkg.this.CaseTest[_](data) versus new pkg.CaseTest[Any](data).) So this comes close to passing under the radar.

        The spec says that the motivation for the shorthand A[_] is visibility and relevance.

        One use case for top-level _, in quick test code, is to stub some UserKlass[This, That, Other] as class UserKlass[_,_,_].

        I have to come up with symbols? Couldn't the compiler just make me a symbol? So I don't have to remember if we prefer A,B,C or T,U,V? This goes to relevance. And it depletes my decision-making budget.

        Another use case is where a type param is deprecated because improvements in type inference make it superfluous.
        trait T[@deprecated("I'm free","2.11") _, B <: S[_]]
        Then, hypothetically, one could warn on usage of T[X, Y] but not T[_, Y].

        Just thinking aloud.

        Show
        A. P. Marki added a comment - This is degenerate, but ought it to be illegal? In the real world, there are many behaviors that are degenerate but not illegal, or no longer illegal, except in certain states of the Union where they frown on that sort of thing. (For instance, buying beer on Sunday.) I took a quick look, and the example CaseTest [_] fails in the "copy" method but not in the companion module factory method, which are not exactly the same for some reason. The error is the wildcard in the new expr. (-Xprint:typer shows new pkg.this.CaseTest [_] (data) versus new pkg.CaseTest [Any] (data).) So this comes close to passing under the radar. The spec says that the motivation for the shorthand A [_] is visibility and relevance. One use case for top-level _, in quick test code, is to stub some UserKlass [This, That, Other] as class UserKlass [_,_,_] . I have to come up with symbols? Couldn't the compiler just make me a symbol? So I don't have to remember if we prefer A,B,C or T,U,V? This goes to relevance. And it depletes my decision-making budget. Another use case is where a type param is deprecated because improvements in type inference make it superfluous. trait T[@deprecated("I'm free","2.11") _, B <: S [_] ] Then, hypothetically, one could warn on usage of T [X, Y] but not T [_, Y] . Just thinking aloud.
        Hide
        Paul Phillips added a comment -

        It could be allowed to work; my point is that it would be better for it not to work at all than for it to accidentally work for the one-type-parameter case only.

        Show
        Paul Phillips added a comment - It could be allowed to work; my point is that it would be better for it not to work at all than for it to accidentally work for the one-type-parameter case only.
        Hide
        Jason Zaugg added a comment -

        The failure mode changed in 2.10.0. In https://github.com/scala/scala/pull/826, the return type of the synthetic copy method was inferred, rather than explicitly provided. If the type parameter is named _, it is inferred as CaseClass[Any].

        ~/code/scala2 scala210
        Welcome to Scala version 2.10.0-20121024-085118-2c554249fd (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_27).
        Type in expressions to have them evaluated.
        Type :help for more information.
        
        scala> case class CaseTest[_](someData:String)
        defined class CaseTest
        
        scala> new CaseTest[Int]("").copy()
        res0: CaseTest[Any] = CaseTest()
        
        scala> case class CaseTest[A](someData:String)
        defined class CaseTest
        
        scala> new CaseTest[Int]("").copy()
        res1: CaseTest[Nothing] = CaseTest()
        
        scala> case class CaseTest[_](someData:String)
        defined class CaseTest
        
        scala> new CaseTest[Int]("").copy[Int]()
        res2: CaseTest[Any] = CaseTest()
        
        scala> case class CaseTest[A](someData:String)
        defined class CaseTest
        
        scala> new CaseTest[Int]("").copy[Int]()
        res3: CaseTest[Int] = CaseTest()
        
        scala> case class CaseTest[_](someData:String)
        defined class CaseTest
        
        scala> val x = CaseTest[Int]("")
        x: CaseTest[Any] = CaseTest()
        
        scala> x.copy
                                                       def copy[_](someData: String): CaseTest[Any]  
        

        I figured I'd be able to trip up Value Classes with this, but while we can generate some funny looking signatures, they seem to go unnoticed.

        scala> class CCC[_](val value: String) extends AnyVal { def foo[_]: Option[`_`] = None }
        ...
                  final def foo$extension[_ >: Nothing <: Any, _ >: Nothing <: Any]($this: CCC[_]): Option[Any] = scala.None;
        

        That sort of signature comes up without the _, of course, but seems to be innocuous as everything is symful by that stage.

        scala> class C[A](val value: String) extends AnyVal { def foo[A] = this }
        ...
                  final def foo$extension[A >: Nothing <: Any, A >: Nothing <: Any]($this: C[A]): C[A] = $this;
        
        Show
        Jason Zaugg added a comment - The failure mode changed in 2.10.0. In https://github.com/scala/scala/pull/826 , the return type of the synthetic copy method was inferred, rather than explicitly provided. If the type parameter is named _ , it is inferred as CaseClass [Any] . ~/code/scala2 scala210 Welcome to Scala version 2.10.0-20121024-085118-2c554249fd (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_27). Type in expressions to have them evaluated. Type :help for more information. scala> case class CaseTest[_](someData:String) defined class CaseTest scala> new CaseTest[Int]("").copy() res0: CaseTest[Any] = CaseTest() scala> case class CaseTest[A](someData:String) defined class CaseTest scala> new CaseTest[Int]("").copy() res1: CaseTest[Nothing] = CaseTest() scala> case class CaseTest[_](someData:String) defined class CaseTest scala> new CaseTest[Int]("").copy[Int]() res2: CaseTest[Any] = CaseTest() scala> case class CaseTest[A](someData:String) defined class CaseTest scala> new CaseTest[Int]("").copy[Int]() res3: CaseTest[Int] = CaseTest() scala> case class CaseTest[_](someData:String) defined class CaseTest scala> val x = CaseTest[Int]("") x: CaseTest[Any] = CaseTest() scala> x.copy def copy[_](someData: String): CaseTest[Any] I figured I'd be able to trip up Value Classes with this, but while we can generate some funny looking signatures, they seem to go unnoticed. scala> class CCC[_](val value: String) extends AnyVal { def foo[_]: Option[`_`] = None } ... final def foo$extension[_ >: Nothing <: Any, _ >: Nothing <: Any]($this: CCC[_]): Option[Any] = scala.None; That sort of signature comes up without the _, of course, but seems to be innocuous as everything is symful by that stage. scala> class C[A](val value: String) extends AnyVal { def foo[A] = this } ... final def foo$extension[A >: Nothing <: Any, A >: Nothing <: Any]($this: C[A]): C[A] = $this;
        Hide
        James Iry added a comment -

        2.10.2 is about to be cut. Kicking down the road and un-assigning to foster work stealing.

        Show
        James Iry added a comment - 2.10.2 is about to be cut. Kicking down the road and un-assigning to foster work stealing.
        Hide
        James Iry added a comment -

        2.10.2 is about to be cut. Kicking down the road and un-assigning to foster work stealing.

        Show
        James Iry added a comment - 2.10.2 is about to be cut. Kicking down the road and un-assigning to foster work stealing.

          People

          • Assignee:
            Unassigned
            Reporter:
            Jonas Ohlsson
          • Votes:
            0 Vote for this issue
            Watchers:
            8 Start watching this issue

            Dates

            • Created:
              Updated:

              Development