Skip to content

Commit 7c4a9a9

Browse files
committed
Auto merge of #107498 - JohnTitor:rollup-2i6g4uk, r=JohnTitor
Rollup of 8 pull requests Successful merges: - #107245 (Implement unsizing in the new trait solver) - #107445 (Remove `GenFuture` from core) - #107473 (Update books) - #107476 (rustdoc: remove unnecessary wrapper `div.item-decl` from HTML) - #107477 (Migrate last part of CSS themes to CSS variables) - #107479 (Use `ObligationCtxt::new_in_snapshot` in `satisfied_from_param_env`) - #107482 (rustdoc: remove meta keywords from HTML) - #107494 (fix link in std::path::Path::display()) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
2 parents dc3e59c + 150340b commit 7c4a9a9

File tree

102 files changed

+944
-695
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+944
-695
lines changed

Diff for: compiler/rustc_trait_selection/src/solve/assembly.rs

+25
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,21 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq {
173173
ecx: &mut EvalCtxt<'_, 'tcx>,
174174
goal: Goal<'tcx, Self>,
175175
) -> QueryResult<'tcx>;
176+
177+
// The most common forms of unsizing are array to slice, and concrete (Sized)
178+
// type into a `dyn Trait`. ADTs and Tuples can also have their final field
179+
// unsized if it's generic.
180+
fn consider_builtin_unsize_candidate(
181+
ecx: &mut EvalCtxt<'_, 'tcx>,
182+
goal: Goal<'tcx, Self>,
183+
) -> QueryResult<'tcx>;
184+
185+
// `dyn Trait1` can be unsized to `dyn Trait2` if they are the same trait, or
186+
// if `Trait2` is a (transitive) supertrait of `Trait2`.
187+
fn consider_builtin_dyn_upcast_candidates(
188+
ecx: &mut EvalCtxt<'_, 'tcx>,
189+
goal: Goal<'tcx, Self>,
190+
) -> Vec<CanonicalResponse<'tcx>>;
176191
}
177192

178193
impl<'tcx> EvalCtxt<'_, 'tcx> {
@@ -303,6 +318,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
303318
G::consider_builtin_future_candidate(self, goal)
304319
} else if lang_items.gen_trait() == Some(trait_def_id) {
305320
G::consider_builtin_generator_candidate(self, goal)
321+
} else if lang_items.unsize_trait() == Some(trait_def_id) {
322+
G::consider_builtin_unsize_candidate(self, goal)
306323
} else {
307324
Err(NoSolution)
308325
};
@@ -313,6 +330,14 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
313330
}
314331
Err(NoSolution) => (),
315332
}
333+
334+
// There may be multiple unsize candidates for a trait with several supertraits:
335+
// `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>`
336+
if lang_items.unsize_trait() == Some(trait_def_id) {
337+
for result in G::consider_builtin_dyn_upcast_candidates(self, goal) {
338+
candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result });
339+
}
340+
}
316341
}
317342

318343
fn assemble_param_env_candidates<G: GoalKind<'tcx>>(

Diff for: compiler/rustc_trait_selection/src/solve/project_goals.rs

+14
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,20 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
554554
.to_predicate(tcx),
555555
)
556556
}
557+
558+
fn consider_builtin_unsize_candidate(
559+
_ecx: &mut EvalCtxt<'_, 'tcx>,
560+
goal: Goal<'tcx, Self>,
561+
) -> QueryResult<'tcx> {
562+
bug!("`Unsize` does not have an associated type: {:?}", goal);
563+
}
564+
565+
fn consider_builtin_dyn_upcast_candidates(
566+
_ecx: &mut EvalCtxt<'_, 'tcx>,
567+
goal: Goal<'tcx, Self>,
568+
) -> Vec<super::CanonicalResponse<'tcx>> {
569+
bug!("`Unsize` does not have an associated type: {:?}", goal);
570+
}
557571
}
558572

559573
/// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code.

Diff for: compiler/rustc_trait_selection/src/solve/trait_goals.rs

+202-1
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ use std::iter;
44

