Skip to content

Rollup of 6 pull requests #130016

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Sep 6, 2024
Merged
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
1 change: 0 additions & 1 deletion RELEASES.md
Original file line number Diff line number Diff line change
@@ -34,7 +34,6 @@ Compiler
- [Add Tier 3 `std` Xtensa targets:](https://github.com/rust-lang/rust/pull/126380/) `xtensa-esp32-espidf`, `xtensa-esp32s2-espidf`, `xtensa-esp32s3-espidf`
- [Add Tier 3 i686 Redox OS target:](https://github.com/rust-lang/rust/pull/126192/) `i686-unknown-redox`
- [Promote `arm64ec-pc-windows-msvc` to Tier 2.](https://github.com/rust-lang/rust/pull/126039/)
- [Promote `wasm32-wasip2` to Tier 2.](https://github.com/rust-lang/rust/pull/126967/)
- [Promote `loongarch64-unknown-linux-musl` to Tier 2 with host tools.](https://github.com/rust-lang/rust/pull/126298/)
- [Enable full tools and profiler for LoongArch Linux targets.](https://github.com/rust-lang/rust/pull/127078/)
- [Unconditionally warn on usage of `wasm32-wasi`.](https://github.com/rust-lang/rust/pull/126662/) (see compatibility note below)
3 changes: 2 additions & 1 deletion compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
Original file line number Diff line number Diff line change
@@ -662,9 +662,10 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, '_, 'tcx> {
// `&dyn Trait`
ty::Ref(_, ty, _) if ty.is_trait() => true,
// `Box<dyn Trait>`
_ if ty.is_box() && ty.boxed_ty().is_trait() => {
_ if ty.boxed_ty().is_some_and(Ty::is_trait) => {
true
}

// `dyn Trait`
_ if ty.is_trait() => true,
// Anything else.
4 changes: 2 additions & 2 deletions compiler/rustc_borrowck/src/diagnostics/mod.rs
Original file line number Diff line number Diff line change
@@ -345,9 +345,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
variant_index: Option<VariantIdx>,
including_tuple_field: IncludingTupleField,
) -> Option<String> {
if ty.is_box() {
if let Some(boxed_ty) = ty.boxed_ty() {
// If the type is a box, the field is described from the boxed type
self.describe_field_from_ty(ty.boxed_ty(), field, variant_index, including_tuple_field)
self.describe_field_from_ty(boxed_ty, field, variant_index, including_tuple_field)
} else {
match *ty.kind() {
ty::Adt(def, _) => {
71 changes: 64 additions & 7 deletions compiler/rustc_borrowck/src/type_check/mod.rs
Original file line number Diff line number Diff line change
@@ -1979,19 +1979,76 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {

match cast_kind {
CastKind::PointerCoercion(PointerCoercion::ReifyFnPointer) => {
let fn_sig = op.ty(body, tcx).fn_sig(tcx);
let src_sig = op.ty(body, tcx).fn_sig(tcx);

// HACK: This shouldn't be necessary... We can remove this when we actually
// get binders with where clauses, then elaborate implied bounds into that
// binder, and implement a higher-ranked subtyping algorithm that actually
// respects these implied bounds.
//
// This protects against the case where we are casting from a higher-ranked
// fn item to a non-higher-ranked fn pointer, where the cast throws away
// implied bounds that would've needed to be checked at the call site. This
// only works when we're casting to a non-higher-ranked fn ptr, since
// placeholders in the target signature could have untracked implied
// bounds, resulting in incorrect errors.
//
// We check that this signature is WF before subtyping the signature with
// the target fn sig.
if src_sig.has_bound_regions()
&& let ty::FnPtr(target_fn_tys, target_hdr) = *ty.kind()
&& let target_sig = target_fn_tys.with(target_hdr)
&& let Some(target_sig) = target_sig.no_bound_vars()
{
let src_sig = self.infcx.instantiate_binder_with_fresh_vars(
span,
BoundRegionConversionTime::HigherRankedType,
src_sig,
);
let src_ty = Ty::new_fn_ptr(self.tcx(), ty::Binder::dummy(src_sig));
self.prove_predicate(
ty::ClauseKind::WellFormed(src_ty.into()),
location.to_locations(),
ConstraintCategory::Cast { unsize_to: None },
);

let src_ty = self.normalize(src_ty, location);
if let Err(terr) = self.sub_types(
src_ty,
*ty,
location.to_locations(),
ConstraintCategory::Cast { unsize_to: None },
) {
span_mirbug!(
self,
rvalue,
"equating {:?} with {:?} yields {:?}",
target_sig,
src_sig,
terr
);
};
}

let src_ty = Ty::new_fn_ptr(tcx, src_sig);
// HACK: We want to assert that the signature of the source fn is
// well-formed, because we don't enforce that via the WF of FnDef
// types normally. This should be removed when we improve the tracking
// of implied bounds of fn signatures.
self.prove_predicate(
ty::ClauseKind::WellFormed(src_ty.into()),
location.to_locations(),
ConstraintCategory::Cast { unsize_to: None },
);

// The type that we see in the fcx is like
// `foo::<'a, 'b>`, where `foo` is the path to a
// function definition. When we extract the
// signature, it comes from the `fn_sig` query,
// and hence may contain unnormalized results.
let fn_sig = self.normalize(fn_sig, location);

let ty_fn_ptr_from = Ty::new_fn_ptr(tcx, fn_sig);

let src_ty = self.normalize(src_ty, location);
if let Err(terr) = self.sub_types(
ty_fn_ptr_from,
src_ty,
*ty,
location.to_locations(),
ConstraintCategory::Cast { unsize_to: None },
@@ -2000,7 +2057,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
self,
rvalue,
"equating {:?} with {:?} yields {:?}",
ty_fn_ptr_from,
src_ty,
ty,
terr
);
2 changes: 1 addition & 1 deletion compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
Original file line number Diff line number Diff line change
@@ -456,7 +456,7 @@ pub(crate) fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) ->
if def.is_box()
&& args.get(1).map_or(true, |arg| cx.layout_of(arg.expect_ty()).is_1zst()) =>
{
build_pointer_or_reference_di_node(cx, t, t.boxed_ty(), unique_type_id)
build_pointer_or_reference_di_node(cx, t, t.expect_boxed_ty(), unique_type_id)
}
ty::FnDef(..) | ty::FnPtr(..) => build_subroutine_type_di_node(cx, unique_type_id),
ty::Closure(..) => build_closure_env_di_node(cx, unique_type_id),
2 changes: 1 addition & 1 deletion compiler/rustc_const_eval/src/interpret/call.rs
Original file line number Diff line number Diff line change
@@ -189,7 +189,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
ty::Ref(_, ty, _) => *ty,
ty::RawPtr(ty, _) => *ty,
// We only accept `Box` with the default allocator.
_ if ty.is_box_global(*self.tcx) => ty.boxed_ty(),
_ if ty.is_box_global(*self.tcx) => ty.expect_boxed_ty(),
_ => return Ok(None),
}))
};
Original file line number Diff line number Diff line change
@@ -63,8 +63,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// Instead, the problem is that the array-into_iter hack will no longer
// apply in Rust 2021.
(ARRAY_INTO_ITER, "2021")
} else if self_ty.is_box()
&& self_ty.boxed_ty().is_slice()
} else if self_ty.boxed_ty().is_some_and(Ty::is_slice)
&& !span.at_least_rust_2024()
{
// In this case, it wasn't really a prelude addition that was the problem.
3 changes: 1 addition & 2 deletions compiler/rustc_hir_typeck/src/method/probe.rs
Original file line number Diff line number Diff line change
@@ -1485,8 +1485,7 @@ impl<'a, 'tcx> ProbeContext<'a, 'tcx> {

// Some trait methods are excluded for boxed slices before 2024.
// (`boxed_slice.into_iter()` wants a slice iterator for compatibility.)
if self_ty.is_box()
&& self_ty.boxed_ty().is_slice()
if self_ty.boxed_ty().is_some_and(Ty::is_slice)
&& !method_name.span.at_least_rust_2024()
{
let trait_def = self.tcx.trait_def(poly_trait_ref.def_id());
7 changes: 2 additions & 5 deletions compiler/rustc_lint/src/shadowed_into_iter.rs
Original file line number Diff line number Diff line change
@@ -94,12 +94,9 @@ impl<'tcx> LateLintPass<'tcx> for ShadowedIntoIter {
fn is_ref_to_array(ty: Ty<'_>) -> bool {
if let ty::Ref(_, pointee_ty, _) = *ty.kind() { pointee_ty.is_array() } else { false }
}
fn is_boxed_slice(ty: Ty<'_>) -> bool {
ty.is_box() && ty.boxed_ty().is_slice()
}
fn is_ref_to_boxed_slice(ty: Ty<'_>) -> bool {
if let ty::Ref(_, pointee_ty, _) = *ty.kind() {
is_boxed_slice(pointee_ty)
pointee_ty.boxed_ty().is_some_and(Ty::is_slice)
} else {
false
}
@@ -119,7 +116,7 @@ impl<'tcx> LateLintPass<'tcx> for ShadowedIntoIter {
.iter()
.copied()
.take_while(|ty| !is_ref_to_boxed_slice(*ty))
.position(|ty| is_boxed_slice(ty))
.position(|ty| ty.boxed_ty().is_some_and(Ty::is_slice))
{
(BOXED_SLICE_INTO_ITER, "Box<[T]>", "2024", idx == 0)
} else {
6 changes: 4 additions & 2 deletions compiler/rustc_lint/src/types.rs
Original file line number Diff line number Diff line change
@@ -1304,8 +1304,10 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {

match *ty.kind() {
ty::Adt(def, args) => {
if def.is_box() && matches!(self.mode, CItemKind::Definition) {
if ty.boxed_ty().is_sized(tcx, self.cx.param_env) {
if let Some(boxed) = ty.boxed_ty()
&& matches!(self.mode, CItemKind::Definition)
{
if boxed.is_sized(tcx, self.cx.param_env) {
return FfiSafe;
} else {
return FfiUnsafe {
5 changes: 2 additions & 3 deletions compiler/rustc_lint/src/unused.rs
Original file line number Diff line number Diff line change
@@ -283,9 +283,8 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
}

match *ty.kind() {
ty::Adt(..) if ty.is_box() => {
let boxed_ty = ty.boxed_ty();
is_ty_must_use(cx, boxed_ty, expr, span)
ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => {
is_ty_must_use(cx, boxed, expr, span)
.map(|inner| MustUsePath::Boxed(Box::new(inner)))
}
ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::Pin) => {
6 changes: 4 additions & 2 deletions compiler/rustc_middle/src/ty/layout.rs
Original file line number Diff line number Diff line change
@@ -1075,11 +1075,13 @@ where
// the raw pointer, so size and align are set to the boxed type, but `pointee.safe`
// will still be `None`.
if let Some(ref mut pointee) = result {
if offset.bytes() == 0 && this.ty.is_box() {
if offset.bytes() == 0
&& let Some(boxed_ty) = this.ty.boxed_ty()
{
debug_assert!(pointee.safe.is_none());
let optimize = tcx.sess.opts.optimize != OptLevel::No;
pointee.safe = Some(PointerKind::Box {
unpin: optimize && this.ty.boxed_ty().is_unpin(tcx, cx.param_env()),
unpin: optimize && boxed_ty.is_unpin(tcx, cx.param_env()),
global: this.ty.is_box_global(tcx),
});
}
15 changes: 10 additions & 5 deletions compiler/rustc_middle/src/ty/sty.rs
Original file line number Diff line number Diff line change
@@ -1170,14 +1170,19 @@ impl<'tcx> Ty<'tcx> {
}
}

/// Panics if called on any type other than `Box<T>`.
pub fn boxed_ty(self) -> Ty<'tcx> {
pub fn boxed_ty(self) -> Option<Ty<'tcx>> {
match self.kind() {
Adt(def, args) if def.is_box() => args.type_at(0),
_ => bug!("`boxed_ty` is called on non-box type {:?}", self),
Adt(def, args) if def.is_box() => Some(args.type_at(0)),
_ => None,
}
}

/// Panics if called on any type other than `Box<T>`.
pub fn expect_boxed_ty(self) -> Ty<'tcx> {
self.boxed_ty()
.unwrap_or_else(|| bug!("`expect_boxed_ty` is called on non-box type {:?}", self))
}

/// A scalar type is one that denotes an atomic datum, with no sub-components.
/// (A RawPtr is scalar because it represents a non-managed pointer, so its
/// contents are abstract to rustc.)
@@ -1323,7 +1328,7 @@ impl<'tcx> Ty<'tcx> {
/// Some types -- notably unsafe ptrs -- can only be dereferenced explicitly.
pub fn builtin_deref(self, explicit: bool) -> Option<Ty<'tcx>> {
match *self.kind() {
Adt(def, _) if def.is_box() => Some(self.boxed_ty()),
_ if let Some(boxed) = self.boxed_ty() => Some(boxed),
Ref(_, ty, _) => Some(ty),
RawPtr(ty, _) if explicit => Some(ty),
_ => None,
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/util.rs
Original file line number Diff line number Diff line change
@@ -1628,7 +1628,7 @@ impl<'tcx> ExplicitSelf<'tcx> {
_ if is_self_ty(self_arg_ty) => ByValue,
ty::Ref(region, ty, mutbl) if is_self_ty(ty) => ByReference(region, mutbl),
ty::RawPtr(ty, mutbl) if is_self_ty(ty) => ByRawPointer(mutbl),
ty::Adt(def, _) if def.is_box() && is_self_ty(self_arg_ty.boxed_ty()) => ByBox,
_ if self_arg_ty.boxed_ty().is_some_and(is_self_ty) => ByBox,
_ => Other,
}
}
34 changes: 21 additions & 13 deletions compiler/rustc_mir_transform/src/coverage/spans.rs
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@ use std::collections::VecDeque;
use rustc_data_structures::captures::Captures;
use rustc_data_structures::fx::FxHashSet;
use rustc_middle::mir;
use rustc_span::Span;
use rustc_span::{DesugaringKind, ExpnKind, MacroKind, Span};
use tracing::{debug, debug_span, instrument};

use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
@@ -25,7 +25,7 @@ pub(super) fn extract_refined_covspans(

// First, perform the passes that need macro information.
covspans.sort_by(|a, b| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb));
remove_unwanted_macro_spans(&mut covspans);
remove_unwanted_expansion_spans(&mut covspans);
split_visible_macro_spans(&mut covspans);

// We no longer need the extra information in `SpanFromMir`, so convert to `Covspan`.
@@ -76,18 +76,24 @@ pub(super) fn extract_refined_covspans(
/// invocation, which is unhelpful. Keeping only the first such span seems to
/// give better mappings, so remove the others.
///
/// Similarly, `await` expands to a branch on the discriminant of `Poll`, which
/// leads to incorrect coverage if the `Future` is immediately ready (#98712).
///
/// (The input spans should be sorted in BCB dominator order, so that the
/// retained "first" span is likely to dominate the others.)
fn remove_unwanted_macro_spans(covspans: &mut Vec<SpanFromMir>) {
let mut seen_macro_spans = FxHashSet::default();
fn remove_unwanted_expansion_spans(covspans: &mut Vec<SpanFromMir>) {
let mut deduplicated_spans = FxHashSet::default();

covspans.retain(|covspan| {
// Ignore (retain) non-macro-expansion spans.
if covspan.visible_macro.is_none() {
return true;
match covspan.expn_kind {
// Retain only the first await-related or macro-expanded covspan with this span.
Some(ExpnKind::Desugaring(kind)) if kind == DesugaringKind::Await => {
deduplicated_spans.insert(covspan.span)
}
Some(ExpnKind::Macro(MacroKind::Bang, _)) => deduplicated_spans.insert(covspan.span),
// Ignore (retain) other spans.
_ => true,
}

// Retain only the first macro-expanded covspan with this span.
seen_macro_spans.insert(covspan.span)
});
}

@@ -99,7 +105,9 @@ fn split_visible_macro_spans(covspans: &mut Vec<SpanFromMir>) {
let mut extra_spans = vec![];

covspans.retain(|covspan| {
let Some(visible_macro) = covspan.visible_macro else { return true };
let Some(ExpnKind::Macro(MacroKind::Bang, visible_macro)) = covspan.expn_kind else {
return true;
};

let split_len = visible_macro.as_str().len() as u32 + 1;
let (before, after) = covspan.span.split_at(split_len);
@@ -111,8 +119,8 @@ fn split_visible_macro_spans(covspans: &mut Vec<SpanFromMir>) {
return true;
}

extra_spans.push(SpanFromMir::new(before, covspan.visible_macro, covspan.bcb));
extra_spans.push(SpanFromMir::new(after, covspan.visible_macro, covspan.bcb));
extra_spans.push(SpanFromMir::new(before, covspan.expn_kind.clone(), covspan.bcb));
extra_spans.push(SpanFromMir::new(after, covspan.expn_kind.clone(), covspan.bcb));
false // Discard the original covspan that we just split.
});

22 changes: 11 additions & 11 deletions compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs
Original file line number Diff line number Diff line change
@@ -3,13 +3,13 @@ use rustc_middle::mir::coverage::CoverageKind;
use rustc_middle::mir::{
self, FakeReadCause, Statement, StatementKind, Terminator, TerminatorKind,
};
use rustc_span::{Span, Symbol};
use rustc_span::{ExpnKind, Span};

use crate::coverage::graph::{
BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB,
};
use crate::coverage::spans::Covspan;
use crate::coverage::unexpand::unexpand_into_body_span_with_visible_macro;
use crate::coverage::unexpand::unexpand_into_body_span_with_expn_kind;
use crate::coverage::ExtractedHirInfo;

pub(crate) struct ExtractedCovspans {
@@ -60,17 +60,17 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>(
let data = &mir_body[bb];

let unexpand = move |expn_span| {
unexpand_into_body_span_with_visible_macro(expn_span, body_span)
unexpand_into_body_span_with_expn_kind(expn_span, body_span)
// Discard any spans that fill the entire body, because they tend
// to represent compiler-inserted code, e.g. implicitly returning `()`.
.filter(|(span, _)| !span.source_equal(body_span))
};

let mut extract_statement_span = |statement| {
let expn_span = filtered_statement_span(statement)?;
let (span, visible_macro) = unexpand(expn_span)?;
let (span, expn_kind) = unexpand(expn_span)?;

initial_covspans.push(SpanFromMir::new(span, visible_macro, bcb));
initial_covspans.push(SpanFromMir::new(span, expn_kind, bcb));
Some(())
};
for statement in data.statements.iter() {
@@ -79,9 +79,9 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>(

let mut extract_terminator_span = |terminator| {
let expn_span = filtered_terminator_span(terminator)?;
let (span, visible_macro) = unexpand(expn_span)?;
let (span, expn_kind) = unexpand(expn_span)?;

initial_covspans.push(SpanFromMir::new(span, visible_macro, bcb));
initial_covspans.push(SpanFromMir::new(span, expn_kind, bcb));
Some(())
};
extract_terminator_span(data.terminator());
@@ -214,7 +214,7 @@ pub(crate) struct SpanFromMir {
/// With the exception of `fn_sig_span`, this should always be contained
/// within `body_span`.
pub(crate) span: Span,
pub(crate) visible_macro: Option<Symbol>,
pub(crate) expn_kind: Option<ExpnKind>,
pub(crate) bcb: BasicCoverageBlock,
}

@@ -223,12 +223,12 @@ impl SpanFromMir {
Self::new(fn_sig_span, None, START_BCB)
}

pub(crate) fn new(span: Span, visible_macro: Option<Symbol>, bcb: BasicCoverageBlock) -> Self {
Self { span, visible_macro, bcb }
pub(crate) fn new(span: Span, expn_kind: Option<ExpnKind>, bcb: BasicCoverageBlock) -> Self {
Self { span, expn_kind, bcb }
}

pub(crate) fn into_covspan(self) -> Covspan {
let Self { span, visible_macro: _, bcb } = self;
let Self { span, expn_kind: _, bcb } = self;
Covspan { span, bcb }
}
}
15 changes: 5 additions & 10 deletions compiler/rustc_mir_transform/src/coverage/unexpand.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use rustc_span::{ExpnKind, MacroKind, Span, Symbol};
use rustc_span::{ExpnKind, Span};

/// Walks through the expansion ancestors of `original_span` to find a span that
/// is contained in `body_span` and has the same [syntax context] as `body_span`.
@@ -13,20 +13,15 @@ pub(crate) fn unexpand_into_body_span(original_span: Span, body_span: Span) -> O
///
/// If the returned span represents a bang-macro invocation (e.g. `foo!(..)`),
/// the returned symbol will be the name of that macro (e.g. `foo`).
pub(crate) fn unexpand_into_body_span_with_visible_macro(
pub(crate) fn unexpand_into_body_span_with_expn_kind(
original_span: Span,
body_span: Span,
) -> Option<(Span, Option<Symbol>)> {
) -> Option<(Span, Option<ExpnKind>)> {
let (span, prev) = unexpand_into_body_span_with_prev(original_span, body_span)?;

let visible_macro = prev
.map(|prev| match prev.ctxt().outer_expn_data().kind {
ExpnKind::Macro(MacroKind::Bang, name) => Some(name),
_ => None,
})
.flatten();
let expn_kind = prev.map(|prev| prev.ctxt().outer_expn_data().kind);

Some((span, visible_macro))
Some((span, expn_kind))
}

/// Walks through the expansion ancestors of `original_span` to find a span that
12 changes: 8 additions & 4 deletions compiler/rustc_mir_transform/src/elaborate_box_derefs.rs
Original file line number Diff line number Diff line change
@@ -62,11 +62,13 @@ impl<'tcx, 'a> MutVisitor<'tcx> for ElaborateBoxDerefVisitor<'tcx, 'a> {
let base_ty = self.local_decls[place.local].ty;

// Derefer ensures that derefs are always the first projection
if place.projection.first() == Some(&PlaceElem::Deref) && base_ty.is_box() {
if let Some(PlaceElem::Deref) = place.projection.first()
&& let Some(boxed_ty) = base_ty.boxed_ty()
{
let source_info = self.local_decls[place.local].source_info;

let (unique_ty, nonnull_ty, ptr_ty) =
build_ptr_tys(tcx, base_ty.boxed_ty(), self.unique_did, self.nonnull_did);
build_ptr_tys(tcx, boxed_ty, self.unique_did, self.nonnull_did);

let ptr_local = self.patch.new_temp(ptr_ty, source_info.span);

@@ -120,13 +122,15 @@ impl<'tcx> crate::MirPass<'tcx> for ElaborateBoxDerefs {
for (base, elem) in place.iter_projections() {
let base_ty = base.ty(&body.local_decls, tcx).ty;

if elem == PlaceElem::Deref && base_ty.is_box() {
if let PlaceElem::Deref = elem
&& let Some(boxed_ty) = base_ty.boxed_ty()
{
// Clone the projections before us, since now we need to mutate them.
let new_projections =
new_projections.get_or_insert_with(|| base.projection.to_vec());

let (unique_ty, nonnull_ty, ptr_ty) =
build_ptr_tys(tcx, base_ty.boxed_ty(), unique_did, nonnull_did);
build_ptr_tys(tcx, boxed_ty, unique_did, nonnull_did);

new_projections.extend_from_slice(&build_projection(
unique_ty, nonnull_ty, ptr_ty,
7 changes: 5 additions & 2 deletions compiler/rustc_monomorphize/src/collector.rs
Original file line number Diff line number Diff line change
@@ -1041,8 +1041,11 @@ fn find_vtable_types_for_unsizing<'tcx>(
match (source_ty.kind(), target_ty.kind()) {
(&ty::Ref(_, a, _), &ty::Ref(_, b, _) | &ty::RawPtr(b, _))
| (&ty::RawPtr(a, _), &ty::RawPtr(b, _)) => ptr_vtable(a, b),
(&ty::Adt(def_a, _), &ty::Adt(def_b, _)) if def_a.is_box() && def_b.is_box() => {
ptr_vtable(source_ty.boxed_ty(), target_ty.boxed_ty())
(_, _)
if let Some(source_boxed) = source_ty.boxed_ty()
&& let Some(target_boxed) = target_ty.boxed_ty() =>
{
ptr_vtable(source_boxed, target_boxed)
}

// T as dyn* Trait
2 changes: 2 additions & 0 deletions compiler/rustc_monomorphize/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// tidy-alphabetical-start
#![feature(array_windows)]
#![feature(if_let_guard)]
#![feature(let_chains)]
#![warn(unreachable_pub)]
// tidy-alphabetical-end

Original file line number Diff line number Diff line change
@@ -348,8 +348,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}
if let Some(ty::error::ExpectedFound { found, .. }) = exp_found
&& ty.is_box()
&& ty.boxed_ty() == found
&& ty.boxed_ty() == Some(found)
&& let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
{
err.span_suggestion(
2 changes: 1 addition & 1 deletion library/std/src/ffi/os_str.rs
Original file line number Diff line number Diff line change
@@ -852,7 +852,7 @@ impl OsStr {

/// Converts an `OsStr` to a <code>[Cow]<[str]></code>.
///
/// Any non-Unicode sequences are replaced with
/// Any non-UTF-8 sequences are replaced with
/// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD].
///
/// [U+FFFD]: crate::char::REPLACEMENT_CHARACTER
2 changes: 1 addition & 1 deletion library/std/src/path.rs
Original file line number Diff line number Diff line change
@@ -2200,7 +2200,7 @@ impl Path {

/// Converts a `Path` to a [`Cow<str>`].
///
/// Any non-Unicode sequences are replaced with
/// Any non-UTF-8 sequences are replaced with
/// [`U+FFFD REPLACEMENT CHARACTER`][U+FFFD].
///
/// [U+FFFD]: super::char::REPLACEMENT_CHARACTER
2 changes: 1 addition & 1 deletion src/bootstrap/src/core/build_steps/check.rs
Original file line number Diff line number Diff line change
@@ -228,7 +228,7 @@ impl Step for Rustc {
self.override_build_kind.unwrap_or(builder.kind),
);

rustc_cargo(builder, &mut cargo, target, &compiler);
rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates);

// For ./x.py clippy, don't run with --all-targets because
// linting tests and benchmarks can produce very noisy results
2 changes: 1 addition & 1 deletion src/bootstrap/src/core/build_steps/clippy.rs
Original file line number Diff line number Diff line change
@@ -197,7 +197,7 @@ impl Step for Rustc {
Kind::Clippy,
);

rustc_cargo(builder, &mut cargo, target, &compiler);
rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates);

// Explicitly pass -p for all compiler crates -- this will force cargo
// to also lint the tests/benches/examples for these crates, rather
5 changes: 3 additions & 2 deletions src/bootstrap/src/core/build_steps/compile.rs
Original file line number Diff line number Diff line change
@@ -988,7 +988,7 @@ impl Step for Rustc {
Kind::Build,
);

rustc_cargo(builder, &mut cargo, target, &compiler);
rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates);

// NB: all RUSTFLAGS should be added to `rustc_cargo()` so they will be
// consistently applied by check/doc/test modes too.
@@ -1047,10 +1047,11 @@ pub fn rustc_cargo(
cargo: &mut Cargo,
target: TargetSelection,
compiler: &Compiler,
crates: &[String],
) {
cargo
.arg("--features")
.arg(builder.rustc_features(builder.kind, target))
.arg(builder.rustc_features(builder.kind, target, crates))
.arg("--manifest-path")
.arg(builder.src.join("compiler/rustc/Cargo.toml"));

2 changes: 1 addition & 1 deletion src/bootstrap/src/core/build_steps/doc.rs
Original file line number Diff line number Diff line change
@@ -826,7 +826,7 @@ impl Step for Rustc {
// see https://github.com/rust-lang/rust/pull/122066#issuecomment-1983049222
// cargo.rustdocflag("--generate-link-to-definition");

compile::rustc_cargo(builder, &mut cargo, target, &compiler);
compile::rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates);
cargo.arg("-Zskip-rustdoc-fingerprint");

// Only include compiler crates, no dependencies of those, such as `libc`.
2 changes: 1 addition & 1 deletion src/bootstrap/src/core/build_steps/test.rs
Original file line number Diff line number Diff line change
@@ -2690,7 +2690,7 @@ impl Step for Crate {
}
}
Mode::Rustc => {
compile::rustc_cargo(builder, &mut cargo, target, &compiler);
compile::rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates);
}
_ => panic!("can only test libraries"),
};
10 changes: 9 additions & 1 deletion src/bootstrap/src/core/metadata.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::collections::BTreeMap;
use std::path::PathBuf;

use serde_derive::Deserialize;
@@ -21,6 +22,7 @@ struct Package {
manifest_path: String,
dependencies: Vec<Dependency>,
targets: Vec<Target>,
features: BTreeMap<String, Vec<String>>,
}

/// For more information, see the output of
@@ -51,7 +53,13 @@ pub fn build(build: &mut Build) {
.map(|dep| dep.name)
.collect();
let has_lib = package.targets.iter().any(|t| t.kind.iter().any(|k| k == "lib"));
let krate = Crate { name: name.clone(), deps, path, has_lib };
let krate = Crate {
name: name.clone(),
deps,
path,
has_lib,
features: package.features.keys().cloned().collect(),
};
let relative_path = krate.local_path(build);
build.crates.insert(name.clone(), krate);
let existing_path = build.crate_paths.insert(relative_path, name);
19 changes: 14 additions & 5 deletions src/bootstrap/src/lib.rs
Original file line number Diff line number Diff line change
@@ -183,6 +183,7 @@ struct Crate {
deps: HashSet<String>,
path: PathBuf,
has_lib: bool,
features: Vec<String>,
}

impl Crate {
@@ -672,16 +673,24 @@ impl Build {
}

/// Gets the space-separated set of activated features for the compiler.
fn rustc_features(&self, kind: Kind, target: TargetSelection) -> String {
fn rustc_features(&self, kind: Kind, target: TargetSelection, crates: &[String]) -> String {
let possible_features_by_crates: HashSet<_> = crates
.iter()
.flat_map(|krate| &self.crates[krate].features)
.map(std::ops::Deref::deref)
.collect();
let check = |feature: &str| -> bool {
crates.is_empty() || possible_features_by_crates.contains(feature)
};
let mut features = vec![];
if self.config.jemalloc {
if self.config.jemalloc && check("jemalloc") {
features.push("jemalloc");
}
if self.config.llvm_enabled(target) || kind == Kind::Check {
if (self.config.llvm_enabled(target) || kind == Kind::Check) && check("llvm") {
features.push("llvm");
}
// keep in sync with `bootstrap/compile.rs:rustc_cargo_env`
if self.config.rustc_parallel {
if self.config.rustc_parallel && check("rustc_use_parallel_compiler") {
features.push("rustc_use_parallel_compiler");
}
if self.config.rust_randomize_layout {
@@ -693,7 +702,7 @@ impl Build {
// which is everything (including debug/trace/etc.)
// if its unset, if debug_assertions is on, then debug_logging will also be on
// as well as tracing *ignoring* this feature when debug_assertions is on
if !self.config.rust_debug_logging {
if !self.config.rust_debug_logging && check("max_level_info") {
features.push("max_level_info");
}

6 changes: 3 additions & 3 deletions src/tools/clippy/clippy_lints/src/escape.rs
Original file line number Diff line number Diff line change
@@ -50,7 +50,7 @@ declare_clippy_lint! {
}

fn is_non_trait_box(ty: Ty<'_>) -> bool {
ty.is_box() && !ty.boxed_ty().is_trait()
ty.boxed_ty().is_some_and(|boxed| !boxed.is_trait())
}

struct EscapeDelegate<'a, 'tcx> {
@@ -191,8 +191,8 @@ impl<'a, 'tcx> Delegate<'tcx> for EscapeDelegate<'a, 'tcx> {
impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> {
fn is_large_box(&self, ty: Ty<'tcx>) -> bool {
// Large types need to be boxed to avoid stack overflows.
if ty.is_box() {
self.cx.layout_of(ty.boxed_ty()).map_or(0, |l| l.size.bytes()) > self.too_large_for_stack
if let Some(boxed_ty) = ty.boxed_ty() {
self.cx.layout_of(boxed_ty).map_or(0, |l| l.size.bytes()) > self.too_large_for_stack
} else {
false
}
4 changes: 2 additions & 2 deletions src/tools/clippy/clippy_lints/src/methods/mod.rs
Original file line number Diff line number Diff line change
@@ -5187,8 +5187,8 @@ impl SelfKind {
fn matches_value<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool {
if ty == parent_ty {
true
} else if ty.is_box() {
ty.boxed_ty() == parent_ty
} else if let Some(boxed_ty) = ty.boxed_ty() {
boxed_ty == parent_ty
} else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) {
if let ty::Adt(_, args) = ty.kind() {
args.types().next().map_or(false, |t| t == parent_ty)
4 changes: 2 additions & 2 deletions src/tools/clippy/clippy_lints/src/methods/utils.rs
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ pub(super) fn derefs_to_slice<'tcx>(
fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool {
match ty.kind() {
ty::Slice(_) => true,
ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => may_slice(cx, boxed),
ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::Vec),
ty::Array(_, size) => size.try_eval_target_usize(cx.tcx, cx.param_env).is_some(),
ty::Ref(_, inner, _) => may_slice(cx, *inner),
@@ -33,7 +33,7 @@ pub(super) fn derefs_to_slice<'tcx>(
} else {
match ty.kind() {
ty::Slice(_) => Some(expr),
ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr),
_ if ty.boxed_ty().is_some_and(|boxed| may_slice(cx, boxed)) => Some(expr),
ty::Ref(_, inner, _) => {
if may_slice(cx, *inner) {
Some(expr)
6 changes: 2 additions & 4 deletions src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs
Original file line number Diff line number Diff line change
@@ -75,11 +75,9 @@ impl UnnecessaryBoxReturns {
.instantiate_bound_regions_with_erased(cx.tcx.fn_sig(def_id).skip_binder())
.output();

if !return_ty.is_box() {
let Some(boxed_ty) = return_ty.boxed_ty() else {
return;
}

let boxed_ty = return_ty.boxed_ty();
};

// It's sometimes useful to return Box<T> if T is unsized, so don't lint those.
// Also, don't lint if we know that T is very large, in which case returning
4 changes: 2 additions & 2 deletions src/tools/clippy/clippy_utils/src/ty.rs
Original file line number Diff line number Diff line change
@@ -704,8 +704,8 @@ pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnS

/// If the type is function like, get the signature for it.
pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
if ty.is_box() {
return ty_sig(cx, ty.boxed_ty());
if let Some(boxed_ty) = ty.boxed_ty() {
return ty_sig(cx, boxed_ty);
}
match *ty.kind() {
ty::Closure(id, subs) => {
48 changes: 21 additions & 27 deletions tests/coverage/async.cov-map
Original file line number Diff line number Diff line change
@@ -92,20 +92,18 @@ Number of file 0 mappings: 1
- Code(Counter(0)) at (prev + 25, 1) to (start + 0, 23)

Function name: async::g::{closure#0} (unused)
Raw bytes (69): 0x[01, 01, 00, 0d, 00, 19, 17, 01, 0c, 00, 02, 09, 00, 0a, 00, 00, 0e, 00, 11, 00, 00, 12, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 11, 00, 00, 12, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02]
Raw bytes (59): 0x[01, 01, 00, 0b, 00, 19, 17, 01, 0c, 00, 02, 09, 00, 0a, 00, 00, 0e, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 09, 00, 0a, 00, 00, 0e, 00, 17, 00, 00, 1b, 00, 1c, 00, 00, 20, 00, 22, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 0
Number of file 0 mappings: 13
Number of file 0 mappings: 11
- Code(Zero) at (prev + 25, 23) to (start + 1, 12)
- Code(Zero) at (prev + 2, 9) to (start + 0, 10)
- Code(Zero) at (prev + 0, 14) to (start + 0, 17)
- Code(Zero) at (prev + 0, 18) to (start + 0, 23)
- Code(Zero) at (prev + 0, 14) to (start + 0, 23)
- Code(Zero) at (prev + 0, 27) to (start + 0, 28)
- Code(Zero) at (prev + 0, 32) to (start + 0, 34)
- Code(Zero) at (prev + 1, 9) to (start + 0, 10)
- Code(Zero) at (prev + 0, 14) to (start + 0, 17)
- Code(Zero) at (prev + 0, 18) to (start + 0, 23)
- Code(Zero) at (prev + 0, 14) to (start + 0, 23)
- Code(Zero) at (prev + 0, 27) to (start + 0, 28)
- Code(Zero) at (prev + 0, 32) to (start + 0, 34)
- Code(Zero) at (prev + 1, 14) to (start + 0, 16)
@@ -120,15 +118,14 @@ Number of file 0 mappings: 1
- Code(Counter(0)) at (prev + 33, 1) to (start + 0, 22)

Function name: async::h::{closure#0} (unused)
Raw bytes (44): 0x[01, 01, 00, 08, 00, 21, 16, 03, 0c, 00, 04, 09, 00, 0a, 00, 00, 0e, 00, 13, 00, 00, 14, 00, 19, 00, 00, 1a, 00, 1b, 00, 00, 20, 00, 22, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02]
Raw bytes (39): 0x[01, 01, 00, 07, 00, 21, 16, 03, 0c, 00, 04, 09, 00, 0a, 00, 00, 0e, 00, 19, 00, 00, 1a, 00, 1b, 00, 00, 20, 00, 22, 00, 01, 0e, 00, 10, 00, 02, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 0
Number of file 0 mappings: 8
Number of file 0 mappings: 7
- Code(Zero) at (prev + 33, 22) to (start + 3, 12)
- Code(Zero) at (prev + 4, 9) to (start + 0, 10)
- Code(Zero) at (prev + 0, 14) to (start + 0, 19)
- Code(Zero) at (prev + 0, 20) to (start + 0, 25)
- Code(Zero) at (prev + 0, 14) to (start + 0, 25)
- Code(Zero) at (prev + 0, 26) to (start + 0, 27)
- Code(Zero) at (prev + 0, 32) to (start + 0, 34)
- Code(Zero) at (prev + 1, 14) to (start + 0, 16)
@@ -143,28 +140,25 @@ Number of file 0 mappings: 1
- Code(Counter(0)) at (prev + 42, 1) to (start + 0, 19)

Function name: async::i::{closure#0}
Raw bytes (78): 0x[01, 01, 02, 07, 21, 19, 1d, 0e, 01, 2a, 13, 04, 0c, 0d, 05, 09, 00, 0a, 01, 00, 0e, 00, 12, 05, 00, 13, 00, 18, 09, 00, 1c, 00, 21, 0d, 00, 27, 00, 2a, 15, 00, 2b, 00, 30, 1d, 01, 09, 00, 0a, 11, 00, 0e, 00, 11, 25, 00, 12, 00, 17, 29, 00, 1b, 00, 20, 1d, 00, 24, 00, 26, 21, 01, 0e, 00, 10, 03, 02, 01, 00, 02]
Raw bytes (63): 0x[01, 01, 02, 07, 19, 11, 15, 0b, 01, 2a, 13, 04, 0c, 09, 05, 09, 00, 0a, 01, 00, 0e, 00, 18, 05, 00, 1c, 00, 21, 09, 00, 27, 00, 30, 15, 01, 09, 00, 0a, 0d, 00, 0e, 00, 17, 1d, 00, 1b, 00, 20, 15, 00, 24, 00, 26, 19, 01, 0e, 00, 10, 03, 02, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 2
- expression 0 operands: lhs = Expression(1, Add), rhs = Counter(8)
- expression 1 operands: lhs = Counter(6), rhs = Counter(7)
Number of file 0 mappings: 14
- expression 0 operands: lhs = Expression(1, Add), rhs = Counter(6)
- expression 1 operands: lhs = Counter(4), rhs = Counter(5)
Number of file 0 mappings: 11
- Code(Counter(0)) at (prev + 42, 19) to (start + 4, 12)
- Code(Counter(3)) at (prev + 5, 9) to (start + 0, 10)
- Code(Counter(0)) at (prev + 0, 14) to (start + 0, 18)
- Code(Counter(1)) at (prev + 0, 19) to (start + 0, 24)
- Code(Counter(2)) at (prev + 0, 28) to (start + 0, 33)
- Code(Counter(3)) at (prev + 0, 39) to (start + 0, 42)
- Code(Counter(5)) at (prev + 0, 43) to (start + 0, 48)
- Code(Counter(7)) at (prev + 1, 9) to (start + 0, 10)
- Code(Counter(4)) at (prev + 0, 14) to (start + 0, 17)
- Code(Counter(9)) at (prev + 0, 18) to (start + 0, 23)
- Code(Counter(10)) at (prev + 0, 27) to (start + 0, 32)
- Code(Counter(7)) at (prev + 0, 36) to (start + 0, 38)
- Code(Counter(8)) at (prev + 1, 14) to (start + 0, 16)
- Code(Counter(2)) at (prev + 5, 9) to (start + 0, 10)
- Code(Counter(0)) at (prev + 0, 14) to (start + 0, 24)
- Code(Counter(1)) at (prev + 0, 28) to (start + 0, 33)
- Code(Counter(2)) at (prev + 0, 39) to (start + 0, 48)
- Code(Counter(5)) at (prev + 1, 9) to (start + 0, 10)
- Code(Counter(3)) at (prev + 0, 14) to (start + 0, 23)
- Code(Counter(7)) at (prev + 0, 27) to (start + 0, 32)
- Code(Counter(5)) at (prev + 0, 36) to (start + 0, 38)
- Code(Counter(6)) at (prev + 1, 14) to (start + 0, 16)
- Code(Expression(0, Add)) at (prev + 2, 1) to (start + 0, 2)
= ((c6 + c7) + c8)
= ((c4 + c5) + c6)

Function name: async::j
Raw bytes (58): 0x[01, 01, 02, 07, 0d, 05, 09, 0a, 01, 35, 01, 00, 0d, 01, 0b, 0b, 00, 0c, 05, 01, 09, 00, 0a, 01, 00, 0e, 00, 1b, 05, 00, 1f, 00, 27, 09, 01, 09, 00, 0a, 11, 00, 0e, 00, 1a, 09, 00, 1e, 00, 20, 0d, 01, 0e, 00, 10, 03, 02, 01, 00, 02]
4 changes: 2 additions & 2 deletions tests/coverage/async.coverage
Original file line number Diff line number Diff line change
@@ -45,9 +45,9 @@
LL| 1| // executed asynchronously.
LL| 1| match x {
LL| 1| y if c(x).await == y + 1 => { d().await; }
^0 ^0 ^0 ^0
^0 ^0
LL| 1| y if f().await == y + 1 => (),
^0 ^0 ^0
^0 ^0
LL| 1| _ => (),
LL| | }
LL| 1|}
25 changes: 25 additions & 0 deletions tests/coverage/await_ready.cov-map
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
Function name: await_ready::await_ready
Raw bytes (9): 0x[01, 01, 00, 01, 01, 0a, 01, 00, 1e]
Number of files: 1
- file 0 => global file 1
Number of expressions: 0
Number of file 0 mappings: 1
- Code(Counter(0)) at (prev + 10, 1) to (start + 0, 30)

Function name: await_ready::await_ready::{closure#0}
Raw bytes (14): 0x[01, 01, 00, 02, 01, 0a, 1e, 03, 0f, 05, 04, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 0
Number of file 0 mappings: 2
- Code(Counter(0)) at (prev + 10, 30) to (start + 3, 15)
- Code(Counter(1)) at (prev + 4, 1) to (start + 0, 2)

Function name: await_ready::main
Raw bytes (9): 0x[01, 01, 00, 01, 01, 10, 01, 03, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 0
Number of file 0 mappings: 1
- Code(Counter(0)) at (prev + 16, 1) to (start + 3, 2)

38 changes: 38 additions & 0 deletions tests/coverage/await_ready.coverage
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
LL| |#![feature(coverage_attribute)]
LL| |#![feature(custom_inner_attributes)] // for #![rustfmt::skip]
LL| |#![feature(noop_waker)]
LL| |#![rustfmt::skip]
LL| |//@ edition: 2021
LL| |
LL| |#[coverage(off)]
LL| |async fn ready() -> u8 { 1 }
LL| |
LL| 1|async fn await_ready() -> u8 {
LL| 1| // await should be covered even if the function never yields
LL| 1| ready()
LL| 1| .await
LL| 1|}
LL| |
LL| 1|fn main() {
LL| 1| let mut future = Box::pin(await_ready());
LL| 1| executor::block_on(future.as_mut());
LL| 1|}
LL| |
LL| |mod executor {
LL| | use core::future::Future;
LL| | use core::pin::pin;
LL| | use core::task::{Context, Poll, Waker};
LL| |
LL| | #[coverage(off)]
LL| | pub fn block_on<F: Future>(mut future: F) -> F::Output {
LL| | let mut future = pin!(future);
LL| | let mut context = Context::from_waker(Waker::noop());
LL| |
LL| | loop {
LL| | if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
LL| | break val;
LL| | }
LL| | }
LL| | }
LL| |}

37 changes: 37 additions & 0 deletions tests/coverage/await_ready.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#![feature(coverage_attribute)]
#![feature(custom_inner_attributes)] // for #![rustfmt::skip]
#![feature(noop_waker)]
#![rustfmt::skip]
//@ edition: 2021

#[coverage(off)]
async fn ready() -> u8 { 1 }

async fn await_ready() -> u8 {
// await should be covered even if the function never yields
ready()
.await
}

fn main() {
let mut future = Box::pin(await_ready());
executor::block_on(future.as_mut());
}

mod executor {
use core::future::Future;
use core::pin::pin;
use core::task::{Context, Poll, Waker};

#[coverage(off)]
pub fn block_on<F: Future>(mut future: F) -> F::Output {
let mut future = pin!(future);
let mut context = Context::from_waker(Waker::noop());

loop {
if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
break val;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//@ check-pass
//@ known-bug: #25860

static UNIT: &'static &'static () = &&();

fn foo<'a, 'b, T>(_: &'a &'b (), v: &'b T, _: &()) -> &'a T { v }

fn bad<'a, T>(x: &'a T) -> &'static T {
let f: fn(_, &'a T, &()) -> &'static T = foo;
f(UNIT, x, &())
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Regression test for #129021.

static UNIT: &'static &'static () = &&();

fn foo<'a: 'a, 'b: 'b, T>(_: &'a &'b (), v: &'b T) -> &'a T { v }

fn bad<'a, T>(x: &'a T) -> &'static T {
let f: fn(_, &'a T) -> &'static T = foo;
//~^ ERROR lifetime may not live long enough
f(UNIT, x)
}

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
error: lifetime may not live long enough
--> $DIR/implied-bounds-on-nested-references-plus-variance-early-bound.rs:8:12
|
LL | fn bad<'a, T>(x: &'a T) -> &'static T {
| -- lifetime `'a` defined here
LL | let f: fn(_, &'a T) -> &'static T = foo;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`

error: aborting due to 1 previous error

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Regression test for #129021.

trait ToArg<T> {
type Arg;
}
impl<T, U> ToArg<T> for U {
type Arg = T;
}

fn extend_inner<'a, 'b>(x: &'a str) -> <&'b &'a () as ToArg<&'b str>>::Arg { x }
fn extend<'a, 'b>(x: &'a str) -> &'b str {
(extend_inner as fn(_) -> _)(x)
//~^ ERROR lifetime may not live long enough
}

fn main() {
let y = extend(&String::from("Hello World"));
println!("{}", y);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error: lifetime may not live long enough
--> $DIR/implied-bounds-on-nested-references-plus-variance-unnormalized.rs:12:5
|
LL | fn extend<'a, 'b>(x: &'a str) -> &'b str {
| -- -- lifetime `'b` defined here
| |
| lifetime `'a` defined here
LL | (extend_inner as fn(_) -> _)(x)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
|
= help: consider adding the following bound: `'a: 'b`

error: aborting due to 1 previous error

Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
//@ check-pass
//@ known-bug: #25860

// Should fail. The combination of variance and implied bounds for nested
// references allows us to infer a longer lifetime than we can prove.
// Regression test for #129021.

static UNIT: &'static &'static () = &&();

fn foo<'a, 'b, T>(_: &'a &'b (), v: &'b T) -> &'a T { v }

fn bad<'a, T>(x: &'a T) -> &'static T {
let f: fn(_, &'a T) -> &'static T = foo;
//~^ ERROR lifetime may not live long enough
f(UNIT, x)
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
error: lifetime may not live long enough
--> $DIR/implied-bounds-on-nested-references-plus-variance.rs:8:12
|
LL | fn bad<'a, T>(x: &'a T) -> &'static T {
| -- lifetime `'a` defined here
LL | let f: fn(_, &'a T) -> &'static T = foo;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static`

error: aborting due to 1 previous error