Repository navigation
Failure to fulfill higher-kinded "outlives" predicate could have better error message #27114
Description
Activity
- addedA-diagnosticsArea: Messages for errors, warnings, and lintsArea: Messages for errors, warnings, and lints
on Jul 20, 2015 - addedA-docsArea: Documentation for any part of the project, including the compiler, standard library, and toolsArea: Documentation for any part of the project, including the compiler, standard library, and toolsC-enhancementCategory: An issue proposing an enhancement or a PR with one.Category: An issue proposing an enhancement or a PR with one.E-easyCall for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue.Call for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue.and removedC-enhancementCategory: An issue proposing an enhancement or a PR with one.Category: An issue proposing an enhancement or a PR with one.
on 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.
I would say something along the lines of "
'acould be'static", pointing to thefor<'a>.
for<'a> T: 'ameans that for every lifetime'a,T: 'amust hold, and that includesT: 'statichere.
In some cases there are "well-formed" preconditions which can limit'ato 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 Tfunction argument?).I'm taking a look at implementing @eddyb's suggestion. Curious about the other examples mentioned, though.
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 aRegionObligationfor that callsite's region to outlive the
static regionReStatic. This loses most of the information about the
cause. I think the only trace is in theObligationCauses
ObligationCauseCode(aDefIdwhich 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&i32outlives
(i.e. is)'static. It turns theObligationCauseinto aSubregionOrigin
(viacode_to_origin), and this only keeps theSpanfrom 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 ofReStatic(via
aRelateParamBoundto which the matter has in the meantime resolved),
which it isn't, and emits aRegionResolutionErrorto that effect.
What seems clear is that while terminating the higher-kinded predicate as
aReStaticis convenient, it makes it impossible to give a better error
message when the callsite's lifetime isn't'staticlater. 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 theObligationCauseinformation on what causes theReStatic
bound, which would presumably have to make it into theSubregionOriginas
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.Reacted by Eduard-Mihai Burtescu- When process_predicates sees the call site,
@tschottdorf You can add an extra field to
fulfill::RegionObligation, so that the definition span of the'ainT: 'a(no matter where the'acomes from, as long as it's a parameter of some sort) is tracked.Then, a
SubregionOriginvariant (if we don't have one already, there might be another source of these, not justfulfill) specific to an outlives bound with a parameter that turned into a'staticbound.@eddyb I think I know how to go about the second part (making a special
SubregionOriginthat 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(notRegionObligationdirectly), 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
'staticbound, and also theSpanof the higher-ranked bound (i.e. something representing some part offn 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
Spanfrom the predicate (or anything contained within) inregister_region_obligation(or higher up?). I do have aDefIdsomewhere in the binder; is that how it's done? If so, appreciate a pointer on the magical incantation.There was a reason I said it like that, because you can change the callers of
code_to_originto avoid poluttingObligationCause, because that refers to the original cause (you might have to makeSubregionOriginrecursive if it's not already).
You can get aSpanfrom theDefIdinside theRegion, if late-bound and named (which is always true for this issue) - checktcx.hirmethods for something that only works in the same crate (otherwise you'd need to addLifetimedefinition spans to crate metadata and that's a bit trickier).Reacted by Tobias GriegerAh, gotcha - thanks for the pointers, I'll put something together.
- removedT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.
on Mar 9, 2017 1 remaining item
- addedT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.and removedA-docsArea: Documentation for any part of the project, including the compiler, standard library, and toolsArea: Documentation for any part of the project, including the compiler, standard library, and tools
on Mar 22, 2017 - addedC-enhancementCategory: An issue proposing an enhancement or a PR with one.Category: An issue proposing an enhancement or a PR with one.
on Jul 22, 2017 @tschottdorf Are you still working on this?
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.
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 lifetimeIs this the error the OP was suggesting?
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 errorIs this good enough? I think this was changed through #95565. I tested it again after reading the blog post by Jack Huey.
- addedE-hardCall for participation: Hard difficulty. Experience needed to fix: A lot.Call for participation: Hard difficulty. Experience needed to fix: A lot.and removedE-easyCall for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue.Call for participation: Easy difficulty. Experience needed to fix: Not much. Good first issue.
on Jul 4, 2022 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 {} | ^^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?
The current output is
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 a current limitation of the type system, this implies a `'static` lifetime --> src/main.rs:1:19 | 1 | fn foo<T>() where for<'a> T: 'a {} | ^^^^^^^^^^^^^We should probably change the message in the note, but otherwise I feel like we can close this ticket, after ensuring we have a test for this.
- removedE-hardCall for participation: Hard difficulty. Experience needed to fix: A lot.Call for participation: Hard difficulty. Experience needed to fix: A lot.
on Aug 7, 2026 @estebank what do you think needs changing about the note?
Maybe changing the "due to a limitation" we could state why it is a limitation? But either way I feel like we're already in a better place than we were before.
Example:
Error:
It's not necessarily obvious why the type has to be
'staticin this case.