Skip to content

Commit 282b68b

Browse files
authored
Fix scala#15883: Interpret inner static object access as this access (scala#15984)
Fix scala#15883: Interpret inner static object access as this access
2 parents eaa2889 + 9dec67f commit 282b68b

11 files changed

+77
-58
lines changed

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -79,35 +79,35 @@ object Errors:
7979
override def toString() = this.getClass.getName.nn
8080

8181
/** Access non-initialized field */
82-
case class AccessNonInit(field: Symbol, trace: Seq[Tree]) extends Error:
82+
case class AccessNonInit(field: Symbol)(val trace: Seq[Tree]) extends Error:
8383
def source: Tree = trace.last
8484
def show(using Context): String =
8585
"Access non-initialized " + field.show + "." + stacktrace
8686

8787
override def pos(using Context): SourcePosition = field.sourcePos
8888

8989
/** Promote a value under initialization to fully-initialized */
90-
case class PromoteError(msg: String, trace: Seq[Tree]) extends Error:
90+
case class PromoteError(msg: String)(val trace: Seq[Tree]) extends Error:
9191
def show(using Context): String = msg + stacktrace
9292

93-
case class AccessCold(field: Symbol, trace: Seq[Tree]) extends Error:
93+
case class AccessCold(field: Symbol)(val trace: Seq[Tree]) extends Error:
9494
def show(using Context): String =
9595
"Access field " + field.show + " on a cold object." + stacktrace
9696

97-
case class CallCold(meth: Symbol, trace: Seq[Tree]) extends Error:
97+
case class CallCold(meth: Symbol)(val trace: Seq[Tree]) extends Error:
9898
def show(using Context): String =
9999
"Call method " + meth.show + " on a cold object." + stacktrace
100100

101-
case class CallUnknown(meth: Symbol, trace: Seq[Tree]) extends Error:
101+
case class CallUnknown(meth: Symbol)(val trace: Seq[Tree]) extends Error:
102102
def show(using Context): String =
103103
val prefix = if meth.is(Flags.Method) then "Calling the external method " else "Accessing the external field"
104104
prefix + meth.show + " may cause initialization errors." + stacktrace
105105

106106
/** Promote a value under initialization to fully-initialized */
107-
case class UnsafePromotion(msg: String, trace: Seq[Tree], error: Error) extends Error:
107+
case class UnsafePromotion(msg: String, error: Error)(val trace: Seq[Tree]) extends Error:
108108
def show(using Context): String =
109109
msg + stacktrace + "\n" +
110-
"Promoting the value to hot failed due to the following problem:\n" + {
110+
"Promoting the value to hot (transitively initialized) failed due to the following problem:\n" + {
111111
val ctx2 = ctx.withProperty(IsFromPromotion, Some(true))
112112
error.show(using ctx2)
113113
}
@@ -116,7 +116,7 @@ object Errors:
116116
*
117117
* Invariant: argsIndices.nonEmpty
118118
*/
119-
case class UnsafeLeaking(trace: Seq[Tree], error: Error, nonHotOuterClass: Symbol, argsIndices: List[Int]) extends Error:
119+
case class UnsafeLeaking(error: Error, nonHotOuterClass: Symbol, argsIndices: List[Int])(val trace: Seq[Tree]) extends Error:
120120
def show(using Context): String =
121121
"Problematic object instantiation: " + argumentInfo() + stacktrace + "\n" +
122122
"It leads to the following error during object initialization:\n" +
@@ -141,5 +141,5 @@ object Errors:
141141
acc + text2
142142
}
143143
val verb = if multiple then " are " else " is "
144-
val adjective = "not hot."
144+
val adjective = "not hot (transitively initialized)."
145145
subject + verb + adjective

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

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ object Semantic:
156156
def hasField(f: Symbol) = fields.contains(f)
157157

158158
object Promoted:
159-
class PromotionInfo:
159+
class PromotionInfo(val entryClass: ClassSymbol):
160160
var isCurrentObjectPromoted: Boolean = false
161161
val values = mutable.Set.empty[Value]
162162
override def toString(): String = values.toString()
@@ -165,14 +165,15 @@ object Semantic:
165165
opaque type Promoted = PromotionInfo
166166

