From fe2fcb39f43737089739c0190670231f18bc7a6a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 18 Nov 2014 15:00:23 -0500 Subject: [PATCH 1/4] Move closure checking into its own file. Shrink check/mod.rs! --- src/librustc/middle/typeck/check/closure.rs | 193 +++++++++++++++++++ src/librustc/middle/typeck/check/mod.rs | 203 ++------------------ 2 files changed, 211 insertions(+), 185 deletions(-) create mode 100644 src/librustc/middle/typeck/check/closure.rs diff --git a/src/librustc/middle/typeck/check/closure.rs b/src/librustc/middle/typeck/check/closure.rs new file mode 100644 index 00000000000..12b0a90bf7c --- /dev/null +++ b/src/librustc/middle/typeck/check/closure.rs @@ -0,0 +1,193 @@ +// 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. + +/*! + * Code for type-checking closure expressions. + */ + +use super::check_fn; +use super::Expectation; +use super::FnCtxt; + +use middle::ty; +use middle::typeck::astconv; +use middle::typeck::infer; +use middle::typeck::rscope::RegionScope; +use syntax::abi; +use syntax::ast; +use syntax::ast_util; +use util::ppaux::Repr; + +pub fn check_unboxed_closure(fcx: &FnCtxt, + expr: &ast::Expr, + kind: ast::UnboxedClosureKind, + decl: &ast::FnDecl, + body: &ast::Block) { + let expr_def_id = ast_util::local_def(expr.id); + + let mut fn_ty = astconv::ty_of_closure( + fcx, + ast::NormalFn, + ast::Many, + + // The `RegionTraitStore` and region_existential_bounds + // are lies, but we ignore them so it doesn't matter. + // + // FIXME(pcwalton): Refactor this API. + ty::region_existential_bound(ty::ReStatic), + ty::RegionTraitStore(ty::ReStatic, ast::MutImmutable), + + decl, + abi::RustCall, + None); + + let region = match fcx.infcx().anon_regions(expr.span, 1) { + Err(_) => { + fcx.ccx.tcx.sess.span_bug(expr.span, + "can't make anon regions here?!") + } + Ok(regions) => regions[0], + }; + + let closure_type = ty::mk_unboxed_closure(fcx.ccx.tcx, + expr_def_id, + region, + fcx.inh.param_env.free_substs.clone()); + + fcx.write_ty(expr.id, closure_type); + + check_fn(fcx.ccx, + ast::NormalFn, + expr.id, + &fn_ty.sig, + decl, + expr.id, + &*body, + fcx.inh); + + // Tuple up the arguments and insert the resulting function type into + // the `unboxed_closures` table. + fn_ty.sig.inputs = vec![ty::mk_tup(fcx.tcx(), fn_ty.sig.inputs)]; + + let kind = match kind { + ast::FnUnboxedClosureKind => ty::FnUnboxedClosureKind, + ast::FnMutUnboxedClosureKind => ty::FnMutUnboxedClosureKind, + ast::FnOnceUnboxedClosureKind => ty::FnOnceUnboxedClosureKind, + }; + + debug!("unboxed_closure for {} --> sig={} kind={}", + expr_def_id.repr(fcx.tcx()), + fn_ty.sig.repr(fcx.tcx()), + kind); + + let unboxed_closure = ty::UnboxedClosure { + closure_type: fn_ty, + kind: kind, + }; + + fcx.inh + .unboxed_closures + .borrow_mut() + .insert(expr_def_id, unboxed_closure); +} + +pub fn check_expr_fn<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, + expr: &ast::Expr, + store: ty::TraitStore, + decl: &ast::FnDecl, + body: &ast::Block, + expected: Expectation<'tcx>) { + let tcx = fcx.ccx.tcx; + + // Find the expected input/output types (if any). Substitute + // fresh bound regions for any bound regions we find in the + // expected types so as to avoid capture. + let expected_sty = expected.map_to_option(fcx, |x| Some((*x).clone())); + let (expected_sig, + expected_onceness, + expected_bounds) = { + match expected_sty { + Some(ty::ty_closure(ref cenv)) => { + let (sig, _) = + ty::replace_late_bound_regions( + tcx, + &cenv.sig, + |_, debruijn| fcx.inh.infcx.fresh_bound_region(debruijn)); + let onceness = match (&store, &cenv.store) { + // As the closure type and onceness go, only three + // combinations are legit: + // once closure + // many closure + // once proc + // If the actual and expected closure type disagree with + // each other, set expected onceness to be always Once or + // Many according to the actual type. Otherwise, it will + // yield either an illegal "many proc" or a less known + // "once closure" in the error message. + (&ty::UniqTraitStore, &ty::UniqTraitStore) | + (&ty::RegionTraitStore(..), &ty::RegionTraitStore(..)) => + cenv.onceness, + (&ty::UniqTraitStore, _) => ast::Once, + (&ty::RegionTraitStore(..), _) => ast::Many, + }; + (Some(sig), onceness, cenv.bounds) + } + _ => { + // Not an error! Means we're inferring the closure type + let (bounds, onceness) = match expr.node { + ast::ExprProc(..) => { + let mut bounds = ty::region_existential_bound(ty::ReStatic); + bounds.builtin_bounds.insert(ty::BoundSend); // FIXME + (bounds, ast::Once) + } + _ => { + let region = fcx.infcx().next_region_var( + infer::AddrOfRegion(expr.span)); + (ty::region_existential_bound(region), ast::Many) + } + }; + (None, onceness, bounds) + } + } + }; + + // construct the function type + let fn_ty = astconv::ty_of_closure(fcx, + ast::NormalFn, + expected_onceness, + expected_bounds, + store, + decl, + abi::Rust, + expected_sig); + let fty_sig = fn_ty.sig.clone(); + let fty = ty::mk_closure(tcx, fn_ty); + debug!("check_expr_fn fty={}", fcx.infcx().ty_to_string(fty)); + + fcx.write_ty(expr.id, fty); + + // If the closure is a stack closure and hasn't had some non-standard + // style inferred for it, then check it under its parent's style. + // Otherwise, use its own + let (inherited_style, inherited_style_id) = match store { + ty::RegionTraitStore(..) => (fcx.ps.borrow().fn_style, + fcx.ps.borrow().def), + ty::UniqTraitStore => (ast::NormalFn, expr.id) + }; + + check_fn(fcx.ccx, + inherited_style, + inherited_style_id, + &fty_sig, + &*decl, + expr.id, + &*body, + fcx.inh); +} diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 543eb44697c..02ac6379a56 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -147,6 +147,7 @@ pub mod regionck; pub mod demand; pub mod method; pub mod wf; +mod closure; /// Fields that are part of a `FnCtxt` which are inherited by /// closures defined within the function. For example: @@ -3519,174 +3520,6 @@ fn check_expr_with_unifier<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, }) } - fn check_unboxed_closure(fcx: &FnCtxt, - expr: &ast::Expr, - kind: ast::UnboxedClosureKind, - decl: &ast::FnDecl, - body: &ast::Block) { - let mut fn_ty = astconv::ty_of_closure( - fcx, - ast::NormalFn, - ast::Many, - - // The `RegionTraitStore` and region_existential_bounds - // are lies, but we ignore them so it doesn't matter. - // - // FIXME(pcwalton): Refactor this API. - ty::region_existential_bound(ty::ReStatic), - ty::RegionTraitStore(ty::ReStatic, ast::MutImmutable), - - decl, - abi::RustCall, - None); - - let region = match fcx.infcx().anon_regions(expr.span, 1) { - Err(_) => { - fcx.ccx.tcx.sess.span_bug(expr.span, - "can't make anon regions here?!") - } - Ok(regions) => regions[0], - }; - let closure_type = ty::mk_unboxed_closure(fcx.ccx.tcx, - local_def(expr.id), - region, - fcx.inh.param_env.free_substs.clone()); - fcx.write_ty(expr.id, closure_type); - - check_fn(fcx.ccx, - ast::NormalFn, - expr.id, - &fn_ty.sig, - decl, - expr.id, - &*body, - fcx.inh); - - // Tuple up the arguments and insert the resulting function type into - // the `unboxed_closures` table. - fn_ty.sig.inputs = vec![ty::mk_tup(fcx.tcx(), fn_ty.sig.inputs)]; - - let kind = match kind { - ast::FnUnboxedClosureKind => ty::FnUnboxedClosureKind, - ast::FnMutUnboxedClosureKind => ty::FnMutUnboxedClosureKind, - ast::FnOnceUnboxedClosureKind => ty::FnOnceUnboxedClosureKind, - }; - - debug!("unboxed_closure for {} --> sig={} kind={}", - local_def(expr.id).repr(fcx.tcx()), - fn_ty.sig.repr(fcx.tcx()), - kind); - - let unboxed_closure = ty::UnboxedClosure { - closure_type: fn_ty, - kind: kind, - }; - - fcx.inh - .unboxed_closures - .borrow_mut() - .insert(local_def(expr.id), unboxed_closure); - } - - fn check_expr_fn<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, - expr: &ast::Expr, - store: ty::TraitStore, - decl: &ast::FnDecl, - body: &ast::Block, - expected: Expectation<'tcx>) { - let tcx = fcx.ccx.tcx; - - debug!("check_expr_fn(expr={}, expected={})", - expr.repr(tcx), - expected.repr(tcx)); - - // Find the expected input/output types (if any). Substitute - // fresh bound regions for any bound regions we find in the - // expected types so as to avoid capture. - let expected_sty = expected.map_to_option(fcx, |x| Some((*x).clone())); - let (expected_sig, - expected_onceness, - expected_bounds) = { - match expected_sty { - Some(ty::ty_closure(ref cenv)) => { - let (sig, _) = - replace_late_bound_regions( - tcx, - &cenv.sig, - |_, debruijn| fcx.inh.infcx.fresh_bound_region(debruijn)); - let onceness = match (&store, &cenv.store) { - // As the closure type and onceness go, only three - // combinations are legit: - // once closure - // many closure - // once proc - // If the actual and expected closure type disagree with - // each other, set expected onceness to be always Once or - // Many according to the actual type. Otherwise, it will - // yield either an illegal "many proc" or a less known - // "once closure" in the error message. - (&ty::UniqTraitStore, &ty::UniqTraitStore) | - (&ty::RegionTraitStore(..), &ty::RegionTraitStore(..)) => - cenv.onceness, - (&ty::UniqTraitStore, _) => ast::Once, - (&ty::RegionTraitStore(..), _) => ast::Many, - }; - (Some(sig), onceness, cenv.bounds) - } - _ => { - // Not an error! Means we're inferring the closure type - let (bounds, onceness) = match expr.node { - ast::ExprProc(..) => { - let mut bounds = ty::region_existential_bound(ty::ReStatic); - bounds.builtin_bounds.insert(ty::BoundSend); // FIXME - (bounds, ast::Once) - } - _ => { - let region = fcx.infcx().next_region_var( - infer::AddrOfRegion(expr.span)); - (ty::region_existential_bound(region), ast::Many) - } - }; - (None, onceness, bounds) - } - } - }; - - // construct the function type - let fn_ty = astconv::ty_of_closure(fcx, - ast::NormalFn, - expected_onceness, - expected_bounds, - store, - decl, - abi::Rust, - expected_sig); - let fty_sig = fn_ty.sig.clone(); - let fty = ty::mk_closure(tcx, fn_ty); - debug!("check_expr_fn fty={}", fcx.infcx().ty_to_string(fty)); - - fcx.write_ty(expr.id, fty); - - // If the closure is a stack closure and hasn't had some non-standard - // style inferred for it, then check it under its parent's style. - // Otherwise, use its own - let (inherited_style, inherited_style_id) = match store { - ty::RegionTraitStore(..) => (fcx.ps.borrow().fn_style, - fcx.ps.borrow().def), - ty::UniqTraitStore => (ast::NormalFn, expr.id) - }; - - check_fn(fcx.ccx, - inherited_style, - inherited_style_id, - &fty_sig, - &*decl, - expr.id, - &*body, - fcx.inh); - } - - // Check field access expressions fn check_field(fcx: &FnCtxt, expr: &ast::Expr, @@ -4320,27 +4153,27 @@ fn check_expr_with_unifier<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, fcx.infcx(), expr.span, &None); - check_expr_fn(fcx, - expr, - ty::RegionTraitStore(region, ast::MutMutable), - &**decl, - &**body, - expected); + closure::check_expr_fn(fcx, + expr, + ty::RegionTraitStore(region, ast::MutMutable), + &**decl, + &**body, + expected); } ast::ExprUnboxedFn(_, kind, ref decl, ref body) => { - check_unboxed_closure(fcx, - expr, - kind, - &**decl, - &**body); + closure::check_unboxed_closure(fcx, + expr, + kind, + &**decl, + &**body); } ast::ExprProc(ref decl, ref body) => { - check_expr_fn(fcx, - expr, - ty::UniqTraitStore, - &**decl, - &**body, - expected); + closure::check_expr_fn(fcx, + expr, + ty::UniqTraitStore, + &**decl, + &**body, + expected); } ast::ExprBlock(ref b) => { check_block_with_expected(fcx, &**b, expected); From 8e44688889b4532919ba5280b9c3fd15d2b49402 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 18 Nov 2014 16:13:24 -0500 Subject: [PATCH 2/4] Deduce the argument types based on the expected type, trawling through the fulfillment contect if necessary. --- src/librustc/middle/traits/fulfill.rs | 4 + src/librustc/middle/typeck/check/closure.rs | 133 ++++++++++++++++-- src/librustc/middle/typeck/check/mod.rs | 7 +- .../regions-escape-unboxed-closure.rs | 19 +++ ...nfer-argument-types-two-region-pointers.rs | 29 ++++ ...nfer-argument-types-from-expected-bound.rs | 24 ++++ ...rgument-types-from-expected-object-type.rs | 20 +++ ...-with-bound-regions-from-expected-bound.rs | 24 ++++ 8 files changed, 249 insertions(+), 11 deletions(-) create mode 100644 src/test/compile-fail/regions-escape-unboxed-closure.rs create mode 100644 src/test/compile-fail/unboxed-closures-infer-argument-types-two-region-pointers.rs create mode 100644 src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-bound.rs create mode 100644 src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-object-type.rs create mode 100644 src/test/run-pass/unboxed-closures-infer-argument-types-with-bound-regions-from-expected-bound.rs diff --git a/src/librustc/middle/traits/fulfill.rs b/src/librustc/middle/traits/fulfill.rs index 5b8edacb28d..62382ac386f 100644 --- a/src/librustc/middle/traits/fulfill.rs +++ b/src/librustc/middle/traits/fulfill.rs @@ -109,6 +109,10 @@ impl<'tcx> FulfillmentContext<'tcx> { self.select(&mut selcx, false) } + pub fn pending_trait_obligations(&self) -> &[Obligation<'tcx>] { + self.trait_obligations[] + } + fn select<'a>(&mut self, selcx: &mut SelectionContext<'a, 'tcx>, only_new_obligations: bool) diff --git a/src/librustc/middle/typeck/check/closure.rs b/src/librustc/middle/typeck/check/closure.rs index 12b0a90bf7c..6e6e2f1eee5 100644 --- a/src/librustc/middle/typeck/check/closure.rs +++ b/src/librustc/middle/typeck/check/closure.rs @@ -13,10 +13,11 @@ */ use super::check_fn; -use super::Expectation; +use super::{Expectation, ExpectCastableToType, ExpectHasType, NoExpectation}; use super::FnCtxt; -use middle::ty; +use middle::subst; +use middle::ty::{mod, Ty}; use middle::typeck::astconv; use middle::typeck::infer; use middle::typeck::rscope::RegionScope; @@ -25,13 +26,40 @@ use syntax::ast; use syntax::ast_util; use util::ppaux::Repr; -pub fn check_unboxed_closure(fcx: &FnCtxt, - expr: &ast::Expr, - kind: ast::UnboxedClosureKind, - decl: &ast::FnDecl, - body: &ast::Block) { +pub fn check_unboxed_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, + expr: &ast::Expr, + kind: ast::UnboxedClosureKind, + decl: &ast::FnDecl, + body: &ast::Block, + expected: Expectation<'tcx>) { let expr_def_id = ast_util::local_def(expr.id); + let expected_sig_and_kind = match expected.resolve(fcx) { + NoExpectation => None, + ExpectCastableToType(t) | ExpectHasType(t) => { + deduce_unboxed_closure_expectations_from_expected_type(fcx, t) + } + }; + + let (expected_sig, expected_kind) = match expected_sig_and_kind { + None => (None, None), + Some((sig, kind)) => { + // Avoid accidental capture of bound regions by renaming + // them to fresh names, basically. + let sig = + ty::replace_late_bound_regions( + fcx.tcx(), + &sig, + |_, debruijn| fcx.inh.infcx.fresh_bound_region(debruijn)).0; + (Some(sig), Some(kind)) + } + }; + + debug!("check_unboxed_closure expected={} expected_sig={} expected_kind={}", + expected.repr(fcx.tcx()), + expected_sig.repr(fcx.tcx()), + expected_kind); + let mut fn_ty = astconv::ty_of_closure( fcx, ast::NormalFn, @@ -46,7 +74,7 @@ pub fn check_unboxed_closure(fcx: &FnCtxt, decl, abi::RustCall, - None); + expected_sig); let region = match fcx.infcx().anon_regions(expr.span, 1) { Err(_) => { @@ -98,6 +126,95 @@ pub fn check_unboxed_closure(fcx: &FnCtxt, .insert(expr_def_id, unboxed_closure); } +fn deduce_unboxed_closure_expectations_from_expected_type<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, + expected_ty: Ty<'tcx>) + -> Option<(ty::FnSig<'tcx>, + ty::UnboxedClosureKind)> +{ + match expected_ty.sty { + ty::ty_trait(ref object_type) => { + deduce_unboxed_closure_expectations_from_trait_ref(fcx, &object_type.principal) + } + ty::ty_infer(ty::TyVar(vid)) => { + deduce_unboxed_closure_expectations_from_obligations(fcx, vid) + } + _ => { + None + } + } +} + +fn deduce_unboxed_closure_expectations_from_trait_ref<'a,'tcx>( + fcx: &FnCtxt<'a,'tcx>, + trait_ref: &ty::TraitRef<'tcx>) + -> Option<(ty::FnSig<'tcx>, ty::UnboxedClosureKind)> +{ + let tcx = fcx.tcx(); + + debug!("deduce_unboxed_closure_expectations_from_object_type({})", + trait_ref.repr(tcx)); + + let def_id_kinds = [ + (tcx.lang_items.fn_trait(), ty::FnUnboxedClosureKind), + (tcx.lang_items.fn_mut_trait(), ty::FnMutUnboxedClosureKind), + (tcx.lang_items.fn_once_trait(), ty::FnOnceUnboxedClosureKind), + ]; + + for &(def_id, kind) in def_id_kinds.iter() { + if Some(trait_ref.def_id) == def_id { + debug!("found object type {}", kind); + + let arg_param_ty = *trait_ref.substs.types.get(subst::TypeSpace, 0); + let arg_param_ty = fcx.infcx().resolve_type_vars_if_possible(arg_param_ty); + debug!("arg_param_ty {}", arg_param_ty.repr(tcx)); + + let input_tys = match arg_param_ty.sty { + ty::ty_tup(ref tys) => { (*tys).clone() } + _ => { continue; } + }; + debug!("input_tys {}", input_tys.repr(tcx)); + + let ret_param_ty = *trait_ref.substs.types.get(subst::TypeSpace, 1); + let ret_param_ty = fcx.infcx().resolve_type_vars_if_possible(ret_param_ty); + debug!("ret_param_ty {}", ret_param_ty.repr(tcx)); + + let fn_sig = ty::FnSig { + inputs: input_tys, + output: ty::FnConverging(ret_param_ty), + variadic: false + }; + debug!("fn_sig {}", fn_sig.repr(tcx)); + + return Some((fn_sig, kind)); + } + } + + None +} + +fn deduce_unboxed_closure_expectations_from_obligations<'a,'tcx>( + fcx: &FnCtxt<'a,'tcx>, + expected_vid: ty::TyVid) + -> Option<(ty::FnSig<'tcx>, ty::UnboxedClosureKind)> +{ + // Here `expected_ty` is known to be a type inference variable. + for obligation in fcx.inh.fulfillment_cx.borrow().pending_trait_obligations().iter() { + let obligation_self_ty = fcx.infcx().shallow_resolve(obligation.self_ty()); + match obligation_self_ty.sty { + ty::ty_infer(ty::TyVar(v)) if expected_vid == v => { } + _ => { continue; } + } + + match deduce_unboxed_closure_expectations_from_trait_ref(fcx, &*obligation.trait_ref) { + Some(e) => { return Some(e); } + None => { } + } + } + + None +} + + pub fn check_expr_fn<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, expr: &ast::Expr, store: ty::TraitStore, diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 02ac6379a56..28648d6d4e2 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -78,7 +78,7 @@ type parameter). pub use self::LvaluePreference::*; pub use self::DerefArgs::*; -use self::Expectation::*; +pub use self::Expectation::*; use self::IsBinopAssignment::*; use self::TupleArgumentsFlag::*; @@ -97,7 +97,7 @@ use middle::ty::{FnSig, VariantInfo}; use middle::ty::{Polytype}; use middle::ty::{Disr, ParamTy, ParameterEnvironment}; use middle::ty::{mod, Ty}; -use middle::ty::{replace_late_bound_regions, liberate_late_bound_regions}; +use middle::ty::liberate_late_bound_regions; use middle::ty_fold::TypeFolder; use middle::typeck::astconv::AstConv; use middle::typeck::astconv::{ast_region_to_region, ast_ty_to_ty}; @@ -4165,7 +4165,8 @@ fn check_expr_with_unifier<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, expr, kind, &**decl, - &**body); + &**body, + expected); } ast::ExprProc(ref decl, ref body) => { closure::check_expr_fn(fcx, diff --git a/src/test/compile-fail/regions-escape-unboxed-closure.rs b/src/test/compile-fail/regions-escape-unboxed-closure.rs new file mode 100644 index 00000000000..70f0d61b5ee --- /dev/null +++ b/src/test/compile-fail/regions-escape-unboxed-closure.rs @@ -0,0 +1,19 @@ +// Copyright 2012 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_closures)] + +fn with_int(f: &mut FnMut(&int)) { +} + +fn main() { + let mut x: Option<&int> = None; + with_int(&mut |&mut: y| x = Some(y)); //~ ERROR cannot infer +} diff --git a/src/test/compile-fail/unboxed-closures-infer-argument-types-two-region-pointers.rs b/src/test/compile-fail/unboxed-closures-infer-argument-types-two-region-pointers.rs new file mode 100644 index 00000000000..72109b22957 --- /dev/null +++ b/src/test/compile-fail/unboxed-closures-infer-argument-types-two-region-pointers.rs @@ -0,0 +1,29 @@ +// 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. + +// That a closure whose expected argument types include two distinct +// bound regions. + +#![feature(unboxed_closures)] + +use std::cell::Cell; + +fn doit(val: T, f: &F) + where F : Fn(&Cell<&T>, &T) +{ + let x = Cell::new(&val); + f.call((&x,&val)) +} + +pub fn main() { + doit(0i, &|&: x, y| { + x.set(y); //~ ERROR cannot infer + }); +} diff --git a/src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-bound.rs b/src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-bound.rs new file mode 100644 index 00000000000..465c324122a --- /dev/null +++ b/src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-bound.rs @@ -0,0 +1,24 @@ +// 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. + +// Test that we are able to infer that the type of `x` is `int` based +// on the expected type from the object. + +#![feature(unboxed_closures)] + +fn doit(val: T, f: &F) + where F : Fn(T) +{ + f.call((val,)) +} + +pub fn main() { + doit(0i, &|&: x /*: int*/ | { x.to_int(); }); +} diff --git a/src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-object-type.rs b/src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-object-type.rs new file mode 100644 index 00000000000..440292d202e --- /dev/null +++ b/src/test/run-pass/unboxed-closures-infer-argument-types-from-expected-object-type.rs @@ -0,0 +1,20 @@ +// 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. + +// Test that we are able to infer that the type of `x` is `int` based +// on the expected type from the object. + +#![feature(unboxed_closures)] + +fn doit(val: T, f: &Fn(T)) { f.call((val,)) } + +pub fn main() { + doit(0i, &|&: x /*: int*/ | { x.to_int(); }); +} diff --git a/src/test/run-pass/unboxed-closures-infer-argument-types-with-bound-regions-from-expected-bound.rs b/src/test/run-pass/unboxed-closures-infer-argument-types-with-bound-regions-from-expected-bound.rs new file mode 100644 index 00000000000..b279eb5fbba --- /dev/null +++ b/src/test/run-pass/unboxed-closures-infer-argument-types-with-bound-regions-from-expected-bound.rs @@ -0,0 +1,24 @@ +// 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. + +// Test that we are able to infer that the type of `x` is `int` based +// on the expected type from the object. + +#![feature(unboxed_closures)] + +fn doit(val: T, f: &F) + where F : Fn(&T) +{ + f.call((&val,)) +} + +pub fn main() { + doit(0i, &|&: x /*: int*/ | { x.to_int(); }); +} From 3e2929d3623dd4c4d601a31e55e9d36dba7afe88 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 19 Nov 2014 11:18:17 -0500 Subject: [PATCH 3/4] Merge the ExprFnBlock and ExprUnboxedClosure into one ExprClosure with an optional unboxed closure kind. --- src/librustc/middle/borrowck/mod.rs | 9 ++-- src/librustc/middle/cfg/construct.rs | 3 +- src/librustc/middle/check_loop.rs | 5 +- src/librustc/middle/expr_use_visitor.rs | 3 +- src/librustc/middle/liveness.rs | 11 ++-- src/librustc/middle/mem_categorization.rs | 7 ++- src/librustc/middle/resolve.rs | 13 ++--- src/librustc/middle/ty.rs | 3 +- src/librustc/middle/typeck/astconv.rs | 2 +- src/librustc/middle/typeck/check/closure.rs | 50 ++++++++++++++----- src/librustc/middle/typeck/check/mod.rs | 37 ++++---------- src/librustc/middle/typeck/check/regionck.rs | 3 +- src/librustc/middle/typeck/check/writeback.rs | 5 +- src/librustc_back/svh.rs | 6 +-- src/librustc_trans/save/mod.rs | 2 +- src/librustc_trans/trans/base.rs | 5 +- src/librustc_trans/trans/debuginfo.rs | 8 ++- src/librustc_trans/trans/expr.rs | 26 ++++++---- src/libsyntax/ast.rs | 3 +- src/libsyntax/ast_map/blocks.rs | 6 +-- src/libsyntax/ext/build.rs | 4 +- src/libsyntax/ext/expand.rs | 7 +-- src/libsyntax/feature_gate.rs | 2 +- src/libsyntax/fold.rs | 11 ++-- src/libsyntax/parse/parser.rs | 26 +++------- src/libsyntax/print/pprust.rs | 47 ++--------------- src/libsyntax/visit.rs | 9 +--- 27 files changed, 126 insertions(+), 187 deletions(-) diff --git a/src/librustc/middle/borrowck/mod.rs b/src/librustc/middle/borrowck/mod.rs index fbb0a87cf3f..d0118514d43 100644 --- a/src/librustc/middle/borrowck/mod.rs +++ b/src/librustc/middle/borrowck/mod.rs @@ -288,9 +288,12 @@ pub fn closure_to_block(closure_id: ast::NodeId, match tcx.map.get(closure_id) { ast_map::NodeExpr(expr) => match expr.node { ast::ExprProc(_, ref block) | - ast::ExprFnBlock(_, _, ref block) | - ast::ExprUnboxedFn(_, _, _, ref block) => { block.id } - _ => panic!("encountered non-closure id: {}", closure_id) + ast::ExprClosure(_, _, _, ref block) => { + block.id + } + _ => { + panic!("encountered non-closure id: {}", closure_id) + } }, _ => panic!("encountered non-expr id: {}", closure_id) } diff --git a/src/librustc/middle/cfg/construct.rs b/src/librustc/middle/cfg/construct.rs index e84e70e2cd9..04c8a4fe594 100644 --- a/src/librustc/middle/cfg/construct.rs +++ b/src/librustc/middle/cfg/construct.rs @@ -496,9 +496,8 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { } ast::ExprMac(..) | - ast::ExprFnBlock(..) | + ast::ExprClosure(..) | ast::ExprProc(..) | - ast::ExprUnboxedFn(..) | ast::ExprLit(..) | ast::ExprPath(..) => { self.straightline(expr, pred, None::.iter()) diff --git a/src/librustc/middle/check_loop.rs b/src/librustc/middle/check_loop.rs index 01ec9bbc13d..36742df9850 100644 --- a/src/librustc/middle/check_loop.rs +++ b/src/librustc/middle/check_loop.rs @@ -48,9 +48,8 @@ impl<'a, 'v> Visitor<'v> for CheckLoopVisitor<'a> { self.visit_expr(&**e); self.with_context(Loop, |v| v.visit_block(&**b)); } - ast::ExprFnBlock(_, _, ref b) | - ast::ExprProc(_, ref b) | - ast::ExprUnboxedFn(_, _, _, ref b) => { + ast::ExprClosure(_, _, _, ref b) | + ast::ExprProc(_, ref b) => { self.with_context(Closure, |v| v.visit_block(&**b)); } ast::ExprBreak(_) => self.require_loop("break", e.span), diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index f4c22c57163..08605f76e02 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -496,8 +496,7 @@ impl<'d,'t,'tcx,TYPER:mc::Typer<'tcx>> ExprUseVisitor<'d,'t,'tcx,TYPER> { self.consume_expr(&**count); } - ast::ExprFnBlock(..) | - ast::ExprUnboxedFn(..) | + ast::ExprClosure(..) | ast::ExprProc(..) => { self.walk_captures(expr) } diff --git a/src/librustc/middle/liveness.rs b/src/librustc/middle/liveness.rs index 82edfdf146e..7d13d2e5f94 100644 --- a/src/librustc/middle/liveness.rs +++ b/src/librustc/middle/liveness.rs @@ -458,7 +458,7 @@ fn visit_expr(ir: &mut IrMaps, expr: &Expr) { } visit::walk_expr(ir, expr); } - ast::ExprFnBlock(..) | ast::ExprProc(..) | ast::ExprUnboxedFn(..) => { + ast::ExprClosure(..) | ast::ExprProc(..) => { // Interesting control flow (for loops can contain labeled // breaks or continues) ir.add_live_node_for_node(expr.id, ExprNode(expr.span)); @@ -975,10 +975,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { self.propagate_through_expr(&**e, succ) } - ast::ExprFnBlock(_, _, ref blk) | - ast::ExprProc(_, ref blk) | - ast::ExprUnboxedFn(_, _, _, ref blk) => { - debug!("{} is an ExprFnBlock, ExprProc, or ExprUnboxedFn", + ast::ExprClosure(_, _, _, ref blk) | + ast::ExprProc(_, ref blk) => { + debug!("{} is an ExprClosure or ExprProc", expr_to_string(expr)); /* @@ -1495,7 +1494,7 @@ fn check_expr(this: &mut Liveness, expr: &Expr) { ast::ExprBreak(..) | ast::ExprAgain(..) | ast::ExprLit(_) | ast::ExprBlock(..) | ast::ExprMac(..) | ast::ExprAddrOf(..) | ast::ExprStruct(..) | ast::ExprRepeat(..) | ast::ExprParen(..) | - ast::ExprFnBlock(..) | ast::ExprProc(..) | ast::ExprUnboxedFn(..) | + ast::ExprClosure(..) | ast::ExprProc(..) | ast::ExprPath(..) | ast::ExprBox(..) | ast::ExprSlice(..) => { visit::walk_expr(this, expr); } diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 93c2e8f0d99..3166f6b1495 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -520,8 +520,8 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { ast::ExprAddrOf(..) | ast::ExprCall(..) | ast::ExprAssign(..) | ast::ExprAssignOp(..) | - ast::ExprFnBlock(..) | ast::ExprProc(..) | - ast::ExprUnboxedFn(..) | ast::ExprRet(..) | + ast::ExprClosure(..) | ast::ExprProc(..) | + ast::ExprRet(..) | ast::ExprUnary(..) | ast::ExprSlice(..) | ast::ExprMethodCall(..) | ast::ExprCast(..) | ast::ExprVec(..) | ast::ExprTup(..) | ast::ExprIf(..) | @@ -693,9 +693,8 @@ impl<'t,'tcx,TYPER:Typer<'tcx>> MemCategorizationContext<'t,TYPER> { }; match fn_expr.node { - ast::ExprFnBlock(_, _, ref body) | ast::ExprProc(_, ref body) | - ast::ExprUnboxedFn(_, _, _, ref body) => body.id, + ast::ExprClosure(_, _, _, ref body) => body.id, _ => unreachable!() } }; diff --git a/src/librustc/middle/resolve.rs b/src/librustc/middle/resolve.rs index baf53cc34ba..302dcf04e12 100644 --- a/src/librustc/middle/resolve.rs +++ b/src/librustc/middle/resolve.rs @@ -50,8 +50,8 @@ use util::nodemap::{NodeMap, NodeSet, DefIdSet, FnvHashMap}; use syntax::ast::{Arm, BindByRef, BindByValue, BindingMode, Block, Crate, CrateNum}; use syntax::ast::{DeclItem, DefId, Expr, ExprAgain, ExprBreak, ExprField}; -use syntax::ast::{ExprFnBlock, ExprForLoop, ExprLoop, ExprWhile, ExprMethodCall}; -use syntax::ast::{ExprPath, ExprProc, ExprStruct, ExprUnboxedFn, FnDecl}; +use syntax::ast::{ExprClosure, ExprForLoop, ExprLoop, ExprWhile, ExprMethodCall}; +use syntax::ast::{ExprPath, ExprProc, ExprStruct, FnDecl}; use syntax::ast::{ForeignItem, ForeignItemFn, ForeignItemStatic, Generics}; use syntax::ast::{Ident, ImplItem, Item, ItemEnum, ItemFn, ItemForeignMod}; use syntax::ast::{ItemImpl, ItemMac, ItemMod, ItemStatic, ItemStruct}; @@ -5903,24 +5903,19 @@ impl<'a> Resolver<'a> { visit::walk_expr(self, expr); } - ExprFnBlock(capture_clause, ref fn_decl, ref block) => { + ExprClosure(capture_clause, _, ref fn_decl, ref block) => { self.capture_mode_map.insert(expr.id, capture_clause); self.resolve_function(ClosureRibKind(expr.id, ast::DUMMY_NODE_ID), Some(&**fn_decl), NoTypeParameters, &**block); } + ExprProc(ref fn_decl, ref block) => { self.capture_mode_map.insert(expr.id, ast::CaptureByValue); self.resolve_function(ClosureRibKind(expr.id, block.id), Some(&**fn_decl), NoTypeParameters, &**block); } - ExprUnboxedFn(capture_clause, _, ref fn_decl, ref block) => { - self.capture_mode_map.insert(expr.id, capture_clause); - self.resolve_function(ClosureRibKind(expr.id, block.id), - Some(&**fn_decl), NoTypeParameters, - &**block); - } ExprStruct(ref path, _, _) => { // Resolve the path to the structure it goes to. We don't diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index bf4260c4631..d3b7e69b854 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -3922,9 +3922,8 @@ pub fn expr_kind(tcx: &ctxt, expr: &ast::Expr) -> ExprKind { ast::ExprTup(..) | ast::ExprIf(..) | ast::ExprMatch(..) | - ast::ExprFnBlock(..) | + ast::ExprClosure(..) | ast::ExprProc(..) | - ast::ExprUnboxedFn(..) | ast::ExprBlock(..) | ast::ExprRepeat(..) | ast::ExprVec(..) => { diff --git a/src/librustc/middle/typeck/astconv.rs b/src/librustc/middle/typeck/astconv.rs index ea652bc7e65..5fff355bafa 100644 --- a/src/librustc/middle/typeck/astconv.rs +++ b/src/librustc/middle/typeck/astconv.rs @@ -1047,7 +1047,7 @@ pub fn ast_ty_to_ty<'tcx, AC: AstConv<'tcx>, RS: RegionScope>( } ast::TyInfer => { // TyInfer also appears as the type of arguments or return - // values in a ExprFnBlock, ExprProc, or ExprUnboxedFn, or as + // values in a ExprClosure or ExprProc, or as // the type of local variables. Both of these cases are // handled specially and will not descend into this routine. this.ty_infer(ast_ty.span) diff --git a/src/librustc/middle/typeck/check/closure.rs b/src/librustc/middle/typeck/check/closure.rs index 6e6e2f1eee5..220c2f70d17 100644 --- a/src/librustc/middle/typeck/check/closure.rs +++ b/src/librustc/middle/typeck/check/closure.rs @@ -26,12 +26,38 @@ use syntax::ast; use syntax::ast_util; use util::ppaux::Repr; -pub fn check_unboxed_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, - expr: &ast::Expr, - kind: ast::UnboxedClosureKind, - decl: &ast::FnDecl, - body: &ast::Block, - expected: Expectation<'tcx>) { +pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, + expr: &ast::Expr, + opt_kind: Option, + decl: &ast::FnDecl, + body: &ast::Block, + expected: Expectation<'tcx>) { + match opt_kind { + None => { // old-school boxed closure + let region = astconv::opt_ast_region_to_region(fcx, + fcx.infcx(), + expr.span, + &None); + check_boxed_closure(fcx, + expr, + ty::RegionTraitStore(region, ast::MutMutable), + decl, + body, + expected); + } + + Some(kind) => { + check_unboxed_closure(fcx, expr, kind, decl, body, expected) + } + } +} + +fn check_unboxed_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, + expr: &ast::Expr, + kind: ast::UnboxedClosureKind, + decl: &ast::FnDecl, + body: &ast::Block, + expected: Expectation<'tcx>) { let expr_def_id = ast_util::local_def(expr.id); let expected_sig_and_kind = match expected.resolve(fcx) { @@ -215,12 +241,12 @@ fn deduce_unboxed_closure_expectations_from_obligations<'a,'tcx>( } -pub fn check_expr_fn<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, - expr: &ast::Expr, - store: ty::TraitStore, - decl: &ast::FnDecl, - body: &ast::Block, - expected: Expectation<'tcx>) { +pub fn check_boxed_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, + expr: &ast::Expr, + store: ty::TraitStore, + decl: &ast::FnDecl, + body: &ast::Block, + expected: Expectation<'tcx>) { let tcx = fcx.ccx.tcx; // Find the expected input/output types (if any). Substitute diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index 28648d6d4e2..65424567be4 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -2823,9 +2823,7 @@ fn check_argument_types<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, }; for (i, arg) in args.iter().take(t).enumerate() { let is_block = match arg.node { - ast::ExprFnBlock(..) | - ast::ExprProc(..) | - ast::ExprUnboxedFn(..) => true, + ast::ExprClosure(..) | ast::ExprProc(..) => true, _ => false }; @@ -4148,33 +4146,16 @@ fn check_expr_with_unifier<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>, ast::ExprMatch(ref discrim, ref arms, _) => { _match::check_match(fcx, expr, &**discrim, arms.as_slice()); } - ast::ExprFnBlock(_, ref decl, ref body) => { - let region = astconv::opt_ast_region_to_region(fcx, - fcx.infcx(), - expr.span, - &None); - closure::check_expr_fn(fcx, - expr, - ty::RegionTraitStore(region, ast::MutMutable), - &**decl, - &**body, - expected); - } - ast::ExprUnboxedFn(_, kind, ref decl, ref body) => { - closure::check_unboxed_closure(fcx, - expr, - kind, - &**decl, - &**body, - expected); + ast::ExprClosure(_, opt_kind, ref decl, ref body) => { + closure::check_expr_closure(fcx, expr, opt_kind, &**decl, &**body, expected); } ast::ExprProc(ref decl, ref body) => { - closure::check_expr_fn(fcx, - expr, - ty::UniqTraitStore, - &**decl, - &**body, - expected); + closure::check_boxed_closure(fcx, + expr, + ty::UniqTraitStore, + &**decl, + &**body, + expected); } ast::ExprBlock(ref b) => { check_block_with_expected(fcx, &**b, expected); diff --git a/src/librustc/middle/typeck/check/regionck.rs b/src/librustc/middle/typeck/check/regionck.rs index 44c11318038..0a5af8baeac 100644 --- a/src/librustc/middle/typeck/check/regionck.rs +++ b/src/librustc/middle/typeck/check/regionck.rs @@ -742,9 +742,8 @@ fn visit_expr(rcx: &mut Rcx, expr: &ast::Expr) { visit::walk_expr(rcx, expr); } - ast::ExprFnBlock(_, _, ref body) | ast::ExprProc(_, ref body) | - ast::ExprUnboxedFn(_, _, _, ref body) => { + ast::ExprClosure(_, _, _, ref body) => { check_expr_fn_block(rcx, expr, &**body); } diff --git a/src/librustc/middle/typeck/check/writeback.rs b/src/librustc/middle/typeck/check/writeback.rs index 76d9ed15e51..23af30b44d9 100644 --- a/src/librustc/middle/typeck/check/writeback.rs +++ b/src/librustc/middle/typeck/check/writeback.rs @@ -122,9 +122,8 @@ impl<'cx, 'tcx, 'v> Visitor<'v> for WritebackCx<'cx, 'tcx> { MethodCall::expr(e.id)); match e.node { - ast::ExprFnBlock(_, ref decl, _) | - ast::ExprProc(ref decl, _) | - ast::ExprUnboxedFn(_, _, ref decl, _) => { + ast::ExprClosure(_, _, ref decl, _) | + ast::ExprProc(ref decl, _) => { for input in decl.inputs.iter() { let _ = self.visit_node_id(ResolvingExpr(e.span), input.id); diff --git a/src/librustc_back/svh.rs b/src/librustc_back/svh.rs index c7a7888c1cd..4f3daa91bbc 100644 --- a/src/librustc_back/svh.rs +++ b/src/librustc_back/svh.rs @@ -241,8 +241,7 @@ mod svh_visitor { SawExprIf, SawExprWhile, SawExprMatch, - SawExprFnBlock, - SawExprUnboxedFn, + SawExprClosure, SawExprProc, SawExprBlock, SawExprAssign, @@ -274,8 +273,7 @@ mod svh_visitor { ExprWhile(..) => SawExprWhile, ExprLoop(_, id) => SawExprLoop(id.map(content)), ExprMatch(..) => SawExprMatch, - ExprFnBlock(..) => SawExprFnBlock, - ExprUnboxedFn(..) => SawExprUnboxedFn, + ExprClosure(..) => SawExprClosure, ExprProc(..) => SawExprProc, ExprBlock(..) => SawExprBlock, ExprAssign(..) => SawExprAssign, diff --git a/src/librustc_trans/save/mod.rs b/src/librustc_trans/save/mod.rs index 2591051e9b9..67ed95f83fd 100644 --- a/src/librustc_trans/save/mod.rs +++ b/src/librustc_trans/save/mod.rs @@ -1345,7 +1345,7 @@ impl<'l, 'tcx, 'v> Visitor<'v> for DxrVisitor<'l, 'tcx> { "Expected struct type, but not ty_struct"), } }, - ast::ExprFnBlock(_, ref decl, ref body) => { + ast::ExprClosure(_, _, ref decl, ref body) => { if generated_code(body.span) { return } diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index b3f64e3fd79..30cdda9366b 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -1385,9 +1385,8 @@ fn has_nested_returns(tcx: &ty::ctxt, id: ast::NodeId) -> bool { } Some(ast_map::NodeExpr(e)) => { match e.node { - ast::ExprFnBlock(_, _, ref blk) | - ast::ExprProc(_, ref blk) | - ast::ExprUnboxedFn(_, _, _, ref blk) => { + ast::ExprClosure(_, _, _, ref blk) | + ast::ExprProc(_, ref blk) => { let mut explicit = CheckForNestedReturnsVisitor::explicit(); let mut implicit = CheckForNestedReturnsVisitor::implicit(); visit::walk_expr(&mut explicit, e); diff --git a/src/librustc_trans/trans/debuginfo.rs b/src/librustc_trans/trans/debuginfo.rs index 7b3f619f41f..075b6b0dd6e 100644 --- a/src/librustc_trans/trans/debuginfo.rs +++ b/src/librustc_trans/trans/debuginfo.rs @@ -1232,9 +1232,8 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, } ast_map::NodeExpr(ref expr) => { match expr.node { - ast::ExprFnBlock(_, ref fn_decl, ref top_level_block) | ast::ExprProc(ref fn_decl, ref top_level_block) | - ast::ExprUnboxedFn(_, _, 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.as_slice()); (name, &**fn_decl, @@ -1310,7 +1309,7 @@ pub fn create_function_debug_context<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, file_metadata, &mut function_name); - // There is no ast_map::Path for ast::ExprFnBlock-type functions. For now, + // There is no ast_map::Path for ast::ExprClosure-type functions. For now, // just don't put them into a namespace. In the future this could be improved // somehow (storing a path in the ast_map, or construct a path using the // enclosing function). @@ -3578,9 +3577,8 @@ fn populate_scope_map(cx: &CrateContext, }) } - ast::ExprFnBlock(_, ref decl, ref block) | ast::ExprProc(ref decl, ref block) | - ast::ExprUnboxedFn(_, _, ref decl, ref block) => { + ast::ExprClosure(_, _, ref decl, ref block) => { with_new_scope(cx, block.span, scope_stack, diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index 8ccc5983199..c095b0e8c8e 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -77,6 +77,7 @@ use trans::machine::{llsize_of, llsize_of_alloc}; use trans::type_::Type; use syntax::ast; +use syntax::ast_util; use syntax::codemap; use syntax::print::pprust::{expr_to_string}; use syntax::ptr::P; @@ -1059,16 +1060,23 @@ fn trans_rvalue_dps_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ast::ExprVec(..) | ast::ExprRepeat(..) => { tvec::trans_fixed_vstore(bcx, expr, dest) } - ast::ExprFnBlock(_, ref decl, ref body) | + ast::ExprClosure(_, _, ref decl, ref body) | ast::ExprProc(ref decl, ref body) => { - let expr_ty = expr_ty(bcx, expr); - let store = ty::ty_closure_store(expr_ty); - debug!("translating block function {} with type {}", - expr_to_string(expr), expr_ty.repr(tcx)); - closure::trans_expr_fn(bcx, store, &**decl, &**body, expr.id, dest) - } - ast::ExprUnboxedFn(_, _, ref decl, ref body) => { - closure::trans_unboxed_closure(bcx, &**decl, &**body, expr.id, dest) + // Check the side-table to see whether this is an unboxed + // closure or an older, legacy style closure. Store this + // into a variable to ensure the the RefCell-lock is + // released before we recurse. + let is_unboxed_closure = + bcx.tcx().unboxed_closures.borrow().contains_key(&ast_util::local_def(expr.id)); + if is_unboxed_closure { + closure::trans_unboxed_closure(bcx, &**decl, &**body, expr.id, dest) + } else { + let expr_ty = expr_ty(bcx, expr); + let store = ty::ty_closure_store(expr_ty); + debug!("translating block function {} with type {}", + expr_to_string(expr), expr_ty.repr(tcx)); + closure::trans_expr_fn(bcx, store, &**decl, &**body, expr.id, dest) + } } ast::ExprCall(ref f, ref args) => { if bcx.tcx().is_method_call(expr.id) { diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 15e14902727..f6f117c9a97 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -667,9 +667,8 @@ pub enum Expr_ { // FIXME #6993: change to Option ... or not, if these are hygienic. ExprLoop(P, Option), ExprMatch(P, Vec, MatchSource), - ExprFnBlock(CaptureClause, P, P), + ExprClosure(CaptureClause, Option, P, P), ExprProc(P, P), - ExprUnboxedFn(CaptureClause, UnboxedClosureKind, P, P), ExprBlock(P), ExprAssign(P, P), diff --git a/src/libsyntax/ast_map/blocks.rs b/src/libsyntax/ast_map/blocks.rs index a35ee3ab1d0..8db12fbd835 100644 --- a/src/libsyntax/ast_map/blocks.rs +++ b/src/libsyntax/ast_map/blocks.rs @@ -37,7 +37,7 @@ use visit; /// /// More specifically, it is one of either: /// - A function item, -/// - A closure expr (i.e. an ExprFnBlock or ExprProc), or +/// - A closure expr (i.e. an ExprClosure or ExprProc), or /// - The default implementation for a trait method. /// /// To construct one, use the `Code::from_node` function. @@ -71,7 +71,7 @@ impl MaybeFnLike for ast::TraitItem { impl MaybeFnLike for ast::Expr { fn is_fn_like(&self) -> bool { match self.node { - ast::ExprFnBlock(..) | ast::ExprProc(..) => true, + ast::ExprClosure(..) | ast::ExprProc(..) => true, _ => false, } } @@ -215,7 +215,7 @@ impl<'a> FnLikeNode<'a> { } } ast_map::NodeExpr(e) => match e.node { - ast::ExprFnBlock(_, ref decl, ref block) => + ast::ExprClosure(_, _, ref decl, ref block) => closure(ClosureParts::new(&**decl, &**block, e.id, e.span)), ast::ExprProc(ref decl, ref block) => closure(ClosureParts::new(&**decl, &**block, e.id, e.span)), diff --git a/src/libsyntax/ext/build.rs b/src/libsyntax/ext/build.rs index ffc42b67033..b18a0c8411c 100644 --- a/src/libsyntax/ext/build.rs +++ b/src/libsyntax/ext/build.rs @@ -864,14 +864,14 @@ impl<'a> AstBuilder for ExtCtxt<'a> { fn lambda_fn_decl(&self, span: Span, fn_decl: P, blk: P) -> P { - self.expr(span, ast::ExprFnBlock(ast::CaptureByRef, fn_decl, blk)) + self.expr(span, ast::ExprClosure(ast::CaptureByRef, None, fn_decl, blk)) } fn lambda(&self, span: Span, ids: Vec, blk: P) -> P { 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::ExprFnBlock(ast::CaptureByRef, fn_decl, blk)) + self.expr(span, ast::ExprClosure(ast::CaptureByRef, None, fn_decl, blk)) } fn lambda0(&self, span: Span, blk: P) -> P { self.lambda(span, Vec::new(), blk) diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 081456bebe1..04132679a03 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -207,10 +207,11 @@ pub fn expand_expr(e: P, fld: &mut MacroExpander) -> P { fld.cx.expr(span, ast::ExprForLoop(pat, head, body, opt_ident)) } - ast::ExprFnBlock(capture_clause, fn_decl, block) => { + ast::ExprClosure(capture_clause, opt_kind, fn_decl, block) => { let (rewritten_fn_decl, rewritten_block) = expand_and_rename_fn_decl_and_block(fn_decl, block, fld); - let new_node = ast::ExprFnBlock(capture_clause, + 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)}) @@ -1555,7 +1556,7 @@ mod test { 0) } - // closure arg hygiene (ExprFnBlock) + // closure arg hygiene (ExprClosure) // expands to fn f(){(|x_1 : int| {(x_2 + x_1)})(3);} #[test] fn closure_arg_hygiene(){ run_renaming_test( diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index ebdcf278934..8a9b3103224 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -309,7 +309,7 @@ impl<'a, 'v> Visitor<'v> for Context<'a> { fn visit_expr(&mut self, e: &ast::Expr) { match e.node { - ast::ExprUnboxedFn(..) => { + ast::ExprClosure(_, Some(_), _, _) => { self.gate_feature("unboxed_closures", e.span, "unboxed closures are a work-in-progress \ diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index b3137ff5f7e..7f8d33edc3c 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1316,18 +1316,13 @@ pub fn noop_fold_expr(Expr {id, node, span}: Expr, folder: &mut T) -> arms.move_map(|x| folder.fold_arm(x)), source) } - ExprFnBlock(capture_clause, decl, body) => { - ExprFnBlock(capture_clause, - folder.fold_fn_decl(decl), - folder.fold_block(body)) - } ExprProc(decl, body) => { ExprProc(folder.fold_fn_decl(decl), folder.fold_block(body)) } - ExprUnboxedFn(capture_clause, kind, decl, body) => { - ExprUnboxedFn(capture_clause, - kind, + ExprClosure(capture_clause, opt_kind, decl, body) => { + ExprClosure(capture_clause, + opt_kind, folder.fold_fn_decl(decl), folder.fold_block(body)) } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 50b1a2204b0..4557170d8b3 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -25,10 +25,10 @@ use ast::{DeclLocal, DefaultBlock, UnDeref, BiDiv, EMPTY_CTXT, EnumDef, Explicit use ast::{Expr, Expr_, ExprAddrOf, ExprMatch, ExprAgain}; use ast::{ExprAssign, ExprAssignOp, ExprBinary, ExprBlock, ExprBox}; use ast::{ExprBreak, ExprCall, ExprCast}; -use ast::{ExprField, ExprTupField, ExprFnBlock, ExprIf, ExprIfLet, ExprIndex, ExprSlice}; +use ast::{ExprField, ExprTupField, ExprClosure, ExprIf, ExprIfLet, ExprIndex, ExprSlice}; use ast::{ExprLit, ExprLoop, ExprMac}; use ast::{ExprMethodCall, ExprParen, ExprPath, ExprProc}; -use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary, ExprUnboxedFn}; +use ast::{ExprRepeat, ExprRet, ExprStruct, ExprTup, ExprUnary}; use ast::{ExprVec, ExprWhile, ExprWhileLet, ExprForLoop, Field, FnDecl}; use ast::{Once, Many}; use ast::{FnUnboxedClosureKind, FnMutUnboxedClosureKind}; @@ -2950,7 +2950,8 @@ impl<'a> Parser<'a> { // `|args| expr` pub fn parse_lambda_expr(&mut self, capture_clause: CaptureClause) - -> P { + -> P + { let lo = self.span.lo; let (decl, optional_unboxed_closure_kind) = self.parse_fn_block_decl(); @@ -2964,21 +2965,10 @@ impl<'a> Parser<'a> { rules: DefaultBlock, }); - match optional_unboxed_closure_kind { - Some(unboxed_closure_kind) => { - self.mk_expr(lo, - fakeblock.span.hi, - ExprUnboxedFn(capture_clause, - unboxed_closure_kind, - decl, - fakeblock)) - } - None => { - self.mk_expr(lo, - fakeblock.span.hi, - ExprFnBlock(capture_clause, decl, fakeblock)) - } - } + self.mk_expr( + lo, + fakeblock.span.hi, + ExprClosure(capture_clause, optional_unboxed_closure_kind, decl, fakeblock)) } pub fn parse_else_expr(&mut self) -> P { diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index e6e0c33a42d..919edee13ca 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -434,9 +434,8 @@ pub fn visibility_qualified(vis: ast::Visibility, s: &str) -> String { fn needs_parentheses(expr: &ast::Expr) -> bool { match expr.node { ast::ExprAssign(..) | ast::ExprBinary(..) | - ast::ExprFnBlock(..) | ast::ExprProc(..) | - ast::ExprUnboxedFn(..) | ast::ExprAssignOp(..) | - ast::ExprCast(..) => true, + ast::ExprClosure(..) | ast::ExprProc(..) | + ast::ExprAssignOp(..) | ast::ExprCast(..) => true, _ => false, } } @@ -1652,49 +1651,11 @@ impl<'a> State<'a> { } try!(self.bclose_(expr.span, indent_unit)); } - ast::ExprFnBlock(capture_clause, ref decl, ref body) => { + ast::ExprClosure(capture_clause, opt_kind, ref decl, ref body) => { try!(self.print_capture_clause(capture_clause)); - // in do/for blocks we don't want to show an empty - // argument list, but at this point we don't know which - // we are inside. - // - // if !decl.inputs.is_empty() { - try!(self.print_fn_block_args(&**decl, None)); + try!(self.print_fn_block_args(&**decl, opt_kind)); try!(space(&mut self.s)); - // } - - if !body.stmts.is_empty() || !body.expr.is_some() { - try!(self.print_block_unclosed(&**body)); - } else { - // we extract the block, so as not to create another set of boxes - match body.expr.as_ref().unwrap().node { - ast::ExprBlock(ref blk) => { - try!(self.print_block_unclosed(&**blk)); - } - _ => { - // this is a bare expression - try!(self.print_expr(&**body.expr.as_ref().unwrap())); - try!(self.end()); // need to close a box - } - } - } - // a box will be closed by print_expr, but we didn't want an overall - // wrapper so we closed the corresponding opening. so create an - // empty box to satisfy the close. - try!(self.ibox(0)); - } - ast::ExprUnboxedFn(capture_clause, kind, ref decl, ref body) => { - try!(self.print_capture_clause(capture_clause)); - - // in do/for blocks we don't want to show an empty - // argument list, but at this point we don't know which - // we are inside. - // - // if !decl.inputs.is_empty() { - try!(self.print_fn_block_args(&**decl, Some(kind))); - try!(space(&mut self.s)); - // } if !body.stmts.is_empty() || !body.expr.is_some() { try!(self.print_block_unclosed(&**body)); diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index efe1e18eda9..1ce1cbf4e59 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -803,14 +803,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr) { visitor.visit_arm(arm) } } - ExprFnBlock(_, ref function_declaration, ref body) => { - visitor.visit_fn(FkFnBlock, - &**function_declaration, - &**body, - expression.span, - expression.id) - } - ExprUnboxedFn(_, _, ref function_declaration, ref body) => { + ExprClosure(_, _, ref function_declaration, ref body) => { visitor.visit_fn(FkFnBlock, &**function_declaration, &**body, From b9c5cd4dc420b90874784b253bcb8fd4ac72441a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 19 Nov 2014 12:39:10 -0500 Subject: [PATCH 4/4] Use the expected type to decide whether `||` is an unboxed or boxed closure. --- src/librustc/middle/typeck/check/closure.rs | 108 ++++++++++-------- .../run-pass/unboxed-closures-infer-kind.rs | 38 ++++++ 2 files changed, 97 insertions(+), 49 deletions(-) create mode 100644 src/test/run-pass/unboxed-closures-infer-kind.rs diff --git a/src/librustc/middle/typeck/check/closure.rs b/src/librustc/middle/typeck/check/closure.rs index 220c2f70d17..51636f00c39 100644 --- a/src/librustc/middle/typeck/check/closure.rs +++ b/src/librustc/middle/typeck/check/closure.rs @@ -32,59 +32,62 @@ pub fn check_expr_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, decl: &ast::FnDecl, body: &ast::Block, expected: Expectation<'tcx>) { + debug!("check_expr_closure(expr={},expected={})", + expr.repr(fcx.tcx()), + expected.repr(fcx.tcx())); + match opt_kind { - None => { // old-school boxed closure - let region = astconv::opt_ast_region_to_region(fcx, - fcx.infcx(), - expr.span, - &None); - check_boxed_closure(fcx, - expr, - ty::RegionTraitStore(region, ast::MutMutable), - decl, - body, - expected); + 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'll fall back to boxed closures. + match deduce_unboxed_closure_expectations_from_expectation(fcx, expected) { + None => { // doesn't look like an unboxed closure + let region = astconv::opt_ast_region_to_region(fcx, + fcx.infcx(), + expr.span, + &None); + check_boxed_closure(fcx, + expr, + ty::RegionTraitStore(region, ast::MutMutable), + decl, + body, + expected); + } + Some((sig, kind)) => { + check_unboxed_closure(fcx, expr, kind, decl, body, Some(sig)); + } + } } Some(kind) => { - check_unboxed_closure(fcx, expr, kind, decl, body, expected) + let kind = match kind { + ast::FnUnboxedClosureKind => ty::FnUnboxedClosureKind, + ast::FnMutUnboxedClosureKind => ty::FnMutUnboxedClosureKind, + ast::FnOnceUnboxedClosureKind => ty::FnOnceUnboxedClosureKind, + }; + + let expected_sig = + deduce_unboxed_closure_expectations_from_expectation(fcx, expected) + .map(|t| t.0); + + check_unboxed_closure(fcx, expr, kind, decl, body, expected_sig); } } } fn check_unboxed_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, expr: &ast::Expr, - kind: ast::UnboxedClosureKind, + kind: ty::UnboxedClosureKind, decl: &ast::FnDecl, body: &ast::Block, - expected: Expectation<'tcx>) { + expected_sig: Option>) { let expr_def_id = ast_util::local_def(expr.id); - let expected_sig_and_kind = match expected.resolve(fcx) { - NoExpectation => None, - ExpectCastableToType(t) | ExpectHasType(t) => { - deduce_unboxed_closure_expectations_from_expected_type(fcx, t) - } - }; - - let (expected_sig, expected_kind) = match expected_sig_and_kind { - None => (None, None), - Some((sig, kind)) => { - // Avoid accidental capture of bound regions by renaming - // them to fresh names, basically. - let sig = - ty::replace_late_bound_regions( - fcx.tcx(), - &sig, - |_, debruijn| fcx.inh.infcx.fresh_bound_region(debruijn)).0; - (Some(sig), Some(kind)) - } - }; - - debug!("check_unboxed_closure expected={} expected_sig={} expected_kind={}", - expected.repr(fcx.tcx()), - expected_sig.repr(fcx.tcx()), - expected_kind); + debug!("check_unboxed_closure kind={} expected_sig={}", + kind, + expected_sig.repr(fcx.tcx())); let mut fn_ty = astconv::ty_of_closure( fcx, @@ -130,12 +133,6 @@ fn check_unboxed_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, // the `unboxed_closures` table. fn_ty.sig.inputs = vec![ty::mk_tup(fcx.tcx(), fn_ty.sig.inputs)]; - let kind = match kind { - ast::FnUnboxedClosureKind => ty::FnUnboxedClosureKind, - ast::FnMutUnboxedClosureKind => ty::FnMutUnboxedClosureKind, - ast::FnOnceUnboxedClosureKind => ty::FnOnceUnboxedClosureKind, - }; - debug!("unboxed_closure for {} --> sig={} kind={}", expr_def_id.repr(fcx.tcx()), fn_ty.sig.repr(fcx.tcx()), @@ -152,10 +149,23 @@ fn check_unboxed_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, .insert(expr_def_id, unboxed_closure); } -fn deduce_unboxed_closure_expectations_from_expected_type<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, - expected_ty: Ty<'tcx>) - -> Option<(ty::FnSig<'tcx>, - ty::UnboxedClosureKind)> +fn deduce_unboxed_closure_expectations_from_expectation<'a,'tcx>( + fcx: &FnCtxt<'a,'tcx>, + expected: Expectation<'tcx>) + -> Option<(ty::FnSig<'tcx>,ty::UnboxedClosureKind)> +{ + match expected.resolve(fcx) { + NoExpectation => None, + ExpectCastableToType(t) | ExpectHasType(t) => { + deduce_unboxed_closure_expectations_from_expected_type(fcx, t) + } + } +} + +fn deduce_unboxed_closure_expectations_from_expected_type<'a,'tcx>( + fcx: &FnCtxt<'a,'tcx>, + expected_ty: Ty<'tcx>) + -> Option<(ty::FnSig<'tcx>,ty::UnboxedClosureKind)> { match expected_ty.sty { ty::ty_trait(ref object_type) => { diff --git a/src/test/run-pass/unboxed-closures-infer-kind.rs b/src/test/run-pass/unboxed-closures-infer-kind.rs new file mode 100644 index 00000000000..0cb719ecd7f --- /dev/null +++ b/src/test/run-pass/unboxed-closures-infer-kind.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. + +// Test that we can infer the "kind" of an unboxed closure based on +// the expected type. + +#![feature(unboxed_closures)] + +// Test by-ref capture of environment in unboxed closure types + +fn call_fn(f: F) { + f() +} + +fn call_fn_mut(mut f: F) { + f() +} + +fn call_fn_once(f: F) { + f() +} + +fn main() { + let mut x = 0u; + let y = 2u; + + call_fn(|| assert_eq!(x, 0)); + call_fn_mut(|| x += y); + call_fn_once(|| x += y); + assert_eq!(x, y * 2); +}