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

Typer crash related to literal singleton types #10768

Closed
smarter opened this issue Mar 10, 2018 · 5 comments · Fixed by scala/scala#7620
Closed

Typer crash related to literal singleton types #10768

smarter opened this issue Mar 10, 2018 · 5 comments · Fixed by scala/scala#7620

Comments

@smarter
Copy link
Member

smarter commented Mar 10, 2018

I haven't been able to reproduce this outside of the REPL, I assume this is related to literal singleton types since val a = 1; foo(a) does not trigger the crash:

Welcome to Scala 2.13.0-M3 (OpenJDK 64-Bit Server VM, Java 1.8.0_151).
Type in expressions for evaluation. Or try :help.

scala> type Id[T] = T
defined type alias Id

scala> def foo(x: Int): Id[x.type] = x
foo: (x: Int)Id[x.type]

scala> foo(1)
scala.reflect.internal.Types$TypeError: assignment to non variable
        at scala.tools.nsc.typechecker.Contexts$ThrowingReporter.handleError(Contexts.scala:1414)
        at scala.tools.nsc.typechecker.Contexts$ContextReporter.issue(Contexts.scala:1266)
        at scala.tools.nsc.typechecker.Contexts$Context.issue(Contexts.scala:584)
        at scala.tools.nsc.typechecker.ContextErrors$ErrorUtils$.issueTypeError(ContextErrors.scala:98)
        at scala.tools.nsc.typechecker.ContextErrors$ErrorUtils$.issueNormalTypeError(ContextErrors.scala:91)
        at scala.tools.nsc.typechecker.ContextErrors$TyperContextErrors$TyperErrorGen$.AssignmentError(ContextErrors.scala:321)
        at scala.tools.nsc.typechecker.Typers$Typer.fail$2(Typers.scala:4413)
        at scala.tools.nsc.typechecker.Typers$Typer.typedAssign$1(Typers.scala:4416)
        at scala.tools.nsc.typechecker.Typers$Typer.typedOutsidePatternMode$1(Typers.scala:5628)
        at scala.tools.nsc.typechecker.Typers$Typer.typedInAnyMode$1(Typers.scala:5657)
        at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5664)
        at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5701)
        at scala.tools.nsc.typechecker.Typers$Typer.typedInternal(Typers.scala:5733)
        at scala.tools.nsc.typechecker.Typers$Typer.body$2(Typers.scala:5674)
        at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5679)
        at scala.tools.nsc.typechecker.Typers$Typer.typedByValueExpr(Typers.scala:5764)
        at scala.tools.nsc.typechecker.Typers$Typer.typedStat$1(Typers.scala:3127)
        at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$typedStats$10(Typers.scala:3271)
        at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:3271)
        at scala.tools.nsc.typechecker.Typers$Typer.typedBlock(Typers.scala:2461)
        at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$typed1$98(Typers.scala:5622)
        at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$typerWithLocalContext$1(Typers.scala:493)
        at scala.tools.nsc.typechecker.Typers$Typer.typedOutsidePatternMode$1(Typers.scala:493)
        at scala.tools.nsc.typechecker.Typers$Typer.typedInAnyMode$1(Typers.scala:5657)
        at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5664)
        at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5701)
        at scala.tools.nsc.typechecker.Typers$Typer.typedInternal(Typers.scala:5733)
        at scala.tools.nsc.typechecker.Typers$Typer.body$2(Typers.scala:5674)
        at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5679)
        at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5775)
        at scala.tools.nsc.typechecker.Typers$Typer.typedIf$1(Typers.scala:4449)
        at scala.tools.nsc.typechecker.Typers$Typer.typedOutsidePatternMode$1(Typers.scala:5623)
        at scala.tools.nsc.typechecker.Typers$Typer.typedInAnyMode$1(Typers.scala:5657)
        at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5664)
        at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5701)
        at scala.tools.nsc.typechecker.Typers$Typer.typedInternal(Typers.scala:5733)
        at scala.tools.nsc.typechecker.Typers$Typer.body$2(Typers.scala:5674)
        at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5679)
        at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$typedArg$1(Typers.scala:3287)
        at scala.tools.nsc.typechecker.Typers$Typer.typedArg(Typers.scala:484)
        at scala.tools.nsc.typechecker.Typers$Typer.typedArgToPoly$1(Typers.scala:3701)
        at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$doTypedApply$36(Typers.scala:3709)
        at scala.tools.nsc.typechecker.Typers$Typer.handlePolymorphicCall$1(Typers.scala:3709)
        at scala.tools.nsc.typechecker.Typers$Typer.doTypedApply(Typers.scala:3720)
        at scala.tools.nsc.typechecker.Typers$Typer.normalTypedApply$1(Typers.scala:4792)
        at scala.tools.nsc.typechecker.Typers$Typer.typedApply$1(Typers.scala:4820)
        at scala.tools.nsc.typechecker.Typers$Typer.typedInAnyMode$1(Typers.scala:5647)
        at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5664)
        at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5701)
        at scala.tools.nsc.typechecker.Typers$Typer.typedInternal(Typers.scala:5733)
        at scala.tools.nsc.typechecker.Typers$Typer.body$2(Typers.scala:5674)
        at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5679)
        at scala.tools.nsc.typechecker.Typers$Typer.typedByValueExpr(Typers.scala:5764)
        at scala.tools.nsc.typechecker.Typers$Typer.typedStat$1(Typers.scala:3127)
        at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$typedStats$10(Typers.scala:3271)
        at scala.tools.nsc.typechecker.Typers$Typer.typedStats(Typers.scala:3271)
        at scala.tools.nsc.typechecker.Typers$Typer.typedBlock(Typers.scala:2461)
        at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$typed1$98(Typers.scala:5622)
        at scala.tools.nsc.typechecker.Typers$Typer.$anonfun$typerWithLocalContext$1(Typers.scala:493)
        at scala.tools.nsc.typechecker.Typers$Typer.typedOutsidePatternMode$1(Typers.scala:493)
        at scala.tools.nsc.typechecker.Typers$Typer.typedInAnyMode$1(Typers.scala:5657)
        at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5664)
        at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5701)
        at scala.tools.nsc.typechecker.Typers$Typer.typedInternal(Typers.scala:5733)
        at scala.tools.nsc.typechecker.Typers$Typer.body$2(Typers.scala:5674)
        at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5679)
        at scala.tools.nsc.typechecker.Typers$Typer.transformedOrTyped(Typers.scala:5915)
        at scala.tools.nsc.typechecker.Typers$Typer.typedDefDef(Typers.scala:2292)
        at scala.tools.nsc.typechecker.Typers$Typer.typedMemberDef$1(Typers.scala:5601)
        at scala.tools.nsc.typechecker.Typers$Typer.typed1(Typers.scala:5663)
        at scala.tools.nsc.typechecker.Typers$Typer.runTyper$1(Typers.scala:5701)
        at scala.tools.nsc.typechecker.Typers$Typer.typedInternal(Typers.scala:5733)
        at scala.tools.nsc.typechecker.Typers$Typer.body$2(Typers.scala:5674)
        at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5679)
        at scala.tools.nsc.typechecker.Typers$Typer.typed(Typers.scala:5760)
        at scala.tools.nsc.typechecker.Typers$Typer.typedPos(Typers.scala:5767)
        at scala.tools.nsc.transform.Fields$FieldsTransformer.typedPos(Fields.scala:541)
        at scala.tools.nsc.transform.AccessorSynthesis$CheckedAccessorTreeSynthesis$SynthLazyAccessorsIn.$anonfun$expandLazyClassMember$2(AccessorSynthesis.scala:298)
        at scala.tools.nsc.transform.AccessorSynthesis$CheckedAccessorTreeSynthesis$SynthLazyAccessorsIn.$anonfun$expandLazyClassMember$1(AccessorSynthesis.scala:298)
        at scala.tools.nsc.transform.AccessorSynthesis$CheckedAccessorTreeSynthesis$SynthLazyAccessorsIn.expandLazyClassMember(AccessorSynthesis.scala:298)
        at scala.tools.nsc.transform.Fields$FieldsTransformer.transform(Fields.scala:736)
        at scala.tools.nsc.transform.Fields$FieldsTransformer.transformTermsAtExprOwner(Fields.scala:763)
        at scala.tools.nsc.transform.Fields$FieldsTransformer.$anonfun$transformStats$3(Fields.scala:776)
        at scala.tools.nsc.transform.Fields$FieldsTransformer.transformStats(Fields.scala:776)
        at scala.tools.nsc.transform.Fields$FieldsTransformer.transformStats(Fields.scala:540)
        at scala.reflect.internal.Trees$Template.transform(Trees.scala:486)
        at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.$anonfun$transform$1(TypingTransformers.scala:40)
        at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:30)
        at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:25)
        at scala.tools.nsc.transform.Fields$FieldsTransformer.transform(Fields.scala:755)
        at scala.tools.nsc.transform.Fields$FieldsTransformer.transform(Fields.scala:540)
        at scala.reflect.api.Trees$Transformer.transformTemplate(Trees.scala:2565)
        at scala.reflect.internal.Trees$ClassDef.$anonfun$transform$2(Trees.scala:317)
        at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:30)
        at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:25)
        at scala.reflect.internal.Trees$ClassDef.transform(Trees.scala:316)
        at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:44)
        at scala.tools.nsc.transform.Fields$FieldsTransformer.transform(Fields.scala:746)
        at scala.tools.nsc.transform.Fields$FieldsTransformer.$anonfun$transformStats$4(Fields.scala:776)
        at scala.tools.nsc.transform.Fields$FieldsTransformer.transformStats(Fields.scala:776)
        at scala.tools.nsc.transform.Fields$FieldsTransformer.transformStats(Fields.scala:540)
        at scala.reflect.internal.Trees$PackageDef.$anonfun$transform$1(Trees.scala:298)
        at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:30)
        at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:25)
        at scala.reflect.internal.Trees$PackageDef.transform(Trees.scala:298)
        at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.$anonfun$transform$2(TypingTransformers.scala:42)
        at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.atOwner(TypingTransformers.scala:30)
        at scala.tools.nsc.transform.TypingTransformers$TypingTransformer.transform(TypingTransformers.scala:25)
        at scala.tools.nsc.transform.Fields$FieldsTransformer.transform(Fields.scala:755)
        at scala.tools.nsc.ast.Trees$Transformer.transformUnit(Trees.scala:155)
        at scala.tools.nsc.transform.Transform$Phase.apply(Transform.scala:30)
        at scala.tools.nsc.Global$GlobalPhase.$anonfun$applyPhase$1(Global.scala:436)
        at scala.tools.nsc.Global$GlobalPhase.applyPhase(Global.scala:429)
        at scala.tools.nsc.Global$GlobalPhase.$anonfun$run$1(Global.scala:400)
        at scala.tools.nsc.Global$GlobalPhase.$anonfun$run$1$adapted(Global.scala:400)
        at scala.collection.Iterator.foreach(Iterator.scala:938)
        at scala.collection.Iterator.foreach$(Iterator.scala:938)
        at scala.collection.AbstractIterator.foreach(Iterator.scala:1426)
        at scala.tools.nsc.Global$GlobalPhase.run(Global.scala:400)
        at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1453)
        at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1437)
        at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.compile(IMain.scala:711)
        at scala.tools.nsc.interpreter.IMain$Request.$anonfun$compile$7(IMain.scala:918)
        at scala.runtime.java8.JFunction0$mcZ$sp.apply(JFunction0$mcZ$sp.java:12)
        at scala.tools.nsc.interpreter.IMain.$anonfun$withoutWarnings$1(IMain.scala:95)
        at scala.tools.nsc.interpreter.shell.ReplReporterImpl.withoutPrintingResults(Reporter.scala:44)
        at scala.tools.nsc.interpreter.IMain.withoutWarnings(IMain.scala:90)
        at scala.tools.nsc.interpreter.IMain$Request.compile(IMain.scala:918)
        at scala.tools.nsc.interpreter.IMain.$anonfun$compile$2(IMain.scala:504)
        at scala.tools.nsc.interpreter.IMain.compile(IMain.scala:504)
        at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:498)
        at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:481)
        at scala.tools.nsc.interpreter.shell.ILoop.interpretStartingWith(ILoop.scala:892)
        at scala.tools.nsc.interpreter.shell.ILoop.command(ILoop.scala:750)
        at scala.tools.nsc.interpreter.shell.ILoop.processLine(ILoop.scala:455)
        at scala.tools.nsc.interpreter.shell.ILoop.loop(ILoop.scala:478)
        at scala.tools.nsc.interpreter.shell.ILoop.$anonfun$run$1(ILoop.scala:985)
        at scala.tools.nsc.interpreter.shell.ILoop.run(ILoop.scala:950)
        at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:80)
        at scala.tools.nsc.MainGenericRunner.run$1(MainGenericRunner.scala:83)
        at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:94)
        at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:99)
        at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
