@@ -625,37 +625,36 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
625
625
block.tpe namedPartsWith (tp => locals.contains(tp.symbol))
626
626
}
627
627
628
- /** Check that expression's type can be expressed without references to locally defined
629
- * symbols. The following two remedies are tried before giving up:
630
- * 1. If the expected type of the expression is fully defined, pick it as the
631
- * type of the result expressed by adding a type ascription.
632
- * 2. If (1) fails, force all type variables so that the block's type is
633
- * fully defined and try again.
628
+ /** Ensure that an expression's type can be expressed without references to locally defined
629
+ * symbols. This is done by adding a type ascription of a widened type that does
630
+ * not refer to the locally defined symbols. The widened type is computed using
631
+ * `TyperAssigner#avoid`. However, if the expected type is fully defined and not
632
+ * a supertype of the widened type, we ascribe with the expected type instead.
633
+ *
634
+ * There's a special case having to do with anonymous classes. Sometimes the
635
+ * expected type of a block is the anonymous class defined inside it. In that
636
+ * case there's technically a leak which is not removed by the ascription.
634
637
*/
635
- protected def ensureNoLocalRefs (tree : Tree , pt : Type , localSyms : => List [Symbol ], forcedDefined : Boolean = false )(implicit ctx : Context ): Tree = {
638
+ protected def ensureNoLocalRefs (tree : Tree , pt : Type , localSyms : => List [Symbol ])(implicit ctx : Context ): Tree = {
636
639
def ascribeType (tree : Tree , pt : Type ): Tree = tree match {
637
640
case block @ Block (stats, expr) =>
638
641
val expr1 = ascribeType(expr, pt)
639
642
cpy.Block (block)(stats, expr1) withType expr1.tpe // no assignType here because avoid is redundant
640
643
case _ =>
641
644
Typed (tree, TypeTree (pt.simplified))
642
645
}
643
- val leaks = escapingRefs(tree , localSyms)
644
- if (leaks.isEmpty ) tree
645
- else if ( ! forcedDefined) {
646
+ def noLeaks ( t : Tree ) : Boolean = escapingRefs(t , localSyms).isEmpty
647
+ if (noLeaks(tree) ) tree
648
+ else {
646
649
fullyDefinedType(tree.tpe, " block" , tree.pos)
647
- val avoidingType = avoid(tree.tpe, localSyms)
648
- if (isFullyDefined(pt, ForceDegree .none) && ! (avoidingType <:< pt))
649
- ascribeType(tree, pt)
650
- else {
651
- val tree1 = ascribeType(tree, avoidingType)
652
- ensureNoLocalRefs(tree1, pt, localSyms, forcedDefined = true )
653
- }
654
- } else if (isFullyDefined(pt, ForceDegree .none))
655
- ascribeType(tree, pt)
656
- else
657
- errorTree(tree,
658
- em " local definition of ${leaks.head.name} escapes as part of expression's type ${tree.tpe}" /* ; full type: ${result.tpe.toString}"*/ )
650
+ var avoidingType = avoid(tree.tpe, localSyms)
651
+ val ptDefined = isFullyDefined(pt, ForceDegree .none)
652
+ if (ptDefined && ! (avoidingType <:< pt)) avoidingType = pt
653
+ val tree1 = ascribeType(tree, avoidingType)
654
+ assert(ptDefined || noLeaks(tree1), // `ptDefined` needed because of special case of anonymous classes
655
+ i " leak: ${escapingRefs(tree1, localSyms).toList}%, % in $tree1" )
656
+ tree1
657
+ }
659
658
}
660
659
661
660
def typedIf (tree : untpd.If , pt : Type )(implicit ctx : Context ): Tree = track(" typedIf" ) {
0 commit comments