Skip to content

Commit 408298d

Browse files
authored
Handle assertion error in TyperState (#23665)
#23609 triggers an assertion error in TyperState. The relevant explanation seems to be in ProtoTypes.scala: ```scala // To respect the pre-condition of `mergeConstraintWith` and keep // `protoTyperState` committable we must ensure that it does not // contain any type variable which don't already exist in the passed // TyperState. This is achieved by instantiating any such type // variable. NOTE: this does not suffice to discard type variables // in ancestors of `protoTyperState`, if this situation ever // comes up, an assertion in TyperState will trigger and this code // will need to be generalized. ``` We should go to the bottom of it and fix the assertion. But before that's done this PR offers a temporary hack to catch the exception when it is triggered from a new code path created by PR #23532. This should fix the regression reported in #23609. We should leave the issue open as a reminder that we still need a better fix. Also: handle crash due to missing span in a migration helper.
1 parent a9c214f commit 408298d

File tree

3 files changed

+19
-5
lines changed

3 files changed

+19
-5
lines changed

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ object TyperState {
2828

2929
opaque type Snapshot = (Constraint, TypeVars, LevelMap)
3030

31+
class BadTyperStateAssertion(msg: String) extends AssertionError(msg)
32+
3133
extension (ts: TyperState)
3234
def snapshot()(using Context): Snapshot =
3335
(ts.constraint, ts.ownedVars, ts.upLevels)
@@ -43,7 +45,7 @@ object TyperState {
4345
}
4446

4547
class TyperState() {
46-
import TyperState.LevelMap
48+
import TyperState.{LevelMap, BadTyperStateAssertion}
4749

4850
private var myId: Int = uninitialized
4951
def id: Int = myId
@@ -269,8 +271,10 @@ class TyperState() {
269271
*/
270272
private def includeVar(tvar: TypeVar)(using Context): Unit =
271273
val oldState = tvar.owningState.nn.get
272-
assert(oldState == null || !oldState.isCommittable,
273-
i"$this attempted to take ownership of $tvar which is already owned by committable $oldState")
274+
275+
if oldState != null && oldState.isCommittable then
276+
throw BadTyperStateAssertion(
277+
i"$this attempted to take ownership of $tvar which is already owned by committable $oldState")
274278
tvar.owningState = new WeakReference(this)
275279
ownedVars += tvar
276280

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ trait Migrations:
162162
private def checkParentheses(tree: Tree, pt: FunProto)(using Context): Boolean =
163163
val ptSpan = pt.args.head.span
164164
ptSpan.exists
165+
&& tree.span.exists
165166
&& ctx.source.content
166167
.slice(tree.span.end, ptSpan.start)
167168
.exists(_ == '(')

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4368,11 +4368,20 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
43684368
|| !isFullyDefined(formal, ForceDegree.none)
43694369
// more context might constrain type variables which could make implicit scope larger
43704370

4371+
// Try to constrain the result using `pt1`, but back out if a BadTyperStateAssertion
4372+
// is thrown. TODO Find out why the bad typer state arises and prevent it. The try-catch
4373+
// is a temporary hack to keep projects compiling that would fail otherwise due to
4374+
// searching more arguments to instantiate implicits (PR #23532). A failing project
4375+
// is described in issue #23609.
4376+
def tryConstrainResult(pt: Type): Boolean =
4377+
try constrainResult(tree.symbol, wtp, pt)
4378+
catch case ex: TyperState.BadTyperStateAssertion => false
4379+
43714380
arg.tpe match
43724381
case failed: SearchFailureType if canProfitFromMoreConstraints =>
43734382
val pt1 = pt.deepenProtoTrans
4374-
if (pt1 `ne` pt) && (pt1 ne sharpenedPt) && constrainResult(tree.symbol, wtp, pt1)
4375-
then return implicitArgs(formals, argIndex, pt1)
4383+
if (pt1 `ne` pt) && (pt1 ne sharpenedPt) && tryConstrainResult(pt1) then
4384+
return implicitArgs(formals, argIndex, pt1)
43764385
case _ =>
43774386

43784387
arg.tpe match

0 commit comments

Comments
 (0)