@smarter
Copy link
Member Author

smarter commented Mar 10, 2018

Hmm, looks like this is the same bug as #10358, but now it can be triggered without using an external library.

@SethTisue
Copy link
Member

looks like this is the same bug as #10358

closed that one, let's consolidate here

@milessabin
Copy link

milessabin commented Mar 21, 2018

This turns out not to be REPL only ... it's an artefact of the fact that the REPL result is a final lazy val. We can reproduce the underlying problem with,

object Test {
  type Id[T] = T
  def foo(x: Int): Id[x.type] = x

  lazy val result = foo(1)
}

This is due to the inferred FoldableConstantType for T being hidden in the Id type constructor so not deconsted early. It then reemerges during Uncurry where Id[Int(1)] is dealiased to Int(1). Subsequently during Fields this constant type interferes with synthesis of the various fields and accessors associated with the lazy val.

This fix is to deconst more thoroughly earlier. PR incoming ...

milessabin added a commit to milessabin/scala that referenced this issue Mar 21, 2018
Prior to this commit deconst only converted a top level
FoldableConstantType to a LiteralType. This left FoldableConstantTypes
possibly embedded somewhere in a TypeRef or suchlike where it might
reemerge unexpectedly at a later point. This could result in result
bogus inlining and the elimination of expected side-effects. For
instance, no output is produced when the following is run,

  class Box[T](t: T) {
    def foo: T = {
      println("effect")
      t
    }
  }

  object Box {
    def apply(x: String): Box[x.type] = new Box[x.type](x)
  }

  val bar = Box("foo").foo

