Skip to content

Commit 7840ce1

Browse files
committed
Only allow StoreModule at the top-level of constructor bodies.
This allows to get rid of `Env.inConstructor` in the `ClassDefChecker`. In theory, this is a breaking change. However, we never emitted IR with `StoreModule`s that would violate the new rule. In fact, all the IR we emit is even stricter: there is always a `StoreModule` *immediately after* the super constructor call of (JS) module class constructors, and never anywhere else.
1 parent 347ba06 commit 7840ce1

File tree

2 files changed

+60
-32
lines changed

2 files changed

+60
-32
lines changed

linker/shared/src/main/scala/org/scalajs/linker/checker/ClassDefChecker.scala

+24-28
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,6 @@ private final class ClassDefChecker(classDef: ClassDef,
303303
val thisType = if (static) NoType else instanceThisType
304304
val bodyEnv = Env.fromParams(params)
305305
.withThisType(thisType)
306-
.withInConstructor(isConstructor)
307306

308307
if (isConstructor)
309308
checkConstructorBody(body, bodyEnv)
@@ -328,12 +327,13 @@ private final class ClassDefChecker(classDef: ClassDef,
328327

329328
val startEnv = Env.fromParams(classDef.jsClassCaptures.getOrElse(Nil) ++ params ++ restParam)
330329
.withHasNewTarget(true)
331-
.withInConstructor(true)
332330

333331
val envJustBeforeSuper = checkBlockStats(body.beforeSuper, startEnv)
334332
checkTreeOrSpreads(body.superCall.args, envJustBeforeSuper)
335333
val envJustAfterSuper = envJustBeforeSuper.withThisType(instanceThisType)
336-
checkBlockStats(body.afterSuper, envJustAfterSuper)
334+
val afterSuperStoreModulesHandled =
335+
handleStoreModulesAfterSuperCtorCall(body.afterSuper)
336+
checkBlockStats(afterSuperStoreModulesHandled, envJustAfterSuper)
337337
}
338338

339339
private def checkJSMethodDef(methodDef: JSMethodDef): Unit = withPerMethodState {
@@ -525,12 +525,10 @@ private final class ClassDefChecker(classDef: ClassDef,
525525
* - There is no such `ApplyStatically` anywhere else in the body.
526526
* - `cls` must be the enclosing class or its direct superclass.
527527
* - In the statements before the delegate constructor call, and within
528-
* `args`:
529-
* - `This()` cannot be used except in `Assign(Select(This(), _), _)`,
530-
* i.e., to assign to a field (but not read from one).
531-
* - `StoreModule()` cannot be used.
528+
* `args`, `This()` cannot be used except in `Assign(Select(This(), _), _)`,
529+
* i.e., to assign to a field (but not read from one).
532530
* - After the delegate constructor call, `This` can be used without
533-
* restriction.
531+
* restriction. Moreover, we can have `StoreModule`s at the top-level.
534532
*
535533
* After the optimizer, there may be no delegate constructor call at all.
536534
* This frequently happens as the optimizer inlines super constructor
@@ -561,7 +559,9 @@ private final class ClassDefChecker(classDef: ClassDef,
561559
if (!postOptimizer)
562560
reportError(i"Constructor must contain a delegate constructor call")
563561

564-
checkBlockStats(bodyStats, bodyEnv)
562+
val bodyStatsStoreModulesHandled =
563+
handleStoreModulesAfterSuperCtorCall(bodyStats)
564+
checkBlockStats(bodyStatsStoreModulesHandled, bodyEnv)
565565
} else {
566566
val (delegateCtorCall: ApplyStatically) :: afterDelegateCtor = rest
567567
val ApplyStatically(_, receiver, cls, MethodIdent(ctor), args) = delegateCtorCall
@@ -585,11 +585,23 @@ private final class ClassDefChecker(classDef: ClassDef,
585585
}
586586
}
587587

588-
checkBlockStats(afterDelegateCtor, unrestrictedEnv)
588+
val afterDelegateCtorStoreModulesHandled =
589+
handleStoreModulesAfterSuperCtorCall(afterDelegateCtor)
590+
checkBlockStats(afterDelegateCtorStoreModulesHandled, unrestrictedEnv)
589591
}
590592
}
591593
}
592594

