diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/typechecker/Namers.scala | 30 | ||||
-rw-r--r-- | test/files/pos/no-widen-locals.scala | 17 | ||||
-rw-r--r-- | test/files/run/constrained-types.check | 4 | ||||
-rw-r--r-- | test/files/run/constrained-types.scala | 2 |
4 files changed, 44 insertions, 9 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala index 7b2b737fb5..70d326ceb7 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala @@ -577,9 +577,27 @@ trait Namers { self: Analyzer => sym.setInfo(selftpe) } - private def widenIfNotFinal(sym: Symbol, tpe: Type, pt: Type): Type = { + /** This method has a big impact on the eventual compiled code. + * At this point many values have the most specific possible + * type (e.g. in val x = 42, x's type is Int(42), not Int) but + * most need to be widened to avoid undesirable propagation of + * those singleton types. + * + * However, the compilation of pattern matches into switch + * statements depends on constant folding, which will only take + * place for those values which aren't widened. The "final" + * modifier is the present means of signaling that a constant + * value should not be widened, so it has a use even in situations + * whether it is otherwise redundant (such as in a singleton.) + * Locally defined symbols are also excluded from widening. + * + * PP:is there a useful difference in practice between widen + * and deconst which wouldn't be achieved by simply calling widen + * in both situations? If so, could an example of this be added? + */ + private def widenIfNecessary(sym: Symbol, tpe: Type, pt: Type): Type = { val getter = - if (sym.isValue && sym.owner.isClass && (sym hasFlag PRIVATE)) + if (sym.isValue && sym.owner.isClass && sym.isPrivate) sym.getter(sym.owner) else sym def isHidden(tp: Type): Boolean = tp match { @@ -597,8 +615,8 @@ trait Namers { self: Analyzer => if ((sym.isVariable || sym.isMethod && !(sym hasFlag ACCESSOR))) if (tpe2 <:< pt) tpe2 else tpe1 else if (isHidden(tpe)) tpe2 - else if (!(sym hasFlag FINAL)) tpe1 - else tpe + else if (sym.isFinal || sym.isLocal) tpe + else tpe1 } // sets each ValDef's symbol @@ -895,7 +913,7 @@ trait Namers { self: Analyzer => // replace deSkolemized symbols with skolemized ones (for resultPt computed by looking at overridden symbol, right?) val pt = resultPt.substSym(tparamSyms, tparams map (_.symbol)) // compute result type from rhs - tpt defineType widenIfNotFinal(meth, typer.computeType(rhs, pt), pt) + tpt defineType widenIfNecessary(meth, typer.computeType(rhs, pt), pt) tpt setPos meth.pos.focus tpt.tpe } else typer.typedType(tpt).tpe @@ -1138,7 +1156,7 @@ trait Namers { self: Analyzer => context.error(tpt.pos, "missing parameter type"); ErrorType } else { - tpt defineType widenIfNotFinal( + tpt defineType widenIfNecessary( sym, newTyper(typer1.context.make(vdef, sym)).computeType(rhs, WildcardType), WildcardType) diff --git a/test/files/pos/no-widen-locals.scala b/test/files/pos/no-widen-locals.scala new file mode 100644 index 0000000000..32579404b2 --- /dev/null +++ b/test/files/pos/no-widen-locals.scala @@ -0,0 +1,17 @@ +import annotation.switch + +object Test { + def f(x: Int) = { + val X1 = 5 + val X2 = 10 + val X3 = 15 + val X4 = 20 + + (x: @switch) match { + case X1 => 1 + case X2 => 2 + case X3 => 3 + case X4 => 4 + } + } +} diff --git a/test/files/run/constrained-types.check b/test/files/run/constrained-types.check index 8050017659..dbad841d99 100644 --- a/test/files/run/constrained-types.check +++ b/test/files/run/constrained-types.check @@ -92,8 +92,8 @@ def m = { val x = "three" val y : String @Annot(x) = x y -} // x should be existentially bound -m: java.lang.String @Annot(x) forSome { val x: java.lang.String } +} // x should not escape the local scope with a narrow type +m: String @Annot("three") ----- def n(y: String) = { diff --git a/test/files/run/constrained-types.scala b/test/files/run/constrained-types.scala index c03c144ad1..62abfe9e31 100644 --- a/test/files/run/constrained-types.scala +++ b/test/files/run/constrained-types.scala @@ -61,7 +61,7 @@ object Test { | val x = "three" | val y : String @Annot(x) = x | y - |} // x should be existentially bound""", + |} // x should not escape the local scope with a narrow type""", """def n(y: String) = { | def m(x: String) : String @Annot(x) = { |