|
2 | 2 | //!
|
3 | 3 | //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/mir/index.html
|
4 | 4 |
|
| 5 | +use crate::error::{DynUnsizedLocal, DynUnsizedParam}; |
5 | 6 | use crate::mir::interpret::{AllocRange, ConstAllocation, ErrorHandled, Scalar};
|
6 | 7 | use crate::mir::visit::MirVisitable;
|
7 | 8 | use crate::ty::codec::{TyDecoder, TyEncoder};
|
@@ -586,14 +587,70 @@ impl<'tcx> Body<'tcx> {
|
586 | 587 | tcx: TyCtxt<'tcx>,
|
587 | 588 | param_env: ty::ParamEnv<'tcx>,
|
588 | 589 | normalize_const: impl Fn(Const<'tcx>) -> Result<Const<'tcx>, ErrorHandled>,
|
| 590 | + normalize_ty: impl Fn(Ty<'tcx>) -> Result<Ty<'tcx>, ErrorHandled>, |
589 | 591 | ) -> Result<(), ErrorHandled> {
|
590 |
| - // For now, the only thing we have to check is is to ensure that all the constants used in |
| 592 | + // The main thing we have to check is is to ensure that all the constants used in |
591 | 593 | // the body successfully evaluate.
|
592 | 594 | for &const_ in &self.required_consts {
|
593 | 595 | let c = normalize_const(const_.const_)?;
|
594 | 596 | c.eval(tcx, param_env, Some(const_.span))?;
|
595 | 597 | }
|
596 | 598 |
|
| 599 | + // The second thing we do is guard against unsized locals/arguments that do not have a dynamically computable size. |
| 600 | + // Due to a lack of an appropriate trait, we currently have to hack this in |
| 601 | + // as a post-mono check. |
| 602 | + let mut guaranteed = None; // `Some` if any error was emitted |
| 603 | + // First check all locals (including the arguments). |
| 604 | + for (idx, local) in self.local_decls.iter_enumerated() { |
| 605 | + let ty = local.ty; |
| 606 | + // We get invoked in generic code, so we cannot force normalization. |
| 607 | + let tail = |
| 608 | + tcx.struct_tail_with_normalize(ty, |ty| normalize_ty(ty).unwrap_or(ty), || {}); |
| 609 | + // If the unsized tail is an `extern type`, emit error. |
| 610 | + if matches!(tail.kind(), ty::Foreign(_)) { |
| 611 | + assert!(idx.as_u32() > 0); // cannot be the return local |
| 612 | + let is_arg = (1..=self.arg_count).contains(&idx.as_usize()); |
| 613 | + guaranteed = Some(tcx.sess.emit_err(DynUnsizedLocal { |
| 614 | + span: local.source_info.span, |
| 615 | + ty, |
| 616 | + is_arg, |
| 617 | + })); |
| 618 | + } |
| 619 | + } |
| 620 | + // The above check covers the callee side. We also need to check the caller. |
| 621 | + // For that we check all function calls. |
| 622 | + for bb in self.basic_blocks.iter() { |
| 623 | + let terminator = bb.terminator(); |
| 624 | + if let TerminatorKind::Call { args, .. } = &terminator.kind { |
| 625 | + for arg in args { |
| 626 | + if let Operand::Move(place) = arg { |
| 627 | + if place.is_indirect_first_projection() { |
| 628 | + // Found an argument of the shape `Move(*arg)`. Make sure |
| 629 | + // its size can be determined dynamically. |
| 630 | + let ty = place.ty(&self.local_decls, tcx).ty; |
| 631 | + let tail = tcx.struct_tail_with_normalize( |
| 632 | + ty, |
| 633 | + |ty| normalize_ty(ty).unwrap_or(ty), |
| 634 | + || {}, |
| 635 | + ); |
| 636 | + // If the unsized tail is an `extern type`, emit error. |
| 637 | + if matches!(tail.kind(), ty::Foreign(_)) { |
| 638 | + guaranteed = Some(tcx.sess.emit_err(DynUnsizedParam { |
| 639 | + span: terminator.source_info.span, |
| 640 | + ty, |
| 641 | + })); |
| 642 | + } |
| 643 | + } |
| 644 | + } |
| 645 | + } |
| 646 | + } |
| 647 | + } |
| 648 | + // Above we made sure to report *all* errors by continuing even after reporting something. |
| 649 | + // Here we make sure we return `Err` if there was any error. |
| 650 | + if let Some(guaranteed) = guaranteed { |
| 651 | + return Err(ErrorHandled::Reported(guaranteed.into(), DUMMY_SP)); |
| 652 | + } |
| 653 | + |
597 | 654 | Ok(())
|
598 | 655 | }
|
599 | 656 | }
|
|
0 commit comments