]> git.proxmox.com Git - rustc.git/blobdiff - src/librustc_const_eval/eval.rs
New upstream version 1.12.0+dfsg1
[rustc.git] / src / librustc_const_eval / eval.rs
index 6c37662206ce252f5592eeeee86853fe43c4b1e6..73b54c4374ffb65dce75dcf20a6a0bbe29545af5 100644 (file)
@@ -17,14 +17,15 @@ use self::EvalHint::*;
 
 use rustc::hir::map as ast_map;
 use rustc::hir::map::blocks::FnLikeNode;
-use rustc::middle::cstore::{self, InlinedItem};
+use rustc::middle::cstore::InlinedItem;
 use rustc::traits;
 use rustc::hir::def::{Def, PathResolution};
 use rustc::hir::def_id::DefId;
 use rustc::hir::pat_util::def_to_path;
 use rustc::ty::{self, Ty, TyCtxt, subst};
 use rustc::ty::util::IntTypeExt;
-use rustc::traits::ProjectionMode;
+use rustc::traits::Reveal;
+use rustc::util::common::ErrorReported;
 use rustc::util::nodemap::NodeMap;
 use rustc::lint;
 
@@ -43,6 +44,7 @@ use std::cmp::Ordering;
 use std::collections::hash_map::Entry::Vacant;
 
 use rustc_const_math::*;
