Skip to content

Commit 9f10d77

Browse files
committed
Refine handling of bottom types in inferredParam
We need to split `notBottom` into two states - flipBottom the old noBottom: when encountering bottom, maximimze instead - failBottom when encountering bottom, return false That way, we can have an inferredParamType which is closer to the old one. Try first to instantiate the formal type known from the context, but fail on bottom. If that fails, compute target type and proceed as before.
1 parent 914787c commit 9f10d77

File tree

5 files changed

+29
-21
lines changed

5 files changed

+29
-21
lines changed

compiler/src/dotty/tools/dotc/core/TypeOps.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import typer.Applications._
1919
import typer.ProtoTypes._
2020
import typer.ForceDegree
2121
import typer.Inferencing.isFullyDefined
22+
import typer.IfBottom
2223

2324
import scala.annotation.internal.sharable
2425

@@ -644,7 +645,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
644645
tvar =>
645646
!(ctx.typerState.constraint.entry(tvar.origin) `eq` tvar.origin.underlying) ||
646647
(tvar `eq` removeThisType.prefixTVar),
647-
allowBottom = false
648+
IfBottom.flip
648649
)
649650

650651
// If parent contains a reference to an abstract type, then we should

compiler/src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1865,7 +1865,7 @@ trait Applications extends Compatibility {
18651865
if (isPartial) defn.PartialFunctionOf(commonParamTypes.head, WildcardType)
18661866
else defn.FunctionOf(commonParamTypes, WildcardType)
18671867
overload.println(i"pretype arg $arg with expected type $commonFormal")
1868-
if (commonParamTypes.forall(isFullyDefined(_, ForceDegree.noBottom)))
1868+
if (commonParamTypes.forall(isFullyDefined(_, ForceDegree.flipBottom)))
18691869
pt.typedArg(arg, commonFormal)(ctx.addMode(Mode.ImplicitsEnabled))
18701870
}
18711871
case None =>

