From 83acebc46213e30eac3e9e71b213440104914f4c Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Tue, 5 May 2015 19:36:47 +0300 Subject: [PATCH] Overhaul cast semantics and make them follow RFC401 This should hopefully fix all cast-related ICEs once and for all. I managed to make diagnostics hate me and give me spurious "decoder error" - removing $build/tmp/extended-errors seems to fix it. --- src/librustc/diagnostics.rs | 1 - src/librustc/lib.rs | 1 + src/librustc/metadata/common.rs | 1 + src/librustc/middle/astencode.rs | 25 ++ src/librustc/middle/cast.rs | 71 ++++ src/librustc/middle/check_const.rs | 35 +- src/librustc/middle/const_eval.rs | 2 +- src/librustc/middle/ty.rs | 24 +- src/librustc_trans/trans/consts.rs | 70 ++-- src/librustc_trans/trans/expr.rs | 252 ++++++-------- src/librustc_typeck/check/cast.rs | 459 ++++++++++++++++++-------- src/librustc_typeck/check/mod.rs | 13 +- src/librustc_typeck/diagnostics.rs | 8 +- src/librustc_typeck/lib.rs | 2 +- src/test/compile-fail/fat-ptr-cast.rs | 16 +- src/test/compile-fail/issue-22289.rs | 2 +- 16 files changed, 614 insertions(+), 368 deletions(-) create mode 100644 src/librustc/middle/cast.rs diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index e9f4860f451..f5cb1bd25d6 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -801,7 +801,6 @@ struct Foo { register_diagnostics! { E0011, - E0012, E0014, E0016, E0017, diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 2936b6b1c2b..993d0dcf115 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -94,6 +94,7 @@ pub mod back { pub mod middle { pub mod astconv_util; pub mod astencode; + pub mod cast; pub mod cfg; pub mod check_const; pub mod check_static_recursion; diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index f410626714f..478c0f2f564 100644 --- a/src/librustc/metadata/common.rs +++ b/src/librustc/metadata/common.rs @@ -148,6 +148,7 @@ enum_from_u32! { tag_table_capture_modes = 0x67, tag_table_object_cast_map = 0x68, tag_table_const_qualif = 0x69, + tag_table_cast_kinds = 0x6a, } } diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs index 328972c54e3..e325c03d52d 100644 --- a/src/librustc/middle/astencode.rs +++ b/src/librustc/middle/astencode.rs @@ -23,6 +23,7 @@ use metadata::tydecode; use metadata::tydecode::{DefIdSource, NominalType, TypeWithId, TypeParameter}; use metadata::tydecode::{RegionParameter, ClosureSource}; use metadata::tyencode; +use middle::cast; use middle::check_const::ConstQualif; use middle::mem_categorization::Typer; use middle::privacy::{AllPublic, LastMod}; @@ -688,6 +689,10 @@ pub fn encode_closure_kind(ebml_w: &mut Encoder, kind: ty::ClosureKind) { kind.encode(ebml_w).unwrap(); } +pub fn encode_cast_kind(ebml_w: &mut Encoder, kind: cast::CastKind) { + kind.encode(ebml_w).unwrap(); +} + pub trait vtable_decoder_helpers<'tcx> { fn read_vec_per_param_space(&mut self, f: F) -> VecPerParamSpace where F: FnMut(&mut Self) -> T; @@ -1248,6 +1253,13 @@ fn encode_side_tables_for_id(ecx: &e::EncodeContext, }) } + if let Some(cast_kind) = tcx.cast_kinds.borrow().get(&id) { + rbml_w.tag(c::tag_table_cast_kinds, |rbml_w| { + rbml_w.id(id); + encode_cast_kind(rbml_w, *cast_kind) + }) + } + for &qualif in tcx.const_qualif_map.borrow().get(&id).iter() { rbml_w.tag(c::tag_table_const_qualif, |rbml_w| { rbml_w.id(id); @@ -1289,6 +1301,8 @@ trait rbml_decoder_decoder_helpers<'tcx> { -> subst::Substs<'tcx>; fn read_auto_adjustment<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>) -> ty::AutoAdjustment<'tcx>; + fn read_cast_kind<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>) + -> cast::CastKind; fn read_closure_kind<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>) -> ty::ClosureKind; fn read_closure_ty<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>) @@ -1641,6 +1655,12 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> { }).unwrap() } + fn read_cast_kind<'b, 'c>(&mut self, _dcx: &DecodeContext<'b, 'c, 'tcx>) + -> cast::CastKind + { + Decodable::decode(self).unwrap() + } + fn read_closure_kind<'b, 'c>(&mut self, _dcx: &DecodeContext<'b, 'c, 'tcx>) -> ty::ClosureKind { @@ -1801,6 +1821,11 @@ fn decode_side_tables(dcx: &DecodeContext, dcx.tcx.closure_kinds.borrow_mut().insert(ast_util::local_def(id), closure_kind); } + c::tag_table_cast_kinds => { + let cast_kind = + val_dsr.read_cast_kind(dcx); + dcx.tcx.cast_kinds.borrow_mut().insert(id, cast_kind); + } c::tag_table_const_qualif => { let qualif: ConstQualif = Decodable::decode(val_dsr).unwrap(); dcx.tcx.const_qualif_map.borrow_mut().insert(id, qualif); diff --git a/src/librustc/middle/cast.rs b/src/librustc/middle/cast.rs new file mode 100644 index 00000000000..4b876cfd80f --- /dev/null +++ b/src/librustc/middle/cast.rs @@ -0,0 +1,71 @@ +// Copyright 2015 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. + +// Helpers for handling cast expressions, used in both +// typeck and trans. + +use middle::ty::{self, Ty}; + +use syntax::ast; + + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum IntTy { + U(ast::UintTy), + I, + CEnum, + Bool, + Char +} + +// Valid types for the result of a non-coercion cast +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum CastTy<'tcx> { + Int(IntTy), + Float, + FPtr, + Ptr(&'tcx ty::mt<'tcx>), + RPtr(&'tcx ty::mt<'tcx>), +} + +/// Cast Kind. See RFC 401 (or librustc_typeck/check/cast.rs) +#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)] +pub enum CastKind { + CoercionCast, + PtrPtrCast, + PtrAddrCast, + AddrPtrCast, + NumericCast, + EnumCast, + PrimIntCast, + U8CharCast, + ArrayPtrCast, + FPtrPtrCast, + FPtrAddrCast +} + +impl<'tcx> CastTy<'tcx> { + pub fn recognize(tcx: &ty::ctxt<'tcx>, t: Ty<'tcx>) + -> Option> { + match t.sty { + ty::ty_bool => Some(CastTy::Int(IntTy::Bool)), + ty::ty_char => Some(CastTy::Int(IntTy::Char)), + ty::ty_int(_) => Some(CastTy::Int(IntTy::I)), + ty::ty_uint(u) => Some(CastTy::Int(IntTy::U(u))), + ty::ty_float(_) => Some(CastTy::Float), + ty::ty_enum(..) if ty::type_is_c_like_enum( + tcx, t) => Some(CastTy::Int(IntTy::CEnum)), + ty::ty_ptr(ref mt) => Some(CastTy::Ptr(mt)), + ty::ty_rptr(_, ref mt) => Some(CastTy::RPtr(mt)), + ty::ty_bare_fn(..) => Some(CastTy::FPtr), + _ => None, + } + } +} diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index 8bb83c54da8..6d2465a5cb9 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -24,6 +24,7 @@ // - It's not possible to take the address of a static item with unsafe interior. This is enforced // by borrowck::gather_loans +use middle::cast::{CastKind}; use middle::const_eval; use middle::def; use middle::expr_use_visitor as euv; @@ -32,11 +33,10 @@ use middle::mem_categorization as mc; use middle::traits; use middle::ty::{self, Ty}; use util::nodemap::NodeMap; -use util::ppaux; +use util::ppaux::Repr; use syntax::ast; use syntax::codemap::Span; -use syntax::print::pprust; use syntax::visit::{self, Visitor}; use std::collections::hash_map::Entry; @@ -197,7 +197,7 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> { impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { fn visit_item(&mut self, i: &ast::Item) { - debug!("visit_item(item={})", pprust::item_to_string(i)); + debug!("visit_item(item={})", i.repr(self.tcx)); match i.node { ast::ItemStatic(_, ast::MutImmutable, ref expr) => { self.check_static_type(&**expr); @@ -440,26 +440,17 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, } } ast::ExprCast(ref from, _) => { - let toty = ty::expr_ty(v.tcx, e); - let fromty = ty::expr_ty(v.tcx, &**from); - let is_legal_cast = - ty::type_is_numeric(toty) || - ty::type_is_unsafe_ptr(toty) || - (ty::type_is_bare_fn(toty) && ty::type_is_bare_fn_item(fromty)); - if !is_legal_cast { - v.add_qualif(ConstQualif::NOT_CONST); - if v.mode != Mode::Var { - span_err!(v.tcx.sess, e.span, E0012, - "can not cast to `{}` in {}s", - ppaux::ty_to_string(v.tcx, toty), v.msg()); - } - } - if ty::type_is_unsafe_ptr(fromty) && ty::type_is_numeric(toty) { - v.add_qualif(ConstQualif::NOT_CONST); - if v.mode != Mode::Var { - span_err!(v.tcx.sess, e.span, E0018, - "can not cast a pointer to an integer in {}s", v.msg()); + debug!("Checking const cast(id={})", from.id); + match v.tcx.cast_kinds.borrow().get(&from.id) { + None => v.tcx.sess.span_bug(e.span, "no kind for cast"), + Some(&CastKind::PtrAddrCast) | Some(&CastKind::FPtrAddrCast) => { + v.add_qualif(ConstQualif::NOT_CONST); + if v.mode != Mode::Var { + span_err!(v.tcx.sess, e.span, E0018, + "can not cast a pointer to an integer in {}s", v.msg()); + } } + _ => {} } } ast::ExprPath(..) => { diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 72415f54336..03de553e648 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -1002,7 +1002,7 @@ fn cast_const<'tcx>(tcx: &ty::ctxt<'tcx>, val: const_val, ty: Ty) -> CastResult macro_rules! convert_val { ($intermediate_ty:ty, $const_type:ident, $target_ty:ty) => { match val { - const_bool(b) => Ok($const_type(b as $intermediate_ty as $target_ty)), + const_bool(b) => Ok($const_type(b as u64 as $intermediate_ty as $target_ty)), const_uint(u) => Ok($const_type(u as $intermediate_ty as $target_ty)), const_int(i) => Ok($const_type(i as $intermediate_ty as $target_ty)), const_float(f) => Ok($const_type(f as $intermediate_ty as $target_ty)), diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index e988423ac57..15e1ac2f2c6 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -41,6 +41,7 @@ use session::Session; use lint; use metadata::csearch; use middle; +use middle::cast; use middle::check_const; use middle::const_eval; use middle::def::{self, DefMap, ExportMap}; @@ -288,15 +289,6 @@ pub struct field_ty { pub origin: ast::DefId, // The DefId of the struct in which the field is declared. } -// Contains information needed to resolve types and (in the future) look up -// the types of AST nodes. -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -pub struct creader_cache_key { - pub cnum: CrateNum, - pub pos: usize, - pub len: usize -} - #[derive(Clone, PartialEq, RustcDecodable, RustcEncodable)] pub struct ItemVariances { pub types: VecPerParamSpace, @@ -562,6 +554,15 @@ pub enum vtable_origin<'tcx> { // expr to the associated trait ref. pub type ObjectCastMap<'tcx> = RefCell>>; +// Contains information needed to resolve types and (in the future) look up +// the types of AST nodes. +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub struct creader_cache_key { + pub cnum: CrateNum, + pub pos: usize, + pub len: usize +} + /// A restriction that certain types must be the same size. The use of /// `transmute` gives rise to these restrictions. These generally /// cannot be checked until trans; therefore, each call to `transmute` @@ -827,6 +828,10 @@ pub struct ctxt<'tcx> { /// Caches CoerceUnsized kinds for impls on custom types. pub custom_coerce_unsized_kinds: RefCell>, + + /// Maps a cast expression to its kind. This is keyed on the + /// *from* expression of the cast, not the cast itself. + pub cast_kinds: RefCell>, } impl<'tcx> ctxt<'tcx> { @@ -2817,6 +2822,7 @@ pub fn mk_ctxt<'tcx>(s: Session, type_impls_sized_cache: RefCell::new(HashMap::new()), const_qualif_map: RefCell::new(NodeMap()), custom_coerce_unsized_kinds: RefCell::new(DefIdMap()), + cast_kinds: RefCell::new(NodeMap()), } } diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index 503bdf8dadb..29430ad10e6 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -29,6 +29,7 @@ use trans::declare; use trans::monomorphize; use trans::type_::Type; use trans::type_of; +use middle::cast::{CastTy,IntTy}; use middle::subst::Substs; use middle::ty::{self, Ty}; use util::ppaux::{Repr, ty_to_string}; @@ -616,53 +617,62 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, } } ast::ExprCast(ref base, _) => { - let llty = type_of::type_of(cx, ety); - let (v, basety) = const_expr(cx, &**base, param_substs); - if expr::cast_is_noop(basety, ety) { + let t_1 = ety; + let llty = type_of::type_of(cx, t_1); + let (v, t_e) = const_expr(cx, &**base, param_substs); + debug!("trans_const_cast({} as {})", t_e.repr(cx.tcx()), t_1.repr(cx.tcx())); + if expr::cast_is_noop(cx.tcx(), base, t_e, t_1) { return v; } - match (expr::cast_type_kind(cx.tcx(), basety), - expr::cast_type_kind(cx.tcx(), ety)) { - - (expr::cast_integral, expr::cast_integral) => { - let s = ty::type_is_signed(basety) as Bool; + if type_is_fat_ptr(cx.tcx(), t_e) { + // Fat pointer casts. + let t_1_inner = ty::deref(t_1, true).expect("cast to non-pointer").ty; + let ptr_ty = type_of::in_memory_type_of(cx, t_1_inner).ptr_to(); + let addr = ptrcast(const_get_elt(cx, v, &[abi::FAT_PTR_ADDR as u32]), + ptr_ty); + if type_is_fat_ptr(cx.tcx(), t_1) { + let info = const_get_elt(cx, v, &[abi::FAT_PTR_EXTRA as u32]); + return C_struct(cx, &[addr, info], false) + } else { + return addr; + } + } + match (CastTy::recognize(cx.tcx(), t_e).expect("bad input type for cast"), + CastTy::recognize(cx.tcx(), t_1).expect("bad output type for cast")) { + (CastTy::Int(IntTy::CEnum), CastTy::Int(_)) => { + let repr = adt::represent_type(cx, t_e); + let discr = adt::const_get_discrim(cx, &*repr, v); + let iv = C_integral(cx.int_type(), discr, false); + let s = adt::is_discr_signed(&*repr) as Bool; + llvm::LLVMConstIntCast(iv, llty.to_ref(), s) + } + (CastTy::Int(_), CastTy::Int(_)) => { + let s = ty::type_is_signed(t_e) as Bool; llvm::LLVMConstIntCast(v, llty.to_ref(), s) } - (expr::cast_integral, expr::cast_float) => { - if ty::type_is_signed(basety) { + (CastTy::Int(_), CastTy::Float) => { + if ty::type_is_signed(t_e) { llvm::LLVMConstSIToFP(v, llty.to_ref()) } else { llvm::LLVMConstUIToFP(v, llty.to_ref()) } } - (expr::cast_float, expr::cast_float) => { + (CastTy::Float, CastTy::Float) => { llvm::LLVMConstFPCast(v, llty.to_ref()) } - (expr::cast_float, expr::cast_integral) => { - if ty::type_is_signed(ety) { llvm::LLVMConstFPToSI(v, llty.to_ref()) } + (CastTy::Float, CastTy::Int(_)) => { + if ty::type_is_signed(t_1) { llvm::LLVMConstFPToSI(v, llty.to_ref()) } else { llvm::LLVMConstFPToUI(v, llty.to_ref()) } } - (expr::cast_enum, expr::cast_integral) => { - let repr = adt::represent_type(cx, basety); - let discr = adt::const_get_discrim(cx, &*repr, v); - let iv = C_integral(cx.int_type(), discr, false); - let ety_cast = expr::cast_type_kind(cx.tcx(), ety); - match ety_cast { - expr::cast_integral => { - let s = ty::type_is_signed(ety) as Bool; - llvm::LLVMConstIntCast(iv, llty.to_ref(), s) - } - _ => cx.sess().bug("enum cast destination is not \ - integral") - } - } - (expr::cast_pointer, expr::cast_pointer) => { + (CastTy::Ptr(_), CastTy::Ptr(_)) | (CastTy::FPtr, CastTy::Ptr(_)) + | (CastTy::RPtr(_), CastTy::Ptr(_)) => { ptrcast(v, llty) } - (expr::cast_integral, expr::cast_pointer) => { + (CastTy::FPtr, CastTy::FPtr) => ptrcast(v, llty), // isn't this a coercion? + (CastTy::Int(_), CastTy::Ptr(_)) => { llvm::LLVMConstIntToPtr(v, llty.to_ref()) } - (expr::cast_pointer, expr::cast_integral) => { + (CastTy::Ptr(_), CastTy::Int(_)) | (CastTy::FPtr, CastTy::Int(_)) => { llvm::LLVMConstPtrToInt(v, llty.to_ref()) } _ => { diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index 270aacfe143..a65d4225a1f 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -48,7 +48,6 @@ #![allow(non_camel_case_types)] -pub use self::cast_kind::*; pub use self::Dest::*; use self::lazy_binop_ty::*; @@ -73,6 +72,7 @@ use trans::meth; use trans::monomorphize; use trans::tvec; use trans::type_of; +use middle::cast::{CastKind, CastTy}; use middle::ty::{struct_fields, tup_fields}; use middle::ty::{AdjustDerefRef, AdjustReifyFnPointer, AdjustUnsafeFnPointer}; use middle::ty::{self, Ty}; @@ -1981,177 +1981,143 @@ fn trans_overloaded_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, bcx } -fn int_cast(bcx: Block, - lldsttype: Type, - llsrctype: Type, - llsrc: ValueRef, - signed: bool) - -> ValueRef { - let _icx = push_ctxt("int_cast"); - let srcsz = llsrctype.int_width(); - let dstsz = lldsttype.int_width(); - return if dstsz == srcsz { - BitCast(bcx, llsrc, lldsttype) - } else if srcsz > dstsz { - TruncOrBitCast(bcx, llsrc, lldsttype) - } else if signed { - SExtOrBitCast(bcx, llsrc, lldsttype) - } else { - ZExtOrBitCast(bcx, llsrc, lldsttype) +pub fn cast_is_noop<'tcx>(tcx: &ty::ctxt<'tcx>, + expr: &ast::Expr, + t_in: Ty<'tcx>, + t_out: Ty<'tcx>) + -> bool { + if let Some(&CastKind::CoercionCast) = tcx.cast_kinds.borrow().get(&expr.id) { + return true; } -} -fn float_cast(bcx: Block, - lldsttype: Type, - llsrctype: Type, - llsrc: ValueRef) - -> ValueRef { - let _icx = push_ctxt("float_cast"); - let srcsz = llsrctype.float_width(); - let dstsz = lldsttype.float_width(); - return if dstsz > srcsz { - FPExt(bcx, llsrc, lldsttype) - } else if srcsz > dstsz { - FPTrunc(bcx, llsrc, lldsttype) - } else { llsrc }; -} - -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum cast_kind { - cast_pointer, - cast_fat_ptr, - cast_integral, - cast_float, - cast_enum, - cast_other, -} - -pub fn cast_type_kind<'tcx>(tcx: &ty::ctxt<'tcx>, t: Ty<'tcx>) -> cast_kind { - match t.sty { - ty::ty_char => cast_integral, - ty::ty_float(..) => cast_float, - ty::ty_rptr(_, mt) | ty::ty_ptr(mt) => { - if type_is_sized(tcx, mt.ty) { - cast_pointer - } else { - cast_fat_ptr - } - } - ty::ty_bare_fn(..) => cast_pointer, - ty::ty_int(..) => cast_integral, - ty::ty_uint(..) => cast_integral, - ty::ty_bool => cast_integral, - ty::ty_enum(..) => cast_enum, - _ => cast_other - } -} - -pub fn cast_is_noop<'tcx>(t_in: Ty<'tcx>, t_out: Ty<'tcx>) -> bool { match (ty::deref(t_in, true), ty::deref(t_out, true)) { (Some(ty::mt{ ty: t_in, .. }), Some(ty::mt{ ty: t_out, .. })) => { t_in == t_out } - _ => false + _ => { + // This condition isn't redundant with the check for CoercionCast: + // different types can be substituted into the same type, and + // == equality can be overconservative if there are regions. + t_in == t_out + } } } fn trans_imm_cast<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, expr: &ast::Expr, id: ast::NodeId) - -> DatumBlock<'blk, 'tcx, Expr> { + -> DatumBlock<'blk, 'tcx, Expr> +{ + use middle::cast::CastTy::*; + use middle::cast::IntTy::*; + + fn int_cast(bcx: Block, + lldsttype: Type, + llsrctype: Type, + llsrc: ValueRef, + signed: bool) + -> ValueRef + { + let _icx = push_ctxt("int_cast"); + let srcsz = llsrctype.int_width(); + let dstsz = lldsttype.int_width(); + return if dstsz == srcsz { + BitCast(bcx, llsrc, lldsttype) + } else if srcsz > dstsz { + TruncOrBitCast(bcx, llsrc, lldsttype) + } else if signed { + SExtOrBitCast(bcx, llsrc, lldsttype) + } else { + ZExtOrBitCast(bcx, llsrc, lldsttype) + } + } + + fn float_cast(bcx: Block, + lldsttype: Type, + llsrctype: Type, + llsrc: ValueRef) + -> ValueRef + { + let _icx = push_ctxt("float_cast"); + let srcsz = llsrctype.float_width(); + let dstsz = lldsttype.float_width(); + return if dstsz > srcsz { + FPExt(bcx, llsrc, lldsttype) + } else if srcsz > dstsz { + FPTrunc(bcx, llsrc, lldsttype) + } else { llsrc }; + } + let _icx = push_ctxt("trans_cast"); let mut bcx = bcx; let ccx = bcx.ccx(); let t_in = expr_ty_adjusted(bcx, expr); let t_out = node_id_type(bcx, id); - let k_in = cast_type_kind(bcx.tcx(), t_in); - let k_out = cast_type_kind(bcx.tcx(), t_out); - let s_in = k_in == cast_integral && ty::type_is_signed(t_in); - let ll_t_in = type_of::arg_type_of(ccx, t_in); - let ll_t_out = type_of::arg_type_of(ccx, t_out); + debug!("trans_cast({} as {})", t_in.repr(bcx.tcx()), t_out.repr(bcx.tcx())); + let mut ll_t_in = type_of::arg_type_of(ccx, t_in); + let ll_t_out = type_of::arg_type_of(ccx, t_out); // Convert the value to be cast into a ValueRef, either by-ref or // by-value as appropriate given its type: let mut datum = unpack_datum!(bcx, trans(bcx, expr)); let datum_ty = monomorphize_type(bcx, datum.ty); - if cast_is_noop(datum_ty, t_out) { + + if cast_is_noop(bcx.tcx(), expr, datum_ty, t_out) { datum.ty = t_out; return DatumBlock::new(bcx, datum); } - let newval = match (k_in, k_out) { - (cast_integral, cast_integral) => { - let llexpr = datum.to_llscalarish(bcx); - int_cast(bcx, ll_t_out, ll_t_in, llexpr, s_in) + if type_is_fat_ptr(bcx.tcx(), t_in) { + assert!(datum.kind.is_by_ref()); + if type_is_fat_ptr(bcx.tcx(), t_out) { + return DatumBlock::new(bcx, Datum::new( + PointerCast(bcx, datum.val, ll_t_out.ptr_to()), + t_out, + Rvalue::new(ByRef) + )).to_expr_datumblock(); + } else { + // Return the address + return immediate_rvalue_bcx(bcx, + Load(bcx, get_dataptr(bcx, datum.val)), + t_out).to_expr_datumblock(); } - (cast_float, cast_float) => { - let llexpr = datum.to_llscalarish(bcx); - float_cast(bcx, ll_t_out, ll_t_in, llexpr) - } - (cast_integral, cast_float) => { - let llexpr = datum.to_llscalarish(bcx); - if s_in { - SIToFP(bcx, llexpr, ll_t_out) - } else { UIToFP(bcx, llexpr, ll_t_out) } - } - (cast_float, cast_integral) => { - let llexpr = datum.to_llscalarish(bcx); - if ty::type_is_signed(t_out) { - FPToSI(bcx, llexpr, ll_t_out) - } else { FPToUI(bcx, llexpr, ll_t_out) } - } - (cast_integral, cast_pointer) => { - let llexpr = datum.to_llscalarish(bcx); - IntToPtr(bcx, llexpr, ll_t_out) - } - (cast_pointer, cast_integral) => { - let llexpr = datum.to_llscalarish(bcx); - PtrToInt(bcx, llexpr, ll_t_out) - } - (cast_fat_ptr, cast_integral) => { - let data_ptr = Load(bcx, get_dataptr(bcx, datum.val)); - PtrToInt(bcx, data_ptr, ll_t_out) - } - (cast_pointer, cast_pointer) => { - let llexpr = datum.to_llscalarish(bcx); - PointerCast(bcx, llexpr, ll_t_out) - } - (cast_fat_ptr, cast_pointer) => { - let data_ptr = Load(bcx, get_dataptr(bcx, datum.val)); - PointerCast(bcx, data_ptr, ll_t_out) - } - (cast_enum, cast_integral) | - (cast_enum, cast_float) => { - let mut bcx = bcx; - let repr = adt::represent_type(ccx, t_in); - let datum = unpack_datum!( - bcx, datum.to_lvalue_datum(bcx, "trans_imm_cast", expr.id)); - let llexpr_ptr = datum.to_llref(); - let lldiscrim_a = - adt::trans_get_discr(bcx, &*repr, llexpr_ptr, Some(Type::i64(ccx))); - match k_out { - cast_integral => int_cast(bcx, ll_t_out, - val_ty(lldiscrim_a), - lldiscrim_a, true), - cast_float => SIToFP(bcx, lldiscrim_a, ll_t_out), - _ => { - ccx.sess().bug(&format!("translating unsupported cast: \ - {} ({:?}) -> {} ({:?})", - t_in.repr(bcx.tcx()), - k_in, - t_out.repr(bcx.tcx()), - k_out)) - } - } - } - _ => ccx.sess().bug(&format!("translating unsupported cast: \ - {} ({:?}) -> {} ({:?})", - t_in.repr(bcx.tcx()), - k_in, - t_out.repr(bcx.tcx()), - k_out)) + } + + let r_t_in = CastTy::recognize(bcx.tcx(), t_in).expect("bad input type for cast"); + let r_t_out = CastTy::recognize(bcx.tcx(), t_out).expect("bad output type for cast"); + + let (llexpr, signed) = if let Int(CEnum) = r_t_in { + let repr = adt::represent_type(ccx, t_in); + let datum = unpack_datum!( + bcx, datum.to_lvalue_datum(bcx, "trans_imm_cast", expr.id)); + let llexpr_ptr = datum.to_llref(); + let discr = adt::trans_get_discr(bcx, &*repr, llexpr_ptr, Some(Type::i64(ccx))); + ll_t_in = val_ty(discr); + (discr, adt::is_discr_signed(&*repr)) + } else { + (datum.to_llscalarish(bcx), ty::type_is_signed(t_in)) + }; + + let newval = match (r_t_in, r_t_out) { + (Ptr(_), Ptr(_)) | (FPtr, Ptr(_)) | (RPtr(_), Ptr(_)) => PointerCast(bcx, llexpr, ll_t_out), + (Ptr(_), Int(_)) | (FPtr, Int(_)) => PtrToInt(bcx, llexpr, ll_t_out), + (Int(_), Ptr(_)) => IntToPtr(bcx, llexpr, ll_t_out), + + (Int(_), Int(_)) => int_cast(bcx, ll_t_out, ll_t_in, llexpr, signed), + (Float, Float) => float_cast(bcx, ll_t_out, ll_t_in, llexpr), + (Int(_), Float) if signed => SIToFP(bcx, llexpr, ll_t_out), + (Int(_), Float) => UIToFP(bcx, llexpr, ll_t_out), + (Float, Int(I)) => FPToSI(bcx, llexpr, ll_t_out), + (Float, Int(_)) => FPToUI(bcx, llexpr, ll_t_out), + + _ => ccx.sess().span_bug(expr.span, + &format!("translating unsupported cast: \ + {} -> {}", + t_in.repr(bcx.tcx()), + t_out.repr(bcx.tcx())) + ) }; return immediate_rvalue_bcx(bcx, newval, t_out).to_expr_datumblock(); } diff --git a/src/librustc_typeck/check/cast.rs b/src/librustc_typeck/check/cast.rs index bc6159c0cff..6ae6d21f987 100644 --- a/src/librustc_typeck/check/cast.rs +++ b/src/librustc_typeck/check/cast.rs @@ -9,6 +9,29 @@ // except according to those terms. //! Code for type-checking cast expressions. +//! +//! A cast `e as U` is valid if one of the following holds: +//! * `e` has type `T` and `T` coerces to `U`; *coercion-cast* +//! * `e` has type `*T`, `U` is `*U_0`, and either `U_0: Sized` or +//! unsize_kind(`T`) = unsize_kind(`U_0`); *ptr-ptr-cast* +//! * `e` has type `*T` and `U` is a numeric type, while `T: Sized`; *ptr-addr-cast* +//! * `e` is an integer and `U` is `*U_0`, while `U_0: Sized`; *addr-ptr-cast* +//! * `e` has type `T` and `T` and `U` are any numeric types; *numeric-cast* +//! * `e` is a C-like enum and `U` is an integer type; *enum-cast* +//! * `e` has type `bool` or `char` and `U` is an integer; *prim-int-cast* +//! * `e` has type `u8` and `U` is `char`; *u8-char-cast* +//! * `e` has type `&[T; n]` and `U` is `*const T`; *array-ptr-cast* +//! * `e` is a function pointer type and `U` has type `*T`, +//! while `T: Sized`; *fptr-ptr-cast* +//! * `e` is a function pointer type and `U` is an integer; *fptr-addr-cast* +//! +//! where `&.T` and `*T` are references of either mutability, +//! and where unsize_kind(`T`) is the kind of the unsize info +//! in `T` - a vtable or a length (or `()` if `T: Sized`). +//! +//! Casting is not transitive, that is, even if `e as U1 as U2` is a valid +//! expression, `e as U2` is not necessarily so (in fact it will only be valid if +//! `U1` coerces to `U2`). use super::coercion; use super::demand; @@ -16,11 +39,13 @@ use super::FnCtxt; use super::structurally_resolved_type; use lint; -use middle::infer; +use middle::cast::{CastKind, CastTy}; use middle::ty; use middle::ty::Ty; use syntax::ast; +use syntax::ast::UintTy::{TyU8}; use syntax::codemap::Span; +use util::ppaux::Repr; /// Reifies a cast check to be checked once we have full type information for /// a function context. @@ -31,6 +56,46 @@ pub struct CastCheck<'tcx> { span: Span, } +#[derive(Copy, Clone, PartialEq, Eq)] +enum UnsizeKind<'tcx> { + Vtable, + Length, + OfTy(Ty<'tcx>) +} + +/// Returns the kind of unsize information of t, or None +/// if t is sized or it is unknown. +fn unsize_kind<'a,'tcx>(fcx: &FnCtxt<'a, 'tcx>, + t: Ty<'tcx>) + -> Option> { + match t.sty { + ty::ty_vec(_, None) | ty::ty_str => Some(UnsizeKind::Length), + ty::ty_trait(_) => Some(UnsizeKind::Vtable), + ty::ty_struct(did, substs) => { + match ty::struct_fields(fcx.tcx(), did, substs).pop() { + None => None, + Some(f) => unsize_kind(fcx, f.mt.ty) + } + } + ty::ty_projection(..) | ty::ty_param(..) => + Some(UnsizeKind::OfTy(t)), + _ => None + } +} + +#[derive(Copy, Clone)] +enum CastError { + CastToBool, + CastToChar, + DifferingKinds, + IllegalCast, + NeedViaPtr, + NeedViaInt, + NeedViaUsize, + NonScalar, + RefToMutPtr +} + impl<'tcx> CastCheck<'tcx> { pub fn new(expr: ast::Expr, expr_ty: Ty<'tcx>, cast_ty: Ty<'tcx>, span: Span) -> CastCheck<'tcx> { @@ -41,155 +106,271 @@ impl<'tcx> CastCheck<'tcx> { span: span, } } -} -pub fn check_cast<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, cast: &CastCheck<'tcx>) { - fn cast_through_integer_err<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, - span: Span, - t_1: Ty<'tcx>, - t_e: Ty<'tcx>) { - fcx.type_error_message(span, |actual| { - format!("illegal cast; cast through an \ - integer first: `{}` as `{}`", - actual, - fcx.infcx().ty_to_string(t_1)) - }, t_e, None); - } - - let span = cast.span; - let e = &cast.expr; - let t_e = structurally_resolved_type(fcx, span, cast.expr_ty); - let t_1 = structurally_resolved_type(fcx, span, cast.cast_ty); - let tcx = fcx.tcx(); - - // Check for trivial casts. - if !ty::type_has_ty_infer(t_1) { - if let Ok(()) = coercion::mk_assignty(fcx, e, t_e, t_1) { - if ty::type_is_numeric(t_1) && ty::type_is_numeric(t_e) { - tcx.sess.add_lint(lint::builtin::TRIVIAL_NUMERIC_CASTS, - e.id, - span, - format!("trivial numeric cast: `{}` as `{}`. Cast can be \ - replaced by coercion, this might require type \ - ascription or a temporary variable", - fcx.infcx().ty_to_string(t_e), - fcx.infcx().ty_to_string(t_1))); - } else { - tcx.sess.add_lint(lint::builtin::TRIVIAL_CASTS, - e.id, - span, - format!("trivial cast: `{}` as `{}`. Cast can be \ - replaced by coercion, this might require type \ - ascription or a temporary variable", - fcx.infcx().ty_to_string(t_e), - fcx.infcx().ty_to_string(t_1))); + fn report_cast_error<'a>(&self, fcx: &FnCtxt<'a, 'tcx>, + e: CastError) { + match e { + CastError::NeedViaPtr | + CastError::NeedViaInt | + CastError::NeedViaUsize => { + fcx.type_error_message(self.span, |actual| { + format!("illegal cast; cast through {} first: `{}` as `{}`", + match e { + CastError::NeedViaPtr => "a raw pointer", + CastError::NeedViaInt => "an integer", + CastError::NeedViaUsize => "a usize", + _ => unreachable!() + }, + actual, + fcx.infcx().ty_to_string(self.cast_ty)) + }, self.expr_ty, None) + } + CastError::CastToBool => { + span_err!(fcx.tcx().sess, self.span, E0054, + "cannot cast as `bool`, compare with zero instead"); + } + CastError::CastToChar => { + fcx.type_error_message(self.span, |actual| { + format!("only `u8` can be cast as `char`, not `{}`", actual) + }, self.expr_ty, None); + } + CastError::NonScalar => { + fcx.type_error_message(self.span, |actual| { + format!("non-scalar cast: `{}` as `{}`", + actual, + fcx.infcx().ty_to_string(self.cast_ty)) + }, self.expr_ty, None); + } + CastError::IllegalCast => { + fcx.type_error_message(self.span, |actual| { + format!("illegal cast: `{}` as `{}`", + actual, + fcx.infcx().ty_to_string(self.cast_ty)) + }, self.expr_ty, None); + } + CastError::DifferingKinds => { + fcx.type_error_message(self.span, |actual| { + format!("illegal cast: `{}` as `{}`; vtable kinds may not match", + actual, + fcx.infcx().ty_to_string(self.cast_ty)) + }, self.expr_ty, None); + } + CastError::RefToMutPtr => { + span_err!(fcx.tcx().sess, self.span, E0188, + "cannot cast an immutable reference to a \ + mutable pointer"); } - return; } } - let t_e_is_bare_fn_item = ty::type_is_bare_fn_item(t_e); - let t_e_is_scalar = ty::type_is_scalar(t_e); - let t_e_is_integral = ty::type_is_integral(t_e); - let t_e_is_float = ty::type_is_floating_point(t_e); - let t_e_is_c_enum = ty::type_is_c_like_enum(tcx, t_e); - - let t_1_is_scalar = ty::type_is_scalar(t_1); - let t_1_is_integral = ty::type_is_integral(t_1); - let t_1_is_char = ty::type_is_char(t_1); - let t_1_is_bare_fn = ty::type_is_bare_fn(t_1); - let t_1_is_float = ty::type_is_floating_point(t_1); - let t_1_is_c_enum = ty::type_is_c_like_enum(tcx, t_1); - let t1_is_fat_ptr = fcx.type_is_fat_ptr(t_1, span); - - // casts to scalars other than `char` and `bare fn` are trivial - let t_1_is_trivial = t_1_is_scalar && !t_1_is_char && !t_1_is_bare_fn; - - if t_e_is_bare_fn_item && t_1_is_bare_fn { - demand::coerce(fcx, e.span, t_1, &e); - } else if t_1_is_char { - let t_e = fcx.infcx().shallow_resolve(t_e); - if t_e.sty != ty::ty_uint(ast::TyU8) { - fcx.type_error_message(span, |actual| { - format!("only `u8` can be cast as `char`, not `{}`", actual) - }, t_e, None); - } - } else if t_1.sty == ty::ty_bool { - span_err!(tcx.sess, span, E0054, - "cannot cast as `bool`, compare with zero instead"); - } else if t_e_is_float && (t_1_is_scalar || t_1_is_c_enum) && - !(t_1_is_integral || t_1_is_float) { - // Casts from float must go through an integer - cast_through_integer_err(fcx, span, t_1, t_e) - } else if t_1_is_float && (t_e_is_scalar || t_e_is_c_enum) && - !(t_e_is_integral || t_e_is_float || t_e.sty == ty::ty_bool) { - // Casts to float must go through an integer or boolean - cast_through_integer_err(fcx, span, t_1, t_e) - } else if t_e_is_c_enum && t_1_is_trivial { - if ty::type_is_unsafe_ptr(t_1) { - // ... and likewise with C enum -> *T - cast_through_integer_err(fcx, span, t_1, t_e) - } - // casts from C-like enums are allowed - } else if ty::type_is_region_ptr(t_e) && ty::type_is_unsafe_ptr(t_1) { - fn types_compatible<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, sp: Span, - t1: Ty<'tcx>, t2: Ty<'tcx>) -> bool { - match t1.sty { - ty::ty_vec(_, Some(_)) => {} - _ => return false - } - if ty::type_needs_infer(t2) { - // This prevents this special case from going off when casting - // to a type that isn't fully specified; e.g. `as *_`. (Issue - // #14893.) - return false - } - - let el = ty::sequence_element_type(fcx.tcx(), t1); - infer::mk_eqty(fcx.infcx(), - false, - infer::Misc(sp), - el, - t2).is_ok() + fn trivial_cast_lint<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) { + let t_1 = self.cast_ty; + let t_e = self.expr_ty; + if ty::type_is_numeric(t_1) && ty::type_is_numeric(t_e) { + fcx.tcx().sess.add_lint(lint::builtin::TRIVIAL_NUMERIC_CASTS, + self.expr.id, + self.span, + format!("trivial numeric cast: `{}` as `{}`. Cast can be \ + replaced by coercion, this might require type \ + ascription or a temporary variable", + fcx.infcx().ty_to_string(t_e), + fcx.infcx().ty_to_string(t_1))); + } else { + fcx.tcx().sess.add_lint(lint::builtin::TRIVIAL_CASTS, + self.expr.id, + self.span, + format!("trivial cast: `{}` as `{}`. Cast can be \ + replaced by coercion, this might require type \ + ascription or a temporary variable", + fcx.infcx().ty_to_string(t_e), + fcx.infcx().ty_to_string(t_1))); } - // Due to the limitations of LLVM global constants, - // region pointers end up pointing at copies of - // vector elements instead of the original values. - // To allow unsafe pointers to work correctly, we - // need to special-case obtaining an unsafe pointer - // from a region pointer to a vector. + } - /* this cast is only allowed from &[T, ..n] to *T or - &T to *T. */ - match (&t_e.sty, &t_1.sty) { - (&ty::ty_rptr(_, ty::mt { ty: mt1, mutbl: ast::MutImmutable }), - &ty::ty_ptr(ty::mt { ty: mt2, mutbl: ast::MutImmutable })) - if types_compatible(fcx, e.span, mt1, mt2) => { - /* this case is allowed */ + pub fn check<'a>(mut self, fcx: &FnCtxt<'a, 'tcx>) { + self.expr_ty = structurally_resolved_type(fcx, self.span, self.expr_ty); + self.cast_ty = structurally_resolved_type(fcx, self.span, self.cast_ty); + + debug!("check_cast({}, {} as {})", self.expr.id, self.expr_ty.repr(fcx.tcx()), + self.cast_ty.repr(fcx.tcx())); + + if ty::type_is_error(self.expr_ty) || ty::type_is_error(self.cast_ty) { + // No sense in giving duplicate error messages + } else if self.try_coercion_cast(fcx) { + self.trivial_cast_lint(fcx); + debug!(" -> CoercionCast"); + fcx.tcx().cast_kinds.borrow_mut().insert(self.expr.id, + CastKind::CoercionCast); + } else { match self.do_check(fcx) { + Ok(k) => { + debug!(" -> {:?}", k); + fcx.tcx().cast_kinds.borrow_mut().insert(self.expr.id, k); } + Err(e) => self.report_cast_error(fcx, e) + };} + } + + /// Check a cast, and report an error if one exists. In some cases, + /// this can return Ok and create type errors rather than returning + /// directly. coercion-cast is handled in check instead of here. + fn do_check<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result { + use middle::cast::IntTy::*; + use middle::cast::CastTy::*; + + let (t_e, t_1) = match (CastTy::recognize(fcx.tcx(), self.expr_ty), + CastTy::recognize(fcx.tcx(), self.cast_ty)) { + (Some(t_e), Some(t_1)) => (t_e, t_1), _ => { - demand::coerce(fcx, e.span, t_1, &e); + return Err(CastError::NonScalar) + } + }; + + match (t_e, t_1) { + // These types have invariants! can't cast into them. + (_, RPtr(_)) | (_, Int(CEnum)) | (_, FPtr) => Err(CastError::NonScalar), + + // * -> Bool + (_, Int(Bool)) => Err(CastError::CastToBool), + + // * -> Char + (Int(U(ast::TyU8)), Int(Char)) => Ok(CastKind::U8CharCast), // u8-char-cast + (_, Int(Char)) => Err(CastError::CastToChar), + + // prim -> float,ptr + (Int(Bool), Float) | (Int(CEnum), Float) | (Int(Char), Float) + => Err(CastError::NeedViaInt), + (Int(Bool), Ptr(_)) | (Int(CEnum), Ptr(_)) | (Int(Char), Ptr(_)) + => Err(CastError::NeedViaUsize), + + // ptr -> * + (Ptr(m1), Ptr(m2)) => self.check_ptr_ptr_cast(fcx, m1, m2), // ptr-ptr-cast + (Ptr(m_e), Int(_)) => self.check_ptr_addr_cast(fcx, m_e), // ptr-addr-cast + (Ptr(_), Float) | (FPtr, Float) => Err(CastError::NeedViaUsize), + (FPtr, Int(_)) => Ok(CastKind::FPtrAddrCast), + (RPtr(_), Int(_)) | (RPtr(_), Float) => Err(CastError::NeedViaPtr), + // * -> ptr + (Int(_), Ptr(mt)) => self.check_addr_ptr_cast(fcx, mt), // addr-ptr-cast + (FPtr, Ptr(mt)) => self.check_fptr_ptr_cast(fcx, mt), + (Float, Ptr(_)) => Err(CastError::NeedViaUsize), + (RPtr(rmt), Ptr(mt)) => self.check_ref_cast(fcx, rmt, mt), // array-ptr-cast + + // prim -> prim + (Int(CEnum), Int(_)) => Ok(CastKind::EnumCast), + (Int(Char), Int(_)) | (Int(Bool), Int(_)) => Ok(CastKind::PrimIntCast), + + (Int(_), Int(_)) | + (Int(_), Float) | + (Float, Int(_)) | + (Float, Float) => Ok(CastKind::NumericCast), + + } + } + + fn check_ptr_ptr_cast<'a>(&self, + fcx: &FnCtxt<'a, 'tcx>, + m_e: &'tcx ty::mt<'tcx>, + m_1: &'tcx ty::mt<'tcx>) + -> Result + { + debug!("check_ptr_ptr_cast m_e={} m_1={}", + m_e.repr(fcx.tcx()), m_1.repr(fcx.tcx())); + // ptr-ptr cast. vtables must match. + + // Cast to sized is OK + if fcx.type_is_known_to_be_sized(m_1.ty, self.span) { + return Ok(CastKind::PtrPtrCast); + } + + // sized -> unsized? report illegal cast (don't complain about vtable kinds) + if fcx.type_is_known_to_be_sized(m_e.ty, self.span) { + return Err(CastError::IllegalCast); + } + + // vtable kinds must match + match (unsize_kind(fcx, m_1.ty), unsize_kind(fcx, m_e.ty)) { + (Some(a), Some(b)) if a == b => Ok(CastKind::PtrPtrCast), + _ => Err(CastError::DifferingKinds) + } + } + + fn check_fptr_ptr_cast<'a>(&self, + fcx: &FnCtxt<'a, 'tcx>, + m_1: &'tcx ty::mt<'tcx>) + -> Result + { + // fptr-ptr cast. must be to sized ptr + + if fcx.type_is_known_to_be_sized(m_1.ty, self.span) { + Ok(CastKind::FPtrPtrCast) + } else { + Err(CastError::IllegalCast) + } + } + + fn check_ptr_addr_cast<'a>(&self, + fcx: &FnCtxt<'a, 'tcx>, + m_e: &'tcx ty::mt<'tcx>) + -> Result + { + // ptr-addr cast. must be from sized ptr + + if fcx.type_is_known_to_be_sized(m_e.ty, self.span) { + Ok(CastKind::PtrAddrCast) + } else { + Err(CastError::NeedViaPtr) + } + } + + fn check_ref_cast<'a>(&self, + fcx: &FnCtxt<'a, 'tcx>, + m_e: &'tcx ty::mt<'tcx>, + m_1: &'tcx ty::mt<'tcx>) + -> Result + { + // array-ptr-cast. + + if m_e.mutbl == ast::MutImmutable && m_1.mutbl == ast::MutImmutable { + if let ty::ty_vec(ety, Some(_)) = m_e.ty.sty { + // Due to the limitations of LLVM global constants, + // region pointers end up pointing at copies of + // vector elements instead of the original values. + // To allow unsafe pointers to work correctly, we + // need to special-case obtaining an unsafe pointer + // from a region pointer to a vector. + // TODO: explain comment. + + // this will report a type mismatch if needed + demand::eqtype(fcx, self.span, ety, m_1.ty); + return Ok(CastKind::ArrayPtrCast); } } - } else if t1_is_fat_ptr { - // FIXME This should be allowed where the lefthandside is also a fat - // pointer and is the same kind of fat pointer, i.e., array to array, - // trait object to trait object. That is a bit looser than the current - // rquirement that they are pointers to the same type. - if !(fcx.type_is_fat_ptr(t_e, span) && - ty::deref(t_1, true).unwrap().ty == ty::deref(t_e, true).unwrap().ty) { - fcx.type_error_message(span, |actual| { - format!("cast to fat pointer: `{}` as `{}`", - actual, - fcx.infcx().ty_to_string(t_1)) - }, t_e, None); - } - } else if !(t_e_is_scalar && t_1_is_trivial) { - fcx.type_error_message(span, |actual| { - format!("non-scalar cast: `{}` as `{}`", - actual, - fcx.infcx().ty_to_string(t_1)) - }, t_e, None); + + Err(CastError::IllegalCast) } + + fn check_addr_ptr_cast<'a>(&self, + fcx: &FnCtxt<'a, 'tcx>, + m_1: &'tcx ty::mt<'tcx>) + -> Result + { + // ptr-addr cast. pointer must be thin. + if fcx.type_is_known_to_be_sized(m_1.ty, self.span) { + Ok(CastKind::AddrPtrCast) + } else { + Err(CastError::IllegalCast) + } + } + + fn try_coercion_cast<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) -> bool { + if let Ok(()) = coercion::mk_assignty(fcx, + &self.expr, + self.expr_ty, + self.cast_ty) { + true + } else { + false + } + } + } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index db39d3c05ed..d3cf9ae6dff 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -1581,13 +1581,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span) } - pub fn type_is_fat_ptr(&self, ty: Ty<'tcx>, span: Span) -> bool { - if let Some(mt) = ty::deref(ty, true) { - return !self.type_is_known_to_be_sized(mt.ty, span); - } - false - } - pub fn register_builtin_bound(&self, ty: Ty<'tcx>, builtin_bound: ty::BuiltinBound, @@ -1810,11 +1803,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn check_casts(&self) { let mut deferred_cast_checks = self.inh.deferred_cast_checks.borrow_mut(); - for check in deferred_cast_checks.iter() { - cast::check_cast(self, check); + for cast in deferred_cast_checks.drain(..) { + cast.check(self); } - - deferred_cast_checks.clear(); } fn select_all_obligations_and_apply_defaults(&self) { diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index bb60de955f0..51c0f8fea1c 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -825,11 +825,11 @@ register_diagnostics! { E0185, E0186, E0187, // can't infer the kind of the closure - E0188, // types differ in mutability - E0189, // can only cast a boxed pointer to a boxed object - E0190, // can only cast a &-pointer to an &-object + E0188, // can not cast a immutable reference to a mutable pointer + E0189, // deprecated: can only cast a boxed pointer to a boxed object + E0190, // deprecated: can only cast a &-pointer to an &-object E0191, // value of the associated type must be specified - E0192, // negative imples are allowed just for `Send` and `Sync` + E0192, // negative impls are allowed just for `Send` and `Sync` E0193, // cannot bound type where clause bounds may only be attached to types // involving type parameters E0194, diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index baecaa2e221..25602cb0ee3 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -77,7 +77,7 @@ This API is completely unstable and subject to change. #![feature(box_patterns)] #![feature(box_syntax)] -#![feature(collections)] +#![feature(collections, collections_drain)] #![feature(core)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] diff --git a/src/test/compile-fail/fat-ptr-cast.rs b/src/test/compile-fail/fat-ptr-cast.rs index 2099424b05c..672dec01e94 100644 --- a/src/test/compile-fail/fat-ptr-cast.rs +++ b/src/test/compile-fail/fat-ptr-cast.rs @@ -8,20 +8,24 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Make sure casts between thin pointer <-> fat pointer are illegal. +// Make sure casts between thin-pointer <-> fat pointer obey RFC401 pub trait Trait {} fn main() { let a: &[i32] = &[1, 2, 3]; let b: Box<[i32]> = Box::new([1, 2, 3]); + let p = a as *const [i32]; + let q = a.as_ptr(); - a as usize; //~ ERROR non-scalar cast + a as usize; //~ ERROR illegal cast b as usize; //~ ERROR non-scalar cast + p as usize; //~ ERROR illegal cast - let a: usize = 42; - a as *const [i32]; //~ ERROR cast to fat pointer: `usize` as `*const [i32]` + // #22955 + q as *const [i32]; //~ ERROR illegal cast - let a: *const u8 = &42; - a as *const [u8]; //~ ERROR cast to fat pointer: `*const u8` as `*const [u8]` + // #21397 + let t: *mut (Trait + 'static) = 0 as *mut _; //~ ERROR illegal cast + let mut fail: *const str = 0 as *const str; //~ ERROR illegal cast } diff --git a/src/test/compile-fail/issue-22289.rs b/src/test/compile-fail/issue-22289.rs index 1fdc8735714..bcbc414d353 100644 --- a/src/test/compile-fail/issue-22289.rs +++ b/src/test/compile-fail/issue-22289.rs @@ -9,5 +9,5 @@ // except according to those terms. fn main() { - 0 as &std::any::Any; //~ ERROR cast to fat pointer: `i32` as `&core::any::Any` + 0 as &std::any::Any; //~ ERROR non-scalar cast }