From f02b6f3a8bf82ca11ba50a285841fb372c4da459 Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Sun, 1 Jun 2014 18:41:46 -0700 Subject: [PATCH] librustc: Implement sugar for the `FnMut` trait --- src/librustc/front/feature_gate.rs | 6 ++ src/librustc/middle/resolve.rs | 14 ++- src/librustc/middle/typeck/astconv.rs | 96 +++++++++++++++++-- src/librustc/middle/typeck/collect.rs | 23 ++++- .../middle/typeck/infer/error_reporting.rs | 3 + src/librustdoc/clean/mod.rs | 4 + src/libsyntax/ast.rs | 7 ++ src/libsyntax/fold.rs | 10 ++ src/libsyntax/parse/parser.rs | 84 +++++++++++++--- src/libsyntax/print/pprust.rs | 91 ++++++++++++++---- src/libsyntax/visit.rs | 13 +++ src/test/run-pass/fn-trait-sugar.rs | 38 ++++++++ 12 files changed, 343 insertions(+), 46 deletions(-) create mode 100644 src/test/run-pass/fn-trait-sugar.rs diff --git a/src/librustc/front/feature_gate.rs b/src/librustc/front/feature_gate.rs index a9b9892c2c9..cd472321237 100644 --- a/src/librustc/front/feature_gate.rs +++ b/src/librustc/front/feature_gate.rs @@ -57,6 +57,7 @@ static KNOWN_FEATURES: &'static [(&'static str, Status)] = &[ ("linkage", Active), ("struct_inherit", Active), ("overloaded_calls", Active), + ("unboxed_closure_sugar", Active), ("quad_precision_float", Active), @@ -291,6 +292,11 @@ impl<'a> Visitor<()> for Context<'a> { }, ast::TyBox(_) => { self.gate_box(t.span); } + ast::TyUnboxedFn(_) => { + self.gate_feature("unboxed_closure_sugar", + t.span, + "unboxed closure trait sugar is experimental"); + } _ => {} } diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index 9bfa0e10aed..89d37bb98b8 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -3856,14 +3856,20 @@ impl<'a> Resolver<'a> { } fn resolve_type_parameter_bound(&mut self, - id: NodeId, - type_parameter_bound: &TyParamBound) { + id: NodeId, + type_parameter_bound: &TyParamBound) { match *type_parameter_bound { TraitTyParamBound(ref tref) => { self.resolve_trait_reference(id, tref, TraitBoundingTypeParameter) } - StaticRegionTyParamBound => {} - OtherRegionTyParamBound(_) => {} + UnboxedFnTyParamBound(ref unboxed_function) => { + for argument in unboxed_function.decl.inputs.iter() { + self.resolve_type(argument.ty); + } + + self.resolve_type(unboxed_function.decl.output); + } + StaticRegionTyParamBound | OtherRegionTyParamBound(_) => {} } } diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index bdb23aea067..1e6f5fe870b 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -51,13 +51,14 @@ use middle::const_eval; use middle::def; -use middle::subst; +use middle::lang_items::FnMutTraitLangItem; use middle::subst::{Subst, Substs}; -use middle::ty::{ty_param_substs_and_ty}; +use middle::subst; +use middle::ty::ty_param_substs_and_ty; use middle::ty; -use middle::typeck::rscope; -use middle::typeck::rscope::{RegionScope}; use middle::typeck::lookup_def_tcx; +use middle::typeck::rscope::RegionScope; +use middle::typeck::rscope; use util::ppaux::Repr; use std::rc::Rc; @@ -469,6 +470,38 @@ fn ast_ty_to_mt(this: &AC, ty::mt {ty: ast_ty_to_ty(this, rscope, ty), mutbl: ast::MutImmutable} } +pub fn trait_ref_for_unboxed_function( + this: &AC, + rscope: &RS, + unboxed_function: &ast::UnboxedFnTy) + -> ty::TraitRef { + let fn_mut_trait_did = this.tcx() + .lang_items + .require(FnMutTraitLangItem) + .unwrap(); + let input_types = + unboxed_function.decl + .inputs + .iter() + .map(|input| { + ast_ty_to_ty(this, rscope, input.ty) + }).collect::>(); + let input_tuple = ty::mk_tup(this.tcx(), input_types); + let output_type = ast_ty_to_ty(this, + rscope, + unboxed_function.decl.output); + let substs = subst::Substs { + self_ty: None, + tps: vec!(input_tuple, output_type), + regions: subst::NonerasedRegions(Vec::new()), + }; + ty::TraitRef { + def_id: fn_mut_trait_did, + substs: substs, + } +} + // Handle `~`, `Box`, and `&` being able to mean strs and vecs. // If a_seq_ty is a str or a vec, make it a str/vec. // Also handle first-class trait types. @@ -491,6 +524,32 @@ fn mk_pointer { + let trait_store = match ptr_ty { + Uniq => ty::UniqTraitStore, + RPtr(r) => { + ty::RegionTraitStore(r, a_seq_ty.mutbl) + } + _ => { + tcx.sess.span_err( + a_seq_ty.ty.span, + "~trait or &trait are the only supported \ + forms of casting-to-trait"); + return ty::mk_err(); + } + }; + let ty::TraitRef { + def_id, + substs + } = trait_ref_for_unboxed_function(this, + rscope, + *unboxed_function); + return ty::mk_trait(this.tcx(), + def_id, + substs, + trait_store, + ty::empty_builtin_bounds()); + } ast::TyPath(ref path, ref bounds, id) => { // Note that the "bounds must be empty if path is not a trait" // restriction is enforced in the below case for ty_path, which @@ -528,7 +587,10 @@ fn mk_pointer( // Use corresponding trait store to figure out default bounds // if none were specified. - let bounds = conv_builtin_bounds(this.tcx(), &f.bounds, store); + let bounds = conv_builtin_bounds(this.tcx(), + ast_ty.span, + &f.bounds, + store); let fn_decl = ty_of_closure(this, ast_ty.id, @@ -636,7 +701,10 @@ pub fn ast_ty_to_ty( ast::TyProc(ref f) => { // Use corresponding trait store to figure out default bounds // if none were specified. - let bounds = conv_builtin_bounds(this.tcx(), &f.bounds, ty::UniqTraitStore); + let bounds = conv_builtin_bounds(this.tcx(), + ast_ty.span, + &f.bounds, + ty::UniqTraitStore); let fn_decl = ty_of_closure(this, ast_ty.id, @@ -648,6 +716,11 @@ pub fn ast_ty_to_ty( None); ty::mk_closure(tcx, fn_decl) } + ast::TyUnboxedFn(_) => { + tcx.sess.span_err(ast_ty.span, + "cannot use unboxed functions here"); + ty::mk_err() + } ast::TyPath(ref path, ref bounds, id) => { let a_def = match tcx.def_map.borrow().find(&id) { None => { @@ -891,7 +964,9 @@ pub fn ty_of_closure( } } -fn conv_builtin_bounds(tcx: &ty::ctxt, ast_bounds: &Option>, +fn conv_builtin_bounds(tcx: &ty::ctxt, + span: Span, + ast_bounds: &Option>, store: ty::TraitStore) -> ty::BuiltinBounds { //! Converts a list of bounds from the AST into a `BuiltinBounds` @@ -928,6 +1003,11 @@ fn conv_builtin_bounds(tcx: &ty::ctxt, ast_bounds: &Option { builtin_bounds.add(ty::BoundStatic); } + ast::UnboxedFnTyParamBound(_) => { + tcx.sess.span_err(span, + "unboxed functions are not allowed \ + here"); + } ast::OtherRegionTyParamBound(span) => { if !tcx.sess.features.issue_5723_bootstrap.get() { tcx.sess.span_err( diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index c6bc6ce7297..edb7f589e95 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -48,22 +48,21 @@ use middle::typeck::{CrateCtxt, lookup_def_tcx, no_params, write_ty_to_tcx}; use util::ppaux; use util::ppaux::Repr; -use std::rc::Rc; use std::collections::{HashMap, HashSet}; - +use std::rc::Rc; use syntax::abi; -use syntax::ast::{StaticRegionTyParamBound, OtherRegionTyParamBound, - TraitTyParamBound}; +use syntax::ast::{StaticRegionTyParamBound, OtherRegionTyParamBound}; +use syntax::ast::{TraitTyParamBound, UnboxedFnTyParamBound}; use syntax::ast; use syntax::ast_map; use syntax::ast_util::{local_def, split_trait_methods}; use syntax::codemap::Span; use syntax::codemap; +use syntax::owned_slice::OwnedSlice; use syntax::parse::token::special_idents; use syntax::parse::token; use syntax::print::pprust::{path_to_str}; use syntax::visit; -use syntax::owned_slice::OwnedSlice; struct CollectItemTypesVisitor<'a> { ccx: &'a CrateCtxt<'a> @@ -1114,6 +1113,20 @@ fn ty_generics(ccx: &CrateCtxt, param_bounds.builtin_bounds.add(ty::BoundStatic); } + UnboxedFnTyParamBound(ref unboxed_function) => { + let rscope = ExplicitRscope; + let mut trait_ref = + astconv::trait_ref_for_unboxed_function( + ccx, + &rscope, + unboxed_function); + let self_ty = ty::mk_param(ccx.tcx, + param_ty.idx, + param_ty.def_id); + trait_ref.substs.self_ty = Some(self_ty); + param_bounds.trait_bounds.push(Rc::new(trait_ref)); + } + OtherRegionTyParamBound(span) => { if !ccx.tcx.sess.features.issue_5723_bootstrap.get() { ccx.tcx.sess.span_err( diff --git a/src/librustc/middle/typeck/infer/error_reporting.rs b/src/librustc/middle/typeck/infer/error_reporting.rs index 6464b191b76..8f53c0a7530 100644 --- a/src/librustc/middle/typeck/infer/error_reporting.rs +++ b/src/librustc/middle/typeck/infer/error_reporting.rs @@ -909,6 +909,9 @@ impl<'a> Rebuilder<'a> { match tpb { &ast::StaticRegionTyParamBound => ast::StaticRegionTyParamBound, &ast::OtherRegionTyParamBound(s) => ast::OtherRegionTyParamBound(s), + &ast::UnboxedFnTyParamBound(unboxed_function_type) => { + ast::UnboxedFnTyParamBound(unboxed_function_type) + } &ast::TraitTyParamBound(ref tr) => { let last_seg = tr.path.segments.last().unwrap(); let mut insert = Vec::new(); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 650cd749af6..54128fda6c6 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -477,6 +477,10 @@ impl Clean for ast::TyParamBound { match *self { ast::StaticRegionTyParamBound => RegionBound, ast::OtherRegionTyParamBound(_) => RegionBound, + ast::UnboxedFnTyParamBound(_) => { + // FIXME(pcwalton): Wrong. + RegionBound + } ast::TraitTyParamBound(ref t) => TraitBound(t.clean()), } } diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 2bc24fd1eb3..9c3960d0f06 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -175,6 +175,7 @@ pub static DUMMY_NODE_ID: NodeId = -1; pub enum TyParamBound { TraitTyParamBound(TraitRef), StaticRegionTyParamBound, + UnboxedFnTyParamBound(UnboxedFnTy), OtherRegionTyParamBound(Span) // FIXME -- just here until work for #5723 lands } @@ -769,6 +770,11 @@ pub struct BareFnTy { pub decl: P } +#[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash)] +pub struct UnboxedFnTy { + pub decl: P, +} + #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash)] pub enum Ty_ { TyNil, @@ -782,6 +788,7 @@ pub enum Ty_ { TyClosure(@ClosureTy, Option), TyProc(@ClosureTy), TyBareFn(@BareFnTy), + TyUnboxedFn(@UnboxedFnTy), TyTup(Vec> ), TyPath(Path, Option>, NodeId), // for #7264; see above TyTypeof(@Expr), diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 8903eb80829..03d0c283bcc 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -185,6 +185,11 @@ pub trait Folder { decl: self.fold_fn_decl(f.decl) }) } + TyUnboxedFn(ref f) => { + TyUnboxedFn(@UnboxedFnTy { + decl: self.fold_fn_decl(f.decl), + }) + } TyTup(ref tys) => TyTup(tys.iter().map(|&ty| self.fold_ty(ty)).collect()), TyPath(ref path, ref bounds, id) => { let id = self.new_id(id); @@ -440,6 +445,11 @@ fn fold_ty_param_bound(tpb: &TyParamBound, fld: &mut T) match *tpb { TraitTyParamBound(ref ty) => TraitTyParamBound(fold_trait_ref(ty, fld)), StaticRegionTyParamBound => StaticRegionTyParamBound, + UnboxedFnTyParamBound(ref unboxed_function_type) => { + UnboxedFnTyParamBound(UnboxedFnTy { + decl: fld.fold_fn_decl(unboxed_function_type.decl), + }) + } OtherRegionTyParamBound(s) => OtherRegionTyParamBound(s) } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 4af4385e3c1..360b8daa948 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -52,9 +52,9 @@ use ast::{TTNonterminal, TupleVariantKind, Ty, Ty_, TyBot, TyBox}; use ast::{TypeField, TyFixedLengthVec, TyClosure, TyProc, TyBareFn}; use ast::{TyTypeof, TyInfer, TypeMethod}; use ast::{TyNil, TyParam, TyParamBound, TyPath, TyPtr, TyRptr}; -use ast::{TyTup, TyU32, TyUniq, TyVec, UnUniq}; -use ast::{UnnamedField, UnsafeBlock, UnsafeFn, ViewItem}; -use ast::{ViewItem_, ViewItemExternCrate, ViewItemUse}; +use ast::{TyTup, TyU32, TyUnboxedFn, TyUniq, TyVec, UnUniq}; +use ast::{UnboxedFnTy, UnboxedFnTyParamBound, UnnamedField, UnsafeBlock}; +use ast::{UnsafeFn, ViewItem, ViewItem_, ViewItemExternCrate, ViewItemUse}; use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple}; use ast::Visibility; use ast; @@ -1058,15 +1058,27 @@ impl<'a> Parser<'a> { Vec::new() }; - let inputs = if self.eat(&token::OROR) { - Vec::new() + let (is_unboxed, inputs) = if self.eat(&token::OROR) { + (false, Vec::new()) } else { self.expect_or(); + + let is_unboxed = self.token == token::BINOP(token::AND) && + self.look_ahead(1, |t| { + token::is_keyword(keywords::Mut, t) + }) && + self.look_ahead(2, |t| *t == token::COLON); + if is_unboxed { + self.bump(); + self.bump(); + self.bump(); + } + let inputs = self.parse_seq_to_before_or( &token::COMMA, |p| p.parse_arg_general(false)); self.expect_or(); - inputs + (is_unboxed, inputs) }; let (region, bounds) = self.parse_optional_ty_param_bounds(true); @@ -1079,13 +1091,19 @@ impl<'a> Parser<'a> { variadic: false }); - TyClosure(@ClosureTy { - fn_style: fn_style, - onceness: onceness, - bounds: bounds, - decl: decl, - lifetimes: lifetimes, - }, region) + if is_unboxed { + TyUnboxedFn(@UnboxedFnTy { + decl: decl, + }) + } else { + TyClosure(@ClosureTy { + fn_style: fn_style, + onceness: onceness, + bounds: bounds, + decl: decl, + lifetimes: lifetimes, + }, region) + } } pub fn parse_unsafety(&mut self) -> FnStyle { @@ -3345,6 +3363,41 @@ impl<'a> Parser<'a> { }) } + fn parse_unboxed_function_type(&mut self) -> UnboxedFnTy { + let inputs = if self.eat(&token::OROR) { + Vec::new() + } else { + self.expect_or(); + + if self.token == token::BINOP(token::AND) && + self.look_ahead(1, |t| { + token::is_keyword(keywords::Mut, t) + }) && + self.look_ahead(2, |t| *t == token::COLON) { + self.bump(); + self.bump(); + self.bump(); + } + + let inputs = self.parse_seq_to_before_or(&token::COMMA, + |p| { + p.parse_arg_general(false) + }); + self.expect_or(); + inputs + }; + + let (return_style, output) = self.parse_ret_ty(); + UnboxedFnTy { + decl: P(FnDecl { + inputs: inputs, + output: output, + cf: return_style, + variadic: false, + }) + } + } + // matches optbounds = ( ( : ( boundseq )? )? ) // where boundseq = ( bound + boundseq ) | bound // and bound = 'static | ty @@ -3394,6 +3447,11 @@ impl<'a> Parser<'a> { let tref = self.parse_trait_ref(); result.push(TraitTyParamBound(tref)); } + token::BINOP(token::OR) | token::OROR => { + let unboxed_function_type = + self.parse_unboxed_function_type(); + result.push(UnboxedFnTyParamBound(unboxed_function_type)); + } _ => break, } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 05c2558da48..f22b24b5a29 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -10,7 +10,7 @@ use abi; use ast::{P, StaticRegionTyParamBound, OtherRegionTyParamBound, - TraitTyParamBound, Required, Provided}; + TraitTyParamBound, UnboxedFnTyParamBound, Required, Provided}; use ast; use ast_util; use owned_slice::OwnedSlice; @@ -505,27 +505,64 @@ impl<'a> State<'a> { lifetimes: f.lifetimes.clone(), ty_params: OwnedSlice::empty() }; - try!(self.print_ty_fn(Some(f.abi), None, &None, - f.fn_style, ast::Many, f.decl, None, &None, - Some(&generics), None)); + try!(self.print_ty_fn(Some(f.abi), + None, + &None, + f.fn_style, + ast::Many, + f.decl, + None, + &None, + Some(&generics), + None, + false)); } ast::TyClosure(f, ref region) => { let generics = ast::Generics { lifetimes: f.lifetimes.clone(), ty_params: OwnedSlice::empty() }; - try!(self.print_ty_fn(None, Some('&'), region, f.fn_style, - f.onceness, f.decl, None, &f.bounds, - Some(&generics), None)); + try!(self.print_ty_fn(None, + Some('&'), + region, + f.fn_style, + f.onceness, + f.decl, + None, + &f.bounds, + Some(&generics), + None, + false)); } ast::TyProc(f) => { let generics = ast::Generics { lifetimes: f.lifetimes.clone(), ty_params: OwnedSlice::empty() }; - try!(self.print_ty_fn(None, Some('~'), &None, f.fn_style, - f.onceness, f.decl, None, &f.bounds, - Some(&generics), None)); + try!(self.print_ty_fn(None, + Some('~'), + &None, + f.fn_style, + f.onceness, + f.decl, + None, + &f.bounds, + Some(&generics), + None, + false)); + } + ast::TyUnboxedFn(f) => { + try!(self.print_ty_fn(None, + None, + &None, + ast::NormalFn, + ast::Many, + f.decl, + None, + &None, + None, + None, + true)); } ast::TyPath(ref path, ref bounds, _) => { try!(self.print_bounded_path(path, bounds)); @@ -930,7 +967,8 @@ impl<'a> State<'a> { Some(m.ident), &None, Some(&m.generics), - Some(m.explicit_self.node))); + Some(m.explicit_self.node), + false)); word(&mut self.s, ";") } @@ -1925,6 +1963,19 @@ impl<'a> State<'a> { try!(match *bound { TraitTyParamBound(ref tref) => self.print_trait_ref(tref), StaticRegionTyParamBound => word(&mut self.s, "'static"), + UnboxedFnTyParamBound(ref unboxed_function_type) => { + self.print_ty_fn(None, + None, + &None, + ast::NormalFn, + ast::Many, + unboxed_function_type.decl, + None, + &None, + None, + None, + true) + } OtherRegionTyParamBound(_) => Ok(()) }) } @@ -2112,8 +2163,9 @@ impl<'a> State<'a> { id: Option, opt_bounds: &Option>, generics: Option<&ast::Generics>, - opt_explicit_self: Option) - -> IoResult<()> { + opt_explicit_self: Option, + is_unboxed: bool) + -> IoResult<()> { try!(self.ibox(indent_unit)); // Duplicates the logic in `print_fn_header_info()`. This is because that @@ -2129,7 +2181,9 @@ impl<'a> State<'a> { try!(self.print_fn_style(fn_style)); try!(self.print_opt_abi_and_extern_if_nondefault(opt_abi)); try!(self.print_onceness(onceness)); - try!(word(&mut self.s, "fn")); + if !is_unboxed { + try!(word(&mut self.s, "fn")); + } } match id { @@ -2143,15 +2197,20 @@ impl<'a> State<'a> { match generics { Some(g) => try!(self.print_generics(g)), _ => () } try!(zerobreak(&mut self.s)); - if opt_sigil == Some('&') { + if is_unboxed || opt_sigil == Some('&') { try!(word(&mut self.s, "|")); } else { try!(self.popen()); } + if is_unboxed { + try!(word(&mut self.s, "&mut")); + try!(self.word_space(":")); + } + try!(self.print_fn_args(decl, opt_explicit_self)); - if opt_sigil == Some('&') { + if is_unboxed || opt_sigil == Some('&') { try!(word(&mut self.s, "|")); } else { if decl.variadic { diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 906f0c16f39..b5ae8a3dea0 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -383,6 +383,12 @@ pub fn walk_ty>(visitor: &mut V, typ: &Ty, env: E) { walk_lifetime_decls(visitor, &function_declaration.lifetimes, env.clone()); } + TyUnboxedFn(ref function_declaration) => { + for argument in function_declaration.decl.inputs.iter() { + visitor.visit_ty(argument.ty, env.clone()) + } + visitor.visit_ty(function_declaration.decl.output, env.clone()); + } TyPath(ref path, ref bounds, id) => { visitor.visit_path(path, id, env.clone()); for bounds in bounds.iter() { @@ -501,6 +507,13 @@ pub fn walk_ty_param_bounds>(visitor: &mut V, walk_trait_ref_helper(visitor, typ, env.clone()) } StaticRegionTyParamBound => {} + UnboxedFnTyParamBound(ref function_declaration) => { + for argument in function_declaration.decl.inputs.iter() { + visitor.visit_ty(argument.ty, env.clone()) + } + visitor.visit_ty(function_declaration.decl.output, + env.clone()); + } OtherRegionTyParamBound(..) => {} } } diff --git a/src/test/run-pass/fn-trait-sugar.rs b/src/test/run-pass/fn-trait-sugar.rs new file mode 100644 index 00000000000..b0c8d84b664 --- /dev/null +++ b/src/test/run-pass/fn-trait-sugar.rs @@ -0,0 +1,38 @@ + +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(unboxed_closure_sugar)] + +use std::ops::FnMut; + +struct S; + +impl FnMut<(int,),int> for S { + fn call_mut(&mut self, (x,): (int,)) -> int { + x * x + } +} + +fn call_itint>(mut f: F, x: int) -> int { + f.call_mut((x,)) + 3 +} + +fn call_box(f: &mut |&mut: int|->int, x: int) -> int { + f.call_mut((x,)) + 3 +} + +fn main() { + let x = call_it(S, 1); + let y = call_box(&mut S, 1); + assert!(x == 4); + assert!(y == 4); +} +