Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 23 additions & 8 deletions compiler/rustc_middle/src/ty/normalize_erasing_regions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -218,16 +222,27 @@ impl<'tcx> FallibleTypeFolder<TyCtxt<'tcx>> for TryNormalizeAfterErasingRegionsF
}

fn try_fold_ty(&mut self, ty: Ty<'tcx>) -> Result<Ty<'tcx>, 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<ty::Const<'tcx>, 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<ty::Const<'tcx>, 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)
}
}
}
120 changes: 78 additions & 42 deletions compiler/rustc_traits/src/normalize_erasing_regions.rs
Original file line number Diff line number Diff line change
@@ -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) {
Expand All @@ -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<TyCtxt<'tcx>> + PartialEq + Copy>(
tcx: TyCtxt<'tcx>,
goal: PseudoCanonicalInput<'tcx, T>,
) -> Result<T, NoSolution> {
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<Option<ty::UniverseIndex>>,
}

impl<'tcx> TypeFolder<TyCtxt<'tcx>> for FoldConsts<'_, 'tcx> {
fn cx(&self) -> TyCtxt<'tcx> {
self.ocx.infcx.tcx
}

fn fold_binder<T>(&mut self, binder: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T>
where
T: TypeFoldable<TyCtxt<'tcx>>,
{
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)
}
}
20 changes: 0 additions & 20 deletions tests/crashes/125801.rs

This file was deleted.

2 changes: 1 addition & 1 deletion tests/ui/consts/const-size_of-cycle.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ LL | bytes: [u8; std::mem::size_of::<Foo>()]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...which requires computing layout of `Foo`...
= note: ...which requires computing layout of `[u8; std::mem::size_of::<Foo>()]`...
= note: ...which requires normalizing `[u8; std::mem::size_of::<Foo>()]`...
= note: ...which requires normalizing `std::mem::size_of::<Foo>()`...
= 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
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/consts/issue-44415.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ LL | bytes: [u8; unsafe { intrinsics::size_of::<Foo>() }],
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...which requires computing layout of `Foo`...
= note: ...which requires computing layout of `[u8; unsafe { intrinsics::size_of::<Foo>() }]`...
= note: ...which requires normalizing `[u8; unsafe { intrinsics::size_of::<Foo>() }]`...
= note: ...which requires normalizing `unsafe { intrinsics::size_of::<Foo>() }`...
= 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
Expand Down
Original file line number Diff line number Diff line change
@@ -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>;

Expand Down
Original file line number Diff line number Diff line change
@@ -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`.
Loading