595+
private def handleStoreModulesAfterSuperCtorCall(trees: List[Tree]): List[Tree] = {
596+
/* If StoreModule's are allowed, there is nothing left to check about them,
597+
* so we filter them out.
598+
*/
599+
if (classDef.kind.hasModuleAccessor)
600+
trees.filter(!_.isInstanceOf[StoreModule])
601+
else
602+
trees
603+
}
604+
593605
private def checkBlockStats(stats: List[Tree], env: Env): Env = {
594606
stats.foldLeft(env) { (prevEnv, stat) =>
595607
checkTree(stat, prevEnv)
@@ -717,14 +729,7 @@ private final class ClassDefChecker(classDef: ClassDef,
717729
case _: LoadModule =>
718730

719731
case StoreModule() =>
720-
if (!classDef.kind.hasModuleAccessor)
721-
reportError(i"Illegal StoreModule inside class of kind ${classDef.kind}")
722-
if (!env.inConstructor)
723-
reportError(i"Illegal StoreModule outside of constructor")
724-
if (env.thisType == NoType) // can happen before JSSuperConstructorCall in JSModuleClass
725-
reportError(i"Cannot find `this` in scope for StoreModule()")
726-
if (env.isThisRestricted)
727-
reportError(i"Restricted use of `this` for StoreModule() before super constructor call")
732+
reportError(i"Illegal StoreModule")
728733

729734
case Select(qualifier, _) =>
730735
checkTree(qualifier, env)
@@ -1063,8 +1068,6 @@ object ClassDefChecker {
10631068
val locals: Map[LocalName, LocalDef],
10641069
/** Return types by label. */
10651070
val returnLabels: Set[LabelName],
1066-
/** Whether we are in a constructor of the class. */
1067-
val inConstructor: Boolean,
10681071
/** Whether usages of `this` are restricted in this scope. */
10691072
val isThisRestricted: Boolean
10701073
) {
@@ -1082,9 +1085,6 @@ object ClassDefChecker {
10821085
def withLabel(label: LabelName): Env =
10831086
copy(returnLabels = returnLabels + label)
10841087

1085-
def withInConstructor(inConstructor: Boolean): Env =
1086-
copy(inConstructor = inConstructor)
1087-
10881088
def withIsThisRestricted(isThisRestricted: Boolean): Env =
10891089
copy(isThisRestricted = isThisRestricted)
10901090

@@ -1093,11 +1093,9 @@ object ClassDefChecker {
10931093
thisType: Type = thisType,
10941094
locals: Map[LocalName, LocalDef] = locals,
10951095
returnLabels: Set[LabelName] = returnLabels,
1096-
inConstructor: Boolean = inConstructor,
10971096
isThisRestricted: Boolean = isThisRestricted
10981097
): Env = {
1099-
new Env(hasNewTarget, thisType, locals, returnLabels, inConstructor,
1100-
isThisRestricted)
1098+
new Env(hasNewTarget, thisType, locals, returnLabels, isThisRestricted)
11011099
}
11021100
}
11031101

@@ -1108,7 +1106,6 @@ object ClassDefChecker {
11081106
thisType = NoType,
11091107
locals = Map.empty,
11101108
returnLabels = Set.empty,
1111-
inConstructor = false,
11121109
isThisRestricted = false
11131110
)
11141111
}
@@ -1123,7 +1120,6 @@ object ClassDefChecker {
11231120
thisType = NoType,
11241121
paramLocalDefs.toMap,
11251122
Set.empty,
1126-
inConstructor = false,
11271123
isThisRestricted = false
11281124
)
11291125
}

linker/shared/src/test/scala/org/scalajs/linker/checker/ClassDefCheckerTest.scala

+36-4
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ class ClassDefCheckerTest {
516516
})(EOH, UNV)
517517
)
518518
),
519-
"Illegal StoreModule inside class of kind Class"
519+
"Illegal StoreModule"
520520
)
521521

522522
assertError(
@@ -533,7 +533,24 @@ class ClassDefCheckerTest {
533533
})(EOH, UNV)
534534
)
535535
),
536-
"Restricted use of `this` for StoreModule() before super constructor call"
536+
"Illegal StoreModule"
537+
)
538+
539+
assertError(
540+
classDef(
541+
"Foo",
542+
kind = ClassKind.ModuleClass,
543+
superClass = Some(ObjectClass),
544+
methods = List(
545+
MethodDef(ctorFlags, NoArgConstructorName, NON, Nil, NoType, Some {
546+
Block(
547+
superCtorCall,
548+
If(BooleanLiteral(true), StoreModule(), Skip())(NoType)
549+
)
550+
})(EOH, UNV)
551+
)
552+
),
553+
"Illegal StoreModule"
537554
)
538555

539556
assertError(
@@ -550,7 +567,7 @@ class ClassDefCheckerTest {
550567
})(EOH, UNV)
551568
)
552569
),
553-
"Illegal StoreModule outside of constructor"
570+
"Illegal StoreModule"
554571
)
555572

556573
assertError(
@@ -564,7 +581,22 @@ class ClassDefCheckerTest {
564581
EOH, UNV)
565582
)
566583
),
567-
"Cannot find `this` in scope for StoreModule()"
584+
"Illegal StoreModule"
585+
)
586+
587+
assertError(
588+
classDef(
589+
"Foo",
590+
kind = ClassKind.JSModuleClass,
591+
superClass = Some("scala.scalajs.js.Object"),
592+
jsConstructor = Some(
593+
JSConstructorDef(JSCtorFlags, Nil, None,
594+
JSConstructorBody(Nil, JSSuperConstructorCall(Nil),
595+
If(BooleanLiteral(true), StoreModule(), Skip())(NoType) :: Undefined() :: Nil))(
596+
EOH, UNV)
597+
)
598+
),
599+
"Illegal StoreModule"
568600
)
569601
}
570602

0 commit comments

Comments
 (0)