Skip to content

Lazy in constructor parameter causes Nil$.head #4058

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

Closed
sir-wabbit opened this issue Mar 1, 2018 · 4 comments
Closed

Lazy in constructor parameter causes Nil$.head #4058

sir-wabbit opened this issue Mar 1, 2018 · 4 comments

Comments

@sir-wabbit
Copy link

object App {
  def main(args: Array[String]): Unit = {
    class T(lazy val a: Int)
    new T(1)
  }
}

////////////////////////////////////////////////////////

Exception in thread "main" java.util.NoSuchElementException: head of empty list
	at scala.collection.immutable.Nil$.head(List.scala:428)
	at scala.collection.immutable.Nil$.head(List.scala:425)
	at dotty.tools.dotc.transform.SymUtils$.loop$1(SymUtils.scala:83)
	at dotty.tools.dotc.transform.SymUtils$.subst$extension(SymUtils.scala:85)
	at dotty.tools.dotc.transform.Constructors.$anonfun$transformTemplate$3(Constructors.scala:230)
	at scala.collection.immutable.List.flatMap(List.scala:335)
	at dotty.tools.dotc.transform.Constructors.transformTemplate(Constructors.scala:222)
	at dotty.tools.dotc.transform.MegaPhase.goTemplate(MegaPhase.scala:906)
	at dotty.tools.dotc.transform.MegaPhase.transformUnnamed$1(MegaPhase.scala:318)
	at dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:365)
	at dotty.tools.dotc.transform.MegaPhase.transformNamed$1(MegaPhase.scala:234)
	at dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:364)
	at dotty.tools.dotc.transform.MegaPhase.transformStat$1(MegaPhase.scala:373)
	at dotty.tools.dotc.transform.MegaPhase.$anonfun$transformStats$2(MegaPhase.scala:378)
	at scala.collection.immutable.List.mapConserve(List.scala:176)
	at dotty.tools.dotc.transform.MegaPhase.transformStats(MegaPhase.scala:378)
	at dotty.tools.dotc.transform.MegaPhase.transformUnnamed$1(MegaPhase.scala:264)
	at dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:365)
	at dotty.tools.dotc.transform.MegaPhase.mapDefDef$1(MegaPhase.scala:228)
	at dotty.tools.dotc.transform.MegaPhase.transformNamed$1(MegaPhase.scala:231)
	at dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:364)
	at dotty.tools.dotc.transform.MegaPhase.transformStat$1(MegaPhase.scala:373)
	at dotty.tools.dotc.transform.MegaPhase.$anonfun$transformStats$2(MegaPhase.scala:378)
	at scala.collection.immutable.List.mapConserve(List.scala:176)
	at dotty.tools.dotc.transform.MegaPhase.transformStats(MegaPhase.scala:378)
	at dotty.tools.dotc.transform.MegaPhase.transformUnnamed$1(MegaPhase.scala:317)
	at dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:365)
	at dotty.tools.dotc.transform.MegaPhase.transformNamed$1(MegaPhase.scala:234)
	at dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:364)
	at dotty.tools.dotc.transform.MegaPhase.transformStat$1(MegaPhase.scala:373)
	at dotty.tools.dotc.transform.MegaPhase.$anonfun$transformStats$2(MegaPhase.scala:378)
	at scala.collection.immutable.List.mapConserve(List.scala:176)
	at dotty.tools.dotc.transform.MegaPhase.transformStats(MegaPhase.scala:378)
	at dotty.tools.dotc.transform.MegaPhase.mapPackage$1(MegaPhase.scala:334)
	at dotty.tools.dotc.transform.MegaPhase.transformUnnamed$1(MegaPhase.scala:337)
	at dotty.tools.dotc.transform.MegaPhase.transformTree(MegaPhase.scala:365)
	at dotty.tools.dotc.transform.MegaPhase.transformUnit(MegaPhase.scala:384)
	at dotty.tools.dotc.transform.MegaPhase.run(MegaPhase.scala:396)
	at dotty.tools.dotc.core.Phases$Phase.$anonfun$runOn$1(Phases.scala:293)
	at scala.collection.immutable.List.map(List.scala:283)
	at dotty.tools.dotc.core.Phases$Phase.runOn(Phases.scala:291)
	at dotty.tools.dotc.core.Phases$Phase.runOn$(Phases.scala:290)
	at dotty.tools.dotc.transform.MegaPhase.runOn(MegaPhase.scala:138)
	at dotty.tools.dotc.Run.$anonfun$compileUnits$3(Run.scala:171)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:12)
	at dotty.tools.dotc.util.Stats$.trackTime(Stats.scala:47)
	at dotty.tools.dotc.Run.$anonfun$compileUnits$2(Run.scala:168)
	at dotty.tools.dotc.Run.$anonfun$compileUnits$2$adapted(Run.scala:166)
	at scala.collection.IndexedSeqOptimized.foreach(IndexedSeqOptimized.scala:32)
	at scala.collection.IndexedSeqOptimized.foreach$(IndexedSeqOptimized.scala:29)
	at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:191)
	at dotty.tools.dotc.Run.runPhases$1(Run.scala:166)
	at dotty.tools.dotc.Run.$anonfun$compileUnits$1(Run.scala:191)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:12)
	at dotty.tools.dotc.util.Stats$.maybeMonitored(Stats.scala:88)
	at dotty.tools.dotc.Run.compileUnits(Run.scala:149)
	at dotty.tools.dotc.Run.compileSources(Run.scala:136)
	at dotty.tools.dotc.Run.compile(Run.scala:120)
	at dotty.tools.dotc.Driver.doCompile(Driver.scala:29)
	at dotty.tools.dotc.Driver.process(Driver.scala:127)
	at dotty.tools.dotc.Driver.process(Driver.scala:96)
	at dotty.tools.dotc.Driver.process(Driver.scala:108)
	at dotty.tools.dotc.Driver.main(Driver.scala:135)
	at dotty.tools.dotc.Main.main(Main.scala)
@Blaisorblade
Copy link
Contributor

TL;DR, Scalac complains that lazy modifier not allowed here. Use call-by-name parameters instead, while Dotty appears to lack this check (or not have it early enough?).

Taking inspiration from Compiler.scala, maybe this could be checked by a new miniphase before RefChecks (say, like CheckStatic?)

Looking at the trace from the final call, the first interesting (non-obviously-correct) line is Constructors.scala:230, because subst is correct if its arguments are lists of the same length, but the call in Constructors.scala:230 is

val param = acc.subst(accessors, paramSyms)

A breakpoint there shows that paramSyms contains val a while accessors contains method a and a@lzy1, causing the crash. Given how late is the Constructors miniphase, I'm confident the issue should be fixed earlier.

@Blaisorblade
Copy link
Contributor

Let's see if I make any progress here.

@allanrenucci
Copy link
Contributor

allanrenucci commented Mar 1, 2018

I don't think we should introduce a new mini-phase. The parser should reject it (like in scalac). Probably here: https://github.com/lampepfl/dotty/blob/765550305f945a0d53a93ff1fabc9de56a3f0386/compiler/src/dotty/tools/dotc/parsing/Parsers.scala#L1842

Simpler test case: class T(lazy val a: Int)

@Blaisorblade
Copy link
Contributor

Can try that, might be even easier!

Blaisorblade added a commit to dotty-staging/dotty that referenced this issue Mar 4, 2018
I noticed `sealed` while reviewing `modifiers`.
Blaisorblade added a commit to dotty-staging/dotty that referenced this issue Mar 5, 2018
I noticed `sealed` while reviewing `modifiers`.
Blaisorblade added a commit to dotty-staging/dotty that referenced this issue Mar 7, 2018
I noticed `sealed` while reviewing `modifiers`.
Blaisorblade added a commit that referenced this issue Mar 16, 2018
Fix #4058: Fix two problems with flag combinations
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants