]> git.lizzy.rs Git - rust.git/blobdiff - src/librustc_const_eval/eval.rs
move projection mode into parameter environment
[rust.git] / src / librustc_const_eval / eval.rs
index 9c5a669bef0d92623aeb12488802a49f321c6cfe..1364898b549d4705b70e004164576af440664cca 100644 (file)
@@ -15,7 +15,7 @@
 use rustc::hir::map as hir_map;
 use rustc::hir::map::blocks::FnLikeNode;
 use rustc::traits;
-use rustc::hir::def::Def;
+use rustc::hir::def::{Def, CtorKind};
 use rustc::hir::def_id::DefId;
 use rustc::ty::{self, Ty, TyCtxt};
 use rustc::ty::maps::Providers;
@@ -27,7 +27,7 @@
 
 use syntax::ast;
 use rustc::hir::{self, Expr};
-use syntax_pos::{Span, DUMMY_SP};
+use syntax_pos::Span;
 
 use std::cmp::Ordering;
 
@@ -48,110 +48,39 @@ macro_rules! math {
     }
 }
 
-fn lookup_variant_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                  variant_def: DefId)
-                                  -> Option<(&'tcx Expr, &'a ty::TypeckTables<'tcx>)> {
-    if let Some(variant_node_id) = tcx.hir.as_local_node_id(variant_def) {
-        let enum_node_id = tcx.hir.get_parent(variant_node_id);
-        if let Some(hir_map::NodeItem(it)) = tcx.hir.find(enum_node_id) {
-            if let hir::ItemEnum(ref edef, _) = it.node {
-                for variant in &edef.variants {
-                    if variant.node.data.id() == variant_node_id {
-                        return variant.node.disr_expr.map(|e| {
-                            let def_id = tcx.hir.body_owner_def_id(e);
-                            (&tcx.hir.body(e).value,
-                             tcx.item_tables(def_id))
-                        });
-                    }
-                }
-            }
-        }
-    }
-    None
-}
-
 /// * `def_id` is the id of the constant.
 /// * `substs` is the monomorphized substitutions for the expression.
 ///
 /// `substs` is optional and is used for associated constants.
 /// This generally happens in late/trans const evaluation.
-pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                        def_id: DefId,
-                                        substs: &'tcx Substs<'tcx>)
-                                        -> Option<(&'tcx Expr,
-                                                   &'a ty::TypeckTables<'tcx>)> {
+pub fn lookup_const_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                    def_id: DefId,
+                                    substs: &'tcx Substs<'tcx>)
+                                    -> Option<(DefId, &'tcx Substs<'tcx>)> {
     if let Some(node_id) = tcx.hir.as_local_node_id(def_id) {
         match tcx.hir.find(node_id) {
-            None => None,
-            Some(hir_map::NodeItem(&hir::Item {
-                node: hir::ItemConst(_, body), ..
-            })) |
-            Some(hir_map::NodeImplItem(&hir::ImplItem {
-                node: hir::ImplItemKind::Const(_, body), ..
-            })) => {
-                Some((&tcx.hir.body(body).value,
-                      tcx.item_tables(def_id)))
+            Some(hir_map::NodeTraitItem(_)) => {
+                // If we have a trait item and the substitutions for it,
+                // `resolve_trait_associated_const` will select an impl
+                // or the default.
+                resolve_trait_associated_const(tcx, def_id, substs)
             }
-            Some(hir_map::NodeTraitItem(ti)) => match ti.node {
-                hir::TraitItemKind::Const(_, default) => {
-                    // If we have a trait item and the substitutions for it,
-                    // `resolve_trait_associated_const` will select an impl
-                    // or the default.
-                    let trait_id = tcx.hir.get_parent(node_id);
-                    let trait_id = tcx.hir.local_def_id(trait_id);
-                    let default_value = default.map(|body| {
-                        (&tcx.hir.body(body).value,
-                            tcx.item_tables(def_id))
-                    });
-                    resolve_trait_associated_const(tcx, def_id, default_value, trait_id, substs)
-                }
-                _ => None
-            },
-            Some(_) => None
+            _ => Some((def_id, substs))
         }
     } else {
-        let expr_and_tables = tcx.sess.cstore.maybe_get_item_body(tcx, def_id).map(|body| {
-            (&body.value, tcx.item_tables(def_id))
-        });
-        match tcx.sess.cstore.describe_def(def_id) {
+        match tcx.describe_def(def_id) {
             Some(Def::AssociatedConst(_)) => {
-                let trait_id = tcx.sess.cstore.trait_of_item(def_id);
                 // As mentioned in the comments above for in-crate
                 // constants, we only try to find the expression for a
                 // trait-associated const if the caller gives us the
                 // substitutions for the reference to it.
-                if let Some(trait_id) = trait_id {
-                    resolve_trait_associated_const(tcx, def_id, expr_and_tables,
-                                                   trait_id, substs)
+                if tcx.trait_of_item(def_id).is_some() {
+                    resolve_trait_associated_const(tcx, def_id, substs)
                 } else {
-                    expr_and_tables
+                    Some((def_id, substs))
                 }
-            },
-            Some(Def::Const(..)) => expr_and_tables,
-            _ => None
-        }
-    }
-}
-
-fn lookup_const_fn_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId)
-                                   -> Option<(&'tcx hir::Body, &'a ty::TypeckTables<'tcx>)>
-{
-    if let Some(node_id) = tcx.hir.as_local_node_id(def_id) {
-        FnLikeNode::from_node(tcx.hir.get(node_id)).and_then(|fn_like| {
-            if fn_like.constness() == hir::Constness::Const {
-                Some((tcx.hir.body(fn_like.body()),
-                      tcx.item_tables(def_id)))
-            } else {
-                None
             }
-        })
-    } else {
-        if tcx.sess.cstore.is_const_fn(def_id) {
-            tcx.sess.cstore.maybe_get_item_body(tcx, def_id).map(|body| {
-                (body, tcx.item_tables(def_id))
-            })
-        } else {
-            None
+            _ => Some((def_id, substs))
         }
     }
 }
@@ -338,14 +267,26 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
         }
       }
       hir::ExprCast(ref base, _) => {
-        match cast_const(tcx, cx.eval(base)?, ety) {
-            Ok(val) => val,
-            Err(kind) => return Err(ConstEvalErr { span: e.span, kind: kind }),
+        let base_val = cx.eval(base)?;
+        let base_ty = cx.tables.expr_ty(base);
+
+        // Avoid applying substitutions if they're empty, that'd ICE.
+        let base_ty = if cx.substs.is_empty() {
+            base_ty
+        } else {
+            base_ty.subst(tcx, cx.substs)
+        };
+        if ety == base_ty {
+            base_val
+        } else {
+            match cast_const(tcx, base_val, ety) {
+                Ok(val) => val,
+                Err(kind) => signal!(e, kind),
+            }
         }
       }
       hir::ExprPath(ref qpath) => {
-        let substs = cx.tables.node_id_item_substs(e.id)
-            .unwrap_or_else(|| tcx.intern_substs(&[]));
+        let substs = cx.tables.node_substs(e.id);
 
         // Avoid applying substitutions if they're empty, that'd ICE.
         let substs = if cx.substs.is_empty() {
@@ -357,42 +298,29 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
           match cx.tables.qpath_def(qpath, e.id) {
               Def::Const(def_id) |
               Def::AssociatedConst(def_id) => {
-                  if let Some((expr, tables)) = lookup_const_by_id(tcx, def_id, substs) {
-                      let cx = ConstContext::with_tables(tcx, tables);
-                      match cx.eval(expr) {
-                          Ok(val) => val,
-                          Err(ConstEvalErr { kind: TypeckError, .. }) => {
-                              signal!(e, TypeckError);
-                          }
-                          Err(err) => {
-                              debug!("bad reference: {:?}, {:?}", err.description(), err.span);
-                              signal!(e, ErroneousReferencedConstant(box err))
-                          },
-                      }
-                  } else {
-                      signal!(e, TypeckError);
-                  }
+                    match tcx.at(e.span).const_eval((def_id, substs)) {
+                        Ok(val) => val,
+                        Err(ConstEvalErr { kind: TypeckError, .. }) => {
+                            signal!(e, TypeckError);
+                        }
+                        Err(err) => {
+                            debug!("bad reference: {:?}, {:?}", err.description(), err.span);
+                            signal!(e, ErroneousReferencedConstant(box err))
+                        },
+                    }
               },
-              Def::VariantCtor(variant_def, ..) => {
-                  if let Some((expr, tables)) = lookup_variant_by_id(tcx, variant_def) {
-                      let cx = ConstContext::with_tables(tcx, tables);
-                      match cx.eval(expr) {
-                          Ok(val) => val,
-                          Err(ConstEvalErr { kind: TypeckError, .. }) => {
-                              signal!(e, TypeckError);
-                          }
-                          Err(err) => {
-                              debug!("bad reference: {:?}, {:?}", err.description(), err.span);
-                              signal!(e, ErroneousReferencedConstant(box err))
-                          },
-                      }
-                  } else {
-                      signal!(e, UnimplementedConstVal("enum variants"));
-                  }
+              Def::VariantCtor(variant_def, CtorKind::Const) => {
+                Variant(variant_def)
+              }
+              Def::VariantCtor(_, CtorKind::Fn) => {
+                  signal!(e, UnimplementedConstVal("enum variants"));
               }
-              Def::StructCtor(..) => {
+              Def::StructCtor(_, CtorKind::Const) => {
                   ConstVal::Struct(Default::default())
               }
+              Def::StructCtor(_, CtorKind::Fn) => {
+                  signal!(e, UnimplementedConstVal("tuple struct constructors"))
+              }
               Def::Local(def_id) => {
                   debug!("Def::Local({:?}): {:?}", def_id, cx.fn_args);
                   if let Some(val) = cx.fn_args.as_ref().and_then(|args| args.get(&def_id)) {
@@ -407,14 +335,27 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
           }
       }
       hir::ExprCall(ref callee, ref args) => {
-          let (did, substs) = match cx.eval(callee)? {
-              Function(did, substs) => (did, substs),
-              Struct(_) => signal!(e, UnimplementedConstVal("tuple struct constructors")),
-              callee => signal!(e, CallOn(callee)),
+          let (def_id, substs) = match cx.eval(callee)? {
+              Function(def_id, substs) => (def_id, substs),
+              _ => signal!(e, TypeckError),
           };
-          let (body, tables) = match lookup_const_fn_by_id(tcx, did) {
-              Some(x) => x,
-              None => signal!(e, NonConstPath),
+
+          let body = if let Some(node_id) = tcx.hir.as_local_node_id(def_id) {
+            if let Some(fn_like) = FnLikeNode::from_node(tcx.hir.get(node_id)) {
+                if fn_like.constness() == hir::Constness::Const {
+                    tcx.hir.body(fn_like.body())
+                } else {
+                    signal!(e, TypeckError)
+                }
+            } else {
+                signal!(e, TypeckError)
+            }
+          } else {
+            if tcx.sess.cstore.is_const_fn(def_id) {
+                tcx.sess.cstore.item_body(tcx, def_id)
+            } else {
+                signal!(e, TypeckError)
+            }
           };
 
           let arg_defs = body.arguments.iter().map(|arg| match arg.pat.node {
@@ -434,7 +375,7 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
           debug!("const call({:?})", call_args);
           let callee_cx = ConstContext {
             tcx: tcx,
-            tables: tables,
+            tables: tcx.typeck_tables_of(def_id),
             substs: substs,
             fn_args: Some(call_args)
           };
@@ -532,20 +473,17 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
     Ok(result)
 }
 
-fn resolve_trait_associated_const<'a, 'tcx: 'a>(
-    tcx: TyCtxt<'a, 'tcx, 'tcx>,
-    trait_item_id: DefId,
-    default_value: Option<(&'tcx Expr, &'a ty::TypeckTables<'tcx>)>,
-    trait_id: DefId,
-    rcvr_substs: &'tcx Substs<'tcx>
-) -> Option<(&'tcx Expr, &'a ty::TypeckTables<'tcx>)>
-{
-    let trait_ref = ty::Binder(ty::TraitRef::new(trait_id, rcvr_substs));
+fn resolve_trait_associated_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                                            def_id: DefId,
+                                            substs: &'tcx Substs<'tcx>)
+                                            -> Option<(DefId, &'tcx Substs<'tcx>)> {
+    let trait_item = tcx.associated_item(def_id);
+    let trait_id = trait_item.container.id();
+    let trait_ref = ty::Binder(ty::TraitRef::new(trait_id, substs));
     debug!("resolve_trait_associated_const: trait_ref={:?}",
            trait_ref);
 
-    tcx.populate_implementations_for_trait_if_necessary(trait_id);
-    tcx.infer_ctxt((), Reveal::UserFacing).enter(|infcx| {
+    tcx.infer_ctxt(Reveal::UserFacing).enter(|infcx| {
         let mut selcx = traits::SelectionContext::new(&infcx);
         let obligation = traits::Obligation::new(traits::ObligationCause::dummy(),
                                                  trait_ref.to_poly_trait_predicate());
@@ -569,12 +507,20 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>(
         // when constructing the inference context above.
         match selection {
             traits::VtableImpl(ref impl_data) => {
-                let name = tcx.associated_item(trait_item_id).name;
+                let name = trait_item.name;
                 let ac = tcx.associated_items(impl_data.impl_def_id)
                     .find(|item| item.kind == ty::AssociatedKind::Const && item.name == name);
                 match ac {
-                    Some(ic) => lookup_const_by_id(tcx, ic.def_id, Substs::empty()),
-                    None => default_value,
+                    // FIXME(eddyb) Use proper Instance resolution to
+                    // get the correct Substs returned from here.
+                    Some(ic) => Some((ic.def_id, Substs::empty())),
+                    None => {
+                        if trait_item.defaultness.has_value() {
+                            Some((def_id, substs))
+                        } else {
+                            None
+                        }
+                    }
                 }
             }
             _ => {
@@ -615,7 +561,7 @@ fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
             U8(u) => Ok(Char(u as char)),
             _ => bug!(),
         },
-        _ => bug!(),
+        _ => Err(CannotCast),
     }
 }
 
@@ -659,6 +605,11 @@ fn cast_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
         Bool(b) => cast_const_int(tcx, U8(b as u8), ty),
         Float(f) => cast_const_float(tcx, f, ty),
         Char(c) => cast_const_int(tcx, U32(c as u32), ty),
+        Variant(v) => {
+            let adt = tcx.adt_def(tcx.parent_def_id(v).unwrap());
+            let idx = adt.variant_index_with_id(v);
+            cast_const_int(tcx, adt.discriminant_for_variant(tcx, idx), ty)
+        }
         Function(..) => Err(UnimplementedConstVal("casting fn pointers")),
         ByteStr(b) => match ty.sty {
             ty::TyRawPtr(_) => {
@@ -796,21 +747,35 @@ pub fn compare_lit_exprs(&self,
 
 pub fn provide(providers: &mut Providers) {
     *providers = Providers {
-        monomorphic_const_eval,
+        const_eval,
         ..*providers
     };
 }
 
-fn monomorphic_const_eval<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
-                                    def_id: DefId)
-                                    -> EvalResult<'tcx> {
-    let cx = ConstContext::with_tables(tcx, tcx.item_tables(def_id));
+fn const_eval<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
+                        (def_id, substs): (DefId, &'tcx Substs<'tcx>))
+                        -> EvalResult<'tcx> {
+    let (def_id, substs) = if let Some(resolved) = lookup_const_by_id(tcx, def_id, substs) {
+        resolved
+    } else {
+        return Err(ConstEvalErr {
+            span: tcx.def_span(def_id),
+            kind: TypeckError
+        });
+    };
+
+    let cx = ConstContext {
+        tcx,
+        tables: tcx.typeck_tables_of(def_id),
+        substs: substs,
+        fn_args: None
+    };
 
     let body = if let Some(id) = tcx.hir.as_local_node_id(def_id) {
-        ty::queries::mir_const_qualif::get(tcx, DUMMY_SP, def_id);
+        tcx.mir_const_qualif(def_id);
         tcx.hir.body(tcx.hir.body_owned_by(id))
     } else {
-        tcx.sess.cstore.maybe_get_item_body(tcx, def_id).unwrap()
+        tcx.sess.cstore.item_body(tcx, def_id)
     };
     cx.eval(&body.value)
 }