-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Enforce supertrait outlives obligations hold when confirming impl #124336
Conversation
@bors try |
[crater] Enforce supertrait outlives obligations hold when confirming impl r? `@lcnr` Fixes rust-lang#98117
TODO: also do this in the new solver |
@craterbot check |
🚨 Error: missing start toolchain 🆘 If you have any trouble with Crater please ping |
@craterbot run mode=check-only start=master#5557f8c9d08d7f3f680943dcf97b6d4a7eb13aea end=try#311f8b6ca41358db8ae08490a9ca0916f11e9de3 |
👌 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
🚧 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
🎉 Experiment
|
seems the crater run wasnt too happy @rustbot author |
@craterbot run mode=check-only start=master#5557f8c9d08d7f3f680943dcf97b6d4a7eb13aea end=try#311f8b6ca41358db8ae08490a9ca0916f11e9de3 crates=https://crater-reports.s3.amazonaws.com/pr-124336/retry-regressed-list.txt |
👌 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
🚧 Experiment ℹ️ Crater is a tool to run experiments across parts of the Rust ecosystem. Learn more |
🎉 Experiment
|
better, but still some regressions (if I read the report correctly) @rustbot author |
@@ -2787,6 +2789,34 @@ impl<'tcx> SelectionContext<'_, 'tcx> { | |||
}); | |||
} | |||
|
|||
if matches!(self.tcx().def_kind(def_id), DefKind::Impl { of_trait: true }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if matches!(self.tcx().def_kind(def_id), DefKind::Impl { of_trait: true }) | |
// Register any outlives obligations from the trait here, cc #124336. | |
if matches!(self.tcx().def_kind(def_id), DefKind::Impl { of_trait: true }) |
// We currently elaborate all supertrait obligations from impls. This | ||
// can be removed when we actually do coinduction correctly and just | ||
// register that the impl header must be WF. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this comment doesn't feel right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it should be "actually do coinduction correctly and just register supertrait obligations always". The impl header WF check is a totally different approach, yeah.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We also don't elaborate all supertrait obligations 😅 we only elaborate region obligations as elaborating all of them causes inductive solver cycles
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well I think the point here is that we do deep elaboration elaborate(...)
rather than shallow elaboration tcx.explicit_supertraits(...)
. I'll reword the comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
r=me after comments
9f39b2e
to
fa9ae7b
Compare
@bors rollup=never since i guess we're elaborating more here |
☀️ Test successful - checks-actions |
Finished benchmarking commit (2b78d92): comparison URL. Overall result: ❌ regressions - ACTION NEEDEDNext Steps: If you can justify the regressions found in this perf run, please indicate this with @rustbot label: +perf-regression Instruction countThis is a highly reliable metric that was used to determine the overall result at the top of this comment.
Max RSS (memory usage)Results (primary 1.9%, secondary 1.5%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesResults (primary 1.4%, secondary 4.0%)This is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
Binary sizeThis benchmark run did not return any relevant results for this metric. Bootstrap: 758.182s -> 759.596s (0.19%) |
Ruh roh! |
Let me see if I can queryify the transitive supertrait outlives here... |
…, r=<try> [perf] Cache supertrait outlives of impl header for soundness check Try to win back some perf from rust-lang#124336 (comment) r? `@ghost`
…, r=lcnr Cache supertrait outlives of impl header for soundness check This caches the results of computing the transitive supertraits of an impl and filtering it to its outlives obligations. This is purely an optimization to improve rust-lang#124336.
Cache supertrait outlives of impl header for soundness check This caches the results of computing the transitive supertraits of an impl and filtering it to its outlives obligations. This is purely an optimization to improve rust-lang/rust#124336.
TL;DR: We elaborate super-predicates and apply any outlives obligations when proving an impl holds to fix a mismatch between implied bounds.
Bugs in implied bounds (and implied well-formedness) occur whenever there is a mismatch between the assumptions that some code can assume to hold, and the obligations that a caller/user of that code must prove. If the former is stronger than the latter, then unsoundness occurs.
Take a look at the example unsoundness:
This specific example occurs because we elaborate obligations in
fn foo
:&'static S: Static
&'static S: 'static
<- super predicateS: 'static
<- elaborating outlives boundsHowever, when calling
foo
, we only need to prove the direct set of where clauses. So at the call site for some substitutionS = &'not_static str
, that means only proving&'static &'not_static str: Static
. To prove this, we apply the impl, which itself holds trivially since it has no where clauses.This is the mismatch --
foo
is allowed to assume thatS: 'static
via elaborating supertraits, but callers offoo
never need to prove thatS: 'static
.There are several approaches to fixing this, all of which have problems due to current limitations in our type system:
foo
would have to both prove&'static &'not_static str: Static
and its elaborated bounds, which would surface the problematic'not_static: 'static
outlives obligation.&'static &'not_static str: Static
, we'd need to proveWF(&'static &'not_static str)
, which would surface the problematic'not_static: 'static
outlives obligation.To get around these limitations, we apply a subset of (1.), which is to elaborate the supertrait obligations of the impl but filter only the (region/type) outlives out of that set, since those can never participate in an inductive cycle. This is likely not sufficient to fix a pathological example of this issue, but it does clearly fill in a major gap that we're currently overlooking.
This can also result in 'unintended' errors due to missing implied-bounds on binders. We did not encounter this in the crater run and don't expect people to rely on this code in practice:
Fixes #98117
Crater: #124336 (comment)
Triaged: #124336 (comment)
All of the fallout is due to generic const exprs, and can be ignored.