From d0f70ebcb8d5b879c068deb333dacc5ccee4478a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 16 Dec 2014 15:00:05 -0500 Subject: [PATCH 1/4] Refactor various queries out of `ty` and into `trans/common` --- src/librustc/middle/ty.rs | 44 ---------------------- src/librustc_trans/trans/callee.rs | 2 +- src/librustc_trans/trans/cleanup.rs | 12 +++--- src/librustc_trans/trans/common.rs | 49 +++++++++++++++++++++++++ src/librustc_trans/trans/context.rs | 6 +++ src/librustc_trans/trans/controlflow.rs | 2 +- src/librustc_trans/trans/datum.rs | 4 +- src/librustc_trans/trans/expr.rs | 6 +-- src/librustc_trans/trans/glue.rs | 8 ++-- src/librustc_trans/trans/intrinsic.rs | 2 +- src/librustc_trans/trans/meth.rs | 2 +- src/librustc_trans/trans/tvec.rs | 2 +- 12 files changed, 75 insertions(+), 64 deletions(-) diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 819defdb0caf2..a0cf7590ea367 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -658,7 +658,6 @@ pub struct ctxt<'tcx> { pub tcache: RefCell>>, pub rcache: RefCell>>, pub short_names_cache: RefCell, String>>, - pub needs_unwind_cleanup_cache: RefCell, bool>>, pub tc_cache: RefCell, TypeContents>>, pub ast_ty_to_ty_cache: RefCell>>, pub enum_var_cache: RefCell>>>>>, @@ -2006,7 +2005,6 @@ pub fn mk_ctxt<'tcx>(s: Session, tcache: RefCell::new(DefIdMap::new()), rcache: RefCell::new(FnvHashMap::new()), short_names_cache: RefCell::new(FnvHashMap::new()), - needs_unwind_cleanup_cache: RefCell::new(FnvHashMap::new()), tc_cache: RefCell::new(FnvHashMap::new()), ast_ty_to_ty_cache: RefCell::new(NodeMap::new()), enum_var_cache: RefCell::new(DefIdMap::new()), @@ -2690,48 +2688,6 @@ pub fn type_is_floating_point(ty: Ty) -> bool { } } -pub fn type_needs_drop<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> bool { - type_contents(cx, ty).needs_drop(cx) -} - -// Some things don't need cleanups during unwinding because the -// task can free them all at once later. Currently only things -// that only contain scalars and shared boxes can avoid unwind -// cleanups. -pub fn type_needs_unwind_cleanup<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> bool { - return memoized(&cx.needs_unwind_cleanup_cache, ty, |ty| { - type_needs_unwind_cleanup_(cx, ty, &mut FnvHashSet::new()) - }); - - fn type_needs_unwind_cleanup_<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>, - tycache: &mut FnvHashSet>) -> bool { - // Prevent infinite recursion - if !tycache.insert(ty) { - return false; - } - - let mut needs_unwind_cleanup = false; - maybe_walk_ty(ty, |ty| { - needs_unwind_cleanup |= match ty.sty { - ty_bool | ty_int(_) | ty_uint(_) | - ty_float(_) | ty_tup(_) | ty_ptr(_) => false, - - ty_enum(did, ref substs) => - enum_variants(cx, did).iter().any(|v| - v.args.iter().any(|aty| { - let t = aty.subst(cx, substs); - type_needs_unwind_cleanup_(cx, t, tycache) - }) - ), - - _ => true - }; - !needs_unwind_cleanup - }); - needs_unwind_cleanup - } -} - /// Type contents is how the type checker reasons about kinds. /// They track what kinds of things are found within a type. You can /// think of them as kind of an "anti-kind". They track the kinds of values diff --git a/src/librustc_trans/trans/callee.rs b/src/librustc_trans/trans/callee.rs index 7f22faf050da0..b345bed8096a2 100644 --- a/src/librustc_trans/trans/callee.rs +++ b/src/librustc_trans/trans/callee.rs @@ -721,7 +721,7 @@ pub fn trans_call_inner<'a, 'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>, }; if !is_rust_fn || type_of::return_uses_outptr(ccx, ret_ty) || - ty::type_needs_drop(bcx.tcx(), ret_ty) { + type_needs_drop(bcx.tcx(), ret_ty) { // Push the out-pointer if we use an out-pointer for this // return type, otherwise push "undef". if type_is_zero_size(ccx, ret_ty) { diff --git a/src/librustc_trans/trans/cleanup.rs b/src/librustc_trans/trans/cleanup.rs index c1bb21c496adf..4d02adfa7ee5a 100644 --- a/src/librustc_trans/trans/cleanup.rs +++ b/src/librustc_trans/trans/cleanup.rs @@ -277,10 +277,10 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> { cleanup_scope: ScopeId, val: ValueRef, ty: Ty<'tcx>) { - if !ty::type_needs_drop(self.ccx.tcx(), ty) { return; } + if !common::type_needs_drop(self.ccx.tcx(), ty) { return; } let drop = box DropValue { is_immediate: false, - must_unwind: ty::type_needs_unwind_cleanup(self.ccx.tcx(), ty), + must_unwind: common::type_needs_unwind_cleanup(self.ccx, ty), val: val, ty: ty, zero: false @@ -299,10 +299,10 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> { cleanup_scope: ScopeId, val: ValueRef, ty: Ty<'tcx>) { - if !ty::type_needs_drop(self.ccx.tcx(), ty) { return; } + if !common::type_needs_drop(self.ccx.tcx(), ty) { return; } let drop = box DropValue { is_immediate: false, - must_unwind: ty::type_needs_unwind_cleanup(self.ccx.tcx(), ty), + must_unwind: common::type_needs_unwind_cleanup(self.ccx, ty), val: val, ty: ty, zero: true @@ -323,10 +323,10 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> { val: ValueRef, ty: Ty<'tcx>) { - if !ty::type_needs_drop(self.ccx.tcx(), ty) { return; } + if !common::type_needs_drop(self.ccx.tcx(), ty) { return; } let drop = box DropValue { is_immediate: true, - must_unwind: ty::type_needs_unwind_cleanup(self.ccx.tcx(), ty), + must_unwind: common::type_needs_unwind_cleanup(self.ccx, ty), val: val, ty: ty, zero: false diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index ea2a4ef6b2801..0e3eab4785d6b 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -53,9 +53,58 @@ use syntax::ast_map::{PathElem, PathName}; use syntax::codemap::Span; use syntax::parse::token::InternedString; use syntax::parse::token; +use util::common::memoized; +use util::nodemap::FnvHashSet; pub use trans::context::CrateContext; +// Some things don't need cleanups during unwinding because the +// task can free them all at once later. Currently only things +// that only contain scalars and shared boxes can avoid unwind +// cleanups. +pub fn type_needs_unwind_cleanup<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { + return memoized(ccx.needs_unwind_cleanup_cache(), ty, |ty| { + type_needs_unwind_cleanup_(ccx.tcx(), ty, &mut FnvHashSet::new()) + }); + + fn type_needs_unwind_cleanup_<'tcx>(tcx: &ty::ctxt<'tcx>, + ty: Ty<'tcx>, + tycache: &mut FnvHashSet>) + -> bool + { + // Prevent infinite recursion + if !tycache.insert(ty) { + return false; + } + + let mut needs_unwind_cleanup = false; + ty::maybe_walk_ty(ty, |ty| { + needs_unwind_cleanup |= match ty.sty { + ty::ty_bool | ty::ty_int(_) | ty::ty_uint(_) | + ty::ty_float(_) | ty::ty_tup(_) | ty::ty_ptr(_) => false, + + ty::ty_enum(did, ref substs) => + ty::enum_variants(tcx, did).iter().any(|v| + v.args.iter().any(|aty| { + let t = aty.subst(tcx, substs); + type_needs_unwind_cleanup_(tcx, t, tycache) + }) + ), + + _ => true + }; + !needs_unwind_cleanup + }); + needs_unwind_cleanup + } +} + +pub fn type_needs_drop<'tcx>(cx: &ty::ctxt<'tcx>, + ty: Ty<'tcx>) + -> bool { + ty::type_contents(cx, ty).needs_drop(cx) +} + fn type_is_newtype_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool { match ty.sty { diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs index 2c71dd831fbcb..02eaef885483f 100644 --- a/src/librustc_trans/trans/context.rs +++ b/src/librustc_trans/trans/context.rs @@ -84,6 +84,7 @@ pub struct LocalCrateContext<'tcx> { tn: TypeNames, externs: RefCell, item_vals: RefCell>, + needs_unwind_cleanup_cache: RefCell, bool>>, fn_pointer_shims: RefCell, ValueRef>>, drop_glues: RefCell, ValueRef>>, tydescs: RefCell, Rc>>>, @@ -389,6 +390,7 @@ impl<'tcx> LocalCrateContext<'tcx> { tn: TypeNames::new(), externs: RefCell::new(FnvHashMap::new()), item_vals: RefCell::new(NodeMap::new()), + needs_unwind_cleanup_cache: RefCell::new(FnvHashMap::new()), fn_pointer_shims: RefCell::new(FnvHashMap::new()), drop_glues: RefCell::new(FnvHashMap::new()), tydescs: RefCell::new(FnvHashMap::new()), @@ -569,6 +571,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { &self.shared.link_meta } + pub fn needs_unwind_cleanup_cache(&self) -> &RefCell, bool>> { + &self.local.needs_unwind_cleanup_cache + } + pub fn fn_pointer_shims(&self) -> &RefCell, ValueRef>> { &self.local.fn_pointer_shims } diff --git a/src/librustc_trans/trans/controlflow.rs b/src/librustc_trans/trans/controlflow.rs index 3b24ded6717cc..768de89d5935d 100644 --- a/src/librustc_trans/trans/controlflow.rs +++ b/src/librustc_trans/trans/controlflow.rs @@ -87,7 +87,7 @@ pub fn trans_stmt_semi<'blk, 'tcx>(cx: Block<'blk, 'tcx>, e: &ast::Expr) -> Block<'blk, 'tcx> { let _icx = push_ctxt("trans_stmt_semi"); let ty = expr_ty(cx, e); - if ty::type_needs_drop(cx.tcx(), ty) { + if type_needs_drop(cx.tcx(), ty) { expr::trans_to_lvalue(cx, e, "stmt").bcx } else { expr::trans_into(cx, e, expr::Ignore) diff --git a/src/librustc_trans/trans/datum.rs b/src/librustc_trans/trans/datum.rs index 9ab4e92b51131..ca274ab605e7f 100644 --- a/src/librustc_trans/trans/datum.rs +++ b/src/librustc_trans/trans/datum.rs @@ -218,7 +218,7 @@ impl KindOps for Lvalue { val: ValueRef, ty: Ty<'tcx>) -> Block<'blk, 'tcx> { - if ty::type_needs_drop(bcx.tcx(), ty) { + if type_needs_drop(bcx.tcx(), ty) { // cancel cleanup of affine values by zeroing out let () = zero_mem(bcx, val, ty); bcx @@ -567,7 +567,7 @@ impl<'tcx, K: KindOps + fmt::Show> Datum<'tcx, K> { /// scalar-ish (like an int or a pointer) which (1) does not require drop glue and (2) is /// naturally passed around by value, and not by reference. pub fn to_llscalarish<'blk>(self, bcx: Block<'blk, 'tcx>) -> ValueRef { - assert!(!ty::type_needs_drop(bcx.tcx(), self.ty)); + assert!(!type_needs_drop(bcx.tcx(), self.ty)); assert!(self.appropriate_rvalue_mode(bcx.ccx()) == ByValue); if self.kind.is_by_ref() { load_ty(bcx, self.val, self.ty) diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index 6f9990a3e9ed9..a7ba58bb4604d 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -972,7 +972,7 @@ fn trans_rvalue_stmt_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let src_datum = unpack_datum!(bcx, trans(bcx, &**src)); let dst_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, &**dst, "assign")); - if ty::type_needs_drop(bcx.tcx(), dst_datum.ty) { + if type_needs_drop(bcx.tcx(), dst_datum.ty) { // If there are destructors involved, make sure we // are copying from an rvalue, since that cannot possible // alias an lvalue. We are concerned about code like: @@ -1497,7 +1497,7 @@ pub fn trans_adt<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, assert_eq!(discr, 0); match ty::expr_kind(bcx.tcx(), &*base.expr) { - ty::RvalueDpsExpr | ty::RvalueDatumExpr if !ty::type_needs_drop(bcx.tcx(), ty) => { + ty::RvalueDpsExpr | ty::RvalueDatumExpr if !type_needs_drop(bcx.tcx(), ty) => { bcx = trans_into(bcx, &*base.expr, SaveIn(addr)); }, ty::RvalueStmtExpr => bcx.tcx().sess.bug("unexpected expr kind for struct base expr"), @@ -2101,7 +2101,7 @@ fn trans_assign_op<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // Evaluate LHS (destination), which should be an lvalue let dst_datum = unpack_datum!(bcx, trans_to_lvalue(bcx, dst, "assign_op")); - assert!(!ty::type_needs_drop(bcx.tcx(), dst_datum.ty)); + assert!(!type_needs_drop(bcx.tcx(), dst_datum.ty)); let dst_ty = dst_datum.ty; let dst = load_ty(bcx, dst_datum.val, dst_datum.ty); diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs index 26734d854afc0..e23b29a0ad4df 100644 --- a/src/librustc_trans/trans/glue.rs +++ b/src/librustc_trans/trans/glue.rs @@ -84,11 +84,11 @@ pub fn get_drop_glue_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, if !ty::type_is_sized(tcx, t) { return t } - if !ty::type_needs_drop(tcx, t) { + if !type_needs_drop(tcx, t) { return ty::mk_i8(); } match t.sty { - ty::ty_uniq(typ) if !ty::type_needs_drop(tcx, typ) + ty::ty_uniq(typ) if !type_needs_drop(tcx, typ) && ty::type_is_sized(tcx, typ) => { let llty = sizing_type_of(ccx, typ); // `Box` does not allocate. @@ -110,7 +110,7 @@ pub fn drop_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // NB: v is an *alias* of type t here, not a direct value. debug!("drop_ty(t={})", t.repr(bcx.tcx())); let _icx = push_ctxt("drop_ty"); - if ty::type_needs_drop(bcx.tcx(), t) { + if type_needs_drop(bcx.tcx(), t) { let ccx = bcx.ccx(); let glue = get_drop_glue(ccx, t); let glue_type = get_drop_glue_type(ccx, t); @@ -469,7 +469,7 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, t: Ty<'tcx>) ty::ty_vec(ty, None) => tvec::make_drop_glue_unboxed(bcx, v0, ty, false), _ => { assert!(ty::type_is_sized(bcx.tcx(), t)); - if ty::type_needs_drop(bcx.tcx(), t) && + if type_needs_drop(bcx.tcx(), t) && ty::type_is_structural(t) { iter_structural_ty(bcx, v0, t, |bb, vv, tt| drop_ty(bb, vv, tt, None)) } else { diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs index fff89999d99b5..f471a92f6c321 100644 --- a/src/librustc_trans/trans/intrinsic.rs +++ b/src/librustc_trans/trans/intrinsic.rs @@ -365,7 +365,7 @@ pub fn trans_intrinsic_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, } (_, "needs_drop") => { let tp_ty = *substs.types.get(FnSpace, 0); - C_bool(ccx, ty::type_needs_drop(ccx.tcx(), tp_ty)) + C_bool(ccx, type_needs_drop(ccx.tcx(), tp_ty)) } (_, "owns_managed") => { let tp_ty = *substs.types.get(FnSpace, 0); diff --git a/src/librustc_trans/trans/meth.rs b/src/librustc_trans/trans/meth.rs index 38c6b802d0485..7c2c3373ebe04 100644 --- a/src/librustc_trans/trans/meth.rs +++ b/src/librustc_trans/trans/meth.rs @@ -436,7 +436,7 @@ fn trans_trait_callee<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let self_datum = unpack_datum!( bcx, expr::trans(bcx, self_expr)); - let llval = if ty::type_needs_drop(bcx.tcx(), self_datum.ty) { + let llval = if type_needs_drop(bcx.tcx(), self_datum.ty) { let self_datum = unpack_datum!( bcx, self_datum.to_rvalue_datum(bcx, "trait_callee")); diff --git a/src/librustc_trans/trans/tvec.rs b/src/librustc_trans/trans/tvec.rs index e09032ac2d04f..688a0d0725058 100644 --- a/src/librustc_trans/trans/tvec.rs +++ b/src/librustc_trans/trans/tvec.rs @@ -62,7 +62,7 @@ pub fn make_drop_glue_unboxed<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let _icx = push_ctxt("tvec::make_drop_glue_unboxed"); let dataptr = get_dataptr(bcx, vptr); - let bcx = if ty::type_needs_drop(tcx, unit_ty) { + let bcx = if type_needs_drop(tcx, unit_ty) { let len = get_len(bcx, vptr); iter_vec_raw(bcx, dataptr, unit_ty, len, |bb, vv, tt| glue::drop_ty(bb, vv, tt, None)) } else { From 82c996642cba5300bfe869c1dea463a3215cc7a7 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 16 Dec 2014 15:18:23 -0500 Subject: [PATCH 2/4] Make ExprUseVisitor<..> use inherited parameter environments. --- src/librustc/middle/check_match.rs | 2 +- src/librustc/middle/check_rvalues.rs | 2 +- src/librustc/middle/check_static.rs | 2 +- src/librustc/middle/expr_use_visitor.rs | 15 +++++++-------- src/librustc_borrowck/borrowck/check_loans.rs | 2 +- .../borrowck/gather_loans/mod.rs | 2 +- src/librustc_trans/trans/_match.rs | 2 +- 7 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index da1bd09ceffdd..df90118434dbd 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -1065,7 +1065,7 @@ fn check_for_mutation_in_guard<'a, 'tcx>(cx: &'a MatchCheckCtxt<'a, 'tcx>, }; let mut visitor = ExprUseVisitor::new(&mut checker, checker.cx.tcx, - cx.param_env.clone()); + &cx.param_env); visitor.walk_expr(guard); } diff --git a/src/librustc/middle/check_rvalues.rs b/src/librustc/middle/check_rvalues.rs index a14307b90ee83..494d85f7ba56d 100644 --- a/src/librustc/middle/check_rvalues.rs +++ b/src/librustc/middle/check_rvalues.rs @@ -40,7 +40,7 @@ impl<'a, 'tcx, 'v> visit::Visitor<'v> for RvalueContext<'a, 'tcx> { fn_id: ast::NodeId) { { let param_env = ParameterEnvironment::for_item(self.tcx, fn_id); - let mut euv = euv::ExprUseVisitor::new(self, self.tcx, param_env); + let mut euv = euv::ExprUseVisitor::new(self, self.tcx, ¶m_env); euv.walk_fn(fd, b); } visit::walk_fn(self, fk, fd, b, s) diff --git a/src/librustc/middle/check_static.rs b/src/librustc/middle/check_static.rs index 6ff34d625005f..4c9cd64d7ebd4 100644 --- a/src/librustc/middle/check_static.rs +++ b/src/librustc/middle/check_static.rs @@ -71,7 +71,7 @@ pub fn check_crate(tcx: &ty::ctxt) { }; { let param_env = ty::empty_parameter_environment(); - let visitor = euv::ExprUseVisitor::new(&mut checker, tcx, param_env); + let visitor = euv::ExprUseVisitor::new(&mut checker, tcx, ¶m_env); visit::walk_crate(&mut GlobalVisitor(visitor), tcx.map.krate()); } visit::walk_crate(&mut CheckStaticVisitor { diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index c7dd4dd26469c..8a670b9ec4156 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -295,11 +295,11 @@ impl OverloadedCallType { // supplies types from the tree. After type checking is complete, you // can just use the tcx as the typer. -pub struct ExprUseVisitor<'d,'t,'tcx,TYPER:'t> { +pub struct ExprUseVisitor<'d,'t,'tcx:'t,TYPER:'t> { typer: &'t TYPER, mc: mc::MemCategorizationContext<'t,TYPER>, delegate: &'d mut (Delegate<'tcx>+'d), - param_env: ParameterEnvironment<'tcx>, + param_env: &'t ParameterEnvironment<'tcx>, } /// Whether the elements of an overloaded operation are passed by value or by reference @@ -311,7 +311,7 @@ enum PassArgs { impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { pub fn new(delegate: &'d mut Delegate<'tcx>, typer: &'t TYPER, - param_env: ParameterEnvironment<'tcx>) + param_env: &'t ParameterEnvironment<'tcx>) -> ExprUseVisitor<'d,'t,'tcx,TYPER> { ExprUseVisitor { typer: typer, @@ -355,7 +355,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { cmt: mc::cmt<'tcx>) { let mode = copy_or_move(self.tcx(), cmt.ty, - &self.param_env, + self.param_env, DirectRefMove); self.delegate.consume(consume_id, consume_span, cmt, mode); } @@ -991,7 +991,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { ast::PatIdent(ast::BindByValue(_), _, _) => { match copy_or_move(tcx, cmt_pat.ty, - &self.param_env, + self.param_env, PatBindingMove) { Copy => mode.lub(CopyingMatch), Move(_) => mode.lub(MovingMatch), @@ -1021,8 +1021,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { let typer = self.typer; let def_map = &self.typer.tcx().def_map; let delegate = &mut self.delegate; - let param_env = &mut self.param_env; - + let param_env = self.param_env; mc.cat_pattern(cmt_discr.clone(), pat, |mc, cmt_pat, pat| { if pat_util::pat_is_binding(def_map, pat) { let tcx = typer.tcx(); @@ -1242,7 +1241,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { closure_expr.span, freevar.def); let mode = copy_or_move(self.tcx(), cmt_var.ty, - &self.param_env, CaptureMove); + self.param_env, CaptureMove); self.delegate.consume(closure_expr.id, freevar.span, cmt_var, mode); } } diff --git a/src/librustc_borrowck/borrowck/check_loans.rs b/src/librustc_borrowck/borrowck/check_loans.rs index 046b9547cbbc5..04fe098f7dde5 100644 --- a/src/librustc_borrowck/borrowck/check_loans.rs +++ b/src/librustc_borrowck/borrowck/check_loans.rs @@ -210,7 +210,7 @@ pub fn check_loans<'a, 'b, 'c, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, { let mut euv = euv::ExprUseVisitor::new(&mut clcx, bccx.tcx, - param_env.clone()); + ¶m_env); euv.walk_fn(decl, body); } } diff --git a/src/librustc_borrowck/borrowck/gather_loans/mod.rs b/src/librustc_borrowck/borrowck/gather_loans/mod.rs index c0e892cdd2798..7854c8acb4796 100644 --- a/src/librustc_borrowck/borrowck/gather_loans/mod.rs +++ b/src/librustc_borrowck/borrowck/gather_loans/mod.rs @@ -53,7 +53,7 @@ pub fn gather_loans_in_fn<'a, 'tcx>(bccx: &BorrowckCtxt<'a, 'tcx>, { let mut euv = euv::ExprUseVisitor::new(&mut glcx, bccx.tcx, - param_env); + ¶m_env); euv.walk_fn(decl, body); } diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs index fb8006905f9f2..b2e39c0c3a6ae 100644 --- a/src/librustc_trans/trans/_match.rs +++ b/src/librustc_trans/trans/_match.rs @@ -1261,7 +1261,7 @@ fn is_discr_reassigned(bcx: Block, discr: &ast::Expr, body: &ast::Expr) -> bool }; { let param_env = ty::empty_parameter_environment(); - let mut visitor = euv::ExprUseVisitor::new(&mut rc, bcx, param_env); + let mut visitor = euv::ExprUseVisitor::new(&mut rc, bcx, ¶m_env); visitor.walk_expr(body); } rc.reassigned From 75d5e2f9d85c96ba9e165ec234af3de7362a3e2a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 18 Dec 2014 09:26:10 -0500 Subject: [PATCH 3/4] Move the TypeContents-based "Sized" queries into trans, where the full types are always known and hence the ParameterEnvironment is not necessary. For other `Sized` queries, use the trait infrastructure just like `Copy`. --- src/librustc/middle/check_rvalues.rs | 14 ++- src/librustc/middle/traits/mod.rs | 38 ++++++++ src/librustc/middle/ty.rs | 120 +++++++++++--------------- src/librustc_trans/trans/adt.rs | 8 +- src/librustc_trans/trans/base.rs | 10 +-- src/librustc_trans/trans/common.rs | 56 +++++++++++- src/librustc_trans/trans/consts.rs | 2 +- src/librustc_trans/trans/datum.rs | 4 +- src/librustc_trans/trans/expr.rs | 16 ++-- src/librustc_trans/trans/glue.rs | 26 +++--- src/librustc_trans/trans/intrinsic.rs | 4 +- src/librustc_trans/trans/type_of.rs | 10 +-- src/librustc_typeck/check/mod.rs | 18 +++- 13 files changed, 207 insertions(+), 119 deletions(-) diff --git a/src/librustc/middle/check_rvalues.rs b/src/librustc/middle/check_rvalues.rs index 494d85f7ba56d..3b4ea5234f43f 100644 --- a/src/librustc/middle/check_rvalues.rs +++ b/src/librustc/middle/check_rvalues.rs @@ -28,7 +28,7 @@ pub fn check_crate(tcx: &ty::ctxt, } struct RvalueContext<'a, 'tcx: 'a> { - tcx: &'a ty::ctxt<'tcx> + tcx: &'a ty::ctxt<'tcx>, } impl<'a, 'tcx, 'v> visit::Visitor<'v> for RvalueContext<'a, 'tcx> { @@ -40,21 +40,27 @@ impl<'a, 'tcx, 'v> visit::Visitor<'v> for RvalueContext<'a, 'tcx> { fn_id: ast::NodeId) { { let param_env = ParameterEnvironment::for_item(self.tcx, fn_id); - let mut euv = euv::ExprUseVisitor::new(self, self.tcx, ¶m_env); + let mut delegate = RvalueContextDelegate { tcx: self.tcx, param_env: ¶m_env }; + let mut euv = euv::ExprUseVisitor::new(&mut delegate, self.tcx, ¶m_env); euv.walk_fn(fd, b); } visit::walk_fn(self, fk, fd, b, s) } } -impl<'a, 'tcx> euv::Delegate<'tcx> for RvalueContext<'a, 'tcx> { +struct RvalueContextDelegate<'a, 'tcx: 'a> { + tcx: &'a ty::ctxt<'tcx>, + param_env: &'a ty::ParameterEnvironment<'tcx>, +} + +impl<'a, 'tcx> euv::Delegate<'tcx> for RvalueContextDelegate<'a, 'tcx> { fn consume(&mut self, _: ast::NodeId, span: Span, cmt: mc::cmt<'tcx>, _: euv::ConsumeMode) { debug!("consume; cmt: {}; type: {}", *cmt, ty_to_string(self.tcx, cmt.ty)); - if !ty::type_is_sized(self.tcx, cmt.ty) { + if !ty::type_is_sized(self.tcx, cmt.ty, self.param_env) { span_err!(self.tcx.sess, span, E0161, "cannot move a value of type {0}: the size of {0} cannot be statically determined", ty_to_string(self.tcx, cmt.ty)); diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs index 8028971a46346..4961bcbc2c2e7 100644 --- a/src/librustc/middle/traits/mod.rs +++ b/src/librustc/middle/traits/mod.rs @@ -22,6 +22,7 @@ use std::rc::Rc; use std::slice::Iter; use syntax::ast; use syntax::codemap::{Span, DUMMY_SP}; +use util::ppaux::Repr; pub use self::fulfill::{FulfillmentContext, RegionObligation}; pub use self::select::SelectionContext; @@ -259,6 +260,43 @@ pub fn predicates_for_generics<'tcx>(tcx: &ty::ctxt<'tcx>, util::predicates_for_generics(tcx, cause, 0, generic_bounds) } +/// Determines whether the type `ty` is known to meet `bound` and +/// returns true if so. Returns false if `ty` either does not meet +/// `bound` or is not known to meet bound (note that this is +/// conservative towards *no impl*, which is the opposite of the +/// `evaluate` methods). +pub fn type_known_to_meet_builtin_bound<'a,'tcx>(infcx: &InferCtxt<'a,'tcx>, + param_env: &ty::ParameterEnvironment<'tcx>, + ty: Ty<'tcx>, + bound: ty::BuiltinBound) + -> bool +{ + debug!("type_known_to_meet_builtin_bound(ty={}, bound={})", + ty.repr(infcx.tcx), + bound); + + let mut fulfill_cx = FulfillmentContext::new(); + + // We can use dummy values here because we won't report any errors + // that result nor will we pay any mind to region obligations that arise + // (there shouldn't really be any anyhow). + let cause = ObligationCause::misc(DUMMY_SP, ast::DUMMY_NODE_ID); + + fulfill_cx.register_builtin_bound(infcx.tcx, ty, bound, cause); + + // Note: we only assume something is `Copy` if we can + // *definitively* show that it implements `Copy`. Otherwise, + // assume it is move; linear is always ok. + let result = fulfill_cx.select_all_or_error(infcx, param_env, infcx.tcx).is_ok(); + + debug!("type_known_to_meet_builtin_bound: ty={} bound={} result={}", + ty.repr(infcx.tcx), + bound, + result); + + result +} + impl<'tcx,O> Obligation<'tcx,O> { pub fn new(cause: ObligationCause<'tcx>, trait_ref: O) diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index a0cf7590ea367..c78bc47408c0d 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -56,7 +56,6 @@ use middle::resolve_lifetime; use middle::infer; use middle::stability; use middle::subst::{mod, Subst, Substs, VecPerParamSpace}; -use middle::traits::ObligationCause; use middle::traits; use middle::ty; use middle::ty_fold::{mod, TypeFoldable, TypeFolder}; @@ -65,7 +64,7 @@ use util::ppaux::{trait_store_to_string, ty_to_string}; use util::ppaux::{Repr, UserString}; use util::common::{indenter, memoized, ErrorReported}; use util::nodemap::{NodeMap, NodeSet, DefIdMap, DefIdSet}; -use util::nodemap::{FnvHashMap, FnvHashSet}; +use util::nodemap::{FnvHashMap}; use arena::TypedArena; use std::borrow::BorrowFrom; @@ -80,13 +79,13 @@ use collections::enum_set::{EnumSet, CLike}; use std::collections::{HashMap, HashSet}; use std::collections::hash_map::Entry::{Occupied, Vacant}; use syntax::abi; -use syntax::ast::{CrateNum, DefId, DUMMY_NODE_ID, Ident, ItemTrait, LOCAL_CRATE}; +use syntax::ast::{CrateNum, DefId, Ident, ItemTrait, LOCAL_CRATE}; use syntax::ast::{MutImmutable, MutMutable, Name, NamedField, NodeId}; use syntax::ast::{Onceness, StmtExpr, StmtSemi, StructField, UnnamedField}; use syntax::ast::{Visibility}; use syntax::ast_util::{mod, is_local, lit_is_str, local_def, PostExpansionMethod}; use syntax::attr::{mod, AttrMetaMethods}; -use syntax::codemap::{DUMMY_SP, Span}; +use syntax::codemap::Span; use syntax::parse::token::{mod, InternedString}; use syntax::{ast, ast_map}; @@ -756,8 +755,15 @@ pub struct ctxt<'tcx> { /// Caches the representation hints for struct definitions. pub repr_hint_cache: RefCell>>>, - /// Caches whether types move by default. - pub type_moves_by_default_cache: RefCell,bool>>, + /// Caches whether types are known to impl Copy. Note that type + /// parameters are never placed into this cache, because their + /// results are dependent on the parameter environment. + pub type_impls_copy_cache: RefCell,bool>>, + + /// Caches whether types are known to impl Sized. Note that type + /// parameters are never placed into this cache, because their + /// results are dependent on the parameter environment. + pub type_impls_sized_cache: RefCell,bool>>, } // Flags that we track on types. These flags are propagated upwards @@ -2040,7 +2046,8 @@ pub fn mk_ctxt<'tcx>(s: Session, associated_types: RefCell::new(DefIdMap::new()), selection_cache: traits::SelectionCache::new(), repr_hint_cache: RefCell::new(DefIdMap::new()), - type_moves_by_default_cache: RefCell::new(HashMap::new()), + type_impls_copy_cache: RefCell::new(HashMap::new()), + type_impls_sized_cache: RefCell::new(HashMap::new()), } } @@ -2657,14 +2664,6 @@ pub fn type_is_unique(ty: Ty) -> bool { } } -pub fn type_is_fat_ptr<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> bool { - match ty.sty { - ty_ptr(mt{ty, ..}) | ty_rptr(_, mt{ty, ..}) - | ty_uniq(ty) if !type_is_sized(cx, ty) => true, - _ => false, - } -} - /* A scalar type is one that denotes an atomic datum, with no sub-components. (A ty_ptr is scalar because it represents a non-managed pointer, so its @@ -3154,17 +3153,22 @@ pub fn type_contents<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> TypeContents { } } -pub fn type_moves_by_default<'tcx>(cx: &ctxt<'tcx>, - ty: Ty<'tcx>, - param_env: &ParameterEnvironment<'tcx>) - -> bool +fn type_impls_bound<'tcx>(cx: &ctxt<'tcx>, + cache: &RefCell,bool>>, + param_env: &ParameterEnvironment<'tcx>, + ty: Ty<'tcx>, + bound: ty::BuiltinBound) + -> bool { + assert!(!ty::type_needs_infer(ty)); + if !type_has_params(ty) && !type_has_self(ty) { - match cx.type_moves_by_default_cache.borrow().get(&ty) { + match cache.borrow().get(&ty) { None => {} Some(&result) => { - debug!("determined whether {} moves by default (cached): {}", + debug!("type_impls_bound({}, {}) = {} (cached)", ty_to_string(cx, ty), + bound, result); return result } @@ -3172,27 +3176,35 @@ pub fn type_moves_by_default<'tcx>(cx: &ctxt<'tcx>, } let infcx = infer::new_infer_ctxt(cx); - let mut fulfill_cx = traits::FulfillmentContext::new(); + let is_impld = traits::type_known_to_meet_builtin_bound(&infcx, param_env, ty, bound); - // we can use dummy values here because we won't report any errors - // that result nor will we pay any mind to region obligations that arise - // (there shouldn't really be any anyhow) - let cause = ObligationCause::misc(DUMMY_SP, DUMMY_NODE_ID); + debug!("type_impls_bound({}, {}) = {}", + ty_to_string(cx, ty), + bound, + is_impld); - fulfill_cx.register_builtin_bound(cx, ty, ty::BoundCopy, cause); + if !type_has_params(ty) && !type_has_self(ty) { + let old_value = cache.borrow_mut().insert(ty, is_impld); + assert!(old_value.is_none()); + } - // Note: we only assuming something is `Copy` if we can - // *definitively* show that it implements `Copy`. Otherwise, - // assume it is move; linear is always ok. - let is_copy = fulfill_cx.select_all_or_error(&infcx, param_env, cx).is_ok(); - let is_move = !is_copy; + is_impld +} - debug!("determined whether {} moves by default: {}", - ty_to_string(cx, ty), - is_move); +pub fn type_moves_by_default<'tcx>(cx: &ctxt<'tcx>, + ty: Ty<'tcx>, + param_env: &ParameterEnvironment<'tcx>) + -> bool +{ + !type_impls_bound(cx, &cx.type_impls_copy_cache, param_env, ty, ty::BoundCopy) +} - cx.type_moves_by_default_cache.borrow_mut().insert(ty, is_move); - is_move +pub fn type_is_sized<'tcx>(cx: &ctxt<'tcx>, + ty: Ty<'tcx>, + param_env: &ParameterEnvironment<'tcx>) + -> bool +{ + type_impls_bound(cx, &cx.type_impls_sized_cache, param_env, ty, ty::BoundSized) } pub fn is_ffi_safe<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> bool { @@ -3564,40 +3576,6 @@ pub fn type_is_machine(ty: Ty) -> bool { } } -// Is the type's representation size known at compile time? -pub fn type_is_sized<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> bool { - type_contents(cx, ty).is_sized(cx) -} - -pub fn lltype_is_sized<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> bool { - match ty.sty { - ty_open(_) => true, - _ => type_contents(cx, ty).is_sized(cx) - } -} - -// Return the smallest part of `ty` which is unsized. Fails if `ty` is sized. -// 'Smallest' here means component of the static representation of the type; not -// the size of an object at runtime. -pub fn unsized_part_of_type<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.sty { - ty_str | ty_trait(..) | ty_vec(..) => ty, - ty_struct(def_id, ref substs) => { - let unsized_fields: Vec<_> = struct_fields(cx, def_id, substs).iter() - .map(|f| f.mt.ty).filter(|ty| !type_is_sized(cx, *ty)).collect(); - // Exactly one of the fields must be unsized. - assert!(unsized_fields.len() == 1); - - unsized_part_of_type(cx, unsized_fields[0]) - } - _ => { - assert!(type_is_sized(cx, ty), - "unsized_part_of_type failed even though ty is unsized"); - panic!("called unsized_part_of_type with sized ty"); - } - } -} - // Whether a type is enum like, that is an enum type with only nullary // constructors pub fn type_is_c_like_enum(cx: &ctxt, ty: Ty) -> bool { diff --git a/src/librustc_trans/trans/adt.rs b/src/librustc_trans/trans/adt.rs index 0d2876bdf81ce..7ef534f280684 100644 --- a/src/librustc_trans/trans/adt.rs +++ b/src/librustc_trans/trans/adt.rs @@ -359,7 +359,7 @@ impl<'tcx> Case<'tcx> { // &Trait is a pair of pointers: the actual object and a vtable ty::ty_trait(..) => return Some(FatPointer(i)), - ty::ty_struct(..) if !ty::type_is_sized(cx.tcx(), ty) => { + ty::ty_struct(..) if !type_is_sized(cx.tcx(), ty) => { return Some(FatPointer(i)) } @@ -398,12 +398,12 @@ fn mk_struct<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, tys: &[Ty<'tcx>], packed: bool, scapegoat: Ty<'tcx>) -> Struct<'tcx> { - let sized = tys.iter().all(|&ty| ty::type_is_sized(cx.tcx(), ty)); + let sized = tys.iter().all(|&ty| type_is_sized(cx.tcx(), ty)); let lltys : Vec = if sized { tys.iter() .map(|&ty| type_of::sizing_type_of(cx, ty)).collect() } else { - tys.iter().filter(|&ty| ty::type_is_sized(cx.tcx(), *ty)) + tys.iter().filter(|&ty| type_is_sized(cx.tcx(), *ty)) .map(|&ty| type_of::sizing_type_of(cx, ty)).collect() }; @@ -655,7 +655,7 @@ fn generic_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, fn struct_llfields<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, st: &Struct<'tcx>, sizing: bool, dst: bool) -> Vec { if sizing { - st.fields.iter().filter(|&ty| !dst || ty::type_is_sized(cx.tcx(), *ty)) + st.fields.iter().filter(|&ty| !dst || type_is_sized(cx.tcx(), *ty)) .map(|&ty| type_of::sizing_type_of(cx, ty)).collect() } else { st.fields.iter().map(|&ty| type_of::type_of(cx, ty)).collect() diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index f49fc7f06c501..e310e422290cf 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -581,7 +581,7 @@ pub fn compare_scalar_types<'blk, 'tcx>(cx: Block<'blk, 'tcx>, match t.sty { ty::ty_tup(ref tys) if tys.is_empty() => f(nil_type), ty::ty_bool | ty::ty_uint(_) | ty::ty_char => f(unsigned_int), - ty::ty_ptr(mt) if ty::type_is_sized(cx.tcx(), mt.ty) => f(unsigned_int), + ty::ty_ptr(mt) if common::type_is_sized(cx.tcx(), mt.ty) => f(unsigned_int), ty::ty_int(_) => f(signed_int), ty::ty_float(_) => f(floating_point), // Should never get here, because t is scalar. @@ -719,7 +719,7 @@ pub fn iter_structural_ty<'a, 'blk, 'tcx>(cx: Block<'blk, 'tcx>, return cx; } - let (data_ptr, info) = if ty::type_is_sized(cx.tcx(), t) { + let (data_ptr, info) = if common::type_is_sized(cx.tcx(), t) { (av, None) } else { let data = GEPi(cx, av, &[0, abi::FAT_PTR_ADDR]); @@ -736,7 +736,7 @@ pub fn iter_structural_ty<'a, 'blk, 'tcx>(cx: Block<'blk, 'tcx>, let field_ty = field_ty.mt.ty; let llfld_a = adt::trans_field_ptr(cx, &*repr, data_ptr, discr, i); - let val = if ty::type_is_sized(cx.tcx(), field_ty) { + let val = if common::type_is_sized(cx.tcx(), field_ty) { llfld_a } else { let boxed_ty = ty::mk_open(cx.tcx(), field_ty); @@ -2506,7 +2506,7 @@ pub fn get_fn_llvm_attributes<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_ty: Ty< match ret_ty.sty { // `~` pointer return values never alias because ownership // is transferred - ty::ty_uniq(it) if !ty::type_is_sized(ccx.tcx(), it) => {} + ty::ty_uniq(it) if !common::type_is_sized(ccx.tcx(), it) => {} ty::ty_uniq(_) => { attrs.ret(llvm::NoAliasAttribute); } @@ -2517,7 +2517,7 @@ pub fn get_fn_llvm_attributes<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, fn_ty: Ty< match ret_ty.sty { // These are not really pointers but pairs, (pointer, len) ty::ty_uniq(it) | - ty::ty_rptr(_, ty::mt { ty: it, .. }) if !ty::type_is_sized(ccx.tcx(), it) => {} + ty::ty_rptr(_, ty::mt { ty: it, .. }) if !common::type_is_sized(ccx.tcx(), it) => {} ty::ty_uniq(inner) | ty::ty_rptr(_, ty::mt { ty: inner, .. }) => { let llret_sz = llsize_of_real(ccx, type_of::type_of(ccx, inner)); attrs.ret(llvm::DereferenceableAttribute(llret_sz)); diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index 0e3eab4785d6b..32a9f104763d1 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -58,6 +58,58 @@ use util::nodemap::FnvHashSet; pub use trans::context::CrateContext; +// Is the type's representation size known at compile time? +pub fn type_is_sized<'tcx>(cx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool { + ty::type_contents(cx, ty).is_sized(cx) +} + +pub fn lltype_is_sized<'tcx>(cx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool { + match ty.sty { + ty::ty_open(_) => true, + _ => type_is_sized(cx, ty), + } +} + +pub fn type_is_fat_ptr<'tcx>(cx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> bool { + match ty.sty { + ty::ty_ptr(ty::mt{ty, ..}) | + ty::ty_rptr(_, ty::mt{ty, ..}) | + ty::ty_uniq(ty) => { + !type_is_sized(cx, ty) + } + _ => { + false + } + } +} + +// Return the smallest part of `ty` which is unsized. Fails if `ty` is sized. +// 'Smallest' here means component of the static representation of the type; not +// the size of an object at runtime. +pub fn unsized_part_of_type<'tcx>(cx: &ty::ctxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { + match ty.sty { + ty::ty_str | ty::ty_trait(..) | ty::ty_vec(..) => ty, + ty::ty_struct(def_id, ref substs) => { + let unsized_fields: Vec<_> = + ty::struct_fields(cx, def_id, substs) + .iter() + .map(|f| f.mt.ty) + .filter(|ty| !type_is_sized(cx, *ty)) + .collect(); + + // Exactly one of the fields must be unsized. + assert!(unsized_fields.len() == 1); + + unsized_part_of_type(cx, unsized_fields[0]) + } + _ => { + assert!(type_is_sized(cx, ty), + "unsized_part_of_type failed even though ty is unsized"); + panic!("called unsized_part_of_type with sized ty"); + } + } +} + // Some things don't need cleanups during unwinding because the // task can free them all at once later. Currently only things // that only contain scalars and shared boxes can avoid unwind @@ -128,10 +180,10 @@ pub fn type_is_immediate<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) - ty::type_is_unique(ty) || ty::type_is_region_ptr(ty) || type_is_newtype_immediate(ccx, ty) || ty::type_is_simd(tcx, ty); - if simple && !ty::type_is_fat_ptr(tcx, ty) { + if simple && !type_is_fat_ptr(tcx, ty) { return true; } - if !ty::type_is_sized(tcx, ty) { + if !type_is_sized(tcx, ty) { return false; } match ty.sty { diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index bc386dc96a469..0fd6d286e8b2d 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -138,7 +138,7 @@ fn const_deref<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, v: ValueRef, Some(ref mt) => { match t.sty { ty::ty_ptr(mt) | ty::ty_rptr(_, mt) => { - if ty::type_is_sized(cx.tcx(), mt.ty) { + if type_is_sized(cx.tcx(), mt.ty) { (const_deref_ptr(cx, v), mt.ty) } else { // Derefing a fat pointer does not change the representation, diff --git a/src/librustc_trans/trans/datum.rs b/src/librustc_trans/trans/datum.rs index ca274ab605e7f..83bf06383a89c 100644 --- a/src/librustc_trans/trans/datum.rs +++ b/src/librustc_trans/trans/datum.rs @@ -398,7 +398,7 @@ impl<'tcx> Datum<'tcx, Expr> { -> DatumBlock<'blk, 'tcx, Lvalue> { debug!("to_lvalue_datum self: {}", self.to_string(bcx.ccx())); - assert!(ty::lltype_is_sized(bcx.tcx(), self.ty), + assert!(lltype_is_sized(bcx.tcx(), self.ty), "Trying to convert unsized value to lval"); self.match_kind( |l| DatumBlock::new(bcx, l), @@ -456,7 +456,7 @@ impl<'tcx> Datum<'tcx, Lvalue> { F: FnOnce(ValueRef) -> ValueRef, { let val = match self.ty.sty { - _ if ty::type_is_sized(bcx.tcx(), self.ty) => gep(self.val), + _ if type_is_sized(bcx.tcx(), self.ty) => gep(self.val), ty::ty_open(_) => { let base = Load(bcx, expr::get_dataptr(bcx, self.val)); gep(base) diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index a7ba58bb4604d..624414587c08a 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -280,7 +280,7 @@ fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr.repr(bcx.tcx()), datum.to_string(bcx.ccx())); - if !ty::type_is_sized(bcx.tcx(), datum.ty) { + if !type_is_sized(bcx.tcx(), datum.ty) { debug!("Taking address of unsized type {}", bcx.ty_to_string(datum.ty)); ref_fat_ptr(bcx, expr, datum) @@ -689,7 +689,7 @@ fn trans_field<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>, field_tys[ix].mt.ty, |srcval| adt::trans_field_ptr(bcx, &*repr, srcval, discr, ix)); - if ty::type_is_sized(bcx.tcx(), d.ty) { + if type_is_sized(bcx.tcx(), d.ty) { DatumBlock { datum: d.to_expr_datum(), bcx: bcx } } else { let scratch = rvalue_scratch_datum(bcx, ty::mk_open(bcx.tcx(), d.ty), ""); @@ -769,7 +769,7 @@ fn trans_index<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, Some(SaveIn(scratch.val)), true)); let datum = scratch.to_expr_datum(); - if ty::type_is_sized(bcx.tcx(), elt_ty) { + if type_is_sized(bcx.tcx(), elt_ty) { Datum::new(datum.to_llscalarish(bcx), elt_ty, LvalueExpr) } else { Datum::new(datum.val, ty::mk_open(bcx.tcx(), elt_ty), LvalueExpr) @@ -1506,7 +1506,7 @@ pub fn trans_adt<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, for &(i, t) in base.fields.iter() { let datum = base_datum.get_element( bcx, t, |srcval| adt::trans_field_ptr(bcx, &*repr, srcval, discr, i)); - assert!(ty::type_is_sized(bcx.tcx(), datum.ty)); + assert!(type_is_sized(bcx.tcx(), datum.ty)); let dest = adt::trans_field_ptr(bcx, &*repr, addr, discr, i); bcx = datum.store_to(bcx, dest); } @@ -1634,7 +1634,7 @@ fn trans_uniq_expr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, -> DatumBlock<'blk, 'tcx, Expr> { let _icx = push_ctxt("trans_uniq_expr"); let fcx = bcx.fcx; - assert!(ty::type_is_sized(bcx.tcx(), contents_ty)); + assert!(type_is_sized(bcx.tcx(), contents_ty)); let llty = type_of::type_of(bcx.ccx(), contents_ty); let size = llsize_of(bcx.ccx(), llty); let align = C_uint(bcx.ccx(), type_of::align_of(bcx.ccx(), contents_ty)); @@ -1969,7 +1969,7 @@ pub fn cast_type_kind<'tcx>(tcx: &ty::ctxt<'tcx>, t: Ty<'tcx>) -> cast_kind { ty::ty_char => cast_integral, ty::ty_float(..) => cast_float, ty::ty_rptr(_, mt) | ty::ty_ptr(mt) => { - if ty::type_is_sized(tcx, mt.ty) { + if type_is_sized(tcx, mt.ty) { cast_pointer } else { cast_other @@ -2201,7 +2201,7 @@ fn deref_once<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let r = match datum.ty.sty { ty::ty_uniq(content_ty) => { - if ty::type_is_sized(bcx.tcx(), content_ty) { + if type_is_sized(bcx.tcx(), content_ty) { deref_owned_pointer(bcx, expr, datum, content_ty) } else { // A fat pointer and an opened DST value have the same @@ -2220,7 +2220,7 @@ fn deref_once<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ty::ty_ptr(ty::mt { ty: content_ty, .. }) | ty::ty_rptr(_, ty::mt { ty: content_ty, .. }) => { - if ty::type_is_sized(bcx.tcx(), content_ty) { + if type_is_sized(bcx.tcx(), content_ty) { let ptr = datum.to_llscalarish(bcx); // Always generate an lvalue datum, even if datum.mode is diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs index e23b29a0ad4df..b917966f04ff7 100644 --- a/src/librustc_trans/trans/glue.rs +++ b/src/librustc_trans/trans/glue.rs @@ -63,7 +63,7 @@ pub fn trans_exchange_free<'blk, 'tcx>(cx: Block<'blk, 'tcx>, v: ValueRef, pub fn trans_exchange_free_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ptr: ValueRef, content_ty: Ty<'tcx>) -> Block<'blk, 'tcx> { - assert!(ty::type_is_sized(bcx.ccx().tcx(), content_ty)); + assert!(type_is_sized(bcx.ccx().tcx(), content_ty)); let sizing_type = sizing_type_of(bcx.ccx(), content_ty); let content_size = llsize_of_alloc(bcx.ccx(), sizing_type); @@ -81,7 +81,7 @@ pub fn get_drop_glue_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let tcx = ccx.tcx(); // Even if there is no dtor for t, there might be one deeper down and we // might need to pass in the vtable ptr. - if !ty::type_is_sized(tcx, t) { + if !type_is_sized(tcx, t) { return t } if !type_needs_drop(tcx, t) { @@ -89,7 +89,7 @@ pub fn get_drop_glue_type<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, } match t.sty { ty::ty_uniq(typ) if !type_needs_drop(tcx, typ) - && ty::type_is_sized(tcx, typ) => { + && type_is_sized(tcx, typ) => { let llty = sizing_type_of(ccx, typ); // `Box` does not allocate. if llsize_of_alloc(ccx, llty) == 0 { @@ -150,7 +150,7 @@ pub fn get_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Val _ => { } } - let llty = if ty::type_is_sized(ccx.tcx(), t) { + let llty = if type_is_sized(ccx.tcx(), t) { type_of(ccx, t).ptr_to() } else { type_of(ccx, ty::mk_uniq(ccx.tcx(), t)).ptr_to() @@ -193,7 +193,7 @@ fn trans_struct_drop_flag<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, substs: &subst::Substs<'tcx>) -> Block<'blk, 'tcx> { let repr = adt::represent_type(bcx.ccx(), t); - let struct_data = if ty::type_is_sized(bcx.tcx(), t) { + let struct_data = if type_is_sized(bcx.tcx(), t) { v0 } else { let llval = GEPi(bcx, v0, &[0, abi::FAT_PTR_ADDR]); @@ -234,7 +234,7 @@ fn trans_struct_drop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, bcx.ty_to_string(fty))[]) }; - let (struct_data, info) = if ty::type_is_sized(bcx.tcx(), t) { + let (struct_data, info) = if type_is_sized(bcx.tcx(), t) { (v0, None) } else { let data = GEPi(bcx, v0, &[0, abi::FAT_PTR_ADDR]); @@ -251,7 +251,7 @@ fn trans_struct_drop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // Class dtors have no explicit args, so the params should // just consist of the environment (self). assert_eq!(params.len(), 1); - let self_arg = if ty::type_is_fat_ptr(bcx.tcx(), self_ty) { + let self_arg = if type_is_fat_ptr(bcx.tcx(), self_ty) { // The dtor expects a fat pointer, so make one, even if we have to fake it. let boxed_ty = ty::mk_open(bcx.tcx(), t); let scratch = datum::rvalue_scratch_datum(bcx, boxed_ty, "__fat_ptr_drop_self"); @@ -275,7 +275,7 @@ fn trans_struct_drop<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, for (i, ty) in st.fields.iter().enumerate().rev() { let llfld_a = adt::struct_field_ptr(variant_cx, &*st, value, i, false); - let val = if ty::type_is_sized(bcx.tcx(), *ty) { + let val = if type_is_sized(bcx.tcx(), *ty) { llfld_a } else { let boxed_ty = ty::mk_open(bcx.tcx(), *ty); @@ -303,7 +303,7 @@ fn size_and_align_of_dst<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, t: Ty<'tcx>, info: -> (ValueRef, ValueRef) { debug!("calculate size of DST: {}; with lost info: {}", bcx.ty_to_string(t), bcx.val_to_string(info)); - if ty::type_is_sized(bcx.tcx(), t) { + if type_is_sized(bcx.tcx(), t) { let sizing_type = sizing_type_of(bcx.ccx(), t); let size = C_uint(bcx.ccx(), llsize_of_alloc(bcx.ccx(), sizing_type)); let align = C_uint(bcx.ccx(), align_of(bcx.ccx(), t)); @@ -383,7 +383,7 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, t: Ty<'tcx>) bcx }) } - ty::ty_struct(..) if !ty::type_is_sized(bcx.tcx(), content_ty) => { + ty::ty_struct(..) if !type_is_sized(bcx.tcx(), content_ty) => { let llval = GEPi(bcx, v0, &[0, abi::FAT_PTR_ADDR]); let llbox = Load(bcx, llval); let not_null = IsNotNull(bcx, llbox); @@ -396,7 +396,7 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, t: Ty<'tcx>) }) } _ => { - assert!(ty::type_is_sized(bcx.tcx(), content_ty)); + assert!(type_is_sized(bcx.tcx(), content_ty)); let llval = v0; let llbox = Load(bcx, llval); let not_null = IsNotNull(bcx, llbox); @@ -415,7 +415,7 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, t: Ty<'tcx>) // find the drop flag (which is at the end of the struct). // Lets just ignore the flag and pretend everything will be // OK. - if ty::type_is_sized(bcx.tcx(), t) { + if type_is_sized(bcx.tcx(), t) { trans_struct_drop_flag(bcx, t, v0, dtor, did, substs) } else { // Give the user a heads up that we are doing something @@ -468,7 +468,7 @@ fn make_drop_glue<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v0: ValueRef, t: Ty<'tcx>) } ty::ty_vec(ty, None) => tvec::make_drop_glue_unboxed(bcx, v0, ty, false), _ => { - assert!(ty::type_is_sized(bcx.tcx(), t)); + assert!(type_is_sized(bcx.tcx(), t)); if type_needs_drop(bcx.tcx(), t) && ty::type_is_structural(t) { iter_structural_ty(bcx, v0, t, |bb, vv, tt| drop_ty(bb, vv, tt, None)) diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs index f471a92f6c321..6b0baa5d05e29 100644 --- a/src/librustc_trans/trans/intrinsic.rs +++ b/src/librustc_trans/trans/intrinsic.rs @@ -120,8 +120,8 @@ pub fn check_intrinsics(ccx: &CrateContext) { "s" })[]); } - if ty::type_is_fat_ptr(ccx.tcx(), transmute_restriction.to) || - ty::type_is_fat_ptr(ccx.tcx(), transmute_restriction.from) { + if type_is_fat_ptr(ccx.tcx(), transmute_restriction.to) || + type_is_fat_ptr(ccx.tcx(), transmute_restriction.from) { ccx.sess() .add_lint(::lint::builtin::FAT_PTR_TRANSMUTES, transmute_restriction.id, diff --git a/src/librustc_trans/trans/type_of.rs b/src/librustc_trans/trans/type_of.rs index 499195b51b9ce..aaec82bb1771d 100644 --- a/src/librustc_trans/trans/type_of.rs +++ b/src/librustc_trans/trans/type_of.rs @@ -187,7 +187,7 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ } let llsizingty = match t.sty { - _ if !ty::lltype_is_sized(cx.tcx(), t) => { + _ if !lltype_is_sized(cx.tcx(), t) => { cx.sess().bug(format!("trying to take the sizing type of {}, an unsized type", ppaux::ty_to_string(cx.tcx(), t))[]) } @@ -199,7 +199,7 @@ pub fn sizing_type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Typ ty::ty_float(t) => Type::float_from_ty(cx, t), ty::ty_uniq(ty) | ty::ty_rptr(_, ty::mt{ty, ..}) | ty::ty_ptr(ty::mt{ty, ..}) => { - if ty::type_is_sized(cx.tcx(), ty) { + if type_is_sized(cx.tcx(), ty) { Type::i8p(cx) } else { Type::struct_(cx, &[Type::i8p(cx), Type::i8p(cx)], false) @@ -267,11 +267,11 @@ pub fn type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type { // struct which might be unsized, but is monomorphised to a sized type. // In this case we'll fake a fat pointer with no unsize info (we use 0). // However, its still a fat pointer, so we need some type use. - if ty::type_is_sized(cx.tcx(), t) { + if type_is_sized(cx.tcx(), t) { return Type::i8p(cx); } - match ty::unsized_part_of_type(cx.tcx(), t).sty { + match unsized_part_of_type(cx.tcx(), t).sty { ty::ty_str | ty::ty_vec(..) => Type::uint_from_ty(cx, ast::TyU), ty::ty_trait(_) => Type::vtable_ptr(cx), _ => panic!("Unexpected type returned from unsized_part_of_type : {}", @@ -342,7 +342,7 @@ pub fn type_of<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> Type { cx.tn().find_type("str_slice").unwrap() } ty::ty_trait(..) => Type::opaque_trait(cx), - _ if !ty::type_is_sized(cx.tcx(), ty) => { + _ if !type_is_sized(cx.tcx(), ty) => { let p_ty = type_of(cx, ty).ptr_to(); Type::struct_(cx, &[p_ty, type_of_unsize_info(cx, ty)], false) } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 535a47008541c..8f8788726e5ce 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1356,7 +1356,7 @@ fn check_cast(fcx: &FnCtxt, return } - if !ty::type_is_sized(fcx.tcx(), t_1) { + if !fcx.type_is_known_to_be_sized(t_1) { let tstr = fcx.infcx().ty_to_string(t_1); fcx.type_error_message(span, |actual| { format!("cast to unsized type: `{}` as `{}`", actual, tstr) @@ -1545,10 +1545,14 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn tcx(&self) -> &ty::ctxt<'tcx> { self.ccx.tcx } - pub fn infcx<'b>(&'b self) -> &'b infer::InferCtxt<'a, 'tcx> { + pub fn infcx(&self) -> &infer::InferCtxt<'a, 'tcx> { &self.inh.infcx } + pub fn param_env(&self) -> &ty::ParameterEnvironment<'tcx> { + &self.inh.param_env + } + pub fn sess(&self) -> &Session { &self.tcx().sess } @@ -1792,6 +1796,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.require_type_is_sized(self.expr_ty(expr), expr.span, code); } + pub fn type_is_known_to_be_sized(&self, + ty: Ty<'tcx>) + -> bool + { + traits::type_known_to_meet_builtin_bound(self.infcx(), + self.param_env(), + ty, + ty::BoundSized) + } + pub fn register_builtin_bound(&self, ty: Ty<'tcx>, builtin_bound: ty::BuiltinBound, From 250225dbecbcebb241f0aacd85c0cac1b66360ed Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Mon, 22 Dec 2014 20:57:14 -0500 Subject: [PATCH 4/4] Rewrite the intrinsicck to take the parameter environment into account. Also fixes #20116. --- src/librustc/middle/intrinsicck.rs | 249 ++++++++++++++---- src/librustc/middle/subst.rs | 1 + src/librustc/middle/ty.rs | 107 +++----- src/librustc_trans/trans/intrinsic.rs | 79 +++--- .../compile-fail/transmute-different-sizes.rs | 2 +- .../compile-fail/transmute-fat-pointers.rs | 41 +++ src/test/compile-fail/transmute-impl.rs | 33 +++ 7 files changed, 352 insertions(+), 160 deletions(-) create mode 100644 src/test/compile-fail/transmute-fat-pointers.rs create mode 100644 src/test/compile-fail/transmute-impl.rs diff --git a/src/librustc/middle/intrinsicck.rs b/src/librustc/middle/intrinsicck.rs index 6acbc98b4b27b..1f4ddd1a5d641 100644 --- a/src/librustc/middle/intrinsicck.rs +++ b/src/librustc/middle/intrinsicck.rs @@ -10,9 +10,10 @@ use metadata::csearch; use middle::def::DefFn; -use middle::subst::Subst; +use middle::subst::{Subst, Substs, EnumeratedItems}; use middle::ty::{TransmuteRestriction, ctxt, ty_bare_fn}; use middle::ty::{mod, Ty}; +use util::ppaux::Repr; use syntax::abi::RustIntrinsic; use syntax::ast::DefId; @@ -23,52 +24,31 @@ use syntax::parse::token; use syntax::visit::Visitor; use syntax::visit; -fn type_size_is_affected_by_type_parameters<'tcx>(tcx: &ty::ctxt<'tcx>, typ: Ty<'tcx>) - -> bool { - let mut result = false; - ty::maybe_walk_ty(typ, |typ| { - match typ.sty { - 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 +pub fn check_crate(tcx: &ctxt) { + let mut visitor = IntrinsicCheckingVisitor { + tcx: tcx, + param_envs: Vec::new(), + dummy_sized_ty: ty::mk_int(), + dummy_unsized_ty: ty::mk_vec(tcx, ty::mk_int(), None), + }; + visit::walk_crate(&mut visitor, tcx.map.krate()); } struct IntrinsicCheckingVisitor<'a, 'tcx: 'a> { tcx: &'a ctxt<'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>, + + // 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>, } impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> { @@ -97,26 +77,175 @@ impl<'a, 'tcx> IntrinsicCheckingVisitor<'a, 'tcx> { } fn check_transmute(&self, span: Span, from: Ty<'tcx>, to: Ty<'tcx>, id: ast::NodeId) { - if type_size_is_affected_by_type_parameters(self.tcx, from) { + // Find the parameter environment for the most recent function that + // we entered. + + let param_env = match self.param_envs.last() { + Some(p) => p, + None => { + self.tcx.sess.span_bug( + span, + "transmute encountered outside of any fn"); + } + }; + + // Simple case: no type parameters involved. + if + !ty::type_has_params(from) && !ty::type_has_self(from) && + !ty::type_has_params(to) && !ty::type_has_self(to) + { + 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 `Sized? T` 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=int, U=int + // } + // + // fn bar() { + // // T=int, U=int + // // T=[int], U=int + // } + // + // fn baz() { + // // 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 = ty::type_contents(self.tcx, from); + let to_tc = ty::type_contents(self.tcx, to); + if from_tc.interior_param() || to_tc.interior_param() { span_err!(self.tcx.sess, span, E0139, - "cannot transmute from a type that contains type parameters"); + "cannot transmute to or from a type that contains \ + type parameters in its interior"); + return; } - if type_size_is_affected_by_type_parameters(self.tcx, to) { - span_err!(self.tcx.sess, span, E0140, - "cannot transmute to a type that contains type parameters"); + + let mut substs = param_env.free_substs.clone(); + self.with_each_combination( + 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, + param_env: &ty::ParameterEnvironment<'tcx>, + mut types_in_scope: EnumeratedItems>, + 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.repr(self.tcx)); + + callback.call_mut((substs,)); + } + + Some((space, index, ¶m_ty)) => { + debug!("with_each_combination: space={}, index={}, param_ty={}", + space, index, param_ty.repr(self.tcx)); + + if !ty::type_is_sized(self.tcx, param_ty, param_env) { + debug!("with_each_combination: param_ty is not known to be sized"); + + substs.types.get_mut_slice(space)[index] = self.dummy_unsized_ty; + self.with_each_combination(param_env, types_in_scope.clone(), substs, callback); + } + + substs.types.get_mut_slice(space)[index] = self.dummy_sized_ty; + self.with_each_combination(param_env, types_in_scope, substs, callback); + } } + } - let restriction = TransmuteRestriction { - span: span, - from: from, - to: to, - id: id, - }; + fn push_transmute_restriction(&self, restriction: TransmuteRestriction<'tcx>) { + debug!("Pushing transmute restriction: {}", restriction.repr(self.tcx)); self.tcx.transmute_restrictions.borrow_mut().push(restriction); } } impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> { + fn visit_fn(&mut self, fk: visit::FnKind<'v>, fd: &'v ast::FnDecl, + b: &'v ast::Block, s: Span, id: ast::NodeId) { + match fk { + visit::FkItemFn(..) | visit::FkMethod(..) => { + let param_env = ty::ParameterEnvironment::for_item(self.tcx, id); + self.param_envs.push(param_env); + visit::walk_fn(self, fk, fd, b, s); + self.param_envs.pop(); + } + visit::FkFnBlock(..) => { + visit::walk_fn(self, fk, fd, b, s); + } + } + + } + fn visit_expr(&mut self, expr: &ast::Expr) { if let ast::ExprPath(..) = expr.node { match ty::resolve_expr(self.tcx, expr) { @@ -144,7 +273,13 @@ impl<'a, 'tcx, 'v> Visitor<'v> for IntrinsicCheckingVisitor<'a, 'tcx> { } } -pub fn check_crate(tcx: &ctxt) { - visit::walk_crate(&mut IntrinsicCheckingVisitor { tcx: tcx }, - tcx.map.krate()); +impl<'tcx> Repr<'tcx> for TransmuteRestriction<'tcx> { + fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String { + format!("TransmuteRestriction(id={}, original=({},{}), substituted=({},{}))", + self.id, + self.original_from.repr(tcx), + self.original_to.repr(tcx), + self.substituted_from.repr(tcx), + self.substituted_to.repr(tcx)) + } } diff --git a/src/librustc/middle/subst.rs b/src/librustc/middle/subst.rs index 6ae639e0313e0..40f887cae9841 100644 --- a/src/librustc/middle/subst.rs +++ b/src/librustc/middle/subst.rs @@ -487,6 +487,7 @@ impl VecPerParamSpace { } } +#[deriving(Clone)] pub struct EnumeratedItems<'a,T:'a> { vec: &'a VecPerParamSpace, space_index: uint, diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index c78bc47408c0d..35903818f044b 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -62,7 +62,7 @@ use middle::ty_fold::{mod, TypeFoldable, TypeFolder}; use util::ppaux::{note_and_explain_region, bound_region_ptr_to_string}; use util::ppaux::{trait_store_to_string, ty_to_string}; use util::ppaux::{Repr, UserString}; -use util::common::{indenter, memoized, ErrorReported}; +use util::common::{memoized, ErrorReported}; use util::nodemap::{NodeMap, NodeSet, DefIdMap, DefIdSet}; use util::nodemap::{FnvHashMap}; @@ -590,16 +590,33 @@ pub enum vtable_origin<'tcx> { pub type ObjectCastMap<'tcx> = RefCell>>>; /// A restriction that certain types must be the same size. The use of -/// `transmute` gives rise to these restrictions. +/// `transmute` gives rise to these restrictions. These generally +/// cannot be checked until trans; therefore, each call to `transmute` +/// will push one or more such restriction into the +/// `transmute_restrictions` vector during `intrinsicck`. They are +/// then checked during `trans` by the fn `check_intrinsics`. #[deriving(Copy)] pub struct TransmuteRestriction<'tcx> { - /// The span from whence the restriction comes. + /// The span whence the restriction comes. pub span: Span, + /// The type being transmuted from. - pub from: Ty<'tcx>, + pub original_from: Ty<'tcx>, + /// The type being transmuted to. - pub to: Ty<'tcx>, - /// NodeIf of the transmute intrinsic. + pub original_to: Ty<'tcx>, + + /// The type being transmuted from, with all type parameters + /// substituted for an arbitrary representative. Not to be shown + /// to the end user. + pub substituted_from: Ty<'tcx>, + + /// The type being transmuted to, with all type parameters + /// substituted for an arbitrary representative. Not to be shown + /// to the end user. + pub substituted_to: Ty<'tcx>, + + /// NodeId of the transmute intrinsic. pub id: ast::NodeId, } @@ -2722,6 +2739,7 @@ def_type_content_sets! { // Things that are interior to the value (first nibble): InteriorUnsized = 0b0000_0000__0000_0000__0001, InteriorUnsafe = 0b0000_0000__0000_0000__0010, + InteriorParam = 0b0000_0000__0000_0000__0100, // InteriorAll = 0b00000000__00000000__1111, // Things that are owned by the value (second and third nibbles): @@ -2776,6 +2794,10 @@ impl TypeContents { !self.intersects(TC::Nonsized) } + pub fn interior_param(&self) -> bool { + self.intersects(TC::InteriorParam) + } + pub fn interior_unsafe(&self) -> bool { self.intersects(TC::InteriorUnsafe) } @@ -2904,7 +2926,7 @@ pub fn type_contents<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> TypeContents { } ty_closure(ref c) => { - closure_contents(cx, &**c) | TC::ReachesFfiUnsafe + closure_contents(&**c) | TC::ReachesFfiUnsafe } ty_uniq(typ) => { @@ -2915,7 +2937,7 @@ pub fn type_contents<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> TypeContents { } ty_trait(box TyTrait { bounds, .. }) => { - object_contents(cx, bounds) | TC::ReachesFfiUnsafe | TC::Nonsized + object_contents(bounds) | TC::ReachesFfiUnsafe | TC::Nonsized } ty_ptr(ref mt) => { @@ -3024,26 +3046,7 @@ pub fn type_contents<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> TypeContents { apply_lang_items(cx, did, res) } - ty_param(p) => { - // We only ever ask for the kind of types that are defined in - // the current crate; therefore, the only type parameters that - // could be in scope are those defined in the current crate. - // If this assertion fails, it is likely because of a - // failure of the cross-crate inlining code to translate a - // def-id. - assert_eq!(p.def_id.krate, ast::LOCAL_CRATE); - - let ty_param_defs = cx.ty_param_defs.borrow(); - let tp_def = &(*ty_param_defs)[p.def_id.node]; - kind_bounds_to_contents( - cx, - tp_def.bounds.builtin_bounds, - tp_def.bounds.trait_bounds[]) - } - - ty_infer(_) => { - // This occurs during coherence, but shouldn't occur at other - // times. + ty_param(_) => { TC::All } @@ -3053,6 +3056,7 @@ pub fn type_contents<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> TypeContents { result.unsafe_pointer() | TC::Nonsized } + ty_infer(_) | ty_err => { cx.sess.bug("asked to compute contents of error type"); } @@ -3092,10 +3096,10 @@ pub fn type_contents<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> TypeContents { b | (TC::ReachesBorrowed).when(region != ty::ReStatic) } - fn closure_contents(cx: &ctxt, cty: &ClosureTy) -> TypeContents { + fn closure_contents(cty: &ClosureTy) -> TypeContents { // Closure contents are just like trait contents, but with potentially // even more stuff. - let st = object_contents(cx, cty.bounds); + let st = object_contents(cty.bounds); let st = match cty.store { UniqTraitStore => { @@ -3109,47 +3113,18 @@ pub fn type_contents<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>) -> TypeContents { st } - fn object_contents(cx: &ctxt, - bounds: ExistentialBounds) - -> TypeContents { - // These are the type contents of the (opaque) interior - kind_bounds_to_contents(cx, bounds.builtin_bounds, &[]) - } - - fn kind_bounds_to_contents<'tcx>(cx: &ctxt<'tcx>, - bounds: BuiltinBounds, - traits: &[Rc>]) - -> TypeContents { - let _i = indenter(); - let mut tc = TC::All; - each_inherited_builtin_bound(cx, bounds, traits, |bound| { + fn object_contents(bounds: ExistentialBounds) -> TypeContents { + // These are the type contents of the (opaque) interior. We + // make no assumptions (other than that it cannot have an + // in-scope type parameter within, which makes no sense). + let mut tc = TC::All - TC::InteriorParam; + for bound in bounds.builtin_bounds.iter() { tc = tc - match bound { BoundSync | BoundSend | BoundCopy => TC::None, BoundSized => TC::Nonsized, }; - }); - return tc; - - // Iterates over all builtin bounds on the type parameter def, including - // those inherited from traits with builtin-kind-supertraits. - fn each_inherited_builtin_bound<'tcx, F>(cx: &ctxt<'tcx>, - bounds: BuiltinBounds, - traits: &[Rc>], - mut f: F) where - F: FnMut(BuiltinBound), - { - for bound in bounds.iter() { - f(bound); - } - - each_bound_trait_and_supertraits(cx, traits, |trait_ref| { - let trait_def = lookup_trait_def(cx, trait_ref.def_id()); - for bound in trait_def.bounds.builtin_bounds.iter() { - f(bound); - } - true - }); } + return tc; } } diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs index 6b0baa5d05e29..d49018e00c1cb 100644 --- a/src/librustc_trans/trans/intrinsic.rs +++ b/src/librustc_trans/trans/intrinsic.rs @@ -32,7 +32,7 @@ use middle::ty::{mod, Ty}; use syntax::abi::RustIntrinsic; use syntax::ast; use syntax::parse::token; -use util::ppaux::ty_to_string; +use util::ppaux::{Repr, ty_to_string}; pub fn get_simple_intrinsic(ccx: &CrateContext, item: &ast::ForeignItem) -> Option { let name = match token::get_ident(item.ident).get() { @@ -90,46 +90,53 @@ pub fn get_simple_intrinsic(ccx: &CrateContext, item: &ast::ForeignItem) -> Opti /// Performs late verification that intrinsics are used correctly. At present, /// the only intrinsic that needs such verification is `transmute`. pub fn check_intrinsics(ccx: &CrateContext) { - for transmute_restriction in ccx.tcx() - .transmute_restrictions - .borrow() - .iter() { + let mut last_failing_id = None; + for transmute_restriction in ccx.tcx().transmute_restrictions.borrow().iter() { + // Sometimes, a single call to transmute will push multiple + // type pairs to test in order to exhaustively test the + // possibility around a type parameter. If one of those fails, + // there is no sense reporting errors on the others. + if last_failing_id == Some(transmute_restriction.id) { + continue; + } + + debug!("transmute_restriction: {}", transmute_restriction.repr(ccx.tcx())); + + assert!(!ty::type_has_params(transmute_restriction.substituted_from)); + assert!(!ty::type_has_params(transmute_restriction.substituted_to)); + let llfromtype = type_of::sizing_type_of(ccx, - transmute_restriction.from); + transmute_restriction.substituted_from); let lltotype = type_of::sizing_type_of(ccx, - transmute_restriction.to); + transmute_restriction.substituted_to); let from_type_size = machine::llbitsize_of_real(ccx, llfromtype); let to_type_size = machine::llbitsize_of_real(ccx, lltotype); if from_type_size != to_type_size { - ccx.sess() - .span_err(transmute_restriction.span, - format!("transmute called on types with different sizes: \ - {} ({} bit{}) to {} ({} bit{})", - ty_to_string(ccx.tcx(), transmute_restriction.from), - from_type_size as uint, - if from_type_size == 1 { - "" - } else { - "s" - }, - ty_to_string(ccx.tcx(), transmute_restriction.to), - to_type_size as uint, - if to_type_size == 1 { - "" - } else { - "s" - })[]); - } - if type_is_fat_ptr(ccx.tcx(), transmute_restriction.to) || - type_is_fat_ptr(ccx.tcx(), transmute_restriction.from) { - ccx.sess() - .add_lint(::lint::builtin::FAT_PTR_TRANSMUTES, - transmute_restriction.id, - transmute_restriction.span, - format!("Transmuting fat pointer types; {} to {}.\ - Beware of relying on the compiler's representation", - ty_to_string(ccx.tcx(), transmute_restriction.from), - ty_to_string(ccx.tcx(), transmute_restriction.to))); + last_failing_id = Some(transmute_restriction.id); + + if transmute_restriction.original_from != transmute_restriction.substituted_from { + ccx.sess().span_err( + transmute_restriction.span, + format!("transmute called on types with potentially different sizes: \ + {} (could be {} bit{}) to {} (could be {} bit{})", + ty_to_string(ccx.tcx(), transmute_restriction.original_from), + from_type_size as uint, + if from_type_size == 1 {""} else {"s"}, + ty_to_string(ccx.tcx(), transmute_restriction.original_to), + to_type_size as uint, + if to_type_size == 1 {""} else {"s"}).as_slice()); + } else { + ccx.sess().span_err( + transmute_restriction.span, + format!("transmute called on types with different sizes: \ + {} ({} bit{}) to {} ({} bit{})", + ty_to_string(ccx.tcx(), transmute_restriction.original_from), + from_type_size as uint, + if from_type_size == 1 {""} else {"s"}, + ty_to_string(ccx.tcx(), transmute_restriction.original_to), + to_type_size as uint, + if to_type_size == 1 {""} else {"s"}).as_slice()); + } } } ccx.sess().abort_if_errors(); diff --git a/src/test/compile-fail/transmute-different-sizes.rs b/src/test/compile-fail/transmute-different-sizes.rs index abdfe983e3a8f..5c61212a7f5a5 100644 --- a/src/test/compile-fail/transmute-different-sizes.rs +++ b/src/test/compile-fail/transmute-different-sizes.rs @@ -21,7 +21,7 @@ unsafe fn f() { unsafe fn g(x: &T) { let _: i8 = transmute(x); - //~^ ERROR transmute called on types with different sizes + //~^ ERROR transmute called on types with potentially different sizes } fn main() {} diff --git a/src/test/compile-fail/transmute-fat-pointers.rs b/src/test/compile-fail/transmute-fat-pointers.rs new file mode 100644 index 0000000000000..5e81a4cec2284 --- /dev/null +++ b/src/test/compile-fail/transmute-fat-pointers.rs @@ -0,0 +1,41 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests that are conservative around thin/fat pointer mismatches. + +#![allow(dead_code)] + +use std::mem::transmute; + +fn a(x: &[T]) -> &U { + unsafe { transmute(x) } //~ ERROR transmute called on types with potentially different sizes +} + +fn b(x: &T) -> &U { + unsafe { transmute(x) } //~ ERROR transmute called on types with potentially different sizes +} + +fn c(x: &T) -> &U { + unsafe { transmute(x) } +} + +fn d(x: &[T]) -> &[U] { + unsafe { transmute(x) } +} + +fn e(x: &T) -> &U { + unsafe { transmute(x) } //~ ERROR transmute called on types with potentially different sizes +} + +fn f(x: &T) -> &U { + unsafe { transmute(x) } //~ ERROR transmute called on types with potentially different sizes +} + +fn main() { } diff --git a/src/test/compile-fail/transmute-impl.rs b/src/test/compile-fail/transmute-impl.rs new file mode 100644 index 0000000000000..8b5a8c679b245 --- /dev/null +++ b/src/test/compile-fail/transmute-impl.rs @@ -0,0 +1,33 @@ +// Copyright 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Tests that are conservative around thin/fat pointer mismatches. + +#![allow(dead_code)] + +use std::mem::transmute; + +struct Foo { + t: Box +} + +impl Foo { + fn m(x: &T) -> &int where T : Sized { + // OK here, because T : Sized is in scope. + unsafe { transmute(x) } + } + + fn n(x: &T) -> &int { + // Not OK here, because T : Sized is not in scope. + unsafe { transmute(x) } //~ ERROR transmute called on types with potentially different sizes + } +} + +fn main() { }