rust-lang/rust · error · B::Error
Cannot create a zero-sized constant for type `{ty_internal}`
Error message
Cannot create a zero-sized constant for type `{ty_internal}`: Type `{ty_internal}` has {} bytes What it means
Thrown by `try_new_const_zst` in compiler/rustc_public_bridge/src/context/impls.rs:470 when the type's layout was computed successfully but its size is non-zero. The API's contract is to produce a zero-sized constant; a type with a positive byte size is, by definition, not a ZST, so the call is rejected. This catches mismatches between caller intent (build a unit value) and the actual type.
Source
Thrown at compiler/rustc_public_bridge/src/context/impls.rs:470
}
pub fn eval_target_usize_ty(&self, cnst: ty::Const<'tcx>) -> Result<u64, B::Error> {
cnst.try_to_target_usize(self.tcx)
.ok_or_else(|| B::Error::new(format!("Const `{cnst:?}` cannot be encoded as u64")))
}
pub fn try_new_const_zst(&self, ty_internal: Ty<'tcx>) -> Result<MirConst<'tcx>, B::Error> {
let size = self
.tcx
.layout_of(self.fully_monomorphized().as_query_input(ty_internal))
.map_err(|err| {
B::Error::new(format!(
"Cannot create a zero-sized constant for type `{ty_internal}`: {err}"
))
})?
.size;
if size.bytes() != 0 {
return Err(B::Error::new(format!(
"Cannot create a zero-sized constant for type `{ty_internal}`: \
Type `{ty_internal}` has {} bytes",
size.bytes()
)));
}
Ok(MirConst::Ty(ty_internal, self.const_zero_sized(ty_internal)))
}
pub fn const_zero_sized(&self, ty_internal: Ty<'tcx>) -> ty::Const<'tcx> {
ty::Const::zero_sized(self.tcx, ty_internal)
}
/// Create a caller location constant from a span.
///
/// This produces a `&'static core::panic::Location<'static>` constant,
/// which is the implicit extra argument for `#[track_caller]` functions.
pub fn span_as_caller_location(&self, span: Span) -> MirConst<'tcx> {View on GitHub (pinned to 22057b88b0)
Solutions
- Verify the type is genuinely zero-sized before calling `try_new_const_zst` (check `layout_of(...).size.bytes() == 0`).
- If you need a zero value for a non-ZST type, use the appropriate scalar/aggregate const-construction API rather than `try_new_const_zst`.
- Pass the correct unit type (`Ty::new_unit`) if a ZST value is what you actually need.
Example fix
// before
let c = cx.try_new_const_zst(ty)?; // ty is e.g. struct Foo(u32)
// after
let c = if cx.tcx.layout_of(cx.fully_monomorphized().as_query_input(ty)).unwrap().size.bytes() == 0 {
cx.try_new_const_zst(ty)?
} else {
cx.mir_const_from_scalar(Scalar::Int(scalar), ty) // build a real value
}; Defensive patterns
Strategy: type-guard
Validate before calling
// Before MirConst::try_new_zero_sized(ty):
let layout = ty.layout()?;
let size = layout.shape().size; // rustc_public::target::MachineSize
if size.bytes() != 0 {
return Err(format!(
"type {:?} is {} bytes, not a ZST",
ty, size.bytes()
));
}
let zst = MirConst::try_new_zero_sized(ty)?; Type guard
// True only for zero-sized (ZST) types that have a computable layout.
fn is_zst(ty: &Ty) -> bool {
ty.layout()
.map(|l| !l.shape().is_unsized() && l.shape().size.bytes() == 0)
.unwrap_or(false)
} Try / catch
match MirConst::try_new_zero_sized(ty) {
Ok(c) => c,
Err(e) if e.to_string().contains("bytes") => {
// Type is sized but non-ZST. Use the typed value constructor
// (e.g. MirConst::try_from_uint) instead of the ZST shortcut.
return typed_default(ty);
}
Err(e) => return Err(e.into()),
} Prevention
- Check `layout.shape().size.bytes() == 0` (and `!is_unsized()`) before constructing a ZST constant; `try_new_zero_sized` rejects any non-zero size.
- Common ZSTs: unit `()`, `PhantomData`, empty enums/structs, `Option<&T>::None`-shaped values — but always verify via layout rather than assuming from the source spelling.
- Do not confuse `ZSTValue` (a TyConstKind variant) with this constructor; the constructor enforces the size invariant at construction time.
When it happens
Trigger: Calling `context.try_new_const_zst(ty)` on a type like `struct Foo(u32)`, a non-unit struct, an enum with payload, or any type whose layout size is greater than zero.
Common situations: Assuming a type is a unit/ZST when it actually carries data (e.g. after a refactor added a field). Passing a `PhantomData`-free wrapper type where a ZST was expected. Confusing `()` with a single-field struct.
Related errors
- Cannot create a zero-sized constant for type `{ty_internal}`
- Const `{cnst:?}` cannot be encoded as u64
- Value overflow: cannot convert `{value}` to `{ty_internal}`.
- aggregates can't have `FieldsShape::Primitive`
- assignment does not match variant
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/8d9b9526c44c9641.json.
Report an issue: GitHub.