Skip to content
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

"VerifyError: Bad invokespecial" when explicitly super call an abstract method #8693

Open
scabug opened this issue Jun 30, 2014 · 11 comments
Open
Assignees
Milestone

Comments

@scabug
Copy link

scabug commented Jun 30, 2014

The compiler ends with an java.lang.AssertionError when compiling the following file (and I have to look for another exam question....)

class A {
  def print(): Unit = {  }
}

trait B extends A {
  def print(): Unit
}

trait T3 extends B {
  override def print(): Unit = { super[B].print }
}

The error message looks as follows:

error: java.lang.AssertionError: assertion failed:
  class T3$class
     while compiling: Bug.scala
        during phase: globalPhase=mixin, enteringPhase=cleanup
     library version: version 2.11.0
    compiler version: version 2.11.0
  reconstructed args:

  last tree to typer: Select(Super(trait T3), print)
       tree position: line 10 of Bug.scala
              symbol: method print in class A
   symbol definition: def print(): Unit (a MethodSymbol)
      symbol package: <empty>
       symbol owners: method print -> class A
           call site: object T3$class in package <empty>

== Source file context for tree position ==

     7
     8 trait T3 extends B {
     9   override def print(): Unit = { super[B].print }
    10 }
    11
	at scala.tools.nsc.transform.Mixin$MixinTransformer.scala$tools$nsc$transform$Mixin$MixinTransformer$$postTransform(Mixin.scala:1195)
	at scala.tools.nsc.transform.Mixin$MixinTransformer$$anonfun$transform$1.apply(Mixin.scala:1239)
	at scala.tools.nsc.transform.Mixin$MixinTransformer$$anonfun$transform$1.apply(Mixin.scala:1239)
	at scala.reflect.internal.SymbolTable.enteringPhase(SymbolTable.scala:242)
	at scala.reflect.internal.SymbolTable.exitingPhase(SymbolTable.scala:263)
	at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:1239)
	at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:468)
	at scala.reflect.internal.Trees$$anonfun$itransform$2.apply(Trees.scala:1356)
	at scala.reflect.internal.Trees$$anonfun$itransform$2.apply(Trees.scala:1354)
	at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
	at scala.reflect.internal.Trees$class.itransform(Trees.scala:1353)
	at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
	at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
	at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
	at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:1236)
	at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:468)
	at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2589)
	at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2587)
	at scala.collection.immutable.List.loop$1(List.scala:172)
	at scala.collection.immutable.List.mapConserve(List.scala:188)
	at scala.reflect.api.Trees$Transformer.transformStats(Trees.scala:2587)
	at scala.reflect.internal.Trees$class.itransform(Trees.scala:1397)
	at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
	at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
	at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
	at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:1236)
	at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:468)
	at scala.reflect.api.Trees$Transformer.transformTemplate(Trees.scala:2563)
	at scala.reflect.internal.Trees$$anonfun$itransform$4.apply(Trees.scala:1401)
	at scala.reflect.internal.Trees$$anonfun$itransform$4.apply(Trees.scala:1400)
	at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
	at scala.reflect.internal.Trees$class.itransform(Trees.scala:1399)
	at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
	at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
	at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
	at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:1236)
	at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:468)
	at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2589)
	at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2587)
	at scala.collection.immutable.List.loop$1(List.scala:172)
	at scala.collection.immutable.List.mapConserve(List.scala:188)
	at scala.reflect.api.Trees$Transformer.transformStats(Trees.scala:2587)
	at scala.reflect.internal.Trees$$anonfun$itransform$7.apply(Trees.scala:1419)
	at scala.reflect.internal.Trees$$anonfun$itransform$7.apply(Trees.scala:1419)
	at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
	at scala.reflect.internal.Trees$class.itransform(Trees.scala:1418)
	at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
	at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
	at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
	at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:1236)
	at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:468)
	at scala.tools.nsc.ast.Trees$Transformer.transformUnit(Trees.scala:147)
	at scala.tools.nsc.transform.Transform$Phase.apply(Transform.scala:30)
	at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:430)
	at scala.tools.nsc.Global$GlobalPhase$$anonfun$run$1.apply(Global.scala:397)
	at scala.tools.nsc.Global$GlobalPhase$$anonfun$run$1.apply(Global.scala:397)
	at scala.collection.Iterator$class.foreach(Iterator.scala:743)
	at scala.collection.AbstractIterator.foreach(Iterator.scala:1174)
	at scala.tools.nsc.Global$GlobalPhase.run(Global.scala:397)
	at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1625)
	at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1610)
	at scala.tools.nsc.Global$Run.compileSources(Global.scala:1605)
	at scala.tools.nsc.Global$Run.compile(Global.scala:1703)
	at scala.tools.nsc.Driver.doCompile(Driver.scala:34)
	at scala.tools.nsc.MainClass.doCompile(Main.scala:23)
	at scala.tools.nsc.Driver.process(Driver.scala:55)
	at scala.tools.nsc.Driver.main(Driver.scala:68)
	at scala.tools.nsc.Main.main(Main.scala)
