Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_cranelift/rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[toolchain]
channel = "nightly-2026-09-08"
channel = "nightly-2026-09-10"
components = ["rust-src", "rustc-dev", "llvm-tools", "rustfmt"]
profile = "minimal"
41 changes: 40 additions & 1 deletion compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,15 @@ where
goal: Goal<I, Self>,
assumption: I::Clause,
) -> Result<Candidate<I>, NoSolutionOrRerunNonErased> {
Self::probe_and_match_goal_against_assumption(ecx, source, goal, assumption, |ecx| {
// We inline much of `probe_and_match_goal_against_assumption` and
// `TraitPredicate::match_assumption` here, as we never encounter
// `Sized` or `MetaSized` goals here, and we need to equate `goal`
// and `assumption`'s trait refs directly inside this function in
// order to prevent unsoundness (see below).

Self::fast_reject_assumption(ecx, goal, assumption)?;

ecx.probe_trait_candidate(source).enter(|ecx| {
let cx = ecx.cx();
let ty::Dynamic(bounds, _) = goal.predicate.self_ty().kind() else {
panic!("expected object type in `probe_and_consider_object_bound_candidate`");
Expand All @@ -107,6 +115,37 @@ where
}
});

// If we need to prove `dyn for<'x> Trait<'x> + '?temp: Trait<'static>` with
//
// ```rs
// trait Trait<'a>: 'a {}
// ```
//
// we have the goal's trait ref as `Trait<'static>` and a theoretical impl
// resembling:
//
// ```rs
// impl<'s, 'hr> Trait<'hr> for dyn for<'x> Trait<'x> + 's
// where
// dyn for<'a> Trait<'a> + 's: 'hr
// {}
// ```
//
// where 'hr is our bound var. The where-clause elaborates to `'s: 'hr`;
// in this case we have 's := '?temp. Instantiating the binder gives us
// 'hr := '?infer, and our goal has 'hr := 'static, so we need to equate
// the instantiated trait ref to the goal in order to get '?infer := 'static,
// since what we want is the constraint `'?temp: 'static`.
//
// If we instead passed the binder to predicates_for_object_candidate and let
// it instantiate the binder itself, we would lose '?infer := 'static, since
// predicates_for_object_candidate has no way of equating the trait ref with
// the goal. We would simply have 'hr := '?infer, giving us the constraint
// `?temp: '?infer`, which is satisfiable for any lifetime, leading to
// unsoundness: trait-system-refactor-initiative#295.
let trait_ref = ecx.instantiate_binder_with_infer(trait_ref);
ecx.eq(goal.param_env, goal.predicate.trait_ref(cx), trait_ref)?;

match structural_traits::predicates_for_object_candidate(
ecx,
goal.param_env,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use rustc_type_ir::lang_items::{SolverProjectionLangItem, SolverTraitLangItem};
use rustc_type_ir::solve::SizedTraitKind;
use rustc_type_ir::solve::inspect::ProbeKind;
use rustc_type_ir::{
self as ty, Binder, FallibleTypeFolder, Interner, Movability, Mutability, Region, TypeFoldable,
self as ty, FallibleTypeFolder, Interner, Movability, Mutability, Region, TypeFoldable,
TypeSuperFoldable, Unnormalized, Upcast as _, elaborate,
};
use rustc_type_ir_macros::{TypeFoldable_Generic, TypeVisitable_Generic};
Expand Down Expand Up @@ -891,15 +891,14 @@ pub(in crate::solve) fn const_conditions_for_destruct<I: Interner>(
pub(in crate::solve) fn predicates_for_object_candidate<D, I>(
ecx: &mut EvalCtxt<'_, D>,
param_env: I::ParamEnv,
trait_ref: Binder<I, ty::TraitRef<I>>,
trait_ref: ty::TraitRef<I>,
object_bounds: I::BoundExistentialPredicates,
) -> Result<Vec<Goal<I, I::Predicate>>, AmbiguousOrRerunNonErased>
where
D: SolverDelegate<Interner = I>,
I: Interner,
{
let cx = ecx.cx();
let trait_ref = ecx.instantiate_binder_with_infer(trait_ref);
let mut requirements = vec![];
// Elaborating all supertrait outlives obligations here is not soundness critical,
// since if we just used the unelaborated set, then the transitive supertraits would
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5158,6 +5158,38 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
&& let ty::Ref(_, inner_ty, _) = trait_pred.skip_binder().self_ty().kind()
&& let ty::Uint(ty::UintTy::Usize) = inner_ty.kind()
{
// If the index is written as `&i`, suggest removing the borrow instead of
// dereferencing it, i.e. `v[&i]` -> `v[i]` rather than `v[*&i]`.
let span = obligation.cause.span;
if !span.from_expansion()
&& let Some(body) = self.tcx.hir_maybe_body_owned_by(obligation.cause.body_def_id)
&& let Some(expr) = {
let mut finder = FindExprBySpan::new(span, self.tcx);
finder.visit_expr(body.value);
finder.result
}
&& let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, borrowed) =
expr.kind
&& let Some(amp_span) = borrowed
.span
.find_ancestor_inside_same_ctxt(expr.span)
.map(|borrowed_span| expr.span.until(borrowed_span))
&& self
.tcx
.sess
.source_map()
.span_to_snippet(amp_span)
.is_ok_and(|snippet| snippet.starts_with('&'))
{
err.span_suggestion_verbose(
amp_span,
"remove this reference",
"",
Applicability::MachineApplicable,
);
return;
}

err.span_suggestion_verbose(
obligation.cause.span.shrink_to_lo(),
"dereference this index",
Expand Down
10 changes: 10 additions & 0 deletions tests/ui/suggestions/suggest-remove-reference-index.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//@ run-rustfix

fn main() {
let arr = [false];
let i = 0usize;

println!("{}", arr[i]); //~ ERROR the type `[bool]` cannot be indexed by `&usize`
println!("{}", arr[(i + 0)]); //~ ERROR the type `[bool]` cannot be indexed by `&usize`
println!("{}", arr[i]); //~ ERROR the type `[bool]` cannot be indexed by `&usize`
}
10 changes: 10 additions & 0 deletions tests/ui/suggestions/suggest-remove-reference-index.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
//@ run-rustfix

fn main() {
let arr = [false];
let i = 0usize;

println!("{}", arr[&i]); //~ ERROR the type `[bool]` cannot be indexed by `&usize`
println!("{}", arr[&(i + 0)]); //~ ERROR the type `[bool]` cannot be indexed by `&usize`
println!("{}", arr[& i]); //~ ERROR the type `[bool]` cannot be indexed by `&usize`
}
66 changes: 66 additions & 0 deletions tests/ui/suggestions/suggest-remove-reference-index.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
error[E0277]: the type `[bool]` cannot be indexed by `&usize`
--> $DIR/suggest-remove-reference-index.rs:7:24
|
LL | println!("{}", arr[&i]);
| ^^ slice indices are of type `usize` or ranges of `usize`
|
= help: the trait `SliceIndex<[bool]>` is not implemented for `&usize`
help: `usize` implements trait `SliceIndex<T>`
--> $SRC_DIR/core/src/slice/index.rs:LL:COL
|
= note: `SliceIndex<[T]>`
--> $SRC_DIR/core/src/bstr/traits.rs:LL:COL
|
= note: `SliceIndex<ByteStr>`
= note: required for `[bool]` to implement `Index<&usize>`
help: remove this reference
|
LL - println!("{}", arr[&i]);
LL + println!("{}", arr[i]);
|

error[E0277]: the type `[bool]` cannot be indexed by `&usize`
--> $DIR/suggest-remove-reference-index.rs:8:24
|
LL | println!("{}", arr[&(i + 0)]);
| ^^^^^^^^ slice indices are of type `usize` or ranges of `usize`
|
= help: the trait `SliceIndex<[bool]>` is not implemented for `&usize`
help: `usize` implements trait `SliceIndex<T>`
--> $SRC_DIR/core/src/slice/index.rs:LL:COL
|
= note: `SliceIndex<[T]>`
--> $SRC_DIR/core/src/bstr/traits.rs:LL:COL
|
= note: `SliceIndex<ByteStr>`
= note: required for `[bool]` to implement `Index<&usize>`
help: remove this reference
|
LL - println!("{}", arr[&(i + 0)]);
LL + println!("{}", arr[(i + 0)]);
|

error[E0277]: the type `[bool]` cannot be indexed by `&usize`
--> $DIR/suggest-remove-reference-index.rs:9:24
|
LL | println!("{}", arr[& i]);
| ^^^ slice indices are of type `usize` or ranges of `usize`
|
= help: the trait `SliceIndex<[bool]>` is not implemented for `&usize`
help: `usize` implements trait `SliceIndex<T>`
--> $SRC_DIR/core/src/slice/index.rs:LL:COL
|
= note: `SliceIndex<[T]>`
--> $SRC_DIR/core/src/bstr/traits.rs:LL:COL
|
= note: `SliceIndex<ByteStr>`
= note: required for `[bool]` to implement `Index<&usize>`
help: remove this reference
|
LL - println!("{}", arr[& i]);
LL + println!("{}", arr[i]);
|

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0277`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//! regression test for https://github.com/rust-lang/trait-system-refactor-initiative/issues/295

//@ compile-flags: -Znext-solver

#![forbid(unsafe_code)]

trait Tr<'a> {
type A: 'a;
}

fn f<X: ?Sized + Tr<'static>>(a: <X as Tr<'static>>::A) -> Box<dyn std::any::Any> {
Box::new(a)
}

fn launder<'b>(r: &'b u8) -> &'static u8 {
*f::<dyn for<'a> Tr<'a, A = &'b u8>>(r).downcast_ref::<&'static u8>().unwrap()
//~^ ERROR lifetime may not live long enough
}

fn main() {
let p;
{
let x = Box::new(42u8);
p = launder(&x);
}
println!("{}", *p);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
error: lifetime may not live long enough
--> $DIR/assoc-type-static-lifetime-object-bound.rs:16:6
|
LL | fn launder<'b>(r: &'b u8) -> &'static u8 {
| -- lifetime `'b` defined here
LL | *f::<dyn for<'a> Tr<'a, A = &'b u8>>(r).downcast_ref::<&'static u8>().unwrap()
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'static`

error: aborting due to 1 previous error

24 changes: 24 additions & 0 deletions tests/ui/traits/next-solver/gat-static-in-trait-object.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//! regression test from https://github.com/rust-lang/rust/pull/160831/changes#r3852248101
//! once we allow GATs in object types, we want to make sure this isn't unsound.

//@ compile-flags: -Znext-solver

use std::any::Any;

trait Trait {
type Assoc<'a>: 'a;
}

fn tr<T: Trait>(x: T::Assoc<'static>) -> Box<dyn Any> { Box::new(x) }

fn foo<'s>(x: &'s str) -> Box<dyn Any>
where
dyn for<'hr> Trait<Assoc<'hr> = &'s str>: Trait<Assoc<'static> = &'s str>,
//~^ ERROR the trait `Trait` is not dyn compatible
//~| ERROR the trait `Trait` is not dyn compatible
{
tr::<dyn for<'hr> Trait<Assoc<'hr> = &'s str>>(x)
//~^ ERROR the trait `Trait` is not dyn compatible
}

fn main() {}
51 changes: 51 additions & 0 deletions tests/ui/traits/next-solver/gat-static-in-trait-object.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
error[E0038]: the trait `Trait` is not dyn compatible
--> $DIR/gat-static-in-trait-object.rs:16:47
|
LL | dyn for<'hr> Trait<Assoc<'hr> = &'s str>: Trait<Assoc<'static> = &'s str>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Trait` is not dyn compatible
|
note: for a trait to be dyn compatible it needs to allow building a vtable
for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>
--> $DIR/gat-static-in-trait-object.rs:9:10
|
LL | trait Trait {
| ----- this trait is not dyn compatible...
LL | type Assoc<'a>: 'a;
| ^^^^^ ...because it contains generic associated type `Assoc`
= help: consider moving `Assoc` to another trait

error[E0038]: the trait `Trait` is not dyn compatible
--> $DIR/gat-static-in-trait-object.rs:16:53
|
LL | dyn for<'hr> Trait<Assoc<'hr> = &'s str>: Trait<Assoc<'static> = &'s str>,
| ^^^^^^^^^^^^^^^^^^^^^^^^ `Trait` is not dyn compatible
|
note: for a trait to be dyn compatible it needs to allow building a vtable
for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>
--> $DIR/gat-static-in-trait-object.rs:9:10
|
LL | trait Trait {
| ----- this trait is not dyn compatible...
LL | type Assoc<'a>: 'a;
| ^^^^^ ...because it contains generic associated type `Assoc`
= help: consider moving `Assoc` to another trait

error[E0038]: the trait `Trait` is not dyn compatible
--> $DIR/gat-static-in-trait-object.rs:20:14
|
LL | tr::<dyn for<'hr> Trait<Assoc<'hr> = &'s str>>(x)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `Trait` is not dyn compatible
|
note: for a trait to be dyn compatible it needs to allow building a vtable
for more information, visit <https://doc.rust-lang.org/reference/items/traits.html#dyn-compatibility>
--> $DIR/gat-static-in-trait-object.rs:9:10
|
LL | trait Trait {
| ----- this trait is not dyn compatible...
LL | type Assoc<'a>: 'a;
| ^^^^^ ...because it contains generic associated type `Assoc`
= help: consider moving `Assoc` to another trait

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0038`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//! regression test for https://github.com/rust-lang/trait-system-refactor-initiative/issues/295

//@ compile-flags: -Znext-solver

#![forbid(unsafe_code)]

trait Trait<'a>: 'a {}

fn g<'s>(s: &'s String) -> &'static String
where
dyn for<'x> Trait<'x> + 's: Trait<'static>,
{
s
}

fn main() {
let r = g(&String::from("freed"));
//~^ ERROR temporary value dropped while borrowed
println!("{r}");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error[E0716]: temporary value dropped while borrowed
--> $DIR/supertrait-static-lifetime-object-bound.rs:17:16
|
LL | let r = g(&String::from("freed"));
| ---^^^^^^^^^^^^^^^^^^^^^-- temporary value is freed at the end of this statement
| | |
| | creates a temporary value which is freed while still in use
| argument requires that borrow lasts for `'static`
|
note: requirement that the value outlives `'static` introduced here
--> $DIR/supertrait-static-lifetime-object-bound.rs:11:33
|
LL | dyn for<'x> Trait<'x> + 's: Trait<'static>,
| ^^^^^^^^^^^^^^

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0716`.
Loading