Auto merge of #145240 - Zalathar:rollup-7r97lia, r=Zalathar

Rollup of 5 pull requests

Successful merges:

 - rust-lang/rust#135331 (Reject relaxed bounds inside associated type bounds (ATB))
 - rust-lang/rust#144156 (Check coroutine upvars in dtorck constraint)
 - rust-lang/rust#145091 (`NllRegionVariableOrigin` remove `from_forall`)
 - rust-lang/rust#145194 (Ignore coroutine witness type region args in auto trait confirmation)
 - rust-lang/rust#145225 (Fix macro infinite recursion test to not trigger warning about semicolon in expr)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-08-11 09:03:18 +00:00
commit 577166503a
22 changed files with 239 additions and 82 deletions

View file

@ -296,6 +296,7 @@ enum RelaxedBoundPolicy<'a> {
enum RelaxedBoundForbiddenReason {
TraitObjectTy,
SuperTrait,
AssocTyBounds,
LateBoundVarsInScope,
}
@ -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 }
}
}
@ -2124,7 +2127,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
diag.emit();
return;
}
RelaxedBoundForbiddenReason::LateBoundVarsInScope => {}
RelaxedBoundForbiddenReason::AssocTyBounds
| RelaxedBoundForbiddenReason::LateBoundVarsInScope => {}
};
}
}

View file

@ -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 };

View file

@ -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

View file

@ -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())
})

View file

@ -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);
@ -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")]

View file

@ -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);
}

View file

@ -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,
},
}

View file

@ -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,
);
}
}
}

View file

@ -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(),

View file

@ -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`.

View file

@ -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);
};
}
}

View file

@ -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() {}

View file

@ -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`.

View file

@ -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());
}

View file

@ -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
};

View file

@ -14,11 +14,11 @@ LL | stack!("overflow");
| ^^^^^^^^^^^^^^^^^^
|
= note: expanding `stack! { "overflow" }`
= note: to `print! (stack! ("overflow"));`
= note: to `print! (stack! ("overflow"))`
= note: expanding `print! { stack! ("overflow") }`
= note: to `{ $crate :: io :: _print($crate :: format_args! (stack! ("overflow"))); }`
= note: expanding `stack! { "overflow" }`
= note: to `print! (stack! ("overflow"));`
= note: to `print! (stack! ("overflow"))`
= note: expanding `print! { stack! ("overflow") }`
= note: to `{ $crate :: io :: _print($crate :: format_args! (stack! ("overflow"))); }`
@ -31,7 +31,7 @@ LL | stack!("overflow");
= note: this error originates in the macro `print` which comes from the expansion of the macro `stack` (in Nightly builds, run with -Z macro-backtrace for more info)
help: you might be missing a string literal to format with
|
LL | print!("{}", stack!($overflow));
LL | print!("{}", stack!($overflow))
| +++++
error: aborting due to 2 previous errors

View file

