@@ -236,7 +236,7 @@ private final class ClassDefChecker(classDef: ClassDef,
236
236
}
237
237
238
238
private def checkMethodDef (methodDef : MethodDef ): Unit = withPerMethodState {
239
- val MethodDef (flags, MethodIdent (name), _, params, _, body ) = methodDef
239
+ val MethodDef (flags, MethodIdent (name), _, params, _, optBody ) = methodDef
240
240
implicit val ctx = ErrorContext (methodDef)
241
241
242
242
val namespace = flags.namespace
@@ -251,7 +251,7 @@ private final class ClassDefChecker(classDef: ClassDef,
251
251
if (! methods(namespace.ordinal).add(name))
252
252
reportError(i " duplicate ${namespace.prefixString}method ' $name' " )
253
253
254
- if (body .isEmpty && namespace != MemberNamespace .Public )
254
+ if (optBody .isEmpty && namespace != MemberNamespace .Public )
255
255
reportError(" Abstract methods may only be in the public namespace" )
256
256
257
257
// ClassInitializer
@@ -299,11 +299,17 @@ private final class ClassDefChecker(classDef: ClassDef,
299
299
}
300
300
301
301
// Body
302
- val thisType = if (static) NoType else instanceThisType
303
- val bodyEnv = Env .fromParams(params)
304
- .withThisType(thisType)
305
- .withInConstructor(isConstructor)
306
- body.foreach(checkTree(_, bodyEnv))
302
+ for (body <- optBody) {
303
+ val thisType = if (static) NoType else instanceThisType
304
+ val bodyEnv = Env .fromParams(params)
305
+ .withThisType(thisType)
306
+ .withInConstructor(isConstructor)
307
+
308
+ if (isConstructor)
309
+ checkConstructorBody(body, bodyEnv)
310
+ else
311
+ checkTree(body, bodyEnv)
312
+ }
307
313
}
308
314
309
315
private def checkJSConstructorDef (ctorDef : JSConstructorDef ): Unit = withPerMethodState {
@@ -324,14 +330,10 @@ private final class ClassDefChecker(classDef: ClassDef,
324
330
.withHasNewTarget(true )
325
331
.withInConstructor(true )
326
332
327
- val envJustBeforeSuper = body.beforeSuper.foldLeft(startEnv) { (prevEnv, stat) =>
328
- checkTree(stat, prevEnv)
329
- }
333
+ val envJustBeforeSuper = checkBlockStats(body.beforeSuper, startEnv)
330
334
checkTreeOrSpreads(body.superCall.args, envJustBeforeSuper)
331
335
val envJustAfterSuper = envJustBeforeSuper.withThisType(instanceThisType)
332
- body.afterSuper.foldLeft(envJustAfterSuper) { (prevEnv, stat) =>
333
- checkTree(stat, prevEnv)
334
- }
336
+ checkBlockStats(body.afterSuper, envJustAfterSuper)
335
337
}
336
338
337
339
private def checkJSMethodDef (methodDef : JSMethodDef ): Unit = withPerMethodState {
@@ -510,6 +512,80 @@ private final class ClassDefChecker(classDef: ClassDef,
510
512
}
511
513
}
512
514
515
+ private def checkConstructorBody (body : Tree , bodyEnv : Env ): Unit = {
516
+ /* If the enclosing class is `jl.Object`, the `body` cannot contain any
517
+ * delegate constructor call.
518
+ *
519
+ * Otherwise:
520
+ *
521
+ * - Let `stats` be the list of flattened statements in `body`.
522
+ * - There exists a unique `stat` in the `stats` list that is an
523
+ * `ApplyStatically(_, This(), cls, someConstructor, args)`, called the
524
+ * delegate constructor call.
525
+ * - There is no such `ApplyStatically` anywhere else in the body.
526
+ * - `cls` must be the enclosing class or its direct superclass.
527
+ *
528
+ * After the optimizer, there may be no delegate constructor call at all.
529
+ * This frequently happens as the optimizer inlines super constructor
530
+ * calls. If there is one, `cls` can be any class (it must still be some
531
+ * class in the superclass chain for the types to align, but this is not
532
+ * checked here).
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
+ if (! postOptimizer)
555
+ reportError(i " Constructor must contain a delegate constructor call " )
556
+
557
+ checkBlockStats(bodyStats, bodyEnv)
558
+ } else {
559
+ val (delegateCtorCall : ApplyStatically ) :: afterDelegateCtor = rest
560
+ val ApplyStatically (_, receiver, cls, MethodIdent (ctor), args) = delegateCtorCall
561
+
562
+ val envJustBeforeDelegate = checkBlockStats(beforeDelegateCtor, bodyEnv)
563
+
564
+ checkApplyArgs(ctor, args, envJustBeforeDelegate)
565
+
566
+ checkTree(receiver, envJustBeforeDelegate) // check that the This itself is valid
567
+
568
+ if (! postOptimizer) {
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
+
578
+ checkBlockStats(afterDelegateCtor, envJustBeforeDelegate)
579
+ }
580
+ }
581
+ }
582
+
583
+ private def checkBlockStats (stats : List [Tree ], env : Env ): Env = {
584
+ stats.foldLeft(env) { (prevEnv, stat) =>
585
+ checkTree(stat, prevEnv)
586
+ }
587
+ }
588
+
513
589
private def checkTreeOrSpreads (trees : List [TreeOrJSSpread ], env : Env ): Unit = {
514
590
trees.foreach {
515
591
case JSSpread (items) => checkTree(items, env)
@@ -520,15 +596,19 @@ private final class ClassDefChecker(classDef: ClassDef,
520
596
private def checkTrees (trees : List [Tree ], env : Env ): Unit =
521
597
trees.foreach(checkTree(_, env))
522
598
599
+ private def checkApplyArgs (methodName : MethodName , args : List [Tree ], env : Env )(
600
+ implicit ctx : ErrorContext ): Unit = {
601
+ val paramRefsCount = methodName.paramTypeRefs.size
602
+ if (args.size != paramRefsCount)
603
+ reportError(i " Arity mismatch: $paramRefsCount expected but ${args.size} found " )
604
+ checkTrees(args, env)
605
+ }
606
+
523
607
private def checkTree (tree : Tree , env : Env ): Env = {
524
608
implicit val ctx = ErrorContext (tree)
525
609
526
- def checkApplyGeneric (methodName : MethodName , args : List [Tree ]): Unit = {
527
- val paramRefsCount = methodName.paramTypeRefs.size
528
- if (args.size != paramRefsCount)
529
- reportError(i " Arity mismatch: $paramRefsCount expected but ${args.size} found " )
530
- checkTrees(args, env)
531
- }
610
+ def checkApplyGeneric (methodName : MethodName , args : List [Tree ]): Unit =
611
+ checkApplyArgs(methodName, args, env)
532
612
533
613
val newEnv = tree match {
534
614
case VarDef (ident, _, vtpe, mutable, _) =>
@@ -545,9 +625,7 @@ private final class ClassDefChecker(classDef: ClassDef,
545
625
case Skip () =>
546
626
547
627
case Block (stats) =>
548
- stats.foldLeft(env) { (prevEnv, stat) =>
549
- checkTree(stat, prevEnv)
550
- }
628
+ checkBlockStats(stats, env)
551
629
552
630
case Labeled (label, _, body) =>
553
631
checkDeclareLabel(label)
@@ -645,6 +723,8 @@ private final class ClassDefChecker(classDef: ClassDef,
645
723
checkApplyGeneric(method, args)
646
724
647
725
case ApplyStatically (_, receiver, _, MethodIdent (method), args) =>
726
+ if (method.isConstructor)
727
+ reportError(i " Illegal constructor call " )
648
728
checkTree(receiver, env)
649
729
checkApplyGeneric(method, args)
650
730
0 commit comments