]> git.lizzy.rs Git - rust.git/commitdiff
Const eval error refactoring
authorOliver Schneider <git-spam-no-reply9815368754983@oli-obk.de>
Wed, 31 Jan 2018 14:06:45 +0000 (15:06 +0100)
committerOliver Schneider <git-spam-no-reply9815368754983@oli-obk.de>
Thu, 8 Mar 2018 07:34:16 +0000 (08:34 +0100)
src/librustc/ich/impls_ty.rs
src/librustc/middle/const_val.rs
src/librustc/mir/interpret/error.rs
src/librustc/ty/structural_impls.rs
src/librustc_mir/interpret/const_eval.rs
src/librustc_mir/interpret/eval_context.rs

index a398549f35930c324bf8beb81b0faf1d9c43b9c0..d5476f742c896f0b7b7ae5fca7488c19e301d94b 100644 (file)
@@ -531,6 +531,7 @@ fn hash_stable<W: StableHasherResult>(&self,
             TypeckError |
             DerefFunctionPointer |
             ExecuteMemory |
+            ReferencedConstant |
             OverflowingMath => {}
             MachineError(ref err) => err.hash_stable(hcx, hasher),
             FunctionPointerTyMismatch(a, b) => {
index d6d23c5cad8ad45f34acb74c2febcf8cc6558b73..151cc9b1ce2ab7999a3fb659bcb5637f6f3355ab 100644 (file)
@@ -173,6 +173,13 @@ pub fn report(&self,
     {
         match *self.kind {
             ErrKind::TypeckError | ErrKind::CheckMatchError => return,
+            ErrKind::Miri(ref miri, _) => {
+                match miri.kind {
+                    ::mir::interpret::EvalErrorKind::TypeckError |
+                    ::mir::interpret::EvalErrorKind::Layout(_) => return,
+                    _ => {},
+                }
+            }
             _ => {}
         }
         self.struct_error(tcx, primary_span, primary_kind).emit();
index 90d10df151530c96dc1e83161ff943d32dd68dff..bb27628fa9c73f4dab295ecd80dc3e1d4ed16414 100644 (file)
@@ -124,6 +124,9 @@ pub enum EvalErrorKind<'tcx> {
     UnimplementedTraitSelection,
     /// Abort in case type errors are reached
     TypeckError,
+    /// Cannot compute this constant because it depends on another one
+    /// which already produced an error
+    ReferencedConstant,
 }
 
 pub type EvalResult<'tcx, T = ()> = Result<T, EvalError<'tcx>>;
@@ -245,6 +248,8 @@ fn description(&self) -> &str {
                 "there were unresolved type arguments during trait selection",
             TypeckError =>
                 "encountered constants with type errors, stopping evaluation",
+            ReferencedConstant =>
+                "referenced constant has errors",
         }
     }
 }
index c1a4f5ef45884b93080b9c4ee5f795e06a45c294..243e17ee5c7ecb4d96b37cda3065d62c9ac18d3a 100644 (file)
@@ -689,6 +689,7 @@ fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option<Self::Lif
             PathNotFound(ref v) => PathNotFound(v.clone()),
             UnimplementedTraitSelection => UnimplementedTraitSelection,
             TypeckError => TypeckError,
+            ReferencedConstant => ReferencedConstant,
         };
         Some(interpret::EvalError {
             kind: kind,
index b141106aa60f551785a0d9b89426a0087c0b78c8..a4100a9d28af6a5276b2ac33c4d6f5240c84ffb6 100644 (file)
@@ -1,7 +1,6 @@
 use rustc::hir;
 use rustc::middle::const_val::{ConstEvalErr, ConstVal, ErrKind};
 use rustc::middle::const_val::ErrKind::{TypeckError, CheckMatchError};
-use rustc::traits;
 use rustc::mir;
 use rustc::ty::{self, TyCtxt, Ty, Instance};
 use rustc::ty::layout::{self, LayoutOf};
@@ -15,6 +14,7 @@
 
 use std::fmt;
 use std::error::Error;
+use std::rc::Rc;
 
 pub fn mk_borrowck_eval_cx<'a, 'mir, 'tcx>(
     tcx: TyCtxt<'a, 'tcx, 'tcx>,
@@ -236,7 +236,7 @@ fn eval_fn_call<'a>(
         let mir = match ecx.load_mir(instance.def) {
             Ok(mir) => mir,
             Err(err) => {
-                if let EvalErrorKind::NoMirFor(ref path) = *err.kind {
+                if let EvalErrorKind::NoMirFor(ref path) = err.kind {
                     return Err(
                         ConstEvalError::NeedsRfc(format!("calling extern function `{}`", path))
                             .into(),
@@ -333,15 +333,8 @@ fn init_static<'a>(
         ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
         cid: GlobalId<'tcx>,
     ) -> EvalResult<'tcx, AllocId> {
-        let param_env = ty::ParamEnv::empty(traits::Reveal::All);
         // ensure the static is computed
-        if let Err(err) = ecx.tcx.const_eval(param_env.and(cid)) {
-            match err.kind {
-                ErrKind::Miri(miri) => return Err(miri),
-                ErrKind::TypeckError => return err!(TypeckError),
-                other => bug!("const eval returned {:?}", other),
-            }
-        };
+        ecx.const_eval(cid)?;
         Ok(ecx
             .tcx
             .interpret_interner
@@ -377,52 +370,47 @@ pub fn const_val_field<'a, 'tcx>(
     span: Span,
     variant: Option<usize>,
     field: mir::Field,
-    val: Value,
+    value: Value,
     ty: Ty<'tcx>,
 ) -> ::rustc::middle::const_val::EvalResult<'tcx> {
-    match const_val_field_inner(tcx, param_env, instance, variant, field, val, ty) {
+    trace!("const_val_field: {:?}, {:?}, {:?}, {:?}", instance, field, value, ty);
+    let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
+    let result = (|| {
+        let (mut field, ty) = match value {
+            Value::ByValPair(..) | Value::ByVal(_) => ecx.read_field(value, variant, field, ty)?.expect("const_val_field on non-field"),
+            Value::ByRef(ptr, align) => {
+                let place = Place::Ptr {
+                    ptr,
+                    align,
+                    extra: variant.map_or(PlaceExtra::None, PlaceExtra::DowncastVariant),
+                };
+                let layout = ecx.layout_of(ty)?;
+                let (place, layout) = ecx.place_field(place, field, layout)?;
+                let (ptr, align) = place.to_ptr_align();
+                (Value::ByRef(ptr, align), layout.ty)
+            }
+        };
+        if let Value::ByRef(ptr, align) = field {
+            if let Some(val) = ecx.try_read_value(ptr, align, ty)? {
+                field = val;
+            }
+        }
+        Ok((field, ty))
+    })();
+    match result {
         Ok((field, ty)) => Ok(tcx.mk_const(ty::Const {
             val: ConstVal::Value(field),
             ty,
         })),
-        Err(err) => Err(ConstEvalErr {
-            span,
-            kind: err.into(),
-        }),
-    }
-}
-
-fn const_val_field_inner<'a, 'tcx>(
-    tcx: TyCtxt<'a, 'tcx, 'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    instance: ty::Instance<'tcx>,
-    variant: Option<usize>,
-    field: mir::Field,
-    value: Value,
-    ty: Ty<'tcx>,
-) -> EvalResult<'tcx, (Value, Ty<'tcx>)> {
-    trace!("const_val_field: {:?}, {:?}, {:?}, {:?}", instance, field, value, ty);
-    let mut ecx = mk_eval_cx(tcx, instance, param_env).unwrap();
-    let (mut field, ty) = match value {
-        Value::ByValPair(..) | Value::ByVal(_) => ecx.read_field(value, variant, field, ty)?.expect("const_val_field on non-field"),
-        Value::ByRef(ptr, align) => {
-            let place = Place::Ptr {
-                ptr,
-                align,
-                extra: variant.map_or(PlaceExtra::None, PlaceExtra::DowncastVariant),
-            };
-            let layout = ecx.layout_of(ty)?;
-            let (place, layout) = ecx.place_field(place, field, layout)?;
-            let (ptr, align) = place.to_ptr_align();
-            (Value::ByRef(ptr, align), layout.ty)
-        }
-    };
-    if let Value::ByRef(ptr, align) = field {
-        if let Some(val) = ecx.try_read_value(ptr, align, ty)? {
-            field = val;
-        }
+        Err(err) => {
+            let trace = ecx.generate_stacktrace(None);
+            let err = ErrKind::Miri(err, trace);
+            Err(ConstEvalErr {
+                kind: err.into(),
+                span,
+            })
+        },
     }
-    Ok((field, ty))
 }
 
 pub fn const_discr<'a, 'tcx>(
@@ -484,7 +472,7 @@ pub fn const_eval_provider<'a, 'tcx>(
         // Do match-check before building MIR
         if tcx.check_match(def_id).is_err() {
             return Err(ConstEvalErr {
-                kind: CheckMatchError,
+                kind: Rc::new(CheckMatchError),
                 span,
             });
         }
@@ -496,7 +484,7 @@ pub fn const_eval_provider<'a, 'tcx>(
         // Do not continue into miri if typeck errors occurred; it will fail horribly
         if tables.tainted_by_errors {
             return Err(ConstEvalErr {
-                kind: TypeckError,
+                kind: Rc::new(TypeckError),
                 span,
             });
         }
@@ -512,6 +500,8 @@ pub fn const_eval_provider<'a, 'tcx>(
         if tcx.is_static(def_id).is_some() {
             ecx.report(&mut err, true, None);
         }
+        let trace = ecx.generate_stacktrace(None);
+        let err = ErrKind::Miri(err, trace);
         ConstEvalErr {
             kind: err.into(),
             span,
index 676f99ea6be0c19526954dcd52c100b877017c34..483649204fbecf7a1700929892731057f307d57b 100644 (file)
@@ -10,6 +10,7 @@
 use rustc::ty::subst::{Subst, Substs};
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc_data_structures::indexed_vec::Idx;
+use rustc::middle::const_val::FrameInfo;
 use syntax::codemap::{self, DUMMY_SP, Span};
 use syntax::ast::Mutability;
 use rustc::mir::interpret::{
@@ -934,17 +935,28 @@ pub fn read_global_as_value(&self, gid: GlobalId<'tcx>, ty: Ty<'tcx>) -> EvalRes
                 return Ok(Value::ByRef(ptr.into(), layout.align))
             }
         }
-        let cv = match self.tcx.const_eval(self.param_env.and(gid)) {
-            Ok(val) => val,
-            Err(err) => match err.kind {
-                ErrKind::Miri(miri) => return Err(miri),
-                ErrKind::TypeckError => return err!(TypeckError),
-                other => bug!("const eval returned {:?}", other),
-            },
-        };
+        let cv = self.const_eval(gid)?;
         self.const_to_value(&cv.val, ty)
     }
 
+    pub fn const_eval(&self, gid: GlobalId<'tcx>) -> EvalResult<'tcx, &'tcx ty::Const<'tcx>> {
+        let param_env = if self.tcx.is_static(gid.instance.def_id()).is_some() {
+            use rustc::traits;
+            ty::ParamEnv::empty(traits::Reveal::All)
+        } else {
+            self.param_env
+        };
+        self.tcx.const_eval(param_env.and(gid)).map_err(|err| match *err.kind {
+            ErrKind::Miri(ref err, _) => match err.kind {
+                EvalErrorKind::TypeckError |
+                EvalErrorKind::Layout(_) => EvalErrorKind::TypeckError.into(),
+                _ => EvalErrorKind::ReferencedConstant.into(),
+            },
+            ErrKind::TypeckError => EvalErrorKind::TypeckError.into(),
+            ref other => bug!("const eval returned {:?}", other),
+        })
+    }
+
     pub fn force_allocation(&mut self, place: Place) -> EvalResult<'tcx, Place> {
         let new_place = match place {
             Place::Local { frame, local } => {
@@ -1496,7 +1508,7 @@ pub fn dump_local(&self, place: Place) {
 
                 match self.stack[frame].get_local(local) {
                     Err(err) => {
-                        if let EvalErrorKind::DeadLocal = *err.kind {
+                        if let EvalErrorKind::DeadLocal = err.kind {
                             write!(msg, " is dead").unwrap();
                         } else {
                             panic!("Failed to access local: {:?}", err);
@@ -1558,9 +1570,38 @@ pub fn modify_local<F>(&mut self, frame: usize, local: mir::Local, f: F) -> Eval
         Ok(())
     }
 
+    pub fn generate_stacktrace(&self, explicit_span: Option<Span>) -> Vec<FrameInfo> {
+        let mut last_span = None;
+        let mut frames = Vec::new();
+        // skip 1 because the last frame is just the environment of the constant
+        for &Frame { instance, span, .. } in self.stack().iter().skip(1).rev() {
+            // make sure we don't emit frames that are duplicates of the previous
+            if explicit_span == Some(span) {
+                last_span = Some(span);
+                continue;
+            }
+            if let Some(last) = last_span {
+                if last == span {
+                    continue;
+                }
+            } else {
+                last_span = Some(span);
+            }
+            let location = if self.tcx.def_key(instance.def_id()).disambiguated_data.data == DefPathData::ClosureExpr {
+                "closure".to_owned()
+            } else {
+                instance.to_string()
+            };
+            frames.push(FrameInfo { span, location });
+        }
+        frames
+    }
+
     pub fn report(&self, e: &mut EvalError, as_err: bool, explicit_span: Option<Span>) {
-        if let EvalErrorKind::TypeckError = *e.kind {
-            return;
+        match e.kind {
+            EvalErrorKind::Layout(_) |
+            EvalErrorKind::TypeckError => return,
+            _ => {},
         }
         if let Some(ref mut backtrace) = e.backtrace {
             let mut trace_text = "\n\nAn error occurred in miri:\n".to_string();
@@ -1618,28 +1659,8 @@ pub fn report(&self, e: &mut EvalError, as_err: bool, explicit_span: Option<Span
                 )
             };
             err.span_label(span, e.to_string());
-            let mut last_span = None;
-            // skip 1 because the last frame is just the environment of the constant
-            for &Frame { instance, span, .. } in self.stack().iter().skip(1).rev() {
-                // make sure we don't emit frames that are duplicates of the previous
-                if explicit_span == Some(span) {
-                    last_span = Some(span);
-                    continue;
-                }
-                if let Some(last) = last_span {
-                    if last == span {
-                        continue;
-                    }
-                } else {
-                    last_span = Some(span);
-                }
-                if self.tcx.def_key(instance.def_id()).disambiguated_data.data ==
-                    DefPathData::ClosureExpr
-                {
-                    err.span_note(span, "inside call to closure");
-                    continue;
-                }
-                err.span_note(span, &format!("inside call to {}", instance));
+            for FrameInfo { span, location } in self.generate_stacktrace(explicit_span) {
+                err.span_note(span, &format!("inside call to {}", location));
             }
             err.emit();
         } else {