rust-lang/rust · error

get element type

Error message

get element type

What it means

Raised inside the fallback branch of `Builder::gcc_lshr` (logical right-shift) for non-native integer operands. When neither `a` nor `b` is a native int and the vector-of-two-vectors path does not apply, the code assumes the operand type is an i128/u128 represented as a two-element array and calls `dyncast_array()` to recover the element (i64/u64) type. A `None` result means a non-array type reached this branch.

Source

Thrown at compiler/rustc_codegen_gcc/src/int.rs:104

                    std::cmp::Ordering::Equal => a >> b,
                    _ => {
                        // NOTE: it is OK to cast even if b has a type bigger than a because b has
                        // been masked by codegen_ssa before calling Builder::lshr or
                        // Builder::ashr.
                        let b = self.context.new_cast(self.location, b, a_type);
                        a >> b
                    }
                }
            }
        } else if a_type.is_vector() && b_type.is_vector() {
            a >> b
        } else if a_native && !b_native {
            self.gcc_lshr(a, self.gcc_int_cast(b, a_type))
        } else {
            // NOTE: we cannot use the lshr builtin because it's calling hi() (to get the most
            // significant half of the number) which uses lshr.

            let native_int_type = a_type.dyncast_array().expect("get element type");

            let func = self.current_func();
            let then_block = func.new_block("then");
            let else_block = func.new_block("else");
            let after_block = func.new_block("after");
            let b0_block = func.new_block("b0");
            let actual_else_block = func.new_block("actual_else");

            let result = func.new_local(self.location, a_type, "shiftResult");

            let sixty_four = self.gcc_int(native_int_type, 64);
            let sixty_three = self.gcc_int(native_int_type, 63);
            let zero = self.gcc_zero(native_int_type);
            let b = self.gcc_int_cast(b, native_int_type);
            let condition = self.gcc_icmp(IntPredicate::IntNE, self.gcc_and(b, sixty_four), zero);
            self.llbb().end_with_conditional(self.location, condition, then_block, else_block);

            let shift_value = self.gcc_sub(b, sixty_four);

View on GitHub (pinned to 22057b88b0)

Solutions

  1. Add an `eprintln!` of `a_type` (and `{:?}`) at the entry to the non-native branch to identify the unexpected type.
  2. Audit callers of `gcc_lshr` in the codegen SSA layer to ensure `b` is always cast to `a_type` (or a native int) before this point.
  3. Re-check `is_native_int_type` / `is_non_native_int_type` helpers for the target's pointer width — an i128 on a 64-bit target should be native.
  4. Confirm libgccjit version: the array-of-two representation is a workaround and is sensitive to libgccjit's handling of 128-bit integers.

Example fix

// before
let native_int_type = a_type.dyncast_array().expect("get element type");

// after
let native_int_type = a_type.dyncast_array().unwrap_or_else(|| {
    panic!("gcc_lshr: non-native operand is not an i128 array: {:?}", a_type);
});
Defensive patterns

Strategy: type-guard

Validate before calling

// gcc_lshr's emulation branch needs a_type.dyncast_array(). Reject mixed
// native/non-native operand pairs before shifting.
let a_type = a.get_type();
let b_type = b.get_type();
let a_native = builder.is_native_int_type(a_type);
let b_native = builder.is_native_int_type(b_type);
if !a_native && a_type.dyncast_array().is_none() {
    return Err("lshr: left operand is neither native int nor an array type");
}
if a_native != b_native && !(a_type.is_vector() && b_type.is_vector()) {
    // cast b to a's native type first to stay on the native shift path
    let b = builder.gcc_int_cast(b, a_type);
}

Type guard

/// True when a >> b can take the gcc_lshr native/vector path without
/// falling into the dyncast_array emulation branch.
fn is_safe_lshr_pair<'gcc>(
    &self,
    a_type: gccjit::Type<'gcc>,
    b_type: gccjit::Type<'gcc>,
) -> bool {
    let a_native = self.is_native_int_type(a_type);
    let b_native = self.is_native_int_type(b_type);
    (a_native && b_native)
        || (a_type.is_vector() && b_type.is_vector())
        || (a_native && !b_native)
        || a_type.dyncast_array().is_some() // emulation branch is valid for arrays
}

Prevention

When it happens

Trigger: `Builder::gcc_lshr(a, b)` is invoked with `a_type` failing `is_native_int_type`, `a_type.is_vector()` false, `b` non-native, and `a_type.dyncast_array()` returning `None`. Typically hit when shifting a 128-bit value on a target where the type is not actually materialized as a 2-element array.

Common situations: ICE during codegen of `>>` on i128/u128 on a 32-bit target, after upgrading libgccjit (array representation may have changed), or after a bug in `TypeReflection::is_non_native_int_type` mislabels a non-int (e.g. a pointer or aggregate) as a non-native int.

Related errors


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