Exception in thread "main" java.lang.AssertionError: assertion failed: 
  class T3$class
     while compiling: Bug.scala
        during phase: globalPhase=mixin, enteringPhase=cleanup
     library version: version 2.11.0
    compiler version: version 2.11.0
  reconstructed args: 

  last tree to typer: Select(Super(trait T3), print)
       tree position: line 10 of Bug.scala
              symbol: method print in class A
   symbol definition: def print(): Unit (a MethodSymbol)
      symbol package: <empty>
       symbol owners: method print -> class A
           call site: object T3$class in package <empty>

== Source file context for tree position ==

     7 
     8 trait T3 extends B {
     9   override def print(): Unit = { super[B].print }
    10 }
    11 
	at scala.tools.nsc.transform.Mixin$MixinTransformer.scala$tools$nsc$transform$Mixin$MixinTransformer$$postTransform(Mixin.scala:1195)
	at scala.tools.nsc.transform.Mixin$MixinTransformer$$anonfun$transform$1.apply(Mixin.scala:1239)
	at scala.tools.nsc.transform.Mixin$MixinTransformer$$anonfun$transform$1.apply(Mixin.scala:1239)
	at scala.reflect.internal.SymbolTable.enteringPhase(SymbolTable.scala:242)
	at scala.reflect.internal.SymbolTable.exitingPhase(SymbolTable.scala:263)
	at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:1239)
	at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:468)
	at scala.reflect.internal.Trees$$anonfun$itransform$2.apply(Trees.scala:1356)
	at scala.reflect.internal.Trees$$anonfun$itransform$2.apply(Trees.scala:1354)
	at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
	at scala.reflect.internal.Trees$class.itransform(Trees.scala:1353)
	at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
	at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
	at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
	at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:1236)
	at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:468)
	at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2589)
	at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2587)
	at scala.collection.immutable.List.loop$1(List.scala:172)
	at scala.collection.immutable.List.mapConserve(List.scala:188)
	at scala.reflect.api.Trees$Transformer.transformStats(Trees.scala:2587)
	at scala.reflect.internal.Trees$class.itransform(Trees.scala:1397)
	at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
	at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
	at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
	at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:1236)
	at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:468)
	at scala.reflect.api.Trees$Transformer.transformTemplate(Trees.scala:2563)
	at scala.reflect.internal.Trees$$anonfun$itransform$4.apply(Trees.scala:1401)
	at scala.reflect.internal.Trees$$anonfun$itransform$4.apply(Trees.scala:1400)
	at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
	at scala.reflect.internal.Trees$class.itransform(Trees.scala:1399)
	at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
	at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
	at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
	at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:1236)
	at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:468)
	at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2589)
	at scala.reflect.api.Trees$Transformer$$anonfun$transformStats$1.apply(Trees.scala:2587)
	at scala.collection.immutable.List.loop$1(List.scala:172)
	at scala.collection.immutable.List.mapConserve(List.scala:188)
	at scala.reflect.api.Trees$Transformer.transformStats(Trees.scala:2587)
	at scala.reflect.internal.Trees$$anonfun$itransform$7.apply(Trees.scala:1419)
	at scala.reflect.internal.Trees$$anonfun$itransform$7.apply(Trees.scala:1419)
	at scala.reflect.api.Trees$Transformer.atOwner(Trees.scala:2600)
	at scala.reflect.internal.Trees$class.itransform(Trees.scala:1418)
	at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
	at scala.reflect.internal.SymbolTable.itransform(SymbolTable.scala:16)
	at scala.reflect.api.Trees$Transformer.transform(Trees.scala:2555)
	at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:1236)
	at scala.tools.nsc.transform.Mixin$MixinTransformer.transform(Mixin.scala:468)
	at scala.tools.nsc.ast.Trees$Transformer.transformUnit(Trees.scala:147)
	at scala.tools.nsc.transform.Transform$Phase.apply(Transform.scala:30)
	at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:430)
	at scala.tools.nsc.Global$GlobalPhase$$anonfun$run$1.apply(Global.scala:397)
	at scala.tools.nsc.Global$GlobalPhase$$anonfun$run$1.apply(Global.scala:397)
	at scala.collection.Iterator$class.foreach(Iterator.scala:743)
	at scala.collection.AbstractIterator.foreach(Iterator.scala:1174)
	at scala.tools.nsc.Global$GlobalPhase.run(Global.scala:397)
	at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1625)
	at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1610)
	at scala.tools.nsc.Global$Run.compileSources(Global.scala:1605)
	at scala.tools.nsc.Global$Run.compile(Global.scala:1703)
	at scala.tools.nsc.Driver.doCompile(Driver.scala:34)
	at scala.tools.nsc.MainClass.doCompile(Main.scala:23)
	at scala.tools.nsc.Driver.process(Driver.scala:55)
	at scala.tools.nsc.Driver.main(Driver.scala:68)
	at scala.tools.nsc.Main.main(Main.scala)
