rust-lang/rust · #163442

Fix fudging

jdonszelmann · merged Oct 14, 20269 files · 215 + / 96 −
compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs65 + / 57 −
@@ -17,11 +17,11 @@ use rustc_hir_analysis::check::potentially_plural_count; use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, ResolvedStructPath}; use rustc_index::IndexVec; use rustc_infer::infer::{-    BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TypeTrace, relate,+    BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk, TypeTrace, relate, }; use rustc_infer::traits::WellFormedLoc; use rustc_middle::ty::adjustment::AllowTwoPhase;-use rustc_middle::ty::error::{ExpectedFound, TypeError};+use rustc_middle::ty::error::TypeError; use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt, Unnormalized};@@ -293,22 +293,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {                     expected_input_tys                         .into_iter()                         .zip(formal_input_tys)-                        // if the expected input type is structurally equal to the formal input type,-                        // i.e. we've only changed some inference variables around, keep the formal-                        // input ty as the expected input ty. Usually fudging helps because it gains-                        // information from a callsite of a function. However, Fudging also sometimes-                        // loses information, when the original, formal, input type had constraints on it,-                        // and fudging replaces all inference variables with fresh ones, those constraints-                        // are discarded. This check makes sure we only keep fudging output if structural-                        // changes were made to the type. If all that was changed were some typevars,-                        // we go back to the unfudged formal input type.                         .map(|(expected_input_ty, formal_input_ty)| {-                            if same_type_modulo_vars(tcx, expected_input_ty, *formal_input_ty) {-                                // if they're the same, fall back to the formal input type-                                *formal_input_ty-                            } else {-                                expected_input_ty-                            }+                            only_keep_structural_changes(+                                &self.infcx,+                                *formal_input_ty,+                                expected_input_ty,+                            )                         })                         .collect()                 }))@@ -3612,50 +3602,63 @@ enum SuggestionText {     DidYouMean, } -fn same_type_modulo_vars<'tcx>(tcx: TyCtxt<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> bool {-    struct SameModuloVars<'tcx> {-        tcx: TyCtxt<'tcx>,+/// Postprocessing step of fudging: after fudging, any changes from the formal input type to the+/// expected input type are only kept, if they result in structural changes. If the only change is+/// one inference variable changing to another (fresh) inference variable, the formal one is kept,+/// not the fresh one. This avoids cases in which those fresh inference variables are *less*+/// constrained than the original formal ones, and making them fresh has no benefit.+fn only_keep_structural_changes<'tcx>(+    infcx: &InferCtxt<'tcx>,+    formal: Ty<'tcx>,+    expected: Ty<'tcx>,+) -> Ty<'tcx> {+    struct OnlyKeepStructuralChanges<'a, 'tcx> {+        infcx: &'a InferCtxt<'tcx>,     }-    impl<'tcx> TypeRelation<TyCtxt<'tcx>> for SameModuloVars<'tcx> {+    impl<'tcx> TypeRelation<TyCtxt<'tcx>> for OnlyKeepStructuralChanges<'_, 'tcx> {         fn cx(&self) -> TyCtxt<'tcx> {-            self.tcx+            self.infcx.tcx         }          fn relate_ty_args(             &mut self,-            a_ty: Ty<'tcx>,-            _b_ty: Ty<'tcx>,-            _ty_def_id: DefId,-            a_args: ty::GenericArgsRef<'tcx>,-            b_args: ty::GenericArgsRef<'tcx>,-            _mk: impl FnOnce(ty::GenericArgsRef<'tcx>) -> Ty<'tcx>,+            formal_ty: Ty<'tcx>,+            _expected_ty: Ty<'tcx>,+            _def_id: DefId,+            formal_args: ty::GenericArgsRef<'tcx>,+            expected_args: ty::GenericArgsRef<'tcx>,+            mk: impl FnOnce(ty::GenericArgsRef<'tcx>) -> Ty<'tcx>,         ) -> RelateResult<'tcx, Ty<'tcx>> {-            relate::relate_args_invariantly(self, a_args, b_args)?;-            Ok(a_ty)+            let new_args = relate::relate_args_invariantly(self, formal_args, expected_args)?;++            if new_args == formal_args { Ok(formal_ty) } else { Ok(mk(new_args)) }         }          fn relate_with_variance<T: Relate<TyCtxt<'tcx>>>(             &mut self,             _variance: ty::Variance,             _info: ty::VarianceDiagInfo<TyCtxt<'tcx>>,-            a: T,-            b: T,+            formal: T,+            expected: T,         ) -> RelateResult<'tcx, T> {-            self.relate(a, b)+            self.relate(formal, expected)         } -        fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {-            if a == b {-                return Ok(a);+        fn tys(&mut self, formal: Ty<'tcx>, expected: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {+            if formal == expected {+                return Ok(formal);             } -            match (a.kind(), b.kind()) {+            match (formal.kind(), expected.kind()) {+                // If both are an infer var, keep the formal one.                 (&ty::Infer(ty::InferTy::TyVar(_)), &ty::Infer(ty::InferTy::TyVar(_)))                 | (&ty::Infer(ty::InferTy::FloatVar(_)), &ty::Infer(ty::InferTy::FloatVar(_)))-                | (&ty::Infer(ty::InferTy::IntVar(_)), &ty::Infer(ty::InferTy::IntVar(_))) => Ok(a),-                (&ty::Infer(_), _) | (_, &ty::Infer(_)) => Err(TypeError::Mismatch),+                | (&ty::Infer(ty::InferTy::IntVar(_)), &ty::Infer(ty::InferTy::IntVar(_))) => {+                    Ok(formal)+                }+                (&ty::Infer(_), _) | (_, &ty::Infer(_)) => Ok(expected),                 (&ty::Error(guar), _) | (_, &ty::Error(guar)) => Ok(Ty::new_error(self.cx(), guar)),-                _ => relate::structurally_relate_tys(self, a, b),+                _ => relate::structurally_relate_tys(self, formal, expected),             }         } @@ -3669,42 +3672,47 @@ fn same_type_modulo_vars<'tcx>(tcx: TyCtxt<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> b          fn consts(             &mut self,-            mut a: ty::Const<'tcx>,-            mut b: ty::Const<'tcx>,+            mut formal: ty::Const<'tcx>,+            mut expected: ty::Const<'tcx>,         ) -> RelateResult<'tcx, ty::Const<'tcx>> {-            if a == b {-                return Ok(a);+            if formal == expected {+                return Ok(formal);             }              // Avoid ICEs when in gce, and `structurally_relate_consts`             // turns a non-infer const into an infer const-            if self.tcx.features().generic_const_exprs() {-                a = self.tcx.expand_abstract_consts(a);-                b = self.tcx.expand_abstract_consts(b);+            if self.cx().features().generic_const_exprs() {+                formal = self.cx().expand_abstract_consts(formal);+                expected = self.cx().expand_abstract_consts(expected);             } -            match (a.kind(), b.kind()) {-                (ty::ConstKind::Infer(_), ty::ConstKind::Infer(_)) => return Ok(a),-                (ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => {-                    return Err(TypeError::ConstMismatch(ExpectedFound::new(a, b)));-                }+            match (formal.kind(), expected.kind()) {+                // If both are an infer var, keep the formal one.+                (ty::ConstKind::Infer(_), ty::ConstKind::Infer(_)) => return Ok(formal),+                (ty::ConstKind::Infer(_), _) | (_, ty::ConstKind::Infer(_)) => return Ok(formal),+                 _ => {}             } -            relate::structurally_relate_consts(self, a, b)+            relate::structurally_relate_consts(self, formal, expected)         }          fn binders<T>(             &mut self,-            a: ty::Binder<'tcx, T>,-            b: ty::Binder<'tcx, T>,+            formal: ty::Binder<'tcx, T>,+            expected: ty::Binder<'tcx, T>,         ) -> RelateResult<'tcx, ty::Binder<'tcx, T>>         where             T: Relate<TyCtxt<'tcx>>,         {-            Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?))+            Ok(formal.rebind(self.relate(formal.skip_binder(), expected.skip_binder())?))         }     } -    SameModuloVars { tcx }.relate(a, b).is_ok()+    // If it errors for some reason, keep the expected type, not the formal one, keeping the fudging.+    let res = OnlyKeepStructuralChanges { infcx }.relate(formal, expected).unwrap_or(expected);+    tracing::debug!(+        "formal: {formal:?} and expected {expected:?} => only keeping structural changes creates {res:?}"+    );+    res }
tests/ui/coercion/fudge-inference/fudge-eager-resolve-breakage-3.rsadded42 + / 0 −
@@ -0,0 +1,42 @@+//@ check-pass+// When we changed from lazy resolving to eager resolving, this was in some cases breaking+// We introduced a workaround in https://github.com/rust-lang/rust/pull/158447 to more often+// avoid breakage. This workaround was not perfect, and we knew this when landing,+// we just had no examples that showed the breakage until this one.+//+// The workaround would try to fudge a formally expected type, but if no structural changes+// to the type happened, would rollback the fudging again, and stuck to the formal expected type.+//+// This rollback behavior worked per function argument, and that's what goes wrong in this+// reproducer: g() is called with two parameters. The 2nd parameter gets fudged, and has a+// structural change (the return type is inferred to unit), so fudging gets accepted,+// not rolled back. In the process, the input of the closure, `x` becomes an unconstrained+// type variable, and type inference fails, despite us knowing that its type is `X` already,+// by the difinion of `g`.++fn f<T>() -> X {+    let v = h(g(X, |x| x.m()));+    //         ^^^^^^^^^^^^^^ two parameters+    v+}++fn g<T, U>(_: T, _: fn(T) -> U) -> Y<T, U> {+    loop {}+}++fn h<U>(_: Y<U, ()>) -> U {+    loop {}+}++struct X;++impl X {+    fn m(self) {}+}++struct Y<T, U> {+    a: fn(T) -> T,+    b: U,+}++fn main() {}
tests/ui/const-generics/const-argument-cross-crate-mismatch.stderr16 + / 17 −
@@ -1,30 +1,29 @@ error[E0308]: mismatched types-  --> $DIR/const-argument-cross-crate-mismatch.rs:6:67+  --> $DIR/const-argument-cross-crate-mismatch.rs:6:41    | LL |     let _ = const_generic_lib::function(const_generic_lib::Struct([0u8, 1u8]));-   |                                         ------------------------- ^^^^^^^^^^ expected an array with a size of 3, found one with a size of 2-   |                                         |-   |                                         arguments to this struct are incorrect+   |             --------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `3`, found `2`+   |             |+   |             arguments to this function are incorrect    |-note: tuple struct defined here-  --> $DIR/auxiliary/const_generic_lib.rs:1:12+   = note: expected struct `Struct<3>`+              found struct `Struct<2>`+note: function defined here+  --> $DIR/auxiliary/const_generic_lib.rs:5:8    |-LL | pub struct Struct<const N: usize>(pub [u8; N]);-   |            ^^^^^^+LL | pub fn function(value: Struct<3>) -> u8 {+   |        ^^^^^^^^  error[E0308]: mismatched types-  --> $DIR/const-argument-cross-crate-mismatch.rs:8:65+  --> $DIR/const-argument-cross-crate-mismatch.rs:8:39    | LL |     let _: const_generic_lib::Alias = const_generic_lib::Struct([0u8, 1u8, 2u8]);-   |                                       ------------------------- ^^^^^^^^^^^^^^^ expected an array with a size of 2, found one with a size of 3-   |                                       |-   |                                       arguments to this struct are incorrect+   |            ------------------------   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `2`, found `3`+   |            |+   |            expected due to this    |-note: tuple struct defined here-  --> $DIR/auxiliary/const_generic_lib.rs:1:12-   |-LL | pub struct Struct<const N: usize>(pub [u8; N]);-   |            ^^^^^^+   = note: expected struct `Struct<2>`+              found struct `Struct<3>`  error: aborting due to 2 previous errors 
tests/ui/const-generics/generic_const_exprs/issue-62504.full.stderr29 + / 7 −
@@ -1,25 +1,47 @@+error: unconstrained generic constant+  --> $DIR/issue-62504.rs:18:25+   |+LL |         ArrayHolder([0; Self::SIZE])+   |                         ^^^^^^^^^^+   |+help: try adding a `where` bound+   |+LL |     pub const fn new() -> Self where [(); Self::SIZE]: {+   |                                +++++++++++++++++++++++++error: unconstrained generic constant+  --> $DIR/issue-62504.rs:18:9+   |+LL |         ArrayHolder([0; Self::SIZE])+   |         ^^^^^^^^^^^+   |+help: try adding a `where` bound+   |+LL |     pub const fn new() -> Self where [(); Self::SIZE]: {+   |                                ++++++++++++++++++++++++ error[E0308]: mismatched types-  --> $DIR/issue-62504.rs:18:21+  --> $DIR/issue-62504.rs:18:9    | LL |         ArrayHolder([0; Self::SIZE])-   |                     ^^^^^^^^^^^^^^^ expected `X`, found `Self::SIZE`+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `X`, found `Self::SIZE`    |    = note: expected constant `X`               found constant `Self::SIZE`  error: unconstrained generic constant-  --> $DIR/issue-62504.rs:18:25+  --> $DIR/issue-62504.rs:18:9    | LL |         ArrayHolder([0; Self::SIZE])-   |                         ^^^^^^^^^^+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^    | help: try adding a `where` bound    | LL |     pub const fn new() -> Self where [(); Self::SIZE]: {    |                                +++++++++++++++++++++++  error[E0284]: type annotations needed for `ArrayHolder<_>`-  --> $DIR/issue-62504.rs:26:9+  --> $DIR/issue-62504.rs:30:9    | LL |     let mut array = ArrayHolder::new();    |         ^^^^^^^^^   ----------- type must be known at this point@@ -35,7 +57,7 @@ LL |     let mut array: ArrayHolder<X> = ArrayHolder::new();    |                  ++++++++++++++++  error[E0284]: type annotations needed for `ArrayHolder<_>`-  --> $DIR/issue-62504.rs:26:9+  --> $DIR/issue-62504.rs:30:9    | LL |     let mut array = ArrayHolder::new();    |         ^^^^^^^^^   ------------------ type must be known at this point@@ -52,7 +74,7 @@ help: consider giving `array` an explicit type, where the value of const paramet LL |     let mut array: ArrayHolder<X> = ArrayHolder::new();    |                  ++++++++++++++++ -error: aborting due to 4 previous errors+error: aborting due to 6 previous errors  Some errors have detailed explanations: E0284, E0308. For more information about an error, try `rustc --explain E0284`.
tests/ui/const-generics/generic_const_exprs/issue-62504.min.stderr24 + / 11 −
@@ -6,22 +6,35 @@ LL |         ArrayHolder([0; Self::SIZE])    |    = note: this may fail depending on what value the parameter takes +error: constant expression depends on a generic parameter+  --> $DIR/issue-62504.rs:18:9+   |+LL |         ArrayHolder([0; Self::SIZE])+   |         ^^^^^^^^^^^+   |+   = note: this may fail depending on what value the parameter takes+ error[E0308]: mismatched types-  --> $DIR/issue-62504.rs:18:21+  --> $DIR/issue-62504.rs:18:9    |+LL |     pub const fn new() -> Self {+   |                           ---- expected `ArrayHolder<X>` because of return type LL |         ArrayHolder([0; Self::SIZE])-   |         ----------- ^^^^^^^^^^^^^^^ expected an array with a size of X, found one with a size of Self::SIZE-   |         |-   |         arguments to this struct are incorrect+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `X`, found `Self::SIZE`    |-note: tuple struct defined here-  --> $DIR/issue-62504.rs:14:8+   = note: expected struct `ArrayHolder<X>`+              found struct `ArrayHolder<Self::SIZE>`++error: constant expression depends on a generic parameter+  --> $DIR/issue-62504.rs:18:9    |-LL | struct ArrayHolder<const X: usize>([u32; X]);-   |        ^^^^^^^^^^^+LL |         ArrayHolder([0; Self::SIZE])+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^+   |+   = note: this may fail depending on what value the parameter takes  error[E0284]: type annotations needed for `ArrayHolder<_>`-  --> $DIR/issue-62504.rs:26:9+  --> $DIR/issue-62504.rs:30:9    | LL |     let mut array = ArrayHolder::new();    |         ^^^^^^^^^   ----------- type must be known at this point@@ -37,7 +50,7 @@ LL |     let mut array: ArrayHolder<X> = ArrayHolder::new();    |                  ++++++++++++++++  error[E0284]: type annotations needed for `ArrayHolder<_>`-  --> $DIR/issue-62504.rs:26:9+  --> $DIR/issue-62504.rs:30:9    | LL |     let mut array = ArrayHolder::new();    |         ^^^^^^^^^   ------------------ type must be known at this point@@ -54,7 +67,7 @@ help: consider giving `array` an explicit type, where the value of const paramet LL |     let mut array: ArrayHolder<X> = ArrayHolder::new();    |                  ++++++++++++++++ -error: aborting due to 4 previous errors+error: aborting due to 6 previous errors  Some errors have detailed explanations: E0284, E0308. For more information about an error, try `rustc --explain E0284`.
tests/ui/const-generics/generic_const_exprs/issue-62504.rs5 + / 1 −
@@ -18,7 +18,11 @@ impl<const X: usize> ArrayHolder<X> {         ArrayHolder([0; Self::SIZE])         //~^ ERROR mismatched types         //[full]~^^ ERROR unconstrained generic constant-        //[min]~^^^ ERROR constant expression depends on a generic parameter+        //[full]~^^^ ERROR unconstrained generic constant+        //[full]~^^^^ ERROR unconstrained generic constant+        //[min]~^^^^^ ERROR constant expression depends on a generic parameter+        //[min]~| ERROR constant expression depends on a generic parameter+        //[min]~| ERROR constant expression depends on a generic parameter     } } 
tests/ui/const-generics/ice-unevaluated.rsrenamed1 + / 1 −
@@ -1,5 +1,5 @@-//@ known-bug: #118603 //@ compile-flags: -Copt-level=0+//@ check-pass // ignore-tidy-file-linelength  #![feature(generic_const_exprs)]
tests/ui/const-generics/transmute_no_gate.rs2 + / 0 −
@@ -83,9 +83,11 @@ fn main() {   let _ = ident([[0; 8]; 16]);   let _ = flatten([[0; 13]; 5]);   let _: [[_; 5]; 13] = coagulate([0; 65]);+  //~^ ERROR mismatched types   let _ = flatten_3d([[[0; 3]; 13]; 5]);   let _ = flatten_somewhat([[[0; 3]; 13]; 5]);   let _ = known_size([16; 13]);   let _: [u16; 5] = condense_bytes([16u8; 10]);+  //~^ ERROR mismatched types   let _ = singleton_each([16; 10]); }
tests/ui/const-generics/transmute_no_gate.stderr31 + / 2 −
@@ -1,3 +1,31 @@+error[E0308]: mismatched types+  --> $DIR/transmute_no_gate.rs:85:35+   |+LL |   let _: [[_; 5]; 13] = coagulate([0; 65]);+   |                         --------- ^^^^^^^ expected an array with a size of _ * _, found one with a size of 65+   |                         |+   |                         arguments to this function are incorrect+   |+note: function defined here+  --> $DIR/transmute_no_gate.rs:25:4+   |+LL | fn coagulate<const W: usize, const H: usize>(v: [u32; H*W]) -> [[u32; W];H] {+   |    ^^^^^^^^^                                 -------------++error[E0308]: mismatched types+  --> $DIR/transmute_no_gate.rs:90:36+   |+LL |   let _: [u16; 5] = condense_bytes([16u8; 10]);+   |                     -------------- ^^^^^^^^^^ expected an array with a size of _ * 2, found one with a size of 10+   |                     |+   |                     arguments to this function are incorrect+   |+note: function defined here+  --> $DIR/transmute_no_gate.rs:57:4+   |+LL | fn condense_bytes<const L: usize>(v: [u8; L * 2]) -> [u16; L] {+   |    ^^^^^^^^^^^^^^                 --------------+ error[E0512]: cannot transmute between types of different sizes, or dependently-sized types   --> $DIR/transmute_no_gate.rs:7:5    |@@ -79,6 +107,7 @@ LL |     std::mem::transmute(v)    = note: source type: `[[u32; 2 * H]; W + W]` (this type does not have a fixed size)    = note: target type: `[[u32; W + W]; 2 * H]` (this type does not have a fixed size) -error: aborting due to 9 previous errors+error: aborting due to 11 previous errors -For more information about this error, try `rustc --explain E0512`.+Some errors have detailed explanations: E0308, E0512.+For more information about an error, try `rustc --explain E0308`.