@@ -148,7 +148,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
148
148
// Scoped state ------------------------------------------------------------
149
149
// Per class body
150
150
val currentClassSym = new ScopedVar [Symbol ]
151
- private val unexpectedMutatedFields = new ScopedVar [mutable.Set [Symbol ]]
151
+ private val fieldsMutatedInCurrentClass = new ScopedVar [mutable.Set [Name ]]
152
152
private val generatedSAMWrapperCount = new ScopedVar [VarBox [Int ]]
153
153
154
154
private def currentClassType = encodeClassType(currentClassSym)
@@ -213,7 +213,7 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
213
213
private def nestedGenerateClass [T ](clsSym : Symbol )(body : => T ): T = {
214
214
withScopedVars(
215
215
currentClassSym := clsSym,
216
- unexpectedMutatedFields := mutable.Set .empty,
216
+ fieldsMutatedInCurrentClass := mutable.Set .empty,
217
217
generatedSAMWrapperCount := new VarBox (0 ),
218
218
currentMethodSym := null ,
219
219
thisLocalVarIdent := null ,
@@ -339,8 +339,8 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
339
339
340
340
if (! isPrimitive && ! isJSImplClass(sym)) {
341
341
withScopedVars(
342
- currentClassSym := sym,
343
- unexpectedMutatedFields := mutable.Set .empty,
342
+ currentClassSym := sym,
343
+ fieldsMutatedInCurrentClass := mutable.Set .empty,
344
344
generatedSAMWrapperCount := new VarBox (0 )
345
345
) {
346
346
try {
@@ -1255,7 +1255,8 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
1255
1255
1256
1256
val mutable = {
1257
1257
static || // static fields must always be mutable
1258
- suspectFieldMutable(f) || unexpectedMutatedFields.contains(f)
1258
+ f.isMutable || // mutable fields can be mutated from anywhere
1259
+ fieldsMutatedInCurrentClass.contains(f.name) // the field is mutated in the current class
1259
1260
}
1260
1261
1261
1262
val namespace =
@@ -2466,13 +2467,34 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
2466
2467
def genRhs = genExpr(rhs)
2467
2468
lhs match {
2468
2469
case Select (qualifier, _) =>
2469
- val ctorAssignment = (
2470
- currentMethodSym.isClassConstructor &&
2471
- currentMethodSym.owner == qualifier.symbol &&
2472
- qualifier.isInstanceOf [This ]
2473
- )
2474
- if (! ctorAssignment && ! suspectFieldMutable(sym))
2475
- unexpectedMutatedFields += sym
2470
+ /* Record assignments to fields of the current class.
2471
+ *
2472
+ * We only do that for fields of the current class sym. For other
2473
+ * fields, even if we recorded it, we would forget them when
2474
+ * `fieldsMutatedInCurrentClass` is reset when going out of the
2475
+ * class. If we assign to an immutable field in a different
2476
+ * class, it will be reported as an IR checking error.
2477
+ *
2478
+ * Assignments to `this.` fields in the constructor are valid
2479
+ * even for immutable fields, and are therefore not recorded.
2480
+ *
2481
+ * #3918 We record the *names* of the fields instead of their
2482
+ * symbols because, in rare cases, scalac has different fields in
2483
+ * the trees than in the class' decls. Since we only record fields
2484
+ * from the current class, names are non ambiguous. For the same
2485
+ * reason, we record assignments to *all* fields, not only the
2486
+ * immutable ones, because in 2.13 the symbol here can be mutable
2487
+ * but not the one in the class' decls.
2488
+ */
2489
+ if (sym.owner == currentClassSym.get) {
2490
+ val ctorAssignment = (
2491
+ currentMethodSym.isClassConstructor &&
2492
+ currentMethodSym.owner == qualifier.symbol &&
2493
+ qualifier.isInstanceOf [This ]
2494
+ )
2495
+ if (! ctorAssignment)
2496
+ fieldsMutatedInCurrentClass += sym.name
2497
+ }
2476
2498
2477
2499
def genBoxedRhs : js.Tree = {
2478
2500
val tpeEnteringPosterasure =
@@ -6688,19 +6710,6 @@ abstract class GenJSCode[G <: Global with Singleton](val global: G)
6688
6710
else result.alternatives.head
6689
6711
}
6690
6712
6691
- /** Whether a field is suspected to be mutable in the IR's terms
6692
- *
6693
- * A field is mutable in the IR, if it is assigned to elsewhere than in the
6694
- * constructor of its class.
6695
- *
6696
- * Mixed-in fields are always mutable, since they will be assigned to in
6697
- * a trait initializer (rather than a constructor).
6698
- */
6699
- private def suspectFieldMutable (sym : Symbol ) = {
6700
- import scala .reflect .internal .Flags
6701
- sym.hasFlag(Flags .MIXEDIN ) || sym.isMutable
6702
- }
6703
-
6704
6713
private def isStringType (tpe : Type ): Boolean =
6705
6714
tpe.typeSymbol == StringClass
6706
6715
0 commit comments