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

@throws annotation synthesized for class files compiled from Java is broken

    Details

      Description

      Power REPL shows the problem. See below for comments.

      scala
      Welcome to Scala version 2.10.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_37).
      Type in expressions to have them evaluated.
      Type :help for more information.
      
      scala> :power
      ** Power User mode enabled - BEEP WHIR GYVE **
      ** :phase has been set to 'typer'.          **
      ** scala.tools.nsc._ has been imported      **
      ** global._, definitions._ also imported    **
      ** Try  :help, :vals, power.<tab>           **
      
      scala> val methodClass = rootMirror.getClassByName(newTermName("java.lang.reflect.Method"))
      methodClass: $r.intp.global.ClassSymbol = class Method
      
      scala> val invokeSymbol = methodClass.info.member(newTermName("invoke"))
      invokeSymbol: $r.intp.global.Symbol = method invoke
      
      scala> val throwsAnn = invokeSymbol.annotations.head
      throwsAnn: $r.intp.global.AnnotationInfo = throws(classOf[java.lang.reflect.InvocationTargetException])
      
      scala> throwsAnn.atp.typeParams
      res0: List[$r.intp.global.Symbol] = List(type T)
      

      As you can see, we get higher-kinded (unapplied) type for annotation. This is broken and I believe it caused by PR-1347 which introduced type parameter for scala.throws annotation but forgot to change parseExceptions in ClassfileParser.scala.

      This might be related to spurious API changes in sbt's incremental compiler reported here:
      https://github.com/sbt/sbt/issues/642#issuecomment-12577946

        Activity

        Show
        Grzegorz Kossakowski added a comment - https://github.com/scala/scala/pull/1961
        Hide
        Grzegorz Kossakowski added a comment -

        Paul pointed out my pull request that in Scala we allow polymorphic exceptions. This is not being handled properly by ClassfileParser probably with an assumption that since we are parsing exception declarations emitted by Java we'll never get polymorphic exceptions there. That assumption is wrong if you mix Scala and Java. The trick is to define polymorphic exception in Scala. Then declare it in throws declaration in Java class. See the following transcript:

        rm -rf classes/*
        Grzegorzs-MacBook-Pro:scala-throws grek$ cat Foo.scala
        package test
        
        class Foo[T] extends Exception
        Grzegorzs-MacBook-Pro:scala-throws grek$ scalac Foo.scala -d classes/
        Grzegorzs-MacBook-Pro:scala-throws grek$ cat Xyz.java 
        package test;
        
        public class Xyz {
        	public void abc() throws Foo {}
        }
        Grzegorzs-MacBook-Pro:scala-throws grek$ 
        
        Grzegorzs-MacBook-Pro:scala-throws grek$ javac -cp /usr/local/Cellar/scala/2.10.0/libexec/lib/scala-library.jar:classes/ -d classes/ Xyz.java
        Grzegorzs-MacBook-Pro:scala-throws grek$ scala -classpath classes/
        Welcome to Scala version 2.10.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_37).
        Type in expressions to have them evaluated.
        Type :help for more information.
        
        scala> :power
        ** Power User mode enabled - BEEP WHIR GYVE **
        ** :phase has been set to 'typer'.          **
        ** scala.tools.nsc._ has been imported      **
        ** global._, definitions._ also imported    **
        ** Try  :help, :vals, power.<tab>           **
        
        scala> rootMirror.getClassByName(newTermName("test.Xyz")).info.member(newTermName("abc"))
        res0: $r.intp.global.Symbol = method abc
        
        scala> res0.annotations
        res1: List[$r.intp.global.AnnotationInfo] = List(throws(classOf[test.Foo]))
        
        scala> val Literal(const) = res1.head.args.head
        const: $r.intp.global.Constant = Constant(test.Foo)
        
        scala> const.typeValue
        res2: $r.intp.global.Type = test.Foo
        
        scala> res2.typeParams
        res3: List[$r.intp.global.Symbol] = List(type T)
        
        scala> exit
        warning: there were 1 deprecation warnings; re-run with -deprecation for details
        
        Show
        Grzegorz Kossakowski added a comment - Paul pointed out my pull request that in Scala we allow polymorphic exceptions. This is not being handled properly by ClassfileParser probably with an assumption that since we are parsing exception declarations emitted by Java we'll never get polymorphic exceptions there. That assumption is wrong if you mix Scala and Java. The trick is to define polymorphic exception in Scala. Then declare it in throws declaration in Java class. See the following transcript: rm -rf classes/* Grzegorzs-MacBook-Pro:scala-throws grek$ cat Foo.scala package test class Foo[T] extends Exception Grzegorzs-MacBook-Pro:scala-throws grek$ scalac Foo.scala -d classes/ Grzegorzs-MacBook-Pro:scala-throws grek$ cat Xyz.java package test; public class Xyz { public void abc() throws Foo {} } Grzegorzs-MacBook-Pro:scala-throws grek$ Grzegorzs-MacBook-Pro:scala-throws grek$ javac -cp /usr/local/Cellar/scala/2.10.0/libexec/lib/scala-library.jar:classes/ -d classes/ Xyz.java Grzegorzs-MacBook-Pro:scala-throws grek$ scala -classpath classes/ Welcome to Scala version 2.10.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_37). Type in expressions to have them evaluated. Type :help for more information. scala> :power ** Power User mode enabled - BEEP WHIR GYVE ** ** :phase has been set to 'typer'. ** ** scala.tools.nsc._ has been imported ** ** global._, definitions._ also imported ** ** Try :help, :vals, power.<tab> ** scala> rootMirror.getClassByName(newTermName("test.Xyz")).info.member(newTermName("abc")) res0: $r.intp.global.Symbol = method abc scala> res0.annotations res1: List[$r.intp.global.AnnotationInfo] = List(throws(classOf[test.Foo])) scala> val Literal(const) = res1.head.args.head const: $r.intp.global.Constant = Constant(test.Foo) scala> const.typeValue res2: $r.intp.global.Type = test.Foo scala> res2.typeParams res3: List[$r.intp.global.Symbol] = List(type T) scala> exit warning: there were 1 deprecation warnings; re-run with -deprecation for details
        Hide
        Grzegorz Kossakowski added a comment -

        Also, here's quote from Java spec:

        It is a compile-time error if a generic class is a direct or indirect subclass of Throwable.

        That's why code above compiles; javac doesn't even bother to check if an exception declared in throws is polymorphic because it assumes that can never happen.

        Show
        Grzegorz Kossakowski added a comment - Also, here's quote from Java spec : It is a compile-time error if a generic class is a direct or indirect subclass of Throwable. That's why code above compiles; javac doesn't even bother to check if an exception declared in throws is polymorphic because it assumes that can never happen.
        Hide
        Grzegorz Kossakowski added a comment -

        FYI: My first pull request got closed but I'm still working on this problem. I'll have less ambitious pull request ready soon.

        Show
        Grzegorz Kossakowski added a comment - FYI: My first pull request got closed but I'm still working on this problem. I'll have less ambitious pull request ready soon.
        Show
        Grzegorz Kossakowski added a comment - Fixed by https://github.com/scala/scala/pull/2021

          People

          • Assignee:
            Grzegorz Kossakowski
            Reporter:
            Grzegorz Kossakowski
          • Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development