Skip to content

Commit ab2e265

Browse files
committed
Accumulate errors in effect checking
1 parent 2fe9717 commit ab2e265

File tree

2 files changed

+52
-94
lines changed

2 files changed

+52
-94
lines changed

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

Lines changed: 30 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import util.NoSourcePosition
1515
import reporting.trace
1616
import config.Printers.init
1717

18-
import Effects._, Potentials._, Summary._, Util._
18+
import Effects._, Potentials._, Summary._, Util._, Errors._
1919

2020
object Checking {
2121
/** The checking state
@@ -41,26 +41,6 @@ object Checking {
4141
visited += eff
4242
copy(path = this.path :+ eff.source)
4343
}
44-
45-
def trace(implicit state: State): String = {
46-
var indentCount = 0
47-
var last = ""
48-
val sb = new StringBuilder
49-
this.path.foreach { tree =>
50-
indentCount += 1
51-
val pos = tree.sourcePos
52-
val line = "[ " + pos.source.file.name + ":" + (pos.line + 1) + " ]"
53-
if (last != line)
54-
sb.append(
55-
if (pos.source.exists)
56-
i"${ " " * indentCount }-> ${pos.lineContent.trim}\t$line\n"
57-
else
58-
i"${tree.show}\n"
59-
)
60-
last = line
61-
}
62-
sb.toString
63-
}
6444
}
6545

6646
private implicit def theEnv(implicit state: State): Env = state.env
@@ -158,66 +138,36 @@ object Checking {
158138
}
159139
}
160140

161-
private def nonInitError(field: Symbol)(implicit state: State) = {
162-
traceIndented("initialized: " + state.fieldsInited, init)
163-
164-
// should issue error, use warning so that it will continue compile subprojects
165-
theCtx.warning(
166-
"Access non-initialized field " + field.show +
167-
". Calling trace:\n" + state.trace,
168-
field.sourcePos
169-
)
170-
}
171-
172-
private def externalCallError(sym: Symbol, source: Tree)(implicit state: State) =
173-
theCtx.warning(
174-
"Calling the external method " + sym.show +
175-
" may cause initialization errors" +
176-
". Calling trace:\n" + state.trace,
177-
source.sourcePos
178-
)
179-
180141
private def checkEffectsIn(effs: Effects, cls: ClassSymbol)(implicit state: State): Unit = {
181142
val rebased = Effects.asSeenFrom(effs, ThisRef(state.thisClass)(null), cls, Potentials.empty)
182143
rebased.foreach { check(_) }
183144
}
184145

185-
private def check(eff: Effect)(implicit state: State): Unit =
186-
if (!state.visited.contains(eff)) traceOp("checking effect " + eff.show, init) {
146+
private def check(eff: Effect)(implicit state: State): Errors =
147+
if (state.visited.contains(eff)) Errors.empty else trace("checking effect " + eff.show, init, errs => Errors.show(errs.asInstanceOf[Errors])) {
187148
implicit val state2: State = state.withVisited(eff)
188149

189150
eff match {
190151
case Promote(pot) =>
191152
pot match {
192-
case ThisRef(cls) =>
153+
case pot @ ThisRef(cls) =>
193154
assert(cls == state.thisClass, "unexpected potential " + pot.show)
194-
195-
theCtx.warning(
196-
"Promote `this` to be initialized while it is not. Calling trace:\n" + state.trace,
197-
eff.source.sourcePos
198-
)
155+
PromoteThis(pot, eff.source, state.path).toErrors
199156

200157
case _: Cold =>
201-
theCtx.warning(
202-
"Promoting the value " + eff.source.show + " to be initialized while it is under initialization" +
203-
". Calling trace:\n" + state.trace,
204-
eff.source.sourcePos
205-
)
158+
PromoteCold(eff.source, state.path).toErrors
206159

207-
case Warm(cls, outer) =>
208-
theCtx.warning(
209-
"Promoting the value under initialization to be initialized: " + pot.source.show +
210-
". Calling trace:\n" + state.trace,
211-
eff.source.sourcePos
212-
)
160+
case pot @ Warm(cls, outer) =>
161+
PromoteWarm(pot, eff.source, state.path).toErrors
213162

214163
case Fun(pots, effs) =>
215-
effs.foreach { check(_) }
216-
pots.foreach { pot => check(Promote(pot)(eff.source)) }
164+
val errs1 = effs.flatMap { check(_) }
165+
val errs2 = pots.flatMap { pot => check(Promote(pot)(eff.source)) }
166+
UnsafePromotion(pot, eff.source, state.path, errs1 ++ errs2).toErrors
217167

218168
case pot =>
219169
val pots = expand(pot)
220-
pots.foreach { pot => check(Promote(pot)(eff.source)) }
170+
pots.flatMap { pot => check(Promote(pot)(eff.source)) }
221171
}
222172

223173
case FieldAccess(pot, field) =>
@@ -228,33 +178,32 @@ object Checking {
228178

229179
val target = resolve(cls, field)
230180
if (target.is(Flags.Lazy)) check(MethodCall(pot, target)(eff.source))
231-
else if (!state.fieldsInited.contains(target)) nonInitError(target)
181+
else if (!state.fieldsInited.contains(target)) AccessNonInit(target, state.path).toErrors
182+
else Errors.empty
232183

233184
case SuperRef(ThisRef(cls), supercls) =>
234185
assert(cls == state.thisClass, "unexpected potential " + pot.show)
235186

236187
val target = resolveSuper(cls, supercls, field)
237188
if (target.is(Flags.Lazy)) check(MethodCall(pot, target)(eff.source))
238-
else if (!state.fieldsInited.contains(target)) nonInitError(target)
189+
else if (!state.fieldsInited.contains(target)) AccessNonInit(target, state.path).toErrors
190+
else Errors.empty
239191

240192
case Warm(cls, outer) =>
241193
// all fields of warm values are initialized
242194
val target = resolve(cls, field)
243195
if (target.is(Flags.Lazy)) check(MethodCall(pot, target)(eff.source))
196+
else Errors.empty
244197

245198
case _: Cold =>
246-
theCtx.warning(
247-
"Access field " + eff.source.show + " on a known value under initialization" +
248-
". Calling trace:\n" + state.trace,
249-
eff.source.sourcePos
250-
)
199+
AccessCold(field, eff.source, state.path).toErrors
251200

252201
case Fun(pots, effs) =>
253202
throw new Exception("Unexpected effect " + eff.show)
254203

255204
case pot =>
256205
val pots = expand(pot)
257-
pots.foreach { pot => check(FieldAccess(pot, field)(eff.source)) }
206+
pots.flatMap { pot => check(FieldAccess(pot, field)(eff.source)) }
258207
}
259208

260209
case MethodCall(pot, sym) =>
@@ -267,9 +216,9 @@ object Checking {
267216
check(FieldAccess(pot, target)(eff.source))
268217
else if (target.isInternal) {
269218
val effs = thisRef.effectsOf(target)
270-
effs.foreach { check(_) }
219+
effs.flatMap { check(_) }
271220
}
272-
else externalCallError(target, eff.source)
221+
else CallUnknown(target, eff.source, state.path).toErrors
273222

274223
case SuperRef(thisRef @ ThisRef(cls), supercls) =>
275224
assert(cls == state.thisClass, "unexpected potential " + pot.show)
@@ -279,37 +228,32 @@ object Checking {
279228
check(FieldAccess(pot, target)(eff.source))
280229
else if (target.isInternal) {
281230
val effs = thisRef.effectsOf(target)
282-
effs.foreach { check(_) }
231+
effs.flatMap { check(_) }
283232
}
284-
else externalCallError(target, eff.source)
233+
else CallUnknown(target, eff.source, state.path).toErrors
285234

286235
case warm @ Warm(cls, outer) =>
287236
val target = resolve(cls, sym)
288237

289238
if (target.isInternal) {
290239
val effs = warm.effectsOf(target)
291-
effs.foreach { check(_) }
240+
effs.flatMap { check(_) }
292241
}
293-
else if (!sym.isConstructor) externalCallError(target, eff.source)
242+
else if (!sym.isConstructor) CallUnknown(target, eff.source, state.path).toErrors
243+
else Errors.empty
294244

295245
case _: Cold =>
296-
theCtx.warning(
297-
"Call method " + eff.source.show + " on a cold value" +
298-
". Calling trace:\n" + state.trace,
299-
eff.source.sourcePos
300-
)
246+
CallCold(sym, eff.source, state.path).toErrors
301247

302248
case Fun(pots, effs) =>
303249
// TODO: assertion might be false, due to SAM
304-
if (sym.name.toString == "apply") {
305-
effs.foreach { check(_) }
306-
pots.foreach { pot => check(Promote(pot)(eff.source)) }
307-
}
250+
if (sym.name.toString == "apply") effs.flatMap { check(_) }
251+
else Errors.empty
308252
// curried, tupled, toString are harmless
309253

310254
case pot =>
311255
val pots = expand(pot)
312-
pots.foreach { pot =>
256+
pots.flatMap { pot =>
313257
check(MethodCall(pot, sym)(eff.source))
314258
}
315259
}

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

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,15 @@ object Errors {
1414
type Errors = Set[Error]
1515
val empty: Errors = Set.empty
1616

17+
def show(errs: Errors)(implicit ctx: Context): String =
18+
errs.map(_.show).mkString(", ")
19+
1720
sealed trait Error {
1821
def trace: Vector[Tree]
1922
def report(implicit ctx: Context): Unit
20-
def message(implicit ctx: Context): String
23+
def show(implicit ctx: Context): String
24+
25+
def toErrors: Errors = Set(this)
2126

2227
def stacktrace(implicit ctx: Context): String = {
2328
var indentCount = 0
@@ -42,45 +47,54 @@ object Errors {
4247

4348
/** Access non-initialized field */
4449
case class AccessNonInit(field: Symbol, trace: Vector[Tree]) extends Error {
45-
def message(implicit ctx: Context): String =
50+
def show(implicit ctx: Context): String =
4651
"Access non-initialized field " + field.show + ". Calling trace:\n" + stacktrace
4752

4853
def report(implicit ctx: Context): Unit = ???
4954
}
5055

5156
/** Promote `this` under initialization to fully-initialized */
5257
case class PromoteThis(pot: ThisRef, source: Tree, trace: Vector[Tree]) extends Error {
53-
def message(implicit ctx: Context): String = "Promote `this` to be initialized while it is not. Calling trace:\n" + stacktrace
58+
def show(implicit ctx: Context): String = "Promote `this` to be initialized while it is not. Calling trace:\n" + stacktrace
59+
def report(implicit ctx: Context): Unit = ???
60+
}
61+
62+
/** Promote `this` under initialization to fully-initialized */
63+
case class PromoteWarm(pot: Warm, source: Tree, trace: Vector[Tree]) extends Error {
64+
def show(implicit ctx: Context): String =
65+
"Promoting the value under initialization to be initialized: " + source.show +
66+
". Calling trace:\n" + stacktrace
67+
5468
def report(implicit ctx: Context): Unit = ???
5569
}
5670

5771
/** Promote a cold value under initialization to fully-initialized */
5872
case class PromoteCold(source: Tree, trace: Vector[Tree]) extends Error {
59-
def message(implicit ctx: Context): String =
73+
def show(implicit ctx: Context): String =
6074
"Promoting the value " + source.show + " to be initialized while it is under initialization" +
6175
". Calling trace:\n" + stacktrace
6276

6377
def report(implicit ctx: Context): Unit = ???
6478
}
6579

6680
case class AccessCold(field: Symbol, source: Tree, trace: Vector[Tree]) extends Error {
67-
def message(implicit ctx: Context): String =
81+
def show(implicit ctx: Context): String =
6882
"Access field " + source.show + " on a value under unknown initialization status" +
6983
". Calling trace:\n" + stacktrace
7084

7185
def report(implicit ctx: Context): Unit = ???
7286
}
7387

7488
case class CallCold(meth: Symbol, source: Tree, trace: Vector[Tree]) extends Error {
75-
def message(implicit ctx: Context): String =
89+
def show(implicit ctx: Context): String =
7690
"Call method " + source.show + " on a value under unknown initialization" +
7791
". Calling trace:\n" + stacktrace
7892

7993
def report(implicit ctx: Context): Unit = ???
8094
}
8195

8296
case class CallUnknown(meth: Symbol, source: Tree, trace: Vector[Tree]) extends Error {
83-
def message(implicit ctx: Context): String =
97+
def show(implicit ctx: Context): String =
8498
"Calling the external method " + meth.show +
8599
" may cause initialization errors" + ". Calling trace:\n" + stacktrace
86100

@@ -89,7 +103,7 @@ object Errors {
89103

90104
/** Promote a value under initialization to fully-initialized */
91105
case class UnsafePromotion(pot: Potential, source: Tree, trace: Vector[Tree], errors: Set[Error]) extends Error {
92-
def message(implicit ctx: Context): String = ???
106+
def show(implicit ctx: Context): String = ???
93107

94108
def report(implicit ctx: Context): Unit = ???
95109
}

0 commit comments

Comments
 (0)