From bf88e1c5af5e4ee70a6cefaf9bd322b1fd6d743b Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 24 Jan 2025 03:03:12 +0000 Subject: [PATCH 1/3] Debug assert that value in normalize_erasing_regions has no escaping bound vars --- compiler/rustc_middle/src/ty/normalize_erasing_regions.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs index e86e01451fefb..96269945f0b6d 100644 --- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs +++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs @@ -46,6 +46,10 @@ impl<'tcx> TyCtxt<'tcx> { value, typing_env, ); + debug_assert!( + !value.has_escaping_bound_vars(), + "{value:?} cannot be normalized with escaping bound vars" + ); // Erase first before we do the real query -- this keeps the // cache from being too polluted. From 14393b39915dfa413b59142eed9245a0bfdfefb5 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 24 Jan 2025 03:31:25 +0000 Subject: [PATCH 2/3] Rewrite normalize_erasing_regions to not use QueryNormalizer --- .../src/normalize_erasing_regions.rs | 120 ++++++++++++------ tests/crashes/125801.rs | 20 --- .../infinite-cycle-involving-weak.rs | 2 +- .../infinite-cycle-involving-weak.stderr | 8 +- 4 files changed, 82 insertions(+), 68 deletions(-) delete mode 100644 tests/crashes/125801.rs diff --git a/compiler/rustc_traits/src/normalize_erasing_regions.rs b/compiler/rustc_traits/src/normalize_erasing_regions.rs index 68ff66bbce7cf..6dbeec26e81d7 100644 --- a/compiler/rustc_traits/src/normalize_erasing_regions.rs +++ b/compiler/rustc_traits/src/normalize_erasing_regions.rs @@ -1,9 +1,16 @@ use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::ScrubbedTraitError; +use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::traits::query::NoSolution; -use rustc_middle::ty::{self, PseudoCanonicalInput, TyCtxt, TypeFoldable, TypeVisitableExt}; -use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; -use rustc_trait_selection::traits::{Normalized, ObligationCause}; +use rustc_middle::ty::{ + self, PseudoCanonicalInput, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, TypingMode, +}; +use rustc_span::DUMMY_SP; +use rustc_trait_selection::error_reporting::InferCtxtErrorExt; +use rustc_trait_selection::error_reporting::traits::OverflowCause; +use rustc_trait_selection::traits::{self, ObligationCause, ObligationCtxt}; use tracing::debug; pub(crate) fn provide(p: &mut Providers) { @@ -17,54 +24,83 @@ pub(crate) fn provide(p: &mut Providers) { }; } +// FIXME(-Znext-solver): This can be simplified further to just a `deeply_normalize` call. fn try_normalize_after_erasing_regions<'tcx, T: TypeFoldable> + PartialEq + Copy>( tcx: TyCtxt<'tcx>, goal: PseudoCanonicalInput<'tcx, T>, ) -> Result { let PseudoCanonicalInput { typing_env, value } = goal; let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); - let cause = ObligationCause::dummy(); - match infcx.at(&cause, param_env).query_normalize(value) { - Ok(Normalized { value: normalized_value, obligations: normalized_obligations }) => { - // We don't care about the `obligations`; they are - // always only region relations, and we are about to - // erase those anyway: - // This has been seen to fail in RL, so making it a non-debug assertion to better catch - // those cases. - assert_eq!( - normalized_obligations.iter().find(|p| not_outlives_predicate(p.predicate)), - None, - ); + let ocx = ObligationCtxt::new(&infcx); + let mut normalized = + ocx.deeply_normalize(&ObligationCause::dummy(), param_env, value).map_err(|errors| { + match infcx.typing_mode() { + TypingMode::PostAnalysis => { + for error in errors { + match error { + ScrubbedTraitError::Cycle(pred) => { + infcx.err_ctxt().report_overflow_error( + OverflowCause::TraitSolver(pred.first().unwrap().predicate), + DUMMY_SP, + false, + |_| {}, + ); + } + _ => {} + } + } + } + _ => {} + } - let resolved_value = infcx.resolve_vars_if_possible(normalized_value); - // It's unclear when `resolve_vars` would have an effect in a - // fresh `InferCtxt`. If this assert does trigger, it will give - // us a test case. - debug_assert_eq!(normalized_value, resolved_value); - let erased = infcx.tcx.erase_regions(resolved_value); - debug_assert!(!erased.has_infer(), "{erased:?}"); - Ok(erased) - } - Err(NoSolution) => Err(NoSolution), + // Otherwise, bail with `NoSolution` + NoSolution + })?; + + if tcx.features().generic_const_exprs() { + normalized = + normalized.fold_with(&mut FoldConsts { ocx: &ocx, param_env, universes: vec![] }); + } + + let resolved = infcx.resolve_vars_if_possible(normalized); + let erased = tcx.erase_regions(resolved); + + if erased.has_non_region_infer() { + bug!("encountered infer when normalizing {value:?} to {erased:?}"); } + + Ok(erased) } -fn not_outlives_predicate(p: ty::Predicate<'_>) -> bool { - match p.kind().skip_binder() { - ty::PredicateKind::Clause(ty::ClauseKind::RegionOutlives(..)) - | ty::PredicateKind::Clause(ty::ClauseKind::TypeOutlives(..)) => false, - ty::PredicateKind::Clause(ty::ClauseKind::Trait(..)) - | ty::PredicateKind::Clause(ty::ClauseKind::Projection(..)) - | ty::PredicateKind::Clause(ty::ClauseKind::HostEffect(..)) - | ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(..)) - | ty::PredicateKind::NormalizesTo(..) - | ty::PredicateKind::AliasRelate(..) - | ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(..)) - | ty::PredicateKind::DynCompatible(..) - | ty::PredicateKind::Subtype(..) - | ty::PredicateKind::Coerce(..) - | ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(..)) - | ty::PredicateKind::ConstEquate(..) - | ty::PredicateKind::Ambiguous => true, +struct FoldConsts<'a, 'tcx> { + ocx: &'a ObligationCtxt<'a, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + universes: Vec>, +} + +impl<'tcx> TypeFolder> for FoldConsts<'_, 'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.ocx.infcx.tcx + } + + fn fold_binder(&mut self, binder: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T> + where + T: TypeFoldable>, + { + self.universes.push(None); + let binder = binder.super_fold_with(self); + self.universes.pop(); + binder + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + let ct = traits::with_replaced_escaping_bound_vars( + self.ocx.infcx, + &mut self.universes, + ct, + |constant| traits::evaluate_const(self.ocx.infcx, constant, self.param_env), + ); + debug!(?ct, ?self.param_env); + ct.super_fold_with(self) } } diff --git a/tests/crashes/125801.rs b/tests/crashes/125801.rs deleted file mode 100644 index aaa76bd0ba699..0000000000000 --- a/tests/crashes/125801.rs +++ /dev/null @@ -1,20 +0,0 @@ -//@ known-bug: rust-lang/rust#125801 - -#![feature(generic_const_exprs)] -#![allow(incomplete_features)] - -trait Foo { - type Output; -} - -impl Foo for [u8; 3] { - type Output = [u8; 3]; -} - -static A: <[u8; N] as Foo>::Output = [1, 2, 3]; - -fn main() { - || { - let _ = A[1]; - }; -} diff --git a/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.rs b/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.rs index 6609d4eb5a274..3a6da3329f957 100644 --- a/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.rs +++ b/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.rs @@ -1,7 +1,7 @@ +//~ ERROR overflow normalizing the opaque type #![feature(type_alias_impl_trait)] type T = impl Copy; -//~^ ERROR cannot resolve opaque type static STATIC: T = None::<&'static T>; diff --git a/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.stderr b/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.stderr index d820df472f95b..f9c29b088b2ff 100644 --- a/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.stderr +++ b/tests/ui/type-alias-impl-trait/infinite-cycle-involving-weak.stderr @@ -1,9 +1,7 @@ -error[E0720]: cannot resolve opaque type - --> $DIR/infinite-cycle-involving-weak.rs:3:10 +error[E0275]: overflow normalizing the opaque type `T::{opaque#0}` | -LL | type T = impl Copy; - | ^^^^^^^^^ cannot resolve opaque type + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`infinite_cycle_involving_weak`) error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0720`. +For more information about this error, try `rustc --explain E0275`. From 2080614fc592cb1a7d67f74905ec9efe3e8246cf Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 27 Jan 2025 20:28:07 +0000 Subject: [PATCH 3/3] Walk into types before normalizing them --- .../src/ty/normalize_erasing_regions.rs | 27 +++++++++++++------ tests/ui/consts/const-size_of-cycle.stderr | 2 +- tests/ui/consts/issue-44415.stderr | 2 +- 3 files changed, 21 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs index 96269945f0b6d..76eeb933b8970 100644 --- a/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs +++ b/compiler/rustc_middle/src/ty/normalize_erasing_regions.rs @@ -11,7 +11,7 @@ use rustc_macros::{HashStable, TyDecodable, TyEncodable}; use tracing::{debug, instrument}; use crate::traits::query::NoSolution; -use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder}; +use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable}; use crate::ty::{self, EarlyBinder, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt}; #[derive(Debug, Copy, Clone, HashStable, TyEncodable, TyDecodable)] @@ -222,16 +222,27 @@ impl<'tcx> FallibleTypeFolder> for TryNormalizeAfterErasingRegionsF } fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result, Self::Error> { - match self.try_normalize_generic_arg_after_erasing_regions(ty.into()) { - Ok(t) => Ok(t.expect_ty()), - Err(_) => Err(NormalizationError::Type(ty)), + // HACKY HACK: dont walk into binders + if let ty::Alias(..) | ty::Dynamic(..) | ty::FnPtr(..) = ty.kind() { + self.try_normalize_generic_arg_after_erasing_regions(ty.into()) + .map(|arg| arg.expect_ty()) + .map_err(|_| NormalizationError::Type(ty)) + } else if ty.has_aliases() { + ty.try_super_fold_with(self) + } else { + Ok(ty) } } - fn try_fold_const(&mut self, c: ty::Const<'tcx>) -> Result, Self::Error> { - match self.try_normalize_generic_arg_after_erasing_regions(c.into()) { - Ok(t) => Ok(t.expect_const()), - Err(_) => Err(NormalizationError::Const(c)), + fn try_fold_const(&mut self, ct: ty::Const<'tcx>) -> Result, Self::Error> { + if let ty::ConstKind::Unevaluated(..) = ct.kind() { + self.try_normalize_generic_arg_after_erasing_regions(ct.into()) + .map(|arg| arg.expect_const()) + .map_err(|_| NormalizationError::Const(ct)) + } else if ct.has_aliases() { + ct.try_super_fold_with(self) + } else { + Ok(ct) } } } diff --git a/tests/ui/consts/const-size_of-cycle.stderr b/tests/ui/consts/const-size_of-cycle.stderr index cd0ea55642545..12435115679cd 100644 --- a/tests/ui/consts/const-size_of-cycle.stderr +++ b/tests/ui/consts/const-size_of-cycle.stderr @@ -11,7 +11,7 @@ LL | bytes: [u8; std::mem::size_of::()] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: ...which requires computing layout of `Foo`... = note: ...which requires computing layout of `[u8; std::mem::size_of::()]`... - = note: ...which requires normalizing `[u8; std::mem::size_of::()]`... + = note: ...which requires normalizing `std::mem::size_of::()`... = note: ...which again requires evaluating type-level constant, completing the cycle note: cycle used when checking that `Foo` is well-formed --> $DIR/const-size_of-cycle.rs:3:1 diff --git a/tests/ui/consts/issue-44415.stderr b/tests/ui/consts/issue-44415.stderr index 641945fce9fd4..bbbd78ce279df 100644 --- a/tests/ui/consts/issue-44415.stderr +++ b/tests/ui/consts/issue-44415.stderr @@ -11,7 +11,7 @@ LL | bytes: [u8; unsafe { intrinsics::size_of::() }], | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: ...which requires computing layout of `Foo`... = note: ...which requires computing layout of `[u8; unsafe { intrinsics::size_of::() }]`... - = note: ...which requires normalizing `[u8; unsafe { intrinsics::size_of::() }]`... + = note: ...which requires normalizing `unsafe { intrinsics::size_of::() }`... = note: ...which again requires evaluating type-level constant, completing the cycle note: cycle used when checking that `Foo` is well-formed --> $DIR/issue-44415.rs:5:1