{"id":"416d296197067e51","repo":"rust-lang/rust","slug":"first-index-in-inbounds-gep","errorCode":null,"errorMessage":"first index in inbounds_gep","messagePattern":"first index in inbounds_gep","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler/rustc_codegen_gcc/src/builder.rs","lineNumber":1242,"sourceCode":"            #[cfg(not(feature = \"master\"))]\n            let pointee_size =\n                self.context.new_rvalue_from_int(index.get_type(), pointee_type.get_size() as i32);\n            result = result + self.gcc_int_cast(*index * pointee_size, self.sizet_type);\n        }\n        self.context.new_bitcast(self.location, result, ptr_type)\n    }\n\n    fn inbounds_gep(\n        &mut self,\n        typ: Type<'gcc>,\n        ptr: RValue<'gcc>,\n        indices: &[RValue<'gcc>],\n    ) -> RValue<'gcc> {\n        // NOTE: due to opaque pointers now being used, we need to cast here.\n        let ptr = self.context.new_cast(self.location, ptr, typ.make_pointer());\n        // NOTE: array indexing is always considered in bounds in GCC (FIXME(antoyo): to be verified).\n        let mut indices = indices.iter();\n        let index = indices.next().expect(\"first index in inbounds_gep\");\n        let mut result = self.context.new_array_access(self.location, ptr, *index);\n        for index in indices {\n            result = self.context.new_array_access(self.location, result, *index);\n        }\n        result.get_address(self.location)\n    }\n\n    /* Casts */\n    fn trunc(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {\n        // FIXME(antoyo): check that it indeed truncate the value.\n        self.gcc_int_cast(value, dest_ty)\n    }\n\n    fn sext(&mut self, value: RValue<'gcc>, dest_ty: Type<'gcc>) -> RValue<'gcc> {\n        // FIXME(antoyo): check that it indeed sign extend the value.\n        if dest_ty.dyncast_vector().is_some() {\n            // FIXME(antoyo): nothing to do as it is only for LLVM?\n            return value;","sourceCodeStart":1224,"sourceCodeEnd":1260,"githubUrl":"https://github.com/rust-lang/rust/blob/22057b88b091743bc0fd8d592a9264f0a6951403/compiler/rustc_codegen_gcc/src/builder.rs#L1224-L1260","documentation":"Thrown by the `inbounds_gep` implementation of rustc's `BuilderMethods` trait in rustc_codegen_gcc. After casting the pointer for opaque-pointer support, it does `indices.next().expect(\"first index in inbounds_gep\")`, so it panics if the `indices` slice passed by rustc's codegen is empty. A get-element-pointer (GEP) operation always needs at least one index to compute an offset from a base pointer, so an empty slice is a contract violation between rustc and the GCC backend.","triggerScenarios":"rustc's MIR-to-backend lowering emits an `inbounds_gep` instruction with an empty `indices` array (e.g. a zero-offset pointer projection or a codegen path that folded all indices away but still emitted the GEP call). The panic fires on the very first line of `inbounds_gep` before any array access is built.","commonSituations":"Almost always a symptom of cg_gcc lagging behind the exact rustc toolchain it was built against (the backend tracks specific rustc commits). Also seen with zero-sized type access, `repr(packed)` struct field projection, or after upstream rustc changes to how GEP indices are generated on nightly.","solutions":["Confirm the panic reproduces only under `-Zcodegen-backend=gcc` and not the default LLVM backend; if LLVM-only, it is a rustc_codegen_gcc bug","Rebuild rustc_codegen_gcc against the exact rustc commit/toolchain version it ships with (mismatched versions are the dominant cause)","Isolate the offending construct in a minimal crate and file an issue on rustc_codegen_gcc with the MIR/IR","As a local workaround, patch `inbounds_gep` to return the cast pointer unchanged when `indices` is empty"],"exampleFix":"// before\nlet mut indices = indices.iter();\nlet index = indices.next().expect(\"first index in inbounds_gep\");\nlet mut result = self.context.new_array_access(self.location, ptr, *index);\nfor index in indices {\n    result = self.context.new_array_access(self.location, result, *index);\n}\nresult.get_address(self.location)\n\n// after\nlet mut indices = indices.iter();\nlet mut result = match indices.next() {\n    Some(index) => self.context.new_array_access(self.location, ptr, *index),\n    None => return ptr,\n};\nfor index in indices {\n    result = self.context.new_array_access(self.location, result, *index);\n}\nresult.get_address(self.location)","handlingStrategy":"validation","validationCode":"// inbounds_gep panics via .expect() when `indices` is empty.\n// Validate the slice length BEFORE calling the backend.\nif indices.is_empty() {\n    return Err(\"inbounds_gep requires at least one index (the structural pointer index)\");\n}\nlet first = indices[0];\n// ...now safe to call builder.inbounds_gep(typ, ptr, indices)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always supply the structural pointer index as the first element of `indices` for any GEP-style helper.","Treat an empty `indices` slice as a programmer error and reject it at the call site before it reaches the backend.","In frontends/codegen layers, wrap all GEP emission in a helper that asserts `!indices.is_empty()` so the invariant lives in one place."],"tags":["rust","codegen","gcc","gccjit","gep","pointer","panic"],"analyzedSha":"22057b88b091743bc0fd8d592a9264f0a6951403","analyzedAt":"2026-08-03T08:09:25.915Z","schemaVersion":2}