@@ -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.
@@ -401,36 +401,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
401401 }
402402
403403 /// Whether this pattern constitutes a read of value of the scrutinee that
404- /// it is matching against.
404+ /// it is matching against. This is used to determine whether we should
405+ /// perform `NeverToAny` coercions.
405406 ///
406407 /// See above for the nuances of what happens when this returns true.
407- pub ( super ) fn pat_constitutes_read ( & self , pat : & hir:: Pat < ' _ > ) -> bool {
408- let mut does_read = false ;
409- pat. walk ( |pat| {
410- match pat. kind {
411- hir:: PatKind :: Wild | hir:: PatKind :: Never | hir:: PatKind :: Or ( _) => {
412- // Recurse
413- true
414- }
415- hir:: PatKind :: Binding ( _, _, _, _)
416- | hir:: PatKind :: Struct ( _, _, _)
417- | hir:: PatKind :: TupleStruct ( _, _, _)
418- | hir:: PatKind :: Path ( _)
419- | hir:: PatKind :: Tuple ( _, _)
420- | hir:: PatKind :: Box ( _)
421- | hir:: PatKind :: Ref ( _, _)
422- | hir:: PatKind :: Deref ( _)
423- | hir:: PatKind :: Lit ( _)
424- | hir:: PatKind :: Range ( _, _, _)
425- | hir:: PatKind :: Slice ( _, _, _)
426- | hir:: PatKind :: Err ( _) => {
427- does_read = true ;
428- // No need to continue.
429- false
430- }
431- }
432- } ) ;
433- does_read
408+ pub ( super ) fn pat_guaranteed_to_constitute_read_for_never ( & self , pat : & hir:: Pat < ' _ > ) -> bool {
409+ match pat. kind {
410+ // Does not constitute a read.
411+ hir:: PatKind :: Wild => false ,
412+
413+ // This is unnecessarily restrictive when the pattern that doesn't
414+ // constitute a read is unreachable.
415+ //
416+ // For example `match *never_ptr { value => {}, _ => {} }` or
417+ // `match *never_ptr { _ if false => {}, value => {} }`.
418+ //
419+ // It is however fine to be restrictive here; only returning `true`
420+ // can lead to unsoundness.
421+ hir:: PatKind :: Or ( subpats) => {
422+ subpats. iter ( ) . all ( |pat| self . pat_guaranteed_to_constitute_read_for_never ( pat) )
423+ }
424+
425+ // Does constitute a read, since it is equivalent to a discriminant read.
426+ hir:: PatKind :: Never => true ,
427+
428+ // All of these constitute a read, or match on something that isn't `!`,
429+ // which would require a `NeverToAny` coercion.
430+ hir:: PatKind :: Binding ( _, _, _, _)
431+ | hir:: PatKind :: Struct ( _, _, _)
432+ | hir:: PatKind :: TupleStruct ( _, _, _)
433+ | hir:: PatKind :: Path ( _)
434+ | hir:: PatKind :: Tuple ( _, _)
435+ | hir:: PatKind :: Box ( _)
436+ | hir:: PatKind :: Ref ( _, _)
437+ | hir:: PatKind :: Deref ( _)
438+ | hir:: PatKind :: Lit ( _)
439+ | hir:: PatKind :: Range ( _, _, _)
440+ | hir:: PatKind :: Slice ( _, _, _)
441+ | hir:: PatKind :: Err ( _) => true ,
442+ }
434443 }
435444
436445 #[ instrument( skip( self , expr) , level = "debug" ) ]
0 commit comments