about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2013-05-16 11:31:46 -0700
committerbors <bors@rust-lang.org>2013-05-16 11:31:46 -0700
commitcf8341fc9e1c18c973a6744ecd7ebb97adbd3025 (patch)
tree226edd1b4ba496dedd2c4aeb450b49384d0845d6 /src
parent53196bb364426fb2b8db16f6748836779cd031c7 (diff)
parent035c01af93fd23f10e233a8a9b651af20744e1d1 (diff)
downloadrust-cf8341fc9e1c18c973a6744ecd7ebb97adbd3025.tar.gz
rust-cf8341fc9e1c18c973a6744ecd7ebb97adbd3025.zip
auto merge of #6457 : nikomatsakis/rust/issue-6308-closure-bounds, r=brson
Add BuiltinBounds to closure type: parse and handle subtyping,
but do not integrate with kindck etc (requires a snapshot first)

r? @brson
Diffstat (limited to 'src')
-rw-r--r--src/librustc/metadata/tydecode.rs8
-rw-r--r--src/librustc/metadata/tyencode.rs5
-rw-r--r--src/librustc/middle/resolve.rs28
-rw-r--r--src/librustc/middle/trans/foreign.rs1
-rw-r--r--src/librustc/middle/trans/monomorphize.rs1
-rw-r--r--src/librustc/middle/ty.rs29
-rw-r--r--src/librustc/middle/typeck/astconv.rs88
-rw-r--r--src/librustc/middle/typeck/check/mod.rs29
-rw-r--r--src/librustc/middle/typeck/collect.rs20
-rw-r--r--src/librustc/middle/typeck/infer/combine.rs4
-rw-r--r--src/librustc/middle/typeck/infer/glb.rs7
-rw-r--r--src/librustc/middle/typeck/infer/lub.rs7
-rw-r--r--src/librustc/middle/typeck/infer/sub.rs14
-rw-r--r--src/librustc/util/enum_set.rs12
-rw-r--r--src/libsyntax/ast.rs3
-rw-r--r--src/libsyntax/fold.rs1
-rw-r--r--src/libsyntax/parse/parser.rs23
-rw-r--r--src/test/compile-fail/closure-bounds-not-builtin.rs8
-rw-r--r--src/test/compile-fail/closure-bounds-subtype.rs34
19 files changed, 261 insertions, 61 deletions
diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs
index 151ccad88ea..55a0755f5e3 100644
--- a/src/librustc/metadata/tydecode.rs
+++ b/src/librustc/metadata/tydecode.rs
@@ -470,12 +470,14 @@ fn parse_closure_ty(st: @mut PState, conv: conv_did) -> ty::ClosureTy {
     let purity = parse_purity(next(st));
     let onceness = parse_onceness(next(st));
     let region = parse_region(st);
+    let bounds = parse_bounds(st, conv);
     let sig = parse_sig(st, conv);
     ty::ClosureTy {
         purity: purity,
         sigil: sigil,
         onceness: onceness,
         region: region,
+        bounds: bounds.builtin_bounds,
         sig: sig
     }
 }
@@ -540,10 +542,10 @@ pub fn parse_type_param_def_data(data: @~[u8], start: uint,
 
 fn parse_type_param_def(st: @mut PState, conv: conv_did) -> ty::TypeParameterDef {
     ty::TypeParameterDef {def_id: parse_def(st, NominalType, conv),
-                          bounds: parse_bounds(st, conv)}
+                          bounds: @parse_bounds(st, conv)}
 }
 
-fn parse_bounds(st: @mut PState, conv: conv_did) -> @ty::ParamBounds {
+fn parse_bounds(st: @mut PState, conv: conv_did) -> ty::ParamBounds {
     let mut param_bounds = ty::ParamBounds {
         builtin_bounds: ty::EmptyBuiltinBounds(),
         trait_bounds: ~[]
@@ -566,7 +568,7 @@ fn parse_bounds(st: @mut PState, conv: conv_did) -> @ty::ParamBounds {
                 param_bounds.trait_bounds.push(@parse_trait_ref(st, conv));
             }
             '.' => {
-                return @param_bounds;
+                return param_bounds;
             }
             _ => {
                 fail!("parse_bounds: bad bounds")
diff --git a/src/librustc/metadata/tyencode.rs b/src/librustc/metadata/tyencode.rs
index 2cb95e1a2fc..5f799f49946 100644
--- a/src/librustc/metadata/tyencode.rs
+++ b/src/librustc/metadata/tyencode.rs
@@ -380,6 +380,9 @@ fn enc_closure_ty(w: @io::Writer, cx: @ctxt, ft: &ty::ClosureTy) {
     enc_purity(w, ft.purity);
     enc_onceness(w, ft.onceness);
     enc_region(w, cx, ft.region);
+    let bounds = ty::ParamBounds {builtin_bounds: ft.bounds,
+                                  trait_bounds: ~[]};
+    enc_bounds(w, cx, &bounds);
     enc_fn_sig(w, cx, &ft.sig);
 }
 
@@ -392,7 +395,7 @@ fn enc_fn_sig(w: @io::Writer, cx: @ctxt, fsig: &ty::FnSig) {
     enc_ty(w, cx, fsig.output);
 }
 
-fn enc_bounds(w: @io::Writer, cx: @ctxt, bs: @ty::ParamBounds) {
+fn enc_bounds(w: @io::Writer, cx: @ctxt, bs: &ty::ParamBounds) {
     for bs.builtin_bounds.each |bound| {
         match bound {
             ty::BoundOwned => w.write_char('S'),
diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs
index 0d8d4baaa80..dd5658d7600 100644
--- a/src/librustc/middle/resolve.rs
+++ b/src/librustc/middle/resolve.rs
@@ -20,6 +20,7 @@ use middle::lint::{allow, level, unused_imports};
 use middle::lint::{get_lint_level, get_lint_settings_level};
 use middle::pat_util::pat_bindings;
 
+use syntax::ast::{TyParamBound, ty_closure};
 use syntax::ast::{RegionTyParamBound, TraitTyParamBound, _mod, add, arm};
 use syntax::ast::{binding_mode, bitand, bitor, bitxor, blk};
 use syntax::ast::{bind_infer, bind_by_ref, bind_by_copy};
@@ -3732,14 +3733,20 @@ pub impl Resolver {
                                type_parameters: &OptVec<TyParam>,
                                visitor: ResolveVisitor) {
         for type_parameters.each |type_parameter| {
-            for type_parameter.bounds.each |&bound| {
-                match bound {
-                    TraitTyParamBound(tref) => {
-                        self.resolve_trait_reference(tref, visitor)
-                    }
-                    RegionTyParamBound => {}
-                }
+            for type_parameter.bounds.each |bound| {
+                self.resolve_type_parameter_bound(bound, visitor);
+            }
+        }
+    }
+
+    fn resolve_type_parameter_bound(@mut self,
+                                    type_parameter_bound: &TyParamBound,
+                                    visitor: ResolveVisitor) {
+        match *type_parameter_bound {
+            TraitTyParamBound(tref) => {
+                self.resolve_trait_reference(tref, visitor)
             }
+            RegionTyParamBound => {}
         }
     }
 
@@ -4070,6 +4077,13 @@ pub impl Resolver {
                 }
             }
 
+            ty_closure(c) => {
+                for c.bounds.each |bound| {
+                    self.resolve_type_parameter_bound(bound, visitor);
+                }
+                visit_ty(ty, (), visitor);
+            }
+
             _ => {
                 // Just resolve embedded types.
                 visit_ty(ty, (), visitor);
diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs
index 5b1cae473f7..fd545ca2c6e 100644
--- a/src/librustc/middle/trans/foreign.rs
+++ b/src/librustc/middle/trans/foreign.rs
@@ -818,6 +818,7 @@ pub fn trans_intrinsic(ccx: @CrateContext,
                 sigil: ast::BorrowedSigil,
                 onceness: ast::Many,
                 region: ty::re_bound(ty::br_anon(0)),
+                bounds: ty::EmptyBuiltinBounds(),
                 sig: FnSig {
                     bound_lifetime_names: opt_vec::Empty,
                     inputs: ~[ star_u8 ],
diff --git a/src/librustc/middle/trans/monomorphize.rs b/src/librustc/middle/trans/monomorphize.rs
index 3b0c03cdc99..ccc906f2ee8 100644
--- a/src/librustc/middle/trans/monomorphize.rs
+++ b/src/librustc/middle/trans/monomorphize.rs
@@ -330,6 +330,7 @@ pub fn normalize_for_monomorphization(tcx: ty::ctxt,
                 sigil: sigil,
                 onceness: ast::Many,
                 region: ty::re_static,
+                bounds: ty::EmptyBuiltinBounds(),
                 sig: ty::FnSig {bound_lifetime_names: opt_vec::Empty,
                                 inputs: ~[],
                                 output: ty::mk_nil()}})
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index e85c7d00c58..fe1326fcdca 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -22,7 +22,7 @@ use middle::typeck;
 use middle;
 use util::ppaux::{note_and_explain_region, bound_region_to_str};
 use util::ppaux::{trait_store_to_str, ty_to_str, vstore_to_str};
-use util::ppaux::Repr;
+use util::ppaux::{Repr, UserString};
 use util::common::{indenter};
 use util::enum_set::{EnumSet, CLike};
 
@@ -390,7 +390,8 @@ pub struct ClosureTy {
     sigil: ast::Sigil,
     onceness: ast::Onceness,
     region: Region,
-    sig: FnSig
+    bounds: BuiltinBounds,
+    sig: FnSig,
 }
 
 /**
@@ -685,6 +686,7 @@ pub enum type_err {
     terr_int_mismatch(expected_found<IntVarValue>),
     terr_float_mismatch(expected_found<ast::float_ty>),
     terr_traits(expected_found<ast::def_id>),
+    terr_builtin_bounds(expected_found<BuiltinBounds>),
 }
 
 #[deriving(Eq, IterBytes)]
@@ -707,6 +709,15 @@ pub fn EmptyBuiltinBounds() -> BuiltinBounds {
     EnumSet::empty()
 }
 
+pub fn AllBuiltinBounds() -> BuiltinBounds {
+    let mut set = EnumSet::empty();
+    set.add(BoundCopy);
+    set.add(BoundStatic);
+    set.add(BoundOwned);
+    set.add(BoundConst);
+    set
+}
+
 impl CLike for BuiltinBound {
     pub fn to_uint(&self) -> uint {
         *self as uint
@@ -3169,6 +3180,7 @@ pub fn adjust_ty(cx: ctxt,
                                        sigil: s,
                                        onceness: ast::Many,
                                        region: r,
+                                       bounds: ty::AllBuiltinBounds(),
                                        sig: copy b.sig})
                 }
                 ref b => {
@@ -3697,6 +3709,19 @@ pub fn type_err_to_str(cx: ctxt, err: &type_err) -> ~str {
                  item_path_str(cx, values.expected),
                  item_path_str(cx, values.found))
         }
+        terr_builtin_bounds(values) => {
+            if values.expected.is_empty() {
+                fmt!("expected no bounds but found `%s`",
+                     values.found.user_string(cx))
+            } else if values.found.is_empty() {
+                fmt!("expected bounds `%s` but found no bounds",
+                     values.expected.user_string(cx))
+            } else {
+                fmt!("expected bounds `%s` but found bounds `%s`",
+                     values.expected.user_string(cx),
+                     values.found.user_string(cx))
+            }
+        }
         terr_self_substs => {
             ~"inconsistent self substitution" // XXX this is more of a bug
         }
diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs
index de6064b0a31..222493b0564 100644
--- a/src/librustc/middle/typeck/astconv.rs
+++ b/src/librustc/middle/typeck/astconv.rs
@@ -59,6 +59,7 @@ use middle::ty;
 use middle::typeck::rscope::in_binding_rscope;
 use middle::typeck::rscope::{region_scope, RegionError};
 use middle::typeck::rscope::RegionParamNames;
+use middle::typeck::lookup_def_tcx;
 
 use syntax::abi::AbiSet;
 use syntax::{ast, ast_util};
@@ -220,7 +221,6 @@ pub fn ast_path_to_trait_ref<AC:AstConv,RS:region_scope + Copy + 'static>(
     return trait_ref;
 }
 
-
 pub fn ast_path_to_ty<AC:AstConv,RS:region_scope + Copy + 'static>(
         this: &AC,
         rscope: &RS,
@@ -377,11 +377,13 @@ pub fn ast_ty_to_ty<AC:AstConv, RS:region_scope + Copy + 'static>(
                                             bf.abis, &bf.lifetimes, &bf.decl))
       }
       ast::ty_closure(ref f) => {
+          let bounds = conv_builtin_bounds(this.tcx(), &f.bounds);
           let fn_decl = ty_of_closure(this,
                                       rscope,
                                       f.sigil,
                                       f.purity,
                                       f.onceness,
+                                      bounds,
                                       f.region,
                                       &f.decl,
                                       None,
@@ -646,17 +648,18 @@ fn ty_of_method_or_bare_fn<AC:AstConv,RS:region_scope + Copy + 'static>(
 }
 
 pub fn ty_of_closure<AC:AstConv,RS:region_scope + Copy + 'static>(
-        this: &AC,
-        rscope: &RS,
-        sigil: ast::Sigil,
-        purity: ast::purity,
-        onceness: ast::Onceness,
-        opt_lifetime: Option<@ast::Lifetime>,
-        decl: &ast::fn_decl,
-        expected_sig: Option<ty::FnSig>,
-        lifetimes: &OptVec<ast::Lifetime>,
-        span: span)
-     -> ty::ClosureTy
+    this: &AC,
+    rscope: &RS,
+    sigil: ast::Sigil,
+    purity: ast::purity,
+    onceness: ast::Onceness,
+    bounds: ty::BuiltinBounds,
+    opt_lifetime: Option<@ast::Lifetime>,
+    decl: &ast::fn_decl,
+    expected_sig: Option<ty::FnSig>,
+    lifetimes: &OptVec<ast::Lifetime>,
+    span: span)
+    -> ty::ClosureTy
 {
     // The caller should not both provide explicit bound lifetime
     // names and expected types.  Either we infer the bound lifetime
@@ -713,8 +716,69 @@ pub fn ty_of_closure<AC:AstConv,RS:region_scope + Copy + 'static>(
         sigil: sigil,
         onceness: onceness,
         region: bound_region,
+        bounds: bounds,
         sig: ty::FnSig {bound_lifetime_names: bound_lifetime_names,
                         inputs: input_tys,
                         output: output_ty}
     }
 }
+
+fn conv_builtin_bounds(tcx: ty::ctxt,
+                       ast_bounds: &OptVec<ast::TyParamBound>)
+                       -> ty::BuiltinBounds {
+    //! Converts a list of bounds from the AST into a `BuiltinBounds`
+    //! struct. Reports an error if any of the bounds that appear
+    //! in the AST refer to general traits and not the built-in traits
+    //! like `Copy` or `Owned`. Used to translate the bounds that
+    //! appear in closure and trait types, where only builtin bounds are
+    //! legal.
+
+    let mut builtin_bounds = ty::EmptyBuiltinBounds();
+    for ast_bounds.each |ast_bound| {
+        match *ast_bound {
+            ast::TraitTyParamBound(b) => {
+                match lookup_def_tcx(tcx, b.path.span, b.ref_id) {
+                    ast::def_trait(trait_did) => {
+                        if try_add_builtin_trait(tcx,
+                                                 trait_did,
+                                                 &mut builtin_bounds) {
+                            loop; // success
+                        }
+                    }
+                    _ => { }
+                }
+                tcx.sess.span_fatal(
+                    b.path.span,
+                    fmt!("only the builtin traits can be used \
+                          as closure or object bounds"));
+            }
+            ast::RegionTyParamBound => {
+                builtin_bounds.add(ty::BoundStatic);
+            }
+        }
+    }
+    builtin_bounds
+}
+
+pub fn try_add_builtin_trait(tcx: ty::ctxt,
+                             trait_def_id: ast::def_id,
+                             builtin_bounds: &mut ty::BuiltinBounds) -> bool {
+    //! Checks whether `trait_ref` refers to one of the builtin
+    //! traits, like `Copy` or `Owned`, and adds the corresponding
+    //! bound to the set `builtin_bounds` if so. Returns true if `trait_ref`
+    //! is a builtin trait.
+
+    let li = &tcx.lang_items;
+    if trait_def_id == li.owned_trait() {
+        builtin_bounds.add(ty::BoundOwned);
+        true
+    } else if trait_def_id == li.copy_trait() {
+        builtin_bounds.add(ty::BoundCopy);
+        true
+    } else if trait_def_id == li.const_trait() {
+        builtin_bounds.add(ty::BoundConst);
+        true
+    } else {
+        false
+    }
+}
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index 548b9e454ce..8d32bb7f677 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -1661,7 +1661,8 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
         let (expected_sig,
              expected_purity,
              expected_sigil,
-             expected_onceness) = {
+             expected_onceness,
+             expected_bounds) = {
             match expected_sty {
                 Some(ty::ty_closure(ref cenv)) => {
                     let id = expr.id;
@@ -1669,11 +1670,13 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
                         replace_bound_regions_in_fn_sig(
                             tcx, @Nil, None, &cenv.sig,
                             |br| ty::re_bound(ty::br_cap_avoid(id, @br)));
-                    (Some(sig), cenv.purity, cenv.sigil, cenv.onceness)
+                    (Some(sig), cenv.purity, cenv.sigil,
+                     cenv.onceness, cenv.bounds)
                 }
                 _ => {
                     // Not an error! Means we're inferring the closure type
-                    (None, ast::impure_fn, ast::BorrowedSigil, ast::Many)
+                    (None, ast::impure_fn, ast::BorrowedSigil,
+                     ast::Many, ty::EmptyBuiltinBounds())
                 }
             }
         };
@@ -1687,15 +1690,16 @@ pub fn check_expr_with_unifier(fcx: @mut FnCtxt,
 
         // construct the function type
         let fn_ty = astconv::ty_of_closure(fcx,
-                                               fcx,
-                                               sigil,
-                                               purity,
-                                               expected_onceness,
-                                               None,
-                                               decl,
-                                               expected_sig,
-                                               &opt_vec::Empty,
-                                               expr.span);
+                                           fcx,
+                                           sigil,
+                                           purity,
+                                           expected_onceness,
+                                           expected_bounds,
+                                           None,
+                                           decl,
+                                           expected_sig,
+                                           &opt_vec::Empty,
+                                           expr.span);
 
         let fty_sig;
         let fty = if error_happened {
@@ -3526,6 +3530,7 @@ pub fn check_intrinsic_type(ccx: @mut CrateCtxt, it: @ast::foreign_item) {
             sigil: ast::BorrowedSigil,
             onceness: ast::Once,
             region: ty::re_bound(ty::br_anon(0)),
+            bounds: ty::EmptyBuiltinBounds(),
             sig: ty::FnSig {
                 bound_lifetime_names: opt_vec::Empty,
                 inputs: ~[ty::mk_imm_ptr(ccx.tcx, ty::mk_mach_uint(ast::ty_u8))],
diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs
index 4773e637c35..6c7f73177fa 100644
--- a/src/librustc/middle/typeck/collect.rs
+++ b/src/librustc/middle/typeck/collect.rs
@@ -1207,25 +1207,21 @@ pub fn ty_generics(ccx: &CrateCtxt,
             builtin_bounds: ty::EmptyBuiltinBounds(),
             trait_bounds: ~[]
         };
-        for ast_bounds.each |b| {
-            match b {
-                &TraitTyParamBound(b) => {
-                    let li = &ccx.tcx.lang_items;
+        for ast_bounds.each |ast_bound| {
+            match *ast_bound {
+                TraitTyParamBound(b) => {
                     let ty = ty::mk_param(ccx.tcx, param_ty.idx, param_ty.def_id);
                     let trait_ref = instantiate_trait_ref(ccx, b, rp, generics, ty);
-                    if trait_ref.def_id == li.owned_trait() {
-                        param_bounds.builtin_bounds.add(ty::BoundOwned);
-                    } else if trait_ref.def_id == li.copy_trait() {
-                        param_bounds.builtin_bounds.add(ty::BoundCopy);
-                    } else if trait_ref.def_id == li.const_trait() {
-                        param_bounds.builtin_bounds.add(ty::BoundConst);
-                    } else {
+                    if !astconv::try_add_builtin_trait(
+                        ccx.tcx, trait_ref.def_id,
+                        &mut param_bounds.builtin_bounds)
+                    {
                         // Must be a user-defined trait
                         param_bounds.trait_bounds.push(trait_ref);
                     }
                 }
 
-                &RegionTyParamBound => {
+                RegionTyParamBound => {
                     param_bounds.builtin_bounds.add(ty::BoundStatic);
                 }
             }
diff --git a/src/librustc/middle/typeck/infer/combine.rs b/src/librustc/middle/typeck/infer/combine.rs
index fcd2c6ffe59..3c337d17f86 100644
--- a/src/librustc/middle/typeck/infer/combine.rs
+++ b/src/librustc/middle/typeck/infer/combine.rs
@@ -56,6 +56,7 @@
 
 use middle::ty::{FloatVar, FnSig, IntVar, TyVar};
 use middle::ty::{IntType, UintType, substs};
+use middle::ty::{BuiltinBounds};
 use middle::ty;
 use middle::typeck::infer::glb::Glb;
 use middle::typeck::infer::lub::Lub;
@@ -100,6 +101,7 @@ pub trait Combine {
     fn purities(&self, a: purity, b: purity) -> cres<purity>;
     fn abis(&self, a: AbiSet, b: AbiSet) -> cres<AbiSet>;
     fn oncenesses(&self, a: Onceness, b: Onceness) -> cres<Onceness>;
+    fn bounds(&self, a: BuiltinBounds, b: BuiltinBounds) -> cres<BuiltinBounds>;
     fn contraregions(&self, a: ty::Region, b: ty::Region)
                   -> cres<ty::Region>;
     fn regions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region>;
@@ -372,11 +374,13 @@ pub fn super_closure_tys<C:Combine>(
     let r = if_ok!(this.contraregions(a_f.region, b_f.region));
     let purity = if_ok!(this.purities(a_f.purity, b_f.purity));
     let onceness = if_ok!(this.oncenesses(a_f.onceness, b_f.onceness));
+    let bounds = if_ok!(this.bounds(a_f.bounds, b_f.bounds));
     let sig = if_ok!(this.fn_sigs(&a_f.sig, &b_f.sig));
     Ok(ty::ClosureTy {purity: purity,
                       sigil: p,
                       onceness: onceness,
                       region: r,
+                      bounds: bounds,
                       sig: sig})
 }
 
diff --git a/src/librustc/middle/typeck/infer/glb.rs b/src/librustc/middle/typeck/infer/glb.rs
index 42e42ddb1e7..9ade6de6cf4 100644
--- a/src/librustc/middle/typeck/infer/glb.rs
+++ b/src/librustc/middle/typeck/infer/glb.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use middle::ty::{BuiltinBounds};
 use middle::ty::RegionVid;
 use middle::ty;
 use middle::typeck::infer::combine::*;
@@ -114,6 +115,12 @@ impl Combine for Glb {
         }
     }
 
+    fn bounds(&self, a: BuiltinBounds, b: BuiltinBounds) -> cres<BuiltinBounds> {
+        // More bounds is a subtype of fewer bounds, so
+        // the GLB (mutual subtype) is the union.
+        Ok(a.union(b))
+    }
+
     fn regions(&self, a: ty::Region, b: ty::Region) -> cres<ty::Region> {
         debug!("%s.regions(%?, %?)",
                self.tag(),
diff --git a/src/librustc/middle/typeck/infer/lub.rs b/src/librustc/middle/typeck/infer/lub.rs
index 20a051f0531..82fd4e3ae6d 100644
--- a/src/librustc/middle/typeck/infer/lub.rs
+++ b/src/librustc/middle/typeck/infer/lub.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use middle::ty::{BuiltinBounds};
 use middle::ty::RegionVid;
 use middle::ty;
 use middle::typeck::infer::combine::*;
@@ -100,6 +101,12 @@ impl Combine for Lub {
         }
     }
 
+    fn bounds(&self, a: BuiltinBounds, b: BuiltinBounds) -> cres<BuiltinBounds> {
+        // More bounds is a subtype of fewer bounds, so
+        // the LUB (mutual supertype) is the intersection.
+        Ok(a.intersection(b))
+    }
+
     fn contraregions(&self, a: ty::Region, b: ty::Region)
                     -> cres<ty::Region> {
         return Glb(**self).regions(a, b);
diff --git a/src/librustc/middle/typeck/infer/sub.rs b/src/librustc/middle/typeck/infer/sub.rs
index ca083bc2d86..8da3d7bfa00 100644
--- a/src/librustc/middle/typeck/infer/sub.rs
+++ b/src/librustc/middle/typeck/infer/sub.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use middle::ty::{BuiltinBounds};
 use middle::ty;
 use middle::ty::TyVar;
 use middle::typeck::check::regionmanip::replace_bound_regions_in_fn_sig;
@@ -99,6 +100,19 @@ impl Combine for Sub {
         })
     }
 
+    fn bounds(&self, a: BuiltinBounds, b: BuiltinBounds) -> cres<BuiltinBounds> {
+        // More bounds is a subtype of fewer bounds.
+        //
+        // e.g., fn:Copy() <: fn(), because the former is a function
+        // that only closes over copyable things, but the latter is
+        // any function at all.
+        if a.contains(b) {
+            Ok(a)
+        } else {
+            Err(ty::terr_builtin_bounds(expected_found(self, a, b)))
+        }
+    }
+
     fn tys(&self, a: ty::t, b: ty::t) -> cres<ty::t> {
         debug!("%s.tys(%s, %s)", self.tag(),
                a.inf_str(self.infcx), b.inf_str(self.infcx));
diff --git a/src/librustc/util/enum_set.rs b/src/librustc/util/enum_set.rs
index dae4bb69c61..801561350ae 100644
--- a/src/librustc/util/enum_set.rs
+++ b/src/librustc/util/enum_set.rs
@@ -12,7 +12,9 @@ use core;
 
 #[deriving(Eq, IterBytes)]
 pub struct EnumSet<E> {
-    bits: uint
+    // We must maintain the invariant that no bits are set
+    // for which no variant exists
+    priv bits: uint
 }
 
 pub trait CLike {
@@ -37,10 +39,18 @@ pub impl<E:CLike> EnumSet<E> {
         (self.bits & e.bits) != 0
     }
 
+    fn intersection(&self, e: EnumSet<E>) -> EnumSet<E> {
+        EnumSet {bits: self.bits & e.bits}
+    }
+
     fn contains(&self, e: EnumSet<E>) -> bool {
         (self.bits & e.bits) == e.bits
     }
 
+    fn union(&self, e: EnumSet<E>) -> EnumSet<E> {
+        EnumSet {bits: self.bits | e.bits}
+    }
+
     fn add(&mut self, e: E) {
         self.bits |= bit(e);
     }
diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs
index 4a3ae7d94ad..dcbbd7ab531 100644
--- a/src/libsyntax/ast.rs
+++ b/src/libsyntax/ast.rs
@@ -828,7 +828,8 @@ pub struct TyClosure {
     lifetimes: OptVec<Lifetime>,
     purity: purity,
     onceness: Onceness,
-    decl: fn_decl
+    decl: fn_decl,
+    bounds: OptVec<TyParamBound>
 }
 
 #[deriving(Eq, Encodable, Decodable)]
diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs
index f6dbbbf420d..275a7b963a4 100644
--- a/src/libsyntax/fold.rs
+++ b/src/libsyntax/fold.rs
@@ -589,6 +589,7 @@ pub fn noop_fold_ty(t: &ty_, fld: @ast_fold) -> ty_ {
                 purity: f.purity,
                 region: f.region,
                 onceness: f.onceness,
+                bounds: f.bounds.map(|x| fold_ty_param_bound(x, fld)),
                 decl: fold_fn_decl(&f.decl, fld),
                 lifetimes: copy f.lifetimes,
             })
diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs
index ce9bbe7e17a..25b45a5f3b5 100644
--- a/src/libsyntax/parse/parser.rs
+++ b/src/libsyntax/parse/parser.rs
@@ -395,12 +395,13 @@ pub impl Parser {
                         -> ty_ {
         /*
 
-        (&|~|@) ['r] [pure|unsafe] [once] fn <'lt> (S) -> T
-        ^~~~~~^ ^~~^ ^~~~~~~~~~~~^ ^~~~~^    ^~~~^ ^~^    ^
-           |     |     |             |         |    |     |
-           |     |     |             |         |    |   Return type
-           |     |     |             |         |  Argument types
-           |     |     |             |     Lifetimes
+        (&|~|@) ['r] [pure|unsafe] [once] fn [:Bounds] <'lt> (S) -> T
+        ^~~~~~^ ^~~^ ^~~~~~~~~~~~^ ^~~~~^    ^~~~~~~~^ ^~~~^ ^~^    ^
+           |     |     |             |           |       |    |     |
+           |     |     |             |           |       |    |   Return type
+           |     |     |             |           |       |  Argument types
+           |     |     |             |           |   Lifetimes
+           |     |     |             |       Closure bounds
            |     |     |          Once-ness (a.k.a., affine)
            |     |   Purity
            | Lifetime bound
@@ -414,6 +415,7 @@ pub impl Parser {
         let purity = self.parse_unsafety();
         let onceness = parse_onceness(self);
         self.expect_keyword("fn");
+        let bounds = self.parse_optional_ty_param_bounds();
 
         if self.parse_fn_ty_sigil().is_some() {
             self.obsolete(*self.span, ObsoletePostFnTySigil);
@@ -426,6 +428,7 @@ pub impl Parser {
             region: region,
             purity: purity,
             onceness: onceness,
+            bounds: bounds,
             decl: decl,
             lifetimes: lifetimes,
         });
@@ -2851,9 +2854,9 @@ pub impl Parser {
     // matches optbounds = ( ( : ( boundseq )? )? )
     // where   boundseq  = ( bound + boundseq ) | bound
     // and     bound     = 'static | ty
-    fn parse_optional_ty_param_bounds(&self) -> @OptVec<TyParamBound> {
+    fn parse_optional_ty_param_bounds(&self) -> OptVec<TyParamBound> {
         if !self.eat(&token::COLON) {
-            return @opt_vec::Empty;
+            return opt_vec::Empty;
         }
 
         let mut result = opt_vec::Empty;
@@ -2907,13 +2910,13 @@ pub impl Parser {
             }
         }
 
-        return @result;
+        return result;
     }
 
     // matches typaram = IDENT optbounds
     fn parse_ty_param(&self) -> TyParam {
         let ident = self.parse_ident();
-        let bounds = self.parse_optional_ty_param_bounds();
+        let bounds = @self.parse_optional_ty_param_bounds();
         ast::TyParam { ident: ident, id: self.get_id(), bounds: bounds }
     }
 
diff --git a/src/test/compile-fail/closure-bounds-not-builtin.rs b/src/test/compile-fail/closure-bounds-not-builtin.rs
new file mode 100644
index 00000000000..a3484cb33dc
--- /dev/null
+++ b/src/test/compile-fail/closure-bounds-not-builtin.rs
@@ -0,0 +1,8 @@
+
+trait Foo {}
+
+fn take(f: &fn:Foo()) {
+    //~^ ERROR only the builtin traits can be used as closure or object bounds
+}
+
+fn main() {}
\ No newline at end of file
diff --git a/src/test/compile-fail/closure-bounds-subtype.rs b/src/test/compile-fail/closure-bounds-subtype.rs
new file mode 100644
index 00000000000..ebec113cedc
--- /dev/null
+++ b/src/test/compile-fail/closure-bounds-subtype.rs
@@ -0,0 +1,34 @@
+fn take_any(_: &fn()) {
+}
+
+fn take_copyable(_: &fn:Copy()) {
+}
+
+fn take_copyable_owned(_: &fn:Copy+Owned()) {
+}
+
+fn give_any(f: &fn()) {
+    take_any(f);
+    take_copyable(f); //~ ERROR expected bounds `Copy` but found no bounds
+    take_copyable_owned(f); //~ ERROR expected bounds `Copy+Owned` but found no bounds
+}
+
+fn give_copyable(f: &fn:Copy()) {
+    take_any(f);
+    take_copyable(f);
+    take_copyable_owned(f); //~ ERROR expected bounds `Copy+Owned` but found bounds `Copy`
+}
+
+fn give_owned(f: &fn:Owned()) {
+    take_any(f);
+    take_copyable(f); //~ ERROR expected bounds `Copy` but found bounds `Owned`
+    take_copyable_owned(f); //~ ERROR expected bounds `Copy+Owned` but found bounds `Owned`
+}
+
+fn give_copyable_owned(f: &fn:Copy+Owned()) {
+    take_any(f);
+    take_copyable(f);
+    take_copyable_owned(f);
+}
+
+fn main() {}
\ No newline at end of file