-
Notifications
You must be signed in to change notification settings - Fork 21
Generic function creates anonymous class that uses function's type parameter, compiles but fails at runtime #2144
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
Comments
Imported From: https://issues.scala-lang.org/browse/SI-2144?orig=1
|
Willis Blackburn (willisblackburn) said: |
@paulp said: def approach1 {
class Wrapper[A](a: A) {
def frob(a2: A) = a
}
implicit def convert[A](a: A) = new Wrapper(a)
"foo".frob("bar")
} Exception in thread "main" java.lang.AssertionError: assertion failed
at scala.Predef$$.assert(Predef.scala:92)
at scala.tools.nsc.transform.LambdaLift$$$$anon$$1.apply(LambdaLift.scala:27)
at scala.tools.nsc.transform.LambdaLift$$$$anon$$1.apply(LambdaLift.scala:23)
at scala.tools.nsc.symtab.Types$$TypeMap.mapOver(Types.scala:2465)
at scala.tools.nsc.transform.LambdaLift$$$$anon$$1.apply(LambdaLift.scala:35)
... |
Willis Blackburn (willisblackburn) said: def convert[A](a: A) = new { def frob(a2: A) = ... }[[BR]]convert("foo").frob("bar") |
andrew cooke (andrew) said: |
@paulp said:
This ticket might actually be zeroing in on closeable. Approaches 1 and 2 above work as they should, and on closer examination the problem with 3 is not that it doesn't work but that it compiles at all. As nearly as I can see it is a fine example of the prohibition "Parameter type in structural refinement may not refer to abstract type defined outside that same refinement", but not being detected. See http://article.gmane.org/gmane.comp.lang.scala/7013 for an old explanation of why this is disallowed; old but I think still valid. It catches this case no problem: scala> type foo[A] = { def bar(x: A): A }
<console>:4: error: Parameter type in structural refinement may not refer to abstract type defined outside that same refinement
type foo[A] = { def bar(x: A): A }
^ But it will let you create this: scala> def foo[A] = new { def bar(x: A): A = x }
foo: [A]java.lang.Object{def bar(x: A): A} ...resulting in a bar method which cannot be called unless A is Object. |
@paulp said: |
andrew cooke (andrew) said: |
andrew cooke (andrew) said: package bug
//import org.jgrapht.graph.SimpleGraph
//import org.jgrapht.graph.DefaultWeightedEdge
object Main extends Application {
class MyGraph[V <: Any] {
def addVertex(v: V): Boolean = true
}
type DuckGraph = {
def addVertex(vertex: Int): Boolean
}
def crash(graph: DuckGraph) {
println("trying...")
graph.addVertex(1)
println("success")
}
crash(new MyGraph[Int])
// crash(new SimpleGraph[Int, DefaultWeightedEdge](classOf[DefaultWeightedEdge]))
} Cheers + thanks for what is (otehrwise) a pretty nice practical language that I hope one day to use at work... |
andrew cooke (andrew) said: trying...
java.lang.ExceptionInInitializerError
at bug.Main.main(Main.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at scala.util.ScalaClassLoader$$$$anonfun$$run$$1.apply(ScalaClassLoader.scala:54)
at scala.util.ScalaClassLoader$$class.asContext(ScalaClassLoader.scala:21)
at scala.util.URLClassLoader.asContext(ScalaClassLoader.scala:58)
at scala.util.ScalaClassLoader$$class.run(ScalaClassLoader.scala:54)
at scala.util.URLClassLoader.run(ScalaClassLoader.scala:58)
at scala.tools.nsc.ObjectRunner$$.run(ObjectRunner.scala:33)
at xsbt.RunInterface.run(RunInterface.scala:17)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at xsbt.AnalyzingCompiler.call(AnalyzingCompiler.scala:61)
at xsbt.AnalyzingCompiler.run(AnalyzingCompiler.scala:45)
at sbt.Run.execute$$1(Run.scala:43)
at sbt.Run$$$$anonfun$$run$$1.apply(Run.scala:44)
at sbt.Run$$$$anonfun$$run$$1.apply(Run.scala:44)
at sbt.TrapExit$$.executeMain$$1(TrapExit.scala:32)
at sbt.TrapExit$$$$anon$$1.run(TrapExit.scala:34)
Caused by: java.lang.NoSuchMethodException: bug.Main$$MyGraph.addVertex(int)
at java.lang.Class.getMethod(Class.java:1605)
at bug.Main$$.reflMethod$$Method1(Main.scala:19)
at bug.Main$$.crash(Main.scala:19)
at bug.Main$$.<init>(Main.scala:23)
at bug.Main$$.<clinit>(Main.scala)
... 23 more |
The method called approach3 in the attached example compiles but fails with NoSuchMethodException at runtime.
The problem concerns this construct:
def convert[A](a: A) = new { def frob(a2: A) = ... }
convert("foo").frob("bar")
Scala throws a NoSuchMethodException naming frob(String). Scala is trying to locate the frob method using the actual argument type, which is String. But the parameter type of frob is the erasure of A, which is Object.
Scala knows that frob is effectively a generic method, because it does correctly check the type of its argument at compile time. This line of code will not compile:
convert("foo").frob(1)
But it seems like the fact that it is a generic method gets lost somewhere within the call-by-reflection code.
I'm reporting this as a bug because approach1 and approach2, which are logically equivalent, work, probably because they don't require Scala to use reflection internally. If approach3 doesn't work by design, then I think that the Scala compiler should flag it as an error.
The text was updated successfully, but these errors were encountered: