Skip to content

Commit

Permalink
Merge pull request #8716 from lrytz/t11756
Browse files Browse the repository at this point in the history
Rework insertion of stabilizing definitions
  • Loading branch information
lrytz committed Apr 15, 2020
2 parents a789d81 + da790c3 commit d433c3e
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 80 deletions.
2 changes: 2 additions & 0 deletions src/compiler/scala/tools/nsc/typechecker/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,8 @@ trait Contexts { self: Analyzer =>
/** A root import is never unused and always bumps context depth. (e.g scala._ / Predef._ and magic REPL imports) */
def isRootImport: Boolean = false

var pendingStabilizers: List[Tree] = Nil

/** Types for which implicit arguments are currently searched */
var openImplicits: List[OpenImplicit] = List()
final def isSearchingForImplicitParam: Boolean = {
Expand Down
21 changes: 0 additions & 21 deletions src/compiler/scala/tools/nsc/typechecker/StdAttachments.scala
Original file line number Diff line number Diff line change
Expand Up @@ -199,27 +199,6 @@ trait StdAttachments {

/** Marks a Typed tree with Unit tpt. */
case object TypedExpectingUnitAttachment

case class StabilizingDefinitions(vdefs: List[ValDef])
private[this] val StabilizingDefinitionsTag: reflect.ClassTag[StabilizingDefinitions] = reflect.classTag[StabilizingDefinitions]

def addStabilizingDefinition(tree: Tree, vdef: ValDef): Tree = {
tree.updateAttachment(StabilizingDefinitions(
tree.attachments.get[StabilizingDefinitions](StabilizingDefinitionsTag) match {
case Some(StabilizingDefinitions(vdefs)) => vdef :: vdefs
case _ => List(vdef)
}
))(StabilizingDefinitionsTag)
}

def stabilizingDefinitions(tree: Tree): List[ValDef] =
tree.attachments.get[StabilizingDefinitions](StabilizingDefinitionsTag) match {
case Some(StabilizingDefinitions(vdefs)) => vdefs
case _ => Nil
}

def removeStabilizingDefinitions(tree: Tree): Tree =
tree.removeAttachment[StabilizingDefinitions](StabilizingDefinitionsTag)
}


Expand Down
99 changes: 46 additions & 53 deletions src/compiler/scala/tools/nsc/typechecker/Typers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -712,10 +712,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val context1 = context.makeSilent(reportAmbiguousErrors, newtree)
context1.undetparams = context.undetparams
context1.savedTypeBounds = context.savedTypeBounds
context1.pendingStabilizers = context.pendingStabilizers
val typer1 = newTyper(context1)
val result = op(typer1)
context.undetparams = context1.undetparams
context.savedTypeBounds = context1.savedTypeBounds
context.pendingStabilizers = context1.pendingStabilizers

// If we have a successful result, emit any warnings it created.
if (!context1.reporter.hasErrors)
Expand Down Expand Up @@ -2568,7 +2570,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
}
val statsTyped = typedStats(block.stats, context.owner, warnPure = false)
val expr1 = typed(block.expr, mode &~ (FUNmode | QUALmode), pt)
val expr1 = typed(block.expr, mode &~ (FUNmode | QUALmode | APPSELmode), pt)

// sanity check block for unintended expr placement
if (!isPastTyper) {
Expand Down Expand Up @@ -5327,35 +5329,13 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
// implicit scopes which are accessible via lhs can be candidates for satisfying
// implicit (conversions to) arguments of name.

// We have to introduce the ValDef before its use here, so we walk up the
// context tree and attach it to the original root of this expression. It will
// be extracted and inserted by insertStabilizer when typer unwinds out of this
// expression. Stabilized args are introduced in the block arg expression.
val insertionContext = context.nextEnclosing { ctx =>
def isInsertionNode(tree: Tree) =
tree match {
case _: Apply | _: TypeApply | _: Select => true
case _ => false
}
def isEnclosingInsertionNode(tree: Tree) =
tree match {
case Apply(_, args) => args.contains(ctx.tree)
case _: TypeApply | _: Select => false
case _ => true
}
isInsertionNode(ctx.tree) && isEnclosingInsertionNode(ctx.outer.tree)
}

if (insertionContext != NoContext) {
val vsym = insertionContext.owner.newValue(freshTermName(nme.STABILIZER_PREFIX), qual.pos.focus, SYNTHETIC | ARTIFACT | STABLE)
vsym.setInfo(uncheckedBounds(qual.tpe))
insertionContext.scope enter vsym
val vdef = atPos(vsym.pos)(ValDef(vsym, focusInPlace(qual)) setType NoType)
qual.changeOwner(insertionContext.owner -> vsym)
addStabilizingDefinition(insertionContext.tree, vdef)
val newQual = Ident(vsym) setType singleType(NoPrefix, vsym) setPos qual.pos.focus
return typedSelect(tree, newQual, name)
}
val vsym = context.owner.newValue(freshTermName(nme.STABILIZER_PREFIX), qual.pos.focus, SYNTHETIC | ARTIFACT | STABLE)
vsym.setInfo(uncheckedBounds(qual.tpe))
val vdef = atPos(vsym.pos)(ValDef(vsym, focusInPlace(qual)) setType NoType)
context.pendingStabilizers ::= vdef
qual.changeOwner(context.owner -> vsym)
val newQual = Ident(vsym) setType singleType(NoPrefix, vsym) setPos qual.pos.focus
return typedSelect(tree, newQual, name)
}

val tree1 = tree match {
Expand Down Expand Up @@ -5956,23 +5936,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
case _ => abort(s"unexpected member def: ${tree.getClass}\n$tree")
}

// Extract and insert stabilizing ValDefs (if any) which might have been
// introduced during the typing of the original expression.
def insertStabilizer(tree: Tree, original: Tree): Tree = {
if (phase.erasedTypes) tree
else stabilizingDefinitions(original) match {
case Nil => tree
case vdefs =>
removeStabilizingDefinitions(tree)
Block(vdefs.reverse, tree) setType tree.tpe setPos tree.pos
}
}

// Trees not allowed during pattern mode.
def typedOutsidePatternMode(tree: Tree): Tree = tree match {
case tree: Block => typerWithLocalContext(context.makeNewScope(tree, context.owner))(_.typedBlock(tree, mode, pt))
case tree: If => typedIf(tree)
case tree: TypeApply => insertStabilizer(typedTypeApply(tree), tree)
case tree: TypeApply => typedTypeApply(tree)
case tree: Function => typedFunction(tree)
case tree: Match => typedVirtualizedMatch(tree)
case tree: New => typedNew(tree)
Expand All @@ -5995,8 +5963,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
@inline def typedInAnyMode(tree: Tree): Tree = tree match {
case tree: Ident => typedIdentOrWildcard(tree)
case tree: Bind => typedBind(tree)
case tree: Apply => insertStabilizer(typedApply(tree), tree)
case tree: Select => insertStabilizer(typedSelectOrSuperCall(tree), tree)
case tree: Apply => typedApply(tree)
case tree: Select => typedSelectOrSuperCall(tree)
case tree: Literal => typedLiteral(tree)
case tree: Typed => typedTyped(tree)
case tree: This => typedThis(tree) // scala/bug#6104
Expand All @@ -6023,9 +5991,20 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (statsEnabled) statistics.incCounter(visitsByType, tree.getClass)
val shouldPrintTyping = printTypings && !phase.erasedTypes && !noPrintTyping(tree)
val shouldPopTypingStack = shouldPrintTyping && typingStack.beforeNextTyped(tree, mode, pt, context)
try {

val ptPlugins = pluginsPt(pt, this, tree, mode)
def shouldInsertStabilizersImpl = tree match {
case _ if phase.erasedTypes || mode.in(APPSELmode) || isMacroImplRef(tree) => false
case _: Select | _: Apply | _: TypeApply => true
case _ => false
}

val shouldInsertStabilizers = shouldInsertStabilizersImpl
val mode1: Mode = if (shouldInsertStabilizers) mode | APPSELmode else mode
val savedPendingStabilizer = context.pendingStabilizers
if (shouldInsertStabilizers) context.pendingStabilizers = Nil

try {
val ptPlugins = pluginsPt(pt, this, tree, mode1)
def retypingOk = (
context.retyping
&& (tree.tpe ne null)
Expand All @@ -6037,11 +6016,12 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
val alreadyTyped = tree.tpe ne null
val shouldPrint = !alreadyTyped && !phase.erasedTypes
val ptWild = if (mode.inPatternMode)
val ptWild = if (mode1.inPatternMode)
ptPlugins // scala/bug#5022 don't widen pt for patterns as types flow from it to the case body.
else
dropExistential(ptPlugins) // FIXME: document why this is done.
val tree1: Tree = if (alreadyTyped) tree else typed1(tree, mode, ptWild)
val tree1: Tree = if (alreadyTyped) tree else typed1(tree, mode1, ptWild)

if (shouldPrint)
typingStack.showTyped(tree1)

Expand All @@ -6050,24 +6030,28 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
if (tree1.tpe eq null)
return setError(tree)

tree1 setType pluginsTyped(tree1.tpe, this, tree1, mode, ptPlugins)
tree1 setType pluginsTyped(tree1.tpe, this, tree1, mode1, ptPlugins)

val result =
val adapted =
if (tree1.isEmpty) tree1
else {
val result = adapt(tree1, mode, ptPlugins, tree)
val result = adapt(tree1, mode1, ptPlugins, tree)
if (typerShouldExpandDeferredMacros) {
macroExpandAll(this, result)
} else result
}

val result =
if (shouldInsertStabilizers) addStabilizers(context.pendingStabilizers, adapted)
else adapted

if (shouldPrint)
typingStack.showAdapt(tree1, result, ptPlugins, context)

if (!isPastTyper)
signalDone(context.asInstanceOf[analyzer.Context], tree, result)

if (mode.inPatternMode && !mode.inPolyMode && result.isType)
if (mode1.inPatternMode && !mode1.inPolyMode && result.isType)
PatternMustBeValue(result, pt)

if (shouldPopTypingStack) typingStack.showPop(result)
Expand All @@ -6091,6 +6075,15 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
} finally {
if (shouldPopTypingStack) typingStack.pop(tree)
if (statsEnabled) statistics.popTimer(byTypeStack, startByType)
if (shouldInsertStabilizers) context.pendingStabilizers = savedPendingStabilizer
}
}

private def addStabilizers(newStabilizers: List[Tree], expr: Tree): Tree = {
if (newStabilizers.isEmpty) expr else {
devWarningIf(newStabilizers.forall(_.symbol.owner == context.owner))(s"${context.owner} - ${(newStabilizers.map(vd => (vd.symbol, vd.symbol.owner.fullNameString)), context.owner)}")
// Insert stabilizing ValDefs (if any) which might have been introduced during the typing of the original expression.
Block(newStabilizers.reverse, expr).setPos(expr.pos).setType(expr.tpe)
}
}

Expand Down
33 changes: 28 additions & 5 deletions src/reflect/scala/reflect/internal/Mode.scala
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,33 @@ object Mode {
*/
final val TYPEPATmode: Mode = 0x10000

/** This mode is set when starting to type check a `Select`, `Apply` or `TypeApply`, e.g., `x.y`
* or `a.b.foo[T](x, y).bar(z)`. Stabilizers (a feature added in PR scala/scala#5999) created
* when typing the expression are emitted in a new enclosing block, e.g.
* {
* val $stabilizer$1 = a.b
* val $stabilizer$2 = $stabilizer1.foo[T](x, y)
* $stabilizer$2.bar(z)
* }
*
* The flag is sticky for typing the function of an Apply (`forFunMode`) and qualifiers of
* nested selections (`MonoQualifierModes`), but cleared for argument expressions
* (`onlySticky`). So `a.b.foo(a.b.bar)` becomes
* {
* val $stabilizer$1 = a.b
* $stabilizer$1.foo({
* val $stabilizer$2 = a.b
* $stabilizer$2.bar
* })
* }
*/
final val APPSELmode: Mode = 0x20000

private val StickyModes: Mode = EXPRmode | PATTERNmode | TYPEmode
private val StickyModesForFun: Mode = StickyModes | SCCmode
final val MonoQualifierModes: Mode = EXPRmode | QUALmode
final val PolyQualifierModes: Mode = EXPRmode | QUALmode | POLYmode
final val OperatorModes: Mode = EXPRmode | POLYmode | TAPPmode | FUNmode
final val MonoQualifierModes: Mode = EXPRmode | QUALmode | APPSELmode
final val PolyQualifierModes: Mode = MonoQualifierModes | POLYmode
final val OperatorModes: Mode = EXPRmode | POLYmode | TAPPmode | FUNmode

/** Translates a mask of mode flags into something readable.
*/
Expand All @@ -97,7 +119,8 @@ object Mode {
(1 << 13) -> "<>", // formerly ALTmode
(1 << 14) -> "<>", // formerly HKmode
(1 << 15) -> "BYVALmode",
(1 << 16) -> "TYPEPATmode"
(1 << 16) -> "TYPEPATmode",
(1 << 17) -> "APPSELmode"
).map({ case (k, v) => Mode(k) -> v })
}
import Mode._
Expand All @@ -109,7 +132,7 @@ final class Mode private (val bits: Int) extends AnyVal {

def onlyTypePat = this & TYPEPATmode
def onlySticky = this & Mode.StickyModes
def forFunMode = this & Mode.StickyModesForFun | FUNmode | POLYmode | BYVALmode
def forFunMode = this & Mode.StickyModesForFun | FUNmode | POLYmode | BYVALmode | APPSELmode
def forTypeMode = if (typingPatternOrTypePat) TYPEmode | TYPEPATmode else TYPEmode

def inAll(required: Mode) = (this & required) == required
Expand Down
2 changes: 1 addition & 1 deletion src/reflect/scala/reflect/internal/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3846,7 +3846,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
def loop(mt: Type): Boolean = {
mt match {
case MethodType(params, restpe) => params.exists(_.info.dealias.exists(_ == tt)) || loop(restpe)
case PolyType(tparams, restpe) => loop(restpe)
case PolyType(_, restpe) => loop(restpe)
case _ => false
}
}
Expand Down
24 changes: 24 additions & 0 deletions test/files/run/t11609.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
new Foo 0
new Foo 1
new Bar 0
new Foo 2
new Foo 3
new Bar 0
new Bar 1
sara
frank
new Foo 4
new Bar 0
new Bar 1
new Foo 5
new Foo 6
new Bar 0
new Foo 7
new Foo 8
new Bar 0
new Bar 1
sara
frank
new Foo 9
new Bar 0
new Bar 1
41 changes: 41 additions & 0 deletions test/files/run/t11609.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
class Foo {
println(s"new Foo ${Foo.c}"); Foo.c += 1

class Bar {
println(s"new Bar ${Bar.c}"); Bar.c += 1
}

object Bar {
var c = 0
implicit def brr: Bar = new Bar
}
def m(implicit b: Bar): Foo = Foo.this
}
object Foo {
var c = 0
}

object Test {
(new Foo).m(null)
def a = (new Foo).m(null)

(new Foo).m
def b = (new Foo).m

(new Foo).m(null).m(null)
def c = (new Foo).m(null).m(null)

(new Foo).m.m
def d = (new Foo).m.m

{ println("sara"); { println("frank"); new Foo}.m}.m
def e = { println("sara"); { println("frank"); new Foo}.m}.m

def main(args: Array[String]): Unit = {
a
b
c
d
e
}
}
Loading

0 comments on commit d433c3e

Please sign in to comment.