55
use super::assembly::{self, Candidate, CandidateSource};
66
use super::infcx_ext::InferCtxtExt;
7-
use super::{Certainty, EvalCtxt, Goal, QueryResult};
7+
use super::{CanonicalResponse, Certainty, EvalCtxt, Goal, QueryResult};
88
use rustc_hir::def_id::DefId;
99
use rustc_infer::infer::InferCtxt;
1010
use rustc_infer::traits::query::NoSolution;
11+
use rustc_infer::traits::util::supertraits;
1112
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams};
1213
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
1314
use rustc_middle::ty::{TraitPredicate, TypeVisitable};
@@ -238,6 +239,206 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
238239
.to_predicate(tcx),
239240
)
240241
}
242+
243+
fn consider_builtin_unsize_candidate(
244+
ecx: &mut EvalCtxt<'_, 'tcx>,
245+
goal: Goal<'tcx, Self>,
246+
) -> QueryResult<'tcx> {
247+
let tcx = ecx.tcx();
248+
let a_ty = goal.predicate.self_ty();
249+
let b_ty = goal.predicate.trait_ref.substs.type_at(1);
250+
if b_ty.is_ty_var() {
251+
return ecx.make_canonical_response(Certainty::AMBIGUOUS);
252+
}
253+
ecx.infcx.probe(|_| {
254+
match (a_ty.kind(), b_ty.kind()) {
255+
// Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`
256+
(&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => {
257+
// Dyn upcasting is handled separately, since due to upcasting,
258+
// when there are two supertraits that differ by substs, we
259+
// may return more than one query response.
260+
return Err(NoSolution);
261+
}
262+
// `T` -> `dyn Trait` unsizing
263+
(_, &ty::Dynamic(data, region, ty::Dyn)) => {
264+
// Can only unsize to an object-safe type
265+
if data
266+
.principal_def_id()
267+
.map_or(false, |def_id| !tcx.check_is_object_safe(def_id))
268+
{
269+
return Err(NoSolution);
270+
}
271+
272+
let Some(sized_def_id) = tcx.lang_items().sized_trait() else {
273+
return Err(NoSolution);
274+
};
275+
let nested_goals: Vec<_> = data
276+
.iter()
277+
// Check that the type implements all of the predicates of the def-id.
278+
// (i.e. the principal, all of the associated types match, and any auto traits)
279+
.map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty)))
280+
.chain([
281+
// The type must be Sized to be unsized.
282+
goal.with(
283+
tcx,
284+
ty::Binder::dummy(tcx.mk_trait_ref(sized_def_id, [a_ty])),
285+
),
286+
// The type must outlive the lifetime of the `dyn` we're unsizing into.
287+
goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region))),
288+
])
289+
.collect();
290+
291+
ecx.evaluate_all_and_make_canonical_response(nested_goals)
292+
}
293+
// `[T; n]` -> `[T]` unsizing
294+
(&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => {
295+
// We just require that the element type stays the same
296+
let nested_goals = ecx.infcx.eq(goal.param_env, a_elem_ty, b_elem_ty)?;
297+
ecx.evaluate_all_and_make_canonical_response(nested_goals)
298+
}
299+
// Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
300+
(&ty::Adt(a_def, a_substs), &ty::Adt(b_def, b_substs))
301+
if a_def.is_struct() && a_def.did() == b_def.did() =>
302+
{
303+
let unsizing_params = tcx.unsizing_params_for_adt(a_def.did());
304+
// We must be unsizing some type parameters. This also implies
305+
// that the struct has a tail field.
306+
if unsizing_params.is_empty() {
307+
return Err(NoSolution);
308+
}
309+
310+
let tail_field = a_def
311+
.non_enum_variant()
312+
.fields
313+
.last()
314+
.expect("expected unsized ADT to have a tail field");
315+
let tail_field_ty = tcx.bound_type_of(tail_field.did);
316+
317+
let a_tail_ty = tail_field_ty.subst(tcx, a_substs);
318+
let b_tail_ty = tail_field_ty.subst(tcx, b_substs);
319+
320+
// Substitute just the unsizing params from B into A. The type after
321+
// this substitution must be equal to B. This is so we don't unsize
322+
// unrelated type parameters.
323+
let new_a_substs = tcx.mk_substs(a_substs.iter().enumerate().map(|(i, a)| {
324+
if unsizing_params.contains(i as u32) { b_substs[i] } else { a }
325+
}));
326+
let unsized_a_ty = tcx.mk_adt(a_def, new_a_substs);
327+
328+
// Finally, we require that `TailA: Unsize<TailB>` for the tail field
329+
// types.
330+
let mut nested_goals = ecx.infcx.eq(goal.param_env, unsized_a_ty, b_ty)?;
331+
nested_goals.push(goal.with(
332+
tcx,
333+
ty::Binder::dummy(
334+
tcx.mk_trait_ref(goal.predicate.def_id(), [a_tail_ty, b_tail_ty]),
335+
),
336+
));
337+
338+
ecx.evaluate_all_and_make_canonical_response(nested_goals)
339+
}
340+
// Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>`
341+
(&ty::Tuple(a_tys), &ty::Tuple(b_tys))
342+
if a_tys.len() == b_tys.len() && !a_tys.is_empty() =>
343+
{
344+
let (a_last_ty, a_rest_tys) = a_tys.split_last().unwrap();
345+
let b_last_ty = b_tys.last().unwrap();
346+
347+
// Substitute just the tail field of B., and require that they're equal.
348+
let unsized_a_ty = tcx.mk_tup(a_rest_tys.iter().chain([b_last_ty]));
349+
let mut nested_goals = ecx.infcx.eq(goal.param_env, unsized_a_ty, b_ty)?;
350+
351+
// Similar to ADTs, require that the rest of the fields are equal.
352+
nested_goals.push(goal.with(
353+
tcx,
354+
ty::Binder::dummy(
355+
tcx.mk_trait_ref(goal.predicate.def_id(), [*a_last_ty, *b_last_ty]),
356+
),
357+
));
358+
359+
ecx.evaluate_all_and_make_canonical_response(nested_goals)
360+
}
361+
_ => Err(NoSolution),
362+
}
363+
})
364+
}
365+
366+
fn consider_builtin_dyn_upcast_candidates(
367+
ecx: &mut EvalCtxt<'_, 'tcx>,
368+
goal: Goal<'tcx, Self>,
369+
) -> Vec<CanonicalResponse<'tcx>> {
370+
let tcx = ecx.tcx();
371+
372+
let a_ty = goal.predicate.self_ty();
373+
let b_ty = goal.predicate.trait_ref.substs.type_at(1);
374+
let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else {
375+
return vec![];
376+
};
377+
let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else {
378+
return vec![];
379+
};
380+
381+
// All of a's auto traits need to be in b's auto traits.
382+
let auto_traits_compatible =
383+
b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b));
384+
if !auto_traits_compatible {
385+
return vec![];
386+
}
387+
388+
let mut unsize_dyn_to_principal = |principal: Option<ty::PolyExistentialTraitRef<'tcx>>| {
389+
ecx.infcx.probe(|_| -> Result<_, NoSolution> {
390+
// Require that all of the trait predicates from A match B, except for
391+
// the auto traits. We do this by constructing a new A type with B's
392+
// auto traits, and equating these types.
393+
let new_a_data = principal
394+
.into_iter()
395+
.map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait))
396+
.chain(a_data.iter().filter(|a| {
397+
matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_))
398+
}))
399+
.chain(
400+
b_data
401+
.auto_traits()
402+
.map(ty::ExistentialPredicate::AutoTrait)
403+
.map(ty::Binder::dummy),
404+
);
405+
let new_a_data = tcx.mk_poly_existential_predicates(new_a_data);
406+
let new_a_ty = tcx.mk_dynamic(new_a_data, b_region, ty::Dyn);
407+
408+
// We also require that A's lifetime outlives B's lifetime.
409+
let mut nested_obligations = ecx.infcx.eq(goal.param_env, new_a_ty, b_ty)?;
410+
nested_obligations.push(
411+
goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region))),
412+
);
413+
414+
ecx.evaluate_all_and_make_canonical_response(nested_obligations)
415+
})
416+
};
417+
418+
let mut responses = vec![];
419+
// If the principal def ids match (or are both none), then we're not doing
420+
// trait upcasting. We're just removing auto traits (or shortening the lifetime).
421+
if a_data.principal_def_id() == b_data.principal_def_id() {
422+
if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) {
423+
responses.push(response);
424+
}
425+
} else if let Some(a_principal) = a_data.principal()
426+
&& let Some(b_principal) = b_data.principal()
427+
{
428+
for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) {
429+
if super_trait_ref.def_id() != b_principal.def_id() {
430+
continue;
431+
}
432+
let erased_trait_ref = super_trait_ref
433+
.map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref));
434+
if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) {
435+
responses.push(response);
436+
}
437+
}
438+
}
439+
440+
responses
441+
}
241442
}
242443

