rust-lang/rust · #163442
Fix fudging
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`.