Skip to content

Commit ff24ef9

Browse files
committed
Auto merge of #123676 - GuillaumeGomez:rollup-1hurixy, r=GuillaumeGomez
Rollup of 8 pull requests Successful merges: - #123254 (Do not allocate for ZST ThinBox (attempt 2 using const_allocate)) - #123626 (Add MC/DC support to coverage test tools) - #123638 (rustdoc: synthetic auto: filter out clauses from the implementor's ParamEnv) - #123653 (Split `non_local_definitions` lint tests in separate test files) - #123658 (Stop making any assumption about the projections applied to the upvars in the `ByMoveBody` pass) - #123662 (Don't rely on upvars being assigned just because coroutine-closure kind is assigned) - #123665 (Fix typo in `Future::poll()` docs) - #123672 (compiletest: unset `RUSTC_LOG_COLOR`) r? `@ghost` `@rustbot` modify labels: rollup
2 parents 2805aed + ed43ac6 commit ff24ef9

39 files changed

+1691
-1321
lines changed

compiler/rustc_middle/src/ty/sty.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2231,7 +2231,7 @@ impl<'tcx> Ty<'tcx> {
22312231
pub fn tuple_fields(self) -> &'tcx List<Ty<'tcx>> {
22322232
match self.kind() {
22332233
Tuple(args) => args,
2234-
_ => bug!("tuple_fields called on non-tuple"),
2234+
_ => bug!("tuple_fields called on non-tuple: {self:?}"),
22352235
}
22362236
}
22372237

compiler/rustc_mir_transform/src/coroutine/by_move_body.rs

+17-19
Original file line numberDiff line numberDiff line change
@@ -281,14 +281,14 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> {
281281
if place.local == ty::CAPTURE_STRUCT_LOCAL
282282
&& let Some((&mir::ProjectionElem::Field(idx, _), projection)) =
283283
place.projection.split_first()
284-
&& let Some(&(remapped_idx, remapped_ty, needs_deref, additional_projections)) =
284+
&& let Some(&(remapped_idx, remapped_ty, needs_deref, bridging_projections)) =
285285
self.field_remapping.get(&idx)
286286
{
287287
// As noted before, if the parent closure captures a field by value, and
288288
// the child captures a field by ref, then for the by-move body we're
289289
// generating, we also are taking that field by value. Peel off a deref,
290-
// since a layer of reffing has now become redundant.
291-
let final_deref = if needs_deref {
290+
// since a layer of ref'ing has now become redundant.
291+
let final_projections = if needs_deref {
292292
let Some((mir::ProjectionElem::Deref, projection)) = projection.split_first()
293293
else {
294294
bug!(
@@ -302,20 +302,18 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> {
302302
projection
303303
};
304304

305-
// The only thing that should be left is a deref, if the parent captured
306-
// an upvar by-ref.
307-
std::assert_matches::assert_matches!(final_deref, [] | [mir::ProjectionElem::Deref]);
308-
309-
// For all of the additional projections that come out of precise capturing,
310-
// re-apply these projections.
311-
let additional_projections =
312-
additional_projections.iter().map(|elem| match elem.kind {
313-
ProjectionKind::Deref => mir::ProjectionElem::Deref,
314-
ProjectionKind::Field(idx, VariantIdx::ZERO) => {
315-
mir::ProjectionElem::Field(idx, elem.ty)
316-
}
317-
_ => unreachable!("precise captures only through fields and derefs"),
318-
});
305+
// These projections are applied in order to "bridge" the local that we are
306+
// currently transforming *from* the old upvar that the by-ref coroutine used
307+
// to capture *to* the upvar of the parent coroutine-closure. For example, if
308+
// the parent captures `&s` but the child captures `&(s.field)`, then we will
309+
// apply a field projection.
310+
let bridging_projections = bridging_projections.iter().map(|elem| match elem.kind {
311+
ProjectionKind::Deref => mir::ProjectionElem::Deref,
312+
ProjectionKind::Field(idx, VariantIdx::ZERO) => {
313+
mir::ProjectionElem::Field(idx, elem.ty)
314+
}
315+
_ => unreachable!("precise captures only through fields and derefs"),
316+
});
319317

320318
// We start out with an adjusted field index (and ty), representing the
321319
// upvar that we get from our parent closure. We apply any of the additional
@@ -326,8 +324,8 @@ impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> {
326324
projection: self.tcx.mk_place_elems_from_iter(
327325
[mir::ProjectionElem::Field(remapped_idx, remapped_ty)]
328326
.into_iter()
329-
.chain(additional_projections)
330-
.chain(final_deref.iter().copied()),
327+
.chain(bridging_projections)
328+
.chain(final_projections.iter().copied()),
331329
),
332330
};
333331
}

compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,9 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
292292
let kind_ty = args.kind_ty();
293293
let sig = args.coroutine_closure_sig().skip_binder();
294294

295-
let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
295+
let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind()
296+
&& !args.tupled_upvars_ty().is_ty_var()
297+
{
296298
if !closure_kind.extends(goal_kind) {
297299
return Err(NoSolution);
298300
}
@@ -401,7 +403,9 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
401403
let kind_ty = args.kind_ty();
402404
let sig = args.coroutine_closure_sig().skip_binder();
403405
let mut nested = vec![];
404-
let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
406+
let coroutine_ty = if let Some(closure_kind) = kind_ty.to_opt_closure_kind()
407+
&& !args.tupled_upvars_ty().is_ty_var()
408+
{
405409
if !closure_kind.extends(goal_kind) {
406410
return Err(NoSolution);
407411
}

compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
487487
bug!();
488488
};
489489

490+
// Bail if the upvars haven't been constrained.
491+
if tupled_upvars_ty.expect_ty().is_ty_var() {
492+
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS);
493+
}
494+
490495
let Some(closure_kind) = closure_fn_kind_ty.expect_ty().to_opt_closure_kind() else {
491496
// We don't need to worry about the self type being an infer var.
492497
return Err(NoSolution);

compiler/rustc_trait_selection/src/traits/project.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -1601,7 +1601,10 @@ fn confirm_closure_candidate<'cx, 'tcx>(
16011601
// If we know the kind and upvars, use that directly.
16021602
// Otherwise, defer to `AsyncFnKindHelper::Upvars` to delay
16031603
// the projection, like the `AsyncFn*` traits do.
1604-
let output_ty = if let Some(_) = kind_ty.to_opt_closure_kind() {
1604+
let output_ty = if let Some(_) = kind_ty.to_opt_closure_kind()
1605+
// Fall back to projection if upvars aren't constrained
1606+
&& !args.tupled_upvars_ty().is_ty_var()
1607+
{
16051608
sig.to_coroutine_given_kind_and_upvars(
16061609
tcx,
16071610
args.parent_args(),
@@ -1731,7 +1734,10 @@ fn confirm_async_closure_candidate<'cx, 'tcx>(
17311734

17321735
let term = match item_name {
17331736
sym::CallOnceFuture | sym::CallRefFuture => {
1734-
if let Some(closure_kind) = kind_ty.to_opt_closure_kind() {
1737+
if let Some(closure_kind) = kind_ty.to_opt_closure_kind()
1738+
// Fall back to projection if upvars aren't constrained
1739+
&& !args.tupled_upvars_ty().is_ty_var()
1740+
{
17351741
if !closure_kind.extends(goal_kind) {
17361742
bug!("we should not be confirming if the closure kind is not met");
17371743
}

compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs

+28-31
Original file line numberDiff line numberDiff line change
@@ -400,39 +400,36 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
400400
}
401401
}
402402
ty::CoroutineClosure(def_id, args) => {
403+
let args = args.as_coroutine_closure();
403404
let is_const = self.tcx().is_const_fn_raw(def_id);
404-
match self.infcx.closure_kind(self_ty) {
405-
Some(closure_kind) => {
406-
let no_borrows = match self
407-
.infcx
408-
.shallow_resolve(args.as_coroutine_closure().tupled_upvars_ty())
409-
.kind()
410-
{
411-
ty::Tuple(tys) => tys.is_empty(),
412-
ty::Error(_) => false,
413-
_ => bug!("tuple_fields called on non-tuple"),
414-
};
415-
// A coroutine-closure implements `FnOnce` *always*, since it may
416-
// always be called once. It additionally implements `Fn`/`FnMut`
417-
// only if it has no upvars (therefore no borrows from the closure
418-
// that would need to be represented with a lifetime) and if the
419-
// closure kind permits it.
420-
// FIXME(async_closures): Actually, it could also implement `Fn`/`FnMut`
421-
// if it takes all of its upvars by copy, and none by ref. This would
422-
// require us to record a bit more information during upvar analysis.
423-
if no_borrows && closure_kind.extends(kind) {
424-
candidates.vec.push(ClosureCandidate { is_const });
425-
} else if kind == ty::ClosureKind::FnOnce {
426-
candidates.vec.push(ClosureCandidate { is_const });
427-
}
405+
if let Some(closure_kind) = self.infcx.closure_kind(self_ty)
406+
// Ambiguity if upvars haven't been constrained yet
407+
&& !args.tupled_upvars_ty().is_ty_var()
408+
{
409+
let no_borrows = match args.tupled_upvars_ty().kind() {
410+
ty::Tuple(tys) => tys.is_empty(),
411+
ty::Error(_) => false,
412+
_ => bug!("tuple_fields called on non-tuple"),
413+
};
414+
// A coroutine-closure implements `FnOnce` *always*, since it may
415+
// always be called once. It additionally implements `Fn`/`FnMut`
416+
// only if it has no upvars (therefore no borrows from the closure
417+
// that would need to be represented with a lifetime) and if the
418+
// closure kind permits it.
419+
// FIXME(async_closures): Actually, it could also implement `Fn`/`FnMut`
420+
// if it takes all of its upvars by copy, and none by ref. This would
421+
// require us to record a bit more information during upvar analysis.
422+
if no_borrows && closure_kind.extends(kind) {
423+
candidates.vec.push(ClosureCandidate { is_const });
424+
} else if kind == ty::ClosureKind::FnOnce {
425+
candidates.vec.push(ClosureCandidate { is_const });
428426
}
429-
None => {
430-
if kind == ty::ClosureKind::FnOnce {
431-
candidates.vec.push(ClosureCandidate { is_const });
432-
} else {
433-
// This stays ambiguous until kind+upvars are determined.
434-
candidates.ambiguous = true;
435-
}
427+
} else {
428+
if kind == ty::ClosureKind::FnOnce {
429+
candidates.vec.push(ClosureCandidate { is_const });
430+
} else {
431+
// This stays ambiguous until kind+upvars are determined.
432+
candidates.ambiguous = true;
436433
}
437434
}
438435
}

library/alloc/src/boxed/thin.rs

+83-13
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@
44
use crate::alloc::{self, Layout, LayoutError};
55
use core::error::Error;
66
use core::fmt::{self, Debug, Display, Formatter};
7+
#[cfg(not(no_global_oom_handling))]
8+
use core::intrinsics::const_allocate;
79
use core::marker::PhantomData;
810
#[cfg(not(no_global_oom_handling))]
911
use core::marker::Unsize;
10-
use core::mem::{self, SizedTypeProperties};
12+
use core::mem;
13+
#[cfg(not(no_global_oom_handling))]
14+
use core::mem::SizedTypeProperties;
1115
use core::ops::{Deref, DerefMut};
1216
use core::ptr::Pointee;
1317
use core::ptr::{self, NonNull};
@@ -109,9 +113,14 @@ impl<Dyn: ?Sized> ThinBox<Dyn> {
109113
where
110114
T: Unsize<Dyn>,
111115
{
112-
let meta = ptr::metadata(&value as &Dyn);
113-
let ptr = WithOpaqueHeader::new(meta, value);
114-
ThinBox { ptr, _marker: PhantomData }
116+
if mem::size_of::<T>() == 0 {
117+
let ptr = WithOpaqueHeader::new_unsize_zst::<Dyn, T>(value);
118+
ThinBox { ptr, _marker: PhantomData }
119+
} else {
120+
let meta = ptr::metadata(&value as &Dyn);
121+
let ptr = WithOpaqueHeader::new(meta, value);
122+
ThinBox { ptr, _marker: PhantomData }
123+
}
115124
}
116125
}
117126

@@ -200,6 +209,16 @@ impl WithOpaqueHeader {
200209
Self(ptr.0)
201210
}
202211

212+
#[cfg(not(no_global_oom_handling))]
213+
fn new_unsize_zst<Dyn, T>(value: T) -> Self
214+
where
215+
Dyn: ?Sized,
216+
T: Unsize<Dyn>,
217+
{
218+
let ptr = WithHeader::<<Dyn as Pointee>::Metadata>::new_unsize_zst::<Dyn, T>(value);
219+
Self(ptr.0)
220+
}
221+
203222
fn try_new<H, T>(header: H, value: T) -> Result<Self, core::alloc::AllocError> {
204223
WithHeader::try_new(header, value).map(|ptr| Self(ptr.0))
205224
}
@@ -288,6 +307,58 @@ impl<H> WithHeader<H> {
288307
}
289308
}
290309

310+
// `Dyn` is `?Sized` type like `[u32]`, and `T` is ZST type like `[u32; 0]`.
311+
#[cfg(not(no_global_oom_handling))]
312+
fn new_unsize_zst<Dyn, T>(value: T) -> WithHeader<H>
313+
where
314+
Dyn: Pointee<Metadata = H> + ?Sized,
315+
T: Unsize<Dyn>,
316+
{
317+
assert!(mem::size_of::<T>() == 0);
318+
319+
const fn max(a: usize, b: usize) -> usize {
320+
if a > b { a } else { b }
321+
}
322+
323+
// Compute a pointer to the right metadata. This will point to the beginning
324+
// of the header, past the padding, so the assigned type makes sense.
325+
// It also ensures that the address at the end of the header is sufficiently
326+
// aligned for T.
327+
let alloc: &<Dyn as Pointee>::Metadata = const {
328+
// FIXME: just call `WithHeader::alloc_layout` with size reset to 0.
329+
// Currently that's blocked on `Layout::extend` not being `const fn`.
330+
331+
let alloc_align =
332+
max(mem::align_of::<T>(), mem::align_of::<<Dyn as Pointee>::Metadata>());
333+
334+
let alloc_size =
335+
max(mem::align_of::<T>(), mem::size_of::<<Dyn as Pointee>::Metadata>());
336+
337+
unsafe {
338+
// SAFETY: align is power of two because it is the maximum of two alignments.
339+
let alloc: *mut u8 = const_allocate(alloc_size, alloc_align);
340+
341+
let metadata_offset =
342+
alloc_size.checked_sub(mem::size_of::<<Dyn as Pointee>::Metadata>()).unwrap();
343+
// SAFETY: adding offset within the allocation.
344+
let metadata_ptr: *mut <Dyn as Pointee>::Metadata =
345+
alloc.add(metadata_offset).cast();
346+
// SAFETY: `*metadata_ptr` is within the allocation.
347+
metadata_ptr.write(ptr::metadata::<Dyn>(ptr::dangling::<T>() as *const Dyn));
348+
349+
// SAFETY: we have just written the metadata.
350+
&*(metadata_ptr)
351+
}
352+
};
353+
354+
// SAFETY: `alloc` points to `<Dyn as Pointee>::Metadata`, so addition stays in-bounds.
355+
let value_ptr =
356+
unsafe { (alloc as *const <Dyn as Pointee>::Metadata).add(1) }.cast::<T>().cast_mut();
357+
debug_assert!(value_ptr.is_aligned());
358+
mem::forget(value);
359+
WithHeader(NonNull::new(value_ptr.cast()).unwrap(), PhantomData)
360+
}
361+
291362
// Safety:
292363
// - Assumes that either `value` can be dereferenced, or is the
293364
// `NonNull::dangling()` we use when both `T` and `H` are ZSTs.
@@ -300,20 +371,19 @@ impl<H> WithHeader<H> {
300371

301372
impl<H> Drop for DropGuard<H> {
302373
fn drop(&mut self) {
374+
// All ZST are allocated statically.
375+
if self.value_layout.size() == 0 {
376+
return;
377+
}
378+
303379
unsafe {
304380
// SAFETY: Layout must have been computable if we're in drop
305381
let (layout, value_offset) =
306382
WithHeader::<H>::alloc_layout(self.value_layout).unwrap_unchecked();
307383

308-
// Note: Don't deallocate if the layout size is zero, because the pointer
309-
// didn't come from the allocator.
310-
if layout.size() != 0 {
311-
alloc::dealloc(self.ptr.as_ptr().sub(value_offset), layout);
312-
} else {
313-
debug_assert!(
314-
value_offset == 0 && H::IS_ZST && self.value_layout.size() == 0
315-
);
316-
}
384+
// Since we only allocate for non-ZSTs, the layout size cannot be zero.
385+
debug_assert!(layout.size() != 0);
386+
alloc::dealloc(self.ptr.as_ptr().sub(value_offset), layout);
317387
}
318388
}
319389
}

library/alloc/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,10 @@
108108
#![feature(const_box)]
109109
#![feature(const_cow_is_borrowed)]
110110
#![feature(const_eval_select)]
111+
#![feature(const_heap)]
111112
#![feature(const_maybe_uninit_as_mut_ptr)]
112113
#![feature(const_maybe_uninit_write)]
114+
#![feature(const_option)]
113115
#![feature(const_pin)]
114116
#![feature(const_refs_to_cell)]
115117
#![feature(const_size_of_val)]

library/core/src/future/future.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ pub trait Future {
8181
/// An implementation of `poll` should strive to return quickly, and should
8282
/// not block. Returning quickly prevents unnecessarily clogging up
8383
/// threads or event loops. If it is known ahead of time that a call to
84-
/// `poll` may end up taking awhile, the work should be offloaded to a
84+
/// `poll` may end up taking a while, the work should be offloaded to a
8585
/// thread pool (or something similar) to ensure that `poll` can return
8686
/// quickly.
8787
///

src/librustdoc/clean/auto_trait.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ fn clean_param_env<'tcx>(
168168

169169
// FIXME(#111101): Incorporate the explicit predicates of the item here...
170170
let item_predicates: FxIndexSet<_> =
171-
tcx.predicates_of(item_def_id).predicates.iter().map(|(pred, _)| pred).collect();
171+
tcx.param_env(item_def_id).caller_bounds().iter().collect();
172172
let where_predicates = param_env
173173
.caller_bounds()
174174
.iter()

src/tools/compiletest/src/header.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ impl TestProps {
266266
aux_crates: vec![],
267267
revisions: vec![],
268268
rustc_env: vec![("RUSTC_ICE".to_string(), "0".to_string())],
269-
unset_rustc_env: vec![],
269+
unset_rustc_env: vec![("RUSTC_LOG_COLOR".to_string())],
270270
exec_env: vec![],
271271
unset_exec_env: vec![],
272272
build_aux_docs: false,

src/tools/compiletest/src/runtest.rs

+13
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,19 @@ impl<'test> TestCx<'test> {
752752
Lazy::new(|| Regex::new(r"(?m:^)(?<prefix>(?: \|)+ Branch \()[0-9]+:").unwrap());
753753
let coverage = BRANCH_LINE_NUMBER_RE.replace_all(&coverage, "${prefix}LL:");
754754

755+
// ` |---> MC/DC Decision Region (1:30) to (2:` => ` |---> MC/DC Decision Region (LL:30) to (LL:`
756+
static MCDC_DECISION_LINE_NUMBER_RE: Lazy<Regex> = Lazy::new(|| {
757+
Regex::new(r"(?m:^)(?<prefix>(?: \|)+---> MC/DC Decision Region \()[0-9]+:(?<middle>[0-9]+\) to \()[0-9]+:").unwrap()
758+
});
759+
let coverage =
760+
MCDC_DECISION_LINE_NUMBER_RE.replace_all(&coverage, "${prefix}LL:${middle}LL:");
761+
762+
// ` | Condition C1 --> (1:` => ` | Condition C1 --> (LL:`
763+
static MCDC_CONDITION_LINE_NUMBER_RE: Lazy<Regex> = Lazy::new(|| {
764+
Regex::new(r"(?m:^)(?<prefix>(?: \|)+ Condition C[0-9]+ --> \()[0-9]+:").unwrap()
765+
});
766+
let coverage = MCDC_CONDITION_LINE_NUMBER_RE.replace_all(&coverage, "${prefix}LL:");
767+
755768
coverage.into_owned()
756769
}
757770

0 commit comments

Comments
 (0)