1
+ // ignore-tidy-filelength
2
+ // FIXME: we should move the field error reporting code somewhere else.
3
+
1
4
//! Type checking expressions.
2
5
//!
3
6
//! See [`rustc_hir_analysis::check`] for more context on type checking in general.
@@ -62,7 +65,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
62
65
63
66
// While we don't allow *arbitrary* coercions here, we *do* allow
64
67
// coercions from ! to `expected`.
65
- if ty. is_never ( ) {
68
+ if ty. is_never ( ) && self . expr_guaranteed_to_constitute_read_for_never ( expr ) {
66
69
if let Some ( _) = self . typeck_results . borrow ( ) . adjustments ( ) . get ( expr. hir_id ) {
67
70
let reported = self . dcx ( ) . span_delayed_bug (
68
71
expr. span ,
@@ -238,8 +241,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
238
241
_ => self . warn_if_unreachable ( expr. hir_id , expr. span , "expression" ) ,
239
242
}
240
243
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) {
243
249
self . diverges . set ( self . diverges . get ( ) | Diverges :: always ( expr. span ) ) ;
244
250
}
245
251
@@ -257,6 +263,185 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
257
263
ty
258
264
}
259
265
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
+
260
445
#[ instrument( skip( self , expr) , level = "debug" ) ]
261
446
fn check_expr_kind (
262
447
& self ,
0 commit comments