From 84a6e686e36b936d322434c674d3c9cff0db0ed5 Mon Sep 17 00:00:00 2001 From: lcnr Date: Fri, 8 Dec 2023 17:29:48 +0100 Subject: [PATCH] refactor writeback: emit normalization errors with new solver --- compiler/rustc_hir_typeck/src/lib.rs | 2 - compiler/rustc_hir_typeck/src/writeback.rs | 131 ++++++++++-------- .../rustc_infer/src/infer/outlives/mod.rs | 28 +--- .../rustc_trait_selection/src/solve/mod.rs | 5 +- .../src/solve/normalize.rs | 4 +- .../traits/new-solver/alias-bound-unsound.rs | 1 + .../new-solver/alias-bound-unsound.stderr | 11 +- .../generalize-proj-new-universe-index-2.rs | 2 - ...eneralize-proj-new-universe-index-2.stderr | 13 +- .../recursive-self-normalization-2.rs | 2 +- .../recursive-self-normalization-2.stderr | 8 +- .../overflow/recursive-self-normalization.rs | 2 +- .../recursive-self-normalization.stderr | 8 +- 13 files changed, 102 insertions(+), 115 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 29e4fd9239a31..6ad7b3010147c 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -283,8 +283,6 @@ fn typeck_with_fallback<'tcx>( fcx.check_asms(); - fcx.infcx.skip_region_resolution(); - let typeck_results = fcx.resolve_type_vars_in_body(body); // Consistency check our TypeckResults instance can hold all ItemLocalIds diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index cc617fd2d0be6..2fb0c9c43c4cb 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -8,12 +8,16 @@ use rustc_errors::{ErrorGuaranteed, StashKey}; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; +use rustc_middle::traits::ObligationCause; use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCoercion}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; use rustc_middle::ty::visit::TypeVisitableExt; +use rustc_middle::ty::TypeSuperFoldable; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_trait_selection::solve; +use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt; use std::mem; @@ -695,24 +699,22 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } - fn resolve(&mut self, x: T, span: &dyn Locatable) -> T + fn resolve(&mut self, value: T, span: &dyn Locatable) -> T where T: TypeFoldable>, { - let mut resolver = Resolver::new(self.fcx, span, self.body); - let x = x.fold_with(&mut resolver); - if cfg!(debug_assertions) && x.has_infer() { - span_bug!(span.to_span(self.fcx.tcx), "writeback: `{:?}` has inference variables", x); - } + let value = self.fcx.resolve_vars_if_possible(value); + let value = value.fold_with(&mut Resolver::new(self.fcx, span, self.body)); + assert!(!value.has_infer()); // We may have introduced e.g. `ty::Error`, if inference failed, make sure // to mark the `TypeckResults` as tainted in that case, so that downstream // users of the typeck results don't produce extra errors, or worse, ICEs. - if let Some(e) = resolver.replaced_with_error { - self.typeck_results.tainted_by_errors = Some(e); + if let Err(guar) = value.error_reported() { + self.typeck_results.tainted_by_errors = Some(guar); } - x + value } } @@ -732,15 +734,13 @@ impl Locatable for hir::HirId { } } -/// The Resolver. This is the type folding engine that detects -/// unresolved types and so forth. struct Resolver<'cx, 'tcx> { fcx: &'cx FnCtxt<'cx, 'tcx>, span: &'cx dyn Locatable, body: &'tcx hir::Body<'tcx>, - - /// Set to `Some` if any `Ty` or `ty::Const` had to be replaced with an `Error`. - replaced_with_error: Option, + /// Whether we should normalize using the new solver, disabled + /// both when using the old solver and when resolving predicates. + should_normalize: bool, } impl<'cx, 'tcx> Resolver<'cx, 'tcx> { @@ -749,7 +749,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { span: &'cx dyn Locatable, body: &'tcx hir::Body<'tcx>, ) -> Resolver<'cx, 'tcx> { - Resolver { fcx, span, body, replaced_with_error: None } + Resolver { fcx, span, body, should_normalize: fcx.next_trait_solver() } } fn report_error(&self, p: impl Into>) -> ErrorGuaranteed { @@ -768,6 +768,44 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { .emit(), } } + + fn handle_term( + &mut self, + value: T, + outer_exclusive_binder: impl FnOnce(T) -> ty::DebruijnIndex, + new_err: impl Fn(TyCtxt<'tcx>, ErrorGuaranteed) -> T, + ) -> T + where + T: Into> + Copy, + T: TypeFoldable>, + T: TypeSuperFoldable>, + { + let tcx = self.fcx.tcx; + // We must deeply normalize in the new solver, since later lints + // expect that types that show up in the typeck are fully + // normalized. + let value = if self.should_normalize { + let body_id = tcx.hir().body_owner_def_id(self.body.id()); + let cause = ObligationCause::misc(self.span.to_span(tcx), body_id); + let at = self.fcx.at(&cause, self.fcx.param_env); + let universes = vec![None; outer_exclusive_binder(value).as_usize()]; + solve::deeply_normalize_with_skipped_universes(at, value, universes).unwrap_or_else( + |errors| { + let guar = self.fcx.err_ctxt().report_fulfillment_errors(errors); + new_err(tcx, guar) + }, + ) + } else { + value + }; + + if value.has_non_region_infer() { + let guar = self.report_error(value); + new_err(tcx, guar) + } else { + tcx.fold_regions(value, |_, _| tcx.lifetimes.re_erased) + } + } } impl<'cx, 'tcx> TypeFolder> for Resolver<'cx, 'tcx> { @@ -775,57 +813,28 @@ impl<'cx, 'tcx> TypeFolder> for Resolver<'cx, 'tcx> { self.fcx.tcx } - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - let tcx = self.fcx.tcx; - match self.fcx.fully_resolve(t) { - Ok(t) if self.fcx.next_trait_solver() => { - // We must normalize erasing regions here, since later lints - // expect that types that show up in the typeck are fully - // normalized. - if let Ok(t) = tcx.try_normalize_erasing_regions(self.fcx.param_env, t) { - t - } else { - tcx.fold_regions(t, |_, _| tcx.lifetimes.re_erased) - } - } - Ok(t) => { - // Do not anonymize late-bound regions - // (e.g. keep `for<'a>` named `for<'a>`). - // This allows NLL to generate error messages that - // refer to the higher-ranked lifetime names written by the user. - tcx.fold_regions(t, |_, _| tcx.lifetimes.re_erased) - } - Err(_) => { - debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t); - let e = self.report_error(t); - self.replaced_with_error = Some(e); - Ty::new_error(self.fcx.tcx, e) - } - } - } - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { debug_assert!(!r.is_bound(), "Should not be resolving bound region."); self.fcx.tcx.lifetimes.re_erased } + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + self.handle_term(ty, Ty::outer_exclusive_binder, Ty::new_error) + } + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - match self.fcx.fully_resolve(ct) { - Ok(ct) => self.fcx.tcx.erase_regions(ct), - Err(_) => { - debug!("Resolver::fold_const: input const `{:?}` not fully resolvable", ct); - let e = self.report_error(ct); - self.replaced_with_error = Some(e); - ty::Const::new_error(self.fcx.tcx, e, ct.ty()) - } - } + self.handle_term(ct, ty::Const::outer_exclusive_binder, |tcx, guar| { + ty::Const::new_error(tcx, guar, ct.ty()) + }) } -} -/////////////////////////////////////////////////////////////////////////// -// During type check, we store promises with the result of trait -// lookup rather than the actual results (because the results are not -// necessarily available immediately). These routines unwind the -// promises. It is expected that we will have already reported any -// errors that may be encountered, so if the promises store an error, -// a dummy result is returned. + fn fold_predicate(&mut self, predicate: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { + // Do not normalize predicates in the new solver. The new solver is + // supposed to handle unnormalized predicates and incorrectly normalizing + // them can be unsound, e.g. for `WellFormed` predicates. + let prev = mem::replace(&mut self.should_normalize, false); + let predicate = predicate.super_fold_with(self); + self.should_normalize = prev; + predicate + } +} diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs index 0987915f4fdb3..f7129a5ad89ae 100644 --- a/compiler/rustc_infer/src/infer/outlives/mod.rs +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -3,7 +3,7 @@ use self::env::OutlivesEnvironment; use super::region_constraints::RegionConstraintData; use super::{InferCtxt, RegionResolutionError}; use crate::infer::free_regions::RegionRelations; -use crate::infer::lexical_region_resolve::{self, LexicalRegionResolutions}; +use crate::infer::lexical_region_resolve; use rustc_middle::traits::query::OutlivesBound; use rustc_middle::ty; @@ -37,32 +37,6 @@ pub fn explicit_outlives_bounds<'tcx>( } impl<'tcx> InferCtxt<'tcx> { - pub fn skip_region_resolution(&self) { - let (var_infos, _) = { - let mut inner = self.inner.borrow_mut(); - let inner = &mut *inner; - // Note: `inner.region_obligations` may not be empty, because we - // didn't necessarily call `process_registered_region_obligations`. - // This is okay, because that doesn't introduce new vars. - inner - .region_constraint_storage - .take() - .expect("regions already resolved") - .with_log(&mut inner.undo_log) - .into_infos_and_data() - }; - - let lexical_region_resolutions = LexicalRegionResolutions { - values: rustc_index::IndexVec::from_elem_n( - crate::infer::lexical_region_resolve::VarValue::Value(self.tcx.lifetimes.re_erased), - var_infos.len(), - ), - }; - - let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions)); - assert!(old_value.is_none()); - } - /// Process the region constraints and return any errors that /// result. After this, no more unification operations should be /// done -- or the compiler will panic -- but it is legal to use diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index cf3f94e26e402..7683425d7e150 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -42,9 +42,8 @@ mod trait_goals; pub use eval_ctxt::{EvalCtxt, GenerateProofTree, InferCtxtEvalExt, InferCtxtSelectExt}; pub use fulfill::FulfillmentCtxt; -pub(crate) use normalize::{ - deeply_normalize, deeply_normalize_for_diagnostics, deeply_normalize_with_skipped_universes, -}; +pub(crate) use normalize::deeply_normalize_for_diagnostics; +pub use normalize::{deeply_normalize, deeply_normalize_with_skipped_universes}; #[derive(Debug, Clone, Copy)] enum SolverMode { diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index 1e495b4d9796a..55b79e6fc3968 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -17,7 +17,7 @@ use super::FulfillmentCtxt; /// Deeply normalize all aliases in `value`. This does not handle inference and expects /// its input to be already fully resolved. -pub(crate) fn deeply_normalize<'tcx, T: TypeFoldable>>( +pub fn deeply_normalize<'tcx, T: TypeFoldable>>( at: At<'_, 'tcx>, value: T, ) -> Result>> { @@ -31,7 +31,7 @@ pub(crate) fn deeply_normalize<'tcx, T: TypeFoldable>>( /// Additionally takes a list of universes which represents the binders which have been /// entered before passing `value` to the function. This is currently needed for /// `normalize_erasing_regions`, which skips binders as it walks through a type. -pub(crate) fn deeply_normalize_with_skipped_universes<'tcx, T: TypeFoldable>>( +pub fn deeply_normalize_with_skipped_universes<'tcx, T: TypeFoldable>>( at: At<'_, 'tcx>, value: T, universes: Vec>, diff --git a/tests/ui/traits/new-solver/alias-bound-unsound.rs b/tests/ui/traits/new-solver/alias-bound-unsound.rs index 907a30103551c..ab84e3875e299 100644 --- a/tests/ui/traits/new-solver/alias-bound-unsound.rs +++ b/tests/ui/traits/new-solver/alias-bound-unsound.rs @@ -27,5 +27,6 @@ fn main() { //~| ERROR overflow evaluating the requirement `<() as Foo>::Item well-formed` //~| ERROR overflow evaluating the requirement `String <: <() as Foo>::Item` //~| ERROR overflow evaluating the requirement `&<() as Foo>::Item well-formed` + //~| ERROR overflow evaluating the requirement `<() as Foo>::Item normalizes-to _` println!("{x}"); } diff --git a/tests/ui/traits/new-solver/alias-bound-unsound.stderr b/tests/ui/traits/new-solver/alias-bound-unsound.stderr index 29d4d983c03ec..b09c22f3f41a1 100644 --- a/tests/ui/traits/new-solver/alias-bound-unsound.stderr +++ b/tests/ui/traits/new-solver/alias-bound-unsound.stderr @@ -52,6 +52,15 @@ LL | drop(<() as Foo>::copy_me(&x)); | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`) -error: aborting due to 6 previous errors +error[E0275]: overflow evaluating the requirement `<() as Foo>::Item normalizes-to _` + --> $DIR/alias-bound-unsound.rs:24:10 + | +LL | drop(<() as Foo>::copy_me(&x)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`alias_bound_unsound`) + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 7 previous errors For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-2.rs b/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-2.rs index 7fe242f4714e0..94d645a98592c 100644 --- a/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-2.rs +++ b/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-2.rs @@ -1,7 +1,5 @@ // compile-flags: -Ztrait-solver=next // known-bug: trait-system-refactor-initiative#60 -// dont-check-failure-status -// dont-check-compiler-stderr // Generalizing a projection containing an inference variable // which cannot be named by the `root_vid` can result in ambiguity. diff --git a/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-2.stderr b/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-2.stderr index b2a523ba62052..e4922b0c3e902 100644 --- a/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-2.stderr +++ b/tests/ui/traits/new-solver/generalize/generalize-proj-new-universe-index-2.stderr @@ -1,17 +1,8 @@ -error[E0284]: type annotations needed: cannot satisfy `<>::Assoc as Id>::Assoc == <>::Assoc as Id>::Assoc` +error[E0284]: type annotations needed: cannot satisfy `<::Assoc as WithAssoc< as Id>::Assoc>>::Assoc normalizes-to <>::Assoc as Id>::Assoc` --> $DIR/generalize-proj-new-universe-index-2.rs:74:5 | LL | bound::<::Assoc, as Id>::Assoc, _>() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `<>::Assoc as Id>::Assoc == <>::Assoc as Id>::Assoc` - | -note: required by a bound in `bound` - --> $DIR/generalize-proj-new-universe-index-2.rs:69:21 - | -LL | fn bound() - | ----- required by a bound in this function -LL | where -LL | T: WithAssoc, - | ^^^^^^^^^ required by this bound in `bound` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `<::Assoc as WithAssoc< as Id>::Assoc>>::Assoc normalizes-to <>::Assoc as Id>::Assoc` error: aborting due to 1 previous error diff --git a/tests/ui/traits/new-solver/overflow/recursive-self-normalization-2.rs b/tests/ui/traits/new-solver/overflow/recursive-self-normalization-2.rs index d086db475ac2c..3e10b2b595eb8 100644 --- a/tests/ui/traits/new-solver/overflow/recursive-self-normalization-2.rs +++ b/tests/ui/traits/new-solver/overflow/recursive-self-normalization-2.rs @@ -1,4 +1,3 @@ -//~ ERROR overflow // compile-flags: -Ztrait-solver=next trait Foo1 { @@ -15,6 +14,7 @@ fn needs_bar() {} fn test::Assoc2> + Foo2::Assoc1>>() { needs_bar::(); //~^ ERROR overflow evaluating the requirement `::Assoc1: Bar` + //~| ERROR overflow evaluating the requirement `::Assoc2` } fn main() {} diff --git a/tests/ui/traits/new-solver/overflow/recursive-self-normalization-2.stderr b/tests/ui/traits/new-solver/overflow/recursive-self-normalization-2.stderr index 1dc63fae98cdd..eda62b99c4405 100644 --- a/tests/ui/traits/new-solver/overflow/recursive-self-normalization-2.stderr +++ b/tests/ui/traits/new-solver/overflow/recursive-self-normalization-2.stderr @@ -1,17 +1,21 @@ error[E0275]: overflow evaluating the requirement `::Assoc1: Bar` - --> $DIR/recursive-self-normalization-2.rs:16:17 + --> $DIR/recursive-self-normalization-2.rs:15:17 | LL | needs_bar::(); | ^^^^^^^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization_2`) note: required by a bound in `needs_bar` - --> $DIR/recursive-self-normalization-2.rs:13:17 + --> $DIR/recursive-self-normalization-2.rs:12:17 | LL | fn needs_bar() {} | ^^^ required by this bound in `needs_bar` error[E0275]: overflow evaluating the requirement `::Assoc2` + --> $DIR/recursive-self-normalization-2.rs:15:5 + | +LL | needs_bar::(); + | ^^^^^^^^^^^^^^^^^^^^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization_2`) diff --git a/tests/ui/traits/new-solver/overflow/recursive-self-normalization.rs b/tests/ui/traits/new-solver/overflow/recursive-self-normalization.rs index d15df7dea73e4..36ef856a46632 100644 --- a/tests/ui/traits/new-solver/overflow/recursive-self-normalization.rs +++ b/tests/ui/traits/new-solver/overflow/recursive-self-normalization.rs @@ -1,4 +1,3 @@ -//~ ERROR overflow evaluating the requirement `::Assoc` [E0275] // compile-flags: -Ztrait-solver=next trait Foo { @@ -11,6 +10,7 @@ fn needs_bar() {} fn test::Assoc>>() { needs_bar::(); //~^ ERROR overflow evaluating the requirement `::Assoc: Bar` + //~| ERROR overflow evaluating the requirement `::Assoc` [E0275] } fn main() {} diff --git a/tests/ui/traits/new-solver/overflow/recursive-self-normalization.stderr b/tests/ui/traits/new-solver/overflow/recursive-self-normalization.stderr index afc5bfa54accd..b0a0a69761aa4 100644 --- a/tests/ui/traits/new-solver/overflow/recursive-self-normalization.stderr +++ b/tests/ui/traits/new-solver/overflow/recursive-self-normalization.stderr @@ -1,17 +1,21 @@ error[E0275]: overflow evaluating the requirement `::Assoc: Bar` - --> $DIR/recursive-self-normalization.rs:12:17 + --> $DIR/recursive-self-normalization.rs:11:17 | LL | needs_bar::(); | ^^^^^^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization`) note: required by a bound in `needs_bar` - --> $DIR/recursive-self-normalization.rs:9:17 + --> $DIR/recursive-self-normalization.rs:8:17 | LL | fn needs_bar() {} | ^^^ required by this bound in `needs_bar` error[E0275]: overflow evaluating the requirement `::Assoc` + --> $DIR/recursive-self-normalization.rs:11:5 + | +LL | needs_bar::(); + | ^^^^^^^^^^^^^^^^^^^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`recursive_self_normalization`)