@scabug
Copy link
Author

scabug commented Jun 30, 2014

Imported From: https://issues.scala-lang.org/browse/SI-8693?orig=1
Reporter: @dgruntz
Affected Versions: 2.11.0
See #4704

@scabug
Copy link
Author

scabug commented Jun 30, 2014

@retronym said:
Maybe similar to #4704

@scabug scabug added this to the Backlog milestone Apr 7, 2017
@diesalbla
Copy link

diesalbla commented Jan 13, 2019

This "works" in version 2.12.8, and even as early as in Scala 2.12.0. By "works" I mean that there is no longer a compiler crash, and the compiler accepts the program and succeeds.

However, I am not sure if that is the correct expected behaviour. In the example, class A declares and defines a method def print(): Unit, and gives an (empty) concrete definition for it. Trait B, which extends from class A, re-declares that method as an abstract method.

Does the specification allow for a sub-trait to include an abstract declaration that shadows a concrete definition from a super-class?

@dgruntz
Copy link

dgruntz commented Jan 13, 2019

I would say that it should not compile and be flagged with an error.

However, it should be possible that a sub-trait shadows an inherited implementation with an abstract one. This is comparable to the case where a method from the base class is overridden in a sub-class with an abstract method:

class A {
  def print(): Unit = {  }
}

abstract class B extends A {
  def print(): Unit
}

This is used e.g. in the Java-Library in class AbstractSequentialList where method listIterator inherited from the base class is overridden with an abstract method. The reason is that the base class implements the iterator based on the abstract method get. Class AbstractSequentialList however implements method get using the listIterator, and in order to prevent an infinite loop, listIterator is cleared, e.g. overridden with an abstract method.

However, if the above given class B is extended in a class which provides a new implementation of the inherited abstract method, a super call is not possible, i.e.

class C extends B {
  override def print(): Unit = { super.print() }
}

