Skip to content

Rollup of 5 pull requests #145240

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 11 commits into from
Aug 11, 2025
Merged
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
12 changes: 8 additions & 4 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,7 @@ enum RelaxedBoundPolicy<'a> {
enum RelaxedBoundForbiddenReason {
TraitObjectTy,
SuperTrait,
AssocTyBounds,
LateBoundVarsInScope,
}

Expand Down Expand Up @@ -1109,9 +1110,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
&*self.arena.alloc(self.ty(constraint.span, hir::TyKind::Err(guar)));
hir::AssocItemConstraintKind::Equality { term: err_ty.into() }
} else {
// FIXME(#135229): These should be forbidden!
let bounds =
self.lower_param_bounds(bounds, RelaxedBoundPolicy::Allowed, itctx);
let bounds = self.lower_param_bounds(
bounds,
RelaxedBoundPolicy::Forbidden(RelaxedBoundForbiddenReason::AssocTyBounds),
itctx,
);
hir::AssocItemConstraintKind::Bound { bounds }
}
}
Expand Down Expand Up @@ -2124,7 +2127,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
diag.emit();
return;
}
RelaxedBoundForbiddenReason::LateBoundVarsInScope => {}
RelaxedBoundForbiddenReason::AssocTyBounds
| RelaxedBoundForbiddenReason::LateBoundVarsInScope => {}
};
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_borrowck/src/handle_placeholders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ fn region_definitions<'tcx>(
for info in var_infos.iter() {
let origin = match info.origin {
RegionVariableOrigin::Nll(origin) => origin,
_ => NllRegionVariableOrigin::Existential { from_forall: false, name: None },
_ => NllRegionVariableOrigin::Existential { name: None },
};

let definition = RegionDefinition { origin, universe: info.universe, external_name: None };
Expand Down
13 changes: 9 additions & 4 deletions compiler/rustc_borrowck/src/region_infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1939,10 +1939,15 @@ impl<'tcx> RegionInferenceContext<'tcx> {
//
// and here we prefer to blame the source (the y = x statement).
let blame_source = match from_region_origin {
NllRegionVariableOrigin::FreeRegion
| NllRegionVariableOrigin::Existential { from_forall: false, name: _ } => true,
NllRegionVariableOrigin::Placeholder(_)
| NllRegionVariableOrigin::Existential { from_forall: true, name: _ } => false,
NllRegionVariableOrigin::FreeRegion => true,
NllRegionVariableOrigin::Placeholder(_) => false,
// `'existential: 'whatever` never results in a region error by itself.
// We may always infer it to `'static` afterall. This means while an error
// path may go through an existential, these existentials are never the
// `from_region`.
NllRegionVariableOrigin::Existential { name: _ } => {
unreachable!("existentials can outlive everything")
}
};

// To pick a constraint to blame, we organize constraints by how interesting we expect them
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_borrowck/src/renumber.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl<'a, 'tcx> RegionRenumberer<'a, 'tcx> {
T: TypeFoldable<TyCtxt<'tcx>>,
F: Fn() -> RegionCtxt,
{
let origin = NllRegionVariableOrigin::Existential { from_forall: false, name: None };
let origin = NllRegionVariableOrigin::Existential { name: None };
fold_regions(self.infcx.tcx, value, |_region, _depth| {
self.infcx.next_nll_region_var(origin, || region_ctxt_fn())
})
Expand Down
16 changes: 4 additions & 12 deletions compiler/rustc_borrowck/src/type_check/relate_tys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> {
*ex_reg_var
} else {
let ex_reg_var =
self.next_existential_region_var(true, br.kind.get_name(infcx.infcx.tcx));
self.next_existential_region_var(br.kind.get_name(infcx.infcx.tcx));
debug!(?ex_reg_var);
reg_map.insert(br, ex_reg_var);

Expand Down Expand Up @@ -244,17 +244,9 @@ impl<'a, 'b, 'tcx> NllTypeRelating<'a, 'b, 'tcx> {
}

#[instrument(skip(self), level = "debug")]
fn next_existential_region_var(
&mut self,
from_forall: bool,
name: Option<Symbol>,
) -> ty::Region<'tcx> {
let origin = NllRegionVariableOrigin::Existential { name, from_forall };

let reg_var =
self.type_checker.infcx.next_nll_region_var(origin, || RegionCtxt::Existential(name));

reg_var
fn next_existential_region_var(&mut self, name: Option<Symbol>) -> ty::Region<'tcx> {
let origin = NllRegionVariableOrigin::Existential { name };
self.type_checker.infcx.next_nll_region_var(origin, || RegionCtxt::Existential(name))
}

#[instrument(skip(self), level = "debug")]
Expand Down
8 changes: 3 additions & 5 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -199,12 +199,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
// However, this can easily get out of sync! Ideally, we would perform this step
// where we are guaranteed to catch *all* bounds like in
// `Self::lower_poly_trait_ref`. List of concrete issues:
// FIXME(more_maybe_bounds): We don't call this for e.g., trait object tys or
// supertrait bounds!
// FIXME(more_maybe_bounds): We don't call this for trait object tys, supertrait
// bounds or associated type bounds (ATB)!
// FIXME(trait_alias, #143122): We don't call it for the RHS. Arguably however,
// AST lowering should reject them outright.
// FIXME(associated_type_bounds): We don't call this for them. However, AST
// lowering should reject them outright (#135229).
// AST lowering should reject them outright.
let bounds = collect_relaxed_bounds(hir_bounds, self_ty_where_predicates);
self.check_and_report_invalid_relaxed_bounds(bounds);
}
Expand Down
11 changes: 0 additions & 11 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,17 +485,6 @@ pub enum NllRegionVariableOrigin {

Existential {
name: Option<Symbol>,
/// If this is true, then this variable was created to represent a lifetime
/// bound in a `for` binder. For example, it might have been created to
/// represent the lifetime `'a` in a type like `for<'a> fn(&'a u32)`.
/// Such variables are created when we are trying to figure out if there
/// is any valid instantiation of `'a` that could fit into some scenario.
///
/// This is used to inform error reporting: in the case that we are trying to
/// determine whether there is any valid instantiation of a `'a` variable that meets
/// some constraint C, we want to blame the "source" of that `for` type,
/// rather than blaming the source of the constraint C.
from_forall: bool,
},
}

Expand Down
74 changes: 50 additions & 24 deletions compiler/rustc_trait_selection/src/traits/query/dropck_outlives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,39 +319,65 @@ pub fn dtorck_constraint_for_ty_inner<'tcx>(
}

ty::Coroutine(def_id, args) => {
// rust-lang/rust#49918: types can be constructed, stored
// in the interior, and sit idle when coroutine yields
// (and is subsequently dropped).
// rust-lang/rust#49918: Locals can be stored across await points in the coroutine,
// called interior/witness types. Since we do not compute these witnesses until after
// building MIR, we consider all coroutines to unconditionally require a drop during
// MIR building. However, considering the coroutine to unconditionally require a drop
// here may unnecessarily require its upvars' regions to be live when they don't need
// to be, leading to borrowck errors: <https://github.com/rust-lang/rust/issues/116242>.
//
// It would be nice to descend into interior of a
// coroutine to determine what effects dropping it might
// have (by looking at any drop effects associated with
// its interior).
// Here, we implement a more precise approximation for the coroutine's dtorck constraint
// by considering whether any of the interior types needs drop. Note that this is still
// an approximation because the coroutine interior has its regions erased, so we must add
// *all* of the upvars to live types set if we find that *any* interior type needs drop.
// This is because any of the regions captured in the upvars may be stored in the interior,
// which then has its regions replaced by a binder (conceptually erasing the regions),
// so there's no way to enforce that the precise region in the interior type is live
// since we've lost that information by this point.
//
// However, the interior's representation uses things like
// CoroutineWitness that explicitly assume they are not
// traversed in such a manner. So instead, we will
// simplify things for now by treating all coroutines as
// if they were like trait objects, where its upvars must
// all be alive for the coroutine's (potential)
// destructor.
// Note also that this check requires that the coroutine's upvars are use-live, since
// a region from a type that does not have a destructor that was captured in an upvar
// may flow into an interior type with a destructor. This is stronger than requiring
// the upvars are drop-live.
//
// In particular, skipping over `_interior` is safe
// because any side-effects from dropping `_interior` can
// only take place through references with lifetimes
// derived from lifetimes attached to the upvars and resume
// argument, and we *do* incorporate those here.
// For example, if we capture two upvar references `&'1 (), &'2 ()` and have some type
// in the interior, `for<'r> { NeedsDrop<'r> }`, we have no way to tell whether the
// region `'r` came from the `'1` or `'2` region, so we require both are live. This
// could even be unnecessary if `'r` was actually a `'static` region or some region
// local to the coroutine! That's why it's an approximation.
let args = args.as_coroutine();

// While we conservatively assume that all coroutines require drop
// to avoid query cycles during MIR building, we can check the actual
// witness during borrowck to avoid unnecessary liveness constraints.
// Note that we don't care about whether the resume type has any drops since this is
// redundant; there is no storage for the resume type, so if it is actually stored
// in the interior, we'll already detect the need for a drop by checking the interior.
let typing_env = tcx.erase_regions(typing_env);
if tcx.mir_coroutine_witnesses(def_id).is_some_and(|witness| {
let needs_drop = tcx.mir_coroutine_witnesses(def_id).is_some_and(|witness| {
witness.field_tys.iter().any(|field| field.ty.needs_drop(tcx, typing_env))
}) {
});
if needs_drop {
// Pushing types directly to `constraints.outlives` is equivalent
// to requiring them to be use-live, since if we were instead to
// recurse on them like we do below, we only end up collecting the
// types that are relevant for drop-liveness.
constraints.outlives.extend(args.upvar_tys().iter().map(ty::GenericArg::from));
constraints.outlives.push(args.resume_ty().into());
} else {
// Even if a witness type doesn't need a drop, we still require that
// the upvars are drop-live. This is only needed if we aren't already
// counting *all* of the upvars as use-live above, since use-liveness
// is a *stronger requirement* than drop-liveness. Recursing here
// unconditionally would just be collecting duplicated types for no
// reason.
for ty in args.upvar_tys() {
dtorck_constraint_for_ty_inner(
tcx,
typing_env,
span,
depth + 1,
ty,
constraints,
);
}
}
}

Expand Down
17 changes: 15 additions & 2 deletions compiler/rustc_trait_selection/src/traits/select/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2333,10 +2333,23 @@ impl<'tcx> SelectionContext<'_, 'tcx> {

ty::Coroutine(def_id, args) => {
let ty = self.infcx.shallow_resolve(args.as_coroutine().tupled_upvars_ty());
let tcx = self.tcx();
let witness = Ty::new_coroutine_witness(
self.tcx(),
tcx,
def_id,
self.tcx().mk_args(args.as_coroutine().parent_args()),
ty::GenericArgs::for_item(tcx, def_id, |def, _| match def.kind {
// HACK: Coroutine witnesse types are lifetime erased, so they
// never reference any lifetime args from the coroutine. We erase
// the regions here since we may get into situations where a
// coroutine is recursively contained within itself, leading to
// witness types that differ by region args. This means that
// cycle detection in fulfillment will not kick in, which leads
// to unnecessary overflows in async code. See the issue:
// <https://github.com/rust-lang/rust/issues/145151>.
ty::GenericParamDefKind::Lifetime => tcx.lifetimes.re_erased.into(),
ty::GenericParamDefKind::Type { .. }
| ty::GenericParamDefKind::Const { .. } => args[def.index as usize],
}),
);
ty::Binder::dummy(AutoImplConstituents {
types: [ty].into_iter().chain(iter::once(witness)).collect(),
Expand Down
18 changes: 18 additions & 0 deletions tests/ui/async-await/drop-live-upvar-2.may_not_dangle.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
error[E0597]: `y` does not live long enough
--> $DIR/drop-live-upvar-2.rs:31:26
|
LL | let y = ();
| - binding `y` declared here
LL | drop_me = Droppy(&y);
| ^^ borrowed value does not live long enough
...
LL | }
| - `y` dropped here while still borrowed
LL | }
| - borrow might be used here, when `fut` is dropped and runs the destructor for coroutine
|
= note: values in a scope are dropped in the opposite order they are defined

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0597`.
37 changes: 37 additions & 0 deletions tests/ui/async-await/drop-live-upvar-2.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//@ revisions: may_dangle may_not_dangle
//@[may_dangle] check-pass
//@ edition: 2018

// Ensure that if a coroutine's interior has no drop types then we don't require the upvars to
// be *use-live*, but instead require them to be *drop-live*. In this case, `Droppy<&'?0 ()>`
// does not require that `'?0` is live for drops since the parameter is `#[may_dangle]` in
// the may_dangle revision, but not in the may_not_dangle revision.

#![feature(dropck_eyepatch)]

struct Droppy<T>(T);

#[cfg(may_dangle)]
unsafe impl<#[may_dangle] T> Drop for Droppy<T> {
fn drop(&mut self) {
// This does not use `T` of course.
}
}

#[cfg(may_not_dangle)]
impl<T> Drop for Droppy<T> {
fn drop(&mut self) {}
}

fn main() {
let drop_me;
let fut;
{
let y = ();
drop_me = Droppy(&y);
//[may_not_dangle]~^ ERROR `y` does not live long enough
fut = async {
std::mem::drop(drop_me);
};
}
}
23 changes: 23 additions & 0 deletions tests/ui/async-await/drop-live-upvar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//@ edition: 2018
// Regression test for <https://github.com/rust-lang/rust/issues/144155>.

struct NeedsDrop<'a>(&'a Vec<i32>);

async fn await_point() {}

impl Drop for NeedsDrop<'_> {
fn drop(&mut self) {}
}

fn foo() {
let v = vec![1, 2, 3];
let x = NeedsDrop(&v);
let c = async {
std::future::ready(()).await;
drop(x);
};
drop(v);
//~^ ERROR cannot move out of `v` because it is borrowed
}

fn main() {}
22 changes: 22 additions & 0 deletions tests/ui/async-await/drop-live-upvar.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
error[E0505]: cannot move out of `v` because it is borrowed
--> $DIR/drop-live-upvar.rs:19:10
|
LL | let v = vec![1, 2, 3];
| - binding `v` declared here
LL | let x = NeedsDrop(&v);
| -- borrow of `v` occurs here
...
LL | drop(v);
| ^ move out of `v` occurs here
LL |
LL | }
| - borrow might be used here, when `c` is dropped and runs the destructor for coroutine
|
help: consider cloning the value if the performance cost is acceptable
|
LL | let x = NeedsDrop(&v.clone());
| ++++++++

error: aborting due to 1 previous error

For more information about this error, try `rustc --explain E0505`.
14 changes: 14 additions & 0 deletions tests/ui/async-await/recursive-async-auto-trait-overflow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Regression test for <https://github.com/rust-lang/rust/issues/145151>.

//@ edition: 2024
//@ check-pass

async fn process<'a>() {
Box::pin(process()).await;
}

fn require_send(_: impl Send) {}

fn main() {
require_send(process());
}
2 changes: 1 addition & 1 deletion tests/ui/infinite/issue-41731-infinite-macro-print.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
fn main() {
macro_rules! stack {
($overflow:expr) => {
print!(stack!($overflow));
print!(stack!($overflow))
//~^ ERROR recursion limit reached while expanding
//~| ERROR format argument must be a string literal
};
Expand Down
Loading
Loading