Remove the explicit closure kind syntax from the parser and AST;

upgrade the inference based on expected type so that it is able to
infer the fn kind in isolation even if the full signature is not
available (and we could perhaps do better still in some cases, such as
extracting just the types of the arguments but not the return value).
This commit is contained in:
Niko Matsakis 2015-02-03 11:34:05 -05:00
parent 47f18659ff
commit 0431134119
23 changed files with 157 additions and 138 deletions

View File

@ -45,7 +45,7 @@ impl<'a, 'v> Visitor<'v> for CheckLoopVisitor<'a> {
ast::ExprLoop(ref b, _) => {
self.with_context(Loop, |v| v.visit_block(&**b));
}
ast::ExprClosure(_, _, _, ref b) => {
ast::ExprClosure(_, _, ref b) => {
self.with_context(Closure, |v| v.visit_block(&**b));
}
ast::ExprBreak(_) => self.require_loop("break", e.span),

View File

@ -959,7 +959,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
self.propagate_through_expr(&**e, succ)
}
ast::ExprClosure(_, _, _, ref blk) => {
ast::ExprClosure(_, _, ref blk) => {
debug!("{} is an ExprClosure",
expr_to_string(expr));

View File

@ -739,7 +739,7 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> {
};
match fn_expr.node {
ast::ExprClosure(_, _, _, ref body) => body.id,
ast::ExprClosure(_, _, ref body) => body.id,
_ => unreachable!()
}
};

View File

@ -324,7 +324,7 @@ pub fn closure_to_block(closure_id: ast::NodeId,
tcx: &ty::ctxt) -> ast::NodeId {
match tcx.map.get(closure_id) {
ast_map::NodeExpr(expr) => match expr.node {
ast::ExprClosure(_, _, _, ref block) => {
ast::ExprClosure(_, _, ref block) => {
block.id
}
_ => {

View File

@ -4521,7 +4521,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
visit::walk_expr(self, expr);
}
ExprClosure(_, _, ref fn_decl, ref block) => {
ExprClosure(_, ref fn_decl, ref block) => {
self.resolve_function(ClosureRibKind(expr.id),
Some(&**fn_decl), NoTypeParameters,
&**block);

View File

@ -1394,7 +1394,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> {
type, found {:?}", ty)[]),
}
},
ast::ExprClosure(_, _, ref decl, ref body) => {
ast::ExprClosure(_, ref decl, ref body) => {
if generated_code(body.span) {
return
}

View File

@ -1340,7 +1340,7 @@ fn build_cfg(tcx: &ty::ctxt, id: ast::NodeId) -> (ast::NodeId, Option<cfg::CFG>)
}
Some(ast_map::NodeExpr(e)) => {
match e.node {
ast::ExprClosure(_, _, _, ref blk) => {
ast::ExprClosure(_, _, ref blk) => {
blk
}
_ => tcx.sess.bug("unexpected expr variant in has_nested_returns")

View File

@ -1283,7 +1283,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
}
ast_map::NodeExpr(ref expr) => {
match expr.node {
ast::ExprClosure(_, _, ref fn_decl, ref top_level_block) => {
ast::ExprClosure(_, ref fn_decl, ref top_level_block) => {
let name = format!("fn{}", token::gensym("fn"));
let name = token::str_to_ident(&name[]);
(name, &**fn_decl,
@ -3595,7 +3595,7 @@ fn create_scope_map(cx: &CrateContext,
})
}
ast::ExprClosure(_, _, ref decl, ref block) => {
ast::ExprClosure(_, ref decl, ref block) => {
with_new_scope(cx,
block.span,
scope_stack,

View File

@ -1094,7 +1094,7 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
ast::ExprVec(..) | ast::ExprRepeat(..) => {
tvec::trans_fixed_vstore(bcx, expr, dest)
}
ast::ExprClosure(_, _, ref decl, ref body) => {
ast::ExprClosure(_, ref decl, ref body) => {
closure::trans_closure_expr(bcx, &**decl, &**body, expr.id, dest)
}
ast::ExprCall(ref f, ref args) => {

View File

@ -25,7 +25,6 @@ use util::ppaux::Repr;
pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
expr: &ast::Expr,
_capture: ast::CaptureClause,
opt_kind: Option<ast::ClosureKind>,
decl: &'tcx ast::FnDecl,
body: &'tcx ast::Block,
expected: Expectation<'tcx>) {
@ -33,38 +32,14 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
expr.repr(fcx.tcx()),
expected.repr(fcx.tcx()));
let expected_sig_and_kind = expected.to_option(fcx).and_then(|ty| {
deduce_expectations_from_expected_type(fcx, ty)
});
match opt_kind {
None => {
// If users didn't specify what sort of closure they want,
// examine the expected type. For now, if we see explicit
// evidence than an unboxed closure is desired, we'll use
// that. Otherwise, we leave it unspecified, to be filled
// in by upvar inference.
match expected_sig_and_kind {
None => { // don't have information about the kind, request explicit annotation
check_closure(fcx, expr, None, decl, body, None);
},
Some((sig, kind)) => {
check_closure(fcx, expr, Some(kind), decl, body, Some(sig));
}
}
}
Some(kind) => {
let kind = match kind {
ast::FnClosureKind => ty::FnClosureKind,
ast::FnMutClosureKind => ty::FnMutClosureKind,
ast::FnOnceClosureKind => ty::FnOnceClosureKind,
};
let expected_sig = expected_sig_and_kind.map(|t| t.0);
check_closure(fcx, expr, Some(kind), decl, body, expected_sig);
}
}
// It's always helpful for inference if we know the kind of
// closure sooner rather than later, so first examine the expected
// type, and see if can glean a closure kind from there.
let (expected_sig,expected_kind) = match expected.to_option(fcx) {
Some(ty) => deduce_expectations_from_expected_type(fcx, ty),
None => (None, None)
};
check_closure(fcx, expr, expected_kind, decl, body, expected_sig)
}
fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
@ -133,21 +108,30 @@ fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>,
fn deduce_expectations_from_expected_type<'a,'tcx>(
fcx: &FnCtxt<'a,'tcx>,
expected_ty: Ty<'tcx>)
-> Option<(ty::FnSig<'tcx>,ty::ClosureKind)>
-> (Option<ty::FnSig<'tcx>>,Option<ty::ClosureKind>)
{
debug!("deduce_expectations_from_expected_type(expected_ty={})",
expected_ty.repr(fcx.tcx()));
match expected_ty.sty {
ty::ty_trait(ref object_type) => {
let proj_bounds = object_type.projection_bounds_with_self_ty(fcx.tcx(),
fcx.tcx().types.err);
proj_bounds.iter()
.filter_map(|pb| deduce_expectations_from_projection(fcx, pb))
.next()
let expectations =
proj_bounds.iter()
.filter_map(|pb| deduce_expectations_from_projection(fcx, pb))
.next();
match expectations {
Some((sig, kind)) => (Some(sig), Some(kind)),
None => (None, None)
}
}
ty::ty_infer(ty::TyVar(vid)) => {
deduce_expectations_from_obligations(fcx, vid)
}
_ => {
None
(None, None)
}
}
}
@ -155,33 +139,61 @@ fn deduce_expectations_from_expected_type<'a,'tcx>(
fn deduce_expectations_from_obligations<'a,'tcx>(
fcx: &FnCtxt<'a,'tcx>,
expected_vid: ty::TyVid)
-> Option<(ty::FnSig<'tcx>, ty::ClosureKind)>
-> (Option<ty::FnSig<'tcx>>, Option<ty::ClosureKind>)
{
let fulfillment_cx = fcx.inh.fulfillment_cx.borrow();
// Here `expected_ty` is known to be a type inference variable.
fulfillment_cx.pending_obligations()
.iter()
.filter_map(|obligation| {
match obligation.predicate {
ty::Predicate::Projection(ref proj_predicate) => {
let trait_ref = proj_predicate.to_poly_trait_ref();
let self_ty = fcx.infcx().shallow_resolve(trait_ref.self_ty());
match self_ty.sty {
ty::ty_infer(ty::TyVar(v)) if expected_vid == v => {
deduce_expectations_from_projection(fcx, proj_predicate)
}
_ => {
None
}
}
}
_ => {
None
}
}
})
.next()
let expected_sig_and_kind =
fulfillment_cx
.pending_obligations()
.iter()
.filter_map(|obligation| {
debug!("deduce_expectations_from_obligations: obligation.predicate={}",
obligation.predicate.repr(fcx.tcx()));
match obligation.predicate {
// Given a Projection predicate, we can potentially infer
// the complete signature.
ty::Predicate::Projection(ref proj_predicate) => {
let trait_ref = proj_predicate.to_poly_trait_ref();
self_type_matches_expected_vid(fcx, trait_ref, expected_vid)
.and_then(|_| deduce_expectations_from_projection(fcx, proj_predicate))
}
_ => {
None
}
}
})
.next();
match expected_sig_and_kind {
Some((sig, kind)) => { return (Some(sig), Some(kind)); }
None => { }
}
// Even if we can't infer the full signature, we may be able to
// infer the kind. This can occur if there is a trait-reference
// like `F : Fn<A>`.
let expected_kind =
fulfillment_cx
.pending_obligations()
.iter()
.filter_map(|obligation| {
let opt_trait_ref = match obligation.predicate {
ty::Predicate::Projection(ref data) => Some(data.to_poly_trait_ref()),
ty::Predicate::Trait(ref data) => Some(data.to_poly_trait_ref()),
ty::Predicate::Equate(..) => None,
ty::Predicate::RegionOutlives(..) => None,
ty::Predicate::TypeOutlives(..) => None,
};
opt_trait_ref
.and_then(|trait_ref| self_type_matches_expected_vid(fcx, trait_ref, expected_vid))
.and_then(|trait_ref| fcx.tcx().lang_items.fn_trait_kind(trait_ref.def_id()))
})
.next();
(None, expected_kind)
}
/// Given a projection like "<F as Fn(X)>::Result == Y", we can deduce
@ -229,3 +241,20 @@ fn deduce_expectations_from_projection<'a,'tcx>(
return Some((fn_sig, kind));
}
fn self_type_matches_expected_vid<'a,'tcx>(
fcx: &FnCtxt<'a,'tcx>,
trait_ref: ty::PolyTraitRef<'tcx>,
expected_vid: ty::TyVid)
-> Option<ty::PolyTraitRef<'tcx>>
{
let self_ty = fcx.infcx().shallow_resolve(trait_ref.self_ty());
debug!("self_type_matches_expected_vid(trait_ref={}, self_ty={})",
trait_ref.repr(fcx.tcx()),
self_ty.repr(fcx.tcx()));
match self_ty.sty {
ty::ty_infer(ty::TyVar(v)) if expected_vid == v => Some(trait_ref),
_ => None,
}
}

View File

@ -3736,8 +3736,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
ast::ExprMatch(ref discrim, ref arms, match_src) => {
_match::check_match(fcx, expr, &**discrim, arms.as_slice(), expected, match_src);
}
ast::ExprClosure(capture, opt_kind, ref decl, ref body) => {
closure::check_expr_closure(fcx, expr, capture, opt_kind, &**decl, &**body, expected);
ast::ExprClosure(capture, ref decl, ref body) => {
closure::check_expr_closure(fcx, expr, capture, &**decl, &**body, expected);
}
ast::ExprBlock(ref b) => {
check_block_with_expected(fcx, &**b, expected);

View File

@ -638,7 +638,7 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) {
visit::walk_expr(rcx, expr);
}
ast::ExprClosure(_, _, _, ref body) => {
ast::ExprClosure(_, _, ref body) => {
check_expr_fn_block(rcx, expr, &**body);
}

View File

@ -83,7 +83,7 @@ struct SeedBorrowKind<'a,'tcx:'a> {
impl<'a, 'tcx, 'v> Visitor<'v> for SeedBorrowKind<'a, 'tcx> {
fn visit_expr(&mut self, expr: &ast::Expr) {
match expr.node {
ast::ExprClosure(cc, _, _, ref body) => {
ast::ExprClosure(cc, _, ref body) => {
self.check_closure(expr, cc, &**body);
}

View File

@ -118,7 +118,7 @@ impl<'cx, 'tcx, 'v> Visitor<'v> for WritebackCx<'cx, 'tcx> {
MethodCall::expr(e.id));
match e.node {
ast::ExprClosure(_, _, ref decl, _) => {
ast::ExprClosure(_, ref decl, _) => {
for input in &decl.inputs {
let _ = self.visit_node_id(ResolvingExpr(e.span),
input.id);

View File

@ -48,7 +48,6 @@ pub use self::TraitItem::*;
pub use self::Ty_::*;
pub use self::TyParamBound::*;
pub use self::UintTy::*;
pub use self::ClosureKind::*;
pub use self::UnOp::*;
pub use self::UnsafeSource::*;
pub use self::VariantKind::*;
@ -736,7 +735,7 @@ pub enum Expr_ {
// FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
ExprLoop(P<Block>, Option<Ident>),
ExprMatch(P<Expr>, Vec<Arm>, MatchSource),
ExprClosure(CaptureClause, Option<ClosureKind>, P<FnDecl>, P<Block>),
ExprClosure(CaptureClause, P<FnDecl>, P<Block>),
ExprBlock(P<Block>),
ExprAssign(P<Expr>, P<Expr>),
@ -1687,13 +1686,6 @@ impl ForeignItem_ {
}
}
#[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug, Copy)]
pub enum ClosureKind {
FnClosureKind,
FnMutClosureKind,
FnOnceClosureKind,
}
/// The data we save and restore about an inlined item or method. This is not
/// part of the AST that we parse from a file, but it becomes part of the tree
/// that we trans.

View File

@ -218,7 +218,7 @@ impl<'a> FnLikeNode<'a> {
}
}
ast_map::NodeExpr(e) => match e.node {
ast::ExprClosure(_, _, ref decl, ref block) =>
ast::ExprClosure(_, ref decl, ref block) =>
closure(ClosureParts::new(&**decl, &**block, e.id, e.span)),
_ => panic!("expr FnLikeNode that is not fn-like"),
},

View File

@ -876,14 +876,14 @@ impl<'a> AstBuilder for ExtCtxt<'a> {
fn lambda_fn_decl(&self, span: Span,
fn_decl: P<ast::FnDecl>, blk: P<ast::Block>) -> P<ast::Expr> {
self.expr(span, ast::ExprClosure(ast::CaptureByRef, None, fn_decl, blk))
self.expr(span, ast::ExprClosure(ast::CaptureByRef, fn_decl, blk))
}
fn lambda(&self, span: Span, ids: Vec<ast::Ident>, blk: P<ast::Block>) -> P<ast::Expr> {
let fn_decl = self.fn_decl(
ids.iter().map(|id| self.arg(span, *id, self.ty_infer(span))).collect(),
self.ty_infer(span));
self.expr(span, ast::ExprClosure(ast::CaptureByRef, None, fn_decl, blk))
self.expr(span, ast::ExprClosure(ast::CaptureByRef, fn_decl, blk))
}
fn lambda0(&self, span: Span, blk: P<ast::Block>) -> P<ast::Expr> {
self.lambda(span, Vec::new(), blk)

View File

@ -322,11 +322,10 @@ pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
fld.cx.expr_match(span, into_iter_expr, vec![iter_arm])
}
ast::ExprClosure(capture_clause, opt_kind, fn_decl, block) => {
ast::ExprClosure(capture_clause, fn_decl, block) => {
let (rewritten_fn_decl, rewritten_block)
= expand_and_rename_fn_decl_and_block(fn_decl, block, fld);
let new_node = ast::ExprClosure(capture_clause,
opt_kind,
rewritten_fn_decl,
rewritten_block);
P(ast::Expr{id:id, node: new_node, span: fld.new_span(span)})

View File

@ -1325,9 +1325,8 @@ pub fn noop_fold_expr<T: Folder>(Expr {id, node, span}: Expr, folder: &mut T) ->
arms.move_map(|x| folder.fold_arm(x)),
source)
}
ExprClosure(capture_clause, opt_kind, decl, body) => {
ExprClosure(capture_clause, decl, body) => {
ExprClosure(capture_clause,
opt_kind,
folder.fold_fn_decl(decl),
folder.fold_block(body))
}

View File

@ -27,6 +27,7 @@ pub enum ObsoleteSyntax {
ProcType,
ProcExpr,
ClosureType,
ClosureKind,
}
pub trait ParserObsoleteMethods {
@ -65,6 +66,10 @@ impl<'a> ParserObsoleteMethods for parser::Parser<'a> {
"`|usize| -> bool` closure type syntax",
"use unboxed closures instead, no type annotation needed"
),
ObsoleteSyntax::ClosureKind => (
"`:`, `&mut:`, or `&:` syntax",
"rely on inference instead"
),
ObsoleteSyntax::Sized => (
"`Sized? T` syntax for removing the `Sized` bound",
"write `T: ?Sized` instead"

View File

@ -28,8 +28,6 @@ use ast::{ExprLit, ExprLoop, ExprMac, ExprRange};
use ast::{ExprMethodCall, ExprParen, ExprPath, ExprQPath};
use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary};
use ast::{ExprVec, ExprWhile, ExprWhileLet, ExprForLoop, Field, FnDecl};
use ast::{FnClosureKind, FnMutClosureKind};
use ast::{FnOnceClosureKind};
use ast::{ForeignItem, ForeignItemStatic, ForeignItemFn, ForeignMod, FunctionRetTy};
use ast::{Ident, Inherited, ImplItem, Item, Item_, ItemStatic};
use ast::{ItemEnum, ItemFn, ItemForeignMod, ItemImpl, ItemConst};
@ -57,7 +55,7 @@ use ast::{TyFixedLengthVec, TyBareFn};
use ast::{TyTypeof, TyInfer, TypeMethod};
use ast::{TyParam, TyParamBound, TyParen, TyPath, TyPolyTraitRef, TyPtr, TyQPath};
use ast::{TyRptr, TyTup, TyU32, TyVec, UnUniq};
use ast::{TypeImplItem, TypeTraitItem, Typedef, ClosureKind};
use ast::{TypeImplItem, TypeTraitItem, Typedef,};
use ast::{UnnamedField, UnsafeBlock};
use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple};
use ast::{Visibility, WhereClause};
@ -1139,29 +1137,36 @@ impl<'a> Parser<'a> {
TyInfer
}
/// Parses an optional closure kind (`&:`, `&mut:`, or `:`).
pub fn parse_optional_closure_kind(&mut self) -> Option<ClosureKind> {
if self.check(&token::BinOp(token::And)) &&
self.look_ahead(1, |t| t.is_keyword(keywords::Mut)) &&
self.look_ahead(2, |t| *t == token::Colon) {
/// Parses an obsolete closure kind (`&:`, `&mut:`, or `:`).
pub fn parse_obsolete_closure_kind(&mut self) {
// let lo = self.span.lo;
if
self.check(&token::BinOp(token::And)) &&
self.look_ahead(1, |t| t.is_keyword(keywords::Mut)) &&
self.look_ahead(2, |t| *t == token::Colon)
{
self.bump();
self.bump();
self.bump();
return Some(FnMutClosureKind)
} else if
self.token == token::BinOp(token::And) &&
self.look_ahead(1, |t| *t == token::Colon)
{
self.bump();
self.bump();
return;
} else if
self.eat(&token::Colon)
{
/* nothing */
} else {
return;
}
if self.token == token::BinOp(token::And) &&
self.look_ahead(1, |t| *t == token::Colon) {
self.bump();
self.bump();
return Some(FnClosureKind)
}
if self.eat(&token::Colon) {
return Some(FnOnceClosureKind)
}
return None
// SNAP a45e117
// Enable these obsolete errors after snapshot:
// let span = mk_sp(lo, self.span.hi);
// self.obsolete(span, ObsoleteSyntax::ClosureKind);
}
pub fn parse_ty_bare_fn_or_ty_closure(&mut self, lifetime_defs: Vec<LifetimeDef>) -> Ty_ {
@ -3047,7 +3052,7 @@ impl<'a> Parser<'a> {
-> P<Expr>
{
let lo = self.span.lo;
let (decl, optional_closure_kind) = self.parse_fn_block_decl();
let decl = self.parse_fn_block_decl();
let body = self.parse_expr();
let fakeblock = P(ast::Block {
id: ast::DUMMY_NODE_ID,
@ -3060,7 +3065,7 @@ impl<'a> Parser<'a> {
self.mk_expr(
lo,
fakeblock.span.hi,
ExprClosure(capture_clause, optional_closure_kind, decl, fakeblock))
ExprClosure(capture_clause, decl, fakeblock))
}
pub fn parse_else_expr(&mut self) -> P<Expr> {
@ -4529,30 +4534,29 @@ impl<'a> Parser<'a> {
}
// parse the |arg, arg| header on a lambda
fn parse_fn_block_decl(&mut self) -> (P<FnDecl>, Option<ClosureKind>) {
let (optional_closure_kind, inputs_captures) = {
fn parse_fn_block_decl(&mut self) -> P<FnDecl> {
let inputs_captures = {
if self.eat(&token::OrOr) {
(None, Vec::new())
Vec::new()
} else {
self.expect(&token::BinOp(token::Or));
let optional_closure_kind =
self.parse_optional_closure_kind();
self.parse_obsolete_closure_kind();
let args = self.parse_seq_to_before_end(
&token::BinOp(token::Or),
seq_sep_trailing_allowed(token::Comma),
|p| p.parse_fn_block_arg()
);
self.bump();
(optional_closure_kind, args)
args
}
};
let output = self.parse_ret_ty();
(P(FnDecl {
P(FnDecl {
inputs: inputs_captures,
output: output,
variadic: false
}), optional_closure_kind)
})
}
/// Parses the `(arg, arg) -> return_type` header on a procedure.

View File

@ -11,11 +11,9 @@
pub use self::AnnNode::*;
use abi;
use ast::{self, FnClosureKind, FnMutClosureKind};
use ast::{FnOnceClosureKind};
use ast;
use ast::{MethodImplItem, RegionTyParamBound, TraitTyParamBound, TraitBoundModifier};
use ast::{RequiredMethod, ProvidedMethod, TypeImplItem, TypeTraitItem};
use ast::{ClosureKind};
use ast_util;
use owned_slice::OwnedSlice;
use attr::{AttrMetaMethods, AttributeMethods};
@ -350,7 +348,7 @@ pub fn method_to_string(p: &ast::Method) -> String {
}
pub fn fn_block_to_string(p: &ast::FnDecl) -> String {
$to_string(|s| s.print_fn_block_args(p, None))
$to_string(|s| s.print_fn_block_args(p))
}
pub fn path_to_string(p: &ast::Path) -> String {
@ -1747,10 +1745,10 @@ impl<'a> State<'a> {
}
try!(self.bclose_(expr.span, indent_unit));
}
ast::ExprClosure(capture_clause, opt_kind, ref decl, ref body) => {
ast::ExprClosure(capture_clause, ref decl, ref body) => {
try!(self.print_capture_clause(capture_clause));
try!(self.print_fn_block_args(&**decl, opt_kind));
try!(self.print_fn_block_args(&**decl));
try!(space(&mut self.s));
if !body.stmts.is_empty() || !body.expr.is_some() {
@ -2350,16 +2348,9 @@ impl<'a> State<'a> {
pub fn print_fn_block_args(
&mut self,
decl: &ast::FnDecl,
closure_kind: Option<ClosureKind>)
decl: &ast::FnDecl)
-> IoResult<()> {
try!(word(&mut self.s, "|"));
match closure_kind {
None => {}
Some(FnClosureKind) => try!(self.word_space("&:")),
Some(FnMutClosureKind) => try!(self.word_space("&mut:")),
Some(FnOnceClosureKind) => try!(self.word_space(":")),
}
try!(self.print_fn_args(decl, None));
try!(word(&mut self.s, "|"));

View File

@ -836,7 +836,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) {
visitor.visit_arm(arm)
}
}
ExprClosure(_, _, ref function_declaration, ref body) => {
ExprClosure(_, ref function_declaration, ref body) => {
visitor.visit_fn(FkFnBlock,
&**function_declaration,
&**body,