about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNiko Matsakis <niko@alum.mit.edu>2013-02-27 19:28:37 -0500
committerNiko Matsakis <niko@alum.mit.edu>2013-02-28 16:03:49 -0500
commitfebdb49e9269724058aacf645610912bf26ecdb4 (patch)
treedce869ea86f1bba8ced97212497e4a6a309ad922
parent85aaf392914626441b3b681edb4d79552e078781 (diff)
downloadrust-febdb49e9269724058aacf645610912bf26ecdb4.tar.gz
rust-febdb49e9269724058aacf645610912bf26ecdb4.zip
Change bare functions so that they are represented by a single pointer.
The basic idea is that we add a new kind of adjustment, AutoAddEnv, that pads
an extern fn into a closure by adding the extra NULL word. Then there are a few
misc changes in trans to get the LLVM types to match up.

Fixes #4808.
-rw-r--r--src/librustc/middle/astencode.rs14
-rw-r--r--src/librustc/middle/borrowck/gather_loans.rs18
-rw-r--r--src/librustc/middle/borrowck/mod.rs17
-rw-r--r--src/librustc/middle/mem_categorization.rs46
-rw-r--r--src/librustc/middle/moves.rs4
-rw-r--r--src/librustc/middle/trans/callee.rs28
-rw-r--r--src/librustc/middle/trans/common.rs6
-rw-r--r--src/librustc/middle/trans/expr.rs137
-rw-r--r--src/librustc/middle/trans/type_of.rs11
-rw-r--r--src/librustc/middle/ty.rs38
-rw-r--r--src/librustc/middle/typeck/check/method.rs18
-rw-r--r--src/librustc/middle/typeck/check/mod.rs4
-rw-r--r--src/librustc/middle/typeck/check/regionck.rs52
-rw-r--r--src/librustc/middle/typeck/check/writeback.rs24
-rw-r--r--src/librustc/middle/typeck/infer/coercion.rs22
15 files changed, 311 insertions, 128 deletions
diff --git a/src/librustc/middle/astencode.rs b/src/librustc/middle/astencode.rs
index 02cd5afc920..1b6452e977e 100644
--- a/src/librustc/middle/astencode.rs
+++ b/src/librustc/middle/astencode.rs
@@ -451,9 +451,17 @@ impl tr for ast::def {
 
 impl tr for ty::AutoAdjustment {
     fn tr(&self, xcx: @ExtendedDecodeContext) -> ty::AutoAdjustment {
-        ty::AutoAdjustment {
-            autoderefs: self.autoderefs,
-            autoref: self.autoref.map(|ar| ar.tr(xcx)),
+        match self {
+            &ty::AutoAddEnv(r, s) => {
+                ty::AutoAddEnv(r.tr(xcx), s)
+            }
+
+            &ty::AutoDerefRef(ref adr) => {
+                ty::AutoDerefRef(ty::AutoDerefRef {
+                    autoderefs: adr.autoderefs,
+                    autoref: adr.autoref.map(|ar| ar.tr(xcx)),
+                })
+            }
         }
     }
 }
diff --git a/src/librustc/middle/borrowck/gather_loans.rs b/src/librustc/middle/borrowck/gather_loans.rs
index 546e9359a32..236592874cb 100644
--- a/src/librustc/middle/borrowck/gather_loans.rs
+++ b/src/librustc/middle/borrowck/gather_loans.rs
@@ -299,17 +299,27 @@ impl GatherLoanCtxt {
                expr_repr(self.tcx(), expr), adjustment);
         let _i = indenter();
 
-        match adjustment.autoref {
-            None => {
+        match *adjustment {
+            ty::AutoAddEnv(*) => {
+                debug!("autoaddenv -- no autoref");
+                return;
+            }
+
+            ty::AutoDerefRef(
+                ty::AutoDerefRef {
+                    autoref: None, _ }) => {
                 debug!("no autoref");
                 return;
             }
 
-            Some(ref autoref) => {
+            ty::AutoDerefRef(
+                ty::AutoDerefRef {
+                    autoref: Some(ref autoref),
+                    autoderefs: autoderefs}) => {
                 let mcx = &mem_categorization_ctxt {
                     tcx: self.tcx(),
                     method_map: self.bccx.method_map};
-                let mut cmt = mcx.cat_expr_autoderefd(expr, adjustment);
+                let mut cmt = mcx.cat_expr_autoderefd(expr, autoderefs);
                 debug!("after autoderef, cmt=%s", self.bccx.cmt_to_repr(cmt));
 
                 match autoref.kind {
diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs
index 568bc5b5e70..87ff8554fd9 100644
--- a/src/librustc/middle/borrowck/mod.rs
+++ b/src/librustc/middle/borrowck/mod.rs
@@ -478,9 +478,20 @@ pub impl BorrowckCtxt {
     }
 
     fn cat_expr_autoderefd(&self, expr: @ast::expr,
-                           adj: @ty::AutoAdjustment)
-                        -> cmt {
-        cat_expr_autoderefd(self.tcx, self.method_map, expr, adj)
+                           adj: @ty::AutoAdjustment) -> cmt {
+        match *adj {
+            ty::AutoAddEnv(*) => {
+                // no autoderefs
+                cat_expr_unadjusted(self.tcx, self.method_map, expr)
+            }
+
+            ty::AutoDerefRef(
+                ty::AutoDerefRef {
+                    autoderefs: autoderefs, _}) => {
+                cat_expr_autoderefd(self.tcx, self.method_map, expr,
+                                    autoderefs)
+            }
+        }
     }
 
     fn cat_def(&self,
diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs
index f027ca99d51..e1a2079be25 100644
--- a/src/librustc/middle/mem_categorization.rs
+++ b/src/librustc/middle/mem_categorization.rs
@@ -241,12 +241,12 @@ pub fn cat_expr_autoderefd(
     tcx: ty::ctxt,
     method_map: typeck::method_map,
     expr: @ast::expr,
-    adj: @ty::AutoAdjustment) -> cmt {
-
+    autoderefs: uint) -> cmt
+{
     let mcx = &mem_categorization_ctxt {
         tcx: tcx, method_map: method_map
     };
-    return mcx.cat_expr_autoderefd(expr, adj);
+    return mcx.cat_expr_autoderefd(expr, autoderefs);
 }
 
 pub fn cat_def(
@@ -361,28 +361,38 @@ pub impl mem_categorization_ctxt {
                 self.cat_expr_unadjusted(expr)
             }
 
-            Some(adjustment) => {
-                match adjustment.autoref {
-                    Some(_) => {
-                        // Equivalent to &*expr or something similar.
-                        // This is an rvalue, effectively.
-                        let expr_ty = ty::expr_ty(self.tcx, expr);
-                        self.cat_rvalue(expr, expr_ty)
-                    }
-                    None => {
-                        // Equivalent to *expr or something similar.
-                        self.cat_expr_autoderefd(expr, adjustment)
-                    }
-                }
+            Some(@ty::AutoAddEnv(*)) => {
+                // Convert a bare fn to a closure by adding NULL env.
+                // Result is an rvalue.
+                let expr_ty = ty::expr_ty_adjusted(self.tcx, expr);
+                self.cat_rvalue(expr, expr_ty)
+            }
+
+            Some(
+                @ty::AutoDerefRef(
+                    ty::AutoDerefRef {
+                        autoref: Some(_), _})) => {
+                // Equivalent to &*expr or something similar.
+                // Result is an rvalue.
+                let expr_ty = ty::expr_ty_adjusted(self.tcx, expr);
+                self.cat_rvalue(expr, expr_ty)
+            }
+
+            Some(
+                @ty::AutoDerefRef(
+                    ty::AutoDerefRef {
+                        autoref: None, autoderefs: autoderefs})) => {
+                // Equivalent to *expr or something similar.
+                self.cat_expr_autoderefd(expr, autoderefs)
             }
         }
     }
 
     fn cat_expr_autoderefd(&self,
                            expr: @ast::expr,
-                           adjustment: &ty::AutoAdjustment) -> cmt {
+                           autoderefs: uint) -> cmt {
         let mut cmt = self.cat_expr_unadjusted(expr);
-        for uint::range(1, adjustment.autoderefs+1) |deref| {
+        for uint::range(1, autoderefs+1) |deref| {
             cmt = self.cat_deref(expr, cmt, deref);
         }
         return cmt;
diff --git a/src/librustc/middle/moves.rs b/src/librustc/middle/moves.rs
index b23066c1d96..8e344c0afc7 100644
--- a/src/librustc/middle/moves.rs
+++ b/src/librustc/middle/moves.rs
@@ -410,7 +410,9 @@ impl VisitContext {
         // those adjustments is to take a reference, then it's only
         // reading the underlying expression, not moving it.
         let comp_mode = match self.tcx.adjustments.find(&expr.id) {
-            Some(adj) if adj.autoref.is_some() => Read,
+            Some(@ty::AutoDerefRef(
+                ty::AutoDerefRef {
+                    autoref: Some(_), _})) => Read,
             _ => expr_mode.component_mode(expr)
         };
 
diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs
index 6924ccf3ab6..ff37e6d76c6 100644
--- a/src/librustc/middle/trans/callee.rs
+++ b/src/librustc/middle/trans/callee.rs
@@ -82,10 +82,25 @@ pub fn trans(bcx: block, expr: @ast::expr) -> Callee {
     }
 
     // any other expressions are closures:
-    return closure_callee(&expr::trans_to_datum(bcx, expr));
-
-    fn closure_callee(db: &DatumBlock) -> Callee {
-        return Callee {bcx: db.bcx, data: Closure(db.datum)};
+    return datum_callee(bcx, expr);
+
+    fn datum_callee(bcx: block, expr: @ast::expr) -> Callee {
+        let DatumBlock {bcx, datum} = expr::trans_to_datum(bcx, expr);
+        match ty::get(datum.ty).sty {
+            ty::ty_bare_fn(*) => {
+                let llval = datum.to_appropriate_llval(bcx);
+                return Callee {bcx: bcx, data: Fn(FnData {llfn: llval})};
+            }
+            ty::ty_closure(*) => {
+                return Callee {bcx: bcx, data: Closure(datum)};
+            }
+            _ => {
+                bcx.tcx().sess.span_bug(
+                    expr.span,
+                    fmt!("Type of callee is neither bare-fn nor closure: %s",
+                         bcx.ty_to_str(datum.ty)));
+            }
+        }
     }
 
     fn fn_callee(bcx: block, fd: FnData) -> Callee {
@@ -117,7 +132,7 @@ pub fn trans(bcx: block, expr: @ast::expr) -> Callee {
             ast::def_binding(*) |
             ast::def_upvar(*) |
             ast::def_self(*) => {
-                closure_callee(&expr::trans_to_datum(bcx, ref_expr))
+                datum_callee(bcx, ref_expr)
             }
             ast::def_mod(*) | ast::def_foreign_mod(*) |
             ast::def_const(*) | ast::def_ty(*) | ast::def_prim_ty(*) |
@@ -380,7 +395,6 @@ pub fn trans_rtcall_or_lang_call_with_type_params(bcx: block,
                                                     fty);
                     let mut llfnty = type_of::type_of(callee.bcx.ccx(),
                                                       substituted);
-                    llfnty = lib::llvm::struct_tys(llfnty)[0];
                     new_llval = PointerCast(callee.bcx, fn_data.llfn, llfnty);
                 }
                 _ => fail!()
@@ -703,6 +717,8 @@ pub fn trans_arg_expr(bcx: block,
                     }
 
                     ast::by_copy => {
+                        debug!("by copy arg with type %s, storing to scratch",
+                               ty_to_str(ccx.tcx, arg_datum.ty));
                         let scratch = scratch_datum(bcx, arg_datum.ty, false);
 
                         arg_datum.store_to_datum(bcx, arg_expr.id,
diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs
index 48a3d2c82c8..04fefd4fb33 100644
--- a/src/librustc/middle/trans/common.rs
+++ b/src/librustc/middle/trans/common.rs
@@ -1344,6 +1344,12 @@ pub fn expr_ty(bcx: block, ex: @ast::expr) -> ty::t {
     node_id_type(bcx, ex.id)
 }
 
+pub fn expr_ty_adjusted(bcx: block, ex: @ast::expr) -> ty::t {
+    let tcx = bcx.tcx();
+    let t = ty::expr_ty_adjusted(tcx, ex);
+    monomorphize_type(bcx, t)
+}
+
 pub fn node_id_type_params(bcx: block, id: ast::node_id) -> ~[ty::t] {
     let tcx = bcx.tcx();
     let params = ty::node_id_to_type_params(tcx, id);
diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs
index a7b12d13d4e..8ceb539351e 100644
--- a/src/librustc/middle/trans/expr.rs
+++ b/src/librustc/middle/trans/expr.rs
@@ -136,7 +136,8 @@ use middle::trans::machine;
 use middle::trans::meth;
 use middle::trans::tvec;
 use middle::ty::struct_mutable_fields;
-use middle::ty::{AutoPtr, AutoBorrowVec, AutoBorrowVecRef, AutoBorrowFn};
+use middle::ty::{AutoPtr, AutoBorrowVec, AutoBorrowVecRef, AutoBorrowFn,
+                 AutoDerefRef, AutoAddEnv};
 use util::common::indenter;
 use util::ppaux::ty_to_str;
 
@@ -189,7 +190,14 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
         None => {
             trans_to_datum_unadjusted(bcx, expr)
         }
-        Some(adj) => {
+        Some(@AutoAddEnv(*)) => {
+            let mut bcx = bcx;
+            let mut datum = unpack_datum!(bcx, {
+                trans_to_datum_unadjusted(bcx, expr)
+            });
+            add_env(bcx, expr, datum)
+        }
+        Some(@AutoDerefRef(ref adj)) => {
             let mut bcx = bcx;
             let mut datum = unpack_datum!(bcx, {
                 trans_to_datum_unadjusted(bcx, expr)
@@ -258,6 +266,25 @@ pub fn trans_to_datum(bcx: block, expr: @ast::expr) -> DatumBlock {
         DatumBlock {bcx: bcx, datum: scratch}
     }
 
+    fn add_env(bcx: block, expr: @ast::expr, datum: Datum) -> DatumBlock {
+        // This is not the most efficient thing possible; since closures
+        // are two words it'd be better if this were compiled in
+        // 'dest' mode, but I can't find a nice way to structure the
+        // code and keep it DRY that accommodates that use case at the
+        // moment.
+
+        let tcx = bcx.tcx();
+        let closure_ty = expr_ty_adjusted(bcx, expr);
+        debug!("add_env(closure_ty=%s)", ty_to_str(tcx, closure_ty));
+        let scratch = scratch_datum(bcx, closure_ty, false);
+        let llfn = GEPi(bcx, scratch.val, [0u, abi::fn_field_code]);
+        assert datum.appropriate_mode() == ByValue;
+        Store(bcx, datum.to_appropriate_llval(bcx), llfn);
+        let llenv = GEPi(bcx, scratch.val, [0u, abi::fn_field_box]);
+        Store(bcx, base::null_env_ptr(bcx), llenv);
+        DatumBlock {bcx: bcx, datum: scratch}
+    }
+
     fn auto_slice_and_ref(bcx: block, datum: Datum) -> DatumBlock {
         let DatumBlock { bcx, datum } = auto_slice(bcx, datum);
         auto_ref(bcx, datum)
@@ -412,6 +439,9 @@ fn trans_rvalue_datum_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
     trace_span!(bcx, expr.span, @shorten(bcx.expr_to_str(expr)));
 
     match expr.node {
+        ast::expr_path(_) => {
+            return trans_def_datum_unadjusted(bcx, expr, bcx.def(expr.id));
+        }
         ast::expr_vstore(contents, ast::expr_vstore_box) |
         ast::expr_vstore(contents, ast::expr_vstore_mut_box) => {
             return tvec::trans_uniq_or_managed_vstore(bcx, heap_managed,
@@ -677,22 +707,13 @@ fn trans_def_dps_unadjusted(bcx: block, ref_expr: @ast::expr,
     };
 
     match def {
-        ast::def_fn(did, _) | ast::def_static_method(did, None, _) => {
-            let fn_data = callee::trans_fn_ref(bcx, did, ref_expr.id);
-            return fn_data_to_datum(bcx, did, fn_data, lldest);
-        }
-        ast::def_static_method(impl_did, Some(trait_did), _) => {
-            let fn_data = meth::trans_static_method_callee(bcx, impl_did,
-                                                           trait_did,
-                                                           ref_expr.id);
-            return fn_data_to_datum(bcx, impl_did, fn_data, lldest);
-        }
         ast::def_variant(tid, vid) => {
             let variant_info = ty::enum_variant_with_id(ccx.tcx, tid, vid);
             if variant_info.args.len() > 0u {
                 // N-ary variant.
                 let fn_data = callee::trans_fn_ref(bcx, vid, ref_expr.id);
-                return fn_data_to_datum(bcx, vid, fn_data, lldest);
+                Store(bcx, fn_data.llfn, lldest);
+                return bcx;
             } else if !ty::enum_is_univariant(ccx.tcx, tid) {
                 // Nullary variant.
                 let lldiscrimptr = GEPi(bcx, lldest, [0u, 0u]);
@@ -717,6 +738,66 @@ fn trans_def_dps_unadjusted(bcx: block, ref_expr: @ast::expr,
     }
 }
 
+fn trans_def_datum_unadjusted(bcx: block,
+                              ref_expr: @ast::expr,
+                              def: ast::def) -> DatumBlock
+{
+    let _icx = bcx.insn_ctxt("trans_def_datum_unadjusted");
+
+    match def {
+        ast::def_fn(did, _) | ast::def_static_method(did, None, _) => {
+            let fn_data = callee::trans_fn_ref(bcx, did, ref_expr.id);
+            return fn_data_to_datum(bcx, ref_expr, did, fn_data);
+        }
+        ast::def_static_method(impl_did, Some(trait_did), _) => {
+            let fn_data = meth::trans_static_method_callee(bcx, impl_did,
+                                                           trait_did,
+                                                           ref_expr.id);
+            return fn_data_to_datum(bcx, ref_expr, impl_did, fn_data);
+        }
+        _ => {
+            bcx.tcx().sess.span_bug(ref_expr.span, fmt!(
+                "Non-DPS def %? referened by %s",
+                def, bcx.node_id_to_str(ref_expr.id)));
+        }
+    }
+
+    fn fn_data_to_datum(bcx: block,
+                        ref_expr: @ast::expr,
+                        def_id: ast::def_id,
+                        fn_data: callee::FnData) -> DatumBlock {
+        /*!
+        *
+        * Translates a reference to a top-level fn item into a rust
+        * value.  This is just a fn pointer.
+        */
+
+        let is_extern = {
+            let fn_tpt = ty::lookup_item_type(bcx.tcx(), def_id);
+            ty::ty_fn_purity(fn_tpt.ty) == ast::extern_fn
+        };
+        let (rust_ty, llval) = if is_extern {
+            let rust_ty = ty::mk_ptr(
+                bcx.tcx(),
+                ty::mt {
+                    ty: ty::mk_mach_uint(bcx.tcx(), ast::ty_u8),
+                    mutbl: ast::m_imm
+                }); // *u8
+            (rust_ty, PointerCast(bcx, fn_data.llfn, T_ptr(T_i8())))
+        } else {
+            let fn_ty = expr_ty(bcx, ref_expr);
+            (fn_ty, fn_data.llfn)
+        };
+        return DatumBlock {
+            bcx: bcx,
+            datum: Datum {val: llval,
+                          ty: rust_ty,
+                          mode: ByValue,
+                          source: RevokeClean}
+        };
+    }
+}
+
 fn trans_lvalue_unadjusted(bcx: block, expr: @ast::expr) -> DatumBlock {
     /*!
      *
@@ -1004,36 +1085,6 @@ pub fn trans_local_var(bcx: block, def: ast::def) -> Datum {
     }
 }
 
-fn fn_data_to_datum(bcx: block,
-                    def_id: ast::def_id,
-                    fn_data: callee::FnData,
-                    lldest: ValueRef) -> block {
-    //!
-    //
-    // Translates a reference to a top-level fn item into a rust
-    // value.  This is generally a Rust closure pair: (fn ptr, env)
-    // where the environment is NULL.  However, extern functions for
-    // interfacing with C are represted as just the fn ptr with type
-    // *u8.
-    //
-    // Strictly speaking, references to extern fns ought to be
-    // RvalueDatumExprs, but it's not worth the complexity to avoid the
-    // extra stack slot that LLVM probably optimizes away anyhow.
-
-    let fn_tpt = ty::lookup_item_type(bcx.tcx(), def_id);
-    if ty::ty_fn_purity(fn_tpt.ty) == ast::extern_fn {
-        let val = PointerCast(bcx, fn_data.llfn, T_ptr(T_i8()));
-        Store(bcx, val, lldest);
-        return bcx;
-    }
-
-    let llfn = GEPi(bcx, lldest, [0u, abi::fn_field_code]);
-    Store(bcx, fn_data.llfn, llfn);
-    let llenv = GEPi(bcx, lldest, [0u, abi::fn_field_box]);
-    Store(bcx, base::null_env_ptr(bcx), llenv);
-    return bcx;
-}
-
 // The optional node ID here is the node ID of the path identifying the enum
 // variant in use. If none, this cannot possibly an enum variant (so, if it
 // is and `node_id_opt` is none, this function fails).
diff --git a/src/librustc/middle/trans/type_of.rs b/src/librustc/middle/trans/type_of.rs
index 8275db8cdb2..7af33eb2b9d 100644
--- a/src/librustc/middle/trans/type_of.rs
+++ b/src/librustc/middle/trans/type_of.rs
@@ -130,8 +130,7 @@ pub fn sizing_type_of(cx: @CrateContext, t: ty::t) -> TypeRef {
             T_struct(~[T_ptr(T_i8()), T_ptr(T_i8())])
         }
 
-        // FIXME(#4804) Bare fn repr
-        ty::ty_bare_fn(*) => T_struct(~[T_ptr(T_i8()), T_ptr(T_i8())]),
+        ty::ty_bare_fn(*) => T_ptr(T_i8()),
         ty::ty_closure(*) => T_struct(~[T_ptr(T_i8()), T_ptr(T_i8())]),
         ty::ty_trait(_, _, vstore) => T_opaque_trait(cx, vstore),
 
@@ -169,7 +168,9 @@ pub fn sizing_type_of(cx: @CrateContext, t: ty::t) -> TypeRef {
         ty::ty_enum(def_id, _) => T_struct(enum_body_types(cx, def_id, t)),
 
         ty::ty_self | ty::ty_infer(*) | ty::ty_param(*) | ty::ty_err(*) => {
-            cx.tcx.sess.bug(~"fictitious type in sizing_type_of()")
+            cx.tcx.sess.bug(
+                fmt!("fictitious type %? in sizing_type_of()",
+                     ty::get(t).sty))
         }
     };
 
@@ -266,9 +267,7 @@ pub fn type_of(cx: @CrateContext, t: ty::t) -> TypeRef {
         T_struct(~[T_struct(tys)])
       }
 
-      // FIXME(#4804) Bare fn repr
-      // ty::ty_bare_fn(_) => T_ptr(type_of_fn_from_ty(cx, t)),
-      ty::ty_bare_fn(_) => T_fn_pair(cx, type_of_fn_from_ty(cx, t)),
+      ty::ty_bare_fn(_) => T_ptr(type_of_fn_from_ty(cx, t)),
       ty::ty_closure(_) => T_fn_pair(cx, type_of_fn_from_ty(cx, t)),
       ty::ty_trait(_, _, vstore) => T_opaque_trait(cx, vstore),
       ty::ty_type => T_ptr(cx.tydesc_type),
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 182ab11b917..deede95dc03 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -172,7 +172,14 @@ impl cmp::Eq for region_variance {
 
 #[auto_encode]
 #[auto_decode]
-pub struct AutoAdjustment {
+pub enum AutoAdjustment {
+    AutoAddEnv(ty::Region, ast::Sigil),
+    AutoDerefRef(AutoDerefRef)
+}
+
+#[auto_encode]
+#[auto_decode]
+pub struct AutoDerefRef {
     autoderefs: uint,
     autoref: Option<AutoRef>
 }
@@ -198,7 +205,7 @@ pub enum AutoRefKind {
     AutoBorrowVecRef,
 
     /// Convert from @fn()/~fn() to &fn()
-    AutoBorrowFn,
+    AutoBorrowFn
 }
 
 // Stores information about provided methods (a.k.a. default methods) in
@@ -1477,7 +1484,6 @@ pub fn type_is_structural(ty: t) -> bool {
     match get(ty).sty {
       ty_rec(_) | ty_struct(*) | ty_tup(_) | ty_enum(*) |
       ty_closure(_) |
-      ty_bare_fn(_) | // FIXME(#4804) Bare fn repr
       ty_trait(*) |
       ty_evec(_, vstore_fixed(_)) | ty_estr(vstore_fixed(_)) |
       ty_evec(_, vstore_slice(_)) | ty_estr(vstore_slice(_))
@@ -1587,7 +1593,7 @@ pub pure fn type_is_scalar(ty: t) -> bool {
     match get(ty).sty {
       ty_nil | ty_bool | ty_int(_) | ty_float(_) | ty_uint(_) |
       ty_infer(IntVar(_)) | ty_infer(FloatVar(_)) | ty_type |
-      ty_ptr(_) => true,
+      ty_bare_fn(*) | ty_ptr(_) => true,
       _ => false
     }
 }
@@ -2884,7 +2890,25 @@ pub fn expr_ty_adjusted(cx: ctxt, expr: @ast::expr) -> t {
     return match cx.adjustments.find(&expr.id) {
         None => unadjusted_ty,
 
-        Some(adj) => {
+        Some(@AutoAddEnv(r, s)) => {
+            match ty::get(unadjusted_ty).sty {
+                ty::ty_bare_fn(ref b) => {
+                    ty::mk_closure(
+                        cx,
+                        ty::ClosureTy {purity: b.purity,
+                                       sigil: s,
+                                       onceness: ast::Many,
+                                       region: r,
+                                       sig: copy b.sig})
+                }
+                ref b => {
+                    cx.sess.bug(
+                        fmt!("add_env adjustment on non-bare-fn: %?", b));
+                }
+            }
+        }
+
+        Some(@AutoDerefRef(ref adj)) => {
             let mut adjusted_ty = unadjusted_ty;
 
             for uint::range(0, adj.autoderefs) |i| {
@@ -3066,9 +3090,11 @@ pub fn expr_kind(tcx: ctxt,
     match expr.node {
         ast::expr_path(*) => {
             match resolve_expr(tcx, expr) {
-                ast::def_fn(*) | ast::def_static_method(*) |
                 ast::def_variant(*) | ast::def_struct(*) => RvalueDpsExpr,
 
+                // Fn pointers are just scalar values.
+                ast::def_fn(*) | ast::def_static_method(*) => RvalueDatumExpr,
+
                 // Note: there is actually a good case to be made that
                 // def_args, particularly those of immediate type, ought to
                 // considered rvalues.
diff --git a/src/librustc/middle/typeck/check/method.rs b/src/librustc/middle/typeck/check/method.rs
index 34b650aa180..63d09d88f52 100644
--- a/src/librustc/middle/typeck/check/method.rs
+++ b/src/librustc/middle/typeck/check/method.rs
@@ -799,26 +799,28 @@ pub impl LookupContext {
                 let region = self.infcx().next_region_var(self.expr.span,
                                                           self.expr.id);
                 (ty::mk_rptr(tcx, region, self_mt),
-                 ty::AutoAdjustment {
+                 ty::AutoDerefRef(ty::AutoDerefRef {
                      autoderefs: autoderefs+1,
                      autoref: Some(ty::AutoRef {kind: AutoPtr,
                                                 region: region,
-                                                mutbl: self_mt.mutbl})})
+                                                mutbl: self_mt.mutbl})}))
             }
             ty::ty_evec(self_mt, vstore_slice(_))
             if self_mt.mutbl == m_mutbl => {
                 let region = self.infcx().next_region_var(self.expr.span,
                                                           self.expr.id);
                 (ty::mk_evec(tcx, self_mt, vstore_slice(region)),
-                 ty::AutoAdjustment {
+                 ty::AutoDerefRef(ty::AutoDerefRef {
                     autoderefs: autoderefs,
                     autoref: Some(ty::AutoRef {kind: AutoBorrowVec,
                                                region: region,
-                                               mutbl: self_mt.mutbl})})
+                                               mutbl: self_mt.mutbl})}))
             }
             _ => {
-                (self_ty, ty::AutoAdjustment {autoderefs: autoderefs,
-                                              autoref: None})
+                (self_ty,
+                 ty::AutoDerefRef(ty::AutoDerefRef {
+                     autoderefs: autoderefs,
+                     autoref: None}))
             }
         };
     }
@@ -947,14 +949,14 @@ pub impl LookupContext {
                 Some(mme) => {
                     self.fcx.write_adjustment(
                         self.self_expr.id,
-                        @ty::AutoAdjustment {
+                        @ty::AutoDerefRef(ty::AutoDerefRef {
                             autoderefs: autoderefs,
                             autoref: Some(ty::AutoRef {
                                 kind: kind,
                                 region: region,
                                 mutbl: *mutbl,
                             }),
-                        });
+                        }));
                     return Some(mme);
                 }
             }
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index e63e46ace3d..ec5156ae1f7 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -736,7 +736,9 @@ pub impl FnCtxt {
         if derefs == 0 { return; }
         self.write_adjustment(
             node_id,
-            @ty::AutoAdjustment { autoderefs: derefs, autoref: None }
+            @ty::AutoDerefRef(ty::AutoDerefRef {
+                autoderefs: derefs,
+                autoref: None })
         );
     }
 
diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs
index c91607489ec..7187f15e749 100644
--- a/src/librustc/middle/typeck/check/regionck.rs
+++ b/src/librustc/middle/typeck/check/regionck.rs
@@ -184,8 +184,13 @@ pub fn visit_expr(expr: @ast::expr, &&rcx: @mut Rcx, v: rvt) {
     debug!("visit_expr(e=%s)", rcx.fcx.expr_to_str(expr));
 
     for rcx.fcx.inh.adjustments.find(&expr.id).each |adjustment| {
-        for adjustment.autoref.each |autoref| {
-            guarantor::for_autoref(rcx, expr, *adjustment, autoref);
+        match *adjustment {
+            @ty::AutoDerefRef(
+                ty::AutoDerefRef {
+                    autoderefs: autoderefs, autoref: Some(ref autoref)}) => {
+                guarantor::for_autoref(rcx, expr, autoderefs, autoref);
+            }
+            _ => {}
         }
     }
 
@@ -329,9 +334,11 @@ pub fn constrain_auto_ref(rcx: @mut Rcx, expr: @ast::expr) {
 
     let adjustment = rcx.fcx.inh.adjustments.find(&expr.id);
     let region = match adjustment {
-        Some(@ty::AutoAdjustment { autoref: Some(ref auto_ref), _ }) => {
+        Some(@ty::AutoDerefRef(
+            ty::AutoDerefRef {
+                autoref: Some(ref auto_ref), _})) => {
             auto_ref.region
-        },
+        }
         _ => { return; }
     };
 
@@ -563,7 +570,7 @@ pub mod guarantor {
 
     pub fn for_autoref(rcx: @mut Rcx,
                        expr: @ast::expr,
-                       adjustment: &ty::AutoAdjustment,
+                       autoderefs: uint,
                        autoref: &ty::AutoRef) {
         /*!
          *
@@ -578,7 +585,7 @@ pub mod guarantor {
 
         let mut expr_ct = categorize_unadjusted(rcx, expr);
         expr_ct = apply_autoderefs(
-            rcx, expr, adjustment.autoderefs, expr_ct);
+            rcx, expr, autoderefs, expr_ct);
         for expr_ct.cat.guarantor.each |g| {
             infallibly_mk_subr(rcx, true, expr.span, autoref.region, *g);
         }
@@ -723,19 +730,32 @@ pub mod guarantor {
         let mut expr_ct = categorize_unadjusted(rcx, expr);
         debug!("before adjustments, cat=%?", expr_ct.cat);
 
-        for rcx.fcx.inh.adjustments.find(&expr.id).each |adjustment| {
-            debug!("adjustment=%?", adjustment);
+        match rcx.fcx.inh.adjustments.find(&expr.id) {
+            Some(@ty::AutoAddEnv(*)) => {
+                // This is basically an rvalue, not a pointer, no regions
+                // involved.
+                expr_ct.cat = ExprCategorization {
+                    guarantor: None,
+                    pointer: NotPointer
+                };
+            }
+
+            Some(@ty::AutoDerefRef(ref adjustment)) => {
+                debug!("adjustment=%?", adjustment);
 
-            expr_ct = apply_autoderefs(
-                rcx, expr, adjustment.autoderefs, expr_ct);
+                expr_ct = apply_autoderefs(
+                    rcx, expr, adjustment.autoderefs, expr_ct);
 
-            for adjustment.autoref.each |autoref| {
-                // If there is an autoref, then the result of this
-                // expression will be some sort of borrowed pointer.
-                expr_ct.cat.guarantor = None;
-                expr_ct.cat.pointer = BorrowedPointer(autoref.region);
-                debug!("autoref, cat=%?", expr_ct.cat);
+                for adjustment.autoref.each |autoref| {
+                    // If there is an autoref, then the result of this
+                    // expression will be some sort of borrowed pointer.
+                    expr_ct.cat.guarantor = None;
+                    expr_ct.cat.pointer = BorrowedPointer(autoref.region);
+                    debug!("autoref, cat=%?", expr_ct.cat);
+                }
             }
+
+            None => {}
         }
 
         debug!("result=%?", expr_ct.cat);
diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs
index a16dab4bdea..a6aed9c7b0c 100644
--- a/src/librustc/middle/typeck/check/writeback.rs
+++ b/src/librustc/middle/typeck/check/writeback.rs
@@ -79,7 +79,24 @@ fn resolve_type_vars_for_node(wbcx: @mut WbCtxt, sp: span, id: ast::node_id)
     // Resolve any borrowings for the node with id `id`
     match fcx.inh.adjustments.find(&id) {
         None => (),
-        Some(adj) => {
+
+        Some(@ty::AutoAddEnv(r, s)) => {
+            match resolve_region(fcx.infcx(), r, resolve_all | force_all) {
+                Err(e) => {
+                    // This should not, I think, happen:
+                    fcx.ccx.tcx.sess.span_err(
+                        sp, fmt!("cannot resolve bound for closure: %s",
+                                 infer::fixup_err_to_str(e)));
+                }
+                Ok(r1) => {
+                    let resolved_adj = @ty::AutoAddEnv(r1, s);
+                    debug!("Adjustments for node %d: %?", id, resolved_adj);
+                    fcx.tcx().adjustments.insert(id, resolved_adj);
+                }
+            }
+        }
+
+        Some(@ty::AutoDerefRef(adj)) => {
             let resolved_autoref = match adj.autoref {
                 Some(ref autoref) => {
                     match resolve_region(fcx.infcx(), autoref.region,
@@ -99,9 +116,10 @@ fn resolve_type_vars_for_node(wbcx: @mut WbCtxt, sp: span, id: ast::node_id)
                 None => None
             };
 
-            let resolved_adj = @ty::AutoAdjustment {
+            let resolved_adj = @ty::AutoDerefRef(ty::AutoDerefRef {
+                autoderefs: adj.autoderefs,
                 autoref: resolved_autoref,
-                ..*adj};
+            });
             debug!("Adjustments for node %d: %?", id, resolved_adj);
             fcx.tcx().adjustments.insert(id, resolved_adj);
         }
diff --git a/src/librustc/middle/typeck/infer/coercion.rs b/src/librustc/middle/typeck/infer/coercion.rs
index 5d291fdde77..17db5d475fd 100644
--- a/src/librustc/middle/typeck/infer/coercion.rs
+++ b/src/librustc/middle/typeck/infer/coercion.rs
@@ -67,7 +67,7 @@ we may want to adjust precisely when coercions occur.
 use core::prelude::*;
 
 use middle::ty::{TyVar, AutoPtr, AutoBorrowVec, AutoBorrowFn};
-use middle::ty::{AutoAdjustment, AutoRef};
+use middle::ty::{AutoAdjustment, AutoDerefRef, AutoRef};
 use middle::ty::{vstore_slice, vstore_box, vstore_uniq, vstore_fixed};
 use middle::ty::{mt};
 use middle::ty;
@@ -206,14 +206,14 @@ impl Coerce {
                                      r_borrow,
                                      mt {ty: inner_ty, mutbl: mt_b.mutbl});
         if_ok!(sub.tys(a_borrowed, b));
-        Ok(Some(@AutoAdjustment {
+        Ok(Some(@AutoDerefRef(AutoDerefRef {
             autoderefs: 1,
             autoref: Some(AutoRef {
                 kind: AutoPtr,
                 region: r_borrow,
                 mutbl: mt_b.mutbl
             })
-        }))
+        })))
     }
 
     fn coerce_borrowed_string(&self,
@@ -236,14 +236,14 @@ impl Coerce {
         let r_a = self.infcx.next_region_var_nb(self.span);
         let a_borrowed = ty::mk_estr(self.infcx.tcx, vstore_slice(r_a));
         if_ok!(self.subtype(a_borrowed, b));
-        Ok(Some(@AutoAdjustment {
+        Ok(Some(@AutoDerefRef(AutoDerefRef {
             autoderefs: 0,
             autoref: Some(AutoRef {
                 kind: AutoBorrowVec,
                 region: r_a,
                 mutbl: m_imm
             })
-        }))
+        })))
     }
 
     fn coerce_borrowed_vector(&self,
@@ -269,14 +269,14 @@ impl Coerce {
                                      mt {ty: ty_inner, mutbl: mt_b.mutbl},
                                      vstore_slice(r_borrow));
         if_ok!(sub.tys(a_borrowed, b));
-        Ok(Some(@AutoAdjustment {
+        Ok(Some(@AutoDerefRef(AutoDerefRef {
             autoderefs: 0,
             autoref: Some(AutoRef {
                 kind: AutoBorrowVec,
                 region: r_borrow,
                 mutbl: mt_b.mutbl
             })
-        }))
+        })))
     }
 
     fn coerce_borrowed_fn(&self,
@@ -309,14 +309,14 @@ impl Coerce {
             });
 
         if_ok!(self.subtype(a_borrowed, b));
-        Ok(Some(@AutoAdjustment {
+        Ok(Some(@AutoDerefRef(AutoDerefRef {
             autoderefs: 0,
             autoref: Some(AutoRef {
                 kind: AutoBorrowFn,
                 region: r_borrow,
                 mutbl: m_imm
             })
-        }))
+        })))
     }
 
     fn coerce_from_bare_fn(&self,
@@ -347,10 +347,12 @@ impl Coerce {
 
         // for now, bare fn and closures have the same
         // representation
+        let adj = @ty::AutoAddEnv(fn_ty_b.region, fn_ty_b.sigil);
         let a_closure = ty::mk_closure(
             self.infcx.tcx,
             ty::ClosureTy {sig: copy fn_ty_a.sig, ..fn_ty_b});
-        self.subtype(a_closure, b)
+        if_ok!(self.subtype(a_closure, b));
+        Ok(Some(adj))
     }
 
     fn coerce_unsafe_ptr(&self,