Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow casting integers to unsafe pointers in constant expressions. #5263

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion src/librustc/metadata/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use metadata::csearch;
use metadata::cstore;
use metadata::decoder;
use metadata::tyencode;
use middle::astencode;
use middle::resolve;
use middle::ty::node_id_to_type;
use middle::ty;
Expand Down Expand Up @@ -617,7 +618,7 @@ fn encode_info_for_item(ecx: @EncodeContext, ebml_w: writer::Encoder,
let must_write =
match item.node {
item_enum(_, _) | item_impl(*) | item_trait(*) | item_struct(*) |
item_mod(*) | item_foreign_mod(*) => true,
item_mod(*) | item_foreign_mod(*) | item_const(*) => true,
_ => false
};
if !must_write && !reachable(ecx, item.id) { return; }
Expand All @@ -640,6 +641,18 @@ fn encode_info_for_item(ecx: @EncodeContext, ebml_w: writer::Encoder,
encode_type(ecx, ebml_w, node_id_to_type(tcx, item.id));
encode_symbol(ecx, ebml_w, item.id);
encode_path(ecx, ebml_w, path, ast_map::path_name(item.ident));
astencode::encode_inlined_item(ecx, ebml_w, path,
ii_item(item),
astencode::Maps {
mutbl_map: HashMap(),
root_map: HashMap(),
last_use_map: HashMap(),
method_map: HashMap(),
vtable_map: HashMap(),
write_guard_map: HashMap(),
moves_map: HashMap(),
capture_map: HashMap()
});
ebml_w.end_tag();
}
item_fn(_, purity, ref generics, _) => {
Expand Down
29 changes: 14 additions & 15 deletions src/librustc/middle/check_const.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,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");
Expand All @@ -126,17 +126,11 @@ pub fn check_expr(sess: Session,
items without type parameters");
}
match def_map.find(&e.id) {
Some(def_variant(_, _)) |
Some(def_struct(_)) => { }
Some(def_const(_)) |
Some(def_fn(_, _)) |
Some(def_variant(_, _)) |
Some(def_struct(_)) => { }

Some(def_const(def_id)) |
Some(def_fn(def_id, _)) => {
if !ast_util::is_local(def_id) {
sess.span_err(
e.span, ~"paths in constants may only refer to \
crate-local constants or functions");
}
}
Some(def) => {
debug!("(checking const) found bad def: %?", def);
sess.span_err(
Expand Down Expand Up @@ -248,11 +242,16 @@ pub fn check_item_recursion(sess: Session,
expr_path(*) => {
match env.def_map.find(&e.id) {
Some(def_const(def_id)) => {
match env.ast_map.get(&def_id.node) {
ast_map::node_item(it, _) => {
(v.visit_item)(it, env, v);
if !ast_util::is_local(def_id) {
//XXXjdm need to check that a::foo doesn't
// refer to b::bar which refers to a::foo
} else {
match env.ast_map.get(&def_id.node) {
ast_map::node_item(it, _) => {
(v.visit_item)(it, env, v);
}
_ => fail!(~"const not bound to an item")
}
_ => fail!(~"const not bound to an item")
}
}
_ => ()
Expand Down
29 changes: 22 additions & 7 deletions src/librustc/middle/trans/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
use core::prelude::*;

use lib::llvm::{llvm, ValueRef, True, TypeRef, False};
use metadata::csearch;
use middle::const_eval;
use middle::trans::base;
use middle::trans::base::get_insn_ctxt;
use middle::trans::common::*;
use middle::trans::consts;
use middle::trans::expr;
use middle::trans::inline;
use middle::trans::machine;
use middle::trans::type_of;
use middle::ty;
Expand Down Expand Up @@ -122,7 +124,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);
Expand All @@ -133,10 +135,12 @@ pub fn const_autoderef(cx: @CrateContext, ty: ty::t, v: ValueRef)
}

pub fn get_const_val(cx: @CrateContext, def_id: ast::def_id) -> ValueRef {
if !ast_util::is_local(def_id) {
cx.tcx.sess.bug(~"cross-crate constants");
}
if !cx.const_values.contains_key(&def_id.node) {
let mut def_id = def_id;
if !ast_util::is_local(def_id) ||
!cx.const_values.contains_key(&def_id.node) {
if !ast_util::is_local(def_id) {
def_id = inline::maybe_instantiate_inline(cx, def_id, true);
}
match cx.tcx.items.get(&def_id.node) {
ast_map::node_item(@ast::item {
node: ast::item_const(_, subexpr), _
Expand Down Expand Up @@ -321,6 +325,12 @@ pub fn const_expr(cx: @CrateContext, e: @ast::expr) -> ValueRef {
if ty::type_is_signed(ety) { llvm::LLVMConstFPToSI(v, llty) }
else { llvm::LLVMConstFPToUI(v, llty) }
}
(expr::cast_pointer, expr::cast_pointer) => {
llvm::LLVMConstPointerCast(v, llty)
}
(expr::cast_integral, expr::cast_pointer) => {
llvm::LLVMConstIntToPtr(v, llty)
}
_ => {
cx.sess.impossible_case(e.span,
~"bad combination of types for cast")
Expand Down Expand Up @@ -395,8 +405,13 @@ pub fn const_expr(cx: @CrateContext, e: @ast::expr) -> ValueRef {
assert pth.types.len() == 0;
match cx.tcx.def_map.find(&e.id) {
Some(ast::def_fn(def_id, purity)) => {
assert ast_util::is_local(def_id);
let f = base::get_item_val(cx, def_id.node);
let f = if !ast_util::is_local(def_id) {
let ty = csearch::get_type(cx.tcx, def_id).ty;
base::trans_external_path(cx, def_id, ty)
} else {
assert ast_util::is_local(def_id);
base::get_item_val(cx, def_id.node)
};
match purity {
ast::extern_fn =>
llvm::LLVMConstPointerCast(f, T_ptr(T_i8())),
Expand Down
50 changes: 45 additions & 5 deletions src/librustc/middle/trans/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ use middle::trans::consts;
use middle::trans::controlflow;
use middle::trans::datum::*;
use middle::trans::debuginfo;
use middle::trans::inline;
use middle::trans::machine;
use middle::trans::meth;
use middle::trans::tvec;
Expand Down Expand Up @@ -983,15 +984,54 @@ fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
match def {
ast::def_const(did) => {
let const_ty = expr_ty(bcx, ref_expr);
let val = if did.crate == ast::local_crate {

#[cfg(stage0)]
fn get_did(_ccx: @CrateContext, did: ast::def_id)
-> ast::def_id {
did
}

#[cfg(stage1)]
#[cfg(stage2)]
#[cfg(stage3)]
fn get_did(ccx: @CrateContext, did: ast::def_id)
-> ast::def_id {
if did.crate != ast::local_crate {
inline::maybe_instantiate_inline(ccx, did, true)
} else {
did
}
}

#[cfg(stage0)]
fn get_val(bcx: block, did: ast::def_id, const_ty: ty::t)
-> ValueRef {
let ccx = bcx.ccx();
if did.crate == ast::local_crate {
// The LLVM global has the type of its initializer,
// which may not be equal to the enum's type for
// non-C-like enums.
PointerCast(bcx, base::get_item_val(ccx, did.node),
T_ptr(type_of(bcx.ccx(), const_ty)))
} else {
base::trans_external_path(ccx, did, const_ty)
}
}

#[cfg(stage1)]
#[cfg(stage2)]
#[cfg(stage3)]
fn get_val(bcx: block, did: ast::def_id, const_ty: ty::t)
-> ValueRef {
// The LLVM global has the type of its initializer,
// which may not be equal to the enum's type for
// non-C-like enums.
PointerCast(bcx, base::get_item_val(ccx, did.node),
PointerCast(bcx, base::get_item_val(bcx.ccx(), did.node),
T_ptr(type_of(bcx.ccx(), const_ty)))
} else {
base::trans_external_path(ccx, did, const_ty)
};
}

let did = get_did(ccx, did);
let val = get_val(bcx, did, const_ty);
DatumBlock {
bcx: bcx,
datum: Datum {val: val,
Expand Down
61 changes: 60 additions & 1 deletion src/librustc/middle/typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{anon_rscope, binding_rscope, bound_self_region};
use middle::typeck::rscope::{empty_rscope, in_anon_rscope};
Expand Down Expand Up @@ -2457,6 +2457,55 @@ 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 &static/[T] to *T or
&static/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(re, mt1), &ty::ty_ptr(mt2))
if types_compatible(fcx, e.span, mt1.ty, mt2.ty) => {
match fcx.mk_subr(true, expr.span, ty::re_static, re) {
result::Ok(()) => {
/* this case is allowed for const expresisons */
}
result::Err(_) => {
fcx.type_error_message(expr.span, |actual| {
fmt!("non-static region to unsafe pointer \
cast: `%s` as `%s`", actual,
fcx.infcx().ty_to_str(t_1))
}, t_e, None);
}
}
}
_ => {
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
Expand Down Expand Up @@ -3086,6 +3135,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);
Expand Down
16 changes: 16 additions & 0 deletions src/test/auxiliary/cci_const.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2013 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.

pub extern fn bar() {
}

pub const foopy: &static/str = "hi there";
pub const uint_val: uint = 12;
pub const uint_expr: uint = (1 << uint_val) - 1;
14 changes: 14 additions & 0 deletions src/test/compile-fail/cast-vector-to-unsafe-nonstatic.rs
Original file line number Diff line number Diff line change
@@ -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 <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.

fn main() {
let foo = ['h' as u8, 'i' as u8, 0 as u8];
let bar = &foo as *u8; //~ ERROR mismatched types
}
16 changes: 16 additions & 0 deletions src/test/compile-fail/const-cast-different-types.rs
Original file line number Diff line number Diff line change
@@ -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 <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.

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() {
}
15 changes: 15 additions & 0 deletions src/test/compile-fail/const-cast-wrong-type.rs
Original file line number Diff line number Diff line change
@@ -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 <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.

const a: [u8 * 3] = ['h' as u8, 'i' as u8, 0 as u8];
const b: *i8 = &a as *i8; //~ ERROR mismatched types

fn main() {
}
15 changes: 15 additions & 0 deletions src/test/run-pass/const-cast-ptr-int.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright 2013 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.

const a: *u8 = 0 as *u8;

fn main() {
assert a == ptr::null();
}
Loading