Skip to content
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

Report a specialized error when a 'static obligation comes from an impl dyn Trait #121274

Open
wants to merge 19 commits into
base: master
Choose a base branch
from

Conversation

estebank
Copy link
Contributor

@estebank estebank commented Feb 18, 2024

error: lifetime may not live long enough
  --> $DIR/static-impl-obligation.rs:8:27
   |
LL |     fn bar<'a>(x: &'a &'a u32) {
   |            -- lifetime `'a` defined here
LL |         let y: &dyn Foo = x;
   |                           ^ cast requires that `'a` must outlive `'static`
LL |         y.hello();
   |         --------- calling this method introduces a `'static` lifetime requirement
   |
help: relax the implicit `'static` bound on the impl
   |
LL |     impl dyn Foo + '_ {
   |                  ++++
error: lifetime may not live long enough
  --> $DIR/static-impl-obligation.rs:173:27
   |
LL |     fn bar<'a>(x: &'a &'a u32) {
   |            -- lifetime `'a` defined here
LL |         let y: &dyn Foo = x;
   |                           ^ cast requires that `'a` must outlive `'static`
LL |         y.hello();
   |         --------- calling this method introduces a `'static` lifetime requirement
   |
note: the `impl` on `(dyn p::Foo + 'static)` has `'static` lifetime requirements
  --> $DIR/static-impl-obligation.rs:169:20
   |
LL |     impl dyn Foo + 'static where Self: 'static {
   |                    ^^^^^^^             ^^^^^^^
LL |         fn hello(&self) where Self: 'static {}
   |                                     ^^^^^^^

Fix #83246, fix #80701.

error[E0478]: lifetime bound not satisfied
   --> tests/ui/lifetimes/point-at-lifetime-obligation-from-trait-in-trait-object.rs:4:21
    |
4   |     broken: Box<dyn Any + 'a>
    |                     ^^^   ^^
    |
note: lifetime parameter instantiated with the lifetime `'a` as defined here
   --> tests/ui/lifetimes/point-at-lifetime-obligation-from-trait-in-trait-object.rs:3:18
    |
3   | struct Something<'a> {
    |                  ^^
    = note: but lifetime parameter must outlive the static lifetime
note: unmet `'static` obligations introduced here
   --> rust/library/core/src/any.rs:115:16
    |
115 | pub trait Any: 'static {
    |                ^^^^^^^

Fix #83325.

@rustbot
Copy link
Collaborator

rustbot commented Feb 18, 2024

r? @compiler-errors

rustbot has assigned @compiler-errors.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Feb 18, 2024
@estebank
Copy link
Contributor Author

There are a couple of things I need to clean up, but most of the logic is close to done.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@fmease
Copy link
Member

fmease commented Feb 18, 2024

Thanks for considering a different approach! <3

Could this be generalized to trait impls? I don't see why this needs to be specific to inherent impls. Ideally we would provide a very similar suggestion & note for trait impls:

trait Foo {}
impl<'a> Foo for &'a u32 {}

trait Trait { fn hello(&self) {} }

impl Trait for dyn Foo {
    fn hello(&self) {}

}
fn convert<'a>(x: &'a &'a u32) {
    let y: &dyn Foo = x;
    y.hello();
}
current stderr
error: lifetime may not live long enough
  --> src/lib.rs:11:23
   |
10 | fn convert<'a>(x: &'a &'a u32) {
   |            -- lifetime `'a` defined here
11 |     let y: &dyn Foo = x;
   |                       ^ cast requires that `'a` must outlive `'static`

@rust-log-analyzer

This comment was marked as resolved.

@estebank
Copy link
Contributor Author

estebank commented Feb 19, 2024

@fmease with the last change to the PR, on that code:

Screenshot 2024-02-18 at 4 54 25 PM

error: lifetime may not live long enough
  --> f103.rs:11:23
   |
10 | fn convert<'a>(x: &'a &'a u32) {
   |            -- lifetime `'a` defined here
