mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 06:44:35 +00:00
implement version of normalize_erasing_regions that doesn't assume value is normalizable
This commit is contained in:
parent
2446a21595
commit
ff448cfcee
@ -1337,7 +1337,9 @@ impl<'tcx> LateLintPass<'tcx> for VariantSizeDifferences {
|
||||
let layout = match cx.layout_of(ty) {
|
||||
Ok(layout) => layout,
|
||||
Err(
|
||||
ty::layout::LayoutError::Unknown(_) | ty::layout::LayoutError::SizeOverflow(_),
|
||||
ty::layout::LayoutError::Unknown(_)
|
||||
| ty::layout::LayoutError::SizeOverflow(_)
|
||||
| ty::layout::LayoutError::NormalizationFailure(_, _),
|
||||
) => return,
|
||||
};
|
||||
let (variants, tag) = match layout.variants {
|
||||
|
@ -493,7 +493,7 @@ impl dyn MachineStopType {
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
|
||||
static_assert_size!(InterpError<'_>, 64);
|
||||
static_assert_size!(InterpError<'_>, 88);
|
||||
|
||||
pub enum InterpError<'tcx> {
|
||||
/// The program caused undefined behavior.
|
||||
|
@ -1658,6 +1658,20 @@ rustc_queries! {
|
||||
desc { "normalizing `{}`", goal.value }
|
||||
}
|
||||
|
||||
/// Do not call this query directly: invoke `try_normalize_erasing_regions` instead.
|
||||
query try_normalize_generic_arg_after_erasing_regions(
|
||||
goal: ParamEnvAnd<'tcx, GenericArg<'tcx>>
|
||||
) -> Result<GenericArg<'tcx>, NoSolution> {
|
||||
desc { "trying to normalize `{}`", goal.value }
|
||||
}
|
||||
|
||||
/// Do not call this query directly: invoke `try_normalize_erasing_regions` instead.
|
||||
query try_normalize_mir_const_after_erasing_regions(
|
||||
goal: ParamEnvAnd<'tcx, mir::ConstantKind<'tcx>>
|
||||
) -> Result<mir::ConstantKind<'tcx>, NoSolution> {
|
||||
desc { "trying to normalize `{}`", goal.value }
|
||||
}
|
||||
|
||||
query implied_outlives_bounds(
|
||||
goal: CanonicalTyGoal<'tcx>
|
||||
) -> Result<
|
||||
|
@ -1,5 +1,6 @@
|
||||
use crate::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use crate::mir::{GeneratorLayout, GeneratorSavedLocal};
|
||||
use crate::ty::normalize_erasing_regions::NormalizationError;
|
||||
use crate::ty::subst::Subst;
|
||||
use crate::ty::{self, subst::SubstsRef, ReprOptions, Ty, TyCtxt, TypeFoldable};
|
||||
use rustc_ast as ast;
|
||||
@ -199,6 +200,7 @@ pub const MAX_SIMD_LANES: u64 = 1 << 0xF;
|
||||
pub enum LayoutError<'tcx> {
|
||||
Unknown(Ty<'tcx>),
|
||||
SizeOverflow(Ty<'tcx>),
|
||||
NormalizationFailure(Ty<'tcx>, NormalizationError<'tcx>),
|
||||
}
|
||||
|
||||
impl<'tcx> fmt::Display for LayoutError<'tcx> {
|
||||
@ -208,16 +210,24 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> {
|
||||
LayoutError::SizeOverflow(ty) => {
|
||||
write!(f, "values of the type `{}` are too big for the current architecture", ty)
|
||||
}
|
||||
LayoutError::NormalizationFailure(t, e) => write!(
|
||||
f,
|
||||
"unable to determine layout for `{}` because `{}` cannot be normalized",
|
||||
t,
|
||||
e.get_type_for_failure()
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(tcx, query), level = "debug")]
|
||||
fn layout_of<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
|
||||
) -> Result<TyAndLayout<'tcx>, LayoutError<'tcx>> {
|
||||
ty::tls::with_related_context(tcx, move |icx| {
|
||||
let (param_env, ty) = query.into_parts();
|
||||
debug!(?ty);
|
||||
|
||||
if !tcx.recursion_limit().value_within_limit(icx.layout_depth) {
|
||||
tcx.sess.fatal(&format!("overflow representing the type `{}`", ty));
|
||||
@ -229,7 +239,14 @@ fn layout_of<'tcx>(
|
||||
ty::tls::enter_context(&icx, |_| {
|
||||
let param_env = param_env.with_reveal_all_normalized(tcx);
|
||||
let unnormalized_ty = ty;
|
||||
let ty = tcx.normalize_erasing_regions(param_env, ty);
|
||||
|
||||
let ty = match tcx.try_normalize_erasing_regions(param_env, ty) {
|
||||
Ok(t) => t,
|
||||
Err(normalization_error) => {
|
||||
return Err(LayoutError::NormalizationFailure(ty, normalization_error));
|
||||
}
|
||||
};
|
||||
|
||||
if ty != unnormalized_ty {
|
||||
// Ensure this layout is also cached for the normalized type.
|
||||
return tcx.layout_of(param_env.and(ty));
|
||||
|
@ -8,10 +8,28 @@
|
||||
//! or constant found within. (This underlying query is what is cached.)
|
||||
|
||||
use crate::mir;
|
||||
use crate::traits::query::NoSolution;
|
||||
use crate::ty::fold::{TypeFoldable, TypeFolder};
|
||||
use crate::ty::subst::{Subst, SubstsRef};
|
||||
use crate::ty::{self, Ty, TyCtxt};
|
||||
|
||||
#[derive(Debug, Copy, Clone, HashStable, TyEncodable, TyDecodable)]
|
||||
pub enum NormalizationError<'tcx> {
|
||||
Type(Ty<'tcx>),
|
||||
Const(ty::Const<'tcx>),
|
||||
ConstantKind(mir::ConstantKind<'tcx>),
|
||||
}
|
||||
|
||||
impl<'tcx> NormalizationError<'tcx> {
|
||||
pub fn get_type_for_failure(&self) -> String {
|
||||
match self {
|
||||
NormalizationError::Type(t) => format!("{}", t),
|
||||
NormalizationError::Const(c) => format!("{}", c),
|
||||
NormalizationError::ConstantKind(ck) => format!("{}", ck),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TyCtxt<'tcx> {
|
||||
/// Erase the regions in `value` and then fully normalize all the
|
||||
/// types found within. The result will also have regions erased.
|
||||
@ -32,6 +50,8 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
// Erase first before we do the real query -- this keeps the
|
||||
// cache from being too polluted.
|
||||
let value = self.erase_regions(value);
|
||||
debug!(?value);
|
||||
|
||||
if !value.has_projections() {
|
||||
value
|
||||
} else {
|
||||
@ -41,6 +61,44 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to erase the regions in `value` and then fully normalize all the
|
||||
/// types found within. The result will also have regions erased.
|
||||
///
|
||||
/// Contrary to `normalize_erasing_regions` this function does not assume that normalization
|
||||
/// succeeds.
|
||||
pub fn try_normalize_erasing_regions<T>(
|
||||
self,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
value: T,
|
||||
) -> Result<T, NormalizationError<'tcx>>
|
||||
where
|
||||
T: TypeFoldable<'tcx>,
|
||||
{
|
||||
debug!(
|
||||
"try_normalize_erasing_regions::<{}>(value={:?}, param_env={:?})",
|
||||
std::any::type_name::<T>(),
|
||||
value,
|
||||
param_env,
|
||||
);
|
||||
|
||||
// Erase first before we do the real query -- this keeps the
|
||||
// cache from being too polluted.
|
||||
let value = self.erase_regions(value);
|
||||
debug!(?value);
|
||||
|
||||
if !value.has_projections() {
|
||||
Ok(value)
|
||||
} else {
|
||||
let mut folder = TryNormalizeAfterErasingRegionsFolder::new(self, param_env);
|
||||
let result = value.fold_with(&mut folder);
|
||||
|
||||
match folder.found_normalization_error() {
|
||||
Some(e) => Err(e),
|
||||
None => Ok(result),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// If you have a `Binder<'tcx, T>`, you can do this to strip out the
|
||||
/// late-bound regions and then normalize the result, yielding up
|
||||
/// a `T` (with regions erased). This is appropriate when the
|
||||
@ -91,11 +149,14 @@ struct NormalizeAfterErasingRegionsFolder<'tcx> {
|
||||
}
|
||||
|
||||
impl<'tcx> NormalizeAfterErasingRegionsFolder<'tcx> {
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn normalize_generic_arg_after_erasing_regions(
|
||||
&self,
|
||||
arg: ty::GenericArg<'tcx>,
|
||||
) -> ty::GenericArg<'tcx> {
|
||||
let arg = self.param_env.and(arg);
|
||||
debug!(?arg);
|
||||
|
||||
self.tcx.normalize_generic_arg_after_erasing_regions(arg)
|
||||
}
|
||||
}
|
||||
@ -126,3 +187,69 @@ impl TypeFolder<'tcx> for NormalizeAfterErasingRegionsFolder<'tcx> {
|
||||
Ok(self.tcx.normalize_mir_const_after_erasing_regions(arg))
|
||||
}
|
||||
}
|
||||
|
||||
struct TryNormalizeAfterErasingRegionsFolder<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
normalization_error: Option<NormalizationError<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> TryNormalizeAfterErasingRegionsFolder<'tcx> {
|
||||
fn new(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self {
|
||||
TryNormalizeAfterErasingRegionsFolder { tcx, param_env, normalization_error: None }
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn try_normalize_generic_arg_after_erasing_regions(
|
||||
&self,
|
||||
arg: ty::GenericArg<'tcx>,
|
||||
) -> Result<ty::GenericArg<'tcx>, NoSolution> {
|
||||
let arg = self.param_env.and(arg);
|
||||
debug!(?arg);
|
||||
|
||||
self.tcx.try_normalize_generic_arg_after_erasing_regions(arg)
|
||||
}
|
||||
|
||||
pub fn found_normalization_error(&self) -> Option<NormalizationError<'tcx>> {
|
||||
self.normalization_error
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeFolder<'tcx> for TryNormalizeAfterErasingRegionsFolder<'tcx> {
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
match self.try_normalize_generic_arg_after_erasing_regions(ty.into()) {
|
||||
Ok(t) => t.expect_ty(),
|
||||
Err(_) => {
|
||||
self.normalization_error = Some(NormalizationError::Type(ty));
|
||||
ty
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> {
|
||||
match self.try_normalize_generic_arg_after_erasing_regions(c.into()) {
|
||||
Ok(t) => t.expect_const(),
|
||||
Err(_) => {
|
||||
self.normalization_error = Some(NormalizationError::Const(*c));
|
||||
c
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn fold_mir_const(&mut self, c: mir::ConstantKind<'tcx>) -> mir::ConstantKind<'tcx> {
|
||||
// FIXME: This *probably* needs canonicalization too!
|
||||
let arg = self.param_env.and(c);
|
||||
match self.tcx.try_normalize_mir_const_after_erasing_regions(arg) {
|
||||
Ok(c) => c,
|
||||
Err(_) => {
|
||||
self.normalization_error = Some(NormalizationError::ConstantKind(c));
|
||||
c
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,14 @@ crate fn provide(p: &mut Providers) {
|
||||
normalize_mir_const_after_erasing_regions: |tcx, goal| {
|
||||
normalize_after_erasing_regions(tcx, goal)
|
||||
},
|
||||
try_normalize_generic_arg_after_erasing_regions: |tcx, goal| {
|
||||
debug!("try_normalize_generic_arg_after_erasing_regions(goal={:#?}", goal);
|
||||
|
||||
try_normalize_after_erasing_regions(tcx, goal)
|
||||
},
|
||||
try_normalize_mir_const_after_erasing_regions: |tcx, goal| {
|
||||
try_normalize_after_erasing_regions(tcx, goal)
|
||||
},
|
||||
..*p
|
||||
};
|
||||
}
|
||||
@ -56,6 +64,38 @@ fn normalize_after_erasing_regions<'tcx, T: TypeFoldable<'tcx> + PartialEq + Cop
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(tcx))]
|
||||
fn try_normalize_after_erasing_regions<'tcx, T: TypeFoldable<'tcx> + PartialEq + Copy>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
goal: ParamEnvAnd<'tcx, T>,
|
||||
) -> Result<T, NoSolution> {
|
||||
let ParamEnvAnd { param_env, value } = goal;
|
||||
tcx.infer_ctxt().enter(|infcx| {
|
||||
let cause = ObligationCause::dummy();
|
||||
match infcx.at(&cause, param_env).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:
|
||||
debug_assert_eq!(
|
||||
normalized_obligations.iter().find(|p| not_outlives_predicate(&p.predicate)),
|
||||
None,
|
||||
);
|
||||
|
||||
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.needs_infer(), "{:?}", erased);
|
||||
Ok(erased)
|
||||
}
|
||||
Err(NoSolution) => Err(NoSolution),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn not_outlives_predicate(p: &ty::Predicate<'tcx>) -> bool {
|
||||
match p.kind().skip_binder() {
|
||||
ty::PredicateKind::RegionOutlives(..) | ty::PredicateKind::TypeOutlives(..) => false,
|
||||
|
Loading…
Reference in New Issue
Block a user