Skip to content

Commit c5e595e

Browse files
authored
Merge pull request #3661 from dotty-staging/fix-#3538
Fix #3538: Better invalidation of typed arguments in FunProtos
2 parents f918a6c + d8b7566 commit c5e595e

File tree

6 files changed

+51
-14
lines changed

6 files changed

+51
-14
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,10 @@ abstract class Constraint extends Showable {
144144

145145
/** Check that constraint only refers to TypeParamRefs bound by itself */
146146
def checkClosed()(implicit ctx: Context): Unit
147+
148+
/** Constraint has not yet been retracted from a typer state */
149+
def isRetracted: Boolean
150+
151+
/** Indicate that constraint has been retracted from a typer state */
152+
def markRetracted(): Unit
147153
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,14 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
570570
private def checkNonCyclic(param: TypeParamRef)(implicit ctx: Context): Unit =
571571
assert(!isLess(param, param), i"cyclic constraint involving $param in $this")
572572

573+
// ---------- Invalidation -------------------------------------------
574+
575+
private var retracted = false
576+
577+
def isRetracted: Boolean = retracted
578+
579+
def markRetracted(): Unit = retracted = true
580+
573581
// ---------- toText -----------------------------------------------------
574582

575583
override def toText(printer: Printer): Text = {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
118118
if (recCount < Config.LogPendingSubTypesThreshold) firstTry(tp1, tp2)
119119
else monitoredIsSubType(tp1, tp2)
120120
recCount = recCount - 1
121-
if (!result) constraint = saved
121+
if (!result) state.resetConstraintTo(saved)
122122
else if (recCount == 0 && needsGc) {
123123
state.gc()
124124
needsGc = false
@@ -129,7 +129,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
129129
case NonFatal(ex) =>
130130
if (ex.isInstanceOf[AssertionError]) showGoal(tp1, tp2)
131131
recCount -= 1
132-
constraint = saved
132+
state.resetConstraintTo(saved)
133133
successCount = savedSuccessCount
134134
throw ex
135135
}

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ class TyperState(previous: TyperState /* | Null */) extends DotClass with Showab
3333
myConstraint = c
3434
}
3535

36+
/** Reset constraint to `c` and mark current constraint as retracted if it differs from `c` */
37+
def resetConstraintTo(c: Constraint) = {
38+
if (c `ne` myConstraint) myConstraint.markRetracted()
39+
myConstraint = c
40+
}
41+
3642
private val previousConstraint =
3743
if (previous == null) constraint else previous.constraint
3844

@@ -90,8 +96,8 @@ class TyperState(previous: TyperState /* | Null */) extends DotClass with Showab
9096

9197
/** Test using `op`, restoring typerState to previous state afterwards */
9298
def test[T](op: => T): T = {
93-
val savedReporter = myReporter
9499
val savedConstraint = myConstraint
100+
val savedReporter = myReporter
95101
val savedCommittable = myIsCommittable
96102
val savedCommitted = isCommitted
97103
myIsCommittable = false
@@ -105,8 +111,8 @@ class TyperState(previous: TyperState /* | Null */) extends DotClass with Showab
105111
}
106112
try op
107113
finally {
114+
resetConstraintTo(savedConstraint)
108115
myReporter = savedReporter
109-
myConstraint = savedConstraint
110116
myIsCommittable = savedCommittable
111117
isCommitted = savedCommitted
112118
}

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

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ object ProtoTypes {
6767
case _ =>
6868
true
6969
}
70-
if (!res) ctx.typerState.constraint = savedConstraint
70+
if (!res) ctx.typerState.resetConstraintTo(savedConstraint)
7171
res
7272
}
7373
}
@@ -183,8 +183,8 @@ object ProtoTypes {
183183
/** A map in which typed arguments can be stored to be later integrated in `typedArgs`. */
184184
private[this] var myTypedArg: SimpleIdentityMap[untpd.Tree, Tree] = SimpleIdentityMap.Empty
185185

186-
/** A map recording the typer states in which arguments stored in myTypedArg were typed */
187-
private[this] var evalState: SimpleIdentityMap[untpd.Tree, TyperState] = SimpleIdentityMap.Empty
186+
/** A map recording the typer states and constraints in which arguments stored in myTypedArg were typed */
187+
private[this] var evalState: SimpleIdentityMap[untpd.Tree, (TyperState, Constraint)] = SimpleIdentityMap.Empty
188188

189189
def isMatchedBy(tp: Type)(implicit ctx: Context) =
190190
typer.isApplicable(tp, Nil, typedArgs, resultType)
@@ -195,17 +195,19 @@ object ProtoTypes {
195195

196196
override def notApplied = WildcardType
197197

198-
/** Forget the types of any arguments that have been typed producing a constraint in a
199-
* typer state that is not yet committed into the one of the current context `ctx`.
198+
/** Forget the types of any arguments that have been typed producing a constraint
199+
* - that is in a typer state that is not yet committed into the one of the current context `ctx`,
200+
* - or that has been retracted from its typestate because oif a failed operation.
200201
* This is necessary to avoid "orphan" TypeParamRefs that are referred to from
201202
* type variables in the typed arguments, but that are not registered in the
202-
* current constraint. A test case is pos/t1756.scala.
203+
* current constraint. Test cases are pos/t1756.scala and pos/i3538.scala.
203204
* @return True if all arguments have types (in particular, no types were forgotten).
204205
*/
205206
def allArgTypesAreCurrent()(implicit ctx: Context): Boolean = {
206-
evalState foreachBinding { (arg, tstate) =>
207-
if (tstate.uncommittedAncestor.constraint ne ctx.typerState.constraint) {
208-
typr.println(i"need to invalidate $arg / ${myTypedArg(arg)}, ${tstate.constraint}, current = ${ctx.typerState.constraint}")
207+
evalState foreachBinding { (arg, tstateConstr) =>
208+
if ((tstateConstr._1.uncommittedAncestor.constraint `ne` ctx.typerState.constraint) ||
209+
tstateConstr._2.isRetracted) {
210+
typr.println(i"need to invalidate $arg / ${myTypedArg(arg)}, ${tstateConstr._2}, current = ${ctx.typerState.constraint}")
209211
myTypedArg = myTypedArg.remove(arg)
210212
evalState = evalState.remove(arg)
211213
}
@@ -219,7 +221,7 @@ object ProtoTypes {
219221
targ = typerFn(arg)
220222
if (!ctx.reporter.hasPending) {
221223
myTypedArg = myTypedArg.updated(arg, targ)
222-
evalState = evalState.updated(arg, ctx.typerState)
224+
evalState = evalState.updated(arg, (ctx.typerState, ctx.typerState.constraint))
223225
}
224226
}
225227
targ

tests/pos/i3538.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
2+
class Inv[T]
3+
4+
class Test {
5+
implicit class Wrong(test: Test) {
6+
def right: Any = ???
7+
}
8+
implicit class Right(test: Test) {
9+
def right(node: Any): Any = ???
10+
}
11+
12+
def inv[T](x: T): Inv[T] = ???
13+
14+
(this).right(inv(1))
15+
}

0 commit comments

Comments
 (0)