@@ -10,7 +10,7 @@ import Names.TermName
10
10
import NameKinds .{InlineAccessorName , InlineBinderName , InlineScrutineeName }
11
11
import config .Printers .inlining
12
12
import util .SimpleIdentityMap
13
-
13
+ import dotty . tools . dotc . reporting . Message
14
14
import collection .mutable
15
15
16
16
/** A utility class offering methods for rewriting inlined code */
@@ -146,17 +146,16 @@ class InlineReducer(inliner: Inliner)(using Context):
146
146
}
147
147
binding1.withSpan(call.span)
148
148
}
149
- // (I guess) these `Option`s should be Either[ErrorMessage, T] to show msg from `@implicitNotFound`.
150
149
/** The result type of reducing a match. It consists optionally of a list of bindings
151
150
* for the pattern-bound variables and the RHS of the selected case.
152
- * Returns `None` if no case was selected.
151
+ * Returns `Left` with an optional message for implicitNotFound if no case was selected.
153
152
*/
154
- type MatchRedux = Option [(List [MemberDef ], Tree )]
153
+ type MatchRedux = Either [ Option [Message ], (List [MemberDef ], Tree )]
155
154
156
155
/** Same as MatchRedux, but also includes a boolean
157
156
* that is true if the guard can be checked at compile time.
158
157
*/
159
- type MatchReduxWithGuard = Option [(List [MemberDef ], Tree , Boolean )]
158
+ type MatchReduxWithGuard = Either [ Option [Message ], (List [MemberDef ], Tree , Boolean )]
160
159
161
160
/** Reduce an inline match
162
161
* @param mtch the match tree
@@ -173,13 +172,14 @@ class InlineReducer(inliner: Inliner)(using Context):
173
172
val isImplicit = scrutinee.isEmpty
174
173
175
174
/** Try to match pattern `pat` against scrutinee reference `scrut`. If successful add
176
- * bindings for variables bound in this pattern to `caseBindingMap`.
175
+ * bindings for variables bound in this pattern to `caseBindingMap` and return None.
176
+ * Otherwise, it returns optional error message.
177
177
*/
178
178
def reducePattern (
179
179
caseBindingMap : mutable.ListBuffer [(Symbol , MemberDef )],
180
180
scrut : TermRef ,
181
181
pat : Tree
182
- )(using Context ): Boolean = {
182
+ )(using Context ): Option [ Option [ Message ]] = {
183
183
184
184
/** Create a binding of a pattern bound variable with matching part of
185
185
* scrutinee as RHS and type that corresponds to RHS.
@@ -193,26 +193,28 @@ class InlineReducer(inliner: Inliner)(using Context):
193
193
val copied = sym.copy(info = TypeAlias (alias), coord = sym.coord).asType
194
194
caseBindingMap += ((sym, TypeDef (copied)))
195
195
}
196
-
197
- def searchImplicit (sym : TermSymbol , tpt : Tree ) = {
196
+
197
+ /**
198
+ * @return
199
+ * None if implicit search finds an instance or fails with a hard error.
200
+ * Otherwise, return the error message for missing implicit instance.
201
+ */
202
+ def searchImplicit (sym : TermSymbol , tpt : Tree ): Option [Message ] = {
198
203
val evTyper = new Typer (ctx.nestingLevel + 1 )
199
204
val evCtx = ctx.fresh.setTyper(evTyper)
200
205
inContext(evCtx) {
201
206
val evidence = evTyper.inferImplicitArg(tpt.tpe, tpt.span)
202
207
evidence.tpe match {
203
208
case fail : Implicits .AmbiguousImplicits =>
204
209
report.error(evTyper.missingArgMsg(evidence, tpt.tpe, " " ), tpt.srcPos)
205
- true // hard error: return true to stop implicit search here
210
+ None // hard error: return None to stop implicit search here
206
211
case fail : Implicits .SearchFailureType =>
207
- // Here, we get a message from `@implicitNotFound(...)` like "there is no Inst!"
208
- // let's give this value back to caller
209
- val msgFromAnnotation = evTyper.missingArgMsg(evidence, tpt.tpe, " " )
210
- println(msgFromAnnotation)
211
- false
212
+ val noImplicitMsg = evTyper.missingArgMsg(evidence, tpt.tpe, " " )
213
+ Some (noImplicitMsg)
212
214
case _ =>
213
215
// inlining.println(i"inferred implicit $sym: ${sym.info} with $evidence: ${evidence.tpe.widen}, ${evCtx.gadt.constraint}, ${evCtx.typerState.constraint}")
214
216
newTermBinding(sym, evidence)
215
- true
217
+ None
216
218
}
217
219
}
218
220
}
@@ -284,48 +286,62 @@ class InlineReducer(inliner: Inliner)(using Context):
284
286
case Typed (pat1, tpt) =>
285
287
val typeBinds = getTypeBindsMap(pat1, tpt)
286
288
registerAsGadtSyms(typeBinds)
287
- scrut <:< tpt.tpe && {
289
+ if ( scrut <:< tpt.tpe) then
288
290
addTypeBindings(typeBinds)
289
291
reducePattern(caseBindingMap, scrut, pat1)
290
- }
292
+ else
293
+ Some (None )
291
294
case pat @ Bind (name : TermName , Typed (_, tpt)) if isImplicit =>
292
295
val typeBinds = getTypeBindsMap(tpt, tpt)
293
296
registerAsGadtSyms(typeBinds)
294
- searchImplicit(pat.symbol.asTerm, tpt) && {
295
- addTypeBindings(typeBinds)
296
- true
297
- }
297
+ searchImplicit(pat.symbol.asTerm, tpt) match
298
+ // found or hard error
299
+ case None =>
300
+ addTypeBindings(typeBinds)
301
+ None
302
+ case Some (msg) =>
303
+ Some (Some (msg))
304
+
298
305
case pat @ Bind (name : TermName , body) =>
299
- reducePattern(caseBindingMap, scrut, body) && {
300
- if (name != nme.WILDCARD ) newTermBinding(pat.symbol.asTerm, ref(scrut))
301
- true
302
- }
306
+ reducePattern(caseBindingMap,scrut,body) match
307
+ case None if name != nme.WILDCARD =>
308
+ newTermBinding(pat.symbol.asTerm, ref(scrut))
309
+ None
310
+ case None =>
311
+ None
312
+ case Some (msg) =>
313
+ Some (msg)
314
+
303
315
case Ident (nme.WILDCARD ) =>
304
- true
316
+ None
305
317
case pat : Literal =>
306
- scrut.widenTermRefExpr =:= pat.tpe
318
+ if scrut.widenTermRefExpr =:= pat.tpe then
319
+ None
320
+ else Some (None )
307
321
case pat : RefTree =>
308
- scrut =:= pat.tpe ||
309
- scrut.classSymbol.is( Module ) && scrut.widen =:= pat.tpe.widen && {
310
- scrut.prefix match {
311
- case _ : SingletonType | NoPrefix => true
312
- case _ => false
313
- }
314
- }
322
+ if ( scrut =:= pat.tpe) then
323
+ None
324
+ else if ( scrut.classSymbol.is( Module ) && scrut.widen =:= pat.tpe.widen) then
325
+ scrut.prefix match
326
+ case _ : SingletonType | NoPrefix => None
327
+ case _ => Some ( None )
328
+ else Some ( None )
315
329
case UnApply (unapp, _, pats) =>
316
330
unapp.tpe.widen match {
317
331
case mt : MethodType if mt.paramInfos.length == 1 =>
318
332
319
- def reduceSubPatterns (pats : List [Tree ], selectors : List [Tree ]): Boolean = (pats, selectors) match {
320
- case (Nil , Nil ) => true
333
+ def reduceSubPatterns (pats : List [Tree ], selectors : List [Tree ]): Option [ Option [ Message ]] = (pats, selectors) match {
334
+ case (Nil , Nil ) => None
321
335
case (pat :: pats1, selector :: selectors1) =>
322
336
val elem = newSym(InlineBinderName .fresh(), Synthetic , selector.tpe.widenInlineScrutinee).asTerm
323
337
val rhs = constToLiteral(selector)
324
338
elem.defTree = rhs
325
339
caseBindingMap += ((NoSymbol , ValDef (elem, rhs).withSpan(elem.span)))
326
- reducePattern(caseBindingMap, elem.termRef, pat) &&
327
- reduceSubPatterns(pats1, selectors1)
328
- case _ => false
340
+ reducePattern(caseBindingMap, elem.termRef, pat) match
341
+ case None => reduceSubPatterns(pats1, selectors1)
342
+ case Some (msg) => Some (msg)
343
+
344
+ case _ => Some (None )
329
345
}
330
346
331
347
val paramType = mt.paramInfos.head
@@ -337,17 +353,42 @@ class InlineReducer(inliner: Inliner)(using Context):
337
353
val selectors =
338
354
for (accessor <- caseAccessors)
339
355
yield constToLiteral(reduceProjection(ref(scrut).select(accessor).ensureApplied))
340
- caseAccessors.length == pats.length && reduceSubPatterns(pats, selectors)
356
+ if caseAccessors.length == pats.length then reduceSubPatterns(pats, selectors)
357
+ else Some (None )
341
358
}
342
- else false
359
+ else Some ( None )
343
360
case _ =>
344
- false
361
+ Some ( None )
345
362
}
346
363
case Alternative (pats) =>
347
- pats.exists(reducePattern(caseBindingMap, scrut, _))
364
+ /**
365
+ * Apply `reducePattern` on `caseBindingMap` until it finds a match.
366
+ *
367
+ * @param ps a list of patterns to match
368
+ * @param theLastMsg the last message for missing implicit instance
369
+ *
370
+ * @return `None` if it finds a match. Otherwise, it returns optional error message.
371
+ */
372
+ def applyReduceUntilFind (
373
+ ps : List [Tree ],
374
+ theLastMsg : Option [Message ] = None
375
+ ): Option [Option [Message ]] =
376
+ ps match
377
+ case Nil => Some (theLastMsg)
378
+ case p :: ps =>
379
+ reducePattern(caseBindingMap, scrut, p) match
380
+ // it finds a match or fails with hard error
381
+ case None => None
382
+ // implicit search fails with message
383
+ case Some (msg @ Some (_)) =>
384
+ applyReduceUntilFind(ps, msg)
385
+ case Some (None ) =>
386
+ applyReduceUntilFind(ps, theLastMsg)
387
+ end applyReduceUntilFind
388
+ applyReduceUntilFind(pats)
348
389
case tree : Inlined if tree.inlinedFromOuterScope =>
349
390
reducePattern(caseBindingMap, scrut, tree.expansion)
350
- case _ => false
391
+ case _ => Some ( None )
351
392
}
352
393
}
353
394
@@ -372,34 +413,30 @@ class InlineReducer(inliner: Inliner)(using Context):
372
413
373
414
if (! isImplicit) caseBindingMap += ((NoSymbol , scrutineeBinding))
374
415
val gadtCtx = ctx.fresh.setFreshGADTBounds.addMode(Mode .GadtConstraintInference )
375
- // `true` from reducePattern implies there be some instance(s) for `reduceFrom`(?)
376
- if (reducePattern(caseBindingMap, scrutineeSym.termRef, cdef.pat)( using gadtCtx)) {
377
- val (caseBindings, from, to) = substBindings(caseBindingMap.toList, mutable. ListBuffer (), Nil , Nil )
378
- val (guardOK, canReduceGuard ) =
379
- if cdef.guard.isEmpty then ( true , true )
380
- else typer.typed( cdef.guard.subst(from, to), defn. BooleanType ) match {
381
- case ConstantValue ( v : Boolean ) => (v, true )
382
- case _ => (false , false )
383
- }
384
- if guardOK then Some ((caseBindings.map(_.subst(from, to)), cdef.body.subst(from, to), canReduceGuard))
385
- else if canReduceGuard then None
386
- else Some ((caseBindings.map(_.subst(from, to)), cdef.body.subst(from, to), canReduceGuard) )
387
- }
388
- else None
416
+ reducePattern(caseBindingMap,scrutineeSym.termRef,cdef.pat)( using gadtCtx) match
417
+ case Some (msg) => Left (msg)
418
+ case None => {
419
+ val (caseBindings, from, to ) = substBindings(caseBindingMap.toList, mutable. ListBuffer (), Nil , Nil )
420
+ val (guardOK, canReduceGuard) =
421
+ if cdef.guard.isEmpty then ( true , true )
422
+ else typer.typed(cdef.guard.subst(from, to), defn. BooleanType ) match {
423
+ case ConstantValue ( v : Boolean ) => (v, true )
424
+ case _ => ( false , false )
425
+ }
426
+ if guardOK then Right ((caseBindings.map(_.subst(from, to)), cdef.body.subst(from, to), canReduceGuard))
427
+ else if canReduceGuard then Left ( None )
428
+ else Right ((caseBindings.map(_.subst(from, to)), cdef.body.subst(from, to), canReduceGuard))
429
+ }
389
430
}
390
431
391
- def recur (cases : List [CaseDef ]): MatchRedux = cases match {
392
- case Nil => None
432
+ def recur (cases : List [CaseDef ], theLastMsg : Option [ Message ] = None ): MatchRedux = cases match
433
+ case Nil => Left (theLastMsg)
393
434
case cdef :: cases1 =>
394
435
reduceCase(cdef) match
395
- // may fail with 1. ambiguous implicits, 2. missing implicits
396
- // Either[Either[MissingErrorMessage,AmbiguousErrorMessage],T]?
397
- // a preceding case may fail, but one of the remainders cases may succeed.
398
- // So return Left[ErrorMessage] when the last reduce fails?
399
- case None => recur(cases1)
400
- case r @ Some ((caseBindings, rhs, canReduceGuard)) if canReduceGuard => Some ((caseBindings, rhs))
401
- case _ => None
402
- }
436
+ case Left (None ) => recur(cases1, theLastMsg)
437
+ case Left (msg @ Some (_)) => recur(cases1, msg)
438
+ case r @ Right ((caseBindings, rhs, canReduceGuard)) if canReduceGuard => Right ((caseBindings, rhs))
439
+ case _ => Left (theLastMsg)
403
440
404
441
recur(cases)
405
442
}
0 commit comments