11 |     let y: &dyn Foo = x;
   |                       ^ cast requires that `'a` must outlive `'static`
12 |     y.hello();
   |     --------- calling this method introduces a `'static` lifetime requirement
   |
help: consider relaxing the implicit `'static` requirement on the impl
   |
6  | impl Trait for dyn Foo + '_ {
   |                        ++++

Edit: added to the test suite.

@compiler-errors
Copy link
Member

r? fmease

@rustbot
Copy link
Collaborator

rustbot commented Feb 19, 2024

These commits modify the Cargo.lock file. Unintentional changes to Cargo.lock can be introduced when switching branches and rebasing PRs.

If this was unintentional then you should revert the changes before this PR is merged.
Otherwise, you can ignore this comment.

@rustbot rustbot assigned fmease and unassigned compiler-errors Feb 19, 2024
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@estebank estebank force-pushed the implicit_static branch 2 times, most recently from 3bc0441 to 5074501 Compare February 19, 2024 17:13
@rust-log-analyzer

This comment has been minimized.

@rustbot
Copy link
Collaborator

rustbot commented Feb 19, 2024

Some changes occurred in src/tools/clippy

cc @rust-lang/clippy

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

```
error[E0478]: lifetime bound not satisfied
   --> tests/ui/lifetimes/point-at-lifetime-obligation-from-trait-in-trait-object.rs:4:21
    |
4   |     broken: Box<dyn Any + 'a>
    |                     ^^^   ^^
    |
note: lifetime parameter instantiated with the lifetime `'a` as defined here
   --> tests/ui/lifetimes/point-at-lifetime-obligation-from-trait-in-trait-object.rs:3:18
    |
3   | struct Something<'a> {
    |                  ^^
    = note: but lifetime parameter must outlive the static lifetime
note: unmet `'static` obligations introduced here
   --> rust/library/core/src/any.rs:115:16
    |
115 | pub trait Any: 'static {
    |                ^^^^^^^
```

Fix rust-lang#83325.
```
error[E0478]: lifetime bound not satisfied
  --> $DIR/point-at-lifetime-obligation-from-trait-in-trait-object.rs:4:27
   |
LL |     broken: Box<dyn Any + 'a>
   |                     ---   ^^ lifetime bound not satisfied
   |                     |
   |                     this requires `'static`
   |
note: lifetime parameter instantiated with the lifetime `'a` as defined here
  --> $DIR/point-at-lifetime-obligation-from-trait-in-trait-object.rs:3:18
   |
LL | struct Something<'a> {
   |                  ^^
   = note: but lifetime parameter must outlive the static lifetime
note: `'static` requirement introduced here
  --> $SRC_DIR/core/src/any.rs:LL:COL
