2014-06-12 16:08:44 -05:00
|
|
|
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
use metadata::csearch;
|
|
|
|
use middle::def::DefFn;
|
|
|
|
use middle::subst::Subst;
|
|
|
|
use middle::ty::{TransmuteRestriction, ctxt, ty_bare_fn};
|
|
|
|
use middle::ty;
|
|
|
|
|
|
|
|
use syntax::abi::RustIntrinsic;
|
|
|
|
use syntax::ast::DefId;
|
|
|
|
use syntax::ast;
|
|
|
|
use syntax::ast_map::NodeForeignItem;
|
|
|
|
use syntax::codemap::Span;
|
|
|
|
use syntax::parse::token;
|
|
|
|
use syntax::visit::Visitor;
|
|
|
|
use syntax::visit;
|
|
|
|
|
|
|
|
fn type_size_is_affected_by_type_parameters(tcx: &ty::ctxt, typ: ty::t)
|
|
|
|
-> bool {
|
|
|
|
let mut result = false;
|
|
|
|
ty::maybe_walk_ty(typ, |typ| {
|
|
|
|
match ty::get(typ).sty {
|
|
|
|
ty::ty_box(_) | ty::ty_uniq(_) | ty::ty_ptr(_) |
|
|
|
|
ty::ty_rptr(..) | ty::ty_bare_fn(..) | ty::ty_closure(..) => {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
ty::ty_param(_) => {
|
|
|
|
result = true;
|
|
|
|
// No need to continue; we now know the result.
|
|
|
|
false
|
|
|
|
}
|
|
|
|
ty::ty_enum(did, ref substs) => {
|
|
|
|
for enum_variant in (*ty::enum_variants(tcx, did)).iter() {
|
|
|
|
for argument_type in enum_variant.args.iter() {
|
|
|
|
let argument_type = argument_type.subst(tcx, substs);
|
|
|
|
result = result ||
|
|
|
|
type_size_is_affected_by_type_parameters(
|
|
|
|
tcx,
|
|
|
|
argument_type);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't traverse substitutions.
|
|
|
|
false
|
|
|
|
}
|
|
|
|
ty::ty_struct(did, ref substs) => {
|
|
|
|
for field in ty::struct_fields(tcx, did, substs).iter() {
|
|
|
|
result = result ||
|
|
|
|
type_size_is_affected_by_type_parameters(tcx,
|
|
|
|
field.mt.ty);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Don't traverse substitutions.
|
|
|
|
false
|
|
|
|
}
|
|
|
|
_ => true,
|
|
|
|
}
|
|
|
|
});
|
|
|
|
result
|
|
|
|
}
|
|
|
|
|
2014-04-22 07:56:37 -05:00
|
|
|
struct IntrinsicCheckingVisitor<'a, 'tcx: 'a> {
|
|
|
|
tcx: &'a ctxt<'tcx>,
|
2014-06-12 16:08:44 -05:00
|
|
|
}
|
|
|
|
|
2014-04-22 07:56:37 -05:00
|
|
|
impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> {
|
2014-06-12 16:08:44 -05:00
|
|
|
fn def_id_is_transmute(&self, def_id: DefId) -> bool {
|
2014-08-25 13:45:19 -05:00
|
|
|
let intrinsic = match ty::get(ty::lookup_item_type(self.tcx, def_id).ty).sty {
|
|
|
|
ty::ty_bare_fn(ref bfty) => bfty.abi == RustIntrinsic,
|
|
|
|
_ => return false
|
|
|
|
};
|
2014-06-12 16:08:44 -05:00
|
|
|
if def_id.krate == ast::LOCAL_CRATE {
|
|
|
|
match self.tcx.map.get(def_id.node) {
|
2014-08-25 13:45:19 -05:00
|
|
|
NodeForeignItem(ref item) if intrinsic => {
|
2014-06-12 16:08:44 -05:00
|
|
|
token::get_ident(item.ident) ==
|
|
|
|
token::intern_and_get_ident("transmute")
|
|
|
|
}
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
match csearch::get_item_path(self.tcx, def_id).last() {
|
2014-08-25 13:45:19 -05:00
|
|
|
Some(ref last) if intrinsic => {
|
2014-06-12 16:08:44 -05:00
|
|
|
token::get_name(last.name()) ==
|
|
|
|
token::intern_and_get_ident("transmute")
|
|
|
|
}
|
2014-08-25 13:45:19 -05:00
|
|
|
_ => false,
|
2014-06-12 16:08:44 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-06 04:59:40 -05:00
|
|
|
fn check_transmute(&self, span: Span, from: ty::t, to: ty::t, id: ast::NodeId) {
|
2014-06-12 16:08:44 -05:00
|
|
|
if type_size_is_affected_by_type_parameters(self.tcx, from) {
|
2014-07-17 12:56:37 -05:00
|
|
|
span_err!(self.tcx.sess, span, E0139,
|
|
|
|
"cannot transmute from a type that contains type parameters");
|
2014-06-12 16:08:44 -05:00
|
|
|
}
|
|
|
|
if type_size_is_affected_by_type_parameters(self.tcx, to) {
|
2014-07-17 12:56:37 -05:00
|
|
|
span_err!(self.tcx.sess, span, E0140,
|
|
|
|
"cannot transmute to a type that contains type parameters");
|
2014-06-12 16:08:44 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
let restriction = TransmuteRestriction {
|
|
|
|
span: span,
|
|
|
|
from: from,
|
|
|
|
to: to,
|
2014-08-06 04:59:40 -05:00
|
|
|
id: id,
|
2014-06-12 16:08:44 -05:00
|
|
|
};
|
|
|
|
self.tcx.transmute_restrictions.borrow_mut().push(restriction);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-12 05:10:30 -05:00
|
|
|
impl<'a, 'tcx> Visitor for IntrinsicCheckingVisitor<'a, 'tcx> {
|
|
|
|
fn visit_expr(&mut self, expr: &ast::Expr) {
|
2014-06-12 16:08:44 -05:00
|
|
|
match expr.node {
|
|
|
|
ast::ExprPath(..) => {
|
|
|
|
match ty::resolve_expr(self.tcx, expr) {
|
|
|
|
DefFn(did, _) if self.def_id_is_transmute(did) => {
|
|
|
|
let typ = ty::node_id_to_type(self.tcx, expr.id);
|
|
|
|
match ty::get(typ).sty {
|
|
|
|
ty_bare_fn(ref bare_fn_ty)
|
|
|
|
if bare_fn_ty.abi == RustIntrinsic => {
|
|
|
|
let from = *bare_fn_ty.sig.inputs.get(0);
|
|
|
|
let to = bare_fn_ty.sig.output;
|
2014-08-06 04:59:40 -05:00
|
|
|
self.check_transmute(expr.span, from, to, expr.id);
|
2014-06-12 16:08:44 -05:00
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
self.tcx
|
|
|
|
.sess
|
|
|
|
.span_bug(expr.span,
|
|
|
|
"transmute wasn't a bare fn?!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_expr(self, expr);
|
2014-06-12 16:08:44 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn check_crate(tcx: &ctxt, krate: &ast::Crate) {
|
|
|
|
let mut visitor = IntrinsicCheckingVisitor {
|
|
|
|
tcx: tcx,
|
|
|
|
};
|
|
|
|
|
2014-09-12 05:10:30 -05:00
|
|
|
visit::walk_crate(&mut visitor, krate);
|
2014-06-12 16:08:44 -05:00
|
|
|
}
|
|
|
|
|