Skip to content

Commit ec07dee

Browse files
committed
Fix #3538: Better invalidation of typed arguments in FunProtos
One scenario we overlooked was that an argument in a FunProto might have been computed in a context that was subsequently retracted from its typerstate (e.g. because an enclosing subtype test failed). In that case we need to re-evaluate the argument in order to avoid orphan typerefs.
1 parent e773e7b commit ec07dee

File tree

7 files changed

+53
-15
lines changed

7 files changed

+53
-15
lines changed

compiler/src/dotty/tools/dotc/config/Config.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ object Config {
148148
final val showCompletions = false
149149

150150
/** If set, enables tracing */
151-
final val tracingEnabled = false
151+
final val tracingEnabled = true
152152

153153
/** Initial capacity of uniques HashMap.
154154
* Note: This MUST BE a power of two to work with util.HashSet

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: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import printing.Texts._
1212
import config.Config
1313
import collection.mutable
1414
import java.lang.ref.WeakReference
15+
import Decorators._
1516

1617
class TyperState(previous: TyperState /* | Null */) extends DotClass with Showable {
1718

@@ -29,10 +30,16 @@ class TyperState(previous: TyperState /* | Null */) extends DotClass with Showab
2930

3031
def constraint = myConstraint
3132
def constraint_=(c: Constraint)(implicit ctx: Context) = {
33+
// println(i"assigning $c to $hashesStr")
3234
if (Config.debugCheckConstraintsClosed && isGlobalCommittable) c.checkClosed()
3335
myConstraint = c
3436
}
3537

38+
def resetConstraintTo(c: Constraint) = {
39+
if (c `ne` myConstraint) myConstraint.markRetracted()
40+
myConstraint = c
41+
}
42+
3643
private val previousConstraint =
3744
if (previous == null) constraint else previous.constraint
3845

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

9198
/** Test using `op`, restoring typerState to previous state afterwards */
9299
def test[T](op: => T): T = {
93-
val savedReporter = myReporter
94100
val savedConstraint = myConstraint
101+
val savedReporter = myReporter
95102
val savedCommittable = myIsCommittable
96103
val savedCommitted = isCommitted
97104
myIsCommittable = false
@@ -105,8 +112,8 @@ class TyperState(previous: TyperState /* | Null */) extends DotClass with Showab
105112
}
106113
try op
107114
finally {
115+
resetConstraintTo(savedConstraint)
108116
myReporter = savedReporter
109-
myConstraint = savedConstraint
110117
myIsCommittable = savedCommittable
111118
isCommitted = savedCommitted
112119
}

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 that is
199+
* either
200+
* - in a typer state that is not yet committed into the one of the current context `ctx`, or
201+
* - has been retracted from its typestate because oif a failed operation.
200202
* This is necessary to avoid "orphan" TypeParamRefs that are referred to from
201203
* type variables in the typed arguments, but that are not registered in the
202-
* current constraint. A test case is pos/t1756.scala.
204+
* current constraint. Test cases are pos/t1756.scala and pos/i3538.scala.
203205
* @return True if all arguments have types (in particular, no types were forgotten).
204206
*/
205207
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}")
208+
evalState foreachBinding { (arg, tstateConstr) =>
209+
if ((tstateConstr._1.uncommittedAncestor.constraint `ne` ctx.typerState.constraint) ||
210+
tstateConstr._2.isRetracted) {
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)