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.
This commit is contained in:
Ariel Ben-Yehuda 2015-05-05 19:36:47 +03:00 committed by Ariel Ben-Yehuda
parent a172f4022d
commit 83acebc462
16 changed files with 614 additions and 368 deletions

View File

@ -801,7 +801,6 @@ struct Foo<T: 'static> {
register_diagnostics! { register_diagnostics! {
E0011, E0011,
E0012,
E0014, E0014,
E0016, E0016,
E0017, E0017,

View File

@ -94,6 +94,7 @@ pub mod back {
pub mod middle { pub mod middle {
pub mod astconv_util; pub mod astconv_util;
pub mod astencode; pub mod astencode;
pub mod cast;
pub mod cfg; pub mod cfg;
pub mod check_const; pub mod check_const;
pub mod check_static_recursion; pub mod check_static_recursion;

View File

@ -148,6 +148,7 @@ enum_from_u32! {
tag_table_capture_modes = 0x67, tag_table_capture_modes = 0x67,
tag_table_object_cast_map = 0x68, tag_table_object_cast_map = 0x68,
tag_table_const_qualif = 0x69, tag_table_const_qualif = 0x69,
tag_table_cast_kinds = 0x6a,
} }
} }

View File

@ -23,6 +23,7 @@ use metadata::tydecode;
use metadata::tydecode::{DefIdSource, NominalType, TypeWithId, TypeParameter}; use metadata::tydecode::{DefIdSource, NominalType, TypeWithId, TypeParameter};
use metadata::tydecode::{RegionParameter, ClosureSource}; use metadata::tydecode::{RegionParameter, ClosureSource};
use metadata::tyencode; use metadata::tyencode;
use middle::cast;
use middle::check_const::ConstQualif; use middle::check_const::ConstQualif;
use middle::mem_categorization::Typer; use middle::mem_categorization::Typer;
use middle::privacy::{AllPublic, LastMod}; 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(); 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> { pub trait vtable_decoder_helpers<'tcx> {
fn read_vec_per_param_space<T, F>(&mut self, f: F) -> VecPerParamSpace<T> where fn read_vec_per_param_space<T, F>(&mut self, f: F) -> VecPerParamSpace<T> where
F: FnMut(&mut Self) -> T; 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() { for &qualif in tcx.const_qualif_map.borrow().get(&id).iter() {
rbml_w.tag(c::tag_table_const_qualif, |rbml_w| { rbml_w.tag(c::tag_table_const_qualif, |rbml_w| {
rbml_w.id(id); rbml_w.id(id);
@ -1289,6 +1301,8 @@ trait rbml_decoder_decoder_helpers<'tcx> {
-> subst::Substs<'tcx>; -> subst::Substs<'tcx>;
fn read_auto_adjustment<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>) fn read_auto_adjustment<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>)
-> ty::AutoAdjustment<'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>) fn read_closure_kind<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>)
-> ty::ClosureKind; -> ty::ClosureKind;
fn read_closure_ty<'a, 'b>(&mut self, dcx: &DecodeContext<'a, 'b, 'tcx>) 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() }).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>) fn read_closure_kind<'b, 'c>(&mut self, _dcx: &DecodeContext<'b, 'c, 'tcx>)
-> ty::ClosureKind -> ty::ClosureKind
{ {
@ -1801,6 +1821,11 @@ fn decode_side_tables(dcx: &DecodeContext,
dcx.tcx.closure_kinds.borrow_mut().insert(ast_util::local_def(id), dcx.tcx.closure_kinds.borrow_mut().insert(ast_util::local_def(id),
closure_kind); 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 => { c::tag_table_const_qualif => {
let qualif: ConstQualif = Decodable::decode(val_dsr).unwrap(); let qualif: ConstQualif = Decodable::decode(val_dsr).unwrap();
dcx.tcx.const_qualif_map.borrow_mut().insert(id, qualif); dcx.tcx.const_qualif_map.borrow_mut().insert(id, qualif);

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// 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<CastTy<'tcx>> {
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,
}
}
}

View File

@ -24,6 +24,7 @@
// - It's not possible to take the address of a static item with unsafe interior. This is enforced // - It's not possible to take the address of a static item with unsafe interior. This is enforced
// by borrowck::gather_loans // by borrowck::gather_loans
use middle::cast::{CastKind};
use middle::const_eval; use middle::const_eval;
use middle::def; use middle::def;
use middle::expr_use_visitor as euv; use middle::expr_use_visitor as euv;
@ -32,11 +33,10 @@ use middle::mem_categorization as mc;
use middle::traits; use middle::traits;
use middle::ty::{self, Ty}; use middle::ty::{self, Ty};
use util::nodemap::NodeMap; use util::nodemap::NodeMap;
use util::ppaux; use util::ppaux::Repr;
use syntax::ast; use syntax::ast;
use syntax::codemap::Span; use syntax::codemap::Span;
use syntax::print::pprust;
use syntax::visit::{self, Visitor}; use syntax::visit::{self, Visitor};
use std::collections::hash_map::Entry; 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> { impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
fn visit_item(&mut self, i: &ast::Item) { 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 { match i.node {
ast::ItemStatic(_, ast::MutImmutable, ref expr) => { ast::ItemStatic(_, ast::MutImmutable, ref expr) => {
self.check_static_type(&**expr); self.check_static_type(&**expr);
@ -440,26 +440,17 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
} }
} }
ast::ExprCast(ref from, _) => { ast::ExprCast(ref from, _) => {
let toty = ty::expr_ty(v.tcx, e); debug!("Checking const cast(id={})", from.id);
let fromty = ty::expr_ty(v.tcx, &**from); match v.tcx.cast_kinds.borrow().get(&from.id) {
let is_legal_cast = None => v.tcx.sess.span_bug(e.span, "no kind for cast"),
ty::type_is_numeric(toty) || Some(&CastKind::PtrAddrCast) | Some(&CastKind::FPtrAddrCast) => {
ty::type_is_unsafe_ptr(toty) || v.add_qualif(ConstQualif::NOT_CONST);
(ty::type_is_bare_fn(toty) && ty::type_is_bare_fn_item(fromty)); if v.mode != Mode::Var {
if !is_legal_cast { span_err!(v.tcx.sess, e.span, E0018,
v.add_qualif(ConstQualif::NOT_CONST); "can not cast a pointer to an integer in {}s", v.msg());
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());
} }
_ => {}
} }
} }
ast::ExprPath(..) => { ast::ExprPath(..) => {

View File

@ -1002,7 +1002,7 @@ fn cast_const<'tcx>(tcx: &ty::ctxt<'tcx>, val: const_val, ty: Ty) -> CastResult
macro_rules! convert_val { macro_rules! convert_val {
($intermediate_ty:ty, $const_type:ident, $target_ty:ty) => { ($intermediate_ty:ty, $const_type:ident, $target_ty:ty) => {
match val { 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_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_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)), const_float(f) => Ok($const_type(f as $intermediate_ty as $target_ty)),

View File

@ -41,6 +41,7 @@ use session::Session;
use lint; use lint;
use metadata::csearch; use metadata::csearch;
use middle; use middle;
use middle::cast;
use middle::check_const; use middle::check_const;
use middle::const_eval; use middle::const_eval;
use middle::def::{self, DefMap, ExportMap}; 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. 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)] #[derive(Clone, PartialEq, RustcDecodable, RustcEncodable)]
pub struct ItemVariances { pub struct ItemVariances {
pub types: VecPerParamSpace<Variance>, pub types: VecPerParamSpace<Variance>,
@ -562,6 +554,15 @@ pub enum vtable_origin<'tcx> {
// expr to the associated trait ref. // expr to the associated trait ref.
pub type ObjectCastMap<'tcx> = RefCell<NodeMap<ty::PolyTraitRef<'tcx>>>; pub type ObjectCastMap<'tcx> = RefCell<NodeMap<ty::PolyTraitRef<'tcx>>>;
// 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 /// A restriction that certain types must be the same size. The use of
/// `transmute` gives rise to these restrictions. These generally /// `transmute` gives rise to these restrictions. These generally
/// cannot be checked until trans; therefore, each call to `transmute` /// 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. /// Caches CoerceUnsized kinds for impls on custom types.
pub custom_coerce_unsized_kinds: RefCell<DefIdMap<CustomCoerceUnsized>>, pub custom_coerce_unsized_kinds: RefCell<DefIdMap<CustomCoerceUnsized>>,
/// 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<NodeMap<cast::CastKind>>,
} }
impl<'tcx> ctxt<'tcx> { impl<'tcx> ctxt<'tcx> {
@ -2817,6 +2822,7 @@ pub fn mk_ctxt<'tcx>(s: Session,
type_impls_sized_cache: RefCell::new(HashMap::new()), type_impls_sized_cache: RefCell::new(HashMap::new()),
const_qualif_map: RefCell::new(NodeMap()), const_qualif_map: RefCell::new(NodeMap()),
custom_coerce_unsized_kinds: RefCell::new(DefIdMap()), custom_coerce_unsized_kinds: RefCell::new(DefIdMap()),
cast_kinds: RefCell::new(NodeMap()),
} }
} }

View File

@ -29,6 +29,7 @@ use trans::declare;
use trans::monomorphize; use trans::monomorphize;
use trans::type_::Type; use trans::type_::Type;
use trans::type_of; use trans::type_of;
use middle::cast::{CastTy,IntTy};
use middle::subst::Substs; use middle::subst::Substs;
use middle::ty::{self, Ty}; use middle::ty::{self, Ty};
use util::ppaux::{Repr, ty_to_string}; 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, _) => { ast::ExprCast(ref base, _) => {
let llty = type_of::type_of(cx, ety); let t_1 = ety;
let (v, basety) = const_expr(cx, &**base, param_substs); let llty = type_of::type_of(cx, t_1);
if expr::cast_is_noop(basety, ety) { 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; return v;
} }
match (expr::cast_type_kind(cx.tcx(), basety), if type_is_fat_ptr(cx.tcx(), t_e) {
expr::cast_type_kind(cx.tcx(), ety)) { // Fat pointer casts.
let t_1_inner = ty::deref(t_1, true).expect("cast to non-pointer").ty;
(expr::cast_integral, expr::cast_integral) => { let ptr_ty = type_of::in_memory_type_of(cx, t_1_inner).ptr_to();
let s = ty::type_is_signed(basety) as Bool; 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) llvm::LLVMConstIntCast(v, llty.to_ref(), s)
} }
(expr::cast_integral, expr::cast_float) => { (CastTy::Int(_), CastTy::Float) => {
if ty::type_is_signed(basety) { if ty::type_is_signed(t_e) {
llvm::LLVMConstSIToFP(v, llty.to_ref()) llvm::LLVMConstSIToFP(v, llty.to_ref())
} else { } else {
llvm::LLVMConstUIToFP(v, llty.to_ref()) llvm::LLVMConstUIToFP(v, llty.to_ref())
} }
} }
(expr::cast_float, expr::cast_float) => { (CastTy::Float, CastTy::Float) => {
llvm::LLVMConstFPCast(v, llty.to_ref()) llvm::LLVMConstFPCast(v, llty.to_ref())
} }
(expr::cast_float, expr::cast_integral) => { (CastTy::Float, CastTy::Int(_)) => {
if ty::type_is_signed(ety) { llvm::LLVMConstFPToSI(v, llty.to_ref()) } if ty::type_is_signed(t_1) { llvm::LLVMConstFPToSI(v, llty.to_ref()) }
else { llvm::LLVMConstFPToUI(v, llty.to_ref()) } else { llvm::LLVMConstFPToUI(v, llty.to_ref()) }
} }
(expr::cast_enum, expr::cast_integral) => { (CastTy::Ptr(_), CastTy::Ptr(_)) | (CastTy::FPtr, CastTy::Ptr(_))
let repr = adt::represent_type(cx, basety); | (CastTy::RPtr(_), CastTy::Ptr(_)) => {
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) => {
ptrcast(v, llty) 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()) 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()) llvm::LLVMConstPtrToInt(v, llty.to_ref())
} }
_ => { _ => {

View File

@ -48,7 +48,6 @@
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
pub use self::cast_kind::*;
pub use self::Dest::*; pub use self::Dest::*;
use self::lazy_binop_ty::*; use self::lazy_binop_ty::*;
@ -73,6 +72,7 @@ use trans::meth;
use trans::monomorphize; use trans::monomorphize;
use trans::tvec; use trans::tvec;
use trans::type_of; use trans::type_of;
use middle::cast::{CastKind, CastTy};
use middle::ty::{struct_fields, tup_fields}; use middle::ty::{struct_fields, tup_fields};
use middle::ty::{AdjustDerefRef, AdjustReifyFnPointer, AdjustUnsafeFnPointer}; use middle::ty::{AdjustDerefRef, AdjustReifyFnPointer, AdjustUnsafeFnPointer};
use middle::ty::{self, Ty}; use middle::ty::{self, Ty};
@ -1981,177 +1981,143 @@ fn trans_overloaded_call<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>,
bcx bcx
} }
fn int_cast(bcx: Block, pub fn cast_is_noop<'tcx>(tcx: &ty::ctxt<'tcx>,
lldsttype: Type, expr: &ast::Expr,
llsrctype: Type, t_in: Ty<'tcx>,
llsrc: ValueRef, t_out: Ty<'tcx>)
signed: bool) -> bool {
-> ValueRef { if let Some(&CastKind::CoercionCast) = tcx.cast_kinds.borrow().get(&expr.id) {
let _icx = push_ctxt("int_cast"); return true;
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 };
}
#[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)) { match (ty::deref(t_in, true), ty::deref(t_out, true)) {
(Some(ty::mt{ ty: t_in, .. }), Some(ty::mt{ ty: t_out, .. })) => { (Some(ty::mt{ ty: t_in, .. }), Some(ty::mt{ ty: t_out, .. })) => {
t_in == 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>, fn trans_imm_cast<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
expr: &ast::Expr, expr: &ast::Expr,
id: ast::NodeId) 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 _icx = push_ctxt("trans_cast");
let mut bcx = bcx; let mut bcx = bcx;
let ccx = bcx.ccx(); let ccx = bcx.ccx();
let t_in = expr_ty_adjusted(bcx, expr); let t_in = expr_ty_adjusted(bcx, expr);
let t_out = node_id_type(bcx, id); 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 // Convert the value to be cast into a ValueRef, either by-ref or
// by-value as appropriate given its type: // by-value as appropriate given its type:
let mut datum = unpack_datum!(bcx, trans(bcx, expr)); let mut datum = unpack_datum!(bcx, trans(bcx, expr));
let datum_ty = monomorphize_type(bcx, datum.ty); 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; datum.ty = t_out;
return DatumBlock::new(bcx, datum); return DatumBlock::new(bcx, datum);
} }
let newval = match (k_in, k_out) { if type_is_fat_ptr(bcx.tcx(), t_in) {
(cast_integral, cast_integral) => { assert!(datum.kind.is_by_ref());
let llexpr = datum.to_llscalarish(bcx); if type_is_fat_ptr(bcx.tcx(), t_out) {
int_cast(bcx, ll_t_out, ll_t_in, llexpr, s_in) 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) 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");
(cast_integral, cast_float) => {
let llexpr = datum.to_llscalarish(bcx); let (llexpr, signed) = if let Int(CEnum) = r_t_in {
if s_in { let repr = adt::represent_type(ccx, t_in);
SIToFP(bcx, llexpr, ll_t_out) let datum = unpack_datum!(
} else { UIToFP(bcx, llexpr, ll_t_out) } bcx, datum.to_lvalue_datum(bcx, "trans_imm_cast", expr.id));
} let llexpr_ptr = datum.to_llref();
(cast_float, cast_integral) => { let discr = adt::trans_get_discr(bcx, &*repr, llexpr_ptr, Some(Type::i64(ccx)));
let llexpr = datum.to_llscalarish(bcx); ll_t_in = val_ty(discr);
if ty::type_is_signed(t_out) { (discr, adt::is_discr_signed(&*repr))
FPToSI(bcx, llexpr, ll_t_out) } else {
} else { FPToUI(bcx, llexpr, ll_t_out) } (datum.to_llscalarish(bcx), ty::type_is_signed(t_in))
} };
(cast_integral, cast_pointer) => {
let llexpr = datum.to_llscalarish(bcx); let newval = match (r_t_in, r_t_out) {
IntToPtr(bcx, llexpr, ll_t_out) (Ptr(_), Ptr(_)) | (FPtr, Ptr(_)) | (RPtr(_), Ptr(_)) => PointerCast(bcx, llexpr, ll_t_out),
} (Ptr(_), Int(_)) | (FPtr, Int(_)) => PtrToInt(bcx, llexpr, ll_t_out),
(cast_pointer, cast_integral) => { (Int(_), Ptr(_)) => IntToPtr(bcx, llexpr, ll_t_out),
let llexpr = datum.to_llscalarish(bcx);
PtrToInt(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),
(cast_fat_ptr, cast_integral) => { (Int(_), Float) if signed => SIToFP(bcx, llexpr, ll_t_out),
let data_ptr = Load(bcx, get_dataptr(bcx, datum.val)); (Int(_), Float) => UIToFP(bcx, llexpr, ll_t_out),
PtrToInt(bcx, data_ptr, ll_t_out) (Float, Int(I)) => FPToSI(bcx, llexpr, ll_t_out),
} (Float, Int(_)) => FPToUI(bcx, llexpr, ll_t_out),
(cast_pointer, cast_pointer) => {
let llexpr = datum.to_llscalarish(bcx); _ => ccx.sess().span_bug(expr.span,
PointerCast(bcx, llexpr, ll_t_out) &format!("translating unsupported cast: \
} {} -> {}",
(cast_fat_ptr, cast_pointer) => { t_in.repr(bcx.tcx()),
let data_ptr = Load(bcx, get_dataptr(bcx, datum.val)); t_out.repr(bcx.tcx()))
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))
}; };
return immediate_rvalue_bcx(bcx, newval, t_out).to_expr_datumblock(); return immediate_rvalue_bcx(bcx, newval, t_out).to_expr_datumblock();
} }

View File

@ -9,6 +9,29 @@
// except according to those terms. // except according to those terms.
//! Code for type-checking cast expressions. //! 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::coercion;
use super::demand; use super::demand;
@ -16,11 +39,13 @@ use super::FnCtxt;
use super::structurally_resolved_type; use super::structurally_resolved_type;
use lint; use lint;
use middle::infer; use middle::cast::{CastKind, CastTy};
use middle::ty; use middle::ty;
use middle::ty::Ty; use middle::ty::Ty;
use syntax::ast; use syntax::ast;
use syntax::ast::UintTy::{TyU8};
use syntax::codemap::Span; use syntax::codemap::Span;
use util::ppaux::Repr;
/// Reifies a cast check to be checked once we have full type information for /// Reifies a cast check to be checked once we have full type information for
/// a function context. /// a function context.
@ -31,6 +56,46 @@ pub struct CastCheck<'tcx> {
span: Span, 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<UnsizeKind<'tcx>> {
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> { impl<'tcx> CastCheck<'tcx> {
pub fn new(expr: ast::Expr, expr_ty: Ty<'tcx>, cast_ty: Ty<'tcx>, span: Span) pub fn new(expr: ast::Expr, expr_ty: Ty<'tcx>, cast_ty: Ty<'tcx>, span: Span)
-> CastCheck<'tcx> { -> CastCheck<'tcx> {
@ -41,155 +106,271 @@ impl<'tcx> CastCheck<'tcx> {
span: span, span: span,
} }
} }
}
pub fn check_cast<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, cast: &CastCheck<'tcx>) { fn report_cast_error<'a>(&self, fcx: &FnCtxt<'a, 'tcx>,
fn cast_through_integer_err<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, e: CastError) {
span: Span, match e {
t_1: Ty<'tcx>, CastError::NeedViaPtr |
t_e: Ty<'tcx>) { CastError::NeedViaInt |
fcx.type_error_message(span, |actual| { CastError::NeedViaUsize => {
format!("illegal cast; cast through an \ fcx.type_error_message(self.span, |actual| {
integer first: `{}` as `{}`", format!("illegal cast; cast through {} first: `{}` as `{}`",
actual, match e {
fcx.infcx().ty_to_string(t_1)) CastError::NeedViaPtr => "a raw pointer",
}, t_e, None); CastError::NeedViaInt => "an integer",
} CastError::NeedViaUsize => "a usize",
_ => unreachable!()
let span = cast.span; },
let e = &cast.expr; actual,
let t_e = structurally_resolved_type(fcx, span, cast.expr_ty); fcx.infcx().ty_to_string(self.cast_ty))
let t_1 = structurally_resolved_type(fcx, span, cast.cast_ty); }, self.expr_ty, None)
let tcx = fcx.tcx(); }
CastError::CastToBool => {
// Check for trivial casts. span_err!(fcx.tcx().sess, self.span, E0054,
if !ty::type_has_ty_infer(t_1) { "cannot cast as `bool`, compare with zero instead");
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) { CastError::CastToChar => {
tcx.sess.add_lint(lint::builtin::TRIVIAL_NUMERIC_CASTS, fcx.type_error_message(self.span, |actual| {
e.id, format!("only `u8` can be cast as `char`, not `{}`", actual)
span, }, self.expr_ty, None);
format!("trivial numeric cast: `{}` as `{}`. Cast can be \ }
replaced by coercion, this might require type \ CastError::NonScalar => {
ascription or a temporary variable", fcx.type_error_message(self.span, |actual| {
fcx.infcx().ty_to_string(t_e), format!("non-scalar cast: `{}` as `{}`",
fcx.infcx().ty_to_string(t_1))); actual,
} else { fcx.infcx().ty_to_string(self.cast_ty))
tcx.sess.add_lint(lint::builtin::TRIVIAL_CASTS, }, self.expr_ty, None);
e.id, }
span, CastError::IllegalCast => {
format!("trivial cast: `{}` as `{}`. Cast can be \ fcx.type_error_message(self.span, |actual| {
replaced by coercion, this might require type \ format!("illegal cast: `{}` as `{}`",
ascription or a temporary variable", actual,
fcx.infcx().ty_to_string(t_e), fcx.infcx().ty_to_string(self.cast_ty))
fcx.infcx().ty_to_string(t_1))); }, 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); fn trivial_cast_lint<'a>(&self, fcx: &FnCtxt<'a, 'tcx>) {
let t_e_is_scalar = ty::type_is_scalar(t_e); let t_1 = self.cast_ty;
let t_e_is_integral = ty::type_is_integral(t_e); let t_e = self.expr_ty;
let t_e_is_float = ty::type_is_floating_point(t_e); if ty::type_is_numeric(t_1) && ty::type_is_numeric(t_e) {
let t_e_is_c_enum = ty::type_is_c_like_enum(tcx, t_e); fcx.tcx().sess.add_lint(lint::builtin::TRIVIAL_NUMERIC_CASTS,
self.expr.id,
let t_1_is_scalar = ty::type_is_scalar(t_1); self.span,
let t_1_is_integral = ty::type_is_integral(t_1); format!("trivial numeric cast: `{}` as `{}`. Cast can be \
let t_1_is_char = ty::type_is_char(t_1); replaced by coercion, this might require type \
let t_1_is_bare_fn = ty::type_is_bare_fn(t_1); ascription or a temporary variable",
let t_1_is_float = ty::type_is_floating_point(t_1); fcx.infcx().ty_to_string(t_e),
let t_1_is_c_enum = ty::type_is_c_like_enum(tcx, t_1); fcx.infcx().ty_to_string(t_1)));
let t1_is_fat_ptr = fcx.type_is_fat_ptr(t_1, span); } else {
fcx.tcx().sess.add_lint(lint::builtin::TRIVIAL_CASTS,
// casts to scalars other than `char` and `bare fn` are trivial self.expr.id,
let t_1_is_trivial = t_1_is_scalar && !t_1_is_char && !t_1_is_bare_fn; self.span,
format!("trivial cast: `{}` as `{}`. Cast can be \
if t_e_is_bare_fn_item && t_1_is_bare_fn { replaced by coercion, this might require type \
demand::coerce(fcx, e.span, t_1, &e); ascription or a temporary variable",
} else if t_1_is_char { fcx.infcx().ty_to_string(t_e),
let t_e = fcx.infcx().shallow_resolve(t_e); fcx.infcx().ty_to_string(t_1)));
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()
} }
// 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 pub fn check<'a>(mut self, fcx: &FnCtxt<'a, 'tcx>) {
&T to *T. */ self.expr_ty = structurally_resolved_type(fcx, self.span, self.expr_ty);
match (&t_e.sty, &t_1.sty) { self.cast_ty = structurally_resolved_type(fcx, self.span, self.cast_ty);
(&ty::ty_rptr(_, ty::mt { ty: mt1, mutbl: ast::MutImmutable }),
&ty::ty_ptr(ty::mt { ty: mt2, mutbl: ast::MutImmutable })) debug!("check_cast({}, {} as {})", self.expr.id, self.expr_ty.repr(fcx.tcx()),
if types_compatible(fcx, e.span, mt1, mt2) => { self.cast_ty.repr(fcx.tcx()));
/* this case is allowed */
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<CastKind, CastError> {
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<CastKind, CastError>
{
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<CastKind, CastError>
{
// 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<CastKind, CastError>
{
// 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<CastKind, CastError>
{
// 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 Err(CastError::IllegalCast)
// 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);
} }
fn check_addr_ptr_cast<'a>(&self,
fcx: &FnCtxt<'a, 'tcx>,
m_1: &'tcx ty::mt<'tcx>)
-> Result<CastKind, CastError>
{
// 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
}
}
} }

View File

@ -1581,13 +1581,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span) 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, pub fn register_builtin_bound(&self,
ty: Ty<'tcx>, ty: Ty<'tcx>,
builtin_bound: ty::BuiltinBound, builtin_bound: ty::BuiltinBound,
@ -1810,11 +1803,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn check_casts(&self) { fn check_casts(&self) {
let mut deferred_cast_checks = self.inh.deferred_cast_checks.borrow_mut(); let mut deferred_cast_checks = self.inh.deferred_cast_checks.borrow_mut();
for check in deferred_cast_checks.iter() { for cast in deferred_cast_checks.drain(..) {
cast::check_cast(self, check); cast.check(self);
} }
deferred_cast_checks.clear();
} }
fn select_all_obligations_and_apply_defaults(&self) { fn select_all_obligations_and_apply_defaults(&self) {

View File

@ -825,11 +825,11 @@ register_diagnostics! {
E0185, E0185,
E0186, E0186,
E0187, // can't infer the kind of the closure E0187, // can't infer the kind of the closure
E0188, // types differ in mutability E0188, // can not cast a immutable reference to a mutable pointer
E0189, // can only cast a boxed pointer to a boxed object E0189, // deprecated: can only cast a boxed pointer to a boxed object
E0190, // can only cast a &-pointer to an &-object E0190, // deprecated: can only cast a &-pointer to an &-object
E0191, // value of the associated type must be specified 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 E0193, // cannot bound type where clause bounds may only be attached to types
// involving type parameters // involving type parameters
E0194, E0194,

View File

@ -77,7 +77,7 @@ This API is completely unstable and subject to change.
#![feature(box_patterns)] #![feature(box_patterns)]
#![feature(box_syntax)] #![feature(box_syntax)]
#![feature(collections)] #![feature(collections, collections_drain)]
#![feature(core)] #![feature(core)]
#![feature(quote)] #![feature(quote)]
#![feature(rustc_diagnostic_macros)] #![feature(rustc_diagnostic_macros)]

View File

@ -8,20 +8,24 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // 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 {} pub trait Trait {}
fn main() { fn main() {
let a: &[i32] = &[1, 2, 3]; let a: &[i32] = &[1, 2, 3];
let b: Box<[i32]> = Box::new([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 b as usize; //~ ERROR non-scalar cast
p as usize; //~ ERROR illegal cast
let a: usize = 42; // #22955
a as *const [i32]; //~ ERROR cast to fat pointer: `usize` as `*const [i32]` q as *const [i32]; //~ ERROR illegal cast
let a: *const u8 = &42; // #21397
a as *const [u8]; //~ ERROR cast to fat pointer: `*const u8` as `*const [u8]` let t: *mut (Trait + 'static) = 0 as *mut _; //~ ERROR illegal cast
let mut fail: *const str = 0 as *const str; //~ ERROR illegal cast
} }

View File

@ -9,5 +9,5 @@
// except according to those terms. // except according to those terms.
fn main() { 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
} }