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

Failure to fulfill higher-kinded "outlives" predicate could have better error message #27114

Open
apasel422 opened this issue Jul 18, 2015 · 15 comments
Labels
A-diagnostics Area: Messages for errors, warnings, and lints C-enhancement Category: An issue proposing an enhancement or a PR with one. E-hard Call for participation: Hard difficulty. Experience needed to fix: A lot. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@apasel422
Copy link
Contributor

Example:

fn foo<T>() where for<'a> T: 'a {}

fn bar<'b>() {
    foo::<&'b i32>();
}

fn main() {}

Error:

foo.rs:4:5: 4:19 error: the type `&'b i32` does not fulfill the required lifetime
foo.rs:4     foo::<&'b i32>();
             ^~~~~~~~~~~~~~
note: type must outlive the static lifetime
error: aborting due to previous error

It's not necessarily obvious why the type has to be 'static in this case.

@steveklabnik steveklabnik added the A-diagnostics Area: Messages for errors, warnings, and lints label Jul 20, 2015
@brson brson added A-docs Area: documentation for any part of the project, including the compiler, standard library, and tools C-enhancement Category: An issue proposing an enhancement or a PR with one. E-easy Call for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue. and removed C-enhancement Category: An issue proposing an enhancement or a PR with one. labels Jan 26, 2017
@brson
Copy link
Contributor

brson commented Jan 26, 2017

Message is the same today. Technically this is not hard to do. The hard part is just coming up wit hsomething better.

@eddyb
Copy link
Member

eddyb commented Jan 26, 2017

I would say something along the lines of "'a could be 'static", pointing to the for<'a>.
for<'a> T: 'a means that for every lifetime 'a, T: 'a must hold, and that includes T: 'static here.
In some cases there are "well-formed" preconditions which can limit 'a to the shortest lifetime in a different type, and the hard part here might be handling those cases - though we can probably still print a lifetime.

