|
| 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.
|
@@ -284,54 +287,149 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
284 | 287 | return true;
|
285 | 288 | }
|
286 | 289 |
|
287 |
| - // If this expression has any adjustments applied after the place expression, |
288 |
| - // they may constitute reads. |
289 |
| - if !self.typeck_results.borrow().expr_adjustments(expr).is_empty() { |
290 |
| - return true; |
291 |
| - } |
| 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 | + } |
292 | 302 |
|
293 |
| - fn pat_does_read(pat: &hir::Pat<'_>) -> bool { |
294 |
| - let mut does_read = false; |
295 |
| - pat.walk(|pat| { |
296 |
| - if matches!( |
297 |
| - pat.kind, |
298 |
| - hir::PatKind::Wild | hir::PatKind::Never | hir::PatKind::Or(_) |
299 |
| - ) { |
300 |
| - true |
301 |
| - } else { |
302 |
| - does_read = true; |
303 |
| - // No need to continue. |
304 |
| - false |
305 |
| - } |
306 |
| - }); |
307 |
| - does_read |
308 |
| - } |
| 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;`. |
| 305 | + ExprKind::Match(scrutinee, arms, _) => { |
| 306 | + assert_eq!(scrutinee.hir_id, expr.hir_id); |
| 307 | + arms.iter().any(|arm| self.pat_constitutes_read(arm.pat)) |
| 308 | + } |
| 309 | + ExprKind::Let(hir::LetExpr { init, pat, .. }) => { |
| 310 | + assert_eq!(init.hir_id, expr.hir_id); |
| 311 | + self.pat_constitutes_read(*pat) |
| 312 | + } |
309 | 313 |
|
310 |
| - match self.tcx.parent_hir_node(expr.hir_id) { |
311 |
| - // Addr-of, field projections, and LHS of assignment don't constitute reads. |
312 |
| - // Assignment does call `drop_in_place`, though, but its safety |
313 |
| - // requirements are not the same. |
314 |
| - hir::Node::Expr(hir::Expr { kind: hir::ExprKind::AddrOf(..), .. }) => false, |
315 |
| - hir::Node::Expr(hir::Expr { |
316 |
| - kind: hir::ExprKind::Assign(target, _, _) | hir::ExprKind::Field(target, _), |
317 |
| - .. |
318 |
| - }) if expr.hir_id == target.hir_id => false, |
| 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 | + } |
319 | 347 |
|
320 | 348 | // If we have a subpattern that performs a read, we want to consider this
|
321 | 349 | // to diverge for compatibility to support something like `let x: () = *never_ptr;`.
|
322 |
| - hir::Node::LetStmt(hir::LetStmt { init: Some(target), pat, .. }) |
323 |
| - | hir::Node::Expr(hir::Expr { |
324 |
| - kind: hir::ExprKind::Let(hir::LetExpr { init: target, pat, .. }), |
| 350 | + hir::Node::LetStmt(hir::LetStmt { init: Some(target), pat, .. }) => { |
| 351 | + assert_eq!(target.hir_id, expr.hir_id); |
| 352 | + self.pat_constitutes_read(*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(..), |
325 | 365 | ..
|
326 |
| - }) if expr.hir_id == target.hir_id && !pat_does_read(*pat) => false, |
327 |
| - hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Match(target, arms, _), .. }) |
328 |
| - if expr.hir_id == target.hir_id |
329 |
| - && !arms.iter().any(|arm| pat_does_read(arm.pat)) => |
330 |
| - { |
331 |
| - false |
| 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 | + unreachable!("no sub-expr expected for {parent_node:?}") |
| 398 | + } |
| 399 | + } |
| 400 | + } |
| 401 | + |
| 402 | + /// Whether this pattern constitutes a read of value of the scrutinee that |
| 403 | + /// it is matching against. |
| 404 | + /// |
| 405 | + /// 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 | + } |
332 | 430 | }
|
333 |
| - _ => true, |
334 |
| - } |
| 431 | + }); |
| 432 | + does_read |
335 | 433 | }
|
336 | 434 |
|
337 | 435 | #[instrument(skip(self, expr), level = "debug")]
|
|
0 commit comments