Skip to content

Fix #3538: Better invalidation of typed arguments in FunProtos #3661

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Dec 18, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Constraint.scala
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,10 @@ abstract class Constraint extends Showable {

/** Check that constraint only refers to TypeParamRefs bound by itself */
def checkClosed()(implicit ctx: Context): Unit

/** Constraint has not yet been retracted from a typer state */
def isRetracted: Boolean

/** Indicate that constraint has been retracted from a typer state */
def markRetracted(): Unit
}
8 changes: 8 additions & 0 deletions compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,14 @@ class OrderingConstraint(private val boundsMap: ParamBounds,
private def checkNonCyclic(param: TypeParamRef)(implicit ctx: Context): Unit =
assert(!isLess(param, param), i"cyclic constraint involving $param in $this")

// ---------- Invalidation -------------------------------------------

private var retracted = false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private[this] ?


def isRetracted: Boolean = retracted

def markRetracted(): Unit = retracted = true

// ---------- toText -----------------------------------------------------

override def toText(printer: Printer): Text = {
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
if (recCount < Config.LogPendingSubTypesThreshold) firstTry(tp1, tp2)
else monitoredIsSubType(tp1, tp2)
recCount = recCount - 1
if (!result) constraint = saved
if (!result) state.resetConstraintTo(saved)
else if (recCount == 0 && needsGc) {
state.gc()
needsGc = false
Expand All @@ -129,7 +129,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
case NonFatal(ex) =>
if (ex.isInstanceOf[AssertionError]) showGoal(tp1, tp2)
recCount -= 1
constraint = saved
state.resetConstraintTo(saved)
successCount = savedSuccessCount
throw ex
}
Expand Down
10 changes: 8 additions & 2 deletions compiler/src/dotty/tools/dotc/core/TyperState.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ class TyperState(previous: TyperState /* | Null */) extends DotClass with Showab
myConstraint = c
}

/** Reset constraint to `c` and mark current constraint as retracted if it differs from `c` */
def resetConstraintTo(c: Constraint) = {
if (c `ne` myConstraint) myConstraint.markRetracted()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why use backquotes around ne ? New coding style for infix calls?

myConstraint = c
}

private val previousConstraint =
if (previous == null) constraint else previous.constraint

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

/** Test using `op`, restoring typerState to previous state afterwards */
def test[T](op: => T): T = {
val savedReporter = myReporter
val savedConstraint = myConstraint
val savedReporter = myReporter
val savedCommittable = myIsCommittable
val savedCommitted = isCommitted
myIsCommittable = false
Expand All @@ -105,8 +111,8 @@ class TyperState(previous: TyperState /* | Null */) extends DotClass with Showab
}
try op
finally {
resetConstraintTo(savedConstraint)
myReporter = savedReporter
myConstraint = savedConstraint
myIsCommittable = savedCommittable
isCommitted = savedCommitted
}
Expand Down
22 changes: 12 additions & 10 deletions compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ object ProtoTypes {
case _ =>
true
}
if (!res) ctx.typerState.constraint = savedConstraint
if (!res) ctx.typerState.resetConstraintTo(savedConstraint)
res
}
}
Expand Down Expand Up @@ -183,8 +183,8 @@ object ProtoTypes {
/** A map in which typed arguments can be stored to be later integrated in `typedArgs`. */
private[this] var myTypedArg: SimpleIdentityMap[untpd.Tree, Tree] = SimpleIdentityMap.Empty

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

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

override def notApplied = WildcardType

/** Forget the types of any arguments that have been typed producing a constraint in a
* typer state that is not yet committed into the one of the current context `ctx`.
/** Forget the types of any arguments that have been typed producing a constraint
* - that is in a typer state that is not yet committed into the one of the current context `ctx`,
* - or that has been retracted from its typestate because oif a failed operation.
* This is necessary to avoid "orphan" TypeParamRefs that are referred to from
* type variables in the typed arguments, but that are not registered in the
* current constraint. A test case is pos/t1756.scala.
* current constraint. Test cases are pos/t1756.scala and pos/i3538.scala.
* @return True if all arguments have types (in particular, no types were forgotten).
*/
def allArgTypesAreCurrent()(implicit ctx: Context): Boolean = {
evalState foreachBinding { (arg, tstate) =>
if (tstate.uncommittedAncestor.constraint ne ctx.typerState.constraint) {
typr.println(i"need to invalidate $arg / ${myTypedArg(arg)}, ${tstate.constraint}, current = ${ctx.typerState.constraint}")
evalState foreachBinding { (arg, tstateConstr) =>
if ((tstateConstr._1.uncommittedAncestor.constraint `ne` ctx.typerState.constraint) ||
tstateConstr._2.isRetracted) {
typr.println(i"need to invalidate $arg / ${myTypedArg(arg)}, ${tstateConstr._2}, current = ${ctx.typerState.constraint}")
myTypedArg = myTypedArg.remove(arg)
evalState = evalState.remove(arg)
}
Expand All @@ -219,7 +221,7 @@ object ProtoTypes {
targ = typerFn(arg)
if (!ctx.reporter.hasPending) {
myTypedArg = myTypedArg.updated(arg, targ)
evalState = evalState.updated(arg, ctx.typerState)
evalState = evalState.updated(arg, (ctx.typerState, ctx.typerState.constraint))
}
}
targ
Expand Down
15 changes: 15 additions & 0 deletions tests/pos/i3538.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

class Inv[T]

class Test {
implicit class Wrong(test: Test) {
def right: Any = ???
}
implicit class Right(test: Test) {
def right(node: Any): Any = ???
}

def inv[T](x: T): Inv[T] = ???

(this).right(inv(1))
}