The cause of the problem is that the Box value is created with its type
parameter T instantiated as x.type which is computed as the
ConstantType(Literal("foo")). This results in the subsequent application
of foo also being seen as having a constant type and so being eligible
to be inlined as the constant value, eliding the object creation, method
call and effect. So the definition of bar,

  val bar = Box("foo").foo

was transformed to,

  val bar: String = "foo";

Note that although the earlier mention of LiteralType suggests that this
is related to the literal types extension, this problem is present in
compiler versions going back to 2.10.x at least.

The fix is for deconst to recurse through the type eliminating all
FoldableConstantType components. This fixes scala/bug#10788 and almost
incidentally fixes scala/bug#10768. In,

  object Test {
    type Id[T] = T
    def foo(x: Int): Id[x.type] = x

    lazy val result = foo(1)
  }

the inferred FoldableConstantType for T is hidden in the Id type
constructor and so is not deconsted. It then reemerges during Uncurry
where Id[Int(1)] is dealiased to Int(1). Subsequently during Fields this
constant type interferes with synthesis of the various fields and
accessors associated with the lazy val.

This fix also changes the output of run/macro-reify-unreify. I've been
able to convince myself that the previous output was incorrect due to
the typer seeing Expr[String("world")](...).splice as having the
constant type String("world") and hence inlining the literal String
without invoking any of the splicing machinery, ie. it's an actual
instance of the issue this commit fixes.
@milessabin
Copy link

