From 0ebb84f22e7cfb5a118297766f7ff687c7917693 Mon Sep 17 00:00:00 2001 From: odersky Date: Sat, 12 Nov 2022 20:17:03 +0100 Subject: [PATCH 1/2] Avoid interpolating to `Nothing` in a special case If we have an selection `e.m` where e's type is a type variable T that does not have a lower bound in the current constraint, avoid interpolating `T` to nothing. Doing this would make the section ill-typed. By both interpolating `T` we leave the possibility open to instantiate it to its upper bound in couldInstantiateTypeVar. --- .../dotty/tools/dotc/typer/Inferencing.scala | 17 +++++++++++++++-- tests/pos/i16323.scala | 7 +++++++ 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 tests/pos/i16323.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index 9d2db773c4d4..cedb61779437 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -635,14 +635,27 @@ trait Inferencing { this: Typer => // isInstantiated needs to be checked again, since previous interpolations could already have // instantiated `tvar` through unification. val v = vs(tvar) + val tparam = tvar.origin if v == null then buf += ((tvar, 0)) - else if v.intValue != 0 then buf += ((tvar, v.intValue)) + else if v.intValue != 0 then + def hasLowerBound = + (constraint.nonParamBounds(tparam).lo ne tparam.underlying.bounds.lo) + || constraint.lower(tparam).nonEmpty + def avoidNothing = + (tp eq tvar) && pt.isInstanceOf[SelectionProto] + if v.intValue != 1 || hasLowerBound || !avoidNothing then + // Don't interpolate to lower if there is no lower bound other than + // the declared one, the current type is exactly `tvar`, and the expression is + // followed by a selection. In this case we should wait so that we can + // instantiate `tvar` to its upper bound later in `couldInstantiateTypeVar`. + // Test case is pos/i16323.scala. + buf += ((tvar, v.intValue)) else comparing(cmp => if !cmp.levelOK(tvar.nestingLevel, ctx.nestingLevel) then // Invariant: The type of a tree whose enclosing scope is level // N only contains type variables of level <= N. typr.println(i"instantiate nonvariant $tvar of level ${tvar.nestingLevel} to a type variable of level <= ${ctx.nestingLevel}, $constraint") - cmp.atLevel(ctx.nestingLevel, tvar.origin) + cmp.atLevel(ctx.nestingLevel, tparam) else typr.println(i"no interpolation for nonvariant $tvar in $state") ) diff --git a/tests/pos/i16323.scala b/tests/pos/i16323.scala new file mode 100644 index 000000000000..ac8c7d498b3a --- /dev/null +++ b/tests/pos/i16323.scala @@ -0,0 +1,7 @@ +class Foo { + def member: String = this.toString +} +def method[T](c: T => Boolean): T = ??? +val y: Foo => Boolean = _ => true +val x : String = "5" +val _ = method(y).member // error \ No newline at end of file From bd77a11af0b75a579e95cb7eb03fd76e22a91e4a Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 13 Nov 2022 10:56:10 +0100 Subject: [PATCH 2/2] Update compiler/src/dotty/tools/dotc/typer/Inferencing.scala Co-authored-by: Guillaume Martres --- compiler/src/dotty/tools/dotc/typer/Inferencing.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala index cedb61779437..8073ca29f789 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inferencing.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inferencing.scala @@ -641,9 +641,9 @@ trait Inferencing { this: Typer => def hasLowerBound = (constraint.nonParamBounds(tparam).lo ne tparam.underlying.bounds.lo) || constraint.lower(tparam).nonEmpty - def avoidNothing = + def isSelectionPrefix = (tp eq tvar) && pt.isInstanceOf[SelectionProto] - if v.intValue != 1 || hasLowerBound || !avoidNothing then + if v.intValue != 1 || hasLowerBound || !isSelectionPrefix then // Don't interpolate to lower if there is no lower bound other than // the declared one, the current type is exactly `tvar`, and the expression is // followed by a selection. In this case we should wait so that we can