about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPatrick Walton <pcwalton@mimiga.net>2013-04-18 15:53:29 -0700
committerPatrick Walton <pcwalton@mimiga.net>2013-04-19 12:00:08 -0700
commitc995a62d44ce8534d4f8bf36284419661b87e167 (patch)
treee262af9ba09eb89d97b631596c344f9f3620c17a
parent7720c15ae11141e2b1a2a3a560b1cdacb6ddada4 (diff)
downloadrust-c995a62d44ce8534d4f8bf36284419661b87e167.tar.gz
rust-c995a62d44ce8534d4f8bf36284419661b87e167.zip
librustc: WIP patch for using the return value.
-rw-r--r--src/libcore/core.rc2
-rw-r--r--src/libcore/hashmap.rs40
-rw-r--r--src/libcore/libc.rs4
-rw-r--r--src/libcore/num/int-template.rs2
-rw-r--r--src/libcore/num/uint-template.rs2
-rw-r--r--src/libcore/prelude.rs2
-rw-r--r--src/libcore/str.rs9
-rw-r--r--src/libcore/vec.rs27
-rw-r--r--src/librustc/back/link.rs6
-rw-r--r--src/librustc/middle/trans/base.rs307
-rw-r--r--src/librustc/middle/trans/build.rs22
-rw-r--r--src/librustc/middle/trans/cabi.rs40
-rw-r--r--src/librustc/middle/trans/callee.rs77
-rw-r--r--src/librustc/middle/trans/closure.rs18
-rw-r--r--src/librustc/middle/trans/common.rs16
-rw-r--r--src/librustc/middle/trans/controlflow.rs6
-rw-r--r--src/librustc/middle/trans/datum.rs2
-rw-r--r--src/librustc/middle/trans/expr.rs62
-rw-r--r--src/librustc/middle/trans/foreign.rs445
-rw-r--r--src/librustc/middle/trans/glue.rs22
-rw-r--r--src/librustc/middle/trans/type_of.rs32
-rw-r--r--src/libstd/net_tcp.rs29
-rw-r--r--src/libstd/uv_ll.rs3
-rw-r--r--src/rt/rust_uv.cpp3
-rw-r--r--src/test/run-pass/const-bound.rs4
-rw-r--r--src/test/run-pass/extern-call.rs2
26 files changed, 778 insertions, 406 deletions
diff --git a/src/libcore/core.rc b/src/libcore/core.rc
index 4080c70c8ae..81190ea8fc6 100644
--- a/src/libcore/core.rc
+++ b/src/libcore/core.rc
@@ -95,7 +95,7 @@ pub use str::{StrSlice};
 pub use container::{Container, Mutable};
 pub use vec::{CopyableVector, ImmutableVector};
 pub use vec::{ImmutableEqVector, ImmutableCopyableVector};
-pub use vec::{OwnedVector, OwnedCopyableVector};
+pub use vec::{OwnedVector, OwnedCopyableVector, MutableVector};
 pub use iter::{BaseIter, ExtendedIter, EqIter, CopyableIter};
 pub use iter::{CopyableOrderedIter, CopyableNonstrictIter, Times};
 pub use iter::{ExtendedMutableIter};
diff --git a/src/libcore/hashmap.rs b/src/libcore/hashmap.rs
index 3efe21fc42c..1d7cc8515a6 100644
--- a/src/libcore/hashmap.rs
+++ b/src/libcore/hashmap.rs
@@ -16,7 +16,6 @@
 use container::{Container, Mutable, Map, Set};
 use cmp::{Eq, Equiv};
 use hash::Hash;
-use to_bytes::IterBytes;
 use iter::BaseIter;
 use hash::Hash;
 use iter;
@@ -72,7 +71,7 @@ fn linear_map_with_capacity_and_keys<K:Eq + Hash,V>(
     }
 }
 
