Repository navigation
field_representing_type_raw mishandles const generics #162127
Description
I am filing this issue on behalf of @carlini, who discovered this bug with an LLM (labelled as "bug 4" at https://rust-lang.zulipchat.com/#narrow/channel/364551-t-types.2Ftrait-system-refactor/topic/Reporting.20a.20collection.20of.20soundness.20bugs.20in.20next-solver/near/617737394)
The following code is unsound. It reads the local variable after the variable has already gone out of scope, when compiled with -Znext-solver. It is possible that a similar unsoundness can also happen on the old solver, but I haven't figured out how.
It appears that the compiler doesn't realize that the two Pick implementations overlap, using two different lifetimes for <W<0> as Pick<'a, 'b>>::Out. I don't quite understand why the compiler seems to think that FieldRepresentingType<Left, 0, F>: Field and FieldRepresentingType<Right, 0, F>: Field can't both hold simultaneously.
Note that this code (and comments in it) are generated by an LLM. I did not verify the correctness of the comments.
cc @BennoLossin, who worked on field_projections
// Old solver: `W<0>: Pick<'_, 'static>` is ambiguous at the call in `main` (both impls apply).
// -Znext-solver: its builtin `FieldRepresentingType<_, 0, F>: Field` candidate answers
// `NoSolution` while `F` is still an inference variable, so coherence believes the two
// impls never overlap and each is then selected alone -- turning `&'a u8` into `&'static u8`.
#![feature(field_projections, field_representing_type_raw)] // needed because: both gates required to name `FieldRepresentingType`
#![allow(incomplete_features, internal_features, dead_code)]
#![forbid(unsafe_code)]
use core::field::{Field, FieldRepresentingType};
struct Left { l: u8 } // needed because: two distinct structs make the two impl bounds distinct
struct Right { r: u8 }
struct W<const F: u32>;
trait Pick<'a, 'b> {
type Out;
}
// Both impls apply at `W<0>` (field 0 exists in `Left` and `Right`); coherence should reject.
impl<'a, 'b, const F: u32> Pick<'a, 'b> for W<F>
where
FieldRepresentingType<Left, 0, F>: Field,
{
type Out = &'a u8;
}
impl<'a, 'b, const F: u32> Pick<'a, 'b> for W<F>
where
FieldRepresentingType<Right, 0, F>: Field,
{
type Out = &'b u8; // needed because: a literal `&'static u8` here hits a mono recursion limit under -Znext-solver
}
// Only the `Left` bound in scope: `Out` normalizes to `&'a u8`.
fn into_out<'a, const F: u32>(x: &'a u8) -> <W<F> as Pick<'a, 'static>>::Out
where
FieldRepresentingType<Left, 0, F>: Field,
{
x
}
// Only the `Right` bound in scope: `Out` normalizes to `&'static u8`.
fn out_into_static<'a, const F: u32>(y: <W<F> as Pick<'a, 'static>>::Out) -> &'static u8
where
FieldRepresentingType<Right, 0, F>: Field,
{
y
}
fn launder<'a, const F: u32>(x: &'a u8) -> &'static u8
where
W<F>: Pick<'a, 'static>, // needed because: keeps `Out` rigid here so the value crosses unnormalized
FieldRepresentingType<Left, 0, F>: Field,
FieldRepresentingType<Right, 0, F>: Field,
{
out_into_static::<F>(into_out::<F>(x))
}
fn main() {
let escaped: &'static u8 = {
let local = 42u8;
launder::<0>(&local) // `&local` escapes its block as `&'static u8`
};
println!("{escaped}");
}Meta
Reproducible with 1.100.0-nightly (2026-08-31 0dfb098f3aeecbe38c25)
Activity
BennoLossin commentedon Sep 3, 2026
our current plan is to remove
FieldRepresentingTypein the future and replace it with a compiler builtin type (a newTyKind), so I'm not concerned about this at all. that type is behind an internal feature for this reason (using it without the macro is very likely unsound). the reason this is a problem is that theFieldimpl check doesn't support const generics inFieldRepresentingType, it expects concrete instantiations (sincefield_of!always sets the variant and field index appropriately and there is no way to make that generic).Rollup merge of rust-lang#163603 - yara-blue:reflection-reorder, r=ol…
Unrolled build for #163603 in rollup 163661
Rollup merge of #163603 - yara-blue:reflection-reorder, r=oli-obk
Rollup merge of #163603 - yara-blue:reflection-reorder, r=oli-obk