Skip to content

Commit c886d5a

Browse files
committed
Allowing non-hot assignment to local val and updating tests
1 parent 372930e commit c886d5a

File tree

11 files changed

+117
-21
lines changed

11 files changed

+117
-21
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ object Config {
192192
inline val flattenContextFunctionResults = true
193193

194194
/** If set, enables tracing */
195-
inline val tracingEnabled = false
195+
inline val tracingEnabled = true
196196

197197
/** Initial capacity of the uniques HashMap.
198198
* Note: This should be a power of two to work with util.HashSet

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ object Printers {
2828
val implicits = noPrinter
2929
val implicitsDetailed = noPrinter
3030
val lexical = noPrinter
31-
val init = noPrinter
31+
val init = new Printer
3232
val inlining = noPrinter
3333
val interactiv = noPrinter
3434
val matchTypes = noPrinter

compiler/src/dotty/tools/dotc/reporting/trace.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ trait TraceSyntax:
9090
finalize(trailing(ex.value))
9191
throw ex
9292
case ex: Throwable =>
93-
val msg = s"<== $q = <missing> (with exception $ex)"
94-
finalize(msg)
93+
// val msg = s"<== $q = <missing> (with exception $ex)"
94+
// finalize(msg)
9595
throw ex
9696
end TraceSyntax

compiler/src/dotty/tools/dotc/transform/init/Semantic.scala

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ class Semantic {
194194
class PromotionInfo {
195195
var isCurrentObjectPromoted: Boolean = false
196196
val values = mutable.Set.empty[Value]
197+
override def toString(): String = values.toString()
197198
}
198199
/** Values that have been safely promoted */
199200
opaque type Promoted = PromotionInfo
@@ -405,6 +406,7 @@ class Semantic {
405406
given Trace = trace1
406407
val cls = target.owner.enclosingClass.asClass
407408
val ddef = target.defTree.asInstanceOf[DefDef]
409+
// try early promotion here; if returns error, returns cold
408410
val env2 = Env(ddef, args.map(_.value).widenArgs)
409411
if target.isPrimaryConstructor then
410412
given Env = env2
@@ -683,8 +685,26 @@ class Semantic {
683685
}
684686

685687
/** Evaluate a list of expressions */
686-
def eval(exprs: List[Tree], thisV: Addr, klass: ClassSymbol): Contextual[List[Result]] =
687-
exprs.map { expr => eval(expr, thisV, klass) }
688+
def eval(exprs: List[Tree], thisV: Addr, klass: ClassSymbol): Contextual[(List[Result], Env)] = exprs match {
689+
case Nil => (Nil, env)
690+
case h :: t => h match {
691+
case v: ValDef => {
692+
val res = eval(h, thisV, klass)
693+
val newEnv = Env(Map(v.symbol -> res.value))
694+
withEnv(env.union(newEnv)) {
695+
val (res2, env2) = eval(t, thisV, klass)
696+
(res :: res2, env2)
697+
}
698+
}
699+
case _ => {
700+
val res = eval(h, thisV, klass)
701+
withEnv(env) {
702+
val (res2, env2) = eval(t, thisV, klass)
703+
(res :: res2, env2)
704+
}
705+
}
706+
}
707+
}
688708

689709
/** Evaluate arguments of methods */
690710
def evalArgs(args: List[Arg], thisV: Addr, klass: ClassSymbol): Contextual[(List[Error], List[ArgInfo])] =
@@ -788,11 +808,13 @@ class Semantic {
788808
Result(value, Nil)
789809

790810
case Block(stats, expr) =>
791-
val ress = eval(stats, thisV, klass)
792-
eval(expr, thisV, klass) ++ ress.flatMap(_.errors)
811+
val (ress, env2) = eval(stats, thisV, klass)
812+
withEnv(env2) {
813+
eval(expr, thisV, klass) ++ ress.flatMap(_.errors)
814+
}
793815

794816
case If(cond, thenp, elsep) =>
795-
val ress = eval(cond :: thenp :: elsep :: Nil, thisV, klass)
817+
val (ress, env2) = eval(cond :: thenp :: elsep :: Nil, thisV, klass)
796818
val value = ress.map(_.value).join
797819
val errors = ress.flatMap(_.errors)
798820
Result(value, errors)
@@ -803,7 +825,7 @@ class Semantic {
803825

804826
case Match(selector, cases) =>
805827
val res1 = eval(selector, thisV, klass).ensureHot("The value to be matched needs to be fully initialized", selector)
806-
val ress = eval(cases.map(_.body), thisV, klass)
828+
val (ress, env) = eval(cases.map(_.body), thisV, klass)
807829
val value = ress.map(_.value).join
808830
val errors = res1.errors ++ ress.flatMap(_.errors)
809831
Result(value, errors)
@@ -812,15 +834,15 @@ class Semantic {
812834
eval(expr, thisV, klass).ensureHot("return expression may only be initialized value", expr)
813835

814836
case WhileDo(cond, body) =>
815-
val ress = eval(cond :: body :: Nil, thisV, klass)
837+
val (ress, env2) = eval(cond :: body :: Nil, thisV, klass)
816838
Result(Hot, ress.flatMap(_.errors))
817839

818840
case Labeled(_, expr) =>
819841
eval(expr, thisV, klass)
820842

821843
case Try(block, cases, finalizer) =>
822844
val res1 = eval(block, thisV, klass)
823-
val ress = eval(cases.map(_.body), thisV, klass)
845+
val (ress, env2) = eval(cases.map(_.body), thisV, klass)
824846
val errors = ress.flatMap(_.errors)
825847
val resValue = ress.map(_.value).join
826848
if finalizer.isEmpty then
@@ -836,7 +858,7 @@ class Semantic {
836858
Result(Hot, ress.flatMap(_.errors))
837859

838860
case Inlined(call, bindings, expansion) =>
839-
val ress = eval(bindings, thisV, klass)
861+
val (ress, env2) = eval(bindings, thisV, klass)
840862
eval(expansion, thisV, klass) ++ ress.flatMap(_.errors)
841863

842864
case Thicket(List()) =>
@@ -846,7 +868,8 @@ class Semantic {
846868
case vdef : ValDef =>
847869
// local val definition
848870
// TODO: support explicit @cold annotation for local definitions
849-
eval(vdef.rhs, thisV, klass).ensureHot("Local definitions may only hold initialized values", vdef)
871+
eval(vdef.rhs, thisV, klass)
872+
// .ensureHot("Local definitions may only hold initialized values", vdef)
850873

851874
case ddef : DefDef =>
852875
// local method
@@ -891,7 +914,7 @@ class Semantic {
891914
// It's always safe to approximate them with `Cold`.
892915
Result(Cold, Nil)
893916
else
894-
default()
917+
Result(env.getOrElse(sym, Hot), Nil)
895918

896919
case tmref: TermRef =>
897920
cases(tmref.prefix, thisV, klass, source).select(tmref.symbol, source)
@@ -987,7 +1010,7 @@ class Semantic {
9871010
// parents
9881011
def initParent(parent: Tree, tasks: Tasks)(using Env) = parent match {
9891012
case tree @ Block(stats, NewExpr(tref, New(tpt), ctor, argss)) => // can happen
990-
eval(stats, thisV, klass).foreach { res => errorBuffer ++= res.errors }
1013+
eval(stats, thisV, klass)._1.foreach { res => errorBuffer ++= res.errors }
9911014
val (errors, args) = evalArgs(argss.flatten, thisV, klass)
9921015
errorBuffer ++= errors
9931016
superCall(tref, ctor, args, tree, tasks)

tests/init/neg/local-warm.check

Lines changed: 0 additions & 5 deletions
This file was deleted.

tests/init/neg/local-warm4.check

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
-- Error: tests/init/neg/local-warm4.scala:18:20 -----------------------------------------------------------------------
2+
18 | a = newA // error
3+
| ^^^^
4+
|Promote the value under initialization to fully-initialized. May only assign fully initialized value. Calling trace:
5+
| -> val a = new A(5) [ local-warm4.scala:26 ]
6+
| -> class A(x: Int) extends Foo(x) { [ local-warm4.scala:6 ]
7+
| -> val b = new B(y) [ local-warm4.scala:10 ]
8+
| -> class B(x: Int) extends A(x) { [ local-warm4.scala:13 ]
9+
| -> class A(x: Int) extends Foo(x) { [ local-warm4.scala:6 ]
10+
| -> increment() [ local-warm4.scala:9 ]
11+
| -> updateA() [ local-warm4.scala:21 ]

tests/init/neg/local-warm4.scala

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
object localWarm {
2+
abstract class Foo(x: Int) {
3+
def increment(): Unit
4+
}
5+
6+
class A(x: Int) extends Foo(x) {
7+
var y = x
8+
override def increment(): Unit = y = y + 1
9+
increment()
10+
val b = new B(y)
11+
}
12+
13+
class B(x: Int) extends A(x) {
14+
var a: A = this
15+
override def increment(): Unit = {
16+
def updateA(): Unit = {
17+
val newA = new A(y)
18+
a = newA // error
19+
}
20+
y = y + 1
21+
updateA()
22+
}
23+
if y < 10 then increment()
24+
val z = b.y
25+
}
26+
val a = new A(5)
27+
}
28+
29+
30+
31+
File renamed without changes.

tests/init/pos/local-warm2.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
class A {
2+
var v = 5
3+
val x = {
4+
class B {
5+
def doubleAndReturnV(): Int = {
6+
v = v * 2
7+
v
8+
}
9+
}
10+
val b = new B
11+
b.doubleAndReturnV()
12+
}
13+
val y = v
14+
}

tests/init/pos/local-warm3.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class A() {
2+
var x = 5
3+
def decreaseX(): Unit = {
4+
val self = this
5+
self.x -= 1
6+
}
7+
def decreaseXToZero(): Unit = if x > 0 then decreaseX()
8+
decreaseXToZero()
9+
val y = x
10+
}

tests/init/pos/local-warm5.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
object leakWarm5 {
2+
case class A(x: Int) {
3+
def double(): A = {
4+
class C {
5+
def double(): A = if x < 10 then A(x * 2).double() else A.this
6+
}
7+
val c = new C
8+
c.double()
9+
}
10+
}
11+
val a = A(2).double()
12+
}

0 commit comments

Comments
 (0)