{"id":"8c5410e304299e69","repo":"rust-lang/rust","slug":"function-ptr","errorCode":null,"errorMessage":"function ptr","messagePattern":"function ptr","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler/rustc_codegen_gcc/src/builder.rs","lineNumber":221,"sourceCode":"                }\n            })\n            .collect();\n\n        debug_assert_eq!(casted_args.len(), args.len());\n\n        Cow::Owned(casted_args)\n    }\n\n    fn check_ptr_call<'b>(\n        &mut self,\n        _typ: &str,\n        func_ptr: RValue<'gcc>,\n        args: &'b [RValue<'gcc>],\n        on_stack_param_indices: &FxHashSet<usize>,\n    ) -> Cow<'b, [RValue<'gcc>]> {\n        let mut all_args_match = true;\n        let mut param_types = vec![];\n        let gcc_func = func_ptr.get_type().dyncast_function_ptr_type().expect(\"function ptr\");\n        for (index, arg) in args.iter().enumerate().take(gcc_func.get_param_count()) {\n            let param = gcc_func.get_param_type(index);\n            if param != arg.get_type() {\n                all_args_match = false;\n            }\n            param_types.push(param);\n        }\n\n        if all_args_match {\n            return Cow::Borrowed(args);\n        }\n\n        let func_name = format!(\"{:?}\", func_ptr);\n\n        let mut casted_args: Vec<_> = param_types\n            .into_iter()\n            .zip(args.iter())\n            .enumerate()","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/rust-lang/rust/blob/22057b88b091743bc0fd8d592a9264f0a6951403/compiler/rustc_codegen_gcc/src/builder.rs#L203-L239","documentation":"Thrown by rustc_codegen_gcc inside check_ptr_call(), which validates and bitcasts the arguments of an indirect (function-pointer) call. The backend calls func_ptr.get_type().dyncast_function_ptr_type().expect(\"function ptr\") to recover the function's parameter/return types; if libgccjit reports the value's type as something other than a function-pointer type (returns None), the expect panics. It indicates the GCC type system lost or never attached function-pointer type information to the callee value.","triggerScenarios":"Calling through a value that the gccjit backend does not recognise as a function pointer: an opaque pointer cast to fn(...), an integer reinterpreted as a function pointer, a libgccjit intrinsic whose argument list has an unusual signature, or a value that was bitcast through a non-function type before the call. The panic occurs in check_ptr_call, which runs for every function_ptr_call after argument adjustment.","commonSituations":"libgccjit version mismatch where opaque-pointer handling changed how dyncast_function_ptr_type behaves; calling intrinsics or raw addresses cast to fn pointers; older gccjit builds that don't tag casted pointers with function-pointer type info. Most frequent after a gcc/libgccjit upgrade.","solutions":["Update (or pin to a known-good) libgccjit matching the rustc_codegen_gcc version - opaque-pointer support changed across gcc 13/14.","Ensure the callee is constructed/cast as an explicit function-pointer type before the call rather than through an integer or generic pointer.","Switch to the LLVM backend for the affected crate while the gccjit issue is reproduced.","Report the failing intrinsic/call signature to rustc_codegen_gcc with a minimal reproducer so check_ptr_call can handle the type gracefully instead of expect-ing."],"exampleFix":"// before - integer cast to fn pointer confuses dyncast\nlet f: extern \"C\" fn(u32) -> u32 = unsafe { std::mem::transmute(0xdeadbeefusize) };\nf(1);\n\n// after - obtain a real function pointer from a defined extern item\nextern \"C\" { fn real_fn(x: u32) -> u32; }\nunsafe { real_fn(1) }","handlingStrategy":"type-guard","validationCode":"// Avoid calling-pattern code that requires cg_gcc to materialize a raw\n// function pointer via bitcast from an integer/non-pointer type.\n// e.g. `let f: fn() = unsafe { std::mem::transmute(0usize) };`\n// Audit for int->fn-ptr transmutes before building with cg_gcc.\nlet re = regex::Regex::new(r\"transmute::<(?:usize|u\\d+|i\\d+),\\s*fn\").unwrap();\nif re.is_match(&src) { eprintln!(\"cg_gcc cannot build function ptr from int; refactor.\"); }","typeGuard":"// Rust: keep function references as real `fn` types, never as usize.\nfn is_real_fn_pointer<T>(_v: &T) -> bool { false }\nfn is_real_fn_pointer(_v: &dyn Fn()) -> bool { true }\n// Better: encode it in the type system so transmute is unnecessary.\ntrait FnPtr: Sized { fn as_ptr(self) -> Self { self } }\nimpl FnPtr for fn() {}","tryCatchPattern":"// Compile-time panic in cg_gcc; not catchable at runtime.\n// Switch crate to LLVM backend, or refactor the offending fn-ptr construction.","preventionTips":["Never transmute integers to `fn(...)` types; store and pass real `fn` / `unsafe extern \"C\" fn` types.","Avoid `as` casts from `usize` to function pointers under cg_gcc.","Use pointer-to-pointer transmute (`*mut c_void` -> `unsafe extern \"C\" fn(...)`), the supported path."],"tags":["rustc","gcc","gccjit","codegen","expect","function-pointer","opaque-pointer"],"analyzedSha":"22057b88b091743bc0fd8d592a9264f0a6951403","analyzedAt":"2026-08-03T08:09:25.915Z","schemaVersion":2}