Fixed in scala/scala#6452.

@milessabin
Copy link

Incidentally, this isn't a regression ... it's present at least as far back as 2.10.x, although prior to literal types you have to use String,

miles@tarski:~% scala
Welcome to Scala 2.12.4-20171023-143547-unknown (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_162).
Type in expressions for evaluation. Or try :help.

scala> type Id[T] = T
defined type alias Id

scala> def foo(x: String): Id[x.type] = x
foo: (x: String)Id[x.type]

scala> foo("bar")
scala.reflect.internal.Types$TypeError: assignment to non variable
        at scala.tools.nsc.typechecker.Contexts$ThrowingReporter.handleError(Contexts.scala:1426)
        at scala.tools.nsc.typechecker.Contexts$ContextReporter.issue(Contexts.scala:1278)
        ...

@SethTisue SethTisue changed the title REPL crash related to literal singleton types Typer crash related to literal singleton types Mar 22, 2018
@lrytz lrytz added the has PR label Apr 19, 2018
@SethTisue SethTisue modified the milestones: 2.13.0-M4, 2.13.0-M5 May 9, 2018
milessabin added a commit to milessabin/scala that referenced this issue Aug 6, 2018
Prior to this commit deconst only converted a top level
FoldableConstantType to a LiteralType. This left FoldableConstantTypes
possibly embedded somewhere in a TypeRef or suchlike where it might
reemerge unexpectedly at a later point. This could result in result
bogus inlining and the elimination of expected side-effects. For
instance, no output is produced when the following is run,

  class Box[T](t: T) {
    def foo: T = {
      println("effect")
      t
    }
  }

  object Box {
    def apply(x: String): Box[x.type] = new Box[x.type](x)
  }

  val bar = Box("foo").foo

