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

Compiler allows to super-call an abstract method. This leads to an AbstractMethodError at runtime.

    Details

    • Type: Bug Bug
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: Scala 2.9.1
    • Fix Version/s: Scala 2.10.0-M4, Scala 2.10.0
    • Component/s: Misc Compiler
    • Labels:
      None
    • Environment:

      % scala -version
      Scala code runner version 2.9.1.final – Copyright 2002-2011, LAMP/EPFL

      Description

      The following program compiles with no error message. At runtime, a java.lang.AbstractMethodError is thrown.

      The point is, that method print defined in class A is overwritten in class B with an abstract definition.
      In class C, which extends B, this abstract method is invoked with a super call. I expected a compilation error
      on this super call.

      object Test {
      
        class A {
          def print() { println("A"); }
        }
        
        abstract class B extends A {
          def print() : Unit
        }
        
        class C extends B {
          override def print() { println("C"); super.print(); }
        }
      
        def main(args: Array[String]): Unit = {
          val c = new C
          c.print
        }
      }
      

        Activity

        Hide
        Jason Zaugg added a comment - - edited

        A bit more analysis:

        scala> abstract class A { def a() }; abstract class B extends A { def a() }
        defined class A
        defined class B
        
        scala> object C extends B { override def a() = super.a() }
        <console>:9: error: method a in class B is accessed from super. It may not be abstract unless it is overridden by a member declared `abstract' and `override'
               object C extends B { override def a() = super.a() }
                                                             ^
        
        scala> import reflect.{mirror => m}
        import reflect.{mirror=>m}
        
        scala> m.classToType(classOf[B]).member(m.newTermName("a")).modifiers
        res1: Set[scala.reflect.api.Modifier] = Set(deferred)
        
        scala> class A { def a() {} }; abstract class B extends A { def a() }
        defined class A
        defined class B
        
        scala> object C extends B { override def a() = super.a() }
        defined module C
        
        scala> m.classToType(classOf[B]).member(m.newTermName("a")).modifiers
        res2: Set[scala.reflect.api.Modifier] = Set()
        
        scala> C.a()
        java.lang.AbstractMethodError: A.a()V
        	at C$.a(<console>:10)
        	at .<init>(<console>:12)
        	at .<clinit>(<console>)
        

        Key question: When does the symbol for method B#a lose the DEFERRED flag? Without it, the check in SuperAccessors doesn't catch this problem.

        Show
        Jason Zaugg added a comment - - edited A bit more analysis: scala> abstract class A { def a() }; abstract class B extends A { def a() } defined class A defined class B scala> object C extends B { override def a() = super.a() } <console>:9: error: method a in class B is accessed from super. It may not be abstract unless it is overridden by a member declared `abstract' and `override' object C extends B { override def a() = super.a() } ^ scala> import reflect.{mirror => m} import reflect.{mirror=>m} scala> m.classToType(classOf[B]).member(m.newTermName("a")).modifiers res1: Set[scala.reflect.api.Modifier] = Set(deferred) scala> class A { def a() {} }; abstract class B extends A { def a() } defined class A defined class B scala> object C extends B { override def a() = super.a() } defined module C scala> m.classToType(classOf[B]).member(m.newTermName("a")).modifiers res2: Set[scala.reflect.api.Modifier] = Set() scala> C.a() java.lang.AbstractMethodError: A.a()V at C$.a(<console>:10) at .<init>(<console>:12) at .<clinit>(<console>) Key question: When does the symbol for method B#a lose the DEFERRED flag? Without it, the check in SuperAccessors doesn't catch this problem.
        Hide
        Paul Phillips added a comment -

        > Key question: When does the symbol for method B#a lose the DEFERRED flag?

        It doesn't. When you request the member of B called a, you get the symbol defined in A, not in B, because concrete always overrides abstract. If you want B#a, use decl (or I guess it's called declaration in the library.)

        Show
        Paul Phillips added a comment - > Key question: When does the symbol for method B#a lose the DEFERRED flag? It doesn't. When you request the member of B called a, you get the symbol defined in A, not in B, because concrete always overrides abstract. If you want B#a, use decl (or I guess it's called declaration in the library.)
        Show
        Jason Zaugg added a comment - https://github.com/scala/scala/pull/732
        Show
        Jason Zaugg added a comment - https://github.com/scala/scala/commit/54c92ca7

          People

          • Assignee:
            Jason Zaugg
            Reporter:
            Dominik Gruntz
          • Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved:

              Development