1+ // ignore-tidy-filelength
2+ // FIXME: we should move the field error reporting code somewhere else.
3+
14//! Type checking expressions.
25//!
36//! See [`rustc_hir_analysis::check`] for more context on type checking in general.
@@ -62,7 +65,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
6265
6366 // While we don't allow *arbitrary* coercions here, we *do* allow
6467 // coercions from ! to `expected`.
65- if ty. is_never ( ) {
68+ if ty. is_never ( ) && self . expr_guaranteed_to_constitute_read_for_never ( expr ) {
6669 if let Some ( _) = self . typeck_results . borrow ( ) . adjustments ( ) . get ( expr. hir_id ) {
6770 let reported = self . dcx ( ) . span_delayed_bug (
6871 expr. span ,
@@ -238,8 +241,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
238241 _ => self . warn_if_unreachable ( expr. hir_id , expr. span , "expression" ) ,
239242 }
240243
241- // Any expression that produces a value of type `!` must have diverged
242- if ty. is_never ( ) {
244+ // Any expression that produces a value of type `!` must have diverged,
245+ // unless it's a place expression that isn't being read from, in which case
246+ // diverging would be unsound since we may never actually read the `!`.
247+ // e.g. `let _ = *never_ptr;` with `never_ptr: *const !`.
248+ if ty. is_never ( ) && self . expr_guaranteed_to_constitute_read_for_never ( expr) {
243249 self . diverges . set ( self . diverges . get ( ) | Diverges :: always ( expr. span ) ) ;
244250 }
245251
@@ -257,6 +263,185 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
257263 ty
258264 }
259265
266+ /// Whether this expression constitutes a read of value of the type that
267+ /// it evaluates to.
268+ ///
269+ /// This is used to determine if we should consider the block to diverge
270+ /// if the expression evaluates to `!`, and if we should insert a `NeverToAny`
271+ /// coercion for values of type `!`.
272+ ///
273+ /// This function generally returns `false` if the expression is a place
274+ /// expression and the *parent* expression is the scrutinee of a match or
275+ /// the pointee of an `&` addr-of expression, since both of those parent
276+ /// expressions take a *place* and not a value.
277+ pub ( super ) fn expr_guaranteed_to_constitute_read_for_never (
278+ & self ,
279+ expr : & ' tcx hir:: Expr < ' tcx > ,
280+ ) -> bool {
281+ // We only care about place exprs. Anything else returns an immediate
282+ // which would constitute a read. We don't care about distinguishing
283+ // "syntactic" place exprs since if the base of a field projection is
284+ // not a place then it would've been UB to read from it anyways since
285+ // that constitutes a read.
286+ if !expr. is_syntactic_place_expr ( ) {
287+ return true ;
288+ }
289+
290+ let parent_node = self . tcx . parent_hir_node ( expr. hir_id ) ;
291+ match parent_node {
292+ hir:: Node :: Expr ( parent_expr) => {
293+ match parent_expr. kind {
294+ // Addr-of, field projections, and LHS of assignment don't constitute reads.
295+ // Assignment does call `drop_in_place`, though, but its safety
296+ // requirements are not the same.
297+ ExprKind :: AddrOf ( ..) | hir:: ExprKind :: Field ( ..) => false ,
298+ ExprKind :: Assign ( lhs, _, _) => {
299+ // Only the LHS does not constitute a read
300+ expr. hir_id != lhs. hir_id
301+ }
302+
303+ // See note on `PatKind::Or` below for why this is `all`.
304+ ExprKind :: Match ( scrutinee, arms, _) => {
305+ assert_eq ! ( scrutinee. hir_id, expr. hir_id) ;
306+ arms. iter ( )
307+ . all ( |arm| self . pat_guaranteed_to_constitute_read_for_never ( arm. pat ) )
308+ }
309+ ExprKind :: Let ( hir:: LetExpr { init, pat, .. } ) => {
310+ assert_eq ! ( init. hir_id, expr. hir_id) ;
311+ self . pat_guaranteed_to_constitute_read_for_never ( * pat)
312+ }
313+
314+ // Any expression child of these expressions constitute reads.
315+ ExprKind :: Array ( _)
316+ | ExprKind :: Call ( _, _)
317+ | ExprKind :: MethodCall ( _, _, _, _)
318+ | ExprKind :: Tup ( _)
319+ | ExprKind :: Binary ( _, _, _)
320+ | ExprKind :: Unary ( _, _)
321+ | ExprKind :: Cast ( _, _)
322+ | ExprKind :: Type ( _, _)
323+ | ExprKind :: DropTemps ( _)
324+ | ExprKind :: If ( _, _, _)
325+ | ExprKind :: Closure ( _)
326+ | ExprKind :: Block ( _, _)
327+ | ExprKind :: AssignOp ( _, _, _)
328+ | ExprKind :: Index ( _, _, _)
329+ | ExprKind :: Break ( _, _)
330+ | ExprKind :: Ret ( _)
331+ | ExprKind :: Become ( _)
332+ | ExprKind :: InlineAsm ( _)
333+ | ExprKind :: Struct ( _, _, _)
334+ | ExprKind :: Repeat ( _, _)
335+ | ExprKind :: Yield ( _, _) => true ,
336+
337+ // These expressions have no (direct) sub-exprs.
338+ ExprKind :: ConstBlock ( _)
339+ | ExprKind :: Loop ( _, _, _, _)
340+ | ExprKind :: Lit ( _)
341+ | ExprKind :: Path ( _)
342+ | ExprKind :: Continue ( _)
343+ | ExprKind :: OffsetOf ( _, _)
344+ | ExprKind :: Err ( _) => unreachable ! ( "no sub-expr expected for {:?}" , expr. kind) ,
345+ }
346+ }
347+
348+ // If we have a subpattern that performs a read, we want to consider this
349+ // to diverge for compatibility to support something like `let x: () = *never_ptr;`.
350+ hir:: Node :: LetStmt ( hir:: LetStmt { init : Some ( target) , pat, .. } ) => {
351+ assert_eq ! ( target. hir_id, expr. hir_id) ;
352+ self . pat_guaranteed_to_constitute_read_for_never ( * pat)
353+ }
354+
355+ // These nodes (if they have a sub-expr) do constitute a read.
356+ hir:: Node :: Block ( _)
357+ | hir:: Node :: Arm ( _)
358+ | hir:: Node :: ExprField ( _)
359+ | hir:: Node :: AnonConst ( _)
360+ | hir:: Node :: ConstBlock ( _)
361+ | hir:: Node :: ConstArg ( _)
362+ | hir:: Node :: Stmt ( _)
363+ | hir:: Node :: Item ( hir:: Item {
364+ kind : hir:: ItemKind :: Const ( ..) | hir:: ItemKind :: Static ( ..) ,
365+ ..
366+ } )
367+ | hir:: Node :: TraitItem ( hir:: TraitItem {
368+ kind : hir:: TraitItemKind :: Const ( ..) , ..
369+ } )
370+ | hir:: Node :: ImplItem ( hir:: ImplItem { kind : hir:: ImplItemKind :: Const ( ..) , .. } ) => true ,
371+
372+ // These nodes do not have direct sub-exprs.
373+ hir:: Node :: Param ( _)
374+ | hir:: Node :: Item ( _)
375+ | hir:: Node :: ForeignItem ( _)
376+ | hir:: Node :: TraitItem ( _)
377+ | hir:: Node :: ImplItem ( _)
378+ | hir:: Node :: Variant ( _)
379+ | hir:: Node :: Field ( _)
380+ | hir:: Node :: PathSegment ( _)
381+ | hir:: Node :: Ty ( _)
382+ | hir:: Node :: AssocItemConstraint ( _)
383+ | hir:: Node :: TraitRef ( _)
384+ | hir:: Node :: Pat ( _)
385+ | hir:: Node :: PatField ( _)
386+ | hir:: Node :: LetStmt ( _)
387+ | hir:: Node :: Synthetic
388+ | hir:: Node :: Err ( _)
389+ | hir:: Node :: Ctor ( _)
390+ | hir:: Node :: Lifetime ( _)
391+ | hir:: Node :: GenericParam ( _)
392+ | hir:: Node :: Crate ( _)
393+ | hir:: Node :: Infer ( _)
394+ | hir:: Node :: WhereBoundPredicate ( _)
395+ | hir:: Node :: ArrayLenInfer ( _)
396+ | hir:: Node :: PreciseCapturingNonLifetimeArg ( _)
397+ | hir:: Node :: OpaqueTy ( _) => {
398+ unreachable ! ( "no sub-expr expected for {parent_node:?}" )
399+ }
400+ }
401+ }
402+
403+ /// Whether this pattern constitutes a read of value of the scrutinee that
404+ /// it is matching against. This is used to determine whether we should
405+ /// perform `NeverToAny` coercions.
406+ ///
407+ /// See above for the nuances of what happens when this returns true.
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+ }
443+ }
444+
260445 #[ instrument( skip( self , expr) , level = "debug" ) ]
261446 fn check_expr_kind (
262447 & self ,
0 commit comments