+use rustc_errors::DiagnosticBuilder;
 
 macro_rules! math {
     ($e:expr, $op:expr) => {
@@ -140,13 +142,13 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         }
         let mut used_substs = false;
         let expr_ty = match tcx.sess.cstore.maybe_get_item_ast(tcx, def_id) {
-            cstore::FoundAst::Found(&InlinedItem::Item(ref item)) => match item.node {
+            Some((&InlinedItem::Item(_, ref item), _)) => match item.node {
                 hir::ItemConst(ref ty, ref const_expr) => {
                     Some((&**const_expr, tcx.ast_ty_to_prim_ty(ty)))
                 },
                 _ => None
             },
-            cstore::FoundAst::Found(&InlinedItem::TraitItem(trait_id, ref ti)) => match ti.node {
+            Some((&InlinedItem::TraitItem(trait_id, ref ti), _)) => match ti.node {
                 hir::ConstTraitItem(_, _) => {
                     used_substs = true;
                     if let Some(substs) = substs {
@@ -161,7 +163,7 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                 }
                 _ => None
             },
-            cstore::FoundAst::Found(&InlinedItem::ImplItem(_, ref ii)) => match ii.node {
+            Some((&InlinedItem::ImplItem(_, ref ii), _)) => match ii.node {
                 hir::ImplItemKind::Const(ref ty, ref expr) => {
                     Some((&**expr, tcx.ast_ty_to_prim_ty(ty)))
                 },
@@ -196,8 +198,8 @@ fn inline_const_fn_from_external_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     }
 
     let fn_id = match tcx.sess.cstore.maybe_get_item_ast(tcx, def_id) {
-        cstore::FoundAst::Found(&InlinedItem::Item(ref item)) => Some(item.id),
-        cstore::FoundAst::Found(&InlinedItem::ImplItem(_, ref item)) => Some(item.id),
+        Some((&InlinedItem::Item(_, ref item), _)) => Some(item.id),
+        Some((&InlinedItem::ImplItem(_, ref item), _)) => Some(item.id),
         _ => None
     };
     tcx.extern_const_fns.borrow_mut().insert(def_id,
@@ -323,7 +325,7 @@ pub fn const_expr_to_pat<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 
         hir::ExprPath(_, ref path) => {
             match tcx.expect_def(expr.id) {
-                Def::Struct(..) | Def::Variant(..) => PatKind::Path(path.clone()),
+                Def::Struct(..) | Def::Variant(..) => PatKind::Path(None, path.clone()),
                 Def::Const(def_id) | Def::AssociatedConst(def_id) => {
                     let substs = Some(tcx.node_id_item_substs(expr.id).substs);
                     let (expr, _ty) = lookup_const_by_id(tcx, def_id, substs).unwrap();
@@ -338,20 +340,67 @@ pub fn const_expr_to_pat<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     Ok(P(hir::Pat { id: expr.id, node: pat, span: span }))
 }
 
+pub fn report_const_eval_err<'a, 'tcx>(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    err: &ConstEvalErr,
+    primary_span: Span,
+    primary_kind: &str)
+    -> DiagnosticBuilder<'tcx>
+{
+    let mut err = err;
+    while let &ConstEvalErr { kind: ErroneousReferencedConstant(box ref i_err), .. } = err {
+        err = i_err;
+    }
+
+    let mut diag = struct_span_err!(tcx.sess, err.span, E0080, "constant evaluation error");
+    note_const_eval_err(tcx, err, primary_span, primary_kind, &mut diag);
+    diag
+}
+
+pub fn fatal_const_eval_err<'a, 'tcx>(
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    err: &ConstEvalErr,
+    primary_span: Span,
+    primary_kind: &str)
+    -> !
+{
+    report_const_eval_err(tcx, err, primary_span, primary_kind).emit();
+    tcx.sess.abort_if_errors();
+    unreachable!()
+}
+
+pub fn note_const_eval_err<'a, 'tcx>(
+    _tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    err: &ConstEvalErr,
+    primary_span: Span,
+    primary_kind: &str,
+    diag: &mut DiagnosticBuilder)
+{
+    match err.description() {
+        ConstEvalErrDescription::Simple(message) => {
+            diag.span_label(err.span, &message);
+        }
+    }
+
+    if !primary_span.contains(err.span) {
+        diag.span_note(primary_span,
+                       &format!("for {} here", primary_kind));
+    }
+}
+
 pub fn eval_const_expr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                  e: &Expr) -> ConstVal {
     match eval_const_expr_partial(tcx, e, ExprTypeChecked, None) {
         Ok(r) => r,
         // non-const path still needs to be a fatal error, because enums are funky
         Err(s) => {
+            report_const_eval_err(tcx, &s, e.span, "expression").emit();
             match s.kind {
                 NonConstPath |
-                UnimplementedConstVal(_) => tcx.sess.span_fatal(s.span, &s.description()),
-                _ => {
-                    tcx.sess.span_err(s.span, &s.description());
-                    Dummy
-                }
+                UnimplementedConstVal(_) => tcx.sess.abort_if_errors(),
+                _ => {}
             }
+            Dummy
         },
     }
 }
@@ -400,6 +449,7 @@ pub enum ErrKind {
     IntermediateUnsignedNegative,
     /// Expected, Got
     TypeMismatch(String, ConstInt),
+
     BadType(ConstVal),
     ErroneousReferencedConstant(Box<ConstEvalErr>),
     CharCast(ConstInt),
@@ -411,57 +461,79 @@ impl From<ConstMathErr> for ErrKind {
     }
 }
 
+#[derive(Clone, Debug)]
+pub enum ConstEvalErrDescription<'a> {
+    Simple(Cow<'a, str>),
+}
+
+impl<'a> ConstEvalErrDescription<'a> {
+    /// Return a one-line description of the error, for lints and such
+    pub fn into_oneline(self) -> Cow<'a, str> {
+        match self {
+            ConstEvalErrDescription::Simple(simple) => simple,
+        }
+    }
+}
+
 impl ConstEvalErr {
-    pub fn description(&self) -> Cow<str> {
+    pub fn description(&self) -> ConstEvalErrDescription {
         use self::ErrKind::*;
+        use self::ConstEvalErrDescription::*;
+
+        macro_rules! simple {
+            ($msg:expr) => ({ Simple($msg.into_cow()) });
+            ($fmt:expr, $($arg:tt)+) => ({
+                Simple(format!($fmt, $($arg)+).into_cow())
+            })
+        }
 
         match self.kind {
-            CannotCast => "can't cast this type".into_cow(),
-            CannotCastTo(s) => format!("can't cast this type to {}", s).into_cow(),
-            InvalidOpForInts(_) =>  "can't do this op on integrals".into_cow(),
-            InvalidOpForBools(_) =>  "can't do this op on bools".into_cow(),
-            InvalidOpForFloats(_) => "can't do this op on floats".into_cow(),
-            InvalidOpForIntUint(..) => "can't do this op on an isize and usize".into_cow(),
-            InvalidOpForUintInt(..) => "can't do this op on a usize and isize".into_cow(),
-            NegateOn(ref const_val) => format!("negate on {}", const_val.description()).into_cow(),
-            NotOn(ref const_val) => format!("not on {}", const_val.description()).into_cow(),
-            CallOn(ref const_val) => format!("call on {}", const_val.description()).into_cow(),
-
-            MissingStructField  => "nonexistent struct field".into_cow(),
-            NonConstPath        => "non-constant path in constant expression".into_cow(),
+            CannotCast => simple!("can't cast this type"),
+            CannotCastTo(s) => simple!("can't cast this type to {}", s),
+            InvalidOpForInts(_) =>  simple!("can't do this op on integrals"),
+            InvalidOpForBools(_) =>  simple!("can't do this op on bools"),
+            InvalidOpForFloats(_) => simple!("can't do this op on floats"),
+            InvalidOpForIntUint(..) => simple!("can't do this op on an isize and usize"),
+            InvalidOpForUintInt(..) => simple!("can't do this op on a usize and isize"),
+            NegateOn(ref const_val) => simple!("negate on {}", const_val.description()),
+            NotOn(ref const_val) => simple!("not on {}", const_val.description()),
+            CallOn(ref const_val) => simple!("call on {}", const_val.description()),
+
+            MissingStructField  => simple!("nonexistent struct field"),
+            NonConstPath        => simple!("non-constant path in constant expression"),
             UnimplementedConstVal(what) =>
-                format!("unimplemented constant expression: {}", what).into_cow(),
-            UnresolvedPath => "unresolved path in constant expression".into_cow(),
-            ExpectedConstTuple => "expected constant tuple".into_cow(),
-            ExpectedConstStruct => "expected constant struct".into_cow(),
-            TupleIndexOutOfBounds => "tuple index out of bounds".into_cow(),
-            IndexedNonVec => "indexing is only supported for arrays".into_cow(),
-            IndexNegative => "indices must be non-negative integers".into_cow(),
-            IndexNotInt => "indices must be integers".into_cow(),
+                simple!("unimplemented constant expression: {}", what),
+            UnresolvedPath => simple!("unresolved path in constant expression"),
+            ExpectedConstTuple => simple!("expected constant tuple"),
+            ExpectedConstStruct => simple!("expected constant struct"),
+            TupleIndexOutOfBounds => simple!("tuple index out of bounds"),
+            IndexedNonVec => simple!("indexing is only supported for arrays"),
+            IndexNegative => simple!("indices must be non-negative integers"),
+            IndexNotInt => simple!("indices must be integers"),
             IndexOutOfBounds { len, index } => {
-                format!("index out of bounds: the len is {} but the index is {}",
-                        len, index).into_cow()
+                simple!("index out of bounds: the len is {} but the index is {}",
+                        len, index)
             }
-            RepeatCountNotNatural => "repeat count must be a natural number".into_cow(),
-            RepeatCountNotInt => "repeat count must be integers".into_cow(),
+            RepeatCountNotNatural => simple!("repeat count must be a natural number"),
+            RepeatCountNotInt => simple!("repeat count must be integers"),
 
-            MiscBinaryOp => "bad operands for binary".into_cow(),
-            MiscCatchAll => "unsupported constant expr".into_cow(),
-            IndexOpFeatureGated => "the index operation on const values is unstable".into_cow(),
-            Math(ref err) => err.description().into_cow(),
+            MiscBinaryOp => simple!("bad operands for binary"),
+            MiscCatchAll => simple!("unsupported constant expr"),
+            IndexOpFeatureGated => simple!("the index operation on const values is unstable"),
+            Math(ref err) => Simple(err.description().into_cow()),
 
-            IntermediateUnsignedNegative => "during the computation of an unsigned a negative \
-                                             number was encountered. This is most likely a bug in\
-                                             the constant evaluator".into_cow(),
+            IntermediateUnsignedNegative => simple!(
+                "during the computation of an unsigned a negative \
+                 number was encountered. This is most likely a bug in\
+                 the constant evaluator"),
 
             TypeMismatch(ref expected, ref got) => {
-                format!("mismatched types: expected `{}`, found `{}`",
-                        expected, got.description()).into_cow()
+                simple!("expected {}, found {}", expected, got.description())
             },
-            BadType(ref i) => format!("value of wrong type: {:?}", i).into_cow(),
-            ErroneousReferencedConstant(_) => "could not evaluate referenced constant".into_cow(),
+            BadType(ref i) => simple!("value of wrong type: {:?}", i),
+            ErroneousReferencedConstant(_) => simple!("could not evaluate referenced constant"),
             CharCast(ref got) => {
-                format!("only `u8` can be cast as `char`, not `{}`", got.description()).into_cow()
+                simple!("only `u8` can be cast as `char`, not `{}`", got.description())
             },
         }
     }
@@ -983,7 +1055,7 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
            trait_ref);
 
     tcx.populate_implementations_for_trait_if_necessary(trait_ref.def_id());
-    tcx.infer_ctxt(None, None, ProjectionMode::AnyFinal).enter(|infcx| {
+    tcx.infer_ctxt(None, None, Reveal::NotSpecializable).enter(|infcx| {
         let mut selcx = traits::SelectionContext::new(&infcx);
         let obligation = traits::Obligation::new(traits::ObligationCause::dummy(),
                                                  trait_ref.to_poly_trait_predicate());
@@ -1001,9 +1073,9 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         };
 
         // NOTE: this code does not currently account for specialization, but when
-        // it does so, it should hook into the ProjectionMode to determine when the
+        // it does so, it should hook into the Reveal to determine when the
         // constant should resolve; this will also require plumbing through to this
-        // function whether we are in "trans mode" to pick the right ProjectionMode
+        // function whether we are in "trans mode" to pick the right Reveal
         // when constructing the inference context above.
         match selection {
             traits::VtableImpl(ref impl_data) => {
@@ -1105,11 +1177,25 @@ fn cast_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstVal, ty: ty::Ty)
         Float(f) => cast_const_float(tcx, f, ty),
         Char(c) => cast_const_int(tcx, Infer(c as u64), ty),
         Function(_) => Err(UnimplementedConstVal("casting fn pointers")),
-        ByteStr(_) => match ty.sty {
+        ByteStr(b) => match ty.sty {
             ty::TyRawPtr(_) => {
                 Err(ErrKind::UnimplementedConstVal("casting a bytestr to a raw ptr"))
             },
-            ty::TyRef(..) => Err(ErrKind::UnimplementedConstVal("casting a bytestr to slice")),
+            ty::TyRef(_, ty::TypeAndMut { ref ty, mutbl: hir::MutImmutable }) => match ty.sty {
+                ty::TyArray(ty, n) if ty == tcx.types.u8 && n == b.len() => Ok(ByteStr(b)),
+                ty::TySlice(_) => {
+                    Err(ErrKind::UnimplementedConstVal("casting a bytestr to slice"))
+                },
+                _ => Err(CannotCast),
+            },
+            _ => Err(CannotCast),
+        },
+        Str(s) => match ty.sty {
+            ty::TyRawPtr(_) => Err(ErrKind::UnimplementedConstVal("casting a str to a raw ptr")),
+            ty::TyRef(_, ty::TypeAndMut { ref ty, mutbl: hir::MutImmutable }) => match ty.sty {
+                ty::TyStr => Ok(Str(s)),
+                _ => Err(CannotCast),
+            },
             _ => Err(CannotCast),
         },
         _ => Err(CannotCast),
@@ -1185,8 +1271,10 @@ fn parse_float(num: &str, fty_hint: Option<ast::FloatTy>, span: Span) -> ConstFl
     })
 }
 
-pub fn compare_const_vals(a: &ConstVal, b: &ConstVal) -> Option<Ordering> {
-    match (a, b) {
+pub fn compare_const_vals(tcx: TyCtxt, span: Span, a: &ConstVal, b: &ConstVal)
+                          -> Result<Ordering, ErrorReported>
+{
+    let result = match (a, b) {
         (&Integral(a), &Integral(b)) => a.try_cmp(b).ok(),
         (&Float(a), &Float(b)) => a.try_cmp(b).ok(),
         (&Str(ref a), &Str(ref b)) => Some(a.cmp(b)),
@@ -1194,62 +1282,85 @@ pub fn compare_const_vals(a: &ConstVal, b: &ConstVal) -> Option<Ordering> {
         (&ByteStr(ref a), &ByteStr(ref b)) => Some(a.cmp(b)),
         (&Char(a), &Char(ref b)) => Some(a.cmp(b)),
         _ => None,
+    };
+
+    match result {
+        Some(result) => Ok(result),
+        None => {
+            // FIXME: can this ever be reached?
+            span_err!(tcx.sess, span, E0298,
+                      "type mismatch comparing {} and {}",
+                      a.description(),
+                      b.description());
+            Err(ErrorReported)
+        }
     }
 }
 
 pub fn compare_lit_exprs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                   span: Span,
                                    a: &Expr,
-                                   b: &Expr) -> Option<Ordering> {
+                                   b: &Expr) -> Result<Ordering, ErrorReported> {
     let a = match eval_const_expr_partial(tcx, a, ExprTypeChecked, None) {
         Ok(a) => a,
         Err(e) => {
-            tcx.sess.span_err(a.span, &e.description());
-            return None;
+            report_const_eval_err(tcx, &e, a.span, "expression").emit();
+            return Err(ErrorReported);
         }
     };
     let b = match eval_const_expr_partial(tcx, b, ExprTypeChecked, None) {
         Ok(b) => b,
         Err(e) => {
-            tcx.sess.span_err(b.span, &e.description());
-            return None;
+            report_const_eval_err(tcx, &e, b.span, "expression").emit();
+            return Err(ErrorReported);
         }
     };
-    compare_const_vals(&a, &b)
+    compare_const_vals(tcx, span, &a, &b)
 }
 
 
-/// Returns the repeat count for a repeating vector expression.
-pub fn eval_repeat_count<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                   count_expr: &hir::Expr) -> usize {
+/// Returns the value of the length-valued expression
+pub fn eval_length<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                             count_expr: &hir::Expr,
+                             reason: &str)
+                             -> Result<usize, ErrorReported>
+{
     let hint = UncheckedExprHint(tcx.types.usize);
     match eval_const_expr_partial(tcx, count_expr, hint, None) {
         Ok(Integral(Usize(count))) => {
             let val = count.as_u64(tcx.sess.target.uint_type);
             assert_eq!(val as usize as u64, val);
-            val as usize
+            Ok(val as usize)
         },
         Ok(const_val) => {
-            span_err!(tcx.sess, count_expr.span, E0306,
-                      "expected positive integer for repeat count, found {}",
-                      const_val.description());
-            0
+            struct_span_err!(tcx.sess, count_expr.span, E0306,
+                             "expected `usize` for {}, found {}",
+                             reason,
+                             const_val.description())
+                .span_label(count_expr.span, &format!("expected `usize`"))
+                .emit();
+
+            Err(ErrorReported)
         }
         Err(err) => {
-            let err_msg = match count_expr.node {
+            let mut diag = report_const_eval_err(
+                tcx, &err, count_expr.span, reason);
+
+            match count_expr.node {
                 hir::ExprPath(None, hir::Path {
                     global: false,
                     ref segments,
                     ..
-                }) if segments.len() == 1 =>
-                    format!("found variable"),
-                _ => match err.kind {
-                    MiscCatchAll => format!("but found {}", err.description()),
-                    _ => format!("but {}", err.description())
+                }) if segments.len() == 1 => {
+                    if let Some(Def::Local(..)) = tcx.expect_def_or_none(count_expr.id) {
+                        diag.note(&format!("`{}` is a variable", segments[0].name));
+                    }
                 }
-            };
-            span_err!(tcx.sess, count_expr.span, E0307,
-                "expected constant integer for repeat count, {}", err_msg);
-            0
+                _ => {}
+            }
+
+            diag.emit();
+            Err(ErrorReported)
         }
     }
 }