243444
impl<'tcx> EvalCtxt<'_, 'tcx> {

Diff for: compiler/rustc_trait_selection/src/traits/const_evaluatable.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ fn satisfied_from_param_env<'tcx>(
219219
}
220220

221221
if let Some(Ok(c)) = single_match {
222-
let ocx = ObligationCtxt::new(infcx);
222+
let ocx = ObligationCtxt::new_in_snapshot(infcx);
223223
assert!(ocx.eq(&ObligationCause::dummy(), param_env, c.ty(), ct.ty()).is_ok());
224224
assert!(ocx.eq(&ObligationCause::dummy(), param_env, c, ct).is_ok());
225225
assert!(ocx.select_all_or_error().is_empty());

Diff for: compiler/rustc_ty_utils/src/ty.rs

-4
Original file line numberDiff line numberDiff line change
@@ -426,10 +426,6 @@ fn unsizing_params_for_adt<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> BitSet<u32
426426
},
427427
};
428428

429-
// FIXME(eddyb) cache this (including computing `unsizing_params`)
430-
// by putting it in a query; it would only need the `DefId` as it
431-
// looks at declared field types, not anything substituted.
432-
433429
// The last field of the structure has to exist and contain type/const parameters.
434430
let Some((tail_field, prefix_fields)) =
435431
def.non_enum_variant().fields.split_last() else