167167
/** Note: don't use `val` to avoid incorrect sharing */
168-
def empty: Promoted = new PromotionInfo
168+
def empty(entryClass: ClassSymbol): Promoted = new PromotionInfo(entryClass)
169169

170170
extension (promoted: Promoted)
171171
def isCurrentObjectPromoted: Boolean = promoted.isCurrentObjectPromoted
172172
def promoteCurrent(thisRef: ThisRef): Unit = promoted.isCurrentObjectPromoted = true
173173
def contains(value: Value): Boolean = promoted.values.contains(value)
174174
def add(value: Value): Unit = promoted.values += value
175175
def remove(value: Value): Unit = promoted.values -= value
176+
def entryClass: ClassSymbol = promoted.entryClass
176177
end extension
177178
end Promoted
178179
type Promoted = Promoted.Promoted
@@ -658,12 +659,12 @@ object Semantic:
658659

659660
def select(field: Symbol, receiver: Type, needResolve: Boolean = true): Contextual[Value] = log("select " + field.show + ", this = " + value, printer, (_: Value).show) {
660661
if promoted.isCurrentObjectPromoted then Hot
661-
else value match {
662+
else value match
662663
case Hot =>
663664
Hot
664665

665666
case Cold =>
666-
val error = AccessCold(field, trace.toVector)
667+
val error = AccessCold(field)(trace.toVector)
667668
reporter.report(error)
668669
Hot
669670

@@ -688,11 +689,11 @@ object Semantic:
688689
val rhs = target.defTree.asInstanceOf[ValOrDefDef].rhs
689690
eval(rhs, ref, target.owner.asClass, cacheResult = true)
690691
else
691-
val error = CallUnknown(field, trace.toVector)
692+
val error = CallUnknown(field)(trace.toVector)
692693
reporter.report(error)
693694
Hot
694695
else
695-
val error = AccessNonInit(target, trace.toVector)
696+
val error = AccessNonInit(target)(trace.toVector)
696697
reporter.report(error)
697698
Hot
698699
else
@@ -710,7 +711,6 @@ object Semantic:
710711

711712
case RefSet(refs) =>
712713
refs.map(_.select(field, receiver)).join
713-
}
714714
}
715715

716716
def call(meth: Symbol, args: List[ArgInfo], receiver: Type, superType: Type, needResolve: Boolean = true): Contextual[Value] = log("call " + meth.show + ", args = " + args.map(_.value.show), printer, (_: Value).show) {
@@ -779,7 +779,7 @@ object Semantic:
779779

780780
case Cold =>
781781
promoteArgs()
782-
val error = CallCold(meth, trace.toVector)
782+
val error = CallCold(meth)(trace.toVector)
783783
reporter.report(error)
784784
Hot
785785

@@ -820,7 +820,7 @@ object Semantic:
820820
// try promoting the receiver as last resort
821821
val hasErrors = Reporter.hasErrors { ref.promote("try promote value to hot") }
822822
if hasErrors then
823-
val error = CallUnknown(target, trace.toVector)
823+
val error = CallUnknown(target)(trace.toVector)
824824
reporter.report(error)
825825
Hot
826826
else if target.exists then
@@ -899,7 +899,7 @@ object Semantic:
899899
Hot
900900
else
901901
// no source code available
902-
val error = CallUnknown(ctor, trace.toVector)
902+
val error = CallUnknown(ctor)(trace.toVector)
903903
reporter.report(error)
904904
Hot
905905
}
@@ -922,7 +922,7 @@ object Semantic:
922922
yield
923923
i + 1
924924

925-
val error = UnsafeLeaking(trace.toVector, errors.head, nonHotOuterClass, indices)
925+
val error = UnsafeLeaking(errors.head, nonHotOuterClass, indices)(trace.toVector)
926926
reporter.report(error)
927927
Hot
928928
else
@@ -947,7 +947,7 @@ object Semantic:
947947
tryLeak(warm, NoSymbol, args2)
948948

949949
case Cold =>
950-
val error = CallCold(ctor, trace.toVector)
950+
val error = CallCold(ctor)(trace.toVector)
951951
reporter.report(error)
952952
Hot
953953

@@ -1078,15 +1078,15 @@ object Semantic:
10781078
case Hot =>
10791079

