@@ -231,7 +231,7 @@ private final class ClassDefChecker(classDef: ClassDef,
231
231
}
232
232
233
233
private def checkMethodDef (methodDef : MethodDef ): Unit = withPerMethodState {
234
- val MethodDef (flags, MethodIdent (name), _, params, _, body ) = methodDef
234
+ val MethodDef (flags, MethodIdent (name), _, params, _, optBody ) = methodDef
235
235
implicit val ctx = ErrorContext (methodDef)
236
236
237
237
val namespace = flags.namespace
@@ -246,7 +246,7 @@ private final class ClassDefChecker(classDef: ClassDef,
246
246
if (! methods(namespace.ordinal).add(name))
247
247
reportError(i " duplicate ${namespace.prefixString}method ' $name' " )
248
248
249
- if (body .isEmpty && namespace != MemberNamespace .Public )
249
+ if (optBody .isEmpty && namespace != MemberNamespace .Public )
250
250
reportError(" Abstract methods may only be in the public namespace" )
251
251
252
252
// ClassInitializer
@@ -294,11 +294,20 @@ private final class ClassDefChecker(classDef: ClassDef,
294
294
}
295
295
296
296
// Body
297
- val thisType = if (static) NoType else instanceThisType
298
- val bodyEnv = Env .fromParams(params)
299
- .withThisType(thisType)
300
- .withInConstructor(isConstructor)
301
- body.foreach(checkTree(_, bodyEnv))
297
+ for (body <- optBody) {
298
+ val thisType = if (static) NoType else instanceThisType
299
+ val bodyEnv = Env .fromParams(params)
300
+ .withThisType(thisType)
301
+ .withInConstructor(isConstructor)
302
+
303
+ if (isConstructor && ! postOptimizer) {
304
+ // Strict checking of `this` values and super/delegate constructor calls
305
+ checkConstructorBodyStrict(body, bodyEnv)
306
+ } else {
307
+ // Regular checking of `this` values
308
+ checkTree(body, bodyEnv)
309
+ }
310
+ }
302
311
}
303
312
304
313
private def checkJSConstructorDef (ctorDef : JSConstructorDef ): Unit = withPerMethodState {
@@ -319,14 +328,10 @@ private final class ClassDefChecker(classDef: ClassDef,
319
328
.withHasNewTarget(true )
320
329
.withInConstructor(true )
321
330
322
- val envJustBeforeSuper = body.beforeSuper.foldLeft(startEnv) { (prevEnv, stat) =>
323
- checkTree(stat, prevEnv)
324
- }
331
+ val envJustBeforeSuper = checkBlockStats(body.beforeSuper, startEnv)
325
332
checkTreeOrSpreads(body.superCall.args, envJustBeforeSuper)
326
333
val envJustAfterSuper = envJustBeforeSuper.withThisType(instanceThisType)
327
- body.afterSuper.foldLeft(envJustAfterSuper) { (prevEnv, stat) =>
328
- checkTree(stat, prevEnv)
329
- }
334
+ checkBlockStats(body.afterSuper, envJustAfterSuper)
330
335
}
331
336
332
337
private def checkJSMethodDef (methodDef : JSMethodDef ): Unit = withPerMethodState {
@@ -505,6 +510,81 @@ private final class ClassDefChecker(classDef: ClassDef,
505
510
}
506
511
}
507
512
513
+ private def checkConstructorBodyStrict (body : Tree , bodyEnv : Env ): Unit = {
514
+ /* In a strictly checked constructor body, usages of the `this` value are
515
+ * restricted according to the following rules.
516
+ *
517
+ * If the enclosing class is `jl.Object`, the full `body` is unrestricted.
518
+ * Otherwise:
519
+ *
520
+ * - The body must be a possible Block with statements `stats`.
521
+ * - There exists a unique `stat` in the `stats` list that is an
522
+ * `ApplyStatically(_, This(), cls, someConstructor, args)`, called the
523
+ * delegate constructor call.
524
+ * - There is no such `ApplyStatically` anywhere else in the body.
525
+ * - `cls` must be the enclosing class or its direct superclass.
526
+ * - In the statements before the delegate constructor call, and within
527
+ * `args`:
528
+ * - `This()` cannot be used except in `Assign(Select(This(), _), _)`,
529
+ * i.e., to assign to a field (but not read from one).
530
+ * - `StoreModule()` cannot be used.
531
+ * - After the delegate constructor call, `This` can be used without
532
+ * restriction.
533
+ */
534
+
535
+ implicit val ctx = ErrorContext (body)
536
+
537
+ val bodyStats = body match {
538
+ case Block (stats) => stats
539
+ case Skip () => Nil
540
+ case _ => body :: Nil
541
+ }
542
+
543
+ if (isJLObject) {
544
+ checkBlockStats(bodyStats, bodyEnv)
545
+ } else {
546
+ val (beforeDelegateCtor, rest) = bodyStats.span {
547
+ case ApplyStatically (_, This (), _, MethodIdent (ctor), _) =>
548
+ ! ctor.isConstructor
549
+ case _ =>
550
+ true
551
+ }
552
+
553
+ if (rest.isEmpty) {
554
+ reportError(i " Constructor must contain a delegate constructor call " )
555
+ checkBlockStats(beforeDelegateCtor, bodyEnv) // to report further errors
556
+ } else {
557
+ val (delegateCtorCall : ApplyStatically ) :: afterDelegateCtor = rest
558
+ val ApplyStatically (_, receiver, cls, MethodIdent (ctor), args) = delegateCtorCall
559
+
560
+ val initEnv = bodyEnv.withIsThisRestricted(true )
561
+ val envJustBeforeSuper = checkBlockStats(beforeDelegateCtor, initEnv)
562
+
563
+ checkApplyArgs(ctor, args, envJustBeforeSuper)
564
+
565
+ val unrestrictedEnv = envJustBeforeSuper.withIsThisRestricted(false )
566
+
567
+ checkTree(receiver, unrestrictedEnv) // check that the This itself is valid
568
+
569
+ if (! (cls == classDef.className || classDef.superClass.exists(_.name == cls))) {
570
+ implicit val ctx = ErrorContext (delegateCtorCall)
571
+ reportError(
572
+ i " Invalid target class $cls for delegate constructor call; " +
573
+ i " expected ${classDef.className}" +
574
+ classDef.superClass.fold(" " )(s => i " or ${s.name}" ))
575
+ }
576
+
577
+ checkBlockStats(afterDelegateCtor, unrestrictedEnv)
578
+ }
579
+ }
580
+ }
581
+
582
+ private def checkBlockStats (stats : List [Tree ], env : Env ): Env = {
583
+ stats.foldLeft(env) { (prevEnv, stat) =>
584
+ checkTree(stat, prevEnv)
585
+ }
586
+ }
587
+
508
588
private def checkTreeOrSpreads (trees : List [TreeOrJSSpread ], env : Env ): Unit = {
509
589
trees.foreach {
510
590
case JSSpread (items) => checkTree(items, env)
@@ -515,15 +595,19 @@ private final class ClassDefChecker(classDef: ClassDef,
515
595
private def checkTrees (trees : List [Tree ], env : Env ): Unit =
516
596
trees.foreach(checkTree(_, env))
517
597
598
+ private def checkApplyArgs (methodName : MethodName , args : List [Tree ], env : Env )(
599
+ implicit ctx : ErrorContext ): Unit = {
600
+ val paramRefsCount = methodName.paramTypeRefs.size
601
+ if (args.size != paramRefsCount)
602
+ reportError(i " Arity mismatch: $paramRefsCount expected but ${args.size} found " )
603
+ checkTrees(args, env)
604
+ }
605
+
518
606
private def checkTree (tree : Tree , env : Env ): Env = {
519
607
implicit val ctx = ErrorContext (tree)
520
608
521
- def checkApplyGeneric (methodName : MethodName , args : List [Tree ]): Unit = {
522
- val paramRefsCount = methodName.paramTypeRefs.size
523
- if (args.size != paramRefsCount)
524
- reportError(i " Arity mismatch: $paramRefsCount expected but ${args.size} found " )
525
- checkTrees(args, env)
526
- }
609
+ def checkApplyGeneric (methodName : MethodName , args : List [Tree ]): Unit =
610
+ checkApplyArgs(methodName, args, env)
527
611
528
612
val newEnv = tree match {
529
613
case VarDef (ident, _, vtpe, mutable, _) =>
@@ -540,16 +624,19 @@ private final class ClassDefChecker(classDef: ClassDef,
540
624
case Skip () =>
541
625
542
626
case Block (stats) =>
543
- stats.foldLeft(env) { (prevEnv, stat) =>
544
- checkTree(stat, prevEnv)
545
- }
627
+ checkBlockStats(stats, env)
546
628
547
629
case Labeled (label, _, body) =>
548
630
checkDeclareLabel(label)
549
631
checkTree(body, env.withLabel(label.name))
550
632
551
633
case Assign (lhs, rhs) =>
552
- checkTree(lhs, env)
634
+ lhs match {
635
+ case Select (This (), _) if env.isThisRestricted =>
636
+ checkTree(lhs, env.withIsThisRestricted(false ))
637
+ case _ =>
638
+ checkTree(lhs, env)
639
+ }
553
640
checkTree(rhs, env)
554
641
555
642
lhs match {
@@ -618,10 +705,12 @@ private final class ClassDefChecker(classDef: ClassDef,
618
705
case StoreModule () =>
619
706
if (! classDef.kind.hasModuleAccessor)
620
707
reportError(i " Illegal StoreModule inside class of kind ${classDef.kind}" )
621
- if (! env.inConstructor)
708
+ else if (! env.inConstructor)
622
709
reportError(i " Illegal StoreModule outside of constructor " )
623
- if (env.thisType == NoType ) // can happen before JSSuperConstructorCall in JSModuleClass
710
+ else if (env.thisType == NoType ) // can happen before JSSuperConstructorCall in JSModuleClass
624
711
reportError(i " Cannot find `this` in scope for StoreModule() " )
712
+ else if (env.isThisRestricted)
713
+ reportError(i " Restricted use of `this` for StoreModule() before super constructor call " )
625
714
626
715
case Select (qualifier, _) =>
627
716
checkTree(qualifier, env)
@@ -637,6 +726,8 @@ private final class ClassDefChecker(classDef: ClassDef,
637
726
checkApplyGeneric(method, args)
638
727
639
728
case ApplyStatically (_, receiver, _, MethodIdent (method), args) =>
729
+ if (! postOptimizer && method.isConstructor)
730
+ reportError(i " Illegal constructor call " )
640
731
checkTree(receiver, env)
641
732
checkApplyGeneric(method, args)
642
733
@@ -816,6 +907,8 @@ private final class ClassDefChecker(classDef: ClassDef,
816
907
reportError(i " Cannot find `this` in scope " )
817
908
else if (tree.tpe != env.thisType)
818
909
reportError(i " `this` of type ${env.thisType} typed as ${tree.tpe}" )
910
+ else if (env.isThisRestricted)
911
+ reportError(i " Restricted use of `this` before the super constructor call " )
819
912
820
913
case Closure (arrow, captureParams, params, restParam, body, captureValues) =>
821
914
/* Check compliance of captureValues wrt. captureParams in the current
@@ -960,7 +1053,9 @@ object ClassDefChecker {
960
1053
/** Return types by label. */
961
1054
val returnLabels : Set [LabelName ],
962
1055
/** Whether we are in a constructor of the class. */
963
- val inConstructor : Boolean
1056
+ val inConstructor : Boolean ,
1057
+ /** Whether usages of `this` are restricted in this scope. */
1058
+ val isThisRestricted : Boolean
964
1059
) {
965
1060
import Env ._
966
1061
@@ -979,20 +1074,33 @@ object ClassDefChecker {
979
1074
def withInConstructor (inConstructor : Boolean ): Env =
980
1075
copy(inConstructor = inConstructor)
981
1076
1077
+ def withIsThisRestricted (isThisRestricted : Boolean ): Env =
1078
+ copy(isThisRestricted = isThisRestricted)
1079
+
982
1080
private def copy (
983
1081
hasNewTarget : Boolean = hasNewTarget,
984
1082
thisType : Type = thisType,
985
1083
locals : Map [LocalName , LocalDef ] = locals,
986
1084
returnLabels : Set [LabelName ] = returnLabels,
987
- inConstructor : Boolean = inConstructor
1085
+ inConstructor : Boolean = inConstructor,
1086
+ isThisRestricted : Boolean = isThisRestricted
988
1087
): Env = {
989
- new Env (hasNewTarget, thisType, locals, returnLabels, inConstructor)
1088
+ new Env (hasNewTarget, thisType, locals, returnLabels, inConstructor,
1089
+ isThisRestricted)
990
1090
}
991
1091
}
992
1092
993
1093
private object Env {
994
- val empty : Env =
995
- new Env (hasNewTarget = false , thisType = NoType , Map .empty, Set .empty, inConstructor = false )
1094
+ val empty : Env = {
1095
+ new Env (
1096
+ hasNewTarget = false ,
1097
+ thisType = NoType ,
1098
+ locals = Map .empty,
1099
+ returnLabels = Set .empty,
1100
+ inConstructor = false ,
1101
+ isThisRestricted = false
1102
+ )
1103
+ }
996
1104
997
1105
def fromParams (params : List [ParamDef ]): Env = {
998
1106
val paramLocalDefs =
@@ -1004,7 +1112,8 @@ object ClassDefChecker {
1004
1112
thisType = NoType ,
1005
1113
paramLocalDefs.toMap,
1006
1114
Set .empty,
1007
- inConstructor = false
1115
+ inConstructor = false ,
1116
+ isThisRestricted = false
1008
1117
)
1009
1118
}
1010
1119
}
0 commit comments