From 1f04bba2f64589dca41b02ee4a54ad3e1661fe37 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 13 Aug 2017 12:57:34 +0200 Subject: [PATCH 01/10] Factor out variance manipulation in TypeMap and TypeAccumulator --- .../src/dotty/tools/dotc/core/Types.scala | 65 +++++++------------ 1 file changed, 24 insertions(+), 41 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index fafffd41f355..de5a97de53f9 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3679,14 +3679,26 @@ object Types { // ----- TypeMaps -------------------------------------------------------------------- - abstract class TypeMap(implicit protected val ctx: Context) extends (Type => Type) { thisMap => + /** Common base class of TypeMap and TypeAccumulator */ + abstract class VariantTraversal { + protected[core] var variance = 1 + + @inline protected def atVariance[T](v: Int)(op: => T): T = { + val saved = variance + variance = v + val res = op + variance = saved + res + } + } + + abstract class TypeMap(implicit protected val ctx: Context) + extends VariantTraversal with (Type => Type) { thisMap => protected def stopAtStatic = true def apply(tp: Type): Type - protected[core] var variance = 1 - protected def derivedSelect(tp: NamedType, pre: Type): Type = tp.derivedSelect(pre) protected def derivedRefinedType(tp: RefinedType, parent: Type, info: Type): Type = @@ -3724,16 +3736,13 @@ object Types { case tp: NamedType => if (stopAtStatic && tp.symbol.isStatic) tp else { - val saved = variance - variance = variance max 0 + val prefix1 = atVariance(variance max 0)(this(tp.prefix)) // A prefix is never contravariant. Even if say `p.A` is used in a contravariant // context, we cannot assume contravariance for `p` because `p`'s lower // bound might not have a binding for `A` (e.g. the lower bound could be `Nothing`). // By contrast, covariance does translate to the prefix, since we have that // if `p <: q` then `p.A <: q.A`, and well-formedness requires that `A` is a member // of `p`'s upper bound. - val prefix1 = this(tp.prefix) - variance = saved derivedSelect(tp, prefix1) } case _: ThisType @@ -3744,11 +3753,7 @@ object Types { derivedRefinedType(tp, this(tp.parent), this(tp.refinedInfo)) case tp: TypeAlias => - val saved = variance - variance *= tp.variance - val alias1 = this(tp.alias) - variance = saved - derivedTypeAlias(tp, alias1) + derivedTypeAlias(tp, atVariance(variance * tp.variance)(this(tp.alias))) case tp: TypeBounds => variance = -variance @@ -3764,12 +3769,8 @@ object Types { if (inst.exists) apply(inst) else tp case tp: HKApply => - def mapArg(arg: Type, tparam: ParamInfo): Type = { - val saved = variance - variance *= tparam.paramVariance - try this(arg) - finally variance = saved - } + def mapArg(arg: Type, tparam: ParamInfo): Type = + atVariance(variance * tparam.paramVariance)(this(arg)) derivedAppliedType(tp, this(tp.tycon), tp.args.zipWithConserve(tp.typeParams)(mapArg)) @@ -3894,12 +3895,6 @@ object Types { case _ => tp } - protected def atVariance[T](v: Int)(op: => T): T = { - val saved = variance - variance = v - try op finally variance = saved - } - /** Derived selection. * @pre the (upper bound of) prefix `pre` has a member named `tp.name`. */ @@ -4058,7 +4053,8 @@ object Types { // ----- TypeAccumulators ---------------------------------------------------- - abstract class TypeAccumulator[T](implicit protected val ctx: Context) extends ((T, Type) => T) { + abstract class TypeAccumulator[T](implicit protected val ctx: Context) + extends VariantTraversal with ((T, Type) => T) { protected def stopAtStatic = true @@ -4066,15 +4062,8 @@ object Types { protected def applyToAnnot(x: T, annot: Annotation): T = x // don't go into annotations - protected var variance = 1 - - protected final def applyToPrefix(x: T, tp: NamedType) = { - val saved = variance - variance = variance max 0 // see remark on NamedType case in TypeMap - val result = this(x, tp.prefix) - variance = saved - result - } + protected final def applyToPrefix(x: T, tp: NamedType) = + atVariance(variance max 0)(this(x, tp.prefix)) // see remark on NamedType case in TypeMap def foldOver(x: T, tp: Type): T = tp match { case tp: TypeRef => @@ -4095,13 +4084,7 @@ object Types { this(this(x, tp.parent), tp.refinedInfo) case bounds @ TypeBounds(lo, hi) => - if (lo eq hi) { - val saved = variance - variance = variance * bounds.variance - val result = this(x, lo) - variance = saved - result - } + if (lo eq hi) atVariance(variance * bounds.variance)(this(x, lo)) else { variance = -variance val y = this(x, lo) From 104516b524be2e1da85773ba4684ef077b316e68 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 13 Aug 2017 18:10:03 +0200 Subject: [PATCH 02/10] Fix derivedRefinedType in ApproximatingTypeMap The logic before was overcomplicated since I did not take into account that Range refinedInfos could only happen at variance 0. On the othet hand, it did not take into account all the subtleties of alias types with variances. --- .../src/dotty/tools/dotc/core/Types.scala | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index de5a97de53f9..a87cfa4f4d30 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3927,20 +3927,27 @@ object Types { case Range(parentLo, parentHi) => range(derivedRefinedType(tp, parentLo, info), derivedRefinedType(tp, parentHi, info)) case _ => + def propagate(lo: Type, hi: Type) = + range(derivedRefinedType(tp, parent, lo), derivedRefinedType(tp, parent, hi)) if (parent.isBottomType) parent else info match { + case Range(infoLo: TypeBounds, infoHi: TypeBounds) => + assert(variance == 0) + val v1 = infoLo.variance + val v2 = infoHi.variance + // There's some weirdness coming from the way aliases can have variance + // If infoLo and infoHi are both aliases with the same non-zero variance + // we can propagate to a range of the refined types. If they are both + // non-alias ranges we know that infoLo <:< infoHi and therefore we can + // propagate to refined types with infoLo and infoHi as bounds. + // In all other cases, Nothing..Any is the only interval that contains + // the range. i966.scala is a test case. + if (v1 > 0 && v2 > 0) propagate(infoLo, infoHi) + else if (v1 < 0 && v2 < 0) propagate(infoHi, infoLo) + else if (!infoLo.isAlias && !infoHi.isAlias) propagate(infoLo, infoHi) + else range(tp.bottomType, tp.topType) case Range(infoLo, infoHi) => - def propagate(lo: Type, hi: Type) = - range(derivedRefinedType(tp, parent, lo), derivedRefinedType(tp, parent, hi)) - tp.refinedInfo match { - case rinfo: TypeBounds => - val v = if (rinfo.isAlias) rinfo.variance * variance else variance - if (v > 0) tp.derivedRefinedType(parent, tp.refinedName, rangeToBounds(info)) - else if (v < 0) propagate(infoHi, infoLo) - else range(tp.bottomType, tp.topType) - case _ => - propagate(infoLo, infoHi) - } + propagate(infoLo, infoHi) case _ => tp.derivedRefinedType(parent, tp.refinedName, info) } From 8d17f578c1cb0f882b78c129f0997d3121b3af3e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 13 Aug 2017 19:33:21 +0200 Subject: [PATCH 03/10] Add comment explaining approximating derivedAppliedType --- compiler/src/dotty/tools/dotc/core/Types.scala | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index a87cfa4f4d30..1c6ea0b3347f 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3989,6 +3989,13 @@ object Types { if (variance > 0) tp.derivedAppliedType(tycon, args.map(rangeToBounds)) else { val loBuf, hiBuf = new mutable.ListBuffer[Type] + // Given `C[A1, ..., An]` where sone A's are ranges, try to find + // non-range arguments L1, ..., Ln and H1, ..., Hn such that + // C[L1, ..., Ln] <: C[H1, ..., Hn] by taking the right limits of + // ranges that appear in as co- or contravariant arguments. + // Fail for non-variant argument ranges. + // If successful, the L-arguments are in loBut, the H-arguments in hiBuf. + // @return operation succeeded for all arguments. def distributeArgs(args: List[Type], tparams: List[ParamInfo]): Boolean = args match { case Range(lo, hi) :: args1 => val v = tparams.head.paramVariance From 6e08737ba90551b6cbbdc5d2d7af7835ed2adb5c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 13 Aug 2017 19:36:39 +0200 Subject: [PATCH 04/10] Polishings and more comments --- compiler/src/dotty/tools/dotc/core/Types.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 1c6ea0b3347f..f2ae16bb1295 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -4021,7 +4021,7 @@ object Types { } override protected def derivedAndOrType(tp: AndOrType, tp1: Type, tp2: Type) = - if (tp1.isInstanceOf[Range] || tp2.isInstanceOf[Range]) + if (isRange(tp1) || isRange(tp2)) if (tp.isAnd) range(lower(tp1) & lower(tp2), upper(tp1) & upper(tp2)) else range(lower(tp1) | lower(tp2), upper(tp1) | upper(tp2)) else tp.derivedAndOrType(tp1, tp2) @@ -4039,7 +4039,9 @@ object Types { } override protected def derivedClassInfo(tp: ClassInfo, pre: Type): Type = { - assert(!pre.isInstanceOf[Range]) + assert(!isRange(pre)) + // we don't know what to do here; this case has to be handled in subclasses + // (typically by handling ClassInfo's specially, in case they can be encountered). tp.derivedClassInfo(pre) } From 826c96f1d2e884af3c1501ed0f6a9164507313f8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 13 Aug 2017 19:43:02 +0200 Subject: [PATCH 05/10] Fix review comments re homogenizedArg --- compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 8fd557981dc7..add58d0d73ad 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -12,6 +12,7 @@ import typer.ImportInfo import config.Config import java.lang.Integer.toOctalString import config.Config.summarizeDepth +import scala.util.control.NonFatal import scala.annotation.switch class PlainPrinter(_ctx: Context) extends Printer { @@ -69,11 +70,11 @@ class PlainPrinter(_ctx: Context) extends Printer { else tp private def sameBound(lo: Type, hi: Type): Boolean = - try lo =:= hi - catch { case ex: Throwable => false } + try ctx.typeComparer.isSameTypeWhenFrozen(lo, hi) + catch { case NonFatal(ex) => false } private def homogenizeArg(tp: Type) = tp match { - case TypeBounds(lo, hi) if sameBound(lo, hi) => homogenize(hi) + case TypeBounds(lo, hi) if homogenizedView && sameBound(lo, hi) => homogenize(hi) case _ => tp } From 46a462bd9fe87a9a9a724cae2f43d4c4bb520e7f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 14 Aug 2017 10:17:37 +0200 Subject: [PATCH 06/10] Address reviewers comments --- .../src/dotty/tools/dotc/core/TypeOps.scala | 18 ++++++------------ compiler/src/dotty/tools/dotc/core/Types.scala | 4 ++++ .../dotty/tools/dotc/typer/TypeAssigner.scala | 5 ++--- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeOps.scala b/compiler/src/dotty/tools/dotc/core/TypeOps.scala index 6915a0197bab..8fbda8daf7b5 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeOps.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeOps.scala @@ -29,10 +29,9 @@ trait TypeOps { this: Context => // TODO: Make standalone object. def apply(tp: Type): Type = { - /** Map a `C.this` type to the right prefix. If the prefix is unstable and - * the `C.this` occurs in nonvariant or contravariant position, mark the map - * to be unstable. - */ + /** Map a `C.this` type to the right prefix. If the prefix is unstable, and + * the current variance is <= 0, return a range. + */ def toPrefix(pre: Type, cls: Symbol, thiscls: ClassSymbol): Type = /*>|>*/ ctx.conditionalTraceIndented(TypeOps.track, s"toPrefix($pre, $cls, $thiscls)") /*<|<*/ { if ((pre eq NoType) || (pre eq NoPrefix) || (cls is PackageClass)) tp @@ -50,16 +49,11 @@ trait TypeOps { this: Context => // TODO: Make standalone object. } /*>|>*/ ctx.conditionalTraceIndented(TypeOps.track, s"asSeen ${tp.show} from (${pre.show}, ${cls.show})", show = true) /*<|<*/ { // !!! DEBUG + // One `case ThisType` is specific to asSeenFrom, all other cases are inlined for performance tp match { - case tp: NamedType => // inlined for performance; TODO: factor out into inline method + case tp: NamedType => if (tp.symbol.isStatic) tp - else { - val saved = variance - variance = variance max 0 - val prefix1 = this(tp.prefix) - variance = saved - derivedSelect(tp, prefix1) - } + else derivedSelect(tp, atVariance(variance max 0)(this(tp.prefix))) case tp: ThisType => toPrefix(pre, cls, tp.cls) case _: BoundType | NoPrefix => diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index f2ae16bb1295..034b50d98a69 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3946,6 +3946,9 @@ object Types { else if (v1 < 0 && v2 < 0) propagate(infoHi, infoLo) else if (!infoLo.isAlias && !infoHi.isAlias) propagate(infoLo, infoHi) else range(tp.bottomType, tp.topType) + // Using `parent` instead of `tp.topType` would be better for normal refinements, + // but it would also turn *-types to a hk-types, which is not what we want. + // We should revisit this point in case we represent applied types not as refinements anymore. case Range(infoLo, infoHi) => propagate(infoLo, infoHi) case _ => @@ -4015,6 +4018,7 @@ object Types { range(tp.derivedAppliedType(tycon, loBuf.toList), tp.derivedAppliedType(tycon, hiBuf.toList)) else range(tp.bottomType, tp.topType) + // TODO: can we give a better bound than `topType`? } } else tp.derivedAppliedType(tycon, args) diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 3641915b53e7..4340cae22ca6 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -46,7 +46,7 @@ trait TypeAssigner { val parentType = info.parentsWithArgs.reduceLeft(ctx.typeComparer.andType(_, _)) def addRefinement(parent: Type, decl: Symbol) = { val inherited = - parentType.findMember(decl.name, info.cls.thisType, Private) + parentType.findMember(decl.name, info.cls.thisType, excluded = Private) .suchThat(decl.matches(_)) val inheritedInfo = inherited.info if (inheritedInfo.exists && decl.info <:< inheritedInfo && !(inheritedInfo <:< decl.info)) { @@ -88,7 +88,7 @@ trait TypeAssigner { case info => range(tp.info.bottomType, apply(info)) } case tp: TypeRef if toAvoid(tp.symbol) => - val avoided = tp.info match { + tp.info match { case TypeAlias(alias) => apply(alias) case TypeBounds(lo, hi) => @@ -98,7 +98,6 @@ trait TypeAssigner { case _ => range(tp.bottomType, tp.topType) // should happen only in error cases } - avoided case tp: ThisType if toAvoid(tp.cls) => range(tp.bottomType, apply(classBound(tp.cls.classInfo))) case tp: TypeVar if ctx.typerState.constraint.contains(tp) => From dbd2e2ce3146e53a2779fc97b484d93b15017f3e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 14 Aug 2017 15:01:35 +0200 Subject: [PATCH 07/10] Drop unused field `unsafeNonvariant` --- compiler/src/dotty/tools/dotc/core/Contexts.scala | 6 ------ 1 file changed, 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Contexts.scala b/compiler/src/dotty/tools/dotc/core/Contexts.scala index 97624aab777a..a3c2ffec202c 100644 --- a/compiler/src/dotty/tools/dotc/core/Contexts.scala +++ b/compiler/src/dotty/tools/dotc/core/Contexts.scala @@ -631,12 +631,6 @@ object Contexts { * of underlying during a controlled operation exists. */ private[core] val pendingUnderlying = new mutable.HashSet[Type] - /** A flag that some unsafe nonvariant instantiation was encountered - * in this run. Used as a shortcut to a avoid scans of types in - * Typer.typedSelect. - */ - private[dotty] var unsafeNonvariant: RunId = NoRunId - /** A map from ErrorType to associated message computation. We use this map * instead of storing message computations directly in ErrorTypes in order * to avoid space leaks - the message computation usually captures a context. From bc5cbb015d885892de977c35055114a0887e17a7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 14 Aug 2017 15:03:20 +0200 Subject: [PATCH 08/10] Fix typo --- compiler/src/dotty/tools/dotc/core/Types.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 034b50d98a69..8f1933a2f8b2 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3947,7 +3947,7 @@ object Types { else if (!infoLo.isAlias && !infoHi.isAlias) propagate(infoLo, infoHi) else range(tp.bottomType, tp.topType) // Using `parent` instead of `tp.topType` would be better for normal refinements, - // but it would also turn *-types to a hk-types, which is not what we want. + // but it would also turn *-types into hk-types, which is not what we want. // We should revisit this point in case we represent applied types not as refinements anymore. case Range(infoLo, infoHi) => propagate(infoLo, infoHi) From b27dadcf6dea671c5f3f8ceee131dfc434216c2a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 15 Aug 2017 15:15:53 +0200 Subject: [PATCH 09/10] Fix derivedSelect in avoid Test case in i2945.scala. --- .../src/dotty/tools/dotc/core/Types.scala | 30 +++++++++++++------ .../dotty/tools/dotc/typer/TypeAssigner.scala | 26 ++++++++++------ tests/pos/i2945.scala | 13 ++++++++ 3 files changed, 51 insertions(+), 18 deletions(-) create mode 100644 tests/pos/i2945.scala diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 8f1933a2f8b2..724363be41d4 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3895,14 +3895,14 @@ object Types { case _ => tp } - /** Derived selection. - * @pre the (upper bound of) prefix `pre` has a member named `tp.name`. + /** Try to widen a named type to its info relative to given prefix `pre`, where possible. + * The possible cases are listed inline in the code. Return `default` if no widening is + * possible. */ - override protected def derivedSelect(tp: NamedType, pre: Type) = - if (pre eq tp.prefix) tp - else pre match { - case Range(preLo, preHi) => - preHi.member(tp.name).info.widenExpr match { + def tryWiden(tp: NamedType, pre: Type)(default: => Type): Type = + pre.member(tp.name) match { + case d: SingleDenotation => + d.info match { case TypeAlias(alias) => // if H#T = U, then for any x in L..H, x.T =:= U, // hence we can replace with U under all variances @@ -3916,9 +3916,21 @@ object Types { // hence we can replace with y.type under all variances reapply(info) case _ => - range(tp.derivedSelect(preLo), tp.derivedSelect(preHi)) + default } - case _ => tp.derivedSelect(pre) + case _ => default + } + + /** Derived selection. + * @pre the (upper bound of) prefix `pre` has a member named `tp.name`. + */ + override protected def derivedSelect(tp: NamedType, pre: Type) = + if (pre eq tp.prefix) tp + else pre match { + case Range(preLo, preHi) => + tryWiden(tp, preHi)(range(tp.derivedSelect(preLo), tp.derivedSelect(preHi))) + case _ => + tp.derivedSelect(pre) } override protected def derivedRefinedType(tp: RefinedType, parent: Type, info: Type) = diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 4340cae22ca6..fe3b80f3417f 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -108,18 +108,26 @@ trait TypeAssigner { mapOver(tp) } - /** Two deviations from standard derivedSelect: - * 1. The teh approximation result is a singleton references C#x.type, we + /** Three deviations from standard derivedSelect: + * 1. We first try a widening conversion to the type's info with + * the original prefix. Since the original prefix is known to + * be a subtype of the returned prefix, this can improve results. + * 2. IThen, if the approximation result is a singleton reference C#x.type, we * replace by the widened type, which is usually more natural. - * 2. We need to handle the case where the prefix type does not have a member - * named `tp.name` anymmore. + * 3. Finally, we need to handle the case where the prefix type does not have a member + * named `tp.name` anymmore. In that case, we need to fall back to Bot..Top. */ override def derivedSelect(tp: NamedType, pre: Type) = - if (pre eq tp.prefix) tp - else if (tp.isTerm && variance > 0 && !pre.isInstanceOf[SingletonType]) - apply(tp.info.widenExpr) - else if (upper(pre).member(tp.name).exists) super.derivedSelect(tp, pre) - else range(tp.bottomType, tp.topType) + if (pre eq tp.prefix) + tp + else tryWiden(tp, tp.prefix) { + if (tp.isTerm && variance > 0 && !pre.isInstanceOf[SingletonType]) + apply(tp.info.widenExpr) + else if (upper(pre).member(tp.name).exists) + super.derivedSelect(tp, pre) + else + range(tp.bottomType, tp.topType) + } } widenMap(tp) diff --git a/tests/pos/i2945.scala b/tests/pos/i2945.scala new file mode 100644 index 000000000000..a8d4af597ba0 --- /dev/null +++ b/tests/pos/i2945.scala @@ -0,0 +1,13 @@ +object Test { + def test = { + object Hi { + type A = Int + } + + val x: Hi.A = 1 + + List(x) + } + + val hi: List[Int] = test // Used to fail because `test` had type `List[Any]` instead of `List[Int]` +} From 491fe97b5cfdffe5cae7ef6dd9b2a711ee454e1b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 15 Aug 2017 15:17:25 +0200 Subject: [PATCH 10/10] Avoid double avoidance when typing blocks For a Block, TypeAssigner does an avoid just for term symbols whereas Typer does one for all local symbols. Since Typer called TypeAssigner, we got a double avoidance pass. This is eliminated by assigning types directly in Typer without passing through TypeAssigner. --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 2e723b381ee0..9883a3f0f006 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -624,7 +624,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit else pt.notApplied val expr1 = typedExpr(tree.expr, ept)(exprCtx) ensureNoLocalRefs( - assignType(cpy.Block(tree)(stats1, expr1), stats1, expr1), pt, localSyms(stats1)) + cpy.Block(tree)(stats1, expr1).withType(expr1.tpe), pt, localSyms(stats1)) } def escapingRefs(block: Tree, localSyms: => List[Symbol])(implicit ctx: Context): collection.Set[NamedType] = {