-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rollup merge of #129021 - compiler-errors:ptr-cast-outlives, r=lcnr
Check WF of source type's signature on fn pointer cast This PR patches the implied bounds holes slightly for #129005, #25860. Like most implied bounds related unsoundness fixes, this isn't complete w.r.t. higher-ranked function signatures, but I believe it implements a pretty good heuristic for now. ### What does this do? This PR makes a partial patch for a soundness hole in a `FnDef` -> `FnPtr` "reifying" pointer cast where we were never checking that the signature we are casting *from* is actually well-formed. Because of this, and because `FnDef` doesn't require its signature to be well-formed (just its predicates must hold), we are essentially allowed to "cast away" implied bounds that are assumed within the body of the `FnDef`: ``` fn foo<'a, 'b, T>(_: &'a &'b (), v: &'b T) -> &'a T { v } fn bad<'short, T>(x: &'short T) -> &'static T { let f: fn(_, &'short T) -> &'static T = foo; f(&&(), x) } ``` In this example, subtyping ends up casting the `_` type (which should be `&'static &'short ()`) to some other type that no longer serves as a "witness" to the lifetime relationship `'short: 'static` which would otherwise be required for this call to be WF. This happens regardless of if `foo`'s lifetimes are early- or late-bound. This PR implements two checks: 1. We check that the signature of the `FnDef` is well-formed *before* casting it. This ensures that there is at least one point in the MIR where we ensure that the `FnDef`'s implied bounds are actually satisfied by the caller. 2. Implements a special case where if we're casting from a higher-ranked `FnDef` to a non-higher-ranked, we instantiate the binder of the `FnDef` with *infer vars* and ensure that it is a supertype of the target of the cast. The (2.) is necessary to validate that these pointer casts are valid for higher-ranked `FnDef`. Otherwise, the example above would still pass even if `help`'s `'a` lifetime were late-bound. ### Further work The WF checks for function calls are scattered all over the MIR. We check the WF of args in call terminators, we check the WF of `FnDef` when we create a `const` operand referencing it, and we check the WF of the return type in #115538, to name a few. One way to make this a bit cleaner is to simply extend #115538 to always check that the signature is WF for `FnDef` types. I may do this as a follow-up, but I wanted to keep this simple since this leads to some pretty bad NLL diagnostics regressions, and AFAICT this solution is *complete enough*. ### Crater triage Done here: #129021 (comment) r? lcnr
- Loading branch information
Showing
8 changed files
with
145 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
13 changes: 13 additions & 0 deletions
13
tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-2.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
//@ check-pass | ||
//@ known-bug: #25860 | ||
|
||
static UNIT: &'static &'static () = &&(); | ||
|
||
fn foo<'a, 'b, T>(_: &'a &'b (), v: &'b T, _: &()) -> &'a T { v } | ||
|
||
fn bad<'a, T>(x: &'a T) -> &'static T { | ||
let f: fn(_, &'a T, &()) -> &'static T = foo; | ||
f(UNIT, x, &()) | ||
} | ||
|
||
fn main() {} |
13 changes: 13 additions & 0 deletions
13
tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-early-bound.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// Regression test for #129021. | ||
|
||
static UNIT: &'static &'static () = &&(); | ||
|
||
fn foo<'a: 'a, 'b: 'b, T>(_: &'a &'b (), v: &'b T) -> &'a T { v } | ||
|
||
fn bad<'a, T>(x: &'a T) -> &'static T { | ||
let f: fn(_, &'a T) -> &'static T = foo; | ||
//~^ ERROR lifetime may not live long enough | ||
f(UNIT, x) | ||
} | ||
|
||
fn main() {} |
10 changes: 10 additions & 0 deletions
10
tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-early-bound.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
error: lifetime may not live long enough | ||
--> $DIR/implied-bounds-on-nested-references-plus-variance-early-bound.rs:8:12 | ||
| | ||
LL | fn bad<'a, T>(x: &'a T) -> &'static T { | ||
| -- lifetime `'a` defined here | ||
LL | let f: fn(_, &'a T) -> &'static T = foo; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` | ||
|
||
error: aborting due to 1 previous error | ||
|
19 changes: 19 additions & 0 deletions
19
tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-unnormalized.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// Regression test for #129021. | ||
|
||
trait ToArg<T> { | ||
type Arg; | ||
} | ||
impl<T, U> ToArg<T> for U { | ||
type Arg = T; | ||
} | ||
|
||
fn extend_inner<'a, 'b>(x: &'a str) -> <&'b &'a () as ToArg<&'b str>>::Arg { x } | ||
fn extend<'a, 'b>(x: &'a str) -> &'b str { | ||
(extend_inner as fn(_) -> _)(x) | ||
//~^ ERROR lifetime may not live long enough | ||
} | ||
|
||
fn main() { | ||
let y = extend(&String::from("Hello World")); | ||
println!("{}", y); | ||
} |
14 changes: 14 additions & 0 deletions
14
...s/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance-unnormalized.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
error: lifetime may not live long enough | ||
--> $DIR/implied-bounds-on-nested-references-plus-variance-unnormalized.rs:12:5 | ||
| | ||
LL | fn extend<'a, 'b>(x: &'a str) -> &'b str { | ||
| -- -- lifetime `'b` defined here | ||
| | | ||
| lifetime `'a` defined here | ||
LL | (extend_inner as fn(_) -> _)(x) | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` | ||
| | ||
= help: consider adding the following bound: `'a: 'b` | ||
|
||
error: aborting due to 1 previous error | ||
|
7 changes: 2 additions & 5 deletions
7
tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
10 changes: 10 additions & 0 deletions
10
tests/ui/implied-bounds/implied-bounds-on-nested-references-plus-variance.stderr
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
error: lifetime may not live long enough | ||
--> $DIR/implied-bounds-on-nested-references-plus-variance.rs:8:12 | ||
| | ||
LL | fn bad<'a, T>(x: &'a T) -> &'static T { | ||
| -- lifetime `'a` defined here | ||
LL | let f: fn(_, &'a T) -> &'static T = foo; | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` | ||
|
||
error: aborting due to 1 previous error | ||
|