mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
9eb03e5bd2
Do not use non-wf input expectations from fudge when checking function calls cc https://github.com/rust-lang/rust/issues/149379 r? lcnr # FCP: Do not use non-wf input expectations from fudge when checking function calls ## What is fudging? https://github.com/rust-lang/rust/blob/71e00273c0921e1bc850ae8cc4161fbb44cfa848/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs#L168-L170 Consider this coercion site: `let _: Box<dyn Fn(&str) -> usize> = Box::new(|s| s.len());` We rely on the expectation to eagerly infer the type of `s` to be `&str`. However, `dyn Fn(&str) -> usize` is not a valid type as the argument of `Box::new`, as it is not `Sized`. *Fudging* is the mechanism we use to propagate the expectation through the `Box::new` call without constraining its generic parameter. Fudging computes the expected argument types by acting as if we're able to propagate the expected return type directly through the function, without any coercions on the return site. Given that we may actually want to coerce afterwards, we cannot actually commit any constraints here. We therefore compute the expectations for the function arguments in a `probe` and rely on *fudging* to be able to name any inference variables created inside of the probe. After the fudging step, we weaken the resulting expectation if it is an unsized type in the following lines: https://github.com/rust-lang/rust/blob/71e00273c0921e1bc850ae8cc4161fbb44cfa848/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs#L354 https://github.com/rust-lang/rust/blob/71e00273c0921e1bc850ae8cc4161fbb44cfa848/compiler/rustc_hir_typeck/src/expectation.rs#L77-L89 Because function arguments must be `Sized`, this weakening prevents us from applying wrong, unsized coercions to them. ## How fudging currently goes wrong We have an opened issue for tracking such cases: https://github.com/rust-lang/rust/issues/149379. Broadly, the failures seem to fall into two buckets. ### We may end up with non–well-formed expectations Fudging can produce an expected type that is not well-formed. That would eventually result in an error failing the well-formedness check, either when we do the coercion with the expected argument types, or when we select the remaining obligations. ```rust fn foo<T>(x: (T, ())) -> Box<T> { Box::new(x.0) } fn main() { // We use `(dyn Send, ())` as the expectation the argument. let _: Box<dyn Send> = foo(((), ())); } ``` ### Weakening fudged expectation is not covering all the cases ```rust fn field_to_box<T>(x: &(T,)) -> &T { &x.0 } fn main() { // `Expectation::rvalue_hint` only checks whether the whole argument // itself is `Sized`. It does not check whether the function requires // its generic parameters to be `Sized`. let _: &dyn Send = field_to_box(&(1,)); } ``` ## What this PR fixes ### One of the problematic cases of the issue This PR fixes the first case, by simply checking well-formedness of the each expected argument types inside the fudge scope. This is a reasonable change because: - Non well-formed expectation would result in a well-formedness error so not using such expectation wouldn't make any previously compiled code being not compiled anymore - Dropping a non well-formed expectation does not mean we stop providing expectations for argument coercions altogether. If fudging fails, we still fall back to using the types from the function signature as expectations in the usual path: https://github.com/rust-lang/rust/blob/71e00273c0921e1bc850ae8cc4161fbb44cfa848/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs#L330-L336 #### Related tests - Fixes [tests/ui/coercion/fudge-inference/fn-ret-trait-object-propagated-to-inputs-issue-149379-1.rs](https://github.com/rust-lang/rust/pull/150316/commits/5668ad597d8293dcfd8917ca6e8f78d2c06555d3#diff-1468a6d8495f7adfb4a64508f002bb934c13d13871662de6efd60433649401fd) ### Limited for `-Znext-solver` Separately (and not directly tied to the above issue), this PR also fixes a next-solver regression tracked at: https://github.com/rust-lang/trait-system-refactor-initiative/issues/259. That regression was introduced by https://github.com/rust-lang/rust/pull/149320, which started normalizing expected function input types inside the fudge scope. Because all inference-variable relationships and pending obligations are dropped out when we exit the fudge scope, we drop the ambiguous goal used to normalize, leaving us with an unconstrained inference variable in the expectation. This PR fixes that by normalizing the expectation outside the fudge scope, so the resulting normalization obligations are preserved to the `InferCtxt`'s `ObligationCtxt`. #### Related tests - Fixes [tests/ui/traits/next-solver/fudge-inference/do-not-drop-ambig-normalization.rs](https://github.com/rust-lang/rust/pull/150316/files#diff-42e97f178fbdee7c3405ae12409eb0bca4eec92488971c703b26c083eadf728a) ## Does this PR break anything I don't think there should be any breakage affecting the old solver. The separate expectation normalization change only affecting the new solver does break an existing [test](https://github.com/rust-lang/rust/pull/150316/files#diff-3b946a09e063aad2a4fa6b0893508d5ffab78763b8465abfe1f689d349fda815). This is unfortunate but I think this change should be done because - The broken test also doesn't compile with the old solver - The expectation normalization change is necessary to compile stuff supported on stable ## What this PR doesn't fix This PR doesn't fix the second case -- *Weakening fudged expectation is not covering all the cases*. @lcnr has [suggested](https://rust-lang.zulipchat.com/#narrow/channel/144729-t-types/topic/expectations.20from.20fudging.20are.20a.20mess.20.23149379/near/560625205) the following solution for that problem: > check whether a function where-bound errors without an out coercion, if so, weaken the expectation to `ExpectRvalueLikeUnsized` I experimented with this and it works well in many cases. However, on the old solver, checking where-bounds cannot reliably be treated as speculative: if we hit an overflow while checking the where-bounds, the old solver can fail the entire compilation rather than merely treating the check as failed and relaxing the expectation. Since this second class of issues affects both the old solver and the next-solver, it seems preferable to keep the conservative behavior for now, at least until the next-solver is stabilized, rather than introducing a next-solver-only relaxation that might create new regressions and complicate stabilization efforts. #### Related tests - Does NOT Fix [tests/ui/coercion/fudge-inference/expectated-input-not-satisfying-fn-bounds-issue-89299.rs](https://github.com/rust-lang/rust/pull/150316/files#diff-fdcfa8ab660c052dbe246db279d167ea8a309bfe10ca6163f7fa1836be2b30d6) - Does NOT Fix [tests/ui/coercion/fudge-inference/expectated-input-not-satisfying-fn-bounds-issue-149881.rs](https://github.com/rust-lang/rust/pull/150316/files#diff-1ccbb181cbf164841ca5af350ecf903c802a4854bda309e83e91c3b917809a55) - Does NOT Fix [tests/ui/coercion/fudge-inference/fn-ret-trait-object-propagated-to-inputs-issue-149379-3.rs](https://github.com/rust-lang/rust/pull/150316/files#diff-b12e01cc3c265db42f135d67425d8b2bd0d9c44c680b3e8c49d1f845a0b25d09)