{"id":"d44d133aceb0d958","repo":"rust-lang/rust","slug":"got-a-pointer-where-a-scalarint-was-expected","errorCode":null,"errorMessage":"got a pointer where a ScalarInt was expected","messagePattern":"got a pointer where a ScalarInt was expected","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler/rustc_middle/src/mir/interpret/value.rs","lineNumber":325,"sourceCode":"        }\n    }\n\n    pub fn clear_provenance(&mut self) -> InterpResult<'tcx> {\n        if matches!(self, Scalar::Ptr(..)) {\n            *self = self.to_scalar_int()?.into();\n        }\n        interp_ok(())\n    }\n\n    #[inline(always)]\n    pub fn to_scalar_int(self) -> InterpResult<'tcx, ScalarInt> {\n        self.try_to_scalar_int().map_err(|_| err_unsup!(ReadPointerAsInt(None))).into()\n    }\n\n    #[inline(always)]\n    #[cfg_attr(debug_assertions, track_caller)] // only in debug builds due to perf (see #98980)\n    pub fn assert_scalar_int(self) -> ScalarInt {\n        self.try_to_scalar_int().expect(\"got a pointer where a ScalarInt was expected\")\n    }\n\n    /// This throws UB (instead of ICEing) on a size mismatch since size mismatches can arise in\n    /// Miri when someone declares a function that we shim (such as `malloc`) with a wrong type.\n    #[inline]\n    pub fn to_bits(self, target_size: Size) -> InterpResult<'tcx, u128> {\n        assert_ne!(target_size.bytes(), 0, \"you should never look at the bits of a ZST\");\n        self.to_scalar_int()?\n            .try_to_bits(target_size)\n            .map_err(|size| {\n                err_ub!(ScalarSizeMismatch(ScalarSizeMismatch {\n                    target_size: target_size.bytes(),\n                    data_size: size.bytes(),\n                }))\n            })\n            .into()\n    }\n","sourceCodeStart":307,"sourceCodeEnd":343,"githubUrl":"https://github.com/rust-lang/rust/blob/22057b88b091743bc0fd8d592a9264f0a6951403/compiler/rustc_middle/src/mir/interpret/value.rs#L307-L343","documentation":"`Scalar::assert_scalar_int` (value.rs:325) panics when called on a `Scalar::Ptr(..)` — it is the unchecked variant that assumes the caller has already ensured the scalar holds an integer. The checked sibling `try_to_scalar_int` returns `Ok(ScalarInt)` only for `Scalar::Int` (or, when `Prov::OFFSET_IS_ADDR`, for pointers that can be folded into an address); `assert_scalar_int` simply `.expect`s that result.","triggerScenarios":"Calling `scalar.assert_scalar_int()` on a value that is actually `Scalar::Ptr(ptr, sz)`. Common when a const-eval path assumes a value is an integer (e.g. discriminant, bit read, integer arithmetic) but the operand holds a pointer — most often because provenance stripping was skipped or a pointer flowed into an integer-only MIR op.","commonSituations":"Miri exercises that expose pointer-as-int paths; const-eval of code that reads the bits of a `usize` cast from a reference; bugs in intrinsic shims (`memcmp`, atomic ops) that call `assert_scalar_int` on operands that may carry provenance; debug builds (`#[cfg_attr(debug_assertions, track_caller)]`) surface this earlier than release.","solutions":["Switch to the checked form `scalar.to_scalar_int()?` which yields a proper `InterpError` (`ReadPointerAsInt`) instead of panicking.","If you truly need an integer and can prove the operand is one, strip provenance first via `Scalar::clear_provenance` before asserting.","Use `try_to_scalar_int()` and branch on the `Err(Scalar::Ptr)` to emit a precise diagnostic.","Inspect the `#[track_caller]` location from the panic backtrace to find the asserting caller."],"exampleFix":"// before\nlet int = scalar.assert_scalar_int();\n\n// after\nlet int = scalar.to_scalar_int()?;","handlingStrategy":"type-guard","validationCode":"// assert_scalar_int panics when the value is a pointer. Use the fallible API\n// instead and branch on whether the value holds an integer.\nfn read_as_int<'tcx>(v: Scalar) -> InterpResult<'tcx, ScalarInt> {\n    v.to_scalar_int() // returns Err(ReadPointerAsInt) instead of panicking\n}","typeGuard":"// Narrow a Scalar before assuming it is an integer.\nfn is_scalar_int(v: &Scalar) -> bool {\n    matches!(v.try_to_scalar_int(), Ok(_))\n}\n\nif is_scalar_int(&scalar) {\n    let bits = scalar.assert_scalar_int();\n} else {\n    // it is a pointer (or uninitialized); handle accordingly\n}","tryCatchPattern":"// Only if you must keep assert_scalar_int in hot path: catch the panic.\nlet bits = std::panic::catch_unwind(|| scalar.assert_scalar_int());\nmatch bits {\n    Ok(i) => /* use i */,\n    Err(_) => /* scalar held a pointer; treat as UB or read it as a pointer */,\n}","preventionTips":["Prefer to_scalar_int() / try_to_scalar_int() over assert_scalar_int(); the assert exists for invariant-holding internals, not untrusted values.","Before reading bits via to_bits, confirm the scalar is not a pointer with the type guard above.","Track which Scalars come from pointer-typed allocations and route them through pointer-reading APIs, never integer ones.","In debug builds assert_scalar_int carries #[track_caller]; when you see this panic, the caller location pinpoints the mistaken integer read."],"tags":["rustc","mir","const-eval","interpreter","scalar","provenance","assertion"],"analyzedSha":"22057b88b091743bc0fd8d592a9264f0a6951403","analyzedAt":"2026-08-03T08:09:25.915Z","schemaVersion":2}