@@ -30,7 +30,7 @@ object Checking {
30
30
*
31
31
*/
32
32
case class State (
33
- visited : mutable.Set [Effect ], // effects that have been expanded
33
+ visited : mutable.Set [Effect ], // effects that have been checked
34
34
path : Vector [Tree ], // the path that leads to the current effect
35
35
thisClass : ClassSymbol , // the concrete class of `this`
36
36
fieldsInited : mutable.Set [Symbol ],
@@ -41,6 +41,8 @@ object Checking {
41
41
visited += eff
42
42
copy(path = this .path :+ eff.source)
43
43
}
44
+
45
+ def withOwner (sym : Symbol ): State = copy(env = env.withOwner(sym))
44
46
}
45
47
46
48
private implicit def theEnv (implicit state : State ): Env = state.env
@@ -51,17 +53,19 @@ object Checking {
51
53
* However, summarization can be done lazily on-demand to improve
52
54
* performance.
53
55
*/
54
- def checkClassBody (cdef : TypeDef )(implicit state : State ): Unit = traceOp(" checking " + cdef.symbol.show, init) {
56
+ def checkClassBody (cdef : TypeDef )(implicit state : State ): Unit = {
57
+ traceIndented(" \n\n >>>> checking " + cdef.symbol.show, init)
58
+
55
59
val cls = cdef.symbol.asClass
56
60
val tpl = cdef.rhs.asInstanceOf [Template ]
57
61
58
62
// mark current class as initialized, required for linearization
59
63
state.parentsInited += cls
60
64
61
- def checkClassBodyStat (tree : Tree )(using Context ): Unit = traceOp(" checking " + tree.show, init) {
65
+ def checkClassBodyStat (tree : Tree )(implicit state : State ): Unit = traceOp(" checking " + tree.show, init) {
62
66
tree match {
63
67
case vdef : ValDef =>
64
- val (pots, effs) = Summarization .analyze(vdef.rhs)(theEnv.withOwner(vdef.symbol))
68
+ val (pots, effs) = Summarization .analyze(vdef.rhs)
65
69
theEnv.summaryOf(cls).cacheFor(vdef.symbol, (pots, effs))
66
70
if (! vdef.symbol.is(Flags .Lazy )) {
67
71
checkEffectsIn(effs, cls)
@@ -79,15 +83,31 @@ object Checking {
79
83
// see spec 5.1 about "Template Evaluation".
80
84
// https://www.scala-lang.org/files/archive/spec/2.13/05-classes-and-objects.html
81
85
82
- def checkCtor (ctor : Symbol , tp : Type , source : Tree )(using Context ): Unit = {
86
+ def checkConstructor (ctor : Symbol , tp : Type , source : Tree )(implicit state : State ): Unit = traceOp( " checking " + ctor.show, init) {
83
87
val cls = ctor.owner
84
88
val classDef = cls.defTree
85
89
if (! classDef.isEmpty) {
86
- if (ctor.isPrimaryConstructor) checkClassBody(classDef.asInstanceOf [TypeDef ])
87
- else checkSecondaryConstructor(ctor)
90
+ if (ctor.isPrimaryConstructor) checkClassBody(classDef.asInstanceOf [TypeDef ])(state.withOwner(cls))
91
+ else checkSecondaryConstructor(ctor)(state.withOwner(cls))
92
+ }
93
+ }
94
+
95
+ def checkSecondaryConstructor (ctor : Symbol )(implicit state : State ): Unit = traceOp(" checking " + ctor.show, init) {
96
+ val Block (ctorCall :: stats, expr) = ctor.defTree.asInstanceOf [DefDef ].rhs
97
+ val cls = ctor.owner.asClass
98
+
99
+ traceOp(" check ctor: " + ctorCall.show, init) {
100
+ val ctor = ctorCall.symbol
101
+ if (ctor.isPrimaryConstructor)
102
+ checkClassBody(cls.defTree.asInstanceOf [TypeDef ])
103
+ else
104
+ checkSecondaryConstructor(ctor)
105
+ }
106
+
107
+ (stats :+ expr).foreach { stat =>
108
+ val (_, effs) = Summarization .analyze(stat)(theEnv.withOwner(ctor))
109
+ checkEffectsIn(effs, cls)
88
110
}
89
- else if (! cls.isOneOf(Flags .EffectivelyOpenFlags ))
90
- report.warning(" Inheriting non-open class may cause initialization errors" , source.srcPos)
91
111
}
92
112
93
113
cls.paramAccessors.foreach { acc =>
@@ -100,61 +120,42 @@ object Checking {
100
120
tpl.parents.foreach {
101
121
case tree @ Block (_, parent) =>
102
122
val (ctor, _, _) = decomposeCall(parent)
103
- checkCtor (ctor.symbol, parent.tpe, tree)
123
+ checkConstructor (ctor.symbol, parent.tpe, tree)
104
124
105
125
case tree @ Apply (Block (_, parent), _) =>
106
126
val (ctor, _, _) = decomposeCall(parent)
107
- checkCtor (ctor.symbol, tree.tpe, tree)
127
+ checkConstructor (ctor.symbol, tree.tpe, tree)
108
128
109
129
case parent : Apply =>
110
130
val (ctor, _, argss) = decomposeCall(parent)
111
- checkCtor (ctor.symbol, parent.tpe, parent)
131
+ checkConstructor (ctor.symbol, parent.tpe, parent)
112
132
113
133
case ref =>
114
134
val cls = ref.tpe.classSymbol.asClass
115
135
if (! state.parentsInited.contains(cls) && cls.primaryConstructor.exists)
116
- checkCtor (cls.primaryConstructor, ref.tpe, ref)
136
+ checkConstructor (cls.primaryConstructor, ref.tpe, ref)
117
137
}
118
138
119
139
// check class body
120
140
tpl.body.foreach { checkClassBodyStat(_) }
121
141
}
122
142
123
- def checkSecondaryConstructor (ctor : Symbol )(implicit state : State ): Unit = traceOp(" checking " + ctor.show, init) {
124
- val Block (ctorCall :: stats, expr) = ctor.defTree.asInstanceOf [DefDef ].rhs
125
- val cls = ctor.owner.asClass
126
-
127
- traceOp(" check ctor: " + ctorCall.show, init) {
128
- val ctor = ctorCall.symbol
129
- if (ctor.isPrimaryConstructor)
130
- checkClassBody(cls.defTree.asInstanceOf [TypeDef ])
131
- else
132
- checkSecondaryConstructor(ctor)
133
- }
134
-
135
- (stats :+ expr).foreach { stat =>
136
- val (_, effs) = Summarization .analyze(stat)(theEnv.withOwner(ctor))
137
- checkEffectsIn(effs, cls)
138
- }
139
- }
140
-
141
143
private def checkEffectsIn (effs : Effects , cls : ClassSymbol )(implicit state : State ): Unit = traceOp(" checking effects " + Effects .show(effs), init) {
142
- val rebased = Effects .asSeenFrom(effs, ThisRef (state.thisClass)(null ), cls, Potentials .empty)
143
144
for {
144
- eff <- rebased
145
+ eff <- effs
145
146
error <- check(eff)
146
147
} error.issue
147
148
}
148
149
149
150
private def check (eff : Effect )(implicit state : State ): Errors =
150
- if (state.visited.contains(eff)) Errors .empty else trace(" checking effect " + eff.show, init, errs => Errors .show(errs.asInstanceOf [Errors ])) {
151
+ if (state.visited.contains(eff)) Errors .empty
152
+ else trace(" checking effect " + eff.show, init, errs => Errors .show(errs.asInstanceOf [Errors ])) {
151
153
implicit val state2 : State = state.withVisited(eff)
152
154
153
155
eff match {
154
156
case Promote (pot) =>
155
157
pot match {
156
- case pot @ ThisRef (cls) =>
157
- assert(cls == state.thisClass, " unexpected potential " + pot.show)
158
+ case pot : ThisRef =>
158
159
PromoteThis (pot, eff.source, state2.path).toErrors
159
160
160
161
case _ : Cold =>
@@ -180,18 +181,14 @@ object Checking {
180
181
case FieldAccess (pot, field) =>
181
182
182
183
pot match {
183
- case ThisRef (cls) =>
184
- assert(cls == state.thisClass, " unexpected potential " + pot.show)
185
-
186
- val target = resolve(cls, field)
184
+ case _ : ThisRef =>
185
+ val target = resolve(state.thisClass, field)
187
186
if (target.is(Flags .Lazy )) check(MethodCall (pot, target)(eff.source))
188
187
else if (! state.fieldsInited.contains(target)) AccessNonInit (target, state2.path).toErrors
189
188
else Errors .empty
190
189
191
- case SuperRef (ThisRef (cls), supercls) =>
192
- assert(cls == state.thisClass, " unexpected potential " + pot.show)
193
-
194
- val target = resolveSuper(cls, supercls, field)
190
+ case SuperRef (_ : ThisRef , supercls) =>
191
+ val target = resolveSuper(state.thisClass, supercls, field)
195
192
if (target.is(Flags .Lazy )) check(MethodCall (pot, target)(eff.source))
196
193
else if (! state.fieldsInited.contains(target)) AccessNonInit (target, state2.path).toErrors
197
194
else Errors .empty
@@ -217,10 +214,8 @@ object Checking {
217
214
218
215
case MethodCall (pot, sym) =>
219
216
pot match {
220
- case thisRef @ ThisRef (cls) =>
221
- assert(cls == state.thisClass, " unexpected potential " + pot.show)
222
-
223
- val target = resolve(cls, sym)
217
+ case thisRef : ThisRef =>
218
+ val target = resolve(state.thisClass, sym)
224
219
if (! target.isOneOf(Flags .Method | Flags .Lazy ))
225
220
check(FieldAccess (pot, target)(eff.source))
226
221
else if (target.isInternal) {
@@ -229,10 +224,8 @@ object Checking {
229
224
}
230
225
else CallUnknown (target, eff.source, state2.path).toErrors
231
226
232
- case SuperRef (thisRef @ ThisRef (cls), supercls) =>
233
- assert(cls == state.thisClass, " unexpected potential " + pot.show)
234
-
235
- val target = resolveSuper(cls, supercls, sym)
227
+ case SuperRef (thisRef : ThisRef , supercls) =>
228
+ val target = resolveSuper(state.thisClass, supercls, sym)
236
229
if (! target.is(Flags .Method ))
237
230
check(FieldAccess (pot, target)(eff.source))
238
231
else if (target.isInternal) {
@@ -272,17 +265,13 @@ object Checking {
272
265
pot match {
273
266
case MethodReturn (pot1, sym) =>
274
267
pot1 match {
275
- case thisRef @ ThisRef (cls) =>
276
- assert(cls == state.thisClass, " unexpected potential " + pot.show)
277
-
278
- val target = resolve(cls, sym)
268
+ case thisRef : ThisRef =>
269
+ val target = resolve(state.thisClass, sym)
279
270
if (target.isInternal) (thisRef.potentialsOf(target), Effects .empty)
280
271
else Summary .empty // warning already issued in call effect
281
272
282
- case SuperRef (thisRef @ ThisRef (cls), supercls) =>
283
- assert(cls == state.thisClass, " unexpected potential " + pot.show)
284
-
285
- val target = resolveSuper(cls, supercls, sym)
273
+ case SuperRef (thisRef : ThisRef , supercls) =>
274
+ val target = resolveSuper(state.thisClass, supercls, sym)
286
275
if (target.isInternal) (thisRef.potentialsOf(target), Effects .empty)
287
276
else Summary .empty // warning already issued in call effect
288
277
@@ -314,17 +303,13 @@ object Checking {
314
303
315
304
case FieldReturn (pot1, sym) =>
316
305
pot1 match {
317
- case thisRef @ ThisRef (cls) =>
318
- assert(cls == state.thisClass, " unexpected potential " + pot.show)
319
-
320
- val target = resolve(cls, sym)
306
+ case thisRef : ThisRef =>
307
+ val target = resolve(state.thisClass, sym)
321
308
if (sym.isInternal) (thisRef.potentialsOf(target), Effects .empty)
322
309
else (Cold ()(pot.source).toPots, Effects .empty)
323
310
324
- case SuperRef (thisRef @ ThisRef (cls), supercls) =>
325
- assert(cls == state.thisClass, " unexpected potential " + pot.show)
326
-
327
- val target = resolveSuper(cls, supercls, sym)
311
+ case SuperRef (thisRef : ThisRef , supercls) =>
312
+ val target = resolveSuper(state.thisClass, supercls, sym)
328
313
if (target.isInternal) (thisRef.potentialsOf(target), Effects .empty)
329
314
else (Cold ()(pot.source).toPots, Effects .empty)
330
315
@@ -347,19 +332,15 @@ object Checking {
347
332
348
333
case Outer (pot1, cls) =>
349
334
pot1 match {
350
- case ThisRef (cls) =>
351
- assert(cls == state.thisClass, " unexpected potential " + pot.show)
352
-
335
+ case _ : ThisRef =>
336
+ // all outers for `this` are assumed to be hot
353
337
Summary .empty
354
338
355
339
case _ : Fun =>
356
340
throw new Exception (" Unexpected code reached" )
357
341
358
342
case warm : Warm =>
359
- (warm.outerFor(cls), Effects .empty)
360
-
361
- case _ : Cold =>
362
- throw new Exception (" Unexpected code reached" )
343
+ (warm.resolveOuter(cls), Effects .empty)
363
344
364
345
case _ =>
365
346
val (pots, effs) = expand(pot1)
0 commit comments