Skip to content

Commit

Permalink
Stable names for lambda lifted method and fresh names
Browse files Browse the repository at this point in the history
Fresh names are created using a FreshNameCreator, which appends
an increasing number to the given prefix.

```
scala> val fresh = new scala.reflect.internal.util.FreshNameCreator()
fresh: scala.reflect.internal.util.FreshNameCreator = scala.reflect.internal.util.FreshNameCreator@42b84286

scala> List("foo$", "bar$", "foo$").map(fresh.newName(_))
res1: List[String] = List(foo$1, bar$1, foo$2)
```

Each compilation unit had its own fresh name creator, which is used
in the regular compiler. Macros and quasiquotes make use of a global
creator (at least, as of #3401).

Both of these are too broadly scoped to help achieve deterministic
fresh names: if sources are recompiled in a different order or separately
recompiled, the fresh name counters can be different. Methods in a given
compilation unit are not necessarily typechecked in a linear fashion;
they might be typechecked ahead of time to provide an inferred type
to a caller.

This commit:

  - Changes all known fresh name creations within the typer phase (in which
    out-of-order typechecking is a factor) to use a fineer grained fresh
    name creator. How fine grained? A fresh name generated as some position
    `p` shares the fresh name generator scoped at the closest method or
    class that encloses that the outermost enclosing tree at the same
    position. This definition is designed to give a shared fresh name
    creator for all fresh names generated in `macro1(macro2())`, even if
    the fresh names are requiested from with a Typer in the macro enclosed
    by a synthetic method.
  - Changes macro fresh names to use the same fresh naming scheme as the regular
    typechecker. An opt-out compiler option allows the old behaviour, but I'm
    interested to find real-world cases where the new scheme actually causes
    a problem

In addition, a small change is made to lambda lift to lift local methods in the
order that they are encountered during traversal, rather than sorting them
based on `Symbol.isLess` (which include `Symbol.id`, an order-of-typechecking
dependent value).
  • Loading branch information
retronym committed May 29, 2018
1 parent c6ab51c commit 69d60cb
Show file tree
Hide file tree
Showing 31 changed files with 375 additions and 116 deletions.
2 changes: 1 addition & 1 deletion src/compiler/scala/reflect/macros/contexts/Names.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ trait Names {

import global._

def freshNameCreator = globalFreshNameCreator
def freshNameCreator = self.callsiteTyper.fresh

def fresh(): String =
freshName()
Expand Down
5 changes: 4 additions & 1 deletion src/compiler/scala/reflect/macros/contexts/Parsers.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package scala.reflect.macros
package contexts

import scala.reflect.internal.util.FreshNameCreator
import scala.tools.nsc.reporters.StoreReporter

trait Parsers {
Expand All @@ -12,7 +13,9 @@ trait Parsers {
val oldReporter = global.reporter
try {
global.reporter = sreporter
val parser = newUnitParser(new CompilationUnit(newSourceFile(code, "<macro>")))
val parser = newUnitParser(new CompilationUnit(newSourceFile(code, "<macro>")) {
override implicit val fresh: FreshNameCreator = currentFreshNameCreator
})
val tree = gen.mkTreeOrBlock(parser.parseStatsOrPackages())
sreporter.infos.foreach {
case sreporter.Info(pos, msg, sreporter.ERROR) => throw ParseException(pos, msg)
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/reflect/reify/utils/Extractors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ trait Extractors {
}
val tpec = ClassDef(
Modifiers(FINAL),
newTypeName(global.currentUnit.fresh.newName(flavor.toString)),
newTypeName(currentFreshNameCreator.newName(flavor.toString)),
List(),
Template(List(Ident(reifierBase)),
noSelfType,
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/reflect/reify/utils/SymbolTables.scala
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ trait SymbolTables {
var name = name0.toString
name = name.replace(".type", "$type")
name = name.replace(" ", "$")
val fresh = typer.context.unit.fresh
val fresh = typer.fresh
newTermName(fresh.newName(name))
}
val bindingAttachment = reification.attachments.get[ReifyBindingAttachment].get
Expand Down
9 changes: 6 additions & 3 deletions src/compiler/scala/tools/nsc/Global.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ package nsc
import java.io.{File, FileNotFoundException, IOException}
import java.net.URL
import java.nio.charset.{Charset, CharsetDecoder, IllegalCharsetNameException, UnsupportedCharsetException}

import scala.collection.{immutable, mutable}
import io.{AbstractFile, Path, SourceReader}
import reporters.Reporter
import util.{ClassPath, returning}
import scala.reflect.ClassTag
import scala.reflect.internal.util.{BatchSourceFile, NoSourceFile, ScalaClassLoader, ScriptSourceFile, SourceFile, StatisticsStatics}
import scala.reflect.internal.util.{BatchSourceFile, FreshNameCreator, NoSourceFile, ScalaClassLoader, ScriptSourceFile, SourceFile, StatisticsStatics}
import scala.reflect.internal.pickling.PickleBuffer
import symtab.{Flags, SymbolTable, SymbolTrackers}
import symtab.classfile.Pickler
Expand All @@ -26,7 +27,7 @@ import typechecker._
import transform.patmat.PatternMatching
import transform._
import backend.{JavaPlatform, ScalaPrimitives}
import backend.jvm.{GenBCode, BackendStats}
import backend.jvm.{BackendStats, GenBCode}
import scala.concurrent.Future
import scala.language.postfixOps
import scala.tools.nsc.ast.{TreeGen => AstTreeGen}
Expand Down Expand Up @@ -965,7 +966,9 @@ class Global(var currentSettings: Settings, reporter0: Reporter)
def currentRun: Run = curRun
def currentUnit: CompilationUnit = if (currentRun eq null) NoCompilationUnit else currentRun.currentUnit
def currentSource: SourceFile = if (currentUnit.exists) currentUnit.source else lastSeenSourceFile
def currentFreshNameCreator = currentUnit.fresh
def currentFreshNameCreator = if (curFreshNameCreator == null) currentUnit.fresh else curFreshNameCreator
private[this] var curFreshNameCreator: FreshNameCreator = null
private[scala] def currentFreshNameCreator_=(fresh: FreshNameCreator): Unit = curFreshNameCreator = fresh

def isGlobalInitialized = (
definitions.isDefinitionsInitialized
Expand Down
13 changes: 9 additions & 4 deletions src/compiler/scala/tools/nsc/ast/TreeGen.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package ast
import scala.collection.mutable.ListBuffer
import symtab.Flags._
import scala.language.postfixOps
import scala.reflect.internal.util.FreshNameCreator

/** XXX to resolve: TreeGen only assumes global is a SymbolTable, but
* TreeDSL at the moment expects a Global. Can we get by with SymbolTable?
Expand Down Expand Up @@ -191,20 +192,24 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {

/** Used in situations where you need to access value of an expression several times
*/
def evalOnce(expr: Tree, owner: Symbol, unit: CompilationUnit)(within: (() => Tree) => Tree): Tree = {
def evalOnce(expr: Tree, owner: Symbol, unit: CompilationUnit)(within: (() => Tree) => Tree): Tree = evalOnce(expr, owner, unit.fresh)(within)
def evalOnce(expr: Tree, owner: Symbol, fresh: FreshNameCreator)(within: (() => Tree) => Tree): Tree = {
var used = false
if (treeInfo.isExprSafeToInline(expr)) {
within(() => if (used) expr.duplicate else { used = true; expr })
}
else {
val (valDef, identFn) = mkPackedValDef(expr, owner, unit.freshTermName("ev$"))
val (valDef, identFn) = mkPackedValDef(expr, owner, freshTermName("ev$")(fresh))
val containing = within(identFn)
ensureNonOverlapping(containing, List(expr))
Block(List(valDef), containing) setPos (containing.pos union expr.pos)
}
}

def evalOnceAll(exprs: List[Tree], owner: Symbol, unit: CompilationUnit)(within: (List[() => Tree]) => Tree): Tree = {
def evalOnceAll(exprs: List[Tree], owner: Symbol, unit: CompilationUnit)(within: (List[() => Tree]) => Tree): Tree =
evalOnceAll(exprs, owner, unit.fresh)(within)

def evalOnceAll(exprs: List[Tree], owner: Symbol, fresh: FreshNameCreator)(within: (List[() => Tree]) => Tree): Tree = {
val vdefs = new ListBuffer[ValDef]
val exprs1 = new ListBuffer[() => Tree]
val used = new Array[Boolean](exprs.length)
Expand All @@ -217,7 +222,7 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL {
}
}
else {
val (valDef, identFn) = mkPackedValDef(expr, owner, unit.freshTermName("ev$"))
val (valDef, identFn) = mkPackedValDef(expr, owner, freshTermName("ev$")(fresh))
vdefs += valDef
exprs1 += identFn
}
Expand Down
1 change: 1 addition & 0 deletions src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ trait ScalaSettings extends AbsScalaSettings
val Yreifycopypaste = BooleanSetting ("-Yreify-copypaste", "Dump the reified trees in copypasteable representation.")
val Ymacroexpand = ChoiceSetting ("-Ymacro-expand", "policy", "Control expansion of macros, useful for scaladoc and presentation compiler.", List(MacroExpand.Normal, MacroExpand.None, MacroExpand.Discard), MacroExpand.Normal)
val Ymacronoexpand = BooleanSetting ("-Ymacro-no-expand", "Don't expand macros. Might be useful for scaladoc and presentation compiler, but will crash anything which uses macros and gets past typer.") withDeprecationMessage(s"Use ${Ymacroexpand.name}:${MacroExpand.None}") withPostSetHook(_ => Ymacroexpand.value = MacroExpand.None)
val YmacroFresh = BooleanSetting ("-Ymacro-global-fresh-names", "Should fresh names in macros be unique across all compilation units")
val YmacroAnnotations = BooleanSetting ("-Ymacro-annotations", "Enable support for macro annotations, formerly in macro paradise.")
val Yreplclassbased = BooleanSetting ("-Yrepl-class-based", "Use classes to wrap REPL snippets instead of objects")
val Yreploutdir = StringSetting ("-Yrepl-outdir", "path", "Write repl-generated classfiles to given output directory (use \"\" to generate a temporary dir)" , "")
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/transform/CleanUp.scala
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL {
val runDefinitions = currentRun.runDefinitions
import runDefinitions._

gen.evalOnce(qual, currentOwner, unit) { qual1 =>
gen.evalOnce(qual, currentOwner, localTyper.fresh) { qual1 =>
/* Some info about the type of the method being called. */
val methSym = ad.symbol
val boxedResType = toBoxedType(resType) // Int -> Integer
Expand Down
6 changes: 3 additions & 3 deletions src/compiler/scala/tools/nsc/transform/Erasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,7 @@ abstract class Erasure extends InfoTransform
else {
val untyped =
// util.trace("new asinstanceof test") {
gen.evalOnce(qual1, context.owner, context.unit) { qual =>
gen.evalOnce(qual1, context.owner, fresh) { qual =>
If(Apply(Select(qual(), nme.eq), List(Literal(Constant(null)) setType NullTpe)),
Literal(Constant(null)) setType targ.tpe,
unbox(qual(), targ.tpe))
Expand Down Expand Up @@ -1044,7 +1044,7 @@ abstract class Erasure extends InfoTransform
case SingletonInstanceCheck(cmpOp, cmpArg) =>
atPos(tree.pos) { Apply(Select(cmpArg, cmpOp), List(qual)) }
case RefinedType(parents, decls) if (parents.length >= 2) =>
gen.evalOnce(qual, currentOwner, unit) { q =>
gen.evalOnce(qual, currentOwner, localTyper.fresh) { q =>
// Optimization: don't generate isInstanceOf tests if the static type
// conforms, because it always succeeds. (Or at least it had better.)
// At this writing the pattern matcher generates some instance tests
Expand Down Expand Up @@ -1097,7 +1097,7 @@ abstract class Erasure extends InfoTransform

global.typer.typedPos(tree.pos) {
if (level == 1) isArrayTest(qual)
else gen.evalOnce(qual, currentOwner, unit) { qual1 =>
else gen.evalOnce(qual, currentOwner, localTyper.fresh) { qual1 =>
gen.mkAnd(
gen.mkMethodCall(
qual1(),
Expand Down
7 changes: 3 additions & 4 deletions src/compiler/scala/tools/nsc/transform/LambdaLift.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ package transform
import symtab._
import Flags._
import scala.collection.mutable
import scala.collection.mutable.{ LinkedHashMap, LinkedHashSet, TreeSet }
import scala.collection.mutable.{ LinkedHashMap, LinkedHashSet }

abstract class LambdaLift extends InfoTransform {
import global._
Expand Down Expand Up @@ -50,7 +50,7 @@ abstract class LambdaLift extends InfoTransform {

class LambdaLifter(unit: CompilationUnit) extends explicitOuter.OuterPathTransformer(unit) {

private type SymSet = TreeSet[Symbol]
private type SymSet = LinkedHashSet[Symbol]

/** A map storing free variables of functions and classes */
private val free = new LinkedHashMap[Symbol, SymSet]
Expand All @@ -64,8 +64,7 @@ abstract class LambdaLift extends InfoTransform {
/** Symbols that are called from an inner class. */
private val calledFromInner = new LinkedHashSet[Symbol]

private val ord = Ordering.fromLessThan[Symbol](_ isLess _)
private def newSymSet = TreeSet.empty[Symbol](ord)
private def newSymSet: LinkedHashSet[Symbol] = new LinkedHashSet[Symbol]

private def symSet(f: LinkedHashMap[Symbol, SymSet], sym: Symbol): SymSet =
f.getOrElseUpdate(sym, newSymSet)
Expand Down
12 changes: 12 additions & 0 deletions src/compiler/scala/tools/nsc/typechecker/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -637,6 +637,18 @@ trait Contexts { self: Analyzer =>
def nextEnclosing(p: Context => Boolean): Context =
if (p(this)) this else outer.nextEnclosing(p)

final def outermostContextAtCurrentPos: Context = {
var pos = tree.pos
var encl = this
while (pos == NoPosition && encl != NoContext) {
encl = encl.outer
pos = encl.tree.pos
}
while (encl.outer.tree.pos == pos && encl != NoContext)
encl = encl.outer
encl
}

def enclosingContextChain: List[Context] = this :: outer.enclosingContextChain

private def treeTruncated = tree.toString.replaceAll("\\s+", " ").lines.mkString("\\n").take(70)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ trait EtaExpansion { self: Analyzer =>
var cnt = 0 // for NoPosition
def freshName() = {
cnt += 1
unit.freshTermName("eta$" + (cnt - 1) + "$")
freshTermName("eta$" + (cnt - 1) + "$")(typer.fresh)
}
val defs = new ListBuffer[Tree]

Expand Down
7 changes: 6 additions & 1 deletion src/compiler/scala/tools/nsc/typechecker/Macros.scala
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,12 @@ trait Macros extends MacroRuntimes with Traces with Helpers {
/** Expands a term macro used in apply role as `M(2)(3)` in `val x = M(2)(3)`.
* @see DefMacroExpander
*/
def macroExpand(typer: Typer, expandee: Tree, mode: Mode, pt: Type): Tree = pluginsMacroExpand(typer, expandee, mode, pt)
def macroExpand(typer: Typer, expandee: Tree, mode: Mode, pt: Type): Tree = {
// By default, use the current typer's fresh name creator in macros. The compiler option
// allows people to opt in to the old behaviour of Scala 2.12, which used a global fresh creator.
if (!settings.YmacroFresh.value) currentFreshNameCreator = typer.fresh
pluginsMacroExpand(typer, expandee, mode, pt)
}

/** Default implementation of `macroExpand`.
* Can be overridden by analyzer plugins (see AnalyzerPlugins.pluginsMacroExpand for more details)
Expand Down
5 changes: 2 additions & 3 deletions src/compiler/scala/tools/nsc/typechecker/NamesDefaults.scala
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ trait NamesDefaults { self: Analyzer =>

// never used for constructor calls, they always have a stable qualifier
def blockWithQualifier(qual: Tree, selected: Name) = {
val sym = blockTyper.context.owner.newValue(unit.freshTermName(nme.QUAL_PREFIX), newFlags = ARTIFACT) setInfo uncheckedBounds(qual.tpe) setPos (qual.pos.makeTransparent)
val sym = blockTyper.context.owner.newValue(freshTermName(nme.QUAL_PREFIX)(typer.fresh), newFlags = ARTIFACT) setInfo uncheckedBounds(qual.tpe) setPos (qual.pos.makeTransparent)
blockTyper.context.scope enter sym
val vd = atPos(sym.pos)(ValDef(sym, qual) setType NoType)
// it stays in Vegas: scala/bug#5720, scala/bug#5727
Expand Down Expand Up @@ -300,8 +300,7 @@ trait NamesDefaults { self: Analyzer =>
arg.tpe
}
)

val s = context.owner.newValue(unit.freshTermName(nme.NAMEDARG_PREFIX), arg.pos, newFlags = ARTIFACT) setInfo {
val s = context.owner.newValue(freshTermName(nme.NAMEDARG_PREFIX)(typer.fresh), arg.pos, newFlags = ARTIFACT) setInfo {
val tp = if (byName) functionType(Nil, argTpe) else argTpe
uncheckedBounds(tp)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ trait PatternTypers {
else TypeBounds.lower(tpSym.tpeHK)
)
// origin must be the type param so we can deskolemize
val skolem = context.owner.newGADTSkolem(unit.freshTypeName("?" + tpSym.name), tpSym, bounds)
val skolem = context.owner.newGADTSkolem(freshTypeName("?" + tpSym.name), tpSym, bounds)
skolemBuffer += skolem
logResult(s"Created gadt skolem $skolem: ${skolem.tpe_*} to stand in for $tpSym")(skolem.tpe_*)
case tp1 => tp1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ trait SyntheticMethods extends ast.TreeDSL {
rt != NothingTpe && rt != NullTpe && rt != UnitTpe
}

val otherName = context.unit.freshTermName(clazz.name + "$")
val otherName = freshTermName(clazz.name + "$")(freshNameCreatorFor(context))
val otherSym = eqmeth.newValue(otherName, eqmeth.pos, SYNTHETIC) setInfo clazz.tpe
val pairwise = accessors collect {
case acc if usefulEquality(acc) =>
Expand Down Expand Up @@ -390,7 +390,7 @@ trait SyntheticMethods extends ast.TreeDSL {
val i = original.owner.caseFieldAccessors.indexOf(original)
def freshAccessorName = {
devWarning(s"Unable to find $original among case accessors of ${original.owner}: ${original.owner.caseFieldAccessors}")
context.unit.freshTermName(original.name + "$")
freshTermName(original.name + "$")(freshNameCreatorFor(context))
}
def nameSuffixedByParamIndex = original.name.append(nme.CASE_ACCESSOR + "$" + i).toTermName
val newName = if (i < 0) freshAccessorName else nameSuffixedByParamIndex
Expand Down
18 changes: 11 additions & 7 deletions src/compiler/scala/tools/nsc/typechecker/Typers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ package tools.nsc
package typechecker

import scala.collection.{immutable, mutable}
import scala.reflect.internal.util.{ListOfNil, Statistics, StatisticsStatics}
import scala.reflect.internal.util.{FreshNameCreator, ListOfNil, Statistics, StatisticsStatics}
import scala.reflect.internal.TypesStats
import mutable.ListBuffer
import symtab.Flags._
Expand Down Expand Up @@ -188,10 +188,14 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}
}

private final val typerFreshNameCreators = perRunCaches.newAnyRefMap[Symbol, FreshNameCreator]()
def freshNameCreatorFor(context: Context) = typerFreshNameCreators.getOrElseUpdate(context.outermostContextAtCurrentPos.enclClassOrMethod.owner, new FreshNameCreator)

abstract class Typer(context0: Context) extends TyperDiagnostics with Adaptation with Tag with PatternTyper with TyperContextErrors {
private def unit = context.unit
import typeDebug.ptTree
import TyperErrorGen._
implicit def fresh: FreshNameCreator = freshNameCreatorFor(context)

private def transformed: mutable.Map[Tree, Tree] = unit.transformed

Expand Down Expand Up @@ -3460,7 +3464,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val args1 = typedArgs(args, forArgMode(fun, mode))
val pts = args1.map(_.tpe.deconst)
val clone = fun.symbol.cloneSymbol.withoutAnnotations
val cloneParams = pts map (pt => clone.newValueParameter(currentUnit.freshTermName()).setInfo(pt))
val cloneParams = pts map (pt => clone.newValueParameter(freshTermName()).setInfo(pt))
val resultType = if (isFullyDefined(pt)) pt else ObjectTpe
clone.modifyInfo(mt => copyMethodType(mt, cloneParams, resultType))
val fun1 = fun.setSymbol(clone).setType(clone.info)
Expand Down Expand Up @@ -4499,14 +4503,14 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
val cases = tree.cases
if (selector == EmptyTree) {
if (pt.typeSymbol == PartialFunctionClass)
synthesizePartialFunction(newTermName(context.unit.fresh.newName("x")), tree.pos, paramSynthetic = true, tree, mode, pt)
synthesizePartialFunction(newTermName(fresh.newName("x")), tree.pos, paramSynthetic = true, tree, mode, pt)
else {
val arity = functionArityFromType(pt) match { case -1 => 1 case arity => arity } // scala/bug#8429: consider sam and function type equally in determining function arity

val params = for (i <- List.range(0, arity)) yield
atPos(tree.pos.focusStart) {
ValDef(Modifiers(PARAM | SYNTHETIC),
unit.freshTermName("x" + i + "$"), TypeTree(), EmptyTree)
freshTermName("x" + i + "$"), TypeTree(), EmptyTree)
}
val ids = for (p <- params) yield Ident(p.name)
val selector1 = atPos(tree.pos.focusStart) { if (arity == 1) ids.head else gen.mkTuple(ids) }
Expand Down Expand Up @@ -4846,7 +4850,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
) setPos tree.pos

def mkUpdate(table: Tree, indices: List[Tree], args_? : Option[List[Tree]]) =
gen.evalOnceAll(table :: indices, context.owner, context.unit) {
gen.evalOnceAll(table :: indices, context.owner, fresh) {
case tab :: is =>
def mkCall(name: Name, extraArgs: Tree*) = (
Apply(
Expand All @@ -4869,7 +4873,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
mkAssign(qual)

case Select(qualqual, vname) =>
gen.evalOnce(qualqual, context.owner, context.unit) { qq =>
gen.evalOnce(qualqual, context.owner, fresh) { qq =>
val qq1 = qq()
mkAssign(Select(qq1, vname) setPos qual.pos)
}
Expand Down Expand Up @@ -5046,7 +5050,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}

if (insertionContext != NoContext) {
val vsym = insertionContext.owner.newValue(unit.freshTermName(nme.STABILIZER_PREFIX), qual.pos.focus, SYNTHETIC | ARTIFACT | STABLE)
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)
Expand Down
Loading

0 comments on commit 69d60cb

Please sign in to comment.