@nikomatsakis or @arielb1 should be able to provide an example there (&'b T function argument?).

@tbg
Copy link
Contributor

tbg commented Feb 27, 2017

I'm taking a look at implementing @eddyb's suggestion. Curious about the other examples mentioned, though.

@tbg
Copy link
Contributor

tbg commented Mar 3, 2017

I've understood the code well enough to see the problem, but not enough to
propose a concrete solution. Could someone give me some pointers on
a reasonable path forward?

  • When process_predicates sees the call site,
    it registers a RegionObligation for that callsite's region to outlive the
    static region ReStatic. This loses most of the information about the
    cause. I think the only trace is in the ObligationCauses
    ObligationCauseCode (a DefId which perhaps you could use later to get
    a hold of the higher-kinded predicate), which is not very immediate.
  • when the region checker visits
    this obligation, it turns it into the requirement that the &i32 outlives
    (i.e. is) 'static. It turns the ObligationCause into a SubregionOrigin
    (via code_to_origin), and this only keeps the Span from the original
    cause (it seems impossible to emit an error message which is aware of the
    circumstances at this point).
  • eventually, collect_errors
    checks whether our region is a super-region of ReStatic (via
    a RelateParamBound to which the matter has in the meantime resolved),
    which it isn't, and emits a RegionResolutionError to that effect.

What seems clear is that while terminating the higher-kinded predicate as
a ReStatic is convenient, it makes it impossible to give a better error
message when the callsite's lifetime isn't 'static later. OTOH, there are
probably good reasons for not leaking the higher type higher up (I read up on
how they are type checked, but I wouldn't say I understand too well in which
order things happen).

One could try to plumb more information up along with the introduced ReStatic
(i.e. adding to the ObligationCause information on what causes the ReStatic
bound, which would presumably have to make it into the SubregionOrigin as
well, and then be used for constructing the user-visible error. But that also
seems like a bunch of plumbing very specific to this very specific situation
(even trying to find documentation on HRTBs, I only found the source code and
a blurb in the nomicon) and more
of a hack than a fix.

@eddyb
Copy link
Member

eddyb commented Mar 3, 2017

@tschottdorf You can add an extra field to fulfill::RegionObligation, so that the definition span of the 'a in T: 'a (no matter where the 'a comes from, as long as it's a parameter of some sort) is tracked.

Then, a SubregionOrigin variant (if we don't have one already, there might be another source of these, not just fulfill) specific to an outlives bound with a parameter that turned into a 'static bound.

cc @nikomatsakis

@tbg
Copy link
Contributor

tbg commented Mar 7, 2017

@eddyb I think I know how to go about the second part (making a special SubregionOrigin that holds enough info to emit a better error) but I'm less sure about the first part.

Let me see if I understand you correctly - you're proposing adding something to ObligationCause (not RegionObligation directly), right? That would then get passed down to code_to_origin and allow me to do the second part.

Whatever I pass down would have to hold the information that we synthesized a 'static bound, and also the Span of the higher-ranked bound (i.e. something representing some part of fn foo<T>() where for<'a> T: 'a {}).

So perhaps a new ObligationCauseCode:

pub enum ObligationCauseCode<'tcx> {
    ///  An obligation incurred from treating a higher-ranked trait situated at the specified Span.
    HigherRankedRegion(Span) // do we need to keep a nested ObligationCauseCode here?
}
// code_to_error can now check the ObligationCauseCode if it sees a `ReStatic` bound
// and emit a custom error with both the obligation's Span (`&'b i32`) as well as the HRTB (`for<'a> T: 'a {}`).

Assuming that that's reasonable (may well not be), one very practical question I have is how to get the Span from the predicate (or anything contained within) in register_region_obligation (or higher up?). I do have a DefId somewhere in the binder; is that how it's done? If so, appreciate a pointer on the magical incantation.

@eddyb
Copy link
Member

eddyb commented Mar 7, 2017

There was a reason I said it like that, because you can change the callers of code_to_origin to avoid polutting ObligationCause, because that refers to the original cause (you might have to make SubregionOrigin recursive if it's not already).
You can get a Span from the DefId inside the Region, if late-bound and named (which is always true for this issue) - check tcx.hir methods for something that only works in the same crate (otherwise you'd need to add Lifetime definition spans to crate metadata and that's a bit trickier).

@tbg
Copy link
Contributor

tbg commented Mar 7, 2017

Ah, gotcha - thanks for the pointers, I'll put something together.

@steveklabnik steveklabnik removed the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Mar 9, 2017
@tbg
Copy link
Contributor

tbg commented Mar 10, 2017

@eddyb I have something working, but it's rough around the edges: tbg#1
If you don't mind taking a look - put my questions/remarks in the diff.

(can also open a PR if you prefer that, but it seemed immature).

@eddyb
Copy link
Member

eddyb commented Mar 10, 2017

@tschottdorf You have a couple binaries you should not commit (run ./x.py test src/tools/tidy).
But other than that, it looks good enough for a PR - I want others to look at it too!

@steveklabnik steveklabnik added T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. and removed A-docs Area: documentation for any part of the project, including the compiler, standard library, and tools labels Mar 22, 2017
@Mark-Simulacrum Mark-Simulacrum added the C-enhancement Category: An issue proposing an enhancement or a PR with one. label Jul 22, 2017
@metajack
Copy link
Contributor

@tschottdorf Are you still working on this?

@tbg
Copy link
Contributor

tbg commented Apr 27, 2018

No, knock yourself out! It wasn't worth doing by the time I stopped working on it, though that might've changed in the meantime.

@Ygg01
Copy link

Ygg01 commented Nov 27, 2018

Hi, is this issue still open. On Rust stable and nightly version 2015/2018 I get following message:

   Compiling playground v0.0.1 (/playground)
error[E0477]: the type `&'b i32` does not fulfill the required lifetime
 --> src/main.rs:4:5
  |
4 |     foo::<&'b i32>();
  |     ^^^^^^^^^^^^^^
  |
  = note: type must satisfy the static lifetime

Is this the error the OP was suggesting?

@Angr1st
Copy link

Angr1st commented Jun 11, 2022

With the current nightly the message is now:

   Compiling playground v0.0.1 (/playground)
error: lifetime may not live long enough
 --> src/main.rs:4:5
  |
3 | fn bar<'b>() {
  |        -- lifetime `'b` defined here
4 |     foo::<&'b i32>();
  |     ^^^^^^^^^^^^^^ requires that `'b` must outlive `'static`

error: could not compile `playground` due to previous error

Is this good enough? I think this was changed through #95565. I tested it again after reading the blog post by Jack Huey.

@oli-obk oli-obk added E-hard Call for participation: Hard difficulty. Experience needed to fix: A lot. and removed E-easy Call for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue. labels Jul 4, 2022
@SamB
Copy link

SamB commented May 3, 2024

I believe the idea is to indicate that the static bound originates from the for<'a>.

It looks like 1.78 does a bit better here:

error: lifetime may not live long enough
 --> src/main.rs:4:5
  |
3 | fn bar<'b>() {
  |        -- lifetime `'b` defined here
4 |     foo::<&'b i32>();
  |     ^^^^^^^^^^^^^^ requires that `'b` must outlive `'static`
  |
note: due to current limitations in the borrow checker, this implies a `'static` lifetime
 --> src/main.rs:1:30
  |
1 | fn foo<T>() where for<'a> T: 'a {}
  |                              ^^

Rust Playground

This at least points to the universally-quantified region name, if not the quantification itself.

I'm not sure what the bit about "current limitations" is for, though. Perhaps the only relevant limitation for this example is the inability to explain the implication?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints C-enhancement Category: An issue proposing an enhancement or a PR with one. E-hard Call for participation: Hard difficulty. Experience needed to fix: A lot. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests