`field_representing_type_raw` mishandles const generics
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
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)
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the supplied reproducer on the stated nightly using -Znext-solver, then trace how coherence handles the FieldRepresentingType<_, 0, F>: Field candidates and const generics. Done means the overlapping impls are not treated as disjoint and the reproducer no longer permits a non-static reference to escape as 'static, with a regression test covering the case.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100