leads to the compiler error

error: method print in class A cannot be directly accessed from class C
because class B redeclares it as abstract

I expect a similar error message at the definition of method print in trait T3.

class A {
  def print(): Unit = {  }
}

trait B extends A {
  def print(): Unit
}

trait T3 extends B {
  override def print(): Unit = { super[B].print }
}

As you wrote, the above code can be compiled with Scala 2.12.8, but if an instance of T3 is created, the JVM throws a VerifyError:

scala> new T3{}
java.lang.VerifyError: Bad invokespecial instruction: current class isn't assign
able to reference class.
Exception Details:
  Location:
    T3.print()V @1: invokespecial
  Reason:
    Error exists in the bytecode
  Bytecode:
    0000000: 2ab7 0028 b1

  ... 28 elided

Actually, I expected the same behavior if trait T3 were defined as follows (as the linearization of T3 is T3 => B => A in this example)

trait T3 extends B {
  override def print(): Unit = { super.print } // i.e. without super[B]
}

However, with the second definition of T3 instances can be created without VerifyErrors and an invocation of print on T3{} will invoke T3.print and then A.print which I would consider to be an error too.

Summary:

@NthPortal
Copy link

I am not an expert here, but I would not expect or want it to compile, and honestly I'm not sure if compiling is an improvement. At least when the compiler crashes, it doesn't compile code which does the wrong thing at runtime (either throw a VerifyError or invoke A's implementation).

@retronym
Copy link
Member

Here are some relevant parts of the spec:

https://www.scala-lang.org/files/archive/spec/2.11/06-expressions.html#this-and-super

A reference C.super.m refers statically to a method or type m in the least proper supertype of the innermost enclosing class or object definition named C which encloses the reference. It evaluates to the member m′ in the actual supertype of that class or object which is equal to m or which overrides m. The statically referenced member m must be a type or a method. If the statically referenced member m is a method, it must be concrete, or the innermost enclosing class or object definition named C must have a member m′ which overrides m and which is labeled abstract override.

The super prefix may be followed by a trait qualifier [T], as in C.super[T].x. This is called a static super reference. In this case, the reference is to the type or method of x in the parent trait of C whose simple name is T. That member must be uniquely defined. If it is a method, it must be concrete.

This would suggest we should report an error for super[B].print.

But, from https://www.scala-lang.org/files/archive/spec/2.11/05-classes-and-objects.html#class-members:

This definition also determines the overriding relationships between matching members of a class C and its parents. First, a concrete definition always overrides an abstract definition. Second, for definitions M and M' which are both concrete or both abstract, M overrides M′ if M appears in a class that precedes (in the linearization of C) the class in which M′ is defined.

Which means the concrete definition A.print is actually overrides the abstract declaration of T.print when finding the member of T. So we either need to issue an error for an implementation restriction if we can't emit valid bytecode, or find a compilation strategy that makes it work.

@adriaanm
Copy link
Contributor

That reminds me, we should deprecate a trait extending a class :-)

@SethTisue
Copy link
Member

That reminds me, we should deprecate a trait extending a class :-)

eh? really?

@adriaanm
Copy link
Contributor

(OT here -- let's discuss at scala/scala-dev#601)

hrhino added a commit to hrhino/scala that referenced this issue Dec 2, 2019
@SethTisue SethTisue modified the milestones: Backlog, 2.12.0 Sep 15, 2020
@SethTisue SethTisue changed the title Compiler bug when explicitly super call an abstract method "VerifyError: Bad invokespecial" when explicitly super call an abstract method Sep 15, 2020
@SethTisue
Copy link
Member

I've updated the issue title to reflect the current status (which is: it compiles, but you can get a VerifyError at runtime, as per #8693 (comment)).

@som-snytt
Copy link

The newly linked ticket avoids the red herring method declaration.

super.print vs super[B].print:

1: invokeinterface #21,  1           // InterfaceMethod T3$$super$print:()V
1: invokespecial #21                 // Method A.print:()V

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants