@@ -65,7 +65,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
65
65
66
66
// While we don't allow *arbitrary* coercions here, we *do* allow
67
67
// coercions from ! to `expected`.
68
- if ty. is_never ( ) && self . expr_constitutes_read ( expr) {
68
+ if ty. is_never ( ) && self . expr_guaranteed_to_constitute_read_for_never ( expr) {
69
69
if let Some ( _) = self . typeck_results . borrow ( ) . adjustments ( ) . get ( expr. hir_id ) {
70
70
let reported = self . dcx ( ) . span_delayed_bug (
71
71
expr. span ,
@@ -245,7 +245,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
245
245
// unless it's a place expression that isn't being read from, in which case
246
246
// diverging would be unsound since we may never actually read the `!`.
247
247
// e.g. `let _ = *never_ptr;` with `never_ptr: *const !`.
248
- if ty. is_never ( ) && self . expr_constitutes_read ( expr) {
248
+ if ty. is_never ( ) && self . expr_guaranteed_to_constitute_read_for_never ( expr) {
249
249
self . diverges . set ( self . diverges . get ( ) | Diverges :: always ( expr. span ) ) ;
250
250
}
251
251
@@ -274,10 +274,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
274
274
/// expression and the *parent* expression is the scrutinee of a match or
275
275
/// the pointee of an `&` addr-of expression, since both of those parent
276
276
/// expressions take a *place* and not a value.
277
- ///
278
- /// This function is unfortunately a bit heuristical, though it is certainly
279
- /// far sounder than the prior status quo: <https://github.com/rust-lang/rust/issues/117288>.
280
- pub ( super ) fn expr_constitutes_read ( & self , expr : & ' tcx hir :: Expr < ' tcx > ) -> bool {
277
+ pub ( super ) fn expr_guaranteed_to_constitute_read_for_never (
278
+ & self ,
279
+ expr : & ' tcx hir :: Expr < ' tcx > ,
280
+ ) -> bool {
281
281
// We only care about place exprs. Anything else returns an immediate
282
282
// which would constitute a read. We don't care about distinguishing
283
283
// "syntactic" place exprs since if the base of a field projection is
@@ -300,15 +300,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
300
300
expr. hir_id != lhs. hir_id
301
301
}
302
302
303
- // If we have a subpattern that performs a read, we want to consider this
304
- // to diverge for compatibility to support something like `let x: () = *never_ptr;`.
303
+ // See note on `PatKind::Or` below for why this is `all`.
305
304
ExprKind :: Match ( scrutinee, arms, _) => {
306
305
assert_eq ! ( scrutinee. hir_id, expr. hir_id) ;
307
- arms. iter ( ) . any ( |arm| self . pat_constitutes_read ( arm. pat ) )
306
+ arms. iter ( )
307
+ . all ( |arm| self . pat_guaranteed_to_constitute_read_for_never ( arm. pat ) )
308
308
}
309
309
ExprKind :: Let ( hir:: LetExpr { init, pat, .. } ) => {
310
310
assert_eq ! ( init. hir_id, expr. hir_id) ;
311
- self . pat_constitutes_read ( * pat)
311
+ self . pat_guaranteed_to_constitute_read_for_never ( * pat)
312
312
}
313
313
314
314
// Any expression child of these expressions constitute reads.
@@ -349,7 +349,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
349
349
// to diverge for compatibility to support something like `let x: () = *never_ptr;`.
350
350
hir:: Node :: LetStmt ( hir:: LetStmt { init : Some ( target) , pat, .. } ) => {
351
351
assert_eq ! ( target. hir_id, expr. hir_id) ;
352
- self . pat_constitutes_read ( * pat)
352
+ self . pat_guaranteed_to_constitute_read_for_never ( * pat)
353
353
}
354
354
355
355
// These nodes (if they have a sub-expr) do constitute a read.
@@ -400,36 +400,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
400
400
}
401
401
402
402
/// Whether this pattern constitutes a read of value of the scrutinee that
403
- /// it is matching against.
403
+ /// it is matching against. This is used to determine whether we should
404
+ /// perform `NeverToAny` coercions
404
405
///
405
406
/// See above for the nuances of what happens when this returns true.
406
- pub ( super ) fn pat_constitutes_read ( & self , pat : & hir:: Pat < ' _ > ) -> bool {
407
- let mut does_read = false ;
408
- pat. walk ( |pat| {
409
- match pat. kind {
410
- hir:: PatKind :: Wild | hir:: PatKind :: Never | hir:: PatKind :: Or ( _) => {
411
- // Recurse
412
- true
413
- }
414
- hir:: PatKind :: Binding ( _, _, _, _)
415
- | hir:: PatKind :: Struct ( _, _, _)
416
- | hir:: PatKind :: TupleStruct ( _, _, _)
417
- | hir:: PatKind :: Path ( _)
418
- | hir:: PatKind :: Tuple ( _, _)
419
- | hir:: PatKind :: Box ( _)
420
- | hir:: PatKind :: Ref ( _, _)
421
- | hir:: PatKind :: Deref ( _)
422
- | hir:: PatKind :: Lit ( _)
423
- | hir:: PatKind :: Range ( _, _, _)
424
- | hir:: PatKind :: Slice ( _, _, _)
425
- | hir:: PatKind :: Err ( _) => {
426
- does_read = true ;
427
- // No need to continue.
428
- false
429
- }
430
- }
431
- } ) ;
432
- does_read
407
+ pub ( super ) fn pat_guaranteed_to_constitute_read_for_never ( & self , pat : & hir:: Pat < ' _ > ) -> bool {
408
+ match pat. kind {
409
+ // Does not constitute a read.
410
+ hir:: PatKind :: Wild => false ,
411
+
412
+ // This is unnecessarily restrictive when the pattern that doesn't
413
+ // constitute a read is unreachable.
414
+ //
415
+ // For example `match *never_ptr { value => {}, _ => {} }` or
416
+ // `match *never_ptr { _ if false => {}, value => {} }`.
417
+ //
418
+ // It is however fine to be restrictive here; only returning `true`
419
+ // can lead to unsoundness.
420
+ hir:: PatKind :: Or ( subpats) => {
421
+ subpats. iter ( ) . all ( |pat| self . pat_guaranteed_to_constitute_read_for_never ( pat) )
422
+ }
423
+
424
+ // Does constitute a read, since it is equivalent to a discriminant read.
425
+ hir:: PatKind :: Never => true ,
426
+
427
+ // All of these constitute a read, or match on something that isn't `!`,
428
+ // which would require a `NeverToAny` coercion.
429
+ hir:: PatKind :: Binding ( _, _, _, _)
430
+ | hir:: PatKind :: Struct ( _, _, _)
431
+ | hir:: PatKind :: TupleStruct ( _, _, _)
432
+ | hir:: PatKind :: Path ( _)
433
+ | hir:: PatKind :: Tuple ( _, _)
434
+ | hir:: PatKind :: Box ( _)
435
+ | hir:: PatKind :: Ref ( _, _)
436
+ | hir:: PatKind :: Deref ( _)
437
+ | hir:: PatKind :: Lit ( _)
438
+ | hir:: PatKind :: Range ( _, _, _)
439
+ | hir:: PatKind :: Slice ( _, _, _)
440
+ | hir:: PatKind :: Err ( _) => true ,
441
+ }
433
442
}
434
443
435
444
#[ instrument( skip( self , expr) , level = "debug" ) ]
0 commit comments