diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs index 8d89f3e0d8700..fe9f9a37b862c 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types/mod.rs @@ -155,13 +155,6 @@ fn add_hidden_type<'tcx>( } } -fn get_hidden_type<'tcx>( - hidden_types: &DefinitionSiteHiddenTypes<'tcx>, - def_id: LocalDefId, -) -> Option>> { - hidden_types.0.get(&def_id).map(|ty| EarlyBinder::bind(*ty)) -} - #[derive(Debug)] struct DefiningUse<'tcx> { /// The opaque type using non NLL vars. This uses the actual @@ -501,7 +494,8 @@ pub(crate) fn apply_definition_site_hidden_types<'tcx>( let tcx = infcx.tcx; let mut errors = Vec::new(); for &(key, hidden_type) in opaque_types { - let Some(expected) = get_hidden_type(hidden_types, key.def_id) else { + let Some(expected) = hidden_types.0.get(&key.def_id).map(|ty| EarlyBinder::bind(*ty)) + else { if !tcx.use_typing_mode_borrowck() { if let ty::Alias(ty::Opaque, alias_ty) = hidden_type.ty.kind() && alias_ty.def_id == key.def_id.to_def_id() diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 9f5a85b692642..a615ac9d912dd 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -219,6 +219,12 @@ fn typeck_with_inspect<'tcx>( // the future. fcx.check_repeat_exprs(); + // We need to handle opaque types before emitting ambiguity errors as applying + // defining uses may guide type inference. + if fcx.next_trait_solver() { + fcx.try_handle_opaque_type_uses_next(); + } + fcx.type_inference_fallback(); // Even though coercion casts provide type hints, we check casts after fallback for diff --git a/compiler/rustc_hir_typeck/src/opaque_types.rs b/compiler/rustc_hir_typeck/src/opaque_types.rs index 4c1fe69405e91..05b5b1a8af803 100644 --- a/compiler/rustc_hir_typeck/src/opaque_types.rs +++ b/compiler/rustc_hir_typeck/src/opaque_types.rs @@ -22,35 +22,50 @@ impl<'tcx> FnCtxt<'_, 'tcx> { /// inference variables. /// /// It then uses these defining uses to guide inference for all other uses. + /// + /// Unlike `handle_opaque_type_uses_next`, this does not report errors. + #[instrument(level = "debug", skip(self))] + pub(super) fn try_handle_opaque_type_uses_next(&mut self) { + // We clone the opaques instead of stealing them here as we still need + // to use them after fallback. + let opaque_types: Vec<_> = self.infcx.clone_opaque_types(); + + self.compute_definition_site_hidden_types(opaque_types, false); + } + + /// This takes all the opaque type uses during HIR typeck. It first computes + /// the concrete hidden type by iterating over all defining uses. + /// + /// A use during HIR typeck is defining if all non-lifetime arguments are + /// unique generic parameters and the hidden type does not reference any + /// inference variables. + /// + /// It then uses these defining uses to guide inference for all other uses. #[instrument(level = "debug", skip(self))] pub(super) fn handle_opaque_type_uses_next(&mut self) { // We clone the opaques instead of stealing them here as they are still used for // normalization in the next generation trait solver. - let mut opaque_types: Vec<_> = self.infcx.clone_opaque_types(); + let opaque_types: Vec<_> = self.infcx.clone_opaque_types(); let num_entries = self.inner.borrow_mut().opaque_types().num_entries(); let prev = self.checked_opaque_types_storage_entries.replace(Some(num_entries)); debug_assert_eq!(prev, None); - for entry in &mut opaque_types { - *entry = self.resolve_vars_if_possible(*entry); - } - debug!(?opaque_types); - self.compute_definition_site_hidden_types(&opaque_types); - self.apply_definition_site_hidden_types(&opaque_types); + self.compute_definition_site_hidden_types(opaque_types, true); } } +#[derive(Copy, Clone, Debug)] enum UsageKind<'tcx> { None, NonDefiningUse(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>), UnconstrainedHiddenType(OpaqueHiddenType<'tcx>), - HasDefiningUse, + HasDefiningUse(OpaqueHiddenType<'tcx>), } impl<'tcx> UsageKind<'tcx> { fn merge(&mut self, other: UsageKind<'tcx>) { match (&*self, &other) { - (UsageKind::HasDefiningUse, _) | (_, UsageKind::None) => unreachable!(), + (UsageKind::HasDefiningUse(_), _) | (_, UsageKind::None) => unreachable!(), (UsageKind::None, _) => *self = other, // When mergining non-defining uses, prefer earlier ones. This means // the error happens as early as possible. @@ -64,7 +79,7 @@ impl<'tcx> UsageKind<'tcx> { // intended to be defining. ( UsageKind::NonDefiningUse(..) | UsageKind::UnconstrainedHiddenType(..), - UsageKind::UnconstrainedHiddenType(..) | UsageKind::HasDefiningUse, + UsageKind::UnconstrainedHiddenType(..) | UsageKind::HasDefiningUse(_), ) => *self = other, } } @@ -73,8 +88,14 @@ impl<'tcx> UsageKind<'tcx> { impl<'tcx> FnCtxt<'_, 'tcx> { fn compute_definition_site_hidden_types( &mut self, - opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], + mut opaque_types: Vec<(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)>, + error_on_missing_defining_use: bool, ) { + for entry in opaque_types.iter_mut() { + *entry = self.resolve_vars_if_possible(*entry); + } + debug!(?opaque_types); + let tcx = self.tcx; let TypingMode::Analysis { defining_opaque_types_and_generators } = self.typing_mode() else { @@ -88,19 +109,47 @@ impl<'tcx> FnCtxt<'_, 'tcx> { _ => unreachable!("not opaque or generator: {def_id:?}"), } + // We do actually need to check this the second pass (we can't just + // store this), because we can go from `UnconstrainedHiddenType` to + // `HasDefiningUse` (because of fallback) let mut usage_kind = UsageKind::None; - for &(opaque_type_key, hidden_type) in opaque_types { + for &(opaque_type_key, hidden_type) in &opaque_types { if opaque_type_key.def_id != def_id { continue; } usage_kind.merge(self.consider_opaque_type_use(opaque_type_key, hidden_type)); - if let UsageKind::HasDefiningUse = usage_kind { + + if let UsageKind::HasDefiningUse(..) = usage_kind { break; } } + if let UsageKind::HasDefiningUse(ty) = usage_kind { + for &(opaque_type_key, hidden_type) in &opaque_types { + if opaque_type_key.def_id != def_id { + continue; + } + + let expected = EarlyBinder::bind(ty.ty).instantiate(tcx, opaque_type_key.args); + self.demand_eqtype(hidden_type.span, expected, hidden_type.ty); + } + + // Being explicit here: it may be possible that we in a + // previous call to this function we did an insert, but this + // should be just fine, since they all get equated anyways and + // we shouldn't ever go from `HasDefiningUse` to anyway else. + let _ = self.typeck_results.borrow_mut().hidden_types.insert(def_id, ty); + } + + // If we're in `fn try_handle_opaque_type_uses_next` then do not + // report any errors. + if !error_on_missing_defining_use { + continue; + } + let guar = match usage_kind { + UsageKind::HasDefiningUse(_) => continue, UsageKind::None => { if let Some(guar) = self.tainted_by_errors() { guar @@ -137,7 +186,6 @@ impl<'tcx> FnCtxt<'_, 'tcx> { .emit() } } - UsageKind::HasDefiningUse => continue, }; self.typeck_results @@ -148,8 +196,9 @@ impl<'tcx> FnCtxt<'_, 'tcx> { } } + #[tracing::instrument(skip(self), ret)] fn consider_opaque_type_use( - &mut self, + &self, opaque_type_key: OpaqueTypeKey<'tcx>, hidden_type: OpaqueHiddenType<'tcx>, ) -> UsageKind<'tcx> { @@ -161,11 +210,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { ) { match err { NonDefiningUseReason::Tainted(guar) => { - self.typeck_results.borrow_mut().hidden_types.insert( - opaque_type_key.def_id, - OpaqueHiddenType::new_error(self.tcx, guar), - ); - return UsageKind::HasDefiningUse; + return UsageKind::HasDefiningUse(OpaqueHiddenType::new_error(self.tcx, guar)); } _ => return UsageKind::NonDefiningUse(opaque_type_key, hidden_type), }; @@ -193,27 +238,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> { self.tcx, DefiningScopeKind::HirTypeck, ); - - let prev = self - .typeck_results - .borrow_mut() - .hidden_types - .insert(opaque_type_key.def_id, hidden_type); - assert!(prev.is_none()); - UsageKind::HasDefiningUse - } - - fn apply_definition_site_hidden_types( - &mut self, - opaque_types: &[(OpaqueTypeKey<'tcx>, OpaqueHiddenType<'tcx>)], - ) { - let tcx = self.tcx; - for &(key, hidden_type) in opaque_types { - let expected = *self.typeck_results.borrow_mut().hidden_types.get(&key.def_id).unwrap(); - - let expected = EarlyBinder::bind(expected.ty).instantiate(tcx, key.args); - self.demand_eqtype(hidden_type.span, expected, hidden_type.ty); - } + UsageKind::HasDefiningUse(hidden_type) } /// We may in theory add further uses of an opaque after cloning the opaque diff --git a/tests/ui/traits/next-solver/opaques/hidden-types-equate-before-fallback.rs b/tests/ui/traits/next-solver/opaques/hidden-types-equate-before-fallback.rs new file mode 100644 index 0000000000000..0bd01e66b02b8 --- /dev/null +++ b/tests/ui/traits/next-solver/opaques/hidden-types-equate-before-fallback.rs @@ -0,0 +1,44 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass + +// Regression test for trait-system-refactor-initiative#240. Hidden types should +// equate *before* inference var fallback, otherwise we can get mismatched types. + +#[derive(Clone, Copy)] +struct FileSystem; +impl FileSystem { + fn build(self, commands: T) -> Option { + match false { + true => Some(commands), + false => { + drop(match self.build::<_>(commands) { + Some(x) => x, + None => return None, + }); + panic!() + }, + } + } +} + +fn build2() -> impl Sized { + if false { + build2::() + } else { + loop {} + }; + 1u32 +} + +fn build3<'a>() -> impl Sized + use<'a> { + if false { + build3() + } else { + loop {} + }; + 1u32 +} + +fn main() {}