The cause of the problem is that the Box value is created with its type
parameter T instantiated as x.type which is computed as the
ConstantType(Literal("foo")). This results in the subsequent application
of foo also being seen as having a constant type and so being eligible
to be inlined as the constant value, eliding the object creation, method
call and effect. So the definition of bar,

  val bar = Box("foo").foo

was transformed to,

  val bar: String = "foo";

Note that although the earlier mention of LiteralType suggests that this
is related to the literal types extension, this problem is present in
compiler versions going back to 2.10.x at least.

The fix is for deconst to recurse through the type eliminating all
FoldableConstantType components. This fixes scala/bug#10788 and almost
incidentally fixes scala/bug#10768. In,

  object Test {
    type Id[T] = T
    def foo(x: Int): Id[x.type] = x

    lazy val result = foo(1)
  }

the inferred FoldableConstantType for T is hidden in the Id type
constructor and so is not deconsted. It then reemerges during Uncurry
where Id[Int(1)] is dealiased to Int(1). Subsequently during Fields this
constant type interferes with synthesis of the various fields and
accessors associated with the lazy val.

This fix also changes the output of run/macro-reify-unreify. I've been
able to convince myself that the previous output was incorrect due to
the typer seeing Expr[String("world")](...).splice as having the
constant type String("world") and hence inlining the literal String
without invoking any of the splicing machinery, ie. it's an actual
instance of the issue this commit fixes.
@SethTisue SethTisue modified the milestones: 2.13.0-M5, 2.13.0-RC1 Aug 15, 2018
adriaanm added a commit to adriaanm/scala that referenced this issue Jan 9, 2019
constant folding time is over (that happens before, during typers),
but constant types have not yet been eliminated (that happens during erasure).

So, we must deconst the result type of methods to avoid treating
their application as a constant...

Fix scala/bug#10768
adriaanm pushed a commit to adriaanm/scala that referenced this issue Jan 9, 2019
(Commit message slightly reworded by adriaanm. While preserving Miles's
analysis & tests, my fix ended up differently.)

Without proper deconsting, no output is produced by:

  class Box[T](t: T) {
    def foo: T = {
      println("effect")
      t
    }
  }

  object Box {
    def apply(x: String): Box[x.type] = new Box[x.type](x)
  }

  val bar = Box("foo").foo

The cause of the problem is that the Box value is created with its type
parameter T instantiated as x.type which is computed as the
ConstantType(Literal("foo")). This results in the subsequent application
of foo also being seen as having a constant type and so being eligible
to be inlined as the constant value, eliding the object creation, method
call and effect. So the definition of bar,

  val bar = Box("foo").foo

was transformed to,

  val bar: String = "foo";

Note that although the earlier mention of LiteralType suggests that this
is related to the literal types extension, this problem is present in
compiler versions going back to 2.10.x at least.

Another occurrence of this bug is seen in scala/bug#10768:

  object Test {
    type Id[T] = T
    def foo(x: Int): Id[x.type] = x

    lazy val result = foo(1)
  }

the inferred FoldableConstantType for T is hidden in the Id type
constructor and so is not deconsted. It then reemerges during Uncurry
where Id[Int(1)] is dealiased to Int(1). Subsequently during Fields this
constant type interferes with synthesis of the various fields and
accessors associated with the lazy val.