Diff for: library/core/src/future/mod.rs

-45
Original file line numberDiff line numberDiff line change
@@ -56,51 +56,6 @@ unsafe impl Send for ResumeTy {}
5656
#[unstable(feature = "gen_future", issue = "50547")]
5757
unsafe impl Sync for ResumeTy {}
5858

59-
/// Wrap a generator in a future.
60-
///
61-
/// This function returns a `GenFuture` underneath, but hides it in `impl Trait` to give
62-
/// better error messages (`impl Future` rather than `GenFuture<[closure.....]>`).
63-
// This is `const` to avoid extra errors after we recover from `const async fn`
64-
#[doc(hidden)]
65-
#[unstable(feature = "gen_future", issue = "50547")]
66-
#[rustc_const_unstable(feature = "gen_future", issue = "50547")]
67-
#[inline]
68-
pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
69-
where
70-
T: crate::ops::Generator<ResumeTy, Yield = ()>,
71-
{
72-
use crate::{
73-
ops::{Generator, GeneratorState},
74-
pin::Pin,
75-
task::Poll,
76-
};
77-
78-
#[rustc_diagnostic_item = "gen_future"]
79-
struct GenFuture<T: Generator<ResumeTy, Yield = ()>>(T);
80-
81-
// We rely on the fact that async/await futures are immovable in order to create
82-
// self-referential borrows in the underlying generator.
83-
impl<T: Generator<ResumeTy, Yield = ()>> !Unpin for GenFuture<T> {}
84-
85-
impl<T: Generator<ResumeTy, Yield = ()>> Future for GenFuture<T> {
86-
type Output = T::Return;
87-
#[track_caller]
88-
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
89-
// SAFETY: Safe because we're !Unpin + !Drop, and this is just a field projection.
90-
let gen = unsafe { Pin::map_unchecked_mut(self, |s| &mut s.0) };
91-
92-
// Resume the generator, turning the `&mut Context` into a `NonNull` raw pointer. The
93-
// `.await` lowering will safely cast that back to a `&mut Context`.
94-
match gen.resume(ResumeTy(NonNull::from(cx).cast::<Context<'static>>())) {
95-
GeneratorState::Yielded(()) => Poll::Pending,
96-
GeneratorState::Complete(x) => Poll::Ready(x),
97-
}
98-
}
99-
}
100-
101-
GenFuture(gen)
102-
}
103-
10459
#[lang = "get_context"]
10560
#[doc(hidden)]
10661
#[unstable(feature = "gen_future", issue = "50547")]

Diff for: library/std/src/path.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2688,6 +2688,7 @@ impl Path {
26882688
/// escapes the path please use [`Debug`] instead.
26892689
///
26902690
/// [`Display`]: fmt::Display
2691+
/// [`Debug`]: fmt::Debug
26912692
///
26922693
/// # Examples
26932694
///

Diff for: src/doc/nomicon

Diff for: src/doc/rust-by-example

0 commit comments

Comments
 (0)