From b9c5cd4dc420b90874784b253bcb8fd4ac72441a Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 19 Nov 2014 12:39:10 -0500 Subject: [PATCH] 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); +}