mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 07:14:28 +00:00
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:
parent
a172f4022d
commit
83acebc462
@ -801,7 +801,6 @@ struct Foo<T: 'static> {
|
||||
|
||||
register_diagnostics! {
|
||||
E0011,
|
||||
E0012,
|
||||
E0014,
|
||||
E0016,
|
||||
E0017,
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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<T, F>(&mut self, f: F) -> VecPerParamSpace<T> 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);
|
||||
|
71
src/librustc/middle/cast.rs
Normal file
71
src/librustc/middle/cast.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
@ -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(..) => {
|
||||
|
@ -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)),
|
||||
|
@ -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<Variance>,
|
||||
@ -562,6 +554,15 @@ pub enum vtable_origin<'tcx> {
|
||||
// expr to the associated trait ref.
|
||||
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
|
||||
/// `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<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> {
|
||||
@ -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()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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())
|
||||
}
|
||||
_ => {
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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<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> {
|
||||
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<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
|
||||
// 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<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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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)]
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user