e903b29dc3
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: https://github.com/rust-lang/rust/pull/129021#issuecomment-2297702647 r? lcnr |
||
---|---|---|
.. | ||
assembly | ||
auxiliary | ||
codegen | ||
codegen-units | ||
coverage | ||
coverage-run-rustdoc | ||
crashes | ||
debuginfo | ||
incremental | ||
mir-opt | ||
pretty | ||
run-make | ||
run-pass-valgrind | ||
rustdoc | ||
rustdoc-gui | ||
rustdoc-js | ||
rustdoc-js-std | ||
rustdoc-json | ||
rustdoc-ui | ||
ui | ||
ui-fulldeps | ||
COMPILER_TESTS.md |