]> git.proxmox.com Git - rustc.git/blobdiff - src/librustc/middle/intrinsicck.rs
Imported Upstream version 1.9.0+dfsg1
[rustc.git] / src / librustc / middle / intrinsicck.rs
index a763677db066109b203bc4a0b7a4224d1d564139..e84be7e45606b0c77f0f7a55a2d4be4e713e619d 100644 (file)
 // except according to those terms.
 
 use dep_graph::DepNode;
-use middle::def::Def;
-use middle::def_id::DefId;
-use middle::subst::{Subst, Substs, EnumeratedItems};
-use middle::ty::{TransmuteRestriction, ctxt, TyBareFn};
-use middle::ty::{self, Ty, TypeFoldable};
-
-use std::fmt;
+use hir::def::Def;
+use hir::def_id::DefId;
+use infer::{InferCtxt, new_infer_ctxt};
+use traits::ProjectionMode;
+use ty::{self, Ty, TyCtxt};
+use ty::layout::{LayoutError, Pointer, SizeSkeleton};
 
 use syntax::abi::Abi::RustIntrinsic;
 use syntax::ast;
 use syntax::codemap::Span;
-use rustc_front::intravisit::{self, Visitor, FnKind};
-use rustc_front::hir;
+use hir::intravisit::{self, Visitor, FnKind};
+use hir;
 
-pub fn check_crate(tcx: &ctxt) {
-    let mut visitor = IntrinsicCheckingVisitor {
-        tcx: tcx,
-        param_envs: Vec::new(),
-        dummy_sized_ty: tcx.types.isize,
-        dummy_unsized_ty: tcx.mk_slice(tcx.types.isize),
+pub fn check_crate(tcx: &TyCtxt) {
+    let mut visitor = ItemVisitor {
+        tcx: tcx
     };
     tcx.visit_all_items_in_krate(DepNode::IntrinsicCheck, &mut visitor);
 }
 
-struct IntrinsicCheckingVisitor<'a, 'tcx: 'a> {
-    tcx: &'a ctxt<'tcx>,
+struct ItemVisitor<'a, 'tcx: 'a> {
+    tcx: &'a TyCtxt<'tcx>
+}
 
-    // As we traverse the AST, we keep a stack of the parameter
-    // environments for each function we encounter. When we find a
-    // call to `transmute`, we can check it in the context of the top
-    // of the stack (which ought not to be empty).
-    param_envs: Vec<ty::ParameterEnvironment<'a,'tcx>>,
+impl<'a, 'tcx> ItemVisitor<'a, 'tcx> {
+    fn visit_const(&mut self, item_id: ast::NodeId, expr: &hir::Expr) {
+        let param_env = ty::ParameterEnvironment::for_item(self.tcx, item_id);
+        let infcx = new_infer_ctxt(self.tcx, &self.tcx.tables,
+                                   Some(param_env),
+                                   ProjectionMode::Any);
+        let mut visitor = ExprVisitor {
+            infcx: &infcx
+        };
+        visitor.visit_expr(expr);
+    }
+}
 
-    // Dummy sized/unsized types that use to substitute for type
-    // parameters in order to estimate how big a type will be for any
-    // possible instantiation of the type parameters in scope.  See
-    // `check_transmute` for more details.
-    dummy_sized_ty: Ty<'tcx>,
-    dummy_unsized_ty: Ty<'tcx>,
+struct ExprVisitor<'a, 'tcx: 'a> {
+    infcx: &'a InferCtxt<'a, 'tcx>
 }
 
-impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> {
+impl<'a, 'tcx> ExprVisitor<'a, 'tcx> {
     fn def_id_is_transmute(&self, def_id: DefId) -> bool {
-        let intrinsic = match self.tcx.lookup_item_type(def_id).ty.sty {
-            ty::TyBareFn(_, ref bfty) => bfty.abi == RustIntrinsic,
+        let intrinsic = match self.infcx.tcx.lookup_item_type(def_id).ty.sty {
+            ty::TyFnDef(_, _, ref bfty) => bfty.abi == RustIntrinsic,
             _ => return false
         };
-        intrinsic && self.tcx.item_name(def_id).as_str() == "transmute"
+        intrinsic && self.infcx.tcx.item_name(def_id).as_str() == "transmute"
     }
 
     fn check_transmute(&self, span: Span, from: Ty<'tcx>, to: Ty<'tcx>, id: ast::NodeId) {
-        // Find the parameter environment for the most recent function that
-        // we entered.
+        let sk_from = SizeSkeleton::compute(from, self.infcx);
+        let sk_to = SizeSkeleton::compute(to, self.infcx);
 
-        let param_env = match self.param_envs.last() {
-            Some(p) => p,
-            None => {
-                self.tcx.sess.span_bug(
-                    span,
-                    "transmute encountered outside of any fn");
+        // Check for same size using the skeletons.
+        if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) {
+            if sk_from.same_size(sk_to) {
+                return;
             }
-        };
-
-        // Simple case: no type parameters involved.
-        if
-            !from.has_param_types() && !from.has_self_ty() &&
-            !to.has_param_types() && !to.has_self_ty()
-        {
-            let restriction = TransmuteRestriction {
-                span: span,
-                original_from: from,
-                original_to: to,
-                substituted_from: from,
-                substituted_to: to,
-                id: id,
-            };
-            self.push_transmute_restriction(restriction);
-            return;
-        }
 
-        // The rules around type parameters are a bit subtle. We are
-        // checking these rules before monomorphization, so there may
-        // be unsubstituted type parameters present in the
-        // types. Obviously we cannot create LLVM types for those.
-        // However, if a type parameter appears only indirectly (i.e.,
-        // through a pointer), it does not necessarily affect the
-        // size, so that should be allowed. The only catch is that we
-        // DO want to be careful around unsized type parameters, since
-        // fat pointers have a different size than a thin pointer, and
-        // hence `&T` and `&U` have different sizes if `T : Sized` but
-        // `U : Sized` does not hold.
-        //
-        // However, it's not as simple as checking whether `T :
-        // Sized`, because even if `T : Sized` does not hold, that
-        // just means that `T` *may* not be sized.  After all, even a
-        // type parameter `T: ?Sized` could be bound to a sized
-        // type. (Issue #20116)
-        //
-        // To handle this, we first check for "interior" type
-        // parameters, which are always illegal. If there are none of
-        // those, then we know that the only way that all type
-        // parameters `T` are referenced indirectly, e.g. via a
-        // pointer type like `&T`. In that case, we only care whether
-        // `T` is sized or not, because that influences whether `&T`
-        // is a thin or fat pointer.
-        //
-        // One could imagine establishing a sophisticated constraint
-        // system to ensure that the transmute is legal, but instead
-        // we do something brutally dumb. We just substitute dummy
-        // sized or unsized types for every type parameter in scope,
-        // exhaustively checking all possible combinations. Here are some examples:
-        //
-        // ```
-        // fn foo<T, U>() {
-        //     // T=int, U=int
-        // }
-        //
-        // fn bar<T: ?Sized, U>() {
-        //     // T=int, U=int
-        //     // T=[int], U=int
-        // }
-        //
-        // fn baz<T: ?Sized, U: ?Sized>() {
-        //     // T=int, U=int
-        //     // T=[int], U=int
-        //     // T=int, U=[int]
-        //     // T=[int], U=[int]
-        // }
-        // ```
-        //
-        // In all cases, we keep the original unsubstituted types
-        // around for error reporting.
-
-        let from_tc = from.type_contents(self.tcx);
-        let to_tc = to.type_contents(self.tcx);
-        if from_tc.interior_param() || to_tc.interior_param() {
-            span_err!(self.tcx.sess, span, E0139,
-                      "cannot transmute to or from a type that contains \
-                       unsubstituted type parameters");
-            return;
+            match (&from.sty, sk_to) {
+                (&ty::TyFnDef(..), SizeSkeleton::Known(size_to))
+                        if size_to == Pointer.size(&self.infcx.tcx.data_layout) => {
+                    // FIXME #19925 Remove this warning after a release cycle.
+                    let msg = format!("`{}` is now zero-sized and has to be cast \
+                                       to a pointer before transmuting to `{}`",
+                                      from, to);
+                    self.infcx.tcx.sess.add_lint(
+                        ::lint::builtin::TRANSMUTE_FROM_FN_ITEM_TYPES, id, span, msg);
+                    return;
+                }
+                _ => {}
+            }
         }
 
-        let mut substs = param_env.free_substs.clone();
-        self.with_each_combination(
-            span,
-            param_env,
-            param_env.free_substs.types.iter_enumerated(),
-            &mut substs,
-            &mut |substs| {
-                let restriction = TransmuteRestriction {
-                    span: span,
-                    original_from: from,
-                    original_to: to,
-                    substituted_from: from.subst(self.tcx, substs),
-                    substituted_to: to.subst(self.tcx, substs),
-                    id: id,
-                };
-                self.push_transmute_restriction(restriction);
-            });
-    }
-
-    fn with_each_combination(&self,
-                             span: Span,
-                             param_env: &ty::ParameterEnvironment<'a,'tcx>,
-                             mut types_in_scope: EnumeratedItems<Ty<'tcx>>,
-                             substs: &mut Substs<'tcx>,
-                             callback: &mut FnMut(&Substs<'tcx>))
-    {
-        // This parameter invokes `callback` many times with different
-        // substitutions that replace all the parameters in scope with
-        // either `int` or `[int]`, depending on whether the type
-        // parameter is known to be sized. See big comment above for
-        // an explanation of why this is a reasonable thing to do.
-
-        match types_in_scope.next() {
-            None => {
-                debug!("with_each_combination(substs={:?})",
-                       substs);
-
-                callback(substs);
+        // Try to display a sensible error with as much information as possible.
+        let skeleton_string = |ty: Ty<'tcx>, sk| {
+            match sk {
+                Ok(SizeSkeleton::Known(size)) => {
+                    format!("{} bits", size.bits())
+                }
+                Ok(SizeSkeleton::Pointer { tail, .. }) => {
+                    format!("pointer to {}", tail)
+                }
+                Err(LayoutError::Unknown(bad)) => {
+                    if bad == ty {
+                        format!("size can vary")
+                    } else {
+                        format!("size can vary because of {}", bad)
+                    }
+                }
+                Err(err) => err.to_string()
             }
+        };
 
-            Some((space, index, &param_ty)) => {
-                debug!("with_each_combination: space={:?}, index={}, param_ty={:?}",
-                       space, index, param_ty);
-
-                if !param_ty.is_sized(param_env, span) {
-                    debug!("with_each_combination: param_ty is not known to be sized");
+        span_err!(self.infcx.tcx.sess, span, E0512,
+                  "transmute called with differently sized types: \
+                   {} ({}) to {} ({})",
+                  from, skeleton_string(from, sk_from),
+                  to, skeleton_string(to, sk_to));
+    }
+}
 
-                    substs.types.get_mut_slice(space)[index] = self.dummy_unsized_ty;
-                    self.with_each_combination(span, param_env, types_in_scope.clone(),
-                                               substs, callback);
-                }
+impl<'a, 'tcx, 'v> Visitor<'v> for ItemVisitor<'a, 'tcx> {
+    // const, static and N in [T; N].
+    fn visit_expr(&mut self, expr: &hir::Expr) {
+        let infcx = new_infer_ctxt(self.tcx, &self.tcx.tables,
+                                   None, ProjectionMode::Any);
+        let mut visitor = ExprVisitor {
+            infcx: &infcx
+        };
+        visitor.visit_expr(expr);
+    }
 
-                substs.types.get_mut_slice(space)[index] = self.dummy_sized_ty;
-                self.with_each_combination(span, param_env, types_in_scope,
-                                           substs, callback);
-            }
+    fn visit_trait_item(&mut self, item: &hir::TraitItem) {
+        if let hir::ConstTraitItem(_, Some(ref expr)) = item.node {
+            self.visit_const(item.id, expr);
+        } else {
+            intravisit::walk_trait_item(self, item);
         }
     }
 
-    fn push_transmute_restriction(&self, restriction: TransmuteRestriction<'tcx>) {
-        debug!("Pushing transmute restriction: {:?}", restriction);
-        self.tcx.transmute_restrictions.borrow_mut().push(restriction);
+    fn visit_impl_item(&mut self, item: &hir::ImplItem) {
+        if let hir::ImplItemKind::Const(_, ref expr) = item.node {
+            self.visit_const(item.id, expr);
+        } else {
+            intravisit::walk_impl_item(self, item);
+        }
     }
-}
 
-impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> {
     fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v hir::FnDecl,
                 b: &'v hir::Block, s: Span, id: ast::NodeId) {
         match fk {
             FnKind::ItemFn(..) | FnKind::Method(..) => {
                 let param_env = ty::ParameterEnvironment::for_item(self.tcx, id);
-                self.param_envs.push(param_env);
-                intravisit::walk_fn(self, fk, fd, b, s);
-                self.param_envs.pop();
+                let infcx = new_infer_ctxt(self.tcx, &self.tcx.tables,
+                                           Some(param_env),
+                                           ProjectionMode::Any);
+                let mut visitor = ExprVisitor {
+                    infcx: &infcx
+                };
+                visitor.visit_fn(fk, fd, b, s, id);
             }
-            FnKind::Closure => {
-                intravisit::walk_fn(self, fk, fd, b, s);
+            FnKind::Closure(..) => {
+                span_bug!(s, "intrinsicck: closure outside of function")
             }
         }
     }
+}
 
+impl<'a, 'tcx, 'v> Visitor<'v> for ExprVisitor<'a, 'tcx> {
     fn visit_expr(&mut self, expr: &hir::Expr) {
         if let hir::ExprPath(..) = expr.node {
-            match self.tcx.resolve_expr(expr) {
+            match self.infcx.tcx.resolve_expr(expr) {
                 Def::Fn(did) if self.def_id_is_transmute(did) => {
-                    let typ = self.tcx.node_id_to_type(expr.id);
+                    let typ = self.infcx.tcx.node_id_to_type(expr.id);
                     match typ.sty {
-                        TyBareFn(_, ref bare_fn_ty) if bare_fn_ty.abi == RustIntrinsic => {
+                        ty::TyFnDef(_, _, ref bare_fn_ty) if bare_fn_ty.abi == RustIntrinsic => {
                             if let ty::FnConverging(to) = bare_fn_ty.sig.0.output {
                                 let from = bare_fn_ty.sig.0.inputs[0];
                                 self.check_transmute(expr.span, from, to, expr.id);
                             }
                         }
                         _ => {
-                            self.tcx
-                                .sess
-                                .span_bug(expr.span, "transmute wasn't a bare fn?!");
+                            span_bug!(expr.span, "transmute wasn't a bare fn?!");
                         }
                     }
                 }
@@ -258,14 +184,3 @@ impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> {
         intravisit::walk_expr(self, expr);
     }
 }
-
-impl<'tcx> fmt::Debug for TransmuteRestriction<'tcx> {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "TransmuteRestriction(id={}, original=({:?},{:?}), substituted=({:?},{:?}))",
-               self.id,
-               self.original_from,
-               self.original_to,
-               self.substituted_from,
-               self.substituted_to)
-    }
-}