From 91e44ea05651ea9d9fda1e903009e3a3390dd7c0 Mon Sep 17 00:00:00 2001 From: Tim Chevalier Date: Mon, 25 Jun 2012 22:11:19 -0700 Subject: [PATCH] Be a little more clever about calculating sizes for class types We could try to calculate the size of a partially-converted class type, and run into an LLVM error because we were trying to calculate the size of a named struct whose fields hadn't yet be filled in. The issue can be dodged by extending simplify_type to convert classes to isomorphic structural records, just for the purposes of size calculations. (for real this time) Closes #2718 --- src/rustc/middle/trans/shape.rs | 28 ++-- src/test/run-pass/issue-2718.rs | 271 ++++++++++++++++++++++++++++++-- 2 files changed, 272 insertions(+), 27 deletions(-) diff --git a/src/rustc/middle/trans/shape.rs b/src/rustc/middle/trans/shape.rs index 9b8f99e4c3642..33e6051d70b2c 100644 --- a/src/rustc/middle/trans/shape.rs +++ b/src/rustc/middle/trans/shape.rs @@ -18,6 +18,7 @@ import syntax::codemap::span; import dvec::{dvec, extensions}; import std::map::hashmap; +import option::is_some; import ty_ctxt = middle::ty::ctxt; @@ -732,19 +733,24 @@ fn simplify_type(tcx: ty::ctxt, typ: ty::t) -> ty::t { ty::ty_estr(ty::vstore_slice(_)) { ty::mk_tup(tcx, [nilptr(tcx), ty::mk_int(tcx)]/~) } + // Reduce a class type to a record type in which all the fields are + // simplified + ty::ty_class(did, substs) { + let simpl_fields = (if is_some(ty::ty_dtor(tcx, did)) { + // remember the drop flag + [{ident: @"drop", mt: {ty: + ty::mk_u8(tcx), + mutbl: ast::m_mutbl}}]/~ } + else { []/~ }) + + ty::lookup_class_fields(tcx, did).map {|f| + let t = ty::lookup_field_type(tcx, did, f.id, substs); + {ident: f.ident, + mt: {ty: simplify_type(tcx, t), mutbl: ast::m_const}} + }; + ty::mk_rec(tcx, simpl_fields) + } _ { typ } } } ty::fold_ty(tcx, typ) {|t| simplifier(tcx, t) } } - -// Given a tag type `ty`, returns the offset of the payload. -//fn tag_payload_offs(bcx: block, tag_id: ast::def_id, tps: [ty::t]/~) -// -> ValueRef { -// alt tag_kind(tag_id) { -// tk_unit | tk_enum | tk_newtype { C_int(bcx.ccx(), 0) } -// tk_complex { -// compute_tag_metrics(tag_id, tps) -// } -// } -//} diff --git a/src/test/run-pass/issue-2718.rs b/src/test/run-pass/issue-2718.rs index e31e57d9213ac..90184bf05af1a 100644 --- a/src/test/run-pass/issue-2718.rs +++ b/src/test/run-pass/issue-2718.rs @@ -1,7 +1,141 @@ -fn sender_terminate(p: *packet) { -} +mod pipes { + import unsafe::{forget, reinterpret_cast}; + + enum state { + empty, + full, + blocked, + terminated + } + + type packet = { + mut state: state, + mut blocked_task: option, + mut payload: option + }; + + fn packet() -> *packet unsafe { + let p: *packet = unsafe::transmute(~{ + mut state: empty, + mut blocked_task: none::, + mut payload: none:: + }); + p + } + + #[abi = "rust-intrinsic"] + mod rusti { + fn atomic_xchng(&dst: int, src: int) -> int { fail; } + fn atomic_xchng_acq(&dst: int, src: int) -> int { fail; } + fn atomic_xchng_rel(&dst: int, src: int) -> int { fail; } + } + + // We should consider moving this to core::unsafe, although I + // suspect graydon would want us to use void pointers instead. + unsafe fn uniquify(x: *T) -> ~T { + unsafe { unsafe::reinterpret_cast(x) } + } + + fn swap_state_acq(&dst: state, src: state) -> state { + unsafe { + reinterpret_cast(rusti::atomic_xchng_acq( + *(ptr::mut_addr_of(dst) as *mut int), + src as int)) + } + } + + fn swap_state_rel(&dst: state, src: state) -> state { + unsafe { + reinterpret_cast(rusti::atomic_xchng_rel( + *(ptr::mut_addr_of(dst) as *mut int), + src as int)) + } + } + + fn send(-p: send_packet, -payload: T) { + let p = p.unwrap(); + let p = unsafe { uniquify(p) }; + assert (*p).payload == none; + (*p).payload <- some(payload); + let old_state = swap_state_rel((*p).state, full); + alt old_state { + empty { + // Yay, fastpath. -class send_packet { + // The receiver will eventually clean this up. + unsafe { forget(p); } + } + full { fail "duplicate send" } + blocked { + // FIXME: once the target will actually block, tell the + // scheduler to wake it up. + + // The receiver will eventually clean this up. + unsafe { forget(p); } + } + terminated { + // The receiver will never receive this. Rely on drop_glue + // to clean everything up. + } + } + } + + fn recv(-p: recv_packet) -> option { + let p = p.unwrap(); + let p = unsafe { uniquify(p) }; + loop { + let old_state = swap_state_acq((*p).state, + blocked); + alt old_state { + empty | blocked { task::yield(); } + full { + let mut payload = none; + payload <-> (*p).payload; + ret some(option::unwrap(payload)) + } + terminated { + assert old_state == terminated; + ret none; + } + } + } + } + + fn sender_terminate(p: *packet) { + let p = unsafe { uniquify(p) }; + alt swap_state_rel((*p).state, terminated) { + empty | blocked { + // The receiver will eventually clean up. + unsafe { forget(p) } + } + full { + // This is impossible + fail "you dun goofed" + } + terminated { + // I have to clean up, use drop_glue + } + } + } + + fn receiver_terminate(p: *packet) { + let p = unsafe { uniquify(p) }; + alt swap_state_rel((*p).state, terminated) { + empty { + // the sender will clean up + unsafe { forget(p) } + } + blocked { + // this shouldn't happen. + fail "terminating a blocked packet" + } + terminated | full { + // I have to clean up, use drop_glue + } + } + } + + class send_packet { let mut p: option<*packet>; new(p: *packet) { self.p = some(p); } drop { @@ -16,23 +150,128 @@ class send_packet { p <-> self.p; option::unwrap(p) } + } + + class recv_packet { + let mut p: option<*packet>; + new(p: *packet) { self.p = some(p); } + drop { + if self.p != none { + let mut p = none; + p <-> self.p; + receiver_terminate(option::unwrap(p)) + } + } + fn unwrap() -> *packet { + let mut p = none; + p <-> self.p; + option::unwrap(p) + } + } + + fn entangle() -> (send_packet, recv_packet) { + let p = packet(); + (send_packet(p), recv_packet(p)) + } } -enum state { - empty, - full, - blocked, - terminated +mod pingpong { + enum ping = pipes::send_packet; + enum pong = pipes::send_packet; + + fn liberate_ping(-p: ping) -> pipes::send_packet unsafe { + let addr : *pipes::send_packet = alt p { + ping(x) { unsafe::reinterpret_cast(ptr::addr_of(x)) } + }; + let liberated_value <- *addr; + unsafe::forget(p); + liberated_value + } + + fn liberate_pong(-p: pong) -> pipes::send_packet unsafe { + let addr : *pipes::send_packet = alt p { + pong(x) { unsafe::reinterpret_cast(ptr::addr_of(x)) } + }; + let liberated_value <- *addr; + unsafe::forget(p); + liberated_value + } + + fn init() -> (client::ping, server::ping) { + pipes::entangle() + } + + mod client { + type ping = pipes::send_packet; + type pong = pipes::recv_packet; + + fn do_ping(-c: ping) -> pong { + let (sp, rp) = pipes::entangle(); + + pipes::send(c, ping(sp)); + rp + } + + fn do_pong(-c: pong) -> (ping, ()) { + let packet = pipes::recv(c); + if packet == none { + fail "sender closed the connection" + } + (liberate_pong(option::unwrap(packet)), ()) + } + } + + mod server { + type ping = pipes::recv_packet; + type pong = pipes::send_packet; + + fn do_ping(-c: ping) -> (pong, ()) { + let packet = pipes::recv(c); + if packet == none { + fail "sender closed the connection" + } + (liberate_ping(option::unwrap(packet)), ()) + } + + fn do_pong(-c: pong) -> ping { + let (sp, rp) = pipes::entangle(); + pipes::send(c, pong(sp)); + rp + } + } } -type packet = { - mut state: state, - mut blocked_task: option, - mut payload: option -}; +fn client(-chan: pingpong::client::ping) { + let chan = pingpong::client::do_ping(chan); + log(error, "Sent ping"); + let (chan, _data) = pingpong::client::do_pong(chan); + log(error, "Received pong"); +} + +fn server(-chan: pingpong::server::ping) { + let (chan, _data) = pingpong::server::do_ping(chan); + log(error, "Received ping"); + let chan = pingpong::server::do_pong(chan); + log(error, "Sent pong"); +} fn main() { - let _s: send_packet = send_packet(ptr::addr_of({mut state: empty, - mut blocked_task: none, - mut payload: some(42)})); + /* +// Commented out because of option::get error + + let (client_, server_) = pingpong::init(); + let client_ = ~mut some(client_); + let server_ = ~mut some(server_); + + task::spawn {|move client_| + let mut client__ = none; + *client_ <-> client__; + client(option::unwrap(client__)); + }; + task::spawn {|move server_| + let mut server_ˊ = none; + *server_ <-> server_ˊ; + server(option::unwrap(server_ˊ)); + }; + */ }