From 035c01af93fd23f10e233a8a9b651af20744e1d1 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Fri, 10 May 2013 15:57:27 -0400 Subject: [PATCH] Add BuiltinBounds to closure type: parse and handle subtyping, but do not integrate with kindck etc (requires a snapshot first) --- src/librustc/metadata/tydecode.rs | 8 +- src/librustc/metadata/tyencode.rs | 5 +- src/librustc/middle/resolve.rs | 28 ++++-- src/librustc/middle/trans/foreign.rs | 1 + src/librustc/middle/trans/monomorphize.rs | 1 + src/librustc/middle/ty.rs | 29 +++++- src/librustc/middle/typeck/astconv.rs | 88 ++++++++++++++++--- src/librustc/middle/typeck/check/mod.rs | 29 +++--- src/librustc/middle/typeck/collect.rs | 20 ++--- src/librustc/middle/typeck/infer/combine.rs | 4 + src/librustc/middle/typeck/infer/glb.rs | 7 ++ src/librustc/middle/typeck/infer/lub.rs | 7 ++ src/librustc/middle/typeck/infer/sub.rs | 14 +++ src/librustc/util/enum_set.rs | 12 ++- src/libsyntax/ast.rs | 3 +- src/libsyntax/fold.rs | 1 + src/libsyntax/parse/parser.rs | 23 ++--- .../closure-bounds-not-builtin.rs | 8 ++ .../compile-fail/closure-bounds-subtype.rs | 34 +++++++ 19 files changed, 261 insertions(+), 61 deletions(-) create mode 100644 src/test/compile-fail/closure-bounds-not-builtin.rs create mode 100644 src/test/compile-fail/closure-bounds-subtype.rs diff --git a/src/librustc/metadata/tydecode.rs b/src/librustc/metadata/tydecode.rs index 151ccad88eaf..55a0755f5e3f 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 2cb95e1a2fc0..5f799f499467 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 0d8d4baaa806..dd5658d7600f 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,17 +3733,23 @@ pub impl Resolver { type_parameters: &OptVec, 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 => {} + } + } + fn resolve_trait_reference(@mut self, trait_reference: &trait_ref, visitor: ResolveVisitor) { @@ -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 5b1cae473f74..fd545ca2c6ea 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 3b0c03cdc991..ccc906f2ee8a 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 e85c7d00c58e..fe1326fcdca3 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), terr_float_mismatch(expected_found), terr_traits(expected_found), + terr_builtin_bounds(expected_found), } #[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 de6064b0a313..222493b05645 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( return trait_ref; } - pub fn ast_path_to_ty( this: &AC, rscope: &RS, @@ -377,11 +377,13 @@ pub fn ast_ty_to_ty( 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( } pub fn ty_of_closure( - 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, - lifetimes: &OptVec, - 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, + lifetimes: &OptVec, + 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( 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) + -> 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 548b9e454ce9..8d32bb7f6775 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 4773e637c352..6c7f73177fad 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 fcd2c6ffe592..3c337d17f868 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; fn abis(&self, a: AbiSet, b: AbiSet) -> cres; fn oncenesses(&self, a: Onceness, b: Onceness) -> cres; + fn bounds(&self, a: BuiltinBounds, b: BuiltinBounds) -> cres; fn contraregions(&self, a: ty::Region, b: ty::Region) -> cres; fn regions(&self, a: ty::Region, b: ty::Region) -> cres; @@ -372,11 +374,13 @@ pub fn super_closure_tys( 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 42e42ddb1e76..9ade6de6cf4f 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 { + // 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 { debug!("%s.regions(%?, %?)", self.tag(), diff --git a/src/librustc/middle/typeck/infer/lub.rs b/src/librustc/middle/typeck/infer/lub.rs index 20a051f05318..82fd4e3ae6dd 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 { + // 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 { 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 ca083bc2d86d..8da3d7bfa000 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 { + // 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 { 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 dae4bb69c618..801561350ae4 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 { - 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 EnumSet { (self.bits & e.bits) != 0 } + fn intersection(&self, e: EnumSet) -> EnumSet { + EnumSet {bits: self.bits & e.bits} + } + fn contains(&self, e: EnumSet) -> bool { (self.bits & e.bits) == e.bits } + fn union(&self, e: EnumSet) -> EnumSet { + 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 4a3ae7d94ad7..dcbbd7ab5311 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -828,7 +828,8 @@ pub struct TyClosure { lifetimes: OptVec, purity: purity, onceness: Onceness, - decl: fn_decl + decl: fn_decl, + bounds: OptVec } #[deriving(Eq, Encodable, Decodable)] diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index f6dbbbf420d8..275a7b963a4b 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 ce9bbe7e17a7..25b45a5f3b5c 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 { + fn parse_optional_ty_param_bounds(&self) -> OptVec { 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 000000000000..a3484cb33dca --- /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 000000000000..ebec113cedc5 --- /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