10801080
case Cold =>
1081-
reporter.report(PromoteError(msg, trace.toVector))
1081+
reporter.report(PromoteError(msg)(trace.toVector))
10821082

10831083
case thisRef: ThisRef =>
10841084
val emptyFields = thisRef.nonInitFields()
10851085
if emptyFields.isEmpty then
10861086
promoted.promoteCurrent(thisRef)
10871087
else
10881088
val fields = "Non initialized field(s): " + emptyFields.map(_.show).mkString(", ") + "."
1089-
reporter.report(PromoteError(msg + "\n" + fields, trace.toVector))
1089+
reporter.report(PromoteError(msg + "\n" + fields)(trace.toVector))
10901090

10911091
case warm: Warm =>
10921092
if !promoted.contains(warm) then
@@ -1106,7 +1106,7 @@ object Semantic:
11061106
res.promote("The function return value is not hot. Found = " + res.show + ".")
11071107
}
11081108
if errors.nonEmpty then
1109-
reporter.report(UnsafePromotion(msg, trace.toVector, errors.head))
1109+
reporter.report(UnsafePromotion(msg, errors.head)(trace.toVector))
11101110
else
11111111
promoted.add(fun)
11121112

@@ -1156,7 +1156,7 @@ object Semantic:
11561156
if !isHotSegment then
11571157
for member <- klass.info.decls do
11581158
if member.isClass then
1159-
val error = PromoteError("Promotion cancelled as the value contains inner " + member.show + ".", Vector.empty)
1159+
val error = PromoteError("Promotion cancelled as the value contains inner " + member.show + ".")(Vector.empty)
11601160
reporter.report(error)
11611161
else if !member.isType && !member.isConstructor && !member.is(Flags.Deferred) then
11621162
given Trace = Trace.empty
@@ -1189,7 +1189,7 @@ object Semantic:
11891189
}
11901190

11911191
if errors.isEmpty then Nil
1192-
else UnsafePromotion(msg, trace.toVector, errors.head) :: Nil
1192+
else UnsafePromotion(msg, errors.head)(trace.toVector) :: Nil
11931193
}
11941194

11951195
end extension
@@ -1230,7 +1230,7 @@ object Semantic:
12301230

