Rollup merge of #91551 - b-naber:const-eval-normalization-ice, r=oli-obk

Allow for failure of subst_normalize_erasing_regions in const_eval

Fixes https://github.com/rust-lang/rust/issues/72845

Using associated types that cannot be normalized previously resulted in an ICE. We now allow for normalization failure and return a "TooGeneric" error in that case.

r? ```@RalfJung``` maybe?
This commit is contained in:
Matthias Krüger 2021-12-08 16:08:07 +01:00 committed by GitHub
commit 317f750ff7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 129 additions and 12 deletions

View File

@ -7,6 +7,7 @@ use rustc_hir::{self as hir, def_id::DefId, definitions::DefPathData};
use rustc_index::vec::IndexVec; use rustc_index::vec::IndexVec;
use rustc_macros::HashStable; use rustc_macros::HashStable;
use rustc_middle::mir; use rustc_middle::mir;
use rustc_middle::mir::interpret::{InterpError, InvalidProgramInfo};
use rustc_middle::ty::layout::{self, LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::layout::{self, LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout};
use rustc_middle::ty::{ use rustc_middle::ty::{
self, query::TyCtxtAt, subst::SubstsRef, ParamEnv, Ty, TyCtxt, TypeFoldable, self, query::TyCtxtAt, subst::SubstsRef, ParamEnv, Ty, TyCtxt, TypeFoldable,
@ -14,7 +15,7 @@ use rustc_middle::ty::{
use rustc_mir_dataflow::storage::AlwaysLiveLocals; use rustc_mir_dataflow::storage::AlwaysLiveLocals;
use rustc_query_system::ich::StableHashingContext; use rustc_query_system::ich::StableHashingContext;
use rustc_session::Limit; use rustc_session::Limit;
use rustc_span::{Pos, Span}; use rustc_span::{Pos, Span, DUMMY_SP};
use rustc_target::abi::{Align, HasDataLayout, Size, TargetDataLayout}; use rustc_target::abi::{Align, HasDataLayout, Size, TargetDataLayout};
use super::{ use super::{
@ -508,7 +509,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
pub(super) fn subst_from_current_frame_and_normalize_erasing_regions<T: TypeFoldable<'tcx>>( pub(super) fn subst_from_current_frame_and_normalize_erasing_regions<T: TypeFoldable<'tcx>>(
&self, &self,
value: T, value: T,
) -> T { ) -> Result<T, InterpError<'tcx>> {
self.subst_from_frame_and_normalize_erasing_regions(self.frame(), value) self.subst_from_frame_and_normalize_erasing_regions(self.frame(), value)
} }
@ -518,8 +519,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
&self, &self,
frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>, frame: &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>,
value: T, value: T,
) -> T { ) -> Result<T, InterpError<'tcx>> {
frame.instance.subst_mir_and_normalize_erasing_regions(*self.tcx, self.param_env, value) frame
.instance
.try_subst_mir_and_normalize_erasing_regions(*self.tcx, self.param_env, value)
.or_else(|e| {
self.tcx.sess.delay_span_bug(
DUMMY_SP,
format!("failed to normalize {}", e.get_type_for_failure()).as_str(),
);
Err(InterpError::InvalidProgram(InvalidProgramInfo::TooGeneric))
})
} }
/// The `substs` are assumed to already be in our interpreter "universe" (param_env). /// The `substs` are assumed to already be in our interpreter "universe" (param_env).
@ -554,7 +565,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
let layout = from_known_layout(self.tcx, self.param_env, layout, || { let layout = from_known_layout(self.tcx, self.param_env, layout, || {
let local_ty = frame.body.local_decls[local].ty; let local_ty = frame.body.local_decls[local].ty;
let local_ty = let local_ty =
self.subst_from_frame_and_normalize_erasing_regions(frame, local_ty); self.subst_from_frame_and_normalize_erasing_regions(frame, local_ty)?;
self.layout_of(local_ty) self.layout_of(local_ty)
})?; })?;
if let Some(state) = frame.locals.get(local) { if let Some(state) = frame.locals.get(local) {
@ -702,7 +713,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
for const_ in &body.required_consts { for const_ in &body.required_consts {
let span = const_.span; let span = const_.span;
let const_ = let const_ =
self.subst_from_current_frame_and_normalize_erasing_regions(const_.literal); self.subst_from_current_frame_and_normalize_erasing_regions(const_.literal)?;
self.mir_const_to_op(&const_, None).map_err(|err| { self.mir_const_to_op(&const_, None).map_err(|err| {
// If there was an error, set the span of the current frame to this constant. // If there was an error, set the span of the current frame to this constant.
// Avoiding doing this when evaluation succeeds. // Avoiding doing this when evaluation succeeds.

View File

@ -512,7 +512,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
self.param_env, self.param_env,
self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions( self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions(
place.ty(&self.frame().body.local_decls, *self.tcx).ty place.ty(&self.frame().body.local_decls, *self.tcx).ty
))?, )?)?,
op.layout, op.layout,
)); ));
Ok(op) Ok(op)
@ -534,7 +534,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Constant(ref constant) => { Constant(ref constant) => {
let val = let val =
self.subst_from_current_frame_and_normalize_erasing_regions(constant.literal); self.subst_from_current_frame_and_normalize_erasing_regions(constant.literal)?;
// This can still fail: // This can still fail:
// * During ConstProp, with `TooGeneric` or since the `requried_consts` were not all // * During ConstProp, with `TooGeneric` or since the `requried_consts` were not all
// checked yet. // checked yet.

View File

@ -643,7 +643,7 @@ where
self.param_env, self.param_env,
self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions( self.layout_of(self.subst_from_current_frame_and_normalize_erasing_regions(
place.ty(&self.frame().body.local_decls, *self.tcx).ty place.ty(&self.frame().body.local_decls, *self.tcx).ty
))?, )?)?,
place_ty.layout, place_ty.layout,
)); ));
Ok(place_ty) Ok(place_ty)

