Skip to content

Commit

Permalink
Refactoring accessLocal
Browse files Browse the repository at this point in the history
Further refactor accessLocal
  • Loading branch information
EnzeXing committed Jul 23, 2021
1 parent 4887b89 commit 9bea086
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 43 deletions.
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/init/Checker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ class Checker extends Phase {

val traverser = new TreeTraverser {
override def traverse(tree: Tree)(using Context): Unit =
traverseChildren(tree)
tree match {
case tdef: MemberDef =>
// self-type annotation ValDef has no symbol
if tdef.name != nme.WILDCARD then
tdef.symbol.defTree = tree
case _ =>
traverseChildren(tree)
}
}

Expand Down
86 changes: 45 additions & 41 deletions compiler/src/dotty/tools/dotc/transform/init/Semantic.scala
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,6 @@ class Semantic {
given Trace = trace1
val cls = target.owner.enclosingClass.asClass
val ddef = target.defTree.asInstanceOf[DefDef]
// try early promotion here; if returns error, returns cold
val env2 = Env(ddef, args.map(_.value).widenArgs)
if target.isPrimaryConstructor then
given Env = env2
Expand Down Expand Up @@ -497,6 +496,48 @@ class Semantic {
Result(value2, errors)
}
}

def accessLocal(tmref: TermRef, klass: ClassSymbol, source: Tree): Contextual[Result] =
val sym = tmref.symbol

def default() = Result(Hot, Nil)

if sym.is(Flags.Param) && sym.owner.isConstructor then
// instances of local classes inside secondary constructors cannot
// reach here, as those values are abstracted by Cold instead of Warm.
// This enables us to simplify the domain without sacrificing
// expressiveness nor soundess, as local classes inside secondary
// constructors are uncommon.
if sym.isContainedIn(klass) then
Result(env.lookup(sym), Nil)
else
// We don't know much about secondary constructor parameters in outer scope.
// It's always safe to approximate them with `Cold`.
Result(Cold, Nil)
else if sym.is(Flags.Param) then
default()
else
sym.defTree match {
case vdef: ValDef =>
// resolve this for local variable
val enclosingClass = sym.owner.enclosingClass.asClass
val thisValue2 = resolveThis(enclosingClass, value, klass, source)
thisValue2 match {
case Hot => Result(Hot, Errors.empty)

case Cold => Result(Cold, Nil)

case addr: Addr =>
val res = eval(vdef.rhs, addr, klass)
if res.value.promote("Try promote", source).isEmpty then Result(Hot, Errors.empty) else res

case _ =>
report.error("unexpected defTree when accessing local variable, sym = " + sym.show + ", defTree = " + sym.defTree.show, source)
default()
}

case _ => default()
}
end extension

// ----- Promotion ----------------------------------------------------
Expand Down Expand Up @@ -694,7 +735,7 @@ class Semantic {
}

/** Evaluate a list of expressions */
def eval(exprs: List[Tree], thisV: Addr, klass: ClassSymbol): Contextual[List[Result]] =
def eval(exprs: List[Tree], thisV: Addr, klass: ClassSymbol): Contextual[List[Result]] =
exprs.map { expr => eval(expr, thisV, klass) }

/** Evaluate arguments of methods */
Expand Down Expand Up @@ -857,8 +898,7 @@ class Semantic {
case vdef : ValDef =>
// local val definition
// TODO: support explicit @cold annotation for local definitions
eval(vdef.rhs, thisV, klass, true)
// .ensureHot("Local definitions may only hold initialized values", vdef)
eval(vdef.rhs, thisV, klass, cacheResult = true)

case ddef : DefDef =>
// local method
Expand Down Expand Up @@ -886,43 +926,7 @@ class Semantic {
Result(Hot, Errors.empty)

case tmref: TermRef if tmref.prefix == NoPrefix =>
val sym = tmref.symbol

def default() = Result(Hot, Nil)

if sym.is(Flags.Param) && sym.owner.isConstructor then
// instances of local classes inside secondary constructors cannot
// reach here, as those values are abstracted by Cold instead of Warm.
// This enables us to simplify the domain without sacrificing
// expressiveness nor soundess, as local classes inside secondary
// constructors are uncommon.
if sym.isContainedIn(klass) then
Result(env.lookup(sym), Nil)
else
// We don't know much about secondary constructor parameters in outer scope.
// It's always safe to approximate them with `Cold`.
Result(Cold, Nil)
else
sym.defTree match {
case vdef: ValDef => {
// resolve this for local variable
val enclosingClass = sym.owner.enclosingClass.asClass
val thisValue2 = resolveThis(enclosingClass, thisV, klass, source)
thisValue2 match {
case Hot => Result(Hot, Errors.empty)
case Cold => {
val error = AccessCold(sym, source, trace.toVector)
Result(Hot, error :: Nil)
}
case addr: Addr => {
val res = eval(vdef.rhs, addr, klass)
if res.value.promote("Try promote", source).isEmpty then Result(Hot, Errors.empty) else res
}
case _ => ???
}
}
case _ => default()
}
thisV.accessLocal(tmref, klass, source)

case tmref: TermRef =>
cases(tmref.prefix, thisV, klass, source).select(tmref.symbol, source)
Expand Down
11 changes: 11 additions & 0 deletions tests/init/neg/access-argument.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class A {
def foo(b: B): A = {
val temp = b
temp.bar(this) // error
}
val x = foo(new B)
}

class B {
def bar(a: A): A = a.x
}
2 changes: 1 addition & 1 deletion tests/init/neg/secondary-ctor2.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class A(b: B, x: Int) {
Inner().foo()

val f = () => new A(b, 3)
f() // ok
f()
}
}

Expand Down

0 comments on commit 9bea086

Please sign in to comment.