{"id":"1cef56a8327b8a90","repo":"rust-lang/rust","slug":"an-interpreter-error-got-improperly-discarded-use","errorCode":null,"errorMessage":"an interpreter error got improperly discarded; use `discard_err()` if this is intentional","messagePattern":"an interpreter error got improperly discarded; use `discard_err\\(\\)` if this is intentional","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler/rustc_middle/src/mir/interpret/error.rs","lineNumber":933,"sourceCode":"#[macro_export]\nmacro_rules! throw_exhaust {\n    ($($tt:tt)*) => { do yeet $crate::err_exhaust!($($tt)*) };\n}\n\n#[macro_export]\nmacro_rules! throw_machine_stop {\n    ($($tt:tt)*) => { do yeet $crate::err_machine_stop!($($tt)*) };\n}\n\n/// Guard type that panics on drop.\n#[derive(Debug)]\nstruct Guard;\n\nimpl Drop for Guard {\n    fn drop(&mut self) {\n        // We silence the guard if we are already panicking, to avoid double-panics.\n        if !std::thread::panicking() {\n            panic!(\n                \"an interpreter error got improperly discarded; use `discard_err()` if this is intentional\"\n            );\n        }\n    }\n}\n\n/// The result type used by the interpreter. This is a newtype around `Result`\n/// to block access to operations like `ok()` that discard UB errors.\n///\n/// We also make things panic if this type is ever implicitly dropped.\n#[derive(Debug)]\n#[must_use]\npub struct InterpResult<'tcx, T = ()> {\n    res: Result<T, InterpErrorInfo<'tcx>>,\n    guard: Guard,\n}\n\nimpl<'tcx, T> ops::Try for InterpResult<'tcx, T> {","sourceCodeStart":915,"sourceCodeEnd":951,"githubUrl":"https://github.com/rust-lang/rust/blob/22057b88b091743bc0fd8d592a9264f0a6951403/compiler/rustc_middle/src/mir/interpret/error.rs#L915-L951","documentation":"`InterpResult<'tcx, T>` is a newtype around `Result` plus a `Guard` whose `Drop` impl panics with this message if the `InterpResult` is dropped without being explicitly consumed. The design forces every interpreter call site to either propagate, match, or deliberately discard its result — silently dropping a UB/invalidity error would hide miscompilation. The panic is suppressed automatically when the thread is already panicking to avoid double-panic aborts.","triggerScenarios":"Letting an `InterpResult` value fall out of scope without `?`, `match`, `.into()`, or `.discard_err()`. Concrete shapes: `ecx.read_scalar(...);` (statement-discarded), `let _ = ecx.mplace_access(...);`, returning from a function that forgets to propagate an interp result, or storing an `InterpResult` in a field that is later dropped instead of matched.","commonSituations":"Refactoring an interpreter intrinsic/shim and forgetting a `?`; writing a new Miri foreign-item hook whose body drops the result of an inner call; cargo build of rustc itself after a `InterpErrorInfo` refactor; new contributors to const-eval who treat `InterpResult` like a plain `Result` and call `.ok()`.","solutions":["Propagate the result with `?` (the idiomatic fix): add `?` to the offending call.","If the error is genuinely to be ignored, call `.discard_err()` explicitly to document intent and defuse the guard.","Match on the result if you need branch-specific handling: `match res { Ok(_) => ..., Err(e) => ... }`.","Use the `#[track_caller]`/backtrace from the panic to jump to the exact dropped value."],"exampleFix":"// before\necx.write_scalar(val, dest); // InterpResult dropped → panic on drop\n\n// after\necx.write_scalar(val, dest)?;\n// or, if intentionally discarding:\necx.write_scalar(val, dest).discard_err();","handlingStrategy":"validation","validationCode":"// InterpResult panics on drop if it still holds an InterpError -- the Guard\n// fires. Always consume the result: propagate with `?`, or explicitly drop\n// the error with `.discard_err()` when you really intend to ignore it.\nfn run_block<'tcx, T>(\n    interp: &mut InterpCx<'tcx>,\n) -> InterpResult<'tcx, T> {\n    let res: InterpResult<'tcx, T> = interp.do_step();\n    // NEVER let an InterpResult value go out of scope unhandled.\n    res\n}\n\n// If you genuinely want to swallow a benign error:\nlet _ = interp.do_step().discard_err(); // explicit, auditable","typeGuard":null,"tryCatchPattern":"// Prefer fixing the call site over catching. catch_unwind works but hides UB.\nlet outcome = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {\n    let r: InterpResult<'tcx, ()> = interp.do_step();\n    r.discard_err(); // make the drop well-defined if you truly intend to ignore\n}));","preventionTips":["Never store an InterpResult in a field or local and forget to consume it; #[must_use] does not catch all drop paths.","Make `?` the default way to propagate; any place that binds InterpResult without `?` deserves a second look.","If an error must be ignored, call .discard_err() at the exact site with a comment explaining why -- the Guard exists precisely to forbid silent drops.","Deny `let _ = interp_result` patterns in review; require either `?`, match, or an explicit discard_err()."],"tags":["rustc","mir","const-eval","interpreter","error-handling","must-use"],"analyzedSha":"22057b88b091743bc0fd8d592a9264f0a6951403","analyzedAt":"2026-08-03T08:09:25.915Z","schemaVersion":2}