@ -5,7 +5,7 @@
fn main() {
macro_rules! stack {
($overflow:expr) => {
println!(stack!($overflow));
println!(stack!($overflow))
//~^ ERROR recursion limit reached while expanding
//~| ERROR format argument must be a string literal
};

View file

@ -14,11 +14,11 @@ LL | stack!("overflow");
| ^^^^^^^^^^^^^^^^^^
|
= note: expanding `stack! { "overflow" }`
= note: to `println! (stack! ("overflow"));`
= note: to `println! (stack! ("overflow"))`
= note: expanding `println! { stack! ("overflow") }`
= note: to `{ $crate :: io :: _print($crate :: format_args_nl! (stack! ("overflow"))); }`
= note: expanding `stack! { "overflow" }`
= note: to `println! (stack! ("overflow"));`
= note: to `println! (stack! ("overflow"))`
= note: expanding `println! { stack! ("overflow") }`
= note: to `{ $crate :: io :: _print($crate :: format_args_nl! (stack! ("overflow"))); }`
@ -31,7 +31,7 @@ LL | stack!("overflow");
= note: this error originates in the macro `println` which comes from the expansion of the macro `stack` (in Nightly builds, run with -Z macro-backtrace for more info)
help: you might be missing a string literal to format with
|
LL | println!("{}", stack!($overflow));
LL | println!("{}", stack!($overflow))
| +++++
error: aborting due to 2 previous errors

View file

@ -1,7 +1,7 @@
// FIXME(more_maybe_bounds): Even under `more_maybe_bounds` / `-Zexperimental-default-bounds`,
// trying to relax non-default bounds should still be an error in all contexts! As you can see
// there are places like supertrait bounds and trait object types where we currently don't perform
// this check.
// there are places like supertrait bounds, trait object types or associated type bounds (ATB)
// where we currently don't perform this check.
#![feature(auto_traits, more_maybe_bounds, negative_impls)]
trait Trait1 {}
@ -13,11 +13,15 @@ trait Trait4 where Self: Trait1 {}
// FIXME: `?Trait2` should be rejected, `Trait2` isn't marked `#[lang = "default_traitN"]`.
fn foo(_: Box<(dyn Trait3 + ?Trait2)>) {}
fn bar<T: ?Sized + ?Trait2 + ?Trait1 + ?Trait4>(_: &T) {}
//~^ ERROR bound modifier `?` can only be applied to default traits like `Sized`
//~| ERROR bound modifier `?` can only be applied to default traits like `Sized`
//~| ERROR bound modifier `?` can only be applied to default traits like `Sized`
// FIXME: `?Trait1` should be rejected, `Trait1` isn't marked `#[lang = "default_traitN"]`.
fn baz<T>() where T: Iterator<Item: ?Trait1> {}
struct S;
impl !Trait2 for S {}
impl Trait1 for S {}

View file

@ -1,17 +1,17 @@
error: bound modifier `?` can only be applied to default traits like `Sized`
--> $DIR/more_maybe_bounds.rs:16:20
--> $DIR/more_maybe_bounds.rs:17:20
|
LL | fn bar<T: ?Sized + ?Trait2 + ?Trait1 + ?Trait4>(_: &T) {}
| ^^^^^^^
error: bound modifier `?` can only be applied to default traits like `Sized`
--> $DIR/more_maybe_bounds.rs:16:30
--> $DIR/more_maybe_bounds.rs:17:30
|
LL | fn bar<T: ?Sized + ?Trait2 + ?Trait1 + ?Trait4>(_: &T) {}
| ^^^^^^^
error: bound modifier `?` can only be applied to default traits like `Sized`
--> $DIR/more_maybe_bounds.rs:16:40
--> $DIR/more_maybe_bounds.rs:17:40
|
LL | fn bar<T: ?Sized + ?Trait2 + ?Trait1 + ?Trait4>(_: &T) {}
| ^^^^^^^

View file

@ -22,6 +22,10 @@ impl<T> S1<T> {
fn f() where T: ?Sized {} //~ ERROR this relaxed bound is not permitted here
}
// Test associated type bounds (ATB).
// issue: <https://github.com/rust-lang/rust/issues/135229>
struct S6<T>(T) where T: Iterator<Item: ?Sized>; //~ ERROR this relaxed bound is not permitted here
trait Tr: ?Sized {} //~ ERROR relaxed bounds are not permitted in supertrait bounds
// Test that relaxed `Sized` bounds are rejected in trait object types:

View file

@ -38,8 +38,16 @@ LL | fn f() where T: ?Sized {}
|
= note: in this context, relaxed bounds are only allowed on type parameters defined by the closest item
error: this relaxed bound is not permitted here
--> $DIR/relaxed-bounds-invalid-places.rs:27:41
|
LL | struct S6<T>(T) where T: Iterator<Item: ?Sized>;
| ^^^^^^
|
= note: in this context, relaxed bounds are only allowed on type parameters defined by the closest item
error: relaxed bounds are not permitted in supertrait bounds
--> $DIR/relaxed-bounds-invalid-places.rs:25:11
--> $DIR/relaxed-bounds-invalid-places.rs:29:11
|
LL | trait Tr: ?Sized {}
| ^^^^^^
@ -47,19 +55,19 @@ LL | trait Tr: ?Sized {}
= note: traits are `?Sized` by default
error: relaxed bounds are not permitted in trait object types
--> $DIR/relaxed-bounds-invalid-places.rs:29:20
--> $DIR/relaxed-bounds-invalid-places.rs:33:20
|
LL | type O1 = dyn Tr + ?Sized;
| ^^^^^^
error: relaxed bounds are not permitted in trait object types
--> $DIR/relaxed-bounds-invalid-places.rs:30:15
--> $DIR/relaxed-bounds-invalid-places.rs:34:15
|
LL | type O2 = dyn ?Sized + ?Sized + Tr;
| ^^^^^^
error: relaxed bounds are not permitted in trait object types
--> $DIR/relaxed-bounds-invalid-places.rs:30:24
--> $DIR/relaxed-bounds-invalid-places.rs:34:24
|
LL | type O2 = dyn ?Sized + ?Sized + Tr;
| ^^^^^^
@ -76,5 +84,5 @@ error: bound modifier `?` can only be applied to `Sized`
LL | struct S5<T>(*const T) where T: ?Trait<'static> + ?Sized;
| ^^^^^^^^^^^^^^^
error: aborting due to 11 previous errors
error: aborting due to 12 previous errors