View File

@ -276,7 +276,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
} }
NullaryOp(null_op, ty) => { NullaryOp(null_op, ty) => {
let ty = self.subst_from_current_frame_and_normalize_erasing_regions(ty); let ty = self.subst_from_current_frame_and_normalize_erasing_regions(ty)?;
let layout = self.layout_of(ty)?; let layout = self.layout_of(ty)?;
if layout.is_unsized() { if layout.is_unsized() {
// FIXME: This should be a span_bug (#80742) // FIXME: This should be a span_bug (#80742)
@ -302,7 +302,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
Cast(cast_kind, ref operand, cast_ty) => { Cast(cast_kind, ref operand, cast_ty) => {
let src = self.eval_operand(operand, None)?; let src = self.eval_operand(operand, None)?;
let cast_ty = self.subst_from_current_frame_and_normalize_erasing_regions(cast_ty); let cast_ty =
self.subst_from_current_frame_and_normalize_erasing_regions(cast_ty)?;
self.cast(&src, cast_kind, cast_ty, &dest)?; self.cast(&src, cast_kind, cast_ty, &dest)?;
} }

View File

@ -7,6 +7,7 @@ use rustc_hir::def::Namespace;
use rustc_hir::def_id::{CrateNum, DefId}; use rustc_hir::def_id::{CrateNum, DefId};
use rustc_hir::lang_items::LangItem; use rustc_hir::lang_items::LangItem;
use rustc_macros::HashStable; use rustc_macros::HashStable;
use rustc_middle::ty::normalize_erasing_regions::NormalizationError;
use std::fmt; use std::fmt;
@ -575,6 +576,23 @@ impl<'tcx> Instance<'tcx> {
} }
} }
#[inline(always)]
pub fn try_subst_mir_and_normalize_erasing_regions<T>(
&self,
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
v: T,
) -> Result<T, NormalizationError<'tcx>>
where
T: TypeFoldable<'tcx> + Clone,
{
if let Some(substs) = self.substs_for_mir_body() {
tcx.try_subst_and_normalize_erasing_regions(substs, param_env, v)
} else {
tcx.try_normalize_erasing_regions(param_env, v)
}
}
/// Returns a new `Instance` where generic parameters in `instance.substs` are replaced by /// Returns a new `Instance` where generic parameters in `instance.substs` are replaced by
/// identity parameters if they are determined to be unused in `instance.def`. /// identity parameters if they are determined to be unused in `instance.def`.
pub fn polymorphize(self, tcx: TyCtxt<'tcx>) -> Self { pub fn polymorphize(self, tcx: TyCtxt<'tcx>) -> Self {

View File

@ -115,6 +115,8 @@ impl<'tcx> TyCtxt<'tcx> {
/// Monomorphizes a type from the AST by first applying the /// Monomorphizes a type from the AST by first applying the
/// in-scope substitutions and then normalizing any associated /// in-scope substitutions and then normalizing any associated
/// types. /// types.
/// Panics if normalization fails. In case normalization might fail
/// use `try_subst_and_normalize_erasing_regions` instead.
pub fn subst_and_normalize_erasing_regions<T>( pub fn subst_and_normalize_erasing_regions<T>(
self, self,
param_substs: SubstsRef<'tcx>, param_substs: SubstsRef<'tcx>,
@ -134,6 +136,30 @@ impl<'tcx> TyCtxt<'tcx> {
let substituted = value.subst(self, param_substs); let substituted = value.subst(self, param_substs);
self.normalize_erasing_regions(param_env, substituted) self.normalize_erasing_regions(param_env, substituted)
} }
/// Monomorphizes a type from the AST by first applying the
/// in-scope substitutions and then trying to normalize any associated
/// types. Contrary to `subst_and_normalize_erasing_regions` this does
/// not assume that normalization succeeds.
pub fn try_subst_and_normalize_erasing_regions<T>(
self,
param_substs: SubstsRef<'tcx>,
param_env: ty::ParamEnv<'tcx>,
value: T,
) -> Result<T, NormalizationError<'tcx>>
where
T: TypeFoldable<'tcx>,
{
debug!(
"subst_and_normalize_erasing_regions(\
param_substs={:?}, \
value={:?}, \
param_env={:?})",
param_substs, value, param_env,
);
let substituted = value.subst(self, param_substs);
self.try_normalize_erasing_regions(param_env, substituted)
}
} }
struct NormalizeAfterErasingRegionsFolder<'tcx> { struct NormalizeAfterErasingRegionsFolder<'tcx> {

View File

@ -0,0 +1,49 @@
#![feature(generic_const_exprs)]
#![feature(specialization)]
#![allow(incomplete_features)]
//--------------------------------------------------
trait Depth {
const C: usize;
}
trait Type {
type AT: Depth;
}
//--------------------------------------------------
enum Predicate<const B: bool> {}
trait Satisfied {}
impl Satisfied for Predicate<true> {}
//--------------------------------------------------
trait Spec1 {}
impl<T: Type> Spec1 for T where Predicate<{T::AT::C > 0}>: Satisfied {}
trait Spec2 {}
//impl<T: Type > Spec2 for T where Predicate<{T::AT::C > 1}>: Satisfied {}
impl<T: Type > Spec2 for T where Predicate<true>: Satisfied {}
//--------------------------------------------------
trait Foo {
fn Bar();
}
impl<T: Spec1> Foo for T {
default fn Bar() {}
}
impl<T: Spec2> Foo for T {
//~^ ERROR conflicting implementations of trait
fn Bar() {}
}
fn main() {}

View File

@ -0,0 +1,12 @@
error[E0119]: conflicting implementations of trait `Foo`
--> $DIR/issue-72845.rs:44:1
|
LL | impl<T: Spec1> Foo for T {
| ------------------------ first implementation here
...
LL | impl<T: Spec2> Foo for T {
| ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation
error: aborting due to previous error
For more information about this error, try `rustc --explain E0119`.

View File

@ -20,7 +20,7 @@ error[E0080]: evaluation of constant value failed
LL | let x: &'static i32 = &X; LL | let x: &'static i32 = &X;
| ^ referenced constant has errors | ^ referenced constant has errors
query stack during panic: query stack during panic:
#0 [normalize_mir_const_after_erasing_regions] normalizing `main::promoted[1]` #0 [try_normalize_mir_const_after_erasing_regions] normalizing `main::promoted[1]`
#1 [optimized_mir] optimizing MIR for `main` #1 [optimized_mir] optimizing MIR for `main`
#2 [collect_and_partition_mono_items] collect_and_partition_mono_items #2 [collect_and_partition_mono_items] collect_and_partition_mono_items
end of query stack end of query stack