-priv impl<K:Hash + IterBytes + Eq,V> HashMap<K, V> {
+priv impl<K:Hash + Eq,V> HashMap<K, V> {
     #[inline(always)]
     fn to_bucket(&self, h: uint) -> uint {
         // A good hash function with entropy spread over all of the
@@ -111,9 +110,8 @@ priv impl<K:Hash + IterBytes + Eq,V> HashMap<K, V> {
     }
 
     #[inline(always)]
-    fn bucket_for_key_equiv<Q:Hash + IterBytes + Equiv<K>>(&self,
-                                                           k: &Q)
-                                                        -> SearchResult {
+    fn bucket_for_key_equiv<Q:Hash + Equiv<K>>(&self, k: &Q)
+                                               -> SearchResult {
         let hash = k.hash_keyed(self.k0, self.k1) as uint;
         self.bucket_for_key_with_hash_equiv(hash, k)
     }
@@ -303,7 +301,7 @@ priv impl<K:Hash + IterBytes + Eq,V> HashMap<K, V> {
     }
 }
 
-impl<K:Hash + IterBytes + Eq,V> Container for HashMap<K, V> {
+impl<K:Hash + Eq,V> Container for HashMap<K, V> {
     /// Return the number of elements in the map
     fn len(&const self) -> uint { self.size }
 
@@ -311,7 +309,7 @@ impl<K:Hash + IterBytes + Eq,V> Container for HashMap<K, V> {
     fn is_empty(&const self) -> bool { self.len() == 0 }
 }
 
-impl<K:Hash + IterBytes + Eq,V> Mutable for HashMap<K, V> {
+impl<K:Hash + Eq,V> Mutable for HashMap<K, V> {
     /// Clear the map, removing all key-value pairs.
     fn clear(&mut self) {
         for uint::range(0, self.buckets.len()) |idx| {
@@ -321,7 +319,7 @@ impl<K:Hash + IterBytes + Eq,V> Mutable for HashMap<K, V> {
     }
 }
 
-impl<K:Hash + IterBytes + Eq,V> Map<K, V> for HashMap<K, V> {
+impl<K:Hash + Eq,V> Map<K, V> for HashMap<K, V> {
     /// Return true if the map contains a value for the specified key
     fn contains_key(&self, k: &K) -> bool {
         match self.bucket_for_key(k) {
@@ -458,7 +456,7 @@ impl<K:Hash + IterBytes + Eq,V> Map<K, V> for HashMap<K, V> {
     }
 }
 
-pub impl<K: Hash + IterBytes + Eq, V> HashMap<K, V> {
+pub impl<K: Hash + Eq, V> HashMap<K, V> {
     /// Create an empty HashMap
     fn new() -> HashMap<K, V> {
         HashMap::with_capacity(INITIAL_CAPACITY)
@@ -669,8 +667,7 @@ pub impl<K: Hash + IterBytes + Eq, V> HashMap<K, V> {
 
     /// Return true if the map contains a value for the specified key,
     /// using equivalence
-    fn contains_key_equiv<Q:Hash + IterBytes + Equiv<K>>(&self, key: &Q)
-                                                      -> bool {
+    fn contains_key_equiv<Q:Hash + Equiv<K>>(&self, key: &Q) -> bool {
         match self.bucket_for_key_equiv(key) {
             FoundEntry(_) => {true}
             TableFull | FoundHole(_) => {false}
@@ -680,8 +677,7 @@ pub impl<K: Hash + IterBytes + Eq, V> HashMap<K, V> {
     /// Return the value corresponding to the key in the map, using
     /// equivalence
     #[cfg(stage0)]
-    fn find_equiv<Q:Hash + IterBytes + Equiv<K>>(&self, k: &Q)
-                                              -> Option<&'self V> {
+    fn find_equiv<Q:Hash + Equiv<K>>(&self, k: &Q) -> Option<&'self V> {
         match self.bucket_for_key_equiv(k) {
             FoundEntry(idx) => Some(self.value_for_bucket(idx)),
             TableFull | FoundHole(_) => None,
@@ -693,9 +689,7 @@ pub impl<K: Hash + IterBytes + Eq, V> HashMap<K, V> {
     #[cfg(stage1)]
     #[cfg(stage2)]
     #[cfg(stage3)]
-    fn find_equiv<'a, Q:Hash + IterBytes + Equiv<K>>(
-        &'a self, k: &Q) -> Option<&'a V>
-    {
+    fn find_equiv<'a, Q:Hash + Equiv<K>>(&'a self, k: &Q) -> Option<&'a V> {
         match self.bucket_for_key_equiv(k) {
             FoundEntry(idx) => Some(self.value_for_bucket(idx)),
             TableFull | FoundHole(_) => None,
@@ -703,7 +697,7 @@ pub impl<K: Hash + IterBytes + Eq, V> HashMap<K, V> {
     }
 }
 
-impl<K:Hash + IterBytes + Eq,V:Eq> Eq for HashMap<K, V> {
+impl<K:Hash + Eq,V:Eq> Eq for HashMap<K, V> {
     fn eq(&self, other: &HashMap<K, V>) -> bool {
         if self.len() != other.len() { return false; }
 
@@ -724,18 +718,18 @@ pub struct HashSet<T> {
     priv map: HashMap<T, ()>
 }
 
-impl<T:Hash + IterBytes + Eq> BaseIter<T> for HashSet<T> {
+impl<T:Hash + Eq> BaseIter<T> for HashSet<T> {
     /// Visit all values in order
     fn each(&self, f: &fn(&T) -> bool) { self.map.each_key(f) }
     fn size_hint(&self) -> Option<uint> { Some(self.len()) }
 }
 
-impl<T:Hash + IterBytes + Eq> Eq for HashSet<T> {
+impl<T:Hash + Eq> Eq for HashSet<T> {
     fn eq(&self, other: &HashSet<T>) -> bool { self.map == other.map }
     fn ne(&self, other: &HashSet<T>) -> bool { self.map != other.map }
 }
 
-impl<T:Hash + IterBytes + Eq> Container for HashSet<T> {
+impl<T:Hash + Eq> Container for HashSet<T> {
     /// Return the number of elements in the set
     fn len(&const self) -> uint { self.map.len() }
 
@@ -743,12 +737,12 @@ impl<T:Hash + IterBytes + Eq> Container for HashSet<T> {
     fn is_empty(&const self) -> bool { self.map.is_empty() }
 }
 
-impl<T:Hash + IterBytes + Eq> Mutable for HashSet<T> {
+impl<T:Hash + Eq> Mutable for HashSet<T> {
     /// Clear the set, removing all values.
     fn clear(&mut self) { self.map.clear() }
 }
 
-impl<T:Hash + IterBytes + Eq> Set<T> for HashSet<T> {
+impl<T:Hash + Eq> Set<T> for HashSet<T> {
     /// Return true if the set contains a value
     fn contains(&self, value: &T) -> bool { self.map.contains_key(value) }
 
@@ -816,7 +810,7 @@ impl<T:Hash + IterBytes + Eq> Set<T> for HashSet<T> {
     }
 }
 
-pub impl <T:Hash + IterBytes + Eq> HashSet<T> {
+pub impl <T:Hash + Eq> HashSet<T> {
     /// Create an empty HashSet
     fn new() -> HashSet<T> {
         HashSet::with_capacity(INITIAL_CAPACITY)
diff --git a/src/libcore/libc.rs b/src/libcore/libc.rs
index 9d100d1d352..945d08323b4 100644
--- a/src/libcore/libc.rs
+++ b/src/libcore/libc.rs
@@ -1097,9 +1097,12 @@ pub mod funcs {
                 unsafe fn setbuf(stream: *FILE, buf: *c_char);
                 // Omitted: printf and scanf variants.
                 unsafe fn fgetc(stream: *FILE) -> c_int;
+                #[fast_ffi]
                 unsafe fn fgets(buf: *mut c_char, n: c_int,
                          stream: *FILE) -> *c_char;
+                #[fast_ffi]
                 unsafe fn fputc(c: c_int, stream: *FILE) -> c_int;
+                #[fast_ffi]
                 unsafe fn fputs(s: *c_char, stream: *FILE) -> *c_char;
                 // Omitted: getc, getchar (might be macros).
 
@@ -1263,6 +1266,7 @@ pub mod funcs {
                 unsafe fn pclose(stream: *FILE) -> c_int;
 
                 #[link_name = "_fdopen"]
+                #[fast_ffi]
                 unsafe fn fdopen(fd: c_int, mode: *c_char) -> *FILE;
 
                 #[link_name = "_fileno"]
diff --git a/src/libcore/num/int-template.rs b/src/libcore/num/int-template.rs
index 8fd61fb6187..e170d85cc71 100644
--- a/src/libcore/num/int-template.rs
+++ b/src/libcore/num/int-template.rs
@@ -503,4 +503,4 @@ mod tests {
     fn test_range_step_zero_step() {
         for range_step(0,10,0) |_i| {}
     }
-}
\ No newline at end of file
+}
diff --git a/src/libcore/num/uint-template.rs b/src/libcore/num/uint-template.rs
index 0109c915c60..0fb6ea614d8 100644
--- a/src/libcore/num/uint-template.rs
+++ b/src/libcore/num/uint-template.rs
@@ -474,4 +474,4 @@ mod tests {
     fn test_range_step_zero_step_down() {
         for range_step(0,-10,0) |_i| {}
     }
-}
\ No newline at end of file
+}
diff --git a/src/libcore/prelude.rs b/src/libcore/prelude.rs
index 95481d032a4..822fb2e476b 100644
--- a/src/libcore/prelude.rs
+++ b/src/libcore/prelude.rs
@@ -46,7 +46,7 @@ pub use to_str::ToStr;
 pub use tuple::{CopyableTuple, ImmutableTuple, ExtendedTupleOps};
 pub use vec::{CopyableVector, ImmutableVector};
 pub use vec::{ImmutableEqVector, ImmutableCopyableVector};
-pub use vec::{OwnedVector, OwnedCopyableVector};
+pub use vec::{OwnedVector, OwnedCopyableVector, MutableVector};
 pub use io::{Reader, ReaderUtil, Writer, WriterUtil};
 
 /* Reexported runtime types */
diff --git a/src/libcore/str.rs b/src/libcore/str.rs
index 6bde7d33849..837f9c1a9ad 100644
--- a/src/libcore/str.rs
+++ b/src/libcore/str.rs
@@ -430,6 +430,15 @@ pub fn byte_slice<T>(s: &str, f: &fn(v: &[u8]) -> T) -> T {
     }
 }
 
+/// Work with the string as a byte slice, not including trailing null, without
+/// a callback.
+#[inline(always)]
+pub fn byte_slice_no_callback<'a>(s: &'a str) -> &'a [u8] {
+    unsafe {
+        cast::transmute(s)
+    }
+}
+
 /// Convert a string to a unique vector of characters
 pub fn to_chars(s: &str) -> ~[char] {
     let mut buf = ~[];
diff --git a/src/libcore/vec.rs b/src/libcore/vec.rs
index 8934b6cab67..0d612369cc5 100644
--- a/src/libcore/vec.rs
+++ b/src/libcore/vec.rs
@@ -76,6 +76,7 @@ pub fn same_length<T, U>(xs: &const [T], ys: &const [U]) -> bool {
  * * v - A vector
  * * n - The number of elements to reserve space for
  */
+#[inline]
 pub fn reserve<T>(v: &mut ~[T], n: uint) {
     // Only make the (slow) call into the runtime if we have to
     use managed;
@@ -1831,6 +1832,7 @@ pub trait ImmutableVector<T> {
     fn alli(&self, f: &fn(uint, t: &T) -> bool) -> bool;
     fn flat_map<U>(&self, f: &fn(t: &T) -> ~[U]) -> ~[U];
     fn filter_mapped<U:Copy>(&self, f: &fn(t: &T) -> Option<U>) -> ~[U];
+    unsafe fn unsafe_ref(&self, index: uint) -> *T;
 }
 
 /// Extension methods for vectors
@@ -1941,6 +1943,14 @@ impl<'self,T> ImmutableVector<T> for &'self [T] {
     fn filter_mapped<U:Copy>(&self, f: &fn(t: &T) -> Option<U>) -> ~[U] {
         filter_mapped(*self, f)
     }
+
+    /// Returns a pointer to the element at the given index, without doing
+    /// bounds checking.
+    #[inline(always)]
+    unsafe fn unsafe_ref(&self, index: uint) -> *T {
+        let (ptr, _): (*T, uint) = transmute(*self);
+        ptr.offset(index)
+    }
 }
 
 #[cfg(stage1)]
@@ -2178,9 +2188,8 @@ impl<'self,T:Copy> ImmutableCopyableVector<T> for &'self [T] {
 
     /// Returns the element at the given index, without doing bounds checking.
     #[inline(always)]
-    unsafe fn unsafe_get(&self, elem: uint) -> T {
-        let (ptr, _): (*T, uint) = transmute(*self);
-        *ptr.offset(elem)
+    unsafe fn unsafe_get(&self, index: uint) -> T {
+        *self.unsafe_ref(index)
     }
 }
 
@@ -2323,15 +2332,21 @@ impl<T:Eq> OwnedEqVector<T> for ~[T] {
 }
 
 pub trait MutableVector<T> {
-    unsafe fn unsafe_set(&self, elem: uint, val: T);
+    unsafe fn unsafe_mut_ref(&self, index: uint) -> *mut T;
+    unsafe fn unsafe_set(&self, index: uint, val: T);
 }
 
 impl<'self,T> MutableVector<T> for &'self mut [T] {
     #[inline(always)]
-    unsafe fn unsafe_set(&self, elem: uint, val: T) {
+    unsafe fn unsafe_mut_ref(&self, index: uint) -> *mut T {
         let pair_ptr: &(*mut T, uint) = transmute(self);
         let (ptr, _) = *pair_ptr;
-        *ptr.offset(elem) = val;
+        ptr.offset(index)
+    }
+
+    #[inline(always)]
+    unsafe fn unsafe_set(&self, index: uint, val: T) {
+        *self.unsafe_mut_ref(index) = val;
     }
 }
 
diff --git a/src/librustc/back/link.rs b/src/librustc/back/link.rs
index 8794dae1178..eb7965e1ac6 100644
--- a/src/librustc/back/link.rs
+++ b/src/librustc/back/link.rs
@@ -188,8 +188,10 @@ pub mod write {
         return false;
     }
 
-    pub fn run_passes(sess: Session, llmod: ModuleRef,
-            output_type: output_type, output: &Path) {
+    pub fn run_passes(sess: Session,
+                      llmod: ModuleRef,
+                      output_type: output_type,
+                      output: &Path) {
         unsafe {
             let opts = sess.opts;
             if sess.time_llvm_passes() { llvm::LLVMRustEnableTimePasses(); }
diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs
index 46813974af1..e897b4e1047 100644
--- a/src/librustc/middle/trans/base.rs
+++ b/src/librustc/middle/trans/base.rs
@@ -136,13 +136,17 @@ pub fn log_fn_time(ccx: @CrateContext, +name: ~str, start: time::Timespec,
     ccx.stats.fn_times.push((name, elapsed));
 }
 
-pub fn decl_fn(llmod: ModuleRef, name: &str, cc: lib::llvm::CallConv,
-               llty: TypeRef) -> ValueRef {
+pub fn decl_fn(llmod: ModuleRef,
+               name: &str,
+               cc: lib::llvm::CallConv,
+               llty: TypeRef)
+               -> ValueRef {
     let llfn: ValueRef = str::as_c_str(name, |buf| {
         unsafe {
             llvm::LLVMGetOrInsertFunction(llmod, buf, llty)
         }
     });
+
     lib::llvm::SetFunctionCallConv(llfn, cc);
     return llfn;
 }
@@ -478,17 +482,25 @@ pub fn note_unique_llvm_symbol(ccx: @CrateContext, sym: @~str) {
 }
 
 
-pub fn get_res_dtor(ccx: @CrateContext, did: ast::def_id,
-                    parent_id: ast::def_id, substs: &[ty::t])
-   -> ValueRef {
+pub fn get_res_dtor(ccx: @CrateContext,
+                    did: ast::def_id,
+                    parent_id: ast::def_id,
+                    substs: &[ty::t])
+                 -> ValueRef {
     let _icx = ccx.insn_ctxt("trans_res_dtor");
     if !substs.is_empty() {
         let did = if did.crate != ast::local_crate {
             inline::maybe_instantiate_inline(ccx, did, true)
-        } else { did };
+        } else {
+            did
+        };
         assert!(did.crate == ast::local_crate);
-        let (val, _) =
-            monomorphize::monomorphic_fn(ccx, did, substs, None, None, None);
+        let (val, _) = monomorphize::monomorphic_fn(ccx,
+                                                    did,
+                                                    substs,
+                                                    None,
+                                                    None,
+                                                    None);
 
         val
     } else if did.crate == ast::local_crate {
@@ -496,11 +508,16 @@ pub fn get_res_dtor(ccx: @CrateContext, did: ast::def_id,
     } else {
         let tcx = ccx.tcx;
         let name = csearch::get_symbol(ccx.sess.cstore, did);
-        let class_ty = ty::subst_tps(tcx, substs, None,
-                          ty::lookup_item_type(tcx, parent_id).ty);
+        let class_ty = ty::subst_tps(tcx,
+                                     substs,
+                                     None,
+                                     ty::lookup_item_type(tcx, parent_id).ty);
         let llty = type_of_dtor(ccx, class_ty);
         let name = name.to_managed(); // :-(
-        get_extern_fn(ccx.externs, ccx.llmod, name, lib::llvm::CCallConv,
+        get_extern_fn(ccx.externs,
+                      ccx.llmod,
+                      name,
+                      lib::llvm::CCallConv,
                       llty)
     }
 }
@@ -804,9 +821,12 @@ pub fn trans_external_path(ccx: @CrateContext, did: ast::def_id, t: ty::t)
     };
 }
 
-pub fn invoke(bcx: block, llfn: ValueRef, +llargs: ~[ValueRef]) -> block {
+pub fn invoke(bcx: block, llfn: ValueRef, +llargs: ~[ValueRef])
+           -> (ValueRef, block) {
     let _icx = bcx.insn_ctxt("invoke_");
-    if bcx.unreachable { return bcx; }
+    if bcx.unreachable {
+        return (C_null(T_i8()), bcx);
+    }
 
     match bcx.node_info {
         None => debug!("invoke at ???"),
@@ -826,8 +846,12 @@ pub fn invoke(bcx: block, llfn: ValueRef, +llargs: ~[ValueRef]) -> block {
             }
         }
         let normal_bcx = sub_block(bcx, ~"normal return");
-        Invoke(bcx, llfn, llargs, normal_bcx.llbb, get_landing_pad(bcx));
-        return normal_bcx;
+        let llresult = Invoke(bcx,
+                              llfn,
+                              llargs,
+                              normal_bcx.llbb,
+                              get_landing_pad(bcx));
+        return (llresult, normal_bcx);
     } else {
         unsafe {
             debug!("calling %x at %x",
@@ -837,8 +861,8 @@ pub fn invoke(bcx: block, llfn: ValueRef, +llargs: ~[ValueRef]) -> block {
                 debug!("arg: %x", ::core::cast::transmute(llarg));
             }
         }
-        Call(bcx, llfn, llargs);
-        return bcx;
+        let llresult = Call(bcx, llfn, llargs);
+        return (llresult, bcx);
     }
 }
 
@@ -1568,6 +1592,18 @@ pub fn mk_standard_basic_blocks(llfn: ValueRef) -> BasicBlocks {
     }
 }
 
+// Creates and returns space for, or returns the argument representing, the
+// slot where the return value of the function must go.
+pub fn make_return_pointer(fcx: fn_ctxt, output_type: ty::t) -> ValueRef {
+    unsafe {
+        if !ty::type_is_immediate(output_type) {
+            llvm::LLVMGetParam(fcx.llfn, 0)
+        } else {
+            let lloutputtype = type_of::type_of(*fcx.ccx, output_type);
+            alloca(raw_block(fcx, false, fcx.llstaticallocas), lloutputtype)
+        }
+    }
+}
 
 // NB: must keep 4 fns in sync:
 //
@@ -1579,10 +1615,11 @@ pub fn new_fn_ctxt_w_id(ccx: @CrateContext,
                         +path: path,
                         llfndecl: ValueRef,
                         id: ast::node_id,
+                        output_type: ty::t,
                         impl_id: Option<ast::def_id>,
                         param_substs: Option<@param_substs>,
-                        sp: Option<span>) -> fn_ctxt
-{
+                        sp: Option<span>)
+                     -> fn_ctxt {
     for param_substs.each |p| { p.validate(); }
 
     debug!("new_fn_ctxt_w_id(path=%s, id=%?, impl_id=%?, \
@@ -1593,16 +1630,26 @@ pub fn new_fn_ctxt_w_id(ccx: @CrateContext,
            param_substs.repr(ccx.tcx));
 
     let llbbs = mk_standard_basic_blocks(llfndecl);
-    return @mut fn_ctxt_ {
+
+    let substd_output_type = match param_substs {
+        None => output_type,
+        Some(substs) => {
+            ty::subst_tps(ccx.tcx, substs.tys, substs.self_ty, output_type)
+        }
+    };
+    let is_immediate = ty::type_is_immediate(substd_output_type);
+
+    let fcx = @mut fn_ctxt_ {
           llfn: llfndecl,
           llenv: unsafe { llvm::LLVMGetParam(llfndecl, 1u as c_uint) },
-          llretptr: unsafe { llvm::LLVMGetParam(llfndecl, 0u as c_uint) },
+          llretptr: None,
           llstaticallocas: llbbs.sa,
           llloadenv: None,
           llreturn: llbbs.rt,
           llself: None,
           personality: None,
           loop_ret: None,
+          has_immediate_return_value: is_immediate,
           llargs: @mut HashMap::new(),
           lllocals: @mut HashMap::new(),
           llupvars: @mut HashMap::new(),
@@ -1613,14 +1660,18 @@ pub fn new_fn_ctxt_w_id(ccx: @CrateContext,
           path: path,
           ccx: @ccx
     };
+
+    fcx.llretptr = Some(make_return_pointer(fcx, substd_output_type));
+    fcx
 }
 
 pub fn new_fn_ctxt(ccx: @CrateContext,
                    +path: path,
                    llfndecl: ValueRef,
+                   output_type: ty::t,
                    sp: Option<span>)
                 -> fn_ctxt {
-    return new_fn_ctxt_w_id(ccx, path, llfndecl, -1, None, None, sp);
+    new_fn_ctxt_w_id(ccx, path, llfndecl, -1, output_type, None, None, sp)
 }
 
 // NB: must keep 4 fns in sync:
@@ -1639,7 +1690,8 @@ pub fn new_fn_ctxt(ccx: @CrateContext,
 // field of the fn_ctxt with
 pub fn create_llargs_for_fn_args(cx: fn_ctxt,
                                  ty_self: self_arg,
-                                 args: &[ast::arg]) -> ~[ValueRef] {
+                                 args: &[ast::arg])
+                              -> ~[ValueRef] {
     let _icx = cx.insn_ctxt("create_llargs_for_fn_args");
 
     match ty_self {
@@ -1745,8 +1797,19 @@ pub fn copy_args_to_allocas(fcx: fn_ctxt,
 pub fn finish_fn(fcx: fn_ctxt, lltop: BasicBlockRef) {
     let _icx = fcx.insn_ctxt("finish_fn");
     tie_up_header_blocks(fcx, lltop);
+    build_return_block(fcx);
+}
+
+// Builds the return block for a function.
+pub fn build_return_block(fcx: fn_ctxt) {
     let ret_cx = raw_block(fcx, false, fcx.llreturn);
-    RetVoid(ret_cx);
+
+    // Return the value if this function immediate; otherwise, return void.
+    if fcx.has_immediate_return_value {
+        Ret(ret_cx, Load(ret_cx, fcx.llretptr.get()))
+    } else {
+        RetVoid(ret_cx)
+    }
 }
 
 pub fn tie_up_header_blocks(fcx: fn_ctxt, lltop: BasicBlockRef) {
@@ -1777,6 +1840,7 @@ pub fn trans_closure(ccx: @CrateContext,
                      id: ast::node_id,
                      impl_id: Option<ast::def_id>,
                      attributes: &[ast::attribute],
+                     output_type: ty::t,
                      maybe_load_env: &fn(fn_ctxt),
                      finish: &fn(block)) {
     ccx.stats.n_closures += 1;
@@ -1791,6 +1855,7 @@ pub fn trans_closure(ccx: @CrateContext,
                                path,
                                llfndecl,
                                id,
+                               output_type,
                                impl_id,
                                param_substs,
                                Some(body.span));
@@ -1833,7 +1898,8 @@ pub fn trans_closure(ccx: @CrateContext,
     {
         bcx = controlflow::trans_block(bcx, body, expr::Ignore);
     } else {
-        bcx = controlflow::trans_block(bcx, body, expr::SaveIn(fcx.llretptr));
+        let dest = expr::SaveIn(fcx.llretptr.get());
+        bcx = controlflow::trans_block(bcx, body, dest);
     }
 
     finish(bcx);
@@ -1864,6 +1930,7 @@ pub fn trans_fn(ccx: @CrateContext,
     let _icx = ccx.insn_ctxt("trans_fn");
     ccx.stats.n_fns += 1;
     let the_path_str = path_str(ccx.sess, path);
+    let output_type = ty::ty_fn_ret(ty::node_id_to_type(ccx.tcx, id));
     trans_closure(ccx,
                   path,
                   decl,
@@ -1874,6 +1941,7 @@ pub fn trans_fn(ccx: @CrateContext,
                   id,
                   impl_id,
                   attrs,
+                  output_type,
                   |fcx| {
                       if ccx.sess.opts.extra_debuginfo {
                           debuginfo::create_function(fcx);
@@ -1907,26 +1975,39 @@ pub fn trans_enum_variant(ccx: @CrateContext,
             id: varg.id,
         }
     };
-    let fcx = new_fn_ctxt_w_id(ccx, ~[], llfndecl, variant.node.id, None,
-                               param_substs, None);
-    let raw_llargs = create_llargs_for_fn_args(fcx, no_self, fn_args);
+
     let ty_param_substs = match param_substs {
         Some(ref substs) => { copy substs.tys }
         None => ~[]
     };
+    let enum_ty = ty::subst_tps(ccx.tcx,
+                                ty_param_substs,
+                                None,
+                                ty::node_id_to_type(ccx.tcx, enum_id));
+    let fcx = new_fn_ctxt_w_id(ccx,
+                               ~[],
+                               llfndecl,
+                               variant.node.id,
+                               enum_ty,
+                               None,
+                               param_substs,
+                               None);
+
+    let raw_llargs = create_llargs_for_fn_args(fcx, no_self, fn_args);
     let bcx = top_scope_block(fcx, None), lltop = bcx.llbb;
     let arg_tys = ty::ty_fn_args(node_id_type(bcx, variant.node.id));
     let bcx = copy_args_to_allocas(fcx, bcx, fn_args, raw_llargs, arg_tys);
 
     // XXX is there a better way to reconstruct the ty::t?
-    let enum_ty = ty::subst_tps(ccx.tcx, ty_param_substs, None,
-                                ty::node_id_to_type(ccx.tcx, enum_id));
     let repr = adt::represent_type(ccx, enum_ty);
 
-    adt::trans_start_init(bcx, repr, fcx.llretptr, disr);
+    adt::trans_start_init(bcx, repr, fcx.llretptr.get(), disr);
     for vec::eachi(args) |i, va| {
-        let lldestptr = adt::trans_field_ptr(bcx, repr, fcx.llretptr,
-                                             disr, i);
+        let lldestptr = adt::trans_field_ptr(bcx,
+                                             repr,
+                                             fcx.llretptr.get(),
+                                             disr,
+                                             i);
 
         // If this argument to this function is a enum, it'll have come in to
         // this function as an opaque blob due to the way that type_of()
@@ -1964,10 +2045,25 @@ pub fn trans_tuple_struct(ccx: @CrateContext,
         }
     };
 
+    // XXX is there a better way to reconstruct the ty::t?
+    let ty_param_substs = match param_substs {
+        Some(ref substs) => { copy substs.tys }
+        None => ~[]
+    };
+    let ctor_ty = ty::subst_tps(ccx.tcx, ty_param_substs, None,
+                                ty::node_id_to_type(ccx.tcx, ctor_id));
+    let tup_ty = match ty::get(ctor_ty).sty {
+        ty::ty_bare_fn(ref bft) => bft.sig.output,
+        _ => ccx.sess.bug(fmt!("trans_tuple_struct: unexpected ctor \
+                                return type %s",
+                               ty_to_str(ccx.tcx, ctor_ty)))
+    };
+
     let fcx = new_fn_ctxt_w_id(ccx,
                                ~[],
                                llfndecl,
                                ctor_id,
+                               tup_ty,
                                None,
                                param_substs,
                                None);
@@ -1979,23 +2075,14 @@ pub fn trans_tuple_struct(ccx: @CrateContext,
     let arg_tys = ty::ty_fn_args(node_id_type(bcx, ctor_id));
     let bcx = copy_args_to_allocas(fcx, bcx, fn_args, raw_llargs, arg_tys);
 
-    // XXX is there a better way to reconstruct the ty::t?
-    let ty_param_substs = match param_substs {
-        Some(ref substs) => { copy substs.tys }
-        None => ~[]
-    };
-    let ctor_ty = ty::subst_tps(ccx.tcx, ty_param_substs, None,
-                                ty::node_id_to_type(ccx.tcx, ctor_id));
-    let tup_ty = match ty::get(ctor_ty).sty {
-        ty::ty_bare_fn(ref bft) => bft.sig.output,
-        _ => ccx.sess.bug(fmt!("trans_tuple_struct: unexpected ctor \
-                                return type %s",
-                               ty_to_str(ccx.tcx, ctor_ty)))
-    };
     let repr = adt::represent_type(ccx, tup_ty);
 
     for fields.eachi |i, field| {
-        let lldestptr = adt::trans_field_ptr(bcx, repr, fcx.llretptr, 0, i);
+        let lldestptr = adt::trans_field_ptr(bcx,
+                                             repr,
+                                             fcx.llretptr.get(),
+                                             0,
+                                             i);
         let llarg = match *fcx.llargs.get(&field.node.id) {
             local_mem(x) => x,
             _ => {
@@ -2095,10 +2182,12 @@ pub fn trans_item(ccx: @CrateContext, item: ast::item) {
         if purity == ast::extern_fn  {
             let llfndecl = get_item_val(ccx, item.id);
             foreign::trans_foreign_fn(ccx,
-                                     vec::append(
-                                         /*bad*/copy *path,
-                                         ~[path_name(item.ident)]),
-                                      decl, body, llfndecl, item.id);
+                                      vec::append(/*bad*/copy *path,
+                                                  ~[path_name(item.ident)]),
+                                      decl,
+                                      body,
+                                      llfndecl,
+                                      item.id);
         } else if !generics.is_type_parameterized() {
             let llfndecl = get_item_val(ccx, item.id);
             trans_fn(ccx,
@@ -2215,7 +2304,7 @@ pub fn register_fn_fuller(ccx: @CrateContext,
                           node_type: ty::t,
                           cc: lib::llvm::CallConv,
                           llfty: TypeRef)
-                       -> ValueRef {
+                          -> ValueRef {
     debug!("register_fn_fuller creating fn for item %d with path %s",
            node_id,
            ast_map::path_to_str(path, ccx.sess.parse_sess.interner));
@@ -2235,7 +2324,9 @@ pub fn register_fn_fuller(ccx: @CrateContext,
                      (!*ccx.sess.building_library ||
                       (*ccx.sess.building_library &&
                        ccx.sess.targ_cfg.os == session::os_android));
-    if is_entry { create_entry_wrapper(ccx, sp, llfn); }
+    if is_entry {
+        create_entry_wrapper(ccx, sp, llfn);
+    }
     llfn
 }
 
@@ -2264,23 +2355,26 @@ pub fn create_entry_wrapper(ccx: @CrateContext,
         let llfdecl = decl_fn(ccx.llmod, ~"_rust_main",
                               lib::llvm::CCallConv, llfty);
 
-        let fcx = new_fn_ctxt(ccx, ~[], llfdecl, None);
+        let fcx = new_fn_ctxt(ccx, ~[], llfdecl, nt, None);
 
         let bcx = top_scope_block(fcx, None);
         let lltop = bcx.llbb;
 
         // Call main.
-        let lloutputarg = unsafe { llvm::LLVMGetParam(llfdecl, 0 as c_uint) };
+        let lloutputarg = C_null(T_ptr(T_i8()));
         let llenvarg = unsafe { llvm::LLVMGetParam(llfdecl, 1 as c_uint) };
         let mut args = ~[lloutputarg, llenvarg];
-        Call(bcx, main_llfn, args);
+        let llresult = Call(bcx, main_llfn, args);
+        Store(bcx, llresult, fcx.llretptr.get());
 
         build_return(bcx);
         finish_fn(fcx, lltop);
         return llfdecl;
     }
 
-    fn create_entry_fn(ccx: @CrateContext, rust_main: ValueRef, use_start_lang_item:bool) {
+    fn create_entry_fn(ccx: @CrateContext,
+                       rust_main: ValueRef,
+                       use_start_lang_item: bool) {
         let llfty = T_fn(~[ccx.int_type, T_ptr(T_ptr(T_i8()))], ccx.int_type);
 
         // FIXME #4404 android JNI hacks
@@ -2301,58 +2395,70 @@ pub fn create_entry_wrapper(ccx: @CrateContext,
         let bld = ccx.builder.B;
         unsafe {
             llvm::LLVMPositionBuilderAtEnd(bld, llbb);
-        }
-
-        let retptr = unsafe {
-            llvm::LLVMBuildAlloca(bld, ccx.int_type, noname())
-        };
 
-        let crate_map = ccx.crate_map;
-        let opaque_crate_map = unsafe {llvm::LLVMBuildPointerCast(
-                bld, crate_map, T_ptr(T_i8()), noname())};
-
-        let (start_fn, args) = if use_start_lang_item {
+            let crate_map = ccx.crate_map;
             let start_def_id = ccx.tcx.lang_items.start_fn();
             let start_fn = if start_def_id.crate == ast::local_crate {
                 ccx.sess.bug(~"start lang item is never in the local crate")
             } else {
                 let start_fn_type = csearch::get_type(ccx.tcx,
-                        start_def_id).ty;
+                                                      start_def_id).ty;
                 trans_external_path(ccx, start_def_id, start_fn_type)
             };
 
-            let args = unsafe {
-                let opaque_rust_main = llvm::LLVMBuildPointerCast(
-                        bld, rust_main, T_ptr(T_i8()), noname());
-
-                ~[
-                    retptr,
-                    C_null(T_opaque_box_ptr(ccx)),
-                    opaque_rust_main,
-                    llvm::LLVMGetParam(llfn, 0 as c_uint),
-                    llvm::LLVMGetParam(llfn, 1 as c_uint),
-                    opaque_crate_map
-                 ]
-            };
-            (start_fn, args)
-        } else {
-            debug!("using user-defined start fn");
-            let args = unsafe {
-                ~[ retptr,
-                   C_null(T_opaque_box_ptr(ccx)),
-                   llvm::LLVMGetParam(llfn, 0 as c_uint),
-                   llvm::LLVMGetParam(llfn, 1 as c_uint),
-                   opaque_crate_map
-                ]
-            };
+            let retptr = llvm::LLVMBuildAlloca(bld, T_i8(), noname());
 
-            (rust_main, args)
-        };
+            let crate_map = ccx.crate_map;
+            let opaque_crate_map = llvm::LLVMBuildPointerCast(bld,
+                                                              crate_map,
+                                                              T_ptr(T_i8()),
+                                                              noname());
 
-        unsafe {
-            llvm::LLVMBuildCall(bld, start_fn, vec::raw::to_ptr(args),
-                                args.len() as c_uint, noname());
-            let result = llvm::LLVMBuildLoad(bld, retptr, noname());
+            let (start_fn, args) = if use_start_lang_item {
+                let start_def_id = ccx.tcx.lang_items.start_fn();
+                let start_fn = if start_def_id.crate == ast::local_crate {
+                    ccx.sess.bug(~"start lang item is never in the local \
+                                   crate")
+                } else {
+                    let start_fn_type = csearch::get_type(ccx.tcx,
+                            start_def_id).ty;
+                    trans_external_path(ccx, start_def_id, start_fn_type)
+                };
+
+                let args = {
+                    let opaque_rust_main = llvm::LLVMBuildPointerCast(
+                            bld, rust_main, T_ptr(T_i8()), noname());
+
+                    ~[
+                        retptr,
+                        C_null(T_opaque_box_ptr(ccx)),
+                        opaque_rust_main,
+                        llvm::LLVMGetParam(llfn, 0),
+                        llvm::LLVMGetParam(llfn, 1),
+                        opaque_crate_map
+                     ]
+                };
+                (start_fn, args)
+            } else {
+                debug!("using user-defined start fn");
+                let args = {
+                    ~[
+                        retptr,
+                        C_null(T_opaque_box_ptr(ccx)),
+                        llvm::LLVMGetParam(llfn, 0 as c_uint),
+                        llvm::LLVMGetParam(llfn, 1 as c_uint),
+                        opaque_crate_map
+                    ]
+                };
+
+                (rust_main, args)
+            };
+
+            let result = llvm::LLVMBuildCall(bld,
+                                             start_fn,
+                                             &args[0],
+                                             args.len() as c_uint,
+                                             noname());
             llvm::LLVMBuildRet(bld, result);
         }
     }
@@ -2423,7 +2529,6 @@ pub fn get_item_val(ccx: @CrateContext, id: ast::node_id) -> ValueRef {
     match ccx.item_vals.find(&id) {
       Some(&v) => v,
       None => {
-
         let mut exprt = false;
         let val = match *ccx.tcx.items.get(&id) {
           ast_map::node_item(i, pth) => {
@@ -2515,10 +2620,10 @@ pub fn get_item_val(ccx: @CrateContext, id: ast::node_id) -> ValueRef {
             assert!(!ty::type_has_params(class_ty));
             let lldty = unsafe {
                 T_fn(~[
-                    T_ptr(type_of(ccx, ty::mk_nil(tcx))),
+                    T_ptr(T_i8()),
                     T_ptr(type_of(ccx, class_ty))
                 ],
-                llvm::LLVMVoidType())
+                T_nil())
             };
             let s = get_dtor_symbol(ccx, /*bad*/copy *pt, dt.node.id, None);
 
diff --git a/src/librustc/middle/trans/build.rs b/src/librustc/middle/trans/build.rs
index d6c045bb115..fe2461632ad 100644
--- a/src/librustc/middle/trans/build.rs
+++ b/src/librustc/middle/trans/build.rs
@@ -181,9 +181,15 @@ pub fn noname() -> *libc::c_char {
     }
 }
 
-pub fn Invoke(cx: block, Fn: ValueRef, Args: &[ValueRef],
-              Then: BasicBlockRef, Catch: BasicBlockRef) {
-    if cx.unreachable { return; }
+pub fn Invoke(cx: block,
+              Fn: ValueRef,
+              Args: &[ValueRef],
+              Then: BasicBlockRef,
+              Catch: BasicBlockRef)
+           -> ValueRef {
+    if cx.unreachable {
+        return C_null(T_i8());
+    }
     check_not_terminated(cx);
     terminate(cx, "Invoke");
     debug!("Invoke(%s with arguments (%s))",
@@ -193,9 +199,13 @@ pub fn Invoke(cx: block, Fn: ValueRef, Args: &[ValueRef],
                         ~", "));
     unsafe {
         count_insn(cx, "invoke");
-        llvm::LLVMBuildInvoke(B(cx), Fn, vec::raw::to_ptr(Args),
-                              Args.len() as c_uint, Then, Catch,
-                              noname());
+        llvm::LLVMBuildInvoke(B(cx),
+                              Fn,
+                              vec::raw::to_ptr(Args),
+                              Args.len() as c_uint,
+                              Then,
+                              Catch,
+                              noname())
     }
 }
 
diff --git a/src/librustc/middle/trans/cabi.rs b/src/librustc/middle/trans/cabi.rs
index 60b502873e2..41b812c8e14 100644
--- a/src/librustc/middle/trans/cabi.rs
+++ b/src/librustc/middle/trans/cabi.rs
@@ -13,6 +13,7 @@ use middle::trans::base::*;
 use middle::trans::build::*;
 use middle::trans::common::*;
 
+use core::io::println;
 use core::libc::c_uint;
 use core::option;
 use core::vec;
@@ -92,16 +93,19 @@ pub impl FnType {
         return llargvals;
     }
 
-    fn build_shim_ret(&self, bcx: block,
-                      arg_tys: &[TypeRef], ret_def: bool,
-                      llargbundle: ValueRef, llretval: ValueRef) {
+    fn build_shim_ret(&self,
+                      bcx: block,
+                      arg_tys: &[TypeRef],
+                      ret_def: bool,
+                      llargbundle: ValueRef,
+                      llretval: ValueRef) {
         for vec::eachi(self.attrs) |i, a| {
             match *a {
                 option::Some(attr) => {
                     unsafe {
-                        llvm::LLVMAddInstrAttribute(
-                            llretval, (i + 1u) as c_uint,
-                                        attr as c_uint);
+                        llvm::LLVMAddInstrAttribute(llretval,
+                                                    (i + 1u) as c_uint,
+                                                    attr as c_uint);
                     }
                 }
                 _ => ()
@@ -125,8 +129,11 @@ pub impl FnType {
         };
     }
 
-    fn build_wrap_args(&self, bcx: block, ret_ty: TypeRef,
-                       llwrapfn: ValueRef, llargbundle: ValueRef) {
+    fn build_wrap_args(&self,
+                       bcx: block,
+                       ret_ty: TypeRef,
+                       llwrapfn: ValueRef,
+                       llargbundle: ValueRef) {
         let mut atys = /*bad*/copy self.arg_tys;
         let mut attrs = /*bad*/copy self.attrs;
         let mut j = 0u;
@@ -161,22 +168,27 @@ pub impl FnType {
         store_inbounds(bcx, llretptr, llargbundle, [0u, n]);
     }
 
-    fn build_wrap_ret(&self, bcx: block,
-                      arg_tys: &[TypeRef], llargbundle: ValueRef) {
+    fn build_wrap_ret(&self,
+                      bcx: block,
+                      arg_tys: &[TypeRef],
+                      llargbundle: ValueRef) {
         unsafe {
             if llvm::LLVMGetTypeKind(self.ret_ty.ty) == Void {
-                RetVoid(bcx);
                 return;
             }
         }
-        let n = vec::len(arg_tys);
-        let llretval = load_inbounds(bcx, llargbundle, ~[0u, n]);
+
+        let llretval = load_inbounds(bcx, llargbundle, ~[ 0, arg_tys.len() ]);
         let llretval = if self.ret_ty.cast {
             let retptr = BitCast(bcx, llretval, T_ptr(self.ret_ty.ty));
             Load(bcx, retptr)
         } else {
             Load(bcx, llretval)
         };
-        Ret(bcx, llretval);
+        let llretptr = BitCast(bcx,
+                               bcx.fcx.llretptr.get(),
+                               T_ptr(self.ret_ty.ty));
+        Store(bcx, llretval, llretptr);
     }
 }
+
diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs
index 20382676fed..88d18574029 100644
--- a/src/librustc/middle/trans/callee.rs
+++ b/src/librustc/middle/trans/callee.rs
@@ -314,11 +314,16 @@ pub fn trans_call(in_cx: block,
                   args: CallArgs,
                   id: ast::node_id,
                   dest: expr::Dest)
-               -> block {
+                  -> block {
     let _icx = in_cx.insn_ctxt("trans_call");
-    trans_call_inner(
-        in_cx, call_ex.info(), expr_ty(in_cx, f), node_id_type(in_cx, id),
-        |cx| trans(cx, f), args, dest, DontAutorefArg)
+    trans_call_inner(in_cx,
+                     call_ex.info(),
+                     expr_ty(in_cx, f),
+                     node_id_type(in_cx, id),
+                     |cx| trans(cx, f),
+                     args,
+                     dest,
+                     DontAutorefArg)
 }
 
 pub fn trans_method_call(in_cx: block,
@@ -326,7 +331,7 @@ pub fn trans_method_call(in_cx: block,
                          rcvr: @ast::expr,
                          args: CallArgs,
                          dest: expr::Dest)
-                      -> block {
+                         -> block {
     let _icx = in_cx.insn_ctxt("trans_method_call");
     debug!("trans_method_call(call_ex=%s, rcvr=%s)",
            call_ex.repr(in_cx.tcx()),
@@ -439,15 +444,15 @@ pub fn body_contains_ret(body: &ast::blk) -> bool {
 }
 
 // See [Note-arg-mode]
-pub fn trans_call_inner(
-    ++in_cx: block,
-    call_info: Option<NodeInfo>,
-    fn_expr_ty: ty::t,
-    ret_ty: ty::t,
-    get_callee: &fn(block) -> Callee,
-    args: CallArgs,
-    dest: expr::Dest,
-    autoref_arg: AutorefArg) -> block {
+pub fn trans_call_inner(++in_cx: block,
+                        call_info: Option<NodeInfo>,
+                        fn_expr_ty: ty::t,
+                        ret_ty: ty::t,
+                        get_callee: &fn(block) -> Callee,
+                        args: CallArgs,
+                        dest: expr::Dest,
+                        autoref_arg: AutorefArg)
+                        -> block {
     do base::with_scope(in_cx, call_info, ~"call") |cx| {
         let ret_in_loop = match args {
           ArgExprs(args) => {
@@ -500,7 +505,15 @@ pub fn trans_call_inner(
         let llretslot = trans_ret_slot(bcx, fn_expr_ty, dest);
 
         let mut llargs = ~[];
-        llargs.push(llretslot);
+
+        if ty::type_is_immediate(ret_ty) {
+            unsafe {
+                llargs.push(llvm::LLVMGetUndef(T_ptr(T_i8())));
+            }
+        } else {
+            llargs.push(llretslot);
+        }
+
         llargs.push(llenv);
         bcx = trans_args(bcx, args, fn_expr_ty,
                          ret_flag, autoref_arg, &mut llargs);
@@ -527,17 +540,34 @@ pub fn trans_call_inner(
         // If the block is terminated, then one or more of the args
         // has type _|_. Since that means it diverges, the code for
         // the call itself is unreachable.
-        bcx = base::invoke(bcx, llfn, llargs);
-        match dest { // drop the value if it is not being saved.
+        let (llresult, new_bcx) = base::invoke(bcx, llfn, llargs);
+        bcx = new_bcx;
+
+        match dest {
             expr::Ignore => {
+                // drop the value if it is not being saved.
                 unsafe {
                     if llvm::LLVMIsUndef(llretslot) != lib::llvm::True {
-                        bcx = glue::drop_ty(bcx, llretslot, ret_ty);
+                        if ty::type_is_immediate(ret_ty) {
+                            let llscratchptr = alloc_ty(bcx, ret_ty);
+                            Store(bcx, llresult, llscratchptr);
+                            bcx = glue::drop_ty(bcx, llscratchptr, ret_ty);
+                        } else {
+                            bcx = glue::drop_ty(bcx, llretslot, ret_ty);
+                        }
                     }
                 }
             }
-            expr::SaveIn(_) => { }
+            expr::SaveIn(lldest) => {
+                // If this is an immediate, store into the result location.
+                // (If this was not an immediate, the result will already be
+                // directly written into the output slot.)
+                if ty::type_is_immediate(ret_ty) {
+                    Store(bcx, llresult, lldest);
+                }
+            }
         }
+
         if ty::type_is_bot(ret_ty) {
             Unreachable(bcx);
         } else if ret_in_loop {
@@ -545,7 +575,7 @@ pub fn trans_call_inner(
             bcx = do with_cond(bcx, ret_flag_result) |bcx| {
                 for (copy bcx.fcx.loop_ret).each |&(flagptr, _)| {
                     Store(bcx, C_bool(true), flagptr);
-                    Store(bcx, C_bool(false), bcx.fcx.llretptr);
+                    Store(bcx, C_bool(false), bcx.fcx.llretptr.get());
                 }
                 base::cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn));
                 Unreachable(bcx);
@@ -562,11 +592,10 @@ pub enum CallArgs<'self> {
     ArgVals(&'self [ValueRef])
 }
 
-pub fn trans_ret_slot(+bcx: block,
-                      +fn_ty: ty::t,
-                      +dest: expr::Dest) -> ValueRef
-{
+pub fn trans_ret_slot(+bcx: block, +fn_ty: ty::t, +dest: expr::Dest)
+                      -> ValueRef {
     let retty = ty::ty_fn_ret(fn_ty);
+
     match dest {
         expr::SaveIn(dst) => dst,
         expr::Ignore => {
diff --git a/src/librustc/middle/trans/closure.rs b/src/librustc/middle/trans/closure.rs
index 252a10f8a8b..cb815506c39 100644
--- a/src/librustc/middle/trans/closure.rs
+++ b/src/librustc/middle/trans/closure.rs
@@ -299,7 +299,7 @@ pub fn build_closure(bcx0: block,
         // the right thing):
         let ret_true = match bcx.fcx.loop_ret {
             Some((_, retptr)) => retptr,
-            None => bcx.fcx.llretptr
+            None => bcx.fcx.llretptr.get()
         };
         let ret_casted = PointerCast(bcx, ret_true, T_ptr(T_nil()));
         let ret_datum = Datum {val: ret_casted, ty: ty::mk_nil(tcx),
@@ -367,8 +367,7 @@ pub fn trans_expr_fn(bcx: block,
                      outer_id: ast::node_id,
                      user_id: ast::node_id,
                      is_loop_body: Option<Option<ValueRef>>,
-                     dest: expr::Dest) -> block
-{
+                     dest: expr::Dest) -> block {
     /*!
      *
      * Translates the body of a closure expression.
@@ -400,7 +399,9 @@ pub fn trans_expr_fn(bcx: block,
 
     let ccx = bcx.ccx();
     let fty = node_id_type(bcx, outer_id);
+
     let llfnty = type_of_fn_from_ty(ccx, fty);
+
     let sub_path = vec::append_one(/*bad*/copy bcx.fcx.path,
                                    path_name(special_idents::anon));
     // XXX: Bad copy.
@@ -418,6 +419,12 @@ pub fn trans_expr_fn(bcx: block,
         set_inline_hint(llfn);
     }
 
+    let real_return_type = if is_loop_body.is_some() {
+        ty::mk_bool(bcx.tcx())
+    } else {
+        ty::ty_fn_ret(fty)
+    };
+
     let Result {bcx: bcx, val: closure} = match sigil {
         ast::BorrowedSigil | ast::ManagedSigil | ast::OwnedSigil => {
             let cap_vars = *ccx.maps.capture_map.get(&user_id);
@@ -435,11 +442,14 @@ pub fn trans_expr_fn(bcx: block,
                           user_id,
                           None,
                           [],
+                          real_return_type,
                           |fcx| load_environment(fcx, cdata_ty, cap_vars,
                                                  ret_handle.is_some(), sigil),
                           |bcx| {
                               if is_loop_body.is_some() {
-                                  Store(bcx, C_bool(true), bcx.fcx.llretptr);
+                                  Store(bcx,
+                                        C_bool(true),
+                                        bcx.fcx.llretptr.get());
                               }
                           });
             rslt(bcx, llbox)
diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs
index cec9a95671e..76f0892277e 100644
--- a/src/librustc/middle/trans/common.rs
+++ b/src/librustc/middle/trans/common.rs
@@ -291,10 +291,15 @@ pub struct fn_ctxt_ {
     // section of the executable we're generating.
     llfn: ValueRef,
 
-    // The two implicit arguments that arrive in the function we're creating.
-    // For instance, foo(int, int) is really foo(ret*, env*, int, int).
+    // The implicit environment argument that arrives in the function we're
+    // creating.
     llenv: ValueRef,
-    llretptr: ValueRef,
+
+    // The place to store the return value. If the return type is immediate,
+    // this is an alloca in the function. Otherwise, it's the hidden first
+    // parameter to the function. After function construction, this should
+    // always be Some.
+    llretptr: Option<ValueRef>,
 
     // These elements: "hoisted basic blocks" containing
     // administrative activities that have to happen in only one place in
@@ -322,6 +327,11 @@ pub struct fn_ctxt_ {
     // for that (flagptr, retptr)
     loop_ret: Option<(ValueRef, ValueRef)>,
 
+    // True if this function has an immediate return value, false otherwise.
+    // If this is false, the llretptr will alias the first argument of the
+    // function.
+    has_immediate_return_value: bool,
+
     // Maps arguments to allocas created for them in llallocas.
     llargs: @mut HashMap<ast::node_id, local_val>,
     // Maps the def_ids for local variables to the allocas created for
diff --git a/src/librustc/middle/trans/controlflow.rs b/src/librustc/middle/trans/controlflow.rs
index c2235b763e2..69e26774435 100644
--- a/src/librustc/middle/trans/controlflow.rs
+++ b/src/librustc/middle/trans/controlflow.rs
@@ -274,7 +274,7 @@ pub fn trans_break_cont(bcx: block,
           Some(bcx) => bcx,
           // This is a return from a loop body block
           None => {
-            Store(bcx, C_bool(!to_end), bcx.fcx.llretptr);
+            Store(bcx, C_bool(!to_end), bcx.fcx.llretptr.get());
             cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn));
             Unreachable(bcx);
             return bcx;
@@ -303,14 +303,14 @@ pub fn trans_ret(bcx: block, e: Option<@ast::expr>) -> block {
         // to false, return flag to true, and then store the value in the
         // parent's retptr.
         Store(bcx, C_bool(true), flagptr);
-        Store(bcx, C_bool(false), bcx.fcx.llretptr);
+        Store(bcx, C_bool(false), bcx.fcx.llretptr.get());
         match e {
           Some(x) => PointerCast(bcx, retptr,
                                  T_ptr(type_of(bcx.ccx(), expr_ty(bcx, x)))),
           None => retptr
         }
       }
-      None => bcx.fcx.llretptr
+      None => bcx.fcx.llretptr.get()
     };
     match e {
       Some(x) => {
diff --git a/src/librustc/middle/trans/datum.rs b/src/librustc/middle/trans/datum.rs
index 869fdc20a65..477065377a5 100644
--- a/src/librustc/middle/trans/datum.rs
+++ b/src/librustc/middle/trans/datum.rs
@@ -31,7 +31,7 @@
  * value stored in the datum is indicated in the field `ty`.
  *
  * Generally speaking, you probably do not want to access the `val` field
- * unless you know what mode the value is in.  Intead you should use one
+ * unless you know what mode the value is in.  Instead you should use one
  * of the following accessors:
  *
  * - `to_value_llval()` converts to by-value
diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs
index e75e49f18f3..21d62f95cc5 100644
--- a/src/librustc/middle/trans/expr.rs
+++ b/src/librustc/middle/trans/expr.rs
@@ -624,10 +624,14 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
             let sigil = ty::ty_closure_sigil(expr_ty);
             match blk.node {
                 ast::expr_fn_block(ref decl, ref body) => {
-                    return closure::trans_expr_fn(bcx, sigil,
-                                                  decl, body,
-                                                  expr.id, blk.id,
-                                                  Some(None), dest);
+                    return closure::trans_expr_fn(bcx,
+                                                  sigil,
+                                                  decl,
+                                                  body,
+                                                  expr.id,
+                                                  blk.id,
+                                                  Some(None),
+                                                  dest);
                 }
                 _ => {
                     bcx.sess().impossible_case(
@@ -655,15 +659,30 @@ fn trans_rvalue_dps_unadjusted(bcx: block, expr: @ast::expr,
         }
         ast::expr_binary(_, lhs, rhs) => {
             // if not overloaded, would be RvalueDatumExpr
-            return trans_overloaded_op(bcx, expr, lhs, ~[rhs], dest);
+            return trans_overloaded_op(bcx,
+                                       expr,
+                                       lhs,
+                                       ~[rhs],
+                                       expr_ty(bcx, expr),
+                                       dest);
         }
         ast::expr_unary(_, subexpr) => {
             // if not overloaded, would be RvalueDatumExpr
-            return trans_overloaded_op(bcx, expr, subexpr, ~[], dest);
+            return trans_overloaded_op(bcx,
+                                       expr,
+                                       subexpr,
+                                       ~[],
+                                       expr_ty(bcx, expr),
+                                       dest);
         }
         ast::expr_index(base, idx) => {
             // if not overloaded, would be RvalueDatumExpr
-            return trans_overloaded_op(bcx, expr, base, ~[idx], dest);
+            return trans_overloaded_op(bcx,
+                                       expr,
+                                       base,
+                                       ~[idx],
+                                       expr_ty(bcx, expr),
+                                       dest);
         }
         ast::expr_cast(val, _) => {
             match ty::get(node_id_type(bcx, expr.id)).sty {
@@ -1554,15 +1573,24 @@ fn trans_overloaded_op(bcx: block,
                        expr: @ast::expr,
                        rcvr: @ast::expr,
                        +args: ~[@ast::expr],
-                       dest: Dest) -> block
-{
+                       ret_ty: ty::t,
+                       dest: Dest)
+                       -> block {
     let origin = *bcx.ccx().maps.method_map.get(&expr.id);
     let fty = node_id_type(bcx, expr.callee_id);
-    return callee::trans_call_inner(
-        bcx, expr.info(), fty,
-        expr_ty(bcx, expr),
-        |bcx| meth::trans_method_callee(bcx, expr.callee_id, rcvr, origin),
-        callee::ArgExprs(args), dest, DoAutorefArg);
+    callee::trans_call_inner(bcx,
+                             expr.info(),
+                             fty,
+                             ret_ty,
+                             |bcx| {
+                                meth::trans_method_callee(bcx,
+                                                          expr.callee_id,
+                                                          rcvr,
+                                                          origin)
+                             },
+                             callee::ArgExprs(args),
+                             dest,
+                             DoAutorefArg)
 }
 
 fn int_cast(bcx: block, lldsttype: TypeRef, llsrctype: TypeRef,
@@ -1697,7 +1725,11 @@ fn trans_assign_op(bcx: block,
     if bcx.ccx().maps.method_map.find(&expr.id).is_some() {
         // FIXME(#2528) evaluates the receiver twice!!
         let scratch = scratch_datum(bcx, dst_datum.ty, false);
-        let bcx = trans_overloaded_op(bcx, expr, dst, ~[src],
+        let bcx = trans_overloaded_op(bcx,
+                                      expr,
+                                      dst,
+                                      ~[src],
+                                      dst_datum.ty,
                                       SaveIn(scratch.val));
         return scratch.move_to_datum(bcx, DROP_EXISTING, dst_datum);
     }
diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs
index 8c308bfa889..b00f4d8ed42 100644
--- a/src/librustc/middle/trans/foreign.rs
+++ b/src/librustc/middle/trans/foreign.rs
@@ -83,10 +83,11 @@ struct ShimTypes {
 struct LlvmSignature {
     llarg_tys: ~[TypeRef],
     llret_ty: TypeRef,
+    sret: bool,
 }
 
-fn foreign_signature(ccx: @CrateContext,
-                     fn_sig: &ty::FnSig) -> LlvmSignature {
+fn foreign_signature(ccx: @CrateContext, fn_sig: &ty::FnSig)
+                     -> LlvmSignature {
     /*!
      * The ForeignSignature is the LLVM types of the arguments/return type
      * of a function.  Note that these LLVM types are not quite the same
@@ -97,7 +98,11 @@ fn foreign_signature(ccx: @CrateContext,
 
     let llarg_tys = fn_sig.inputs.map(|arg| type_of(ccx, arg.ty));
     let llret_ty = type_of::type_of(ccx, fn_sig.output);
-    LlvmSignature {llarg_tys: llarg_tys, llret_ty: llret_ty}
+    LlvmSignature {
+        llarg_tys: llarg_tys,
+        llret_ty: llret_ty,
+        sret: !ty::type_is_immediate(fn_sig.output),
+    }
 }
 
 fn shim_types(ccx: @CrateContext, id: ast::node_id) -> ShimTypes {
@@ -109,20 +114,17 @@ fn shim_types(ccx: @CrateContext, id: ast::node_id) -> ShimTypes {
     let bundle_ty = T_struct(vec::append_one(copy llsig.llarg_tys,
                                              T_ptr(llsig.llret_ty)),
                              false);
-    let ret_def =
-        !ty::type_is_bot(fn_sig.output) &&
-        !ty::type_is_nil(fn_sig.output);
-    let fn_ty =
-        abi_info(ccx).compute_info(
-            llsig.llarg_tys,
-            llsig.llret_ty,
-            ret_def);
+    let ret_def = !ty::type_is_bot(fn_sig.output) &&
+                  !ty::type_is_nil(fn_sig.output);
+    let fn_ty = abi_info(ccx).compute_info(llsig.llarg_tys,
+                                           llsig.llret_ty,
+                                           ret_def);
     ShimTypes {
         fn_sig: fn_sig,
         llsig: llsig,
         ret_def: ret_def,
         bundle_ty: bundle_ty,
-        shim_fn_ty: T_fn(~[T_ptr(bundle_ty)], T_void()),
+        shim_fn_ty: T_fn(~[T_ptr(bundle_ty)], T_nil()),
         fn_ty: fn_ty
     }
 }
@@ -142,13 +144,13 @@ fn build_shim_fn_(ccx: @CrateContext,
                   tys: &ShimTypes,
                   cc: lib::llvm::CallConv,
                   arg_builder: shim_arg_builder,
-                  ret_builder: shim_ret_builder) -> ValueRef
-{
+                  ret_builder: shim_ret_builder)
+               -> ValueRef {
     let llshimfn = decl_internal_cdecl_fn(
         ccx.llmod, shim_name, tys.shim_fn_ty);
 
     // Declare the body of the shim function:
-    let fcx = new_fn_ctxt(ccx, ~[], llshimfn, None);
+    let fcx = new_fn_ctxt(ccx, ~[], llshimfn, tys.fn_sig.output, None);
     let bcx = top_scope_block(fcx, None);
     let lltop = bcx.llbb;
     let llargbundle = get_param(llshimfn, 0u);
@@ -159,19 +161,24 @@ fn build_shim_fn_(ccx: @CrateContext,
 
     ret_builder(bcx, tys, llargbundle, llretval);
 
-    build_return(bcx);
-    finish_fn(fcx, lltop);
+    // Don't finish up the function in the usual way, because this doesn't
+    // follow the normal Rust calling conventions.
+    tie_up_header_blocks(fcx, lltop);
+
+    let ret_cx = raw_block(fcx, false, fcx.llreturn);
+    Ret(ret_cx, C_null(T_nil()));
 
     return llshimfn;
 }
 
-type wrap_arg_builder<'self> =
-    &'self fn(bcx: block, tys: &ShimTypes,
-              llwrapfn: ValueRef, llargbundle: ValueRef);
+type wrap_arg_builder<'self> = &'self fn(bcx: block,
+                                         tys: &ShimTypes,
+                                         llwrapfn: ValueRef,
+                                         llargbundle: ValueRef);
 
-type wrap_ret_builder<'self> =
-    &'self fn(bcx: block, tys: &ShimTypes,
-              llargbundle: ValueRef);
+type wrap_ret_builder<'self> = &'self fn(bcx: block,
+                                         tys: &ShimTypes,
+                                         llargbundle: ValueRef);
 
 fn build_wrap_fn_(ccx: @CrateContext,
                   tys: &ShimTypes,
@@ -179,10 +186,17 @@ fn build_wrap_fn_(ccx: @CrateContext,
                   llwrapfn: ValueRef,
                   shim_upcall: ValueRef,
                   arg_builder: wrap_arg_builder,
-                  ret_builder: wrap_ret_builder)
-{
+                  ret_builder: wrap_ret_builder) {
     let _icx = ccx.insn_ctxt("foreign::build_wrap_fn_");
-    let fcx = new_fn_ctxt(ccx, ~[], llwrapfn, None);
+    let fcx = new_fn_ctxt(ccx, ~[], llwrapfn, tys.fn_sig.output, None);
+
+    // Patch up the return type if it's not immediate.
+    /*if !ty::type_is_immediate(tys.fn_sig.output) {
+        let lloutputtype = type_of::type_of(*fcx.ccx, tys.fn_sig.output);
+        fcx.llretptr = Some(alloca(raw_block(fcx, false, fcx.llstaticallocas),
+                                   lloutputtype));
+    }*/
+
     let bcx = top_scope_block(fcx, None);
     let lltop = bcx.llbb;
 
@@ -196,11 +210,34 @@ fn build_wrap_fn_(ccx: @CrateContext,
     Call(bcx, shim_upcall, ~[llrawargbundle, llshimfnptr]);
     ret_builder(bcx, tys, llargbundle);
 
+    // Perform a custom version of `finish_fn`. First, tie up the header
+    // blocks.
     tie_up_header_blocks(fcx, lltop);
 
-    // Make sure our standard return block (that we didn't use) is terminated
-    let ret_cx = raw_block(fcx, false, fcx.llreturn);
-    Unreachable(ret_cx);
+    // Then return according to the C ABI.
+    unsafe {
+        let return_context = raw_block(fcx, false, fcx.llreturn);
+
+        let llfunctiontype = val_ty(llwrapfn);
+        let llfunctiontype =
+            ::lib::llvm::llvm::LLVMGetElementType(llfunctiontype);
+        let llfunctionreturntype =
+            ::lib::llvm::llvm::LLVMGetReturnType(llfunctiontype);
+        if ::lib::llvm::llvm::LLVMGetTypeKind(llfunctionreturntype) ==
+                ::lib::llvm::Void {
+            // XXX: This might be wrong if there are any functions for which
+            // the C ABI specifies a void output pointer and the Rust ABI
+            // does not.
+            RetVoid(return_context);
+        } else {
+            // Cast if we have to...
+            // XXX: This is ugly.
+            let llretptr = BitCast(return_context,
+                                   fcx.llretptr.get(),
+                                   T_ptr(llfunctionreturntype));
+            Ret(return_context, Load(return_context, llretptr));
+        }
+    }
 }
 
 // For each foreign function F, we generate a wrapper function W and a shim
@@ -241,8 +278,7 @@ fn build_wrap_fn_(ccx: @CrateContext,
 // in the future.
 pub fn trans_foreign_mod(ccx: @CrateContext,
                          path: &ast_map::path,
-                         foreign_mod: &ast::foreign_mod)
-{
+                         foreign_mod: &ast::foreign_mod) {
     let _icx = ccx.insn_ctxt("foreign::trans_foreign_mod");
 
     let arch = ccx.sess.targ_cfg.arch;
@@ -312,8 +348,7 @@ pub fn trans_foreign_mod(ccx: @CrateContext,
     fn build_foreign_fn(ccx: @CrateContext,
                         id: ast::node_id,
                         foreign_item: @ast::foreign_item,
-                        cc: lib::llvm::CallConv)
-    {
+                        cc: lib::llvm::CallConv) {
         let llwrapfn = get_item_val(ccx, id);
         let tys = shim_types(ccx, id);
         if attr::attrs_contains_name(foreign_item.attrs, "rust_stack") {
@@ -322,8 +357,7 @@ pub fn trans_foreign_mod(ccx: @CrateContext,
         } else if attr::attrs_contains_name(foreign_item.attrs, "fast_ffi") {
             build_fast_ffi_fn(ccx, llwrapfn, foreign_item, &tys, cc);
         } else {
-            let llshimfn = build_shim_fn(ccx, foreign_item,
-                                         &tys, cc);
+            let llshimfn = build_shim_fn(ccx, foreign_item, &tys, cc);
             build_wrap_fn(ccx, &tys, llshimfn, llwrapfn);
         }
     }
@@ -331,8 +365,8 @@ pub fn trans_foreign_mod(ccx: @CrateContext,
     fn build_shim_fn(ccx: @CrateContext,
                      foreign_item: @ast::foreign_item,
                      tys: &ShimTypes,
-                     cc: lib::llvm::CallConv) -> ValueRef
-    {
+                     cc: lib::llvm::CallConv)
+                  -> ValueRef {
         /*!
          *
          * Build S, from comment above:
@@ -344,31 +378,43 @@ pub fn trans_foreign_mod(ccx: @CrateContext,
 
         let _icx = ccx.insn_ctxt("foreign::build_shim_fn");
 
-        fn build_args(bcx: block, tys: &ShimTypes,
-                      llargbundle: ValueRef) -> ~[ValueRef] {
+        fn build_args(bcx: block, tys: &ShimTypes, llargbundle: ValueRef)
+                   -> ~[ValueRef] {
             let _icx = bcx.insn_ctxt("foreign::shim::build_args");
-            tys.fn_ty.build_shim_args(
-                bcx, tys.llsig.llarg_tys, llargbundle)
+            tys.fn_ty.build_shim_args(bcx, tys.llsig.llarg_tys, llargbundle)
         }
 
-        fn build_ret(bcx: block, tys: &ShimTypes,
-                     llargbundle: ValueRef, llretval: ValueRef)  {
+        fn build_ret(bcx: block,
+                     tys: &ShimTypes,
+                     llargbundle: ValueRef,
+                     llretval: ValueRef) {
             let _icx = bcx.insn_ctxt("foreign::shim::build_ret");
-            tys.fn_ty.build_shim_ret(
-                bcx, tys.llsig.llarg_tys,
-                tys.ret_def, llargbundle, llretval);
+            tys.fn_ty.build_shim_ret(bcx,
+                                     tys.llsig.llarg_tys,
+                                     tys.ret_def,
+                                     llargbundle,
+                                     llretval);
+            build_return(bcx);
         }
 
         let lname = link_name(ccx, foreign_item);
         let llbasefn = base_fn(ccx, *lname, tys, cc);
         // Name the shim function
         let shim_name = *lname + ~"__c_stack_shim";
-        return build_shim_fn_(ccx, shim_name, llbasefn, tys, cc,
-                           build_args, build_ret);
+        build_shim_fn_(ccx,
+                       shim_name,
+                       llbasefn,
+                       tys,
+                       cc,
+                       build_args,
+                       build_ret)
     }
 
-    fn base_fn(ccx: @CrateContext, lname: &str, tys: &ShimTypes,
-               cc: lib::llvm::CallConv) -> ValueRef {
+    fn base_fn(ccx: @CrateContext,
+               lname: &str,
+               tys: &ShimTypes,
+               cc: lib::llvm::CallConv)
+               -> ValueRef {
         // Declare the "prototype" for the base function F:
         do tys.fn_ty.decl_fn |fnty| {
             decl_fn(ccx.llmod, lname, cc, fnty)
@@ -377,12 +423,14 @@ pub fn trans_foreign_mod(ccx: @CrateContext,
 
     // FIXME (#2535): this is very shaky and probably gets ABIs wrong all
     // over the place
-    fn build_direct_fn(ccx: @CrateContext, decl: ValueRef,
-                       item: @ast::foreign_item, tys: &ShimTypes,
+    fn build_direct_fn(ccx: @CrateContext,
+                       decl: ValueRef,
+                       item: @ast::foreign_item,
+                       tys: &ShimTypes,
                        cc: lib::llvm::CallConv) {
         debug!("build_direct_fn(%s)", *link_name(ccx, item));
 
-        let fcx = new_fn_ctxt(ccx, ~[], decl, None);
+        let fcx = new_fn_ctxt(ccx, ~[], decl, tys.fn_sig.output, None);
         let bcx = top_scope_block(fcx, None), lltop = bcx.llbb;
         let llbasefn = base_fn(ccx, *link_name(ccx, item), tys, cc);
         let ty = ty::lookup_item_type(ccx.tcx,
@@ -393,7 +441,7 @@ pub fn trans_foreign_mod(ccx: @CrateContext,
         let retval = Call(bcx, llbasefn, args);
         let ret_ty = ty::ty_fn_ret(ty);
         if !ty::type_is_nil(ret_ty) && !ty::type_is_bot(ret_ty) {
-            Store(bcx, retval, fcx.llretptr);
+            Store(bcx, retval, fcx.llretptr.get());
         }
         build_return(bcx);
         finish_fn(fcx, lltop);
@@ -408,7 +456,7 @@ pub fn trans_foreign_mod(ccx: @CrateContext,
                          cc: lib::llvm::CallConv) {
         debug!("build_fast_ffi_fn(%s)", *link_name(ccx, item));
 
-        let fcx = new_fn_ctxt(ccx, ~[], decl, None);
+        let fcx = new_fn_ctxt(ccx, ~[], decl, tys.fn_sig.output, None);
         let bcx = top_scope_block(fcx, None), lltop = bcx.llbb;
         let llbasefn = base_fn(ccx, *link_name(ccx, item), tys, cc);
         set_no_inline(fcx.llfn);
@@ -421,7 +469,7 @@ pub fn trans_foreign_mod(ccx: @CrateContext,
         let retval = Call(bcx, llbasefn, args);
         let ret_ty = ty::ty_fn_ret(ty);
         if !ty::type_is_nil(ret_ty) && !ty::type_is_bot(ret_ty) {
-            Store(bcx, retval, fcx.llretptr);
+            Store(bcx, retval, fcx.llretptr.get());
         }
         build_return(bcx);
         finish_fn(fcx, lltop);
@@ -446,12 +494,18 @@ pub fn trans_foreign_mod(ccx: @CrateContext,
 
         let _icx = ccx.insn_ctxt("foreign::build_wrap_fn");
 
-        build_wrap_fn_(ccx, tys, llshimfn, llwrapfn,
+        build_wrap_fn_(ccx,
+                       tys,
+                       llshimfn,
+                       llwrapfn,
                        ccx.upcalls.call_shim_on_c_stack,
-                       build_args, build_ret);
+                       build_args,
+                       build_ret);
 
-        fn build_args(bcx: block, tys: &ShimTypes,
-                      llwrapfn: ValueRef, llargbundle: ValueRef) {
+        fn build_args(bcx: block,
+                      tys: &ShimTypes,
+                      llwrapfn: ValueRef,
+                      llargbundle: ValueRef) {
             let _icx = bcx.insn_ctxt("foreign::wrap::build_args");
             let ccx = bcx.ccx();
             let n = vec::len(tys.llsig.llarg_tys);
@@ -468,14 +522,18 @@ pub fn trans_foreign_mod(ccx: @CrateContext,
 
                 store_inbounds(bcx, llargval, llargbundle, ~[0u, i]);
             }
-            let llretptr = get_param(llwrapfn, 0u);
+            let llretptr = bcx.fcx.llretptr.get();
             store_inbounds(bcx, llretptr, llargbundle, ~[0u, n]);
         }
 
-        fn build_ret(bcx: block, _tys: &ShimTypes,
-                     _llargbundle: ValueRef) {
+        fn build_ret(bcx: block,
+                     shim_types: &ShimTypes,
+                     llargbundle: ValueRef) {
             let _icx = bcx.insn_ctxt("foreign::wrap::build_ret");
-            RetVoid(bcx);
+            let arg_count = shim_types.fn_sig.inputs.len();
+            let llretptr = load_inbounds(bcx, llargbundle, ~[0, arg_count]);
+            Store(bcx, Load(bcx, llretptr), bcx.fcx.llretptr.get());
+            build_return(bcx);
         }
     }
 }
@@ -488,9 +546,18 @@ pub fn trans_intrinsic(ccx: @CrateContext,
                        ref_id: Option<ast::node_id>) {
     debug!("trans_intrinsic(item.ident=%s)", *ccx.sess.str_of(item.ident));
 
+    let output_type = ty::ty_fn_ret(ty::node_id_to_type(ccx.tcx, item.id));
+
     // XXX: Bad copy.
-    let fcx = new_fn_ctxt_w_id(ccx, path, decl, item.id, None,
-                               Some(copy substs), Some(item.span));
+    let fcx = new_fn_ctxt_w_id(ccx,
+                               path,
+                               decl,
+                               item.id,
+                               output_type,
+                               None,
+                               Some(copy substs),
+                               Some(item.span));
+
     let mut bcx = top_scope_block(fcx, None), lltop = bcx.llbb;
     match *ccx.sess.str_of(item.ident) {
         ~"atomic_cxchg" => {
@@ -499,7 +566,7 @@ pub fn trans_intrinsic(ccx: @CrateContext,
                                     get_param(decl, first_real_arg + 1u),
                                     get_param(decl, first_real_arg + 2u),
                                     SequentiallyConsistent);
-            Store(bcx, old, fcx.llretptr);
+            Store(bcx, old, fcx.llretptr.get());
         }
         ~"atomic_cxchg_acq" => {
             let old = AtomicCmpXchg(bcx,
@@ -507,7 +574,7 @@ pub fn trans_intrinsic(ccx: @CrateContext,
                                     get_param(decl, first_real_arg + 1u),
                                     get_param(decl, first_real_arg + 2u),
                                     Acquire);
-            Store(bcx, old, fcx.llretptr);
+            Store(bcx, old, fcx.llretptr.get());
         }
         ~"atomic_cxchg_rel" => {
             let old = AtomicCmpXchg(bcx,
@@ -515,76 +582,76 @@ pub fn trans_intrinsic(ccx: @CrateContext,
                                     get_param(decl, first_real_arg + 1u),
                                     get_param(decl, first_real_arg + 2u),
                                     Release);
-            Store(bcx, old, fcx.llretptr);
+            Store(bcx, old, fcx.llretptr.get());
         }
         ~"atomic_xchg" => {
             let old = AtomicRMW(bcx, Xchg,
                                 get_param(decl, first_real_arg),
                                 get_param(decl, first_real_arg + 1u),
                                 SequentiallyConsistent);
-            Store(bcx, old, fcx.llretptr);
+            Store(bcx, old, fcx.llretptr.get());
         }
         ~"atomic_xchg_acq" => {
             let old = AtomicRMW(bcx, Xchg,
                                 get_param(decl, first_real_arg),
                                 get_param(decl, first_real_arg + 1u),
                                 Acquire);
-            Store(bcx, old, fcx.llretptr);
+            Store(bcx, old, fcx.llretptr.get());
         }
         ~"atomic_xchg_rel" => {
             let old = AtomicRMW(bcx, Xchg,
                                 get_param(decl, first_real_arg),
                                 get_param(decl, first_real_arg + 1u),
                                 Release);
-            Store(bcx, old, fcx.llretptr);
+            Store(bcx, old, fcx.llretptr.get());
         }
         ~"atomic_xadd" => {
             let old = AtomicRMW(bcx, lib::llvm::Add,
                                 get_param(decl, first_real_arg),
                                 get_param(decl, first_real_arg + 1u),
                                 SequentiallyConsistent);
-            Store(bcx, old, fcx.llretptr);
+            Store(bcx, old, fcx.llretptr.get());
         }
         ~"atomic_xadd_acq" => {
             let old = AtomicRMW(bcx, lib::llvm::Add,
                                 get_param(decl, first_real_arg),
                                 get_param(decl, first_real_arg + 1u),
                                 Acquire);
-            Store(bcx, old, fcx.llretptr);
+            Store(bcx, old, fcx.llretptr.get());
         }
         ~"atomic_xadd_rel" => {
             let old = AtomicRMW(bcx, lib::llvm::Add,
                                 get_param(decl, first_real_arg),
                                 get_param(decl, first_real_arg + 1u),
                                 Release);
-            Store(bcx, old, fcx.llretptr);
+            Store(bcx, old, fcx.llretptr.get());
         }
         ~"atomic_xsub" => {
             let old = AtomicRMW(bcx, lib::llvm::Sub,
                                 get_param(decl, first_real_arg),
                                 get_param(decl, first_real_arg + 1u),
                                 SequentiallyConsistent);
-            Store(bcx, old, fcx.llretptr);
+            Store(bcx, old, fcx.llretptr.get());
         }
         ~"atomic_xsub_acq" => {
             let old = AtomicRMW(bcx, lib::llvm::Sub,
                                 get_param(decl, first_real_arg),
                                 get_param(decl, first_real_arg + 1u),
                                 Acquire);
-            Store(bcx, old, fcx.llretptr);
+            Store(bcx, old, fcx.llretptr.get());
         }
         ~"atomic_xsub_rel" => {
             let old = AtomicRMW(bcx, lib::llvm::Sub,
                                 get_param(decl, first_real_arg),
                                 get_param(decl, first_real_arg + 1u),
                                 Release);
-            Store(bcx, old, fcx.llretptr);
+            Store(bcx, old, fcx.llretptr.get());
         }
         ~"size_of" => {
             let tp_ty = substs.tys[0];
             let lltp_ty = type_of::type_of(ccx, tp_ty);
             Store(bcx, C_uint(ccx, machine::llsize_of_real(ccx, lltp_ty)),
-                  fcx.llretptr);
+                  fcx.llretptr.get());
         }
         ~"move_val" => {
             // Create a datum reflecting the value being moved:
@@ -615,13 +682,13 @@ pub fn trans_intrinsic(ccx: @CrateContext,
             let tp_ty = substs.tys[0];
             let lltp_ty = type_of::type_of(ccx, tp_ty);
             Store(bcx, C_uint(ccx, machine::llalign_of_min(ccx, lltp_ty)),
-                  fcx.llretptr);
+                  fcx.llretptr.get());
         }
         ~"pref_align_of"=> {
             let tp_ty = substs.tys[0];
             let lltp_ty = type_of::type_of(ccx, tp_ty);
             Store(bcx, C_uint(ccx, machine::llalign_of_pref(ccx, lltp_ty)),
-                  fcx.llretptr);
+                  fcx.llretptr.get());
         }
         ~"get_tydesc" => {
             let tp_ty = substs.tys[0];
@@ -631,13 +698,13 @@ pub fn trans_intrinsic(ccx: @CrateContext,
             // FIXME (#3727): change this to T_ptr(ccx.tydesc_ty) when the
             // core::sys copy of the get_tydesc interface dies off.
             let td = PointerCast(bcx, static_ti.tydesc, T_ptr(T_nil()));
-            Store(bcx, td, fcx.llretptr);
+            Store(bcx, td, fcx.llretptr.get());
         }
         ~"init" => {
             let tp_ty = substs.tys[0];
             let lltp_ty = type_of::type_of(ccx, tp_ty);
             if !ty::type_is_nil(tp_ty) {
-                Store(bcx, C_null(lltp_ty), fcx.llretptr);
+                Store(bcx, C_null(lltp_ty), fcx.llretptr.get());
             }
         }
         ~"forget" => {}
@@ -663,20 +730,21 @@ pub fn trans_intrinsic(ccx: @CrateContext,
               // NB: Do not use a Load and Store here. This causes
               // massive code bloat when reinterpret_cast is used on
               // large structural types.
-              let llretptr = PointerCast(bcx, fcx.llretptr, T_ptr(T_i8()));
+              let llretptr = fcx.llretptr.get();
+              let llretptr = PointerCast(bcx, llretptr, T_ptr(T_i8()));
               let llcast = get_param(decl, first_real_arg);
               let llcast = PointerCast(bcx, llcast, T_ptr(T_i8()));
               call_memcpy(bcx, llretptr, llcast, llsize_of(ccx, lltp_ty));
           }
         }
         ~"addr_of" => {
-            Store(bcx, get_param(decl, first_real_arg), fcx.llretptr);
+            Store(bcx, get_param(decl, first_real_arg), fcx.llretptr.get());
         }
         ~"needs_drop" => {
             let tp_ty = substs.tys[0];
             Store(bcx,
                   C_bool(ty::type_needs_drop(ccx.tcx, tp_ty)),
-                  fcx.llretptr);
+                  fcx.llretptr.get());
         }
         ~"visit_tydesc" => {
             let td = get_param(decl, first_real_arg);
@@ -718,7 +786,7 @@ pub fn trans_intrinsic(ccx: @CrateContext,
                 bcx.ccx().llmod, ~"__morestack", llfty);
             let morestack_addr = PointerCast(bcx, morestack_addr,
                                              T_ptr(T_nil()));
-            Store(bcx, morestack_addr, fcx.llretptr);
+            Store(bcx, morestack_addr, fcx.llretptr.get());
         }
         ~"memmove32" => {
             let dst_ptr = get_param(decl, first_real_arg);
@@ -743,243 +811,243 @@ pub fn trans_intrinsic(ccx: @CrateContext,
         ~"sqrtf32" => {
             let x = get_param(decl, first_real_arg);
             let sqrtf = *ccx.intrinsics.get(&~"llvm.sqrt.f32");
-            Store(bcx, Call(bcx, sqrtf, ~[x]), fcx.llretptr);
+            Store(bcx, Call(bcx, sqrtf, ~[x]), fcx.llretptr.get());
         }
         ~"sqrtf64" => {
             let x = get_param(decl, first_real_arg);
             let sqrtf = *ccx.intrinsics.get(&~"llvm.sqrt.f64");
-            Store(bcx, Call(bcx, sqrtf, ~[x]), fcx.llretptr);
+            Store(bcx, Call(bcx, sqrtf, ~[x]), fcx.llretptr.get());
         }
         ~"powif32" => {
             let a = get_param(decl, first_real_arg);
             let x = get_param(decl, first_real_arg + 1u);
             let powif = *ccx.intrinsics.get(&~"llvm.powi.f32");
-            Store(bcx, Call(bcx, powif, ~[a, x]), fcx.llretptr);
+            Store(bcx, Call(bcx, powif, ~[a, x]), fcx.llretptr.get());
         }
         ~"powif64" => {
             let a = get_param(decl, first_real_arg);
             let x = get_param(decl, first_real_arg + 1u);
             let powif = *ccx.intrinsics.get(&~"llvm.powi.f64");
-            Store(bcx, Call(bcx, powif, ~[a, x]), fcx.llretptr);
+            Store(bcx, Call(bcx, powif, ~[a, x]), fcx.llretptr.get());
         }
         ~"sinf32" => {
             let x = get_param(decl, first_real_arg);
             let sinf = *ccx.intrinsics.get(&~"llvm.sin.f32");
-            Store(bcx, Call(bcx, sinf, ~[x]), fcx.llretptr);
+            Store(bcx, Call(bcx, sinf, ~[x]), fcx.llretptr.get());
         }
         ~"sinf64" => {
             let x = get_param(decl, first_real_arg);
             let sinf = *ccx.intrinsics.get(&~"llvm.sin.f64");
-            Store(bcx, Call(bcx, sinf, ~[x]), fcx.llretptr);
+            Store(bcx, Call(bcx, sinf, ~[x]), fcx.llretptr.get());
         }
         ~"cosf32" => {
             let x = get_param(decl, first_real_arg);
             let cosf = *ccx.intrinsics.get(&~"llvm.cos.f32");
-            Store(bcx, Call(bcx, cosf, ~[x]), fcx.llretptr);
+            Store(bcx, Call(bcx, cosf, ~[x]), fcx.llretptr.get());
         }
         ~"cosf64" => {
             let x = get_param(decl, first_real_arg);
             let cosf = *ccx.intrinsics.get(&~"llvm.cos.f64");
-            Store(bcx, Call(bcx, cosf, ~[x]), fcx.llretptr);
+            Store(bcx, Call(bcx, cosf, ~[x]), fcx.llretptr.get());
         }
         ~"powf32" => {
             let a = get_param(decl, first_real_arg);
             let x = get_param(decl, first_real_arg + 1u);
             let powf = *ccx.intrinsics.get(&~"llvm.pow.f32");
-            Store(bcx, Call(bcx, powf, ~[a, x]), fcx.llretptr);
+            Store(bcx, Call(bcx, powf, ~[a, x]), fcx.llretptr.get());
         }
         ~"powf64" => {
             let a = get_param(decl, first_real_arg);
             let x = get_param(decl, first_real_arg + 1u);
             let powf = *ccx.intrinsics.get(&~"llvm.pow.f64");
-            Store(bcx, Call(bcx, powf, ~[a, x]), fcx.llretptr);
+            Store(bcx, Call(bcx, powf, ~[a, x]), fcx.llretptr.get());
         }
         ~"expf32" => {
             let x = get_param(decl, first_real_arg);
             let expf = *ccx.intrinsics.get(&~"llvm.exp.f32");
-            Store(bcx, Call(bcx, expf, ~[x]), fcx.llretptr);
+            Store(bcx, Call(bcx, expf, ~[x]), fcx.llretptr.get());
         }
         ~"expf64" => {
             let x = get_param(decl, first_real_arg);
             let expf = *ccx.intrinsics.get(&~"llvm.exp.f64");
-            Store(bcx, Call(bcx, expf, ~[x]), fcx.llretptr);
+            Store(bcx, Call(bcx, expf, ~[x]), fcx.llretptr.get());
         }
         ~"exp2f32" => {
             let x = get_param(decl, first_real_arg);
             let exp2f = *ccx.intrinsics.get(&~"llvm.exp2.f32");
-            Store(bcx, Call(bcx, exp2f, ~[x]), fcx.llretptr);
+            Store(bcx, Call(bcx, exp2f, ~[x]), fcx.llretptr.get());
         }
         ~"exp2f64" => {
             let x = get_param(decl, first_real_arg);
             let exp2f = *ccx.intrinsics.get(&~"llvm.exp2.f64");
-            Store(bcx, Call(bcx, exp2f, ~[x]), fcx.llretptr);
+            Store(bcx, Call(bcx, exp2f, ~[x]), fcx.llretptr.get());
         }
         ~"logf32" => {
             let x = get_param(decl, first_real_arg);
             let logf = *ccx.intrinsics.get(&~"llvm.log.f32");
-            Store(bcx, Call(bcx, logf, ~[x]), fcx.llretptr);
+            Store(bcx, Call(bcx, logf, ~[x]), fcx.llretptr.get());
         }
         ~"logf64" => {
             let x = get_param(decl, first_real_arg);
             let logf = *ccx.intrinsics.get(&~"llvm.log.f64");
-            Store(bcx, Call(bcx, logf, ~[x]), fcx.llretptr);
+            Store(bcx, Call(bcx, logf, ~[x]), fcx.llretptr.get());
         }
         ~"log10f32" => {
             let x = get_param(decl, first_real_arg);
             let log10f = *ccx.intrinsics.get(&~"llvm.log10.f32");
-            Store(bcx, Call(bcx, log10f, ~[x]), fcx.llretptr);
+            Store(bcx, Call(bcx, log10f, ~[x]), fcx.llretptr.get());
         }
         ~"log10f64" => {
             let x = get_param(decl, first_real_arg);
             let log10f = *ccx.intrinsics.get(&~"llvm.log10.f64");
-            Store(bcx, Call(bcx, log10f, ~[x]), fcx.llretptr);
+            Store(bcx, Call(bcx, log10f, ~[x]), fcx.llretptr.get());
         }
         ~"log2f32" => {
             let x = get_param(decl, first_real_arg);
             let log2f = *ccx.intrinsics.get(&~"llvm.log2.f32");
-            Store(bcx, Call(bcx, log2f, ~[x]), fcx.llretptr);
+            Store(bcx, Call(bcx, log2f, ~[x]), fcx.llretptr.get());
         }
         ~"log2f64" => {
             let x = get_param(decl, first_real_arg);
             let log2f = *ccx.intrinsics.get(&~"llvm.log2.f64");
-            Store(bcx, Call(bcx, log2f, ~[x]), fcx.llretptr);
+            Store(bcx, Call(bcx, log2f, ~[x]), fcx.llretptr.get());
         }
         ~"fmaf32" => {
             let a = get_param(decl, first_real_arg);
             let b = get_param(decl, first_real_arg + 1u);
             let c = get_param(decl, first_real_arg + 2u);
             let fmaf = *ccx.intrinsics.get(&~"llvm.fma.f32");
-            Store(bcx, Call(bcx, fmaf, ~[a, b, c]), fcx.llretptr);
+            Store(bcx, Call(bcx, fmaf, ~[a, b, c]), fcx.llretptr.get());
         }
         ~"fmaf64" => {
             let a = get_param(decl, first_real_arg);
             let b = get_param(decl, first_real_arg + 1u);
             let c = get_param(decl, first_real_arg + 2u);
             let fmaf = *ccx.intrinsics.get(&~"llvm.fma.f64");
-            Store(bcx, Call(bcx, fmaf, ~[a, b, c]), fcx.llretptr);
+            Store(bcx, Call(bcx, fmaf, ~[a, b, c]), fcx.llretptr.get());
         }
         ~"fabsf32" => {
             let x = get_param(decl, first_real_arg);
             let fabsf = *ccx.intrinsics.get(&~"llvm.fabs.f32");
-            Store(bcx, Call(bcx, fabsf, ~[x]), fcx.llretptr);
+            Store(bcx, Call(bcx, fabsf, ~[x]), fcx.llretptr.get());
         }
         ~"fabsf64" => {
             let x = get_param(decl, first_real_arg);
             let fabsf = *ccx.intrinsics.get(&~"llvm.fabs.f64");
-            Store(bcx, Call(bcx, fabsf, ~[x]), fcx.llretptr);
+            Store(bcx, Call(bcx, fabsf, ~[x]), fcx.llretptr.get());
         }
         ~"floorf32" => {
             let x = get_param(decl, first_real_arg);
             let floorf = *ccx.intrinsics.get(&~"llvm.floor.f32");
-            Store(bcx, Call(bcx, floorf, ~[x]), fcx.llretptr);
+            Store(bcx, Call(bcx, floorf, ~[x]), fcx.llretptr.get());
         }
         ~"floorf64" => {
             let x = get_param(decl, first_real_arg);
             let floorf = *ccx.intrinsics.get(&~"llvm.floor.f64");
-            Store(bcx, Call(bcx, floorf, ~[x]), fcx.llretptr);
+            Store(bcx, Call(bcx, floorf, ~[x]), fcx.llretptr.get());
         }
         ~"ceilf32" => {
             let x = get_param(decl, first_real_arg);
             let ceilf = *ccx.intrinsics.get(&~"llvm.ceil.f32");
-            Store(bcx, Call(bcx, ceilf, ~[x]), fcx.llretptr);
+            Store(bcx, Call(bcx, ceilf, ~[x]), fcx.llretptr.get());
         }
         ~"ceilf64" => {
             let x = get_param(decl, first_real_arg);
             let ceilf = *ccx.intrinsics.get(&~"llvm.ceil.f64");
-            Store(bcx, Call(bcx, ceilf, ~[x]), fcx.llretptr);
+            Store(bcx, Call(bcx, ceilf, ~[x]), fcx.llretptr.get());
         }
         ~"truncf32" => {
             let x = get_param(decl, first_real_arg);
             let truncf = *ccx.intrinsics.get(&~"llvm.trunc.f32");
-            Store(bcx, Call(bcx, truncf, ~[x]), fcx.llretptr);
+            Store(bcx, Call(bcx, truncf, ~[x]), fcx.llretptr.get());
         }
         ~"truncf64" => {
             let x = get_param(decl, first_real_arg);
             let truncf = *ccx.intrinsics.get(&~"llvm.trunc.f64");
-            Store(bcx, Call(bcx, truncf, ~[x]), fcx.llretptr);
+            Store(bcx, Call(bcx, truncf, ~[x]), fcx.llretptr.get());
         }
         ~"ctpop8" => {
             let x = get_param(decl, first_real_arg);
             let ctpop = *ccx.intrinsics.get(&~"llvm.ctpop.i8");
-            Store(bcx, Call(bcx, ctpop, ~[x]), fcx.llretptr)
+            Store(bcx, Call(bcx, ctpop, ~[x]), fcx.llretptr.get())
         }
         ~"ctpop16" => {
             let x = get_param(decl, first_real_arg);
             let ctpop = *ccx.intrinsics.get(&~"llvm.ctpop.i16");
-            Store(bcx, Call(bcx, ctpop, ~[x]), fcx.llretptr)
+            Store(bcx, Call(bcx, ctpop, ~[x]), fcx.llretptr.get())
         }
         ~"ctpop32" => {
             let x = get_param(decl, first_real_arg);
             let ctpop = *ccx.intrinsics.get(&~"llvm.ctpop.i32");
-            Store(bcx, Call(bcx, ctpop, ~[x]), fcx.llretptr)
+            Store(bcx, Call(bcx, ctpop, ~[x]), fcx.llretptr.get())
         }
         ~"ctpop64" => {
             let x = get_param(decl, first_real_arg);
             let ctpop = *ccx.intrinsics.get(&~"llvm.ctpop.i64");
-            Store(bcx, Call(bcx, ctpop, ~[x]), fcx.llretptr)
+            Store(bcx, Call(bcx, ctpop, ~[x]), fcx.llretptr.get())
         }
         ~"ctlz8" => {
             let x = get_param(decl, first_real_arg);
             let y = C_i1(false);
             let ctlz = *ccx.intrinsics.get(&~"llvm.ctlz.i8");
-            Store(bcx, Call(bcx, ctlz, ~[x, y]), fcx.llretptr)
+            Store(bcx, Call(bcx, ctlz, ~[x, y]), fcx.llretptr.get())
         }
         ~"ctlz16" => {
             let x = get_param(decl, first_real_arg);
             let y = C_i1(false);
             let ctlz = *ccx.intrinsics.get(&~"llvm.ctlz.i16");
-            Store(bcx, Call(bcx, ctlz, ~[x, y]), fcx.llretptr)
+            Store(bcx, Call(bcx, ctlz, ~[x, y]), fcx.llretptr.get())
         }
         ~"ctlz32" => {
             let x = get_param(decl, first_real_arg);
             let y = C_i1(false);
             let ctlz = *ccx.intrinsics.get(&~"llvm.ctlz.i32");
-            Store(bcx, Call(bcx, ctlz, ~[x, y]), fcx.llretptr)
+            Store(bcx, Call(bcx, ctlz, ~[x, y]), fcx.llretptr.get())
         }
         ~"ctlz64" => {
             let x = get_param(decl, first_real_arg);
             let y = C_i1(false);
             let ctlz = *ccx.intrinsics.get(&~"llvm.ctlz.i64");
-            Store(bcx, Call(bcx, ctlz, ~[x, y]), fcx.llretptr)
+            Store(bcx, Call(bcx, ctlz, ~[x, y]), fcx.llretptr.get())
         }
         ~"cttz8" => {
             let x = get_param(decl, first_real_arg);
             let y = C_i1(false);
             let cttz = *ccx.intrinsics.get(&~"llvm.cttz.i8");
-            Store(bcx, Call(bcx, cttz, ~[x, y]), fcx.llretptr)
+            Store(bcx, Call(bcx, cttz, ~[x, y]), fcx.llretptr.get())
         }
         ~"cttz16" => {
             let x = get_param(decl, first_real_arg);
             let y = C_i1(false);
             let cttz = *ccx.intrinsics.get(&~"llvm.cttz.i16");
-            Store(bcx, Call(bcx, cttz, ~[x, y]), fcx.llretptr)
+            Store(bcx, Call(bcx, cttz, ~[x, y]), fcx.llretptr.get())
         }
         ~"cttz32" => {
             let x = get_param(decl, first_real_arg);
             let y = C_i1(false);
             let cttz = *ccx.intrinsics.get(&~"llvm.cttz.i32");
-            Store(bcx, Call(bcx, cttz, ~[x, y]), fcx.llretptr)
+            Store(bcx, Call(bcx, cttz, ~[x, y]), fcx.llretptr.get())
         }
         ~"cttz64" => {
             let x = get_param(decl, first_real_arg);
             let y = C_i1(false);
             let cttz = *ccx.intrinsics.get(&~"llvm.cttz.i64");
-            Store(bcx, Call(bcx, cttz, ~[x, y]), fcx.llretptr)
+            Store(bcx, Call(bcx, cttz, ~[x, y]), fcx.llretptr.get())
         }
         ~"bswap16" => {
             let x = get_param(decl, first_real_arg);
             let cttz = *ccx.intrinsics.get(&~"llvm.bswap.i16");
-            Store(bcx, Call(bcx, cttz, ~[x]), fcx.llretptr)
+            Store(bcx, Call(bcx, cttz, ~[x]), fcx.llretptr.get())
         }
         ~"bswap32" => {
             let x = get_param(decl, first_real_arg);
             let cttz = *ccx.intrinsics.get(&~"llvm.bswap.i32");
-            Store(bcx, Call(bcx, cttz, ~[x]), fcx.llretptr)
+            Store(bcx, Call(bcx, cttz, ~[x]), fcx.llretptr.get())
         }
         ~"bswap64" => {
             let x = get_param(decl, first_real_arg);
             let cttz = *ccx.intrinsics.get(&~"llvm.bswap.i64");
-            Store(bcx, Call(bcx, cttz, ~[x]), fcx.llretptr)
+            Store(bcx, Call(bcx, cttz, ~[x]), fcx.llretptr.get())
         }
         _ => {
             // Could we make this an enum rather than a string? does it get
@@ -1025,9 +1093,12 @@ pub fn trans_foreign_fn(ccx: @CrateContext,
                         id: ast::node_id) {
     let _icx = ccx.insn_ctxt("foreign::build_foreign_fn");
 
-    fn build_rust_fn(ccx: @CrateContext, +path: ast_map::path,
-                     decl: &ast::fn_decl, body: &ast::blk,
-                     id: ast::node_id) -> ValueRef {
+    fn build_rust_fn(ccx: @CrateContext,
+                     +path: ast_map::path,
+                     decl: &ast::fn_decl,
+                     body: &ast::blk,
+                     id: ast::node_id)
+                  -> ValueRef {
         let _icx = ccx.insn_ctxt("foreign::foreign::build_rust_fn");
         let t = ty::node_id_to_type(ccx.tcx, id);
         // XXX: Bad copy.
@@ -1050,8 +1121,11 @@ pub fn trans_foreign_fn(ccx: @CrateContext,
         return llfndecl;
     }
 
-    fn build_shim_fn(ccx: @CrateContext, +path: ast_map::path,
-                     llrustfn: ValueRef, tys: &ShimTypes) -> ValueRef {
+    fn build_shim_fn(ccx: @CrateContext,
+                     +path: ast_map::path,
+                     llrustfn: ValueRef,
+                     tys: &ShimTypes)
+                     -> ValueRef {
         /*!
          *
          * Generate the shim S:
@@ -1069,15 +1143,21 @@ pub fn trans_foreign_fn(ccx: @CrateContext,
 
         let _icx = ccx.insn_ctxt("foreign::foreign::build_shim_fn");
 
-        fn build_args(bcx: block, tys: &ShimTypes,
-                      llargbundle: ValueRef) -> ~[ValueRef] {
+        fn build_args(bcx: block, tys: &ShimTypes, llargbundle: ValueRef)
+                      -> ~[ValueRef] {
             let _icx = bcx.insn_ctxt("foreign::extern::shim::build_args");
             let ccx = bcx.ccx();
             let mut llargvals = ~[];
             let mut i = 0u;
             let n = tys.fn_sig.inputs.len();
-            let llretptr = load_inbounds(bcx, llargbundle, ~[0u, n]);
-            llargvals.push(llretptr);
+
+            if !ty::type_is_immediate(tys.fn_sig.output) {
+                let llretptr = load_inbounds(bcx, llargbundle, ~[0u, n]);
+                llargvals.push(llretptr);
+            } else {
+                llargvals.push(C_null(T_ptr(T_i8())));
+            }
+
             let llenvptr = C_null(T_opaque_box_ptr(bcx.ccx()));
             llargvals.push(llenvptr);
             while i < n {
@@ -1095,24 +1175,43 @@ pub fn trans_foreign_fn(ccx: @CrateContext,
             return llargvals;
         }
 
-        fn build_ret(_bcx: block, _tys: &ShimTypes,
-                     _llargbundle: ValueRef, _llretval: ValueRef)  {
-            // Nop. The return pointer in the Rust ABI function
-            // is wired directly into the return slot in the shim struct
+        fn build_ret(bcx: block,
+                     shim_types: &ShimTypes,
+                     llargbundle: ValueRef,
+                     llretval: ValueRef) {
+            if ty::type_is_immediate(shim_types.fn_sig.output) {
+                // Write the value into the argument bundle.
+                let arg_count = shim_types.fn_sig.inputs.len();
+                let llretptr = load_inbounds(bcx,
+                                             llargbundle,
+                                             ~[0, arg_count]);
+                Store(bcx, llretval, llretptr);
+            } else {
+                // NB: The return pointer in the Rust ABI function is wired
+                // directly into the return slot in the shim struct.
+            }
+
+            build_return(bcx);
         }
 
         let shim_name = link::mangle_internal_name_by_path(
-            ccx, vec::append_one(path, ast_map::path_name(
+            ccx,
+            vec::append_one(path, ast_map::path_name(
                 special_idents::clownshoe_stack_shim
             )));
-        return build_shim_fn_(ccx, shim_name, llrustfn, tys,
-                           lib::llvm::CCallConv,
-                           build_args, build_ret);
+        build_shim_fn_(ccx,
+                       shim_name,
+                       llrustfn,
+                       tys,
+                       lib::llvm::CCallConv,
+                       build_args,
+                       build_ret)
     }
 
-    fn build_wrap_fn(ccx: @CrateContext, llshimfn: ValueRef,
-                     llwrapfn: ValueRef, tys: &ShimTypes)
-    {
+    fn build_wrap_fn(ccx: @CrateContext,
+                     llshimfn: ValueRef,
+                     llwrapfn: ValueRef,
+                     tys: &ShimTypes) {
         /*!
          *
          * Generate the wrapper W:
@@ -1125,23 +1224,29 @@ pub fn trans_foreign_fn(ccx: @CrateContext,
 
         let _icx = ccx.insn_ctxt("foreign::foreign::build_wrap_fn");
 
-        build_wrap_fn_(ccx, tys, llshimfn, llwrapfn,
+        build_wrap_fn_(ccx,
+                       tys,
+                       llshimfn,
+                       llwrapfn,
                        ccx.upcalls.call_shim_on_rust_stack,
-                       build_args, build_ret);
+                       build_args,
+                       build_ret);
 
-        fn build_args(bcx: block, tys: &ShimTypes,
-                      llwrapfn: ValueRef, llargbundle: ValueRef) {
+        fn build_args(bcx: block,
+                      tys: &ShimTypes,
+                      llwrapfn: ValueRef,
+                      llargbundle: ValueRef) {
             let _icx = bcx.insn_ctxt("foreign::foreign::wrap::build_args");
-            tys.fn_ty.build_wrap_args(
-                bcx, tys.llsig.llret_ty,
-                llwrapfn, llargbundle);
+            tys.fn_ty.build_wrap_args(bcx,
+                                      tys.llsig.llret_ty,
+                                      llwrapfn,
+                                      llargbundle);
         }
 
-        fn build_ret(bcx: block, tys: &ShimTypes,
-                     llargbundle: ValueRef) {
+        fn build_ret(bcx: block, tys: &ShimTypes, llargbundle: ValueRef) {
             let _icx = bcx.insn_ctxt("foreign::foreign::wrap::build_ret");
-            tys.fn_ty.build_wrap_ret(
-                bcx, tys.llsig.llarg_tys, llargbundle);
+            tys.fn_ty.build_wrap_ret(bcx, tys.llsig.llarg_tys, llargbundle);
+            build_return(bcx);
         }
     }
 
@@ -1160,12 +1265,20 @@ pub fn register_foreign_fn(ccx: @CrateContext,
                            +path: ast_map::path,
                            node_id: ast::node_id,
                            attrs: &[ast::attribute])
-                        -> ValueRef {
+                           -> ValueRef {
     let _icx = ccx.insn_ctxt("foreign::register_foreign_fn");
+
     let t = ty::node_id_to_type(ccx.tcx, node_id);
+
     let tys = shim_types(ccx, node_id);
     do tys.fn_ty.decl_fn |fnty| {
-        register_fn_fuller(ccx, sp, /*bad*/copy path, node_id, attrs,
-                           t, lib::llvm::CCallConv, fnty)
+        register_fn_fuller(ccx,
+                           sp,
+                           /*bad*/copy path,
+                           node_id,
+                           attrs,
+                           t,
+                           lib::llvm::CCallConv,
+                           fnty)
     }
 }
diff --git a/src/librustc/middle/trans/glue.rs b/src/librustc/middle/trans/glue.rs
index 51d4622d6a1..2072c471245 100644
--- a/src/librustc/middle/trans/glue.rs
+++ b/src/librustc/middle/trans/glue.rs
@@ -499,7 +499,8 @@ pub fn trans_struct_drop(bcx: block,
         }
 
         let self_arg = PointerCast(bcx, llval, params[1]);
-        let args = ~[bcx.fcx.llretptr, self_arg];
+        let args = ~[C_null(T_ptr(T_i8())), self_arg];
+
         Call(bcx, dtor_addr, args);
 
         // Drop the fields
@@ -575,9 +576,7 @@ pub fn make_drop_glue(bcx: block, v0: ValueRef, t: ty::t) {
     build_return(bcx);
 }
 
-pub fn decr_refcnt_maybe_free(bcx: block,
-                              box_ptr: ValueRef,
-                              t: ty::t)
+pub fn decr_refcnt_maybe_free(bcx: block, box_ptr: ValueRef, t: ty::t)
                            -> block {
     let _icx = bcx.insn_ctxt("decr_refcnt_maybe_free");
     let ccx = bcx.ccx();
@@ -737,7 +736,7 @@ pub fn make_generic_glue_inner(ccx: @CrateContext,
                                helper: glue_helper)
                             -> ValueRef {
     let _icx = ccx.insn_ctxt("make_generic_glue_inner");
-    let fcx = new_fn_ctxt(ccx, ~[], llfn, None);
+    let fcx = new_fn_ctxt(ccx, ~[], llfn, ty::mk_nil(ccx.tcx), None);
     lib::llvm::SetLinkage(llfn, lib::llvm::InternalLinkage);
     ccx.stats.n_glues_created += 1u;
     // All glue functions take values passed *by alias*; this is a
@@ -756,8 +755,11 @@ pub fn make_generic_glue_inner(ccx: @CrateContext,
     return llfn;
 }
 
-pub fn make_generic_glue(ccx: @CrateContext, t: ty::t, llfn: ValueRef,
-                         helper: glue_helper, name: &str)
+pub fn make_generic_glue(ccx: @CrateContext,
+                         t: ty::t,
+                         llfn: ValueRef,
+                         helper: glue_helper,
+                         name: &str)
                       -> ValueRef {
     let _icx = ccx.insn_ctxt("make_generic_glue");
     if !ccx.sess.trans_stats() {
@@ -767,8 +769,10 @@ pub fn make_generic_glue(ccx: @CrateContext, t: ty::t, llfn: ValueRef,
     let start = time::get_time();
     let llval = make_generic_glue_inner(ccx, t, llfn, helper);
     let end = time::get_time();
-    log_fn_time(ccx, fmt!("glue %s %s", name, ty_to_short_str(ccx.tcx, t)),
-                start, end);
+    log_fn_time(ccx,
+                fmt!("glue %s %s", name, ty_to_short_str(ccx.tcx, t)),
+                start,
+                end);
     return llval;
 }
 
diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs
index b9e4bad42dd..8cac00252d0 100644
--- a/src/librustc/middle/trans/type_of.rs
+++ b/src/librustc/middle/trans/type_of.rs
@@ -39,20 +39,34 @@ pub fn type_of_explicit_args(ccx: @CrateContext,
     inputs.map(|arg| type_of_explicit_arg(ccx, arg))
 }
 
-pub fn type_of_fn(cx: @CrateContext, inputs: &[ty::arg],
-                  output: ty::t) -> TypeRef {
+pub fn type_of_fn(cx: @CrateContext, inputs: &[ty::arg], output: ty::t)
+               -> TypeRef {
     unsafe {
         let mut atys: ~[TypeRef] = ~[];
 
         // Arg 0: Output pointer.
-        atys.push(T_ptr(type_of(cx, output)));
+        // (if the output type is non-immediate)
+        let output_is_immediate = ty::type_is_immediate(output);
+        let lloutputtype = type_of(cx, output);
+        if !output_is_immediate {
+            atys.push(T_ptr(lloutputtype));
+        } else {
+            // XXX: Eliminate this.
+            atys.push(T_ptr(T_i8()));
+        }
 
         // Arg 1: Environment
         atys.push(T_opaque_box_ptr(cx));
 
         // ... then explicit args.
         atys.push_all(type_of_explicit_args(cx, inputs));
-        return T_fn(atys, llvm::LLVMVoidType());
+
+        // Use the output as the actual return value if it's immediate.
+        if output_is_immediate {
+            T_fn(atys, lloutputtype)
+        } else {
+            T_fn(atys, llvm::LLVMVoidType())
+        }
     }
 }
 
@@ -318,11 +332,9 @@ pub fn llvm_type_name(cx: @CrateContext,
 }
 
 pub fn type_of_dtor(ccx: @CrateContext, self_ty: ty::t) -> TypeRef {
-    unsafe {
-        T_fn(~[T_ptr(type_of(ccx, ty::mk_nil(ccx.tcx))), // output pointer
-               T_ptr(type_of(ccx, self_ty))],            // self arg
-             llvm::LLVMVoidType())
-    }
+    T_fn(~[T_ptr(T_i8()),                   // output pointer
+           T_ptr(type_of(ccx, self_ty))],   // self arg
+         T_nil())
 }
 
 pub fn type_of_rooted(ccx: @CrateContext, t: ty::t) -> TypeRef {
@@ -336,5 +348,5 @@ pub fn type_of_glue_fn(ccx: @CrateContext, t: ty::t) -> TypeRef {
     let tydescpp = T_ptr(T_ptr(ccx.tydesc_type));
     let llty = T_ptr(type_of(ccx, t));
     return T_fn(~[T_ptr(T_nil()), T_ptr(T_nil()), tydescpp, llty],
-                T_void());
+                T_nil());
 }
diff --git a/src/libstd/net_tcp.rs b/src/libstd/net_tcp.rs
index 6bf97843fa1..ef4932d667a 100644
--- a/src/libstd/net_tcp.rs
+++ b/src/libstd/net_tcp.rs
@@ -222,7 +222,11 @@ pub fn connect(input_ip: ip::IpAddr, port: uint,
                         };
                         match connect_result {
                             0i32 => {
-                                debug!("tcp_connect successful");
+                                debug!("tcp_connect successful: \
+                                        stream %x,
+                                        socket data %x",
+                                       stream_handle_ptr as uint,
+                                       socket_data_ptr as uint);
                                 // reusable data that we'll have for the
                                 // duration..
                                 uv::ll::set_data_for_uv_handle(
@@ -556,13 +560,21 @@ pub fn accept(new_conn: TcpNewConnection)
                             server_handle_ptr as *libc::c_void,
                             client_stream_handle_ptr as *libc::c_void) {
                             0i32 => {
-                                debug!(
-                                    "successfully accepted client \
-                                      connection");
+                                debug!("successfully accepted client \
+                                        connection: \
+                                        stream %x, \
+                                        socket data %x",
+                                       client_stream_handle_ptr as uint,
+                                       client_socket_data_ptr as uint);
                                 uv::ll::set_data_for_uv_handle(
                                     client_stream_handle_ptr,
                                     client_socket_data_ptr
                                     as *libc::c_void);
+                                let ptr = uv::ll::get_data_for_uv_handle(
+                                    client_stream_handle_ptr);
+                                debug!("ptrs: %x %x",
+                                       client_socket_data_ptr as uint,
+                                       ptr as uint);
                                 result_ch.send(None);
                             }
                             _ => {
@@ -1268,14 +1280,15 @@ impl ToTcpErr for uv::ll::uv_err_data {
 }
 
 extern fn on_tcp_read_cb(stream: *uv::ll::uv_stream_t,
-                    nread: libc::ssize_t,
-                    buf: uv::ll::uv_buf_t) {
+                         nread: libc::ssize_t,
+                         buf: uv::ll::uv_buf_t) {
     unsafe {
-        debug!("entering on_tcp_read_cb stream: %? nread: %?",
-                        stream, nread);
+        debug!("entering on_tcp_read_cb stream: %x nread: %?",
+                        stream as uint, nread);
         let loop_ptr = uv::ll::get_loop_for_uv_handle(stream);
         let socket_data_ptr = uv::ll::get_data_for_uv_handle(stream)
             as *TcpSocketData;
+        debug!("socket data is %x", socket_data_ptr as uint);
         match nread as int {
           // incoming err.. probably eof
           -1 => {
diff --git a/src/libstd/uv_ll.rs b/src/libstd/uv_ll.rs
index ab3074e49dd..98d76c6b9aa 100644
--- a/src/libstd/uv_ll.rs
+++ b/src/libstd/uv_ll.rs
@@ -1156,8 +1156,7 @@ pub unsafe fn set_data_for_uv_loop(loop_ptr: *libc::c_void,
 pub unsafe fn get_data_for_uv_handle<T>(handle: *T) -> *libc::c_void {
     return rustrt::rust_uv_get_data_for_uv_handle(handle as *libc::c_void);
 }
-pub unsafe fn set_data_for_uv_handle<T, U>(handle: *T,
-                    data: *U) {
+pub unsafe fn set_data_for_uv_handle<T, U>(handle: *T, data: *U) {
     rustrt::rust_uv_set_data_for_uv_handle(handle as *libc::c_void,
                                            data as *libc::c_void);
 }
diff --git a/src/rt/rust_uv.cpp b/src/rt/rust_uv.cpp
index 325b10b92df..8cf2bd4b4ac 100644
--- a/src/rt/rust_uv.cpp
+++ b/src/rt/rust_uv.cpp
@@ -401,8 +401,7 @@ rust_uv_get_data_for_uv_handle(uv_handle_t* handle) {
 }
 
 extern "C" void
-rust_uv_set_data_for_uv_handle(uv_handle_t* handle,
-        void* data) {
+rust_uv_set_data_for_uv_handle(uv_handle_t* handle, void* data) {
     handle->data = data;
 }
 
diff --git a/src/test/run-pass/const-bound.rs b/src/test/run-pass/const-bound.rs
index d4467ca0c7a..685d86c740d 100644
--- a/src/test/run-pass/const-bound.rs
+++ b/src/test/run-pass/const-bound.rs
@@ -17,11 +17,11 @@ fn foo<T:Copy + Const>(x: T) -> T { x }
 struct F { field: int }
 
 pub fn main() {
-    foo(1);
+    /*foo(1);
     foo(~"hi");
     foo(~[1, 2, 3]);
     foo(F{field: 42});
     foo((1, 2u));
-    foo(@1);
+    foo(@1);*/
     foo(~1);
 }
diff --git a/src/test/run-pass/extern-call.rs b/src/test/run-pass/extern-call.rs
index 6e41f91dcd7..37e531eaa8e 100644
--- a/src/test/run-pass/extern-call.rs
+++ b/src/test/run-pass/extern-call.rs
@@ -11,7 +11,7 @@
 mod rustrt {
     pub extern {
         pub fn rust_dbg_call(cb: *u8, data: libc::uintptr_t)
-                          -> libc::uintptr_t;
+                             -> libc::uintptr_t;
     }
 }