rust-lang/rust · error

LLVM does not have support for catchret

Error message

LLVM does not have support for catchret

What it means

This panic is raised by `GenericBuilder::catch_ret` (builder.rs:1807-1813) when the LLVM-C call `LLVMBuildCatchRet` returns None. catchret exits a SEH catch funclet and transfers control back to normal code on MSVC targets; a null return means the linked LLVM cannot produce it. The `.expect("LLVM does not have support for catchret")` guard converts the unsupported instruction into an immediate compiler panic because partial funclet IR is invalid.

Source

Thrown at compiler/rustc_codegen_llvm/src/builder.rs:1813

            &[self.val_ty(src)],
            &[src],
        )
    }
}
impl<'a, 'll, CX: Borrow<SCx<'ll>>> GenericBuilder<'a, 'll, CX> {
    pub(crate) fn add_clause(&mut self, landing_pad: &'ll Value, clause: &'ll Value) {
        unsafe {
            llvm::LLVMAddClause(landing_pad, clause);
        }
    }

    pub(crate) fn catch_ret(
        &mut self,
        funclet: &Funclet<'ll>,
        unwind: &'ll BasicBlock,
    ) -> &'ll Value {
        let ret = unsafe { llvm::LLVMBuildCatchRet(self.llbuilder, funclet.cleanuppad(), unwind) };
        ret.expect("LLVM does not have support for catchret")
    }

    pub(crate) fn check_call<'b>(
        &mut self,
        typ: &str,
        fn_ty: &'ll Type,
        llfn: &'ll Value,
        args: &'b [&'ll Value],
    ) -> Cow<'b, [&'ll Value]> {
        assert!(
            self.cx.type_kind(fn_ty) == TypeKind::Function,
            "builder::{typ} not passed a function, but {fn_ty:?}"
        );

        let param_tys = self.cx.func_params_types(fn_ty);

        let all_args_match = iter::zip(&param_tys, args.iter().map(|&v| self.cx.val_ty(v)))
            .all(|(expected_ty, actual_ty)| *expected_ty == actual_ty);

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Use the rustc toolchain's bundled LLVM.
  2. Rebuild LLVM with EH (`-DLLVM_ENABLE_EH=ON`) and the X86 MSVC target.
  3. Align rustc and LLVM versions (rebuild from source).
  4. Avoid the MSVC target or compile with `-Cpanic=abort` to skip catchret emission.

Example fix

// before
rustc --target x86_64-pc-windows-msvc -Cpanic=unwind
// after
rustc --target x86_64-pc-windows-msvc -Cpanic=abort   # no catchret needed
Defensive patterns

Strategy: validation

Validate before calling

# catchret returns from a catch handler; emitted alongside catchswitch.
# Validate that panic=unwind is even meaningful for this LLVM before relying on it.
rustc -C panic=unwind --print=cfg 2>/dev/null | grep -q panic=unwind \
  || echo 'NOTE: unwind cfg not honoured; SEH catchret may be unlowerable on this LLVM'

Try / catch

out="$(cargo build 2>&1)"; rc=$?
if [ $rc -ne 0 ]; then
  case "$out" in
    *"LLVM does not have support for catchret"*)
      echo "catchret unlowerable; use a WinEH-capable LLVM or avoid catching across FFI" >&2 ;;
    *) echo "$out" >&2 ;;
  esac
  exit $rc
fi

Prevention

When it happens

Trigger: Compiling any SEH catch block whose exit must be lowered to a `catchret` when targeting `*-pc-windows-msvc`, while LLVM returns null from `LLVMBuildCatchRet`. Triggers at builder.rs:1812-1813 inside the codegen of the catch handler's terminator.

Common situations: Custom/system LLVM lacking MSVC EH; rustc-vs-LLVM version skew; cross-compiling to MSVC with a host LLVM that does not implement catchret.

Related errors


AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03). Data as JSON: /data/errors/c587526409015e72.json. Report an issue: GitHub.