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

@specialize does not handle super correctly #4012

Closed
scabug opened this issue Nov 20, 2010 · 12 comments
Closed

@specialize does not handle super correctly #4012

scabug opened this issue Nov 20, 2010 · 12 comments
Assignees
Milestone

Comments

@scabug
Copy link

@scabug scabug commented Nov 20, 2010

[Edit: adding a more serious super-related crash.]

trait C1[+A] {
  def head: A = sys.error("")
}
trait C2[@specialized +A] extends C1[A] {
  override def head: A = super.head
}
class C3 extends C2[Char] 
// 
// error: java.lang.AssertionError: assertion failed: method head
//  at scala.Predef$.assert(Predef.scala:160)
//  at scala.tools.nsc.transform.Mixin$$anonfun$scala$tools$nsc$transform$Mixin$$rebindSuper$1.apply(Mixin.scala:129)
//  at scala.tools.nsc.transform.Mixin$$anonfun$scala$tools$nsc$transform$Mixin$$rebindSuper$1.apply(Mixin.scala:95)
//  at scala.reflect.internal.SymbolTable.atPhase(SymbolTable.scala:103)
//  at scala.tools.nsc.transform.Mixin.scala$tools$nsc$transform$Mixin$$rebindSuper(Mixin.scala:95)
//  at scala.tools.nsc.transform.Mixin$$anonfun$mixinTraitMembers$1$1.apply(Mixin.scala:351)
//  at scala.tools.nsc.transform.Mixin$$anonfun$mixinTraitMembers$1$1.apply(Mixin.scala:304)

[Original report]

trait Super[@specialized(Int) A] {
  def superb = 0
}

object Sub extends Super[Int] {
  super[Super].superb // scala.tools.nsc.symtab.Types$$TypeError: Super does not name a parent class of object Sub
  super.superb        // okay    
  override def superb: Int = super.superb // okay
}
scala.tools.nsc.symtab.Types$$TypeError: Super does not name a parent class of object Sub
	at scala.tools.nsc.typechecker.Contexts$$Context.error(Contexts.scala:273)
	at scala.tools.nsc.typechecker.Infer$$Inferencer.error(Infer.scala:213)
	at scala.tools.nsc.typechecker.Typers$$Typer.findMixinSuper$$1(Typers.scala:3417)
	at scala.tools.nsc.typechecker.Typers$$Typer.typedSuper$$1(Typers.scala:3434)
	at scala.tools.nsc.typechecker.Typers$$Typer.typed1(Typers.scala:3951)
	at scala.tools.nsc.typechecker.Typers$$Typer.typed(Typers.scala:4078)
	at scala.tools.nsc.typechecker.Typers$$Typer.typedQualifier(Typers.scala:4145)
	at scala.tools.nsc.typechecker.Typers$$Typer.typedQualifier(Typers.scala:4151)
	at scala.tools.nsc.typechecker.Typers$$Typer.typed1(Typers.scala:3964)
	at scala.tools.nsc.typechecker.Typers$$Typer.typed(Typers.scala:4078)
	at scala.tools.nsc.typechecker.Typers$$Typer$$$$anonfun$$typedApply$$1$$1.apply(Typers.scala:3284)
	at scala.tools.nsc.typechecker.Typers$$Typer$$$$anonfun$$typedApply$$1$$1.apply(Typers.scala:3284)
	at scala.tools.nsc.typechecker.Typers$$Typer.silent(Typers.scala:705)
	at scala.tools.nsc.typechecker.Typers$$Typer.typedApply$$1(Typers.scala:3284)
	at scala.tools.nsc.typechecker.Typers$$Typer.typed1(Typers.scala:3924)
	at scala.tools.nsc.typechecker.Typers$$Typer.typed(Typers.scala:4078)
	at scala.tools.nsc.typechecker.Typers$$Typer.typed(Typers.scala:4126)
	at scala.tools.nsc.transform.SpecializeTypes$$$$anon$$6.transform(SpecializeTypes.scala
[snip]

In the original code, I originally got a different error during mixins, using an unqualified super reference. I haven't reproduced that yet in isolation. It can be reproduced by compiling scalala with SBT from: https://github.com/retronym/Scalala/tree/ab592a34bc1b5492ab2df65d4ba96d6c585ff724

This results in:

[info] [running phase mixin on Domain.scala]
exception when traversing <root>#java#lang#Object with
<root>#scala#Product2 with
<root>#scalala#tensor#domain#Product2Domain$$mcII$$sp with
<root>#scalala#tensor#domain#Product2DomainLike$$mcII$$sp with
<root>#scala#ScalaObject with <root>#scala#Product {

[snip]

java.lang.UnsupportedOperationException: tail of empty list
       at scala.collection.immutable.Nil$$.tail(List.scala:388)
       at scala.collection.immutable.Nil$$.tail(List.scala:383)
       at scala.tools.nsc.transform.Mixin$$$$anonfun$$scala$$tools$$nsc$$transform$$Mixin$$$$rebindSuper$$1.apply(Mixin.scala:95)
       at scala.tools.nsc.transform.Mixin$$$$anonfun$$scala$$tools$$nsc$$transform$$Mixin$$$$rebindSuper$$1.apply(Mixin.scala:94)
       at scala.tools.nsc.symtab.SymbolTable.atPhase(SymbolTable.scala:103)

I'll try and get a smaller test case for this one, and update this ticket.

See also #3651

What versions of the following are you using?
Scala: 2.8.0 / 2.8.1
Java: 1.6

@scabug
Copy link
Author

@scabug scabug commented Nov 20, 2010

@scabug
Copy link
Author

@scabug scabug commented Nov 20, 2010

@retronym said:
Workaround for the second problem is to avoid using super: retronym/Scalala@7c91800

@scabug
Copy link
Author

@scabug scabug commented Oct 12, 2011

@paulp said:
What is happening I believe is this: mixin assumes that if you have a pending super.foo call to SuperTrait, that SuperTrait will be one of your parents. But if SuperTrait was specialized, the pending super.foo has been rewritten to super.foo$mcI$sp or whatever, but SuperTrait$mcI$sp has not been added to your parents. When exactly these transformations take place, and when they are SUPPOSED to take place, is anyone's supposition.

@scabug
Copy link
Author

@scabug scabug commented Oct 12, 2011

@paulp said:
I should clarify I'm talking about the "tail of empty list" error, because that's coming from mixin looking through the base classes and never finding the one it's looking for.

@scabug
Copy link
Author

@scabug scabug commented Aug 27, 2012

@axel22 said (edited on Aug 27, 2012 5:37:05 PM UTC):
At first glance, I believe that SuperTrait$mcI$sp should be added to the parents during specialize when the class C3 is being specialized - this appears to be currently only done for newly created specialized classes, and not those that merely need a special override for their methods.

@scabug
Copy link
Author

@scabug scabug commented Aug 27, 2012

@axel22 said:
Ah, the problem appears to be that the super call is not rewritten appropriately after Sub is changed to inherit from a specialized superclass.

@scabug
Copy link
Author

@scabug scabug commented Aug 27, 2012

@axel22 said:
The fix for Super does not name a parent class of object Sub:

https://github.com/axel22/scala-github/tree/issue/6265b

The tail of empty list still in progress.

@scabug
Copy link
Author

@scabug scabug commented Aug 27, 2012

@axel22 said:
The tail of empty list exception happends due to the following method in mixin which, instead of looking at the new list of base classes which is obtained after specialization, looks at the list of base classes after pickler:

  private def rebindSuper(base: Symbol, member: Symbol, mixinClass: Symbol): Symbol =
    exitingPickler {
      var bcs = base.info.baseClasses.dropWhile(mixinClass != _).tail

I don't know this phase well enough to understand why this is done this way - I'd just extract the base.info.baseClasses call outside of the closure.

@scabug
Copy link
Author

@scabug scabug commented Aug 28, 2012

@axel22 said:
I think the answer to the above is - yes, the baseClasses call should go before the exitingPickler block.

However, I don't think that this example is easily fixable. Let me illustrate why. From this:

trait C1[A] {
  def head: A = sys.error("")
}

trait C2[@specialized(Int) A] extends C1[A] {
  override def head: A = super.head
}

class C3 extends C2[Int]

After mixin I get a super accessor defined twice, once coming from the specialized class and once from a generic one:

  class C3 extends Object with C2$mcI$sp {
    override <superaccessor> <specialized> <artifact> def super$head(): Int = unbox(C2$class.head(C3.this));
    override <superaccessor> <specialized> <artifact> def super$head$mcI$sp(): Int = unbox(C2$class.head(C3.this));
    override <specialized> def head(): Int = C2$mcI$sp$class.head(C3.this);
    override <specialized> def head$mcI$sp(): Int = C2$mcI$sp$class.head$mcI$sp(C3.this);
    <superaccessor> <artifact> def super$head(): Object = C1$class.head(C3.this);
    <superaccessor> <specialized> <artifact> def super$head$mcI$sp(): Int = unbox(C1$class.head(C3.this));
    override <bridge> <specialized> <artifact> def head(): Object = scala.Int.box(C3.this.head());
    def <init>(): C3 = {
      C3.super.<init>();
      C1$class./*C1$class*/$init$(C3.this);
      C2$class./*C2$class*/$init$(C3.this);
      C2$mcI$sp$class./*C2$mcI$sp$class*/$init$(C3.this);
      ()
    }
  };

Now, the problem is (I gather) that the names of super accessors are not properly mangled (I'm not sure where exactly that happens, but I guess a `makeNotPrivate`` call is supposed to do this, and it should happen some time during specialization).

Even if I fix that, there is still the fundamental problem that I cannot call super.head from C2$mcI$sp:

abstract <specialized> trait C2$mcI$sp extends Object with C2[Int] {
    ...
    // assuming proper name mangling for a super accessor
    override <specialized> def head$mcI$sp(): Int = C2$mcI$sp.this.C2$mcI$sp$$super$head$mcI$sp();
    ...
  }

because that super.head will call this method:

class C3 extends Object with C2$mcI$sp {
    ...
    override <superaccessor> <specialized> <artifact> def C2$mcI$sp$$super$head$mcI$sp(): Int = unbox(C2$class.head(C3.this));

which in turn calls:

abstract trait C2$class extends  {
  ...
  <specialized> def head$mcI$sp($this: C2): Int = scala.Int.unbox($this.head());

And then you start spinning in a cycle of recursive calls and a stack overflow happens.

@scabug
Copy link
Author

@scabug scabug commented Aug 28, 2012

@axel22 said:
Assigning to Scala meeting.

@scabug
Copy link
Author

@scabug scabug commented Jul 10, 2013

@adriaanm said:
Unassigning and rescheduling to M6 as previous deadline was missed.

@scabug
Copy link
Author

@scabug scabug commented Sep 2, 2013

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants