@@ -65,7 +65,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
6565
6666 // While we don't allow *arbitrary* coercions here, we *do* allow
6767 // 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) {
6969 if let Some ( _) = self . typeck_results . borrow ( ) . adjustments ( ) . get ( expr. hir_id ) {
7070 let reported = self . dcx ( ) . span_delayed_bug (
7171 expr. span ,
@@ -245,7 +245,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
245245 // unless it's a place expression that isn't being read from, in which case
246246 // diverging would be unsound since we may never actually read the `!`.
247247 // 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) {
249249 self . diverges . set ( self . diverges . get ( ) | Diverges :: always ( expr. span ) ) ;
250250 }
251251
@@ -274,10 +274,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
274274 /// expression and the *parent* expression is the scrutinee of a match or
275275 /// the pointee of an `&` addr-of expression, since both of those parent
276276 /// 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 {
281281 // We only care about place exprs. Anything else returns an immediate
282282 // which would constitute a read. We don't care about distinguishing
283283 // "syntactic" place exprs since if the base of a field projection is
@@ -300,15 +300,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
300300 expr. hir_id != lhs. hir_id
301301 }
302302
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`.
305304 ExprKind :: Match ( scrutinee, arms, _) => {
306305 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 ) )
308308 }
309309 ExprKind :: Let ( hir:: LetExpr { init, pat, .. } ) => {
310310 assert_eq ! ( init. hir_id, expr. hir_id) ;
311- self . pat_constitutes_read ( * pat)
311+ self . pat_guaranteed_to_constitute_read_for_never ( * pat)
312312 }
313313
314314 // Any expression child of these expressions constitute reads.
@@ -349,7 +349,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
349349 // to diverge for compatibility to support something like `let x: () = *never_ptr;`.
350350 hir:: Node :: LetStmt ( hir:: LetStmt { init : Some ( target) , pat, .. } ) => {
351351 assert_eq ! ( target. hir_id, expr. hir_id) ;
352- self . pat_constitutes_read ( * pat)
352+ self . pat_guaranteed_to_constitute_read_for_never ( * pat)
353353 }
354354
355355 // These nodes (if they have a sub-expr) do constitute a read.
@@ -400,36 +400,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
400400 }
401401
402402 /// 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
404405 ///
405406 /// 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+ }
433442 }
434443
435444 #[ instrument( skip( self , expr) , level = "debug" ) ]
0 commit comments