12311231
@tailrec
12321232
def iterate(): Unit = {
1233-
given Promoted = Promoted.empty
1233+
given Promoted = Promoted.empty(thisRef.klass)
12341234
given Trace = Trace.empty.add(thisRef.klass.defTree)
12351235
given reporter: Reporter.BufferedReporter = new Reporter.BufferedReporter
12361236

@@ -1513,16 +1513,24 @@ object Semantic:
15131513
thisV.accessLocal(tmref, klass)
15141514

15151515
case tmref: TermRef =>
1516-
cases(tmref.prefix, thisV, klass).select(tmref.symbol, receiver = tmref.prefix)
1516+
val cls = tmref.widenSingleton.classSymbol
1517+
if cls.exists && cls.isStaticOwner then
1518+
if klass.isContainedIn(cls) then
1519+
resolveThis(cls.asClass, thisV, klass)
1520+
else if cls.isContainedIn(promoted.entryClass) then
1521+
cases(tmref.prefix, thisV, klass).select(tmref.symbol, receiver = tmref.prefix)
1522+
else
1523+
Hot
1524+
else
1525+
cases(tmref.prefix, thisV, klass).select(tmref.symbol, receiver = tmref.prefix)
15171526

15181527
case tp @ ThisType(tref) =>
15191528
val cls = tref.classSymbol.asClass
15201529
if cls.isStaticOwner && !klass.isContainedIn(cls) then
15211530
// O.this outside the body of the object O
15221531
Hot
15231532
else
1524-
val value = resolveThis(cls, thisV, klass)
1525-
value
1533+
resolveThis(cls, thisV, klass)
15261534

15271535
case _: TermParamRef | _: RecThis =>
15281536
// possible from checking effects of types
@@ -1664,7 +1672,16 @@ object Semantic:
16641672
if thisV.isThisRef || !thisV.asInstanceOf[Warm].isPopulatingParams then tpl.body.foreach {
16651673
case vdef : ValDef if !vdef.symbol.is(Flags.Lazy) && !vdef.rhs.isEmpty =>
16661674
val res = eval(vdef.rhs, thisV, klass)
1667-
thisV.updateField(vdef.symbol, res)
1675+
// TODO: Improve promotion to avoid handling enum initialization specially
1676+
//
1677+
// The failing case is tests/init/pos/i12544.scala due to promotion failure.
1678+
if vdef.symbol.name == nme.DOLLAR_VALUES
1679+
&& vdef.symbol.is(Flags.Synthetic)
1680+
&& vdef.symbol.owner.companionClass.is(Flags.Enum)
1681+
then
1682+
thisV.updateField(vdef.symbol, Hot)
1683+
else
1684+
thisV.updateField(vdef.symbol, res)
16681685
fieldsChanged = true
16691686

16701687
case _: MemberDef =>

tests/init/neg/closureLeak.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
| -> l.foreach(a => a.addX(this)) // error [ closureLeak.scala:11 ]
99
| ^^^^^^^^^^^^^^^^^
1010
|
11-
| Promoting the value to hot failed due to the following problem:
11+
| Promoting the value to hot (transitively initialized) failed due to the following problem:
1212
| Cannot prove the method argument is hot. Only hot values are safe to leak.
1313
| Found = ThisRef[class Outer].
1414
| Non initialized field(s): value p. Promotion trace:

tests/init/neg/cycle-structure.check

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
-- Error: tests/init/neg/cycle-structure.scala:3:13 --------------------------------------------------------------------
22
3 | val x = B(this) // error
33
| ^^^^^^^
4-
| Problematic object instantiation: arg 1 is not hot. Calling trace:
4+
| Problematic object instantiation: arg 1 is not hot (transitively initialized). Calling trace:
55
| -> case class A(b: B) { [ cycle-structure.scala:1 ]
66
| ^
77
| -> val x = B(this) // error [ cycle-structure.scala:3 ]
@@ -16,7 +16,7 @@
1616
-- Error: tests/init/neg/cycle-structure.scala:9:13 --------------------------------------------------------------------
1717
9 | val x = A(this) // error
1818
| ^^^^^^^
19-
| Problematic object instantiation: arg 1 is not hot. Calling trace:
19+
| Problematic object instantiation: arg 1 is not hot (transitively initialized). Calling trace:
2020
| -> case class B(a: A) { [ cycle-structure.scala:7 ]
2121
| ^
2222
| -> val x = A(this) // error [ cycle-structure.scala:9 ]

tests/init/neg/i15363.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
-- Error: tests/init/neg/i15363.scala:3:10 -----------------------------------------------------------------------------
22
3 | val b = new B(this) // error
33
| ^^^^^^^^^^^
4-
| Problematic object instantiation: arg 1 is not hot. Calling trace:
4+
| Problematic object instantiation: arg 1 is not hot (transitively initialized). Calling trace:
55
| -> class A: [ i15363.scala:1 ]
66
| ^
77
| -> val b = new B(this) // error [ i15363.scala:3 ]

tests/init/neg/i15883.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
val a = b
2+
val b = 1 // error

tests/init/neg/inherit-non-hot.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
| -> if b == null then b = new B(this) // error [ inherit-non-hot.scala:6 ]
1212
| ^^^^^^^^^^^^^^^
1313
|
14-
| Promoting the value to hot failed due to the following problem:
14+
| Promoting the value to hot (transitively initialized) failed due to the following problem:
1515
| Cannot prove that the field value a is hot. Found = Cold. Promotion trace:
1616
| -> class B(a: A) { [ inherit-non-hot.scala:10 ]
1717
| ^^^^

tests/init/neg/promotion-loop.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
| -> println(b) // error [ promotion-loop.scala:16 ]
99
| ^
1010
|
11-
| Promoting the value to hot failed due to the following problem:
11+
| Promoting the value to hot (transitively initialized) failed due to the following problem:
1212
| Cannot prove that the field value outer is hot. Found = ThisRef[class Test].
1313
| Non initialized field(s): value n. Promotion trace:
1414
| -> val outer = test [ promotion-loop.scala:12 ]

tests/init/neg/promotion-segment3.check

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88
| -> bar(new B) // error [ promotion-segment3.scala:9 ]
99
| ^^^^^
1010
|
11-
| Promoting the value to hot failed due to the following problem:
11+
| Promoting the value to hot (transitively initialized) failed due to the following problem:
1212
| Promotion cancelled as the value contains inner class C.

tests/init/neg/secondary-ctor4.check

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
-- Error: tests/init/neg/secondary-ctor4.scala:54:14 -------------------------------------------------------------------
22
54 | val c = new C(b, 5) // error
33
| ^^^^^^^^^^^
4-
| Problematic object instantiation: arg 1 is not hot. Calling trace:
4+
| Problematic object instantiation: arg 1 is not hot (transitively initialized). Calling trace:
55
| -> class D { [ secondary-ctor4.scala:52 ]
66
| ^
77
| -> val c = new C(b, 5) // error [ secondary-ctor4.scala:54 ]
@@ -24,21 +24,21 @@
2424
-- Error: tests/init/neg/secondary-ctor4.scala:42:4 --------------------------------------------------------------------
2525
42 | new A(new B(new D)) // error
2626
| ^^^^^^^^^^^^^^^^^^^
27-
| Problematic object instantiation: the outer M.this and arg 1 are not hot. Calling trace:
28-
| -> class N(d: D) extends M(d) { [ secondary-ctor4.scala:59 ]
29-
| ^
30-
| -> def this(d: D) = { [ secondary-ctor4.scala:7 ]
31-
| ^
32-
| -> new A(new B(new D)) // error [ secondary-ctor4.scala:42 ]
33-
| ^^^^^^^^^^^^^^^^^^^
27+
|Problematic object instantiation: the outer M.this and arg 1 are not hot (transitively initialized). Calling trace:
28+
|-> class N(d: D) extends M(d) { [ secondary-ctor4.scala:59 ]
29+
| ^
30+
|-> def this(d: D) = { [ secondary-ctor4.scala:7 ]
31+
| ^
32+
|-> new A(new B(new D)) // error [ secondary-ctor4.scala:42 ]
33+
| ^^^^^^^^^^^^^^^^^^^
3434
|
35-
| It leads to the following error during object initialization:
36-
| Access field value n on a cold object. Calling trace:
37-
| -> def this(b: B) = { [ secondary-ctor4.scala:17 ]
38-
| ^
39-
| -> Inner().foo() [ secondary-ctor4.scala:26 ]
40-
| ^^^^^^^
41-
| -> class Inner() { [ secondary-ctor4.scala:21 ]
42-
| ^
43-
| -> println(b.n) [ secondary-ctor4.scala:23 ]
44-
| ^^^
35+
|It leads to the following error during object initialization:
36+
|Access field value n on a cold object. Calling trace:
37+
|-> def this(b: B) = { [ secondary-ctor4.scala:17 ]
38+
| ^
39+
|-> Inner().foo() [ secondary-ctor4.scala:26 ]
40+
| ^^^^^^^
41+
|-> class Inner() { [ secondary-ctor4.scala:21 ]
42+
| ^
43+
|-> println(b.n) [ secondary-ctor4.scala:23 ]
44+
| ^^^

tests/init/neg/t3273.check

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
| -> val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error [ t3273.scala:4 ]
99
| ^^^^^^^^^^^^^^^
1010
|
11-
| Promoting the value to hot failed due to the following problem:
11+
| Promoting the value to hot (transitively initialized) failed due to the following problem:
1212
| Access non-initialized value num1. Promotion trace:
1313
| -> val num1: LazyList[Int] = 1 #:: num1.map(_ + 1) // error [ t3273.scala:4 ]
1414
| ^^^^
@@ -22,7 +22,7 @@
2222
| -> val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error [ t3273.scala:5 ]
2323
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
2424
|
25-
| Promoting the value to hot failed due to the following problem:
25+
| Promoting the value to hot (transitively initialized) failed due to the following problem:
2626
| Access non-initialized value num2. Promotion trace:
2727
| -> val num2: LazyList[Int] = 1 #:: num2.iterator.map(_ + 1).to(LazyList) // error [ t3273.scala:5 ]
2828
| ^^^^

0 commit comments

Comments
 (0)