Skip to content

Commit 145e3e1

Browse files
author
Ariel Ben-Yehuda
committed
Overhaul cast semantics and make them follow RFC401
This should hopefully fix all cast-related ICEs once and for all.
1 parent 3ea16ce commit 145e3e1

File tree

12 files changed

+575
-346
lines changed

12 files changed

+575
-346
lines changed

src/librustc/diagnostics.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -526,7 +526,6 @@ a compile-time constant.
526526

527527
register_diagnostics! {
528528
E0011,
529-
E0012,
530529
E0014,
531530
E0016,
532531
E0017,

src/librustc/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ pub mod back {
9292
pub mod middle {
9393
pub mod astconv_util;
9494
pub mod astencode;
95+
pub mod cast;
9596
pub mod cfg;
9697
pub mod check_const;
9798
pub mod check_static_recursion;

src/librustc/middle/cast.rs

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Helpers for handling cast expressions, used in both
12+
// typeck and trans.
13+
14+
use middle::ty::{self, Ty};
15+
16+
use syntax::ast;
17+
18+
19+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
20+
pub enum IntTy {
21+
U(ast::UintTy),
22+
I,
23+
CEnum,
24+
Bool,
25+
Char
26+
}
27+
28+
// Valid types for the result of a non-coercion cast
29+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
30+
pub enum CastTy<'tcx> {
31+
Int(IntTy),
32+
Float,
33+
FPtr,
34+
Ptr(&'tcx ty::mt<'tcx>),
35+
RPtr(&'tcx ty::mt<'tcx>),
36+
}
37+
38+
/// Cast Kind. See RFC 401 (or librustc_typeck/check/cast.rs)
39+
#[derive(Copy, Clone, Debug)]
40+
pub enum CastKind {
41+
CoercionCast,
42+
PtrPtrCast,
43+
PtrAddrCast,
44+
AddrPtrCast,
45+
NumericCast,
46+
EnumCast,
47+
PrimIntCast,
48+
U8CharCast,
49+
ArrayPtrCast,
50+
FPtrPtrCast,
51+
FPtrAddrCast
52+
}
53+
54+
impl<'tcx> CastTy<'tcx> {
55+
pub fn recognize(tcx: &ty::ctxt<'tcx>, t: Ty<'tcx>)
56+
-> Option<CastTy<'tcx>> {
57+
match t.sty {
58+
ty::ty_bool => Some(CastTy::Int(IntTy::Bool)),
59+
ty::ty_char => Some(CastTy::Int(IntTy::Char)),
60+
ty::ty_int(_) => Some(CastTy::Int(IntTy::I)),
61+
ty::ty_uint(u) => Some(CastTy::Int(IntTy::U(u))),
62+
ty::ty_float(_) => Some(CastTy::Float),
63+
ty::ty_enum(..) if ty::type_is_c_like_enum(
64+
tcx, t) => Some(CastTy::Int(IntTy::CEnum)),
65+
ty::ty_ptr(ref mt) => Some(CastTy::Ptr(mt)),
66+
ty::ty_rptr(_, ref mt) => Some(CastTy::RPtr(mt)),
67+
ty::ty_bare_fn(..) => Some(CastTy::FPtr),
68+
_ => None,
69+
}
70+
}
71+
}

src/librustc/middle/check_const.rs

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
// - It's not possible to take the address of a static item with unsafe interior. This is enforced
2525
// by borrowck::gather_loans
2626

27+
use middle::cast::{CastKind};
2728
use middle::const_eval;
2829
use middle::def;
2930
use middle::expr_use_visitor as euv;
@@ -32,11 +33,10 @@ use middle::mem_categorization as mc;
3233
use middle::traits;
3334
use middle::ty::{self, Ty};
3435
use util::nodemap::NodeMap;
35-
use util::ppaux;
36+
use util::ppaux::Repr;
3637

3738
use syntax::ast;
3839
use syntax::codemap::Span;
39-
use syntax::print::pprust;
4040
use syntax::visit::{self, Visitor};
4141

4242
use std::collections::hash_map::Entry;
@@ -199,7 +199,7 @@ impl<'a, 'tcx> CheckCrateVisitor<'a, 'tcx> {
199199

200200
impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
201201
fn visit_item(&mut self, i: &ast::Item) {
202-
debug!("visit_item(item={})", pprust::item_to_string(i));
202+
debug!("visit_item(item={})", i.repr(self.tcx));
203203
match i.node {
204204
ast::ItemStatic(_, ast::MutImmutable, ref expr) => {
205205
self.check_static_type(&**expr);
@@ -442,26 +442,17 @@ fn check_expr<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>,
442442
}
443443
}
444444
ast::ExprCast(ref from, _) => {
445-
let toty = ty::expr_ty(v.tcx, e);
446-
let fromty = ty::expr_ty(v.tcx, &**from);
447-
let is_legal_cast =
448-
ty::type_is_numeric(toty) ||
449-
ty::type_is_unsafe_ptr(toty) ||
450-
(ty::type_is_bare_fn(toty) && ty::type_is_bare_fn_item(fromty));
451-
if !is_legal_cast {
452-
v.add_qualif(ConstQualif::NOT_CONST);
453-
if v.mode != Mode::Var {
454-
span_err!(v.tcx.sess, e.span, E0012,
455-
"can not cast to `{}` in {}s",
456-
ppaux::ty_to_string(v.tcx, toty), v.msg());
457-
}
458-
}
459-
if ty::type_is_unsafe_ptr(fromty) && ty::type_is_numeric(toty) {
460-
v.add_qualif(ConstQualif::NOT_CONST);
461-
if v.mode != Mode::Var {
462-
span_err!(v.tcx.sess, e.span, E0018,
463-
"can not cast a pointer to an integer in {}s", v.msg());
445+
debug!("Checking const cast(id={})", from.id);
446+
match v.tcx.cast_kinds.borrow().get(&from.id) {
447+
None => v.tcx.sess.span_bug(e.span, "no kind for cast"),
448+
Some(&CastKind::PtrAddrCast) | Some(&CastKind::FPtrAddrCast) => {
449+
v.add_qualif(ConstQualif::NOT_CONST);
450+
if v.mode != Mode::Var {
451+
span_err!(v.tcx.sess, e.span, E0018,
452+
"can not cast a pointer to an integer in {}s", v.msg());
453+
}
464454
}
455+
_ => {}
465456
}
466457
}
467458
ast::ExprPath(..) => {

src/librustc/middle/const_eval.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -992,7 +992,7 @@ fn cast_const<'tcx>(tcx: &ty::ctxt<'tcx>, val: const_val, ty: Ty) -> CastResult
992992
macro_rules! convert_val {
993993
($intermediate_ty:ty, $const_type:ident, $target_ty:ty) => {
994994
match val {
995-
const_bool(b) => Ok($const_type(b as $intermediate_ty as $target_ty)),
995+
const_bool(b) => Ok($const_type(b as u64 as $intermediate_ty as $target_ty)),
996996
const_uint(u) => Ok($const_type(u as $intermediate_ty as $target_ty)),
997997
const_int(i) => Ok($const_type(i as $intermediate_ty as $target_ty)),
998998
const_float(f) => Ok($const_type(f as $intermediate_ty as $target_ty)),

src/librustc/middle/ty.rs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ use session::Session;
4141
use lint;
4242
use metadata::csearch;
4343
use middle;
44+
use middle::cast;
4445
use middle::check_const;
4546
use middle::const_eval;
4647
use middle::def::{self, DefMap, ExportMap};
@@ -288,15 +289,6 @@ pub struct field_ty {
288289
pub origin: ast::DefId, // The DefId of the struct in which the field is declared.
289290
}
290291

291-
// Contains information needed to resolve types and (in the future) look up
292-
// the types of AST nodes.
293-
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
294-
pub struct creader_cache_key {
295-
pub cnum: CrateNum,
296-
pub pos: usize,
297-
pub len: usize
298-
}
299-
300292
#[derive(Clone, PartialEq, RustcDecodable, RustcEncodable)]
301293
pub struct ItemVariances {
302294
pub types: VecPerParamSpace<Variance>,
@@ -556,6 +548,15 @@ pub enum vtable_origin<'tcx> {
556548
// expr to the associated trait ref.
557549
pub type ObjectCastMap<'tcx> = RefCell<NodeMap<ty::PolyTraitRef<'tcx>>>;
558550

551+
// Contains information needed to resolve types and (in the future) look up
552+
// the types of AST nodes.
553+
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
554+
pub struct creader_cache_key {
555+
pub cnum: CrateNum,
556+
pub pos: usize,
557+
pub len: usize
558+
}
559+
559560
/// A restriction that certain types must be the same size. The use of
560561
/// `transmute` gives rise to these restrictions. These generally
561562
/// cannot be checked until trans; therefore, each call to `transmute`
@@ -818,6 +819,10 @@ pub struct ctxt<'tcx> {
818819

819820
/// Maps Expr NodeId's to their constant qualification.
820821
pub const_qualif_map: RefCell<NodeMap<check_const::ConstQualif>>,
822+
823+
/// Maps a cast expression to its kind. This is keyed on the
824+
/// *from* expression of the cast, not the cast itself.
825+
pub cast_kinds: RefCell<NodeMap<cast::CastKind>>
821826
}
822827

823828
impl<'tcx> ctxt<'tcx> {
@@ -2808,6 +2813,7 @@ pub fn mk_ctxt<'tcx>(s: Session,
28082813
type_impls_copy_cache: RefCell::new(HashMap::new()),
28092814
type_impls_sized_cache: RefCell::new(HashMap::new()),
28102815
const_qualif_map: RefCell::new(NodeMap()),
2816+
cast_kinds: RefCell::new(NodeMap())
28112817
}
28122818
}
28132819

src/librustc_trans/trans/consts.rs

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use trans::declare;
2929
use trans::monomorphize;
3030
use trans::type_::Type;
3131
use trans::type_of;
32+
use middle::cast::{CastTy,IntTy};
3233
use middle::subst::Substs;
3334
use middle::ty::{self, Ty};
3435
use util::ppaux::{Repr, ty_to_string};
@@ -616,53 +617,62 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
616617
}
617618
}
618619
ast::ExprCast(ref base, _) => {
619-
let llty = type_of::type_of(cx, ety);
620-
let (v, basety) = const_expr(cx, &**base, param_substs);
621-
if expr::cast_is_noop(basety, ety) {
620+
let t_1 = ety;
621+
let llty = type_of::type_of(cx, t_1);
622+
let (v, t_e) = const_expr(cx, &**base, param_substs);
623+
debug!("trans_const_cast({} as {})", t_e.repr(cx.tcx()), t_1.repr(cx.tcx()));
624+
if expr::cast_is_noop(cx.tcx(), base, t_e, t_1) {
622625
return v;
623626
}
624-
match (expr::cast_type_kind(cx.tcx(), basety),
625-
expr::cast_type_kind(cx.tcx(), ety)) {
626-
627-
(expr::cast_integral, expr::cast_integral) => {
628-
let s = ty::type_is_signed(basety) as Bool;
627+
if type_is_fat_ptr(cx.tcx(), t_e) {
628+
// Fat pointer casts.
629+
let t_1_inner = ty::deref(t_1, true).expect("cast to non-pointer").ty;
630+
let ptr_ty = type_of::in_memory_type_of(cx, t_1_inner).ptr_to();
631+
let addr = ptrcast(const_get_elt(cx, v, &[abi::FAT_PTR_ADDR as u32]),
632+
ptr_ty);
633+
if type_is_fat_ptr(cx.tcx(), t_1) {
634+
let info = const_get_elt(cx, v, &[abi::FAT_PTR_EXTRA as u32]);
635+
return C_struct(cx, &[addr, info], false)
636+
} else {
637+
return addr;
638+
}
639+
}
640+
match (CastTy::recognize(cx.tcx(), t_e).expect("bad input type for cast"),
641+
CastTy::recognize(cx.tcx(), t_1).expect("bad output type for cast")) {
642+
(CastTy::Int(IntTy::CEnum), CastTy::Int(_)) => {
643+
let repr = adt::represent_type(cx, t_e);
644+
let discr = adt::const_get_discrim(cx, &*repr, v);
645+
let iv = C_integral(cx.int_type(), discr, false);
646+
let s = adt::is_discr_signed(&*repr) as Bool;
647+
llvm::LLVMConstIntCast(iv, llty.to_ref(), s)
648+
}
649+
(CastTy::Int(_), CastTy::Int(_)) => {
650+
let s = ty::type_is_signed(t_e) as Bool;
629651
llvm::LLVMConstIntCast(v, llty.to_ref(), s)
630652
}
631-
(expr::cast_integral, expr::cast_float) => {
632-
if ty::type_is_signed(basety) {
653+
(CastTy::Int(_), CastTy::Float) => {
654+
if ty::type_is_signed(t_e) {
633655
llvm::LLVMConstSIToFP(v, llty.to_ref())
634656
} else {
635657
llvm::LLVMConstUIToFP(v, llty.to_ref())
636658
}
637659
}
638-
(expr::cast_float, expr::cast_float) => {
660+
(CastTy::Float, CastTy::Float) => {
639661
llvm::LLVMConstFPCast(v, llty.to_ref())
640662
}
641-
(expr::cast_float, expr::cast_integral) => {
642-
if ty::type_is_signed(ety) { llvm::LLVMConstFPToSI(v, llty.to_ref()) }
663+
(CastTy::Float, CastTy::Int(_)) => {
664+
if ty::type_is_signed(t_1) { llvm::LLVMConstFPToSI(v, llty.to_ref()) }
643665
else { llvm::LLVMConstFPToUI(v, llty.to_ref()) }
644666
}
645-
(expr::cast_enum, expr::cast_integral) => {
646-
let repr = adt::represent_type(cx, basety);
647-
let discr = adt::const_get_discrim(cx, &*repr, v);
648-
let iv = C_integral(cx.int_type(), discr, false);
649-
let ety_cast = expr::cast_type_kind(cx.tcx(), ety);
650-
match ety_cast {
651-
expr::cast_integral => {
652-
let s = ty::type_is_signed(ety) as Bool;
653-
llvm::LLVMConstIntCast(iv, llty.to_ref(), s)
654-
}
655-
_ => cx.sess().bug("enum cast destination is not \
656-
integral")
657-
}
658-
}
659-
(expr::cast_pointer, expr::cast_pointer) => {
667+
(CastTy::Ptr(_), CastTy::Ptr(_)) | (CastTy::FPtr, CastTy::Ptr(_))
668+
| (CastTy::RPtr(_), CastTy::Ptr(_)) => {
660669
ptrcast(v, llty)
661670
}
662-
(expr::cast_integral, expr::cast_pointer) => {
671+
(CastTy::FPtr, CastTy::FPtr) => ptrcast(v, llty), // isn't this a coercion?
672+
(CastTy::Int(_), CastTy::Ptr(_)) => {
663673
llvm::LLVMConstIntToPtr(v, llty.to_ref())
664674
}
665-
(expr::cast_pointer, expr::cast_integral) => {
675+
(CastTy::Ptr(_), CastTy::Int(_)) | (CastTy::FPtr, CastTy::Int(_)) => {
666676
llvm::LLVMConstPtrToInt(v, llty.to_ref())
667677
}
668678
_ => {

0 commit comments

Comments
 (0)