Skip to content

Commit 835d26f

Browse files
Fix scala#2772: Special case Devalify for java.lang.System.*
Also update DropNoEffects to keep Select on static java field, to make sure a Java class doesn't end up in value position. That's another bug, unrelated to
1 parent 888744f commit 835d26f

File tree

4 files changed

+32
-3
lines changed

4 files changed

+32
-3
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,7 @@ class Definitions {
483483
lazy val BoxedFloatModule = ctx.requiredModule("java.lang.Float")
484484
lazy val BoxedDoubleModule = ctx.requiredModule("java.lang.Double")
485485
lazy val BoxedUnitModule = ctx.requiredModule("java.lang.Void")
486+
lazy val SystemModule = ctx.requiredModule("java.lang.System")
486487

487488
lazy val ByNameParamClass2x = enterSpecialPolyClass(tpnme.BYNAME_PARAM_CLASS, Covariant, Seq(AnyType))
488489
lazy val EqualsPatternClass = enterSpecialPolyClass(tpnme.EQUALS_PATTERN, EmptyFlags, Seq(AnyType))

compiler/src/dotty/tools/dotc/transform/localopt/Devalify.scala

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,10 +166,12 @@ class Devalify extends Optimisation {
166166

167167
def readingOnlyVals(t: Tree)(implicit ctx: Context): Boolean = dropCasts(t) match {
168168
case Typed(exp, _) => readingOnlyVals(exp)
169+
169170
case TypeApply(fun @ Select(rec, _), List(tp)) =>
170171
if ((fun.symbol eq defn.Any_asInstanceOf) && rec.tpe.derivesFrom(tp.tpe.classSymbol))
171172
readingOnlyVals(rec)
172173
else false
174+
173175
case Apply(Select(rec, _), Nil) =>
174176
def isGetterOfAImmutableField = t.symbol.isGetter && !t.symbol.is(Mutable)
175177
def isCaseClassWithVar = t.symbol.info.decls.exists(_.is(Mutable))
@@ -182,8 +184,10 @@ class Devalify extends Optimisation {
182184
if (isGetterOfAImmutableField || isAccessingProductField || isImmutableCaseAccessor)
183185
readingOnlyVals(rec)
184186
else false
187+
185188
case Select(rec, _) if t.symbol.is(Method) =>
186-
if (t.symbol.isGetter && !t.symbol.is(Mutable)) readingOnlyVals(rec) // getter of a immutable field
189+
if (t.symbol.isGetter && !t.symbol.is(Mutable))
190+
readingOnlyVals(rec) // getter of a immutable field
187191
else if (t.symbol.owner.derivesFrom(defn.ProductClass) && t.symbol.owner.is(CaseClass) && t.symbol.name.isSelectorName) {
188192
def isImmutableField = {
189193
val fieldId = t.symbol.name.toString.drop(1).toInt - 1
@@ -194,13 +198,22 @@ class Devalify extends Optimisation {
194198
} else if (t.symbol.is(CaseAccessor) && !t.symbol.is(Mutable))
195199
readingOnlyVals(rec)
196200
else false
201+
197202
case Select(qual, _) if !t.symbol.is(Mutable) =>
198-
readingOnlyVals(qual)
203+
if (t.symbol == defn.SystemModule) {
204+
// System.in is static final fields that, for legacy reasons, must be
205+
// allowed to be changed by the methods System.setIn...
206+
// https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.5.4
207+
false
208+
} else
209+
readingOnlyVals(qual)
210+
199211
case t: Ident if !t.symbol.is(Mutable) && !t.symbol.is(Method) && !t.symbol.info.dealias.isInstanceOf[ExprType] =>
200212
desugarIdent(t) match {
201213
case Some(t) => readingOnlyVals(t)
202214
case None => true
203215
}
216+
204217
case t: This => true
205218
// null => false, or the following fails devalify:
206219
// trait I {

compiler/src/dotty/tools/dotc/transform/localopt/DropNoEffects.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ class DropNoEffects(val simplifyPhase: Simplify) extends Optimisation {
8787
keepOnlySideEffects(rec)
8888

8989
// !name.eq(nme.TYPE_) && // Keep the .TYPE added by ClassOf, would be needed for AfterErasure
90-
case s @ Select(qual, name) if !s.symbol.is(Mutable | Lazy | Method) =>
90+
// Without is(JavaStatic), { System.out } becomes { System }, but "Java class can't be used as value"
91+
case s @ Select(qual, name) if !s.symbol.is(Mutable | Lazy | Method | JavaStatic) =>
9192
keepOnlySideEffects(qual)
9293

9394
case Block(List(t: DefDef), s: Closure) =>

tests/run/2772.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import java.io.OutputStream
2+
3+
object Test {
4+
def main(args: Array[String]): Unit = {
5+
val oldErr = System.err
6+
System.setErr(null) // setOut(null) confuses the testing framework...
7+
val a = () => foo(oldErr)
8+
a()
9+
}
10+
11+
def foo(err: OutputStream): Unit = {
12+
err.write(0)
13+
}
14+
}

0 commit comments

Comments
 (0)