scala / bug

Scala 2 bug reports only. Please, no questions — proper bug reports only.
https://scala-lang.org
232 stars 21 forks source link

A in class Foo cannot be instantiated from Bar when accessed from separate compilation unit #4079

Closed scabug closed 13 years ago

scabug commented 13 years ago

It's possible this is a duplicate of #2741. I've gotten down to a pretty simple test case though.

Compiling the following code seems to work fine:

trait Functor[F[_]] {
  def map[A,B](fa: F[A], f: A => B): F[B]
}

trait ComposeT[F[_],G[_]] {
  type Apply[A] = F[G[A]]
}

case class Compose[F[_],G[_]]() {
  def Functor(implicit f: Functor[F], g: Functor[G]): Functor[ComposeT[F,G]#Apply] =
    new Functor[ComposeT[F,G]#Apply] {
      def map[A,B](c: ComposeT[F,G]#Apply[A], h: A => B) = 
        f.map(c, (x:G[A]) => g.map(x,h))
    }
}

object Cat {
  def compose[F[_],G[_]] = Compose[F,G]()
}

object Functors {
  implicit val List = new Functor[List] { 
    def map[A,B](fa: List[A], f: A => B): List[B] = fa map f
  }
  implicit val Option = new Functor[Option] { 
    def map[A,B](fa: Option[A], f: A => B): Option[B] = fa map f
  }
}

object Main {
  import Functors._
  val cf = Cat.compose[List,Option].Functor
}

If I then invoke this code from the interpreter (or a separate compilation unit), I get this stack trace:

scala> Main.cf
res0: Functor[[A]List[Option[A]]] = Compose$$$$anon$$1@19ddf4e // works fine!

scala> Cat.compose[List,Option].Functor
java.lang.Error: A in class Compose cannot be instantiated from Compose[List,Option]
        at scala.tools.nsc.symtab.SymbolTable.abort(SymbolTable.scala:33)
        at scala.tools.nsc.symtab.Types$$AsSeenFromMap.throwError$$1(Types.scala:3281)
        at scala.tools.nsc.symtab.Types$$AsSeenFromMap.instParam$$1(Types.scala:3284)
        at scala.tools.nsc.symtab.Types$$AsSeenFromMap.toInstance$$1(Types.scala:3295)
        at scala.tools.nsc.symtab.Types$$AsSeenFromMap.apply(Types.scala:3311)
        at scala.tools.nsc.symtab.Types$$AsSeenFromMap.apply(Types.scala:3189)
        at scala.collection.immutable.List.loop$$1(List.scala:115)
        at scala.collection.immutable.List.mapConserve(List.scala:132)
        at scala.tools.nsc.symtab.Types$$AsSeenFromMap.instParam$$1(Types.scala:3287)
        at scala.tools.nsc.symtab.Types$$AsSeenFromMap.toInstance$$1(Types.scala:3295)
        at scala.tools.nsc.symtab.Types$$AsSeenFromMap.apply(Types.scala:3311)
        at scala.tools.nsc.symtab.Types$$AsSeenFromMap.apply(Types.scala:3189)
        at scala.collection.immutable.List.loop$$1(List.scala:115)
        at scala.collection.immutable.List.mapConserve(List.scala:132)
        at scala.tools.nsc.symtab.Types$$AsSeenFromMap.instParam$$1(Types.scala:3287)
        at scala.tools.nsc.symtab.Types$$AsSeenFromMap.toInstance$$1(Types.scala:3295)
        at scala.tools.nsc.symtab.Types$$AsSeenFromMap.apply(Types.scala:3311)
        at scala.tools.nsc.symtab.Types$$AsSeenFromMap.apply(Types.scala:3189)
        at scala.tools.nsc.symtab.Types$$TypeMap.mapOver(Types.scala:2953)
        at scala.tools.nsc.symtab.Types$$AsSeenFromMap.apply(Types.scala:3313)
        at scala.tools.nsc.symtab.Types$$AsSeenFromMap.apply(Types.scala:3189)
        at scala.tools.nsc.symtab.Types$$TypeMap$$$$anonfun$$mapOverArgs$$1.apply(Types.scala:3023)
        at scala.tools.nsc.symtab.Types$$TypeMap$$$$anonfun$$mapOverArgs$$1.apply(Types.scala:3019)
        at scala.tools.nsc.symtab.Types$$class.map2Conserve(Types.scala:4723)
        at scala.tools.nsc.symtab.SymbolTable.map2Conserve(SymbolTable.scala:13)
        at scala.tools.nsc.symtab.Types$$TypeMap.mapOverArgs(Types.scala:3019)
        at scala.tools.nsc.symtab.Types$$TypeMap.mapOver(Types.scala:2929)
        at scala.tools.nsc.symtab.Types$$AsSeenFromMap.apply(Types.scala:3313)
        at scala.tools.nsc.symtab.Types$$AsSeenFromMap.apply(Types.scala:3189)
        at scala.tools.nsc.symtab.Types$$TypeMap.mapOver(Types.scala:2945)
        at scala.tools.nsc.symtab.Types$$AsSeenFromMap.apply(Types.scala:3313)
        at scala.tools.nsc.symtab.Types$$Type.asSeenFrom(Types.scala:516)
        at scala.tools.nsc.symtab.Types$$Type.computeMemberType(Types.scala:540)
        at scala.tools.nsc.symtab.Symbols$$MethodSymbol.typeAsMemberOf(Symbols.scala:1780)
        at scala.tools.nsc.symtab.Types$$Type.memberType(Types.scala:531)
        at scala.tools.nsc.typechecker.Infer$$Inferencer.checkAccessible(Infer.scala:273)
        at scala.tools.nsc.typechecker.Typers$$Typer.makeAccessible(Typers.scala:610)
        at scala.tools.nsc.typechecker.Typers$$Typer.typedSelect$$1(Typers.scala:3607)
        at scala.tools.nsc.typechecker.Typers$$Typer.typed1(Typers.scala:4110)
        at scala.tools.nsc.typechecker.Typers$$Typer.typed(Typers.scala:4203)
        at scala.tools.nsc.typechecker.Typers$$Typer.typed(Typers.scala:4265)
        at scala.tools.nsc.typechecker.Typers$$Typer.computeType(Typers.scala:4341)
        at scala.tools.nsc.typechecker.Namers$$Namer.typeSig(Namers.scala:1145)
        at scala.tools.nsc.typechecker.Namers$$Namer$$$$anonfun$$typeCompleter$$1.apply(Namers.scala:523)
        at scala.tools.nsc.typechecker.Namers$$Namer$$$$anonfun$$typeCompleter$$1.apply(Namers.scala:521)
        at scala.tools.nsc.typechecker.Namers$$$$anon$$1.complete(Namers.scala:1309)
        at scala.tools.nsc.typechecker.Namers$$$$anon$$1.complete(Namers.scala:1307)
        at scala.tools.nsc.symtab.Symbols$$Symbol.info(Symbols.scala:735)
        at scala.tools.nsc.symtab.Symbols$$Symbol.initialize(Symbols.scala:848)
        at scala.tools.nsc.symtab.Symbols$$Symbol.annotations(Symbols.scala:111)
        at scala.tools.nsc.typechecker.Typers$$Typer.addGetterSetter(Typers.scala:1370)
        at scala.tools.nsc.typechecker.Typers$$Typer$$$$anonfun$$10.apply(Typers.scala:1547)
        at scala.tools.nsc.typechecker.Typers$$Typer$$$$anonfun$$10.apply(Typers.scala:1547)
        at scala.collection.TraversableLike$$$$anonfun$$flatMap$$1.apply(TraversableLike.scala:227)
        at scala.collection.TraversableLike$$$$anonfun$$flatMap$$1.apply(TraversableLike.scala:227)
        at scala.collection.LinearSeqOptimized$$class.foreach(LinearSeqOptimized.scala:61)
        at scala.collection.immutable.List.foreach(List.scala:45)
        at scala.collection.TraversableLike$$class.flatMap(TraversableLike.scala:227)
        at scala.collection.immutable.List.flatMap(List.scala:45)
        at scala.tools.nsc.typechecker.Typers$$Typer.typedTemplate(Typers.scala:1547)
        at scala.tools.nsc.typechecker.Typers$$Typer.typedModuleDef(Typers.scala:1348)
        at scala.tools.nsc.typechecker.Typers$$Typer.typed1(Typers.scala:3856)
        at scala.tools.nsc.typechecker.Typers$$Typer.typed(Typers.scala:4203)
        at scala.tools.nsc.typechecker.Typers$$Typer.typedStat$$1(Typers.scala:2128)
        at scala.tools.nsc.typechecker.Typers$$Typer$$$$anonfun$$20.apply(Typers.scala:2196)
        at scala.tools.nsc.typechecker.Typers$$Typer$$$$anonfun$$20.apply(Typers.scala:2196)
        at scala.collection.immutable.List.loop$$1(List.scala:115)
        at scala.collection.immutable.List.mapConserve(List.scala:132)
        at scala.tools.nsc.typechecker.Typers$$Typer.typedStats(Typers.scala:2196)
        at scala.tools.nsc.typechecker.Typers$$Typer.typedTemplate(Typers.scala:1549)
        at scala.tools.nsc.typechecker.Typers$$Typer.typedModuleDef(Typers.scala:1348)
        at scala.tools.nsc.typechecker.Typers$$Typer.typed1(Typers.scala:3856)
        at scala.tools.nsc.typechecker.Typers$$Typer.typed(Typers.scala:4203)
        at scala.tools.nsc.typechecker.Typers$$Typer.typedStat$$1(Typers.scala:2128)
        at scala.tools.nsc.typechecker.Typers$$Typer$$$$anonfun$$20.apply(Typers.scala:2196)
        at scala.tools.nsc.typechecker.Typers$$Typer$$$$anonfun$$20.apply(Typers.scala:2196)
        at scala.collection.immutable.List.loop$$1(List.scala:115)
        at scala.collection.immutable.List.mapConserve(List.scala:132)
        at scala.tools.nsc.typechecker.Typers$$Typer.typedStats(Typers.scala:2196)
        at scala.tools.nsc.typechecker.Typers$$Typer.typedTemplate(Typers.scala:1549)
        at scala.tools.nsc.typechecker.Typers$$Typer.typedModuleDef(Typers.scala:1348)
        at scala.tools.nsc.typechecker.Typers$$Typer.typed1(Typers.scala:3856)
        at scala.tools.nsc.typechecker.Typers$$Typer.typed(Typers.scala:4203)
        at scala.tools.nsc.typechecker.Typers$$Typer.typedStat$$1(Typers.scala:2128)
        at scala.tools.nsc.typechecker.Typers$$Typer$$$$anonfun$$20.apply(Typers.scala:2196)
        at scala.tools.nsc.typechecker.Typers$$Typer$$$$anonfun$$20.apply(Typers.scala:2196)
        at scala.collection.immutable.List.loop$$1(List.scala:115)
        at scala.collection.immutable.List.mapConserve(List.scala:132)
        at scala.tools.nsc.typechecker.Typers$$Typer.typedStats(Typers.scala:2196)
        at scala.tools.nsc.typechecker.Typers$$Typer.typed1(Typers.scala:3849)
        at scala.tools.nsc.typechecker.Typers$$Typer.typed(Typers.scala:4203)
        at scala.tools.nsc.typechecker.Typers$$Typer.typed(Typers.scala:4252)
        at scala.tools.nsc.typechecker.Analyzer$$typerFactory$$$$anon$$3.apply(Analyzer.scala:85)
        at scala.tools.nsc.Global$$GlobalPhase$$$$anonfun$$applyPhase$$1.apply(Global.scala:282)
        at scala.tools.nsc.Global$$GlobalPhase$$$$anonfun$$applyPhase$$1.apply(Global.scala:282)
        at scala.tools.nsc.reporters.Reporter.withSource(Reporter.scala:48)
        at scala.tools.nsc.Global$$GlobalPhase.applyPhase(Global.scala:282)
        at scala.tools.nsc.typechecker.Analyzer$$typerFactory$$$$anon$$3$$$$anonfun$$run$$1.apply(Analyzer.s
        at scala.tools.nsc.typechecker.Analyzer$$typerFactory$$$$anon$$3$$$$anonfun$$run$$1.apply(Analyzer.s
        at scala.collection.Iterator$$class.foreach(Iterator.scala:631)
        at scala.collection.mutable.ListBuffer$$$$anon$$1.foreach(ListBuffer.scala:304)
        at scala.tools.nsc.typechecker.Analyzer$$typerFactory$$$$anon$$3.run(Analyzer.scala:80)
        at scala.tools.nsc.Global$$Run.compileSources(Global.scala:741)
        at scala.tools.nsc.Interpreter.compileAndSaveRun(Interpreter.scala:518)
        at scala.tools.nsc.Interpreter$$Request.objRun(Interpreter.scala:892)
        at scala.tools.nsc.Interpreter$$Request.compile(Interpreter.scala:915)
        at scala.tools.nsc.Interpreter.interpret(Interpreter.scala:598)
        at scala.tools.nsc.Interpreter.interpret(Interpreter.scala:577)
        at scala.tools.nsc.InterpreterLoop.reallyInterpret$$1(InterpreterLoop.scala:472)
        at scala.tools.nsc.InterpreterLoop.interpretStartingWith(InterpreterLoop.scala:515)
        at scala.tools.nsc.InterpreterLoop.command(InterpreterLoop.scala:362)
        at scala.tools.nsc.InterpreterLoop.processLine$$1(InterpreterLoop.scala:243)
        at scala.tools.nsc.InterpreterLoop.repl(InterpreterLoop.scala:249)
        at scala.tools.nsc.InterpreterLoop.main(InterpreterLoop.scala:559)
        at xsbt.ConsoleInterface.run(ConsoleInterface.scala:26)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at xsbt.AnalyzingCompiler.call(AnalyzingCompiler.scala:40)
        at xsbt.AnalyzingCompiler.console(AnalyzingCompiler.scala:32)
        at sbt.Console.console0$$1(Compile.scala:141)
        at sbt.Console$$$$anonfun$$apply$$4$$$$anonfun$$apply$$5.apply(Compile.scala:142)
        at sbt.Console$$$$anonfun$$apply$$4$$$$anonfun$$apply$$5.apply(Compile.scala:142)
        at sbt.TrapExit$$.executeMain$$1(TrapExit.scala:33)
        at sbt.TrapExit$$$$anon$$1.run(TrapExit.scala:42)

What is odd is that if I put the call to Cat.compose to the same file, it doesn't seem to be a problem. The error only occurs when Cat.scala is compiled separately from the call to Cat.compose[F,G].Functor.

So, to reproduce, compile Bug.scala into a jar, bug.jar, then try compiling BugRef.scala, referencing bug.jar.

=== What versions of the following are you using? ===

scabug commented 13 years ago

Imported From: https://issues.scala-lang.org/browse/SI-4079?orig=1 Reporter: @pchiusano Attachments:

scabug commented 13 years ago

@pchiusano said: Compile fine alone

scabug commented 13 years ago

@pchiusano said: Bug.scala compiled as a jar

scabug commented 13 years ago

@pchiusano said: Bombs during compilation when linked against the bug jar

scabug commented 13 years ago

@pchiusano said: I found what looks to be a workaround, using [http://groups.google.com/group/scalaz/browse_thread/thread/862543e872b09f3c type lambdas] instead of the ComposeT trait. The working file looks like:

trait Functor[F[_]] {
  def map[A,B](fa: F[A], f: A => B): F[B]
}

case class Compose[F[_],G[_]]() {
  def Functor(implicit f: Functor[F], g: Functor[G]): Functor[({type f[x] = F[G[x]]})#f] =
    new Functor[({type f[x] = F[G[x]]})#f] {
      def map[A,B](c: F[G[A]], h: A => B): F[G[B]] = 
        f.map(c, (x:G[A]) => g.map(x,h))
    }
}

object Cat {
  def compose[F[_],G[_]] = Compose[F,G]()
}

object Functors {
  implicit val List = new Functor[List] { 
    def map[A,B](fa: List[A], f: A => B): List[B] = fa map f
  }
  implicit val Option = new Functor[Option] { 
    def map[A,B](fa: Option[A], f: A => B): Option[B] = fa map f
  }
}

object Main {
  import Functors._
  val cf = Cat.compose[List,Option].Functor
}
scabug commented 13 years ago

@paulp said: I don't know what I'm doing, but poking around at things is sort of a hobby of mine, so:

scala> import Functors._
import Functors._

scala> Cat.compose[List,Option].Functor.map(List(Some(1), Some(2)), (x: Int) => x + 2)
res0: List[Option[Int]] = List(Some(3), Some(4))  

Here's the diff against trunk which made the bad men go away (in this example anyway, I make no claims of generality.)

diff --git a/src/compiler/scala/tools/nsc/symtab/Types.scala b/src/compiler/scala/tools/nsc/symtab/Types.scala
index 9967d07..9bf9ff9 100644
--- a/src/compiler/scala/tools/nsc/symtab/Types.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Types.scala
@@ -3358,7 +3358,8 @@ A type's typeSymbol should never be inspected directly.
               def throwError = abort("" + tp + sym.locationString + " cannot be instantiated from " + pre.widen)

               def instParam(ps: List[Symbol], as: List[Type]): Type = 
-                if (ps.isEmpty) throwError
+                if (ps.isEmpty && as.isEmpty) mapOver(tp)
+                else if (ps.isEmpty) throwError
                 else if (sym eq ps.head)  
                   // @M! don't just replace the whole thing, might be followed by type application
                   appliedType(as.head, args mapConserve (this)) // @M: was as.head   
scabug commented 13 years ago

@paulp said: It makes the bad men go away in #2741 as well, which makes it pretty safe this is a duplicate of that.

scabug commented 13 years ago

@hubertp said: Looks like a duplicate to me as well, but I will let Adriaan to make a final tick.

scabug commented 13 years ago

@pchiusano said: Paul - awesome.

scabug commented 13 years ago

@adriaanm said: i don't want to be the bad guy, but this is a symptomatic fix

the deeper problem, at least for #2741, and most like here as well, is related to a lacking implementation of anonymous type functions I now how to fix it, but it's a big change.

asSeenFrom shouldn't even be trying to instantiate A in Compose, as it defines no such type parameter

scabug commented 13 years ago

@paulp said: Replying to [comment:6 moors]:

i don't want to be the bad guy, but this is a symptomatic fix

You're not the bad guy - I never doubted it.

scabug commented 13 years ago

@paulp said: And I think it's nice that we have a ticket where both bad men and bad guys make appearances. Be on the lookout for Black Bart and his gang of higher kinded existentials!

scabug commented 13 years ago

@paulp said: Replying to [comment:6 moors]:

i don't want to be the bad guy, but this is a symptomatic fix

But an addendum: now having verified that the symptomatic fix does not break anything we have a test for, we shouldn't let its imperfection keep us from committing it: it's a single spectacularly well-localized line, rather a long ways from the worst hack in the compiler. This goes double if the proper fix is indeed heavy and code-touchy.

NOW we'll find out who's the bad guy!

scabug commented 13 years ago

@adriaanm said: (In r24029) introduce NullaryMethodType to disambiguate PolyType

motivation: given def foo[T]: (T, T) and type Foo[T] = (T, T),

foo.info and TypeRef(_, Foo, Nil).normalize are both PolyType(List(T), Pair[T, T])

uncurry has been relying on an ugly hack to distinguish these cases based on ad-hoc kind inference without this distinction, the type alias's info (a type function) would be transformed to PolyType(List(T), MethodType(Nil, Pair[T, T]))

anonymous type functions are being used more often (see #2741, #4017, #4079, #3443, #3106), which makes a proper treatment of PolyTypes more pressing

change to type representation: PolyType(Nil, tp) -> NullaryMethodType(tp) PolyType(tps, tp) -> PolyType(tps, NullaryMethodType(tp)) (if the polytype denoted a polymorphic nullary method)

PolyType(Nil, tp) is now invalid

the kind of a PolyType is * iff its resulttype is a NullaryMethodType or a MethodType (i.e., it's a polymorphic value) in all other cases a PolyType now denotes a type constructor

NullaryMethodType is eliminated during uncurry

pickling: for backwards compatibility, a NullaryMethodType(tp) is still pickled as a PolyType(Nil, tp), unpickling rewrites pre-2.9-pickled PolyTypes according to the expected kind of the unpickled type (similar to what we used to do in uncurry) a pickled PolyType(Nil, restpe) is unpickled to NullaryMethodType(restpe) a pickled PolyType(tps, restpe) is unpickled to PolyType(tps, NullaryMethodType(restpe)) when the type is expected to have kind *

the rewrite probably isn't complete, but was validated by compiling against the old scalacheck jar (which has plenty of polymorphic nullary methods) nevertheless, this commit includes a new scalacheck jar

summary of the refactoring:

TODO: scalap and eclipse

review by odersky, rytz

scabug commented 13 years ago

@adriaanm said: (In r24091) closes #2741, #4079: pickling now ensures that a local type param with a non-local owner, which will thus get a localized owner, will only get a class as its localized owner if its old owner was a class (otherwise, NoSymbol) this ensures that asSeenFrom does not treat typerefs to this symbol differently after pickling.

todo: should we pro-actively set the owner of these type params to something else than the type alias that they originate from? see notes in typeFunAnon

review by odersky