This also affects run/macro-reify-unreify. I've been
able to convince myself that the previous output was incorrect due to
the typer seeing Expr[String("world")](...).splice as having the
constant type String("world") and hence inlining the literal String
without invoking any of the splicing machinery, ie. it's an actual
instance of the issue this commit fixes.
adriaanm added a commit to adriaanm/scala that referenced this issue Jan 9, 2019
constant folding time is over (that happens before, during typers),
but constant types have not yet been eliminated (that happens during erasure).

So, we must deconst the result type of methods to avoid treating
their application as a constant...

Fix scala/bug#10768
adriaanm pushed a commit to adriaanm/scala that referenced this issue Jan 9, 2019
(Commit message slightly reworded by adriaanm. While preserving Miles's
analysis & tests, my fix ended up differently.)

Without proper deconsting, no output is produced by:

  class Box[T](t: T) {
    def foo: T = {
      println("effect")
      t
    }
  }

  object Box {
    def apply(x: String): Box[x.type] = new Box[x.type](x)
  }

  val bar = Box("foo").foo

The cause of the problem is that the Box value is created with its type
parameter T instantiated as x.type which is computed as the
ConstantType(Literal("foo")). This results in the subsequent application
of foo also being seen as having a constant type and so being eligible
to be inlined as the constant value, eliding the object creation, method
call and effect. So the definition of bar,

  val bar = Box("foo").foo

was transformed to,

  val bar: String = "foo";

Note that although the earlier mention of LiteralType suggests that this
is related to the literal types extension, this problem is present in
compiler versions going back to 2.10.x at least.

Another occurrence of this bug is seen in scala/bug#10768:

  object Test {
    type Id[T] = T
    def foo(x: Int): Id[x.type] = x

    lazy val result = foo(1)
  }

the inferred FoldableConstantType for T is hidden in the Id type
constructor and so is not deconsted. It then reemerges during Uncurry
where Id[Int(1)] is dealiased to Int(1). Subsequently during Fields this
constant type interferes with synthesis of the various fields and
accessors associated with the lazy val.

This also affects run/macro-reify-unreify. I've been
able to convince myself that the previous output was incorrect due to
the typer seeing Expr[String("world")](...).splice as having the
constant type String("world") and hence inlining the literal String
without invoking any of the splicing machinery, ie. it's an actual
instance of the issue this commit fixes.
adriaanm pushed a commit to adriaanm/scala that referenced this issue Jan 18, 2019
(Commit message slightly reworded by adriaanm. While preserving Miles's
analysis & tests, my fix ended up differently.)

Without proper deconsting, no output is produced by:

  class Box[T](t: T) {
    def foo: T = {
      println("effect")
      t
    }
  }

  object Box {
    def apply(x: String): Box[x.type] = new Box[x.type](x)
  }

  val bar = Box("foo").foo

The cause of the problem is that the Box value is created with its type
parameter T instantiated as x.type which is computed as the
ConstantType(Literal("foo")). This results in the subsequent application
of foo also being seen as having a constant type and so being eligible
to be inlined as the constant value, eliding the object creation, method
call and effect. So the definition of bar,

  val bar = Box("foo").foo

was transformed to,

  val bar: String = "foo";

Note that although the earlier mention of LiteralType suggests that this
is related to the literal types extension, this problem is present in
compiler versions going back to 2.10.x at least.

Another occurrence of this bug is seen in scala/bug#10768:

  object Test {
    type Id[T] = T
    def foo(x: Int): Id[x.type] = x

    lazy val result = foo(1)
  }

the inferred FoldableConstantType for T is hidden in the Id type
constructor and so is not deconsted. It then reemerges during Uncurry
where Id[Int(1)] is dealiased to Int(1). Subsequently during Fields this
constant type interferes with synthesis of the various fields and
accessors associated with the lazy val.

This also affects run/macro-reify-unreify. I've been
able to convince myself that the previous output was incorrect due to
the typer seeing Expr[String("world")](...).splice as having the
constant type String("world") and hence inlining the literal String
without invoking any of the splicing machinery, ie. it's an actual
instance of the issue this commit fixes.
@adriaanm adriaanm self-assigned this Feb 4, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment