From 50277ec555ccc0b72b98028c6fcc0b84511cf9ef Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Mon, 4 Mar 2013 18:22:03 -0500 Subject: [PATCH] Permit casting region pointers to unsafe ones. --- src/librustc/middle/check_const.rs | 2 +- src/librustc/middle/trans/consts.rs | 5 +- src/librustc/middle/typeck/check/mod.rs | 50 ++++++++++++++++++- .../cast-vector-to-unsafe-nonstatic.rs | 14 ++++++ .../const-cast-different-types.rs | 16 ++++++ .../compile-fail/const-cast-wrong-type.rs | 15 ++++++ src/test/run-pass/const-cast.rs | 21 ++++++++ src/test/run-pass/const-str-ptr.rs | 22 ++++++++ 8 files changed, 142 insertions(+), 3 deletions(-) create mode 100644 src/test/compile-fail/cast-vector-to-unsafe-nonstatic.rs create mode 100644 src/test/compile-fail/const-cast-different-types.rs create mode 100644 src/test/compile-fail/const-cast-wrong-type.rs create mode 100644 src/test/run-pass/const-cast.rs create mode 100644 src/test/run-pass/const-str-ptr.rs diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index c00856a0a98bd..2e528b4c0dfb4 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -107,7 +107,7 @@ pub fn check_expr(sess: Session, expr_lit(_) => (), expr_cast(_, _) => { let ety = ty::expr_ty(tcx, e); - if !ty::type_is_numeric(ety) { + if !ty::type_is_numeric(ety) && !ty::type_is_unsafe_ptr(ety) { sess.span_err(e.span, ~"can not cast to `" + ppaux::ty_to_str(tcx, ety) + ~"` in a constant expression"); diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs index d19ffe8cb211f..0dca64ee8d374 100644 --- a/src/librustc/middle/trans/consts.rs +++ b/src/librustc/middle/trans/consts.rs @@ -110,7 +110,7 @@ pub fn const_autoderef(cx: @CrateContext, ty: ty::t, v: ValueRef) let mut v1 = v; loop { // Only rptrs can be autoderef'ed in a const context. - match ty::get(ty).sty { + match ty::get(t1).sty { ty::ty_rptr(_, mt) => { t1 = mt.ty; v1 = const_deref(cx, v1); @@ -338,6 +338,9 @@ fn const_expr_unchecked(cx: @CrateContext, e: @ast::expr) -> ValueRef { integral or float") } } + (expr::cast_pointer, expr::cast_pointer) => { + llvm::LLVMConstPointerCast(v, llty) + } _ => { cx.sess.impossible_case(e.span, ~"bad combination of types for cast") diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 66c2a28da3d6b..80b13341e54e1 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -93,7 +93,7 @@ use middle::typeck::check::method::TransformTypeNormally; use middle::typeck::check::regionmanip::replace_bound_regions_in_fn_sig; use middle::typeck::check::vtable::{LocationInfo, VtableContext}; use middle::typeck::CrateCtxt; -use middle::typeck::infer::{resolve_type, force_tvar}; +use middle::typeck::infer::{resolve_type, force_tvar, mk_eqty}; use middle::typeck::infer; use middle::typeck::rscope::{binding_rscope, bound_self_region}; use middle::typeck::rscope::{RegionError}; @@ -2452,6 +2452,44 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt, let t_1_is_scalar = type_is_scalar(fcx, expr.span, t_1); if type_is_c_like_enum(fcx,expr.span,t_e) && t_1_is_scalar { /* this case is allowed */ + } else if type_is_region_ptr(fcx, expr.span, t_e) && + type_is_unsafe_ptr(fcx, expr.span, t_1) { + + fn is_vec(t: ty::t) -> bool { + match ty::get(t).sty { + ty::ty_evec(_,_) => true, + _ => false + } + } + fn types_compatible(fcx: @mut FnCtxt, sp: span, t1: ty::t, + t2: ty::t) -> bool { + if !is_vec(t1) { + false + } else { + let el = ty::sequence_element_type(fcx.tcx(), t1); + infer::mk_eqty(fcx.infcx(), false, 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] to *T or + &T to *T. */ + let te = structurally_resolved_type(fcx, e.span, t_e); + match (&ty::get(te).sty, &ty::get(t_1).sty) { + (&ty::ty_rptr(_, mt1), &ty::ty_ptr(mt2)) + if types_compatible(fcx, e.span, mt1.ty, mt2.ty) => { + /* this case is allowed */ + } + _ => { + demand::coerce(fcx, e.span, t_1, e); + } + } } else if !(type_is_scalar(fcx,expr.span,t_e) && t_1_is_scalar) { /* If more type combinations should be supported than are @@ -3081,6 +3119,16 @@ pub fn type_is_scalar(fcx: @mut FnCtxt, sp: span, typ: ty::t) -> bool { return ty::type_is_scalar(typ_s); } +pub fn type_is_unsafe_ptr(fcx: @mut FnCtxt, sp: span, typ: ty::t) -> bool { + let typ_s = structurally_resolved_type(fcx, sp, typ); + return ty::type_is_unsafe_ptr(typ_s); +} + +pub fn type_is_region_ptr(fcx: @mut FnCtxt, sp: span, typ: ty::t) -> bool { + let typ_s = structurally_resolved_type(fcx, sp, typ); + return ty::type_is_region_ptr(typ_s); +} + pub fn type_is_c_like_enum(fcx: @mut FnCtxt, sp: span, typ: ty::t) -> bool { let typ_s = structurally_resolved_type(fcx, sp, typ); return ty::type_is_c_like_enum(fcx.ccx.tcx, typ_s); diff --git a/src/test/compile-fail/cast-vector-to-unsafe-nonstatic.rs b/src/test/compile-fail/cast-vector-to-unsafe-nonstatic.rs new file mode 100644 index 0000000000000..a083757a0eb9a --- /dev/null +++ b/src/test/compile-fail/cast-vector-to-unsafe-nonstatic.rs @@ -0,0 +1,14 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + let foo = ['h' as u8, 'i' as u8, 0 as u8]; + let bar = &foo as *u8; //~ ERROR mismatched types +} \ No newline at end of file diff --git a/src/test/compile-fail/const-cast-different-types.rs b/src/test/compile-fail/const-cast-different-types.rs new file mode 100644 index 0000000000000..08fa6915106fe --- /dev/null +++ b/src/test/compile-fail/const-cast-different-types.rs @@ -0,0 +1,16 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +const a: &static/str = &"foo"; +const b: *u8 = a as *u8; //~ ERROR non-scalar cast +const c: *u8 = &a as *u8; //~ ERROR mismatched types + +fn main() { +} \ No newline at end of file diff --git a/src/test/compile-fail/const-cast-wrong-type.rs b/src/test/compile-fail/const-cast-wrong-type.rs new file mode 100644 index 0000000000000..fe91056d47b6f --- /dev/null +++ b/src/test/compile-fail/const-cast-wrong-type.rs @@ -0,0 +1,15 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +const a: [u8 * 3] = ['h' as u8, 'i' as u8, 0 as u8]; +const b: *i8 = &a as *i8; //~ ERROR mismatched types + +fn main() { +} \ No newline at end of file diff --git a/src/test/run-pass/const-cast.rs b/src/test/run-pass/const-cast.rs new file mode 100644 index 0000000000000..9174b45d1000c --- /dev/null +++ b/src/test/run-pass/const-cast.rs @@ -0,0 +1,21 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +extern fn foo() {} + +const x: *u8 = foo; +const y: *libc::c_void = x as *libc::c_void; +const a: &static/int = &10; +const b: *int = a as *int; + +fn main() { + assert x as *libc::c_void == y; + assert a as *int == b; +} \ No newline at end of file diff --git a/src/test/run-pass/const-str-ptr.rs b/src/test/run-pass/const-str-ptr.rs new file mode 100644 index 0000000000000..3438e65f05fad --- /dev/null +++ b/src/test/run-pass/const-str-ptr.rs @@ -0,0 +1,22 @@ +// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +const a: [u8 * 3] = ['h' as u8, 'i' as u8, 0 as u8]; +const c: &static/[u8 * 3] = &a; +const b: *u8 = c as *u8; + +fn main() { + let foo = &a as *u8; + assert unsafe { str::raw::from_bytes(a) } == ~"hi\x00"; + assert unsafe { str::raw::from_buf(foo) } == ~"hi"; + assert unsafe { str::raw::from_buf(b) } == ~"hi"; + assert unsafe { *b == a[0] }; + assert unsafe { *(&c[0] as *u8) == a[0] }; +} \ No newline at end of file