```
@bors
Copy link
Contributor

bors commented Mar 22, 2024

☔ The latest upstream changes (presumably #122852) made this pull request unmergeable. Please resolve the merge conflicts.

@apiraino
Copy link
Contributor

@estebank when you have a chance a rebase. @fmease are we good here? :)

thanks folks

@rustbot author

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 20, 2024
@fmease fmease added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jun 24, 2024
@wesleywiser wesleywiser added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 11, 2024
Copy link
Member

@compiler-errors compiler-errors left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have the energy to continue reviewing this since there's a lot going on, but in general, I would really urge you to you leave this code cleaner that you found it. I'm sorry if that requires a lot of annoying work, but that's really the price of making large changes if you care for the code's long-term maintainability.

Please at the very least remove those utility methods you added to the util crate. They're footguns at best.

@@ -235,6 +236,37 @@ pub fn elaborate<'tcx, O: Elaboratable<'tcx>>(
elaborator
}

pub fn elaborate_predicates_of<'tcx>(
Copy link
Member

@compiler-errors compiler-errors Jul 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this only guaranteed to be called on a trait? Never a trait item? If not, using this function is a hazard because it only computes the predicates of the item and not its parents. I would prefer if you remove it and inline it into its callsites like:

elaborate(tcx, tcx.predicates_of(def_id).instantiate_own_identity())

Though if this is intended to be called on non-top-level-items, you need to use:

elaborate(tcx, tcx.predicates_of(def_id).instantiate_identity())

(and a .map(|(clause, sp)| (clause.as_predicate(), sp)) if necessary, but you can likely rework the call-sites to operate solely on Clauses and not Predicates.)

)
}

pub fn filter_predicates<'tcx>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this function should get moved closer to the error reporting. It doesn't seem useful to me other than for making some filter calls in error reporting shorter.

Also, like, what's up with the name? filter_predicates plus the lack of documentation plus the fact that this is in the util module makes it incredibly unclear that this is meant to filter outlives regions only. It should be called filter_outlives_regions or something.

Also, the fact that it only filters TypeOutlives obligations and always takes RegionOutlives obligations seems somewhat unintuitive

@@ -451,6 +451,9 @@ pub enum ObligationCauseCode<'tcx> {

/// Obligations emitted during the normalization of a weak type alias.
TypeAlias(InternedObligationCauseCode<'tcx>, Span, DefId),

/// During borrowck we've found a method call that could have introduced a lifetime requirement.
MethodCallConstraint(Ty<'tcx>, Span),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rename this to make it clear that it's a constraint that only ever results from a region constraint category. MethodCallRegionConstraint or something would be nice.

// ```
if let ty::Ref(region, _, _) = self
.infcx
.instantiate_binder_with_fresh_vars(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to instantiate this binder with vars just to check the region on its self type. You should use skip_binder.

// Look at the receiver for `&'static self`, which introduces a `'static` obligation.
// ```
// impl dyn Trait {
// fn foo(&'static self) {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment should be adjusted to make it obvious that this has nothing to do with dyn as the Self type of the impl.

let mut parent = tcx.parent(def_id);
debug!(?def_id, ?parent);
let trait_preds: Vec<_> = match tcx.def_kind(parent) {
hir::def::DefKind::Impl { .. } => tcx
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please import DefKind

tcx.for_each_relevant_impl(parent, ty, |id| {
impls.push(id);
});
if let [def_id] = impls[..] {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't work if there's a built-in impl that applies, and I kind of prefer that we remove this hack. If Instance::resolve doesn't give you the right item back, it's probably because there's actual polymorphism going on here.

Comment on lines +685 to +686
let mut predicates: Vec<Span> = elaborate_predicates_of(tcx, def_id)
.chain(elaborate_predicates_of(tcx, parent))
Copy link
Member

@compiler-errors compiler-errors Jul 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You shouldn't need to do the elaboration twice if you actually elaborated the full set of predicates for the item, which already contains the parent's predicates, i.e.

elaborate(tcx.predicates_of(tcx, def_id).instantiate_identity())

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only case where this matters is when you set the parent manually due to that impl hack above. I don't think you've explained why it's needed, and I'm kinda tempted to say that we should not have it even if it regresses tests, since it's definitely not well-founded type theoretically.

.chain(elaborate_predicates_of(tcx, parent))
.chain(trait_preds)
.filter_map(filter_predicates(tcx.lifetimes.re_static, |pred_ty| {
self.infcx.can_eq(self.param_env, ty, pred_ty)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't work if there are placeholder types, so this code will fail in weird and unnecessary ways for generic code. The predicates need to be instantiated with inference vars.

.chain(trait_preds)
.filter_map(filter_predicates(tcx.lifetimes.re_static, |pred_ty| {
self.infcx.can_eq(self.param_env, ty, pred_ty)
|| matches!(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, see, that's why you needed to add this hack. it should be unnecessary.

@petrochenkov petrochenkov removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jul 24, 2024
@alex-semenyuk
Copy link
Member

@estebank
Ping from triage. Could you please post your status on this PR? This PR has not been updated in more than 15 days.
It has merge conflicts and do not addressed comments

@fmease
Copy link
Member

fmease commented Sep 15, 2024

They are on vacation rn and will return soon

@alex-semenyuk alex-semenyuk added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Nov 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
10 participants