Skip to content

Commit 3023607

Browse files
Backport "Help implement Metals' infer expected type feature" to LTS (#22108)
Backports #21390 to the 3.3.5. PR submitted by the release tooling. [skip ci]
2 parents d1bf6ee + 493cf40 commit 3023607

17 files changed

+670
-62
lines changed

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2945,9 +2945,10 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
29452945

29462946
/** The trace of comparison operations when performing `op` */
29472947
def explained[T](op: ExplainingTypeComparer => T, header: String = "Subtype trace:", short: Boolean)(using Context): String =
2948-
val cmp = explainingTypeComparer(short)
2949-
inSubComparer(cmp)(op)
2950-
cmp.lastTrace(header)
2948+
explaining(cmp => { op(cmp); cmp.lastTrace(header) }, short)
2949+
2950+
def explaining[T](op: ExplainingTypeComparer => T, short: Boolean)(using Context): T =
2951+
inSubComparer(explainingTypeComparer(short))(op)
29512952

29522953
def tracked[T](op: TrackingTypeComparer => T)(using Context): T =
29532954
inSubComparer(trackingTypeComparer)(op)
@@ -3107,6 +3108,9 @@ object TypeComparer {
31073108
def explained[T](op: ExplainingTypeComparer => T, header: String = "Subtype trace:", short: Boolean = false)(using Context): String =
31083109
comparing(_.explained(op, header, short))
31093110

3111+
def explaining[T](op: ExplainingTypeComparer => T, short: Boolean = false)(using Context): T =
3112+
comparing(_.explaining(op, short))
3113+
31103114
def tracked[T](op: TrackingTypeComparer => T)(using Context): T =
31113115
comparing(_.tracked(op))
31123116
}
@@ -3321,7 +3325,7 @@ class ExplainingTypeComparer(initctx: Context, short: Boolean) extends TypeCompa
33213325
override def recur(tp1: Type, tp2: Type): Boolean =
33223326
def moreInfo =
33233327
if Config.verboseExplainSubtype || ctx.settings.verbose.value
3324-
then s" ${tp1.getClass} ${tp2.getClass}"
3328+
then s" ${tp1.className} ${tp2.className}"
33253329
else ""
33263330
val approx = approxState
33273331
def approxStr = if short then "" else approx.show

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

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -704,11 +704,22 @@ object TypeOps:
704704
val hiBound = instantiate(bounds.hi, skolemizedArgTypes)
705705
val loBound = instantiate(bounds.lo, skolemizedArgTypes)
706706

707-
def check(using Context) = {
708-
if (!(lo <:< hiBound)) violations += ((arg, "upper", hiBound))
709-
if (!(loBound <:< hi)) violations += ((arg, "lower", loBound))
707+
def check(tp1: Type, tp2: Type, which: String, bound: Type)(using Context) = {
708+
val isSub = TypeComparer.explaining { cmp =>
709+
val isSub = cmp.isSubType(tp1, tp2)
710+
if !isSub then
711+
if !ctx.typerState.constraint.domainLambdas.isEmpty then
712+
typr.println(i"${ctx.typerState.constraint}")
713+
if !ctx.gadt.symbols.isEmpty then
714+
typr.println(i"${ctx.gadt}")
715+
typr.println(cmp.lastTrace(i"checkOverlapsBounds($lo, $hi, $arg, $bounds)($which)"))
716+
//trace.dumpStack()
717+
isSub
718+
}//(using ctx.fresh.setSetting(ctx.settings.verbose, true)) // uncomment to enable moreInfo in ExplainingTypeComparer recur
719+
if !isSub then violations += ((arg, which, bound))
710720
}
711-
check(using checkCtx)
721+
check(lo, hiBound, "upper", hiBound)(using checkCtx)
722+
check(loBound, hi, "lower", loBound)(using checkCtx)
712723
}
713724

714725
def loop(args: List[Tree], boundss: List[TypeBounds]): Unit = args match

compiler/src/dotty/tools/dotc/reporting/trace.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,18 @@ object trace extends TraceSyntax:
2727
object log extends TraceSyntax:
2828
inline def isEnabled: true = true
2929
protected val isForced = false
30+
31+
def dumpStack(limit: Int = -1): Unit = {
32+
val out = Console.out
33+
val exc = new Exception("Dump Stack")
34+
var stack = exc.getStackTrace
35+
.filter(e => !e.getClassName.startsWith("dotty.tools.dotc.reporting.TraceSyntax"))
36+
.filter(e => !e.getClassName.startsWith("dotty.tools.dotc.reporting.trace"))
37+
if limit >= 0 then
38+
stack = stack.take(limit)
39+
exc.setStackTrace(stack)
40+
exc.printStackTrace(out)
41+
}
3042
end trace
3143

3244
/** This module is carefully optimized to give zero overhead if Config.tracingEnabled

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ trait Applications extends Compatibility {
517517
fail(TypeMismatch(methType.resultType, resultType, None))
518518

519519
// match all arguments with corresponding formal parameters
520-
matchArgs(orderedArgs, methType.paramInfos, 0)
520+
if success then matchArgs(orderedArgs, methType.paramInfos, 0)
521521
case _ =>
522522
if (methType.isError) ok = false
523523
else fail(em"$methString does not take parameters")
@@ -595,7 +595,7 @@ trait Applications extends Compatibility {
595595
* @param n The position of the first parameter in formals in `methType`.
596596
*/
597597
def matchArgs(args: List[Arg], formals: List[Type], n: Int): Unit =
598-
if (success) formals match {
598+
formals match {
599599
case formal :: formals1 =>
600600

601601
def checkNoVarArg(arg: Arg) =
@@ -807,7 +807,9 @@ trait Applications extends Compatibility {
807807
init()
808808

809809
def addArg(arg: Tree, formal: Type): Unit =
810-
typedArgBuf += adapt(arg, formal.widenExpr)
810+
val typedArg = adapt(arg, formal.widenExpr)
811+
typedArgBuf += typedArg
812+
ok = ok & !typedArg.tpe.isError
811813

812814
def makeVarArg(n: Int, elemFormal: Type): Unit = {
813815
val args = typedArgBuf.takeRight(n).toList
@@ -872,7 +874,7 @@ trait Applications extends Compatibility {
872874
var typedArgs = typedArgBuf.toList
873875
def app0 = cpy.Apply(app)(normalizedFun, typedArgs) // needs to be a `def` because typedArgs can change later
874876
val app1 =
875-
if (!success || typedArgs.exists(_.tpe.isError)) app0.withType(UnspecifiedErrorType)
877+
if !success then app0.withType(UnspecifiedErrorType)
876878
else {
877879
if !sameSeq(args, orderedArgs)
878880
&& !isJavaAnnotConstr(methRef.symbol)

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

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -178,25 +178,12 @@ object Inferencing {
178178
&& ctx.typerState.constraint.contains(tvar)
179179
&& {
180180
var fail = false
181-
val direction = instDirection(tvar.origin)
182-
if minimizeSelected then
183-
if direction <= 0 && tvar.hasLowerBound then
184-
instantiate(tvar, fromBelow = true)
185-
else if direction >= 0 && tvar.hasUpperBound then
186-
instantiate(tvar, fromBelow = false)
187-
// else hold off instantiating unbounded unconstrained variable
188-
else if direction != 0 then
189-
instantiate(tvar, fromBelow = direction < 0)
190-
else if variance >= 0 && tvar.hasLowerBound then
191-
instantiate(tvar, fromBelow = true)
192-
else if (variance > 0 || variance == 0 && !tvar.hasUpperBound)
193-
&& force.ifBottom == IfBottom.ok
194-
then // if variance == 0, prefer upper bound if one is given
195-
instantiate(tvar, fromBelow = true)
196-
else if variance >= 0 && force.ifBottom == IfBottom.fail then
197-
fail = true
198-
else
199-
toMaximize = tvar :: toMaximize
181+
instDecision(tvar, variance, minimizeSelected, force.ifBottom) match
182+
case Decision.Min => instantiate(tvar, fromBelow = true)
183+
case Decision.Max => instantiate(tvar, fromBelow = false)
184+
case Decision.Skip => // hold off instantiating unbounded unconstrained variable
185+
case Decision.Fail => fail = true
186+
case Decision.ToMax => toMaximize ::= tvar
200187
!fail && foldOver(x, tvar)
201188
}
202189
case tp => foldOver(x, tp)
@@ -385,9 +372,32 @@ object Inferencing {
385372
if (!cmp.isSubTypeWhenFrozen(constrained.lo, original.lo)) 1 else 0
386373
val approxAbove =
387374
if (!cmp.isSubTypeWhenFrozen(original.hi, constrained.hi)) 1 else 0
375+
//println(i"instDirection($param) = $approxAbove - $approxBelow original=[$original] constrained=[$constrained]")
388376
approxAbove - approxBelow
389377
}
390378

379+
/** The instantiation decision for given poly param computed from the constraint. */
380+
enum Decision { case Min; case Max; case ToMax; case Skip; case Fail }
381+
private def instDecision(tvar: TypeVar, v: Int, minimizeSelected: Boolean, ifBottom: IfBottom)(using Context): Decision =
382+
import Decision.*
383+
val direction = instDirection(tvar.origin)
384+
val dec = if minimizeSelected then
385+
if direction <= 0 && tvar.hasLowerBound then Min
386+
else if direction >= 0 && tvar.hasUpperBound then Max
387+
else Skip
388+
else if direction != 0 then if direction < 0 then Min else Max
389+
else if tvar.hasLowerBound then if v >= 0 then Min else ToMax
390+
else ifBottom match
391+
// What's left are unconstrained tvars with at most a non-Any param upperbound:
392+
// * IfBottom.flip will always maximise to the param upperbound, for all variances
393+
// * IfBottom.fail will fail the IFD check, for covariant or invariant tvars, maximise contravariant tvars
394+
// * IfBottom.ok will minimise to Nothing covariant and unbounded invariant tvars, and max to Any the others
395+
case IfBottom.ok => if v > 0 || v == 0 && !tvar.hasUpperBound then Min else ToMax // prefer upper bound if one is given
396+
case IfBottom.fail => if v >= 0 then Fail else ToMax
397+
case ifBottom_flip => ToMax
398+
//println(i"instDecision($tvar, v=v, minimizedSelected=$minimizeSelected, $ifBottom) dir=$direction = $dec")
399+
dec
400+
391401
/** Following type aliases and stripping refinements and annotations, if one arrives at a
392402
* class type reference where the class has a companion module, a reference to
393403
* that companion module. Otherwise NoType
@@ -584,7 +594,7 @@ trait Inferencing { this: Typer =>
584594

585595
val ownedVars = state.ownedVars
586596
if (ownedVars ne locked) && !ownedVars.isEmpty then
587-
val qualifying = ownedVars -- locked
597+
val qualifying = (ownedVars -- locked).toList
588598
if (!qualifying.isEmpty) {
589599
typr.println(i"interpolate $tree: ${tree.tpe.widen} in $state, pt = $pt, owned vars = ${state.ownedVars.toList}%, %, qualifying = ${qualifying.toList}%, %, previous = ${locked.toList}%, % / ${state.constraint}")
590600
val resultAlreadyConstrained =
@@ -620,6 +630,10 @@ trait Inferencing { this: Typer =>
620630

621631
def constraint = state.constraint
622632

633+
trace(i"interpolateTypeVars($tree: ${tree.tpe}, $pt, $qualifying)", typr, (_: Any) => i"$qualifying\n$constraint\n${ctx.gadt}") {
634+
//println(i"$constraint")
635+
//println(i"${ctx.gadt}")
636+
623637
/** Values of this type report type variables to instantiate with variance indication:
624638
* +1 variable appears covariantly, can be instantiated from lower bound
625639
* -1 variable appears contravariantly, can be instantiated from upper bound
@@ -737,6 +751,7 @@ trait Inferencing { this: Typer =>
737751
end doInstantiate
738752

739753
doInstantiate(filterByDeps(toInstantiate))
754+
}
740755
}
741756
end if
742757
tree

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ import config.Printers.typr
1616
import Inferencing.*
1717
import ErrorReporting.*
1818
import util.SourceFile
19+
import util.Spans.{NoSpan, Span}
1920
import TypeComparer.necessarySubType
21+
import reporting.*
2022

2123
import scala.annotation.internal.sharable
22-
import dotty.tools.dotc.util.Spans.{NoSpan, Span}
2324

2425
object ProtoTypes {
2526

@@ -81,6 +82,7 @@ object ProtoTypes {
8182
* fits the given expected result type.
8283
*/
8384
def constrainResult(mt: Type, pt: Type)(using Context): Boolean =
85+
trace(i"constrainResult($mt, $pt)", typr):
8486
val savedConstraint = ctx.typerState.constraint
8587
val res = pt.widenExpr match {
8688
case pt: FunProto =>

compiler/src/dotty/tools/dotc/util/Signatures.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,7 @@ object Signatures {
651651
*
652652
* @param err The error message to inspect.
653653
* @param params The parameters that were given at the call site.
654-
* @param alreadyCurried Index of paramss we are currently in.
654+
* @param paramssIndex Index of paramss we are currently in.
655655
*
656656
* @return A pair composed of the index of the best alternative (0 if no alternatives
657657
* were found), and the list of alternatives.

compiler/test/dotc/pos-test-pickling.blacklist

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,7 @@ i15525.scala
113113
i19955a.scala
114114
i19955b.scala
115115
i20053b.scala
116+
117+
118+
# LTS specific
119+
i21390.TrieMap.scala

compiler/test/dotc/run-test-pickling.blacklist

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,3 @@ t6138-2
4545
i12656.scala
4646
trait-static-forwarder
4747
i17255
48-

compiler/test/dotty/tools/dotc/typer/InstantiateModel.scala

Lines changed: 23 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,16 @@ package typer
44

55
// Modelling the decision in IsFullyDefined
66
object InstantiateModel:
7-
enum LB { case NN; case LL; case L1 }; import LB.*
8-
enum UB { case AA; case UU; case U1 }; import UB.*
9-
enum Var { case V; case NotV }; import Var.*
10-
enum MSe { case M; case NotM }; import MSe.*
11-
enum Bot { case Fail; case Ok; case Flip }; import Bot.*
12-
enum Act { case Min; case Max; case ToMax; case Skip; case False }; import Act.*
7+
enum LB { case NN; case LL; case L1 }; import LB.*
8+
enum UB { case AA; case UU; case U1 }; import UB.*
9+
enum Decision { case Min; case Max; case ToMax; case Skip; case Fail }; import Decision.*
1310

1411
// NN/AA = Nothing/Any
1512
// LL/UU = the original bounds, on the type parameter
1613
// L1/U1 = the constrained bounds, on the type variable
17-
// V = variance >= 0 ("non-contravariant")
18-
// MSe = minimisedSelected
19-
// Bot = IfBottom
2014
// ToMax = delayed maximisation, via addition to toMaximize
2115
// Skip = minimisedSelected "hold off instantiating"
22-
// False = return false
16+
// Fail = IfBottom.fail's bail option
2317

2418
// there are 9 combinations:
2519
// # | LB | UB | d | // d = direction
@@ -34,24 +28,27 @@ object InstantiateModel:
3428
// 8 | NN | UU | 0 | T <: UU
3529
// 9 | NN | AA | 0 | T
3630

37-
def decide(lb: LB, ub: UB, v: Var, bot: Bot, m: MSe): Act = (lb, ub) match
31+
def instDecision(lb: LB, ub: UB, v: Int, ifBottom: IfBottom, min: Boolean) = (lb, ub) match
3832
case (L1, AA) => Min
3933
case (L1, UU) => Min
4034
case (LL, U1) => Max
4135
case (NN, U1) => Max
4236

43-
case (L1, U1) => if m==M || v==V then Min else ToMax
44-
case (LL, UU) => if m==M || v==V then Min else ToMax
45-
case (LL, AA) => if m==M || v==V then Min else ToMax
46-
47-
case (NN, UU) => bot match
48-
case _ if m==M => Max
49-
//case Ok if v==V => Min // removed, i14218 fix
50-
case Fail if v==V => False
51-
case _ => ToMax
52-
53-
case (NN, AA) => bot match
54-
case _ if m==M => Skip
55-
case Ok if v==V => Min
56-
case Fail if v==V => False
57-
case _ => ToMax
37+
case (L1, U1) => if min then Min else pickVar(v, Min, Min, ToMax)
38+
case (LL, UU) => if min then Min else pickVar(v, Min, Min, ToMax)
39+
case (LL, AA) => if min then Min else pickVar(v, Min, Min, ToMax)
40+
41+
case (NN, UU) => ifBottom match
42+
case _ if min => Max
43+
case IfBottom.ok => pickVar(v, Min, ToMax, ToMax)
44+
case IfBottom.fail => pickVar(v, Fail, Fail, ToMax)
45+
case IfBottom.flip => ToMax
46+
47+
case (NN, AA) => ifBottom match
48+
case _ if min => Skip
49+
case IfBottom.ok => pickVar(v, Min, Min, ToMax)
50+
case IfBottom.fail => pickVar(v, Fail, Fail, ToMax)
51+
case IfBottom.flip => ToMax
52+
53+
def pickVar[A](v: Int, cov: A, inv: A, con: A) =
54+
if v > 0 then cov else if v == 0 then inv else con

0 commit comments

Comments
 (0)