compiler/src/dotty/tools/dotc/typer/Inferencing.scala

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ object Inferencing {
5555
def instantiateSelected(tp: Type, tvars: List[Type])(implicit ctx: Context): Unit =
5656
if (tvars.nonEmpty)
5757
IsFullyDefinedAccumulator(
58-
ForceDegree.Value(tvars.contains, allowBottom = false), minimizeSelected = true
58+
ForceDegree.Value(tvars.contains, IfBottom.flip), minimizeSelected = true
5959
).process(tp)
6060

6161
/** Instantiate any type variables in `tp` whose bounds contain a reference to
@@ -98,7 +98,7 @@ object Inferencing {
9898
9999
* If (1) and (2) do not apply, and minimizeSelected is not set:
100100
* 6: T is maximized if it appears only contravariantly in the given type,
101-
* or if forceDegree is `noBottom` and T has no lower bound different from Nothing.
101+
* or if forceDegree is `flipBottom` and T has no lower bound different from Nothing.
102102
* 7. Otherwise, T is minimized.
103103
*
104104
* The instantiation for (6) and (7) is done in two phases:
@@ -132,8 +132,10 @@ object Inferencing {
132132
if tvar.hasLowerBound then instantiate(tvar, fromBelow = true)
133133
else if tvar.hasUpperBound then instantiate(tvar, fromBelow = false)
134134
else () // hold off instantiating unbounded unconstrained variables
135-
else if variance >= 0 && (force.allowBottom || tvar.hasLowerBound) then
135+
else if variance >= 0 && (force.ifBottom == IfBottom.ok || tvar.hasLowerBound) then
136136
instantiate(tvar, fromBelow = true)
137+
else if variance >= 0 && force.ifBottom == IfBottom.fail then
138+
return false
137139
else
138140
toMaximize = tvar :: toMaximize
139141
foldOver(x, tvar)
@@ -514,9 +516,13 @@ trait Inferencing { this: Typer =>
514516

515517
/** An enumeration controlling the degree of forcing in "is-dully-defined" checks. */
516518
@sharable object ForceDegree {
517-
class Value(val appliesTo: TypeVar => Boolean, val allowBottom: Boolean)
518-
val none: Value = new Value(_ => false, allowBottom = true)
519-
val all: Value = new Value(_ => true, allowBottom = true)
520-
val noBottom: Value = new Value(_ => true, allowBottom = false)
519+
class Value(val appliesTo: TypeVar => Boolean, val ifBottom: IfBottom)
520+
val none: Value = new Value(_ => false, IfBottom.ok)
521+
val all: Value = new Value(_ => true, IfBottom.ok)
522+
val failBottom: Value = new Value(_ => true, IfBottom.fail)
523+
val flipBottom: Value = new Value(_ => true, IfBottom.flip)
521524
}
522525

526+
enum IfBottom:
527+
case ok, fail, flip
528+

compiler/src/dotty/tools/dotc/typer/Inliner.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
276276
// Make sure all type arguments to the call are fully determined,
277277
// but continue if that's not achievable (or else i7459.scala would crash).
278278
for arg <- callTypeArgs do
279-
isFullyDefined(arg.tpe, ForceDegree.noBottom)
279+
isFullyDefined(arg.tpe, ForceDegree.flipBottom)
280280

281281
/** A map from parameter names of the inlineable method to references of the actual arguments.
282282
* For a type argument this is the full argument type.

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,7 @@ class Typer extends Namer
612612
var templ1 = templ
613613
def isEligible(tp: Type) = tp.exists && !tp.typeSymbol.is(Final) && !tp.isRef(defn.AnyClass)
614614
if (templ1.parents.isEmpty &&
615-
isFullyDefined(pt, ForceDegree.noBottom) &&
615+
isFullyDefined(pt, ForceDegree.flipBottom) &&
616616
isSkolemFree(pt) &&
617617
isEligible(pt.underlyingClassRef(refinementOK = false)))
618618
templ1 = cpy.Template(templ)(parents = untpd.TypeTree(pt) :: Nil)
@@ -1034,7 +1034,7 @@ class Typer extends Namer
10341034
// try to instantiate `pt` if this is possible. If it does not
10351035
// work the error will be reported later in `inferredParam`,
10361036
// when we try to infer the parameter type.
1037-
isFullyDefined(pt, ForceDegree.noBottom)
1037+
isFullyDefined(pt, ForceDegree.flipBottom)
10381038
case _ =>
10391039
}
10401040

@@ -1049,17 +1049,18 @@ class Typer extends Namer
10491049
*
10501050
* The inference makes three attempts:
10511051
*
1052-
* 1. If the expected type `S` is already fully defined pick this one.
1052+
* 1. If the expected type `S` is already fully defined under ForceDegree.failBottom
1053+
* pick this one.
10531054
* 2. Compute the target type `T` and make it known that `S <: T`.
1054-
* If the expected type `S` can be fully defined under ForceDegree.noBottom,
1055+
* If the expected type `S` can be fully defined under ForceDegree.flipBottom,
10551056
* pick this one (this might use the fact that S <: T for an upper approximation).
1056-
* 3. Otherwise, if the target type `T` can be fully defined under ForceDegree.noBottom,
1057+
* 3. Otherwise, if the target type `T` can be fully defined under ForceDegree.flipBottom,
10571058
* pick this one.
10581059
*
10591060
* If all attempts fail, issue a "missing parameter type" error.
10601061
*/
10611062
def inferredParamType(param: untpd.ValDef, formal: Type): Type =
1062-
if isFullyDefined(formal, ForceDegree.none) then return formal
1063+
if isFullyDefined(formal, ForceDegree.failBottom) then return formal
10631064
val target = calleeType.widen match
10641065
case mtpe: MethodType =>
10651066
val pos = paramIndex(param.name)
@@ -1069,8 +1070,8 @@ class Typer extends Namer
10691070
else NoType
10701071
case _ => NoType
10711072
if target.exists then formal <:< target
1072-
if isFullyDefined(formal, ForceDegree.noBottom) then formal
1073-
else if target.exists && isFullyDefined(target, ForceDegree.noBottom) then target
1073+
if isFullyDefined(formal, ForceDegree.flipBottom) then formal
1074+
else if target.exists && isFullyDefined(target, ForceDegree.flipBottom) then target
10741075
else errorType(AnonymousFunctionMissingParamType(param, params, tree, formal), param.sourcePos)
10751076

10761077
def protoFormal(i: Int): Type =
@@ -1079,7 +1080,7 @@ class Typer extends Namer
10791080

10801081
/** Is `formal` a product type which is elementwise compatible with `params`? */
10811082
def ptIsCorrectProduct(formal: Type) =
1082-
isFullyDefined(formal, ForceDegree.noBottom) &&
1083+
isFullyDefined(formal, ForceDegree.flipBottom) &&
10831084
(defn.isProductSubType(formal) || formal.derivesFrom(defn.PairClass)) &&
10841085
productSelectorTypes(formal, tree.sourcePos).corresponds(params) {
10851086
(argType, param) =>
@@ -1393,7 +1394,7 @@ class Typer extends Namer
13931394
}
13941395
case _ =>
13951396
tree.withType(
1396-
if (isFullyDefined(pt, ForceDegree.noBottom)) pt
1397+
if (isFullyDefined(pt, ForceDegree.flipBottom)) pt
13971398
else if (ctx.reporter.errorsReported) UnspecifiedErrorType
13981399
else errorType(i"cannot infer type; expected type $pt is not fully defined", tree.sourcePos))
13991400
}
@@ -3068,7 +3069,7 @@ class Typer extends Namer
30683069
pt match {
30693070
case SAMType(sam)
30703071
if wtp <:< sam.toFunctionType() =>
3071-
// was ... && isFullyDefined(pt, ForceDegree.noBottom)
3072+
// was ... && isFullyDefined(pt, ForceDegree.flipBottom)
30723073
// but this prevents case blocks from implementing polymorphic partial functions,
30733074
// since we do not know the result parameter a priori. Have to wait until the
30743075
// body is typechecked.

0 commit comments

Comments
 (0)