Skip to content

Commit c3075f3

Browse files
authored
Rollup merge of rust-lang#40025 - est31:master, r=eddyb
Implement non-capturing closure to fn coercion Implements non capturing closure coercion ([RFC 1558](https://github.com/rust-lang/rfcs/blob/master/text/1558-closure-to-fn-coercion.md)). cc tracking issue rust-lang#39817
2 parents 32af4ce + 77f131d commit c3075f3

File tree

22 files changed

+275
-3
lines changed

22 files changed

+275
-3
lines changed

src/librustc/middle/expr_use_visitor.rs

+1
Original file line numberDiff line numberDiff line change
@@ -715,6 +715,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
715715
adjustment::Adjust::NeverToAny |
716716
adjustment::Adjust::ReifyFnPointer |
717717
adjustment::Adjust::UnsafeFnPointer |
718+
adjustment::Adjust::ClosureFnPointer |
718719
adjustment::Adjust::MutToConstPointer => {
719720
// Creating a closure/fn-pointer or unsizing consumes
720721
// the input and stores it into the resulting rvalue.

src/librustc/middle/mem_categorization.rs

+1
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
464464
adjustment::Adjust::NeverToAny |
465465
adjustment::Adjust::ReifyFnPointer |
466466
adjustment::Adjust::UnsafeFnPointer |
467+
adjustment::Adjust::ClosureFnPointer |
467468
adjustment::Adjust::MutToConstPointer |
468469
adjustment::Adjust::DerefRef {..} => {
469470
debug!("cat_expr({:?}): {:?}",

src/librustc/mir/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,9 @@ pub enum CastKind {
10221022
/// Convert unique, zero-sized type for a fn to fn()
10231023
ReifyFnPointer,
10241024

1025+
/// Convert non capturing closure to fn()
1026+
ClosureFnPointer,
1027+
10251028
/// Convert safe fn() to unsafe fn()
10261029
UnsafeFnPointer,
10271030

src/librustc/ty/adjustment.rs

+4
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ pub enum Adjust<'tcx> {
3333
/// Go from a safe fn pointer to an unsafe fn pointer.
3434
UnsafeFnPointer,
3535

36+
// Go from a non-capturing closure to an fn pointer.
37+
ClosureFnPointer,
38+
3639
/// Go from a mut raw pointer to a const raw pointer.
3740
MutToConstPointer,
3841

@@ -120,6 +123,7 @@ impl<'tcx> Adjustment<'tcx> {
120123

121124
Adjust::ReifyFnPointer |
122125
Adjust::UnsafeFnPointer |
126+
Adjust::ClosureFnPointer |
123127
Adjust::MutToConstPointer |
124128
Adjust::DerefRef {..} => false,
125129
}

src/librustc_mir/build/expr/as_lvalue.rs

+1
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
9999
ExprKind::Use { .. } |
100100
ExprKind::NeverToAny { .. } |
101101
ExprKind::ReifyFnPointer { .. } |
102+
ExprKind::ClosureFnPointer { .. } |
102103
ExprKind::UnsafeFnPointer { .. } |
103104
ExprKind::Unsize { .. } |
104105
ExprKind::Repeat { .. } |

src/librustc_mir/build/expr/as_rvalue.rs

+4
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
112112
let source = unpack!(block = this.as_operand(block, source));
113113
block.and(Rvalue::Cast(CastKind::UnsafeFnPointer, source, expr.ty))
114114
}
115+
ExprKind::ClosureFnPointer { source } => {
116+
let source = unpack!(block = this.as_operand(block, source));
117+
block.and(Rvalue::Cast(CastKind::ClosureFnPointer, source, expr.ty))
118+
}
115119
ExprKind::Unsize { source } => {
116120
let source = unpack!(block = this.as_operand(block, source));
117121
block.and(Rvalue::Cast(CastKind::Unsize, source, expr.ty))

src/librustc_mir/build/expr/category.rs

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ impl Category {
7070
ExprKind::Cast { .. } |
7171
ExprKind::Use { .. } |
7272
ExprKind::ReifyFnPointer { .. } |
73+
ExprKind::ClosureFnPointer { .. } |
7374
ExprKind::UnsafeFnPointer { .. } |
7475
ExprKind::Unsize { .. } |
7576
ExprKind::Repeat { .. } |

src/librustc_mir/build/expr/into.rs

+1
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
244244
ExprKind::Cast { .. } |
245245
ExprKind::Use { .. } |
246246
ExprKind::ReifyFnPointer { .. } |
247+
ExprKind::ClosureFnPointer { .. } |
247248
ExprKind::UnsafeFnPointer { .. } |
248249
ExprKind::Unsize { .. } |
249250
ExprKind::Repeat { .. } |

src/librustc_mir/hair/cx/expr.rs

+9
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
6060
kind: ExprKind::UnsafeFnPointer { source: expr.to_ref() },
6161
};
6262
}
63+
Some((ty::adjustment::Adjust::ClosureFnPointer, adjusted_ty)) => {
64+
expr = Expr {
65+
temp_lifetime: temp_lifetime,
66+
temp_lifetime_was_shrunk: was_shrunk,
67+
ty: adjusted_ty,
68+
span: self.span,
69+
kind: ExprKind::ClosureFnPointer { source: expr.to_ref() },
70+
};
71+
}
6372
Some((ty::adjustment::Adjust::NeverToAny, adjusted_ty)) => {
6473
expr = Expr {
6574
temp_lifetime: temp_lifetime,

src/librustc_mir/hair/mod.rs

+3
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ pub enum ExprKind<'tcx> {
152152
ReifyFnPointer {
153153
source: ExprRef<'tcx>,
154154
},
155+
ClosureFnPointer {
156+
source: ExprRef<'tcx>,
157+
},
155158
UnsafeFnPointer {
156159
source: ExprRef<'tcx>,
157160
},

src/librustc_mir/transform/qualify_consts.rs

+1
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
619619
Rvalue::CheckedBinaryOp(..) |
620620
Rvalue::Cast(CastKind::ReifyFnPointer, ..) |
621621
Rvalue::Cast(CastKind::UnsafeFnPointer, ..) |
622+
Rvalue::Cast(CastKind::ClosureFnPointer, ..) |
622623
Rvalue::Cast(CastKind::Unsize, ..) => {}
623624

624625
Rvalue::Len(_) => {

src/librustc_passes/consts.rs

+1
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,7 @@ fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Exp
447447
Some(Adjust::NeverToAny) |
448448
Some(Adjust::ReifyFnPointer) |
449449
Some(Adjust::UnsafeFnPointer) |
450+
Some(Adjust::ClosureFnPointer) |
450451
Some(Adjust::MutToConstPointer) => {}
451452

452453
Some(Adjust::DerefRef { autoderefs, .. }) => {

src/librustc_trans/builder.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1188,7 +1188,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
11881188
}
11891189

11901190
assert!(fn_ty.kind() == llvm::TypeKind::Function,
1191-
"builder::{} not passed a function", typ);
1191+
"builder::{} not passed a function, but {:?}", typ, fn_ty);
11921192

11931193
let param_tys = fn_ty.func_params();
11941194

src/librustc_trans/collector.rs

+14
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,20 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
489489
self.output);
490490
}
491491
}
492+
mir::Rvalue::Cast(mir::CastKind::ClosureFnPointer, ref operand, _) => {
493+
let source_ty = operand.ty(self.mir, self.scx.tcx());
494+
match source_ty.sty {
495+
ty::TyClosure(def_id, substs) => {
496+
let closure_trans_item =
497+
create_fn_trans_item(self.scx,
498+
def_id,
499+
substs.substs,
500+
self.param_substs);
501+
self.output.push(closure_trans_item);
502+
}
503+
_ => bug!(),
504+
}
505+
}
492506
mir::Rvalue::Box(..) => {
493507
let exchange_malloc_fn_def_id =
494508
self.scx

src/librustc_trans/mir/constant.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use rustc::mir;
2020
use rustc::mir::tcx::LvalueTy;
2121
use rustc::ty::{self, layout, Ty, TyCtxt, TypeFoldable};
2222
use rustc::ty::cast::{CastTy, IntTy};
23-
use rustc::ty::subst::Substs;
23+
use rustc::ty::subst::{Kind, Substs};
2424
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
2525
use {abi, adt, base, Disr, machine};
2626
use callee::Callee;
@@ -578,6 +578,27 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> {
578578
}
579579
}
580580
}
581+
mir::CastKind::ClosureFnPointer => {
582+
match operand.ty.sty {
583+
ty::TyClosure(def_id, substs) => {
584+
// Get the def_id for FnOnce::call_once
585+
let fn_once = tcx.lang_items.fn_once_trait().unwrap();
586+
let call_once = tcx
587+
.global_tcx().associated_items(fn_once)
588+
.find(|it| it.kind == ty::AssociatedKind::Method)
589+
.unwrap().def_id;
590+
// Now create its substs [Closure, Tuple]
591+
let input = tcx.closure_type(def_id, substs).sig.input(0);
592+
let substs = tcx.mk_substs([operand.ty, input.skip_binder()]
593+
.iter().cloned().map(Kind::from));
594+
Callee::def(self.ccx, call_once, substs)
595+
.reify(self.ccx)
596+
}
597+
_ => {
598+
bug!("{} cannot be cast to a fn ptr", operand.ty)
599+
}
600+
}
601+
}
581602
mir::CastKind::UnsafeFnPointer => {
582603
// this is a no-op at the LLVM level
583604
operand.llval

src/librustc_trans/mir/rvalue.rs

+23
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use llvm::{self, ValueRef};
1212
use rustc::ty::{self, Ty};
1313
use rustc::ty::cast::{CastTy, IntTy};
1414
use rustc::ty::layout::Layout;
15+
use rustc::ty::subst::Kind;
1516
use rustc::mir::tcx::LvalueTy;
1617
use rustc::mir;
1718
use middle::lang_items::ExchangeMallocFnLangItem;
@@ -190,6 +191,28 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
190191
}
191192
}
192193
}
194+
mir::CastKind::ClosureFnPointer => {
195+
match operand.ty.sty {
196+
ty::TyClosure(def_id, substs) => {
197+
// Get the def_id for FnOnce::call_once
198+
let fn_once = bcx.tcx().lang_items.fn_once_trait().unwrap();
199+
let call_once = bcx.tcx()
200+
.global_tcx().associated_items(fn_once)
201+
.find(|it| it.kind == ty::AssociatedKind::Method)
202+
.unwrap().def_id;
203+
// Now create its substs [Closure, Tuple]
204+
let input = bcx.tcx().closure_type(def_id, substs).sig.input(0);
205+
let substs = bcx.tcx().mk_substs([operand.ty, input.skip_binder()]
206+
.iter().cloned().map(Kind::from));
207+
OperandValue::Immediate(
208+
Callee::def(bcx.ccx, call_once, substs)
209+
.reify(bcx.ccx))
210+
}
211+
_ => {
212+
bug!("{} cannot be cast to a fn ptr", operand.ty)
213+
}
214+
}
215+
}
193216
mir::CastKind::UnsafeFnPointer => {
194217
// this is a no-op at the LLVM level
195218
operand.val

src/librustc_typeck/check/coercion.rs

+64-1
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,17 @@
6363
use check::FnCtxt;
6464

6565
use rustc::hir;
66+
use rustc::hir::def_id::DefId;
6667
use rustc::infer::{Coercion, InferOk, TypeTrace};
6768
use rustc::traits::{self, ObligationCause, ObligationCauseCode};
6869
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow};
69-
use rustc::ty::{self, LvaluePreference, TypeAndMut, Ty};
70+
use rustc::ty::{self, LvaluePreference, TypeAndMut,
71+
Ty, ClosureSubsts};
7072
use rustc::ty::fold::TypeFoldable;
7173
use rustc::ty::error::TypeError;
7274
use rustc::ty::relate::RelateResult;
75+
use syntax::abi;
76+
use syntax::feature_gate;
7377
use util::common::indent;
7478

7579
use std::cell::RefCell;
@@ -196,6 +200,11 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
196200
// unsafe qualifier.
197201
self.coerce_from_fn_pointer(a, a_f, b)
198202
}
203+
ty::TyClosure(def_id_a, substs_a) => {
204+
// Non-capturing closures are coercible to
205+
// function pointers
206+
self.coerce_closure_to_fn(a, def_id_a, substs_a, b)
207+
}
199208
_ => {
200209
// Otherwise, just use unification rules.
201210
self.unify_and_identity(a, b)
@@ -551,6 +560,60 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
551560
}
552561
}
553562

563+
fn coerce_closure_to_fn(&self,
564+
a: Ty<'tcx>,
565+
def_id_a: DefId,
566+
substs_a: ClosureSubsts<'tcx>,
567+
b: Ty<'tcx>)
568+
-> CoerceResult<'tcx> {
569+
//! Attempts to coerce from the type of a non-capturing closure
570+
//! into a function pointer.
571+
//!
572+
573+
let b = self.shallow_resolve(b);
574+
575+
let node_id_a = self.tcx.hir.as_local_node_id(def_id_a).unwrap();
576+
match b.sty {
577+
ty::TyFnPtr(_) if self.tcx.with_freevars(node_id_a, |v| v.is_empty()) => {
578+
if !self.tcx.sess.features.borrow().closure_to_fn_coercion {
579+
feature_gate::emit_feature_err(&self.tcx.sess.parse_sess,
580+
"closure_to_fn_coercion",
581+
self.cause.span,
582+
feature_gate::GateIssue::Language,
583+
feature_gate::CLOSURE_TO_FN_COERCION);
584+
return self.unify_and_identity(a, b);
585+
}
586+
// We coerce the closure, which has fn type
587+
// `extern "rust-call" fn((arg0,arg1,...)) -> _`
588+
// to
589+
// `fn(arg0,arg1,...) -> _`
590+
let sig = self.closure_type(def_id_a, substs_a).sig;
591+
let converted_sig = sig.map_bound(|s| {
592+
let params_iter = match s.inputs()[0].sty {
593+
ty::TyTuple(params, _) => {
594+
params.into_iter().cloned()
595+
}
596+
_ => bug!(),
597+
};
598+
self.tcx.mk_fn_sig(params_iter,
599+
s.output(),
600+
s.variadic)
601+
});
602+
let fn_ty = self.tcx.mk_bare_fn(ty::BareFnTy {
603+
unsafety: hir::Unsafety::Normal,
604+
abi: abi::Abi::Rust,
605+
sig: converted_sig,
606+
});
607+
let pointer_ty = self.tcx.mk_fn_ptr(&fn_ty);
608+
debug!("coerce_closure_to_fn(a={:?}, b={:?}, pty={:?})",
609+
a, b, pointer_ty);
610+
self.unify_and_identity(pointer_ty, b)
611+
.map(|(ty, _)| (ty, Adjust::ClosureFnPointer))
612+
}
613+
_ => self.unify_and_identity(a, b),
614+
}
615+
}
616+
554617
fn coerce_unsafe_ptr(&self,
555618
a: Ty<'tcx>,
556619
b: Ty<'tcx>,

src/librustc_typeck/check/writeback.rs

+4
Original file line numberDiff line numberDiff line change
@@ -412,6 +412,10 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
412412
adjustment::Adjust::MutToConstPointer
413413
}
414414

415+
adjustment::Adjust::ClosureFnPointer => {
416+
adjustment::Adjust::ClosureFnPointer
417+
}
418+
415419
adjustment::Adjust::UnsafeFnPointer => {
416420
adjustment::Adjust::UnsafeFnPointer
417421
}

src/libsyntax/feature_gate.rs

+7
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,10 @@ declare_features! (
326326
// `extern "msp430-interrupt" fn()`
327327
(active, abi_msp430_interrupt, "1.16.0", Some(38487)),
328328

329+
// Used to identify crates that contain sanitizer runtimes
330+
// rustc internal
331+
(active, closure_to_fn_coercion, "1.17.0", Some(39817)),
332+
329333
// Used to identify crates that contain sanitizer runtimes
330334
// rustc internal
331335
(active, sanitizer_runtime, "1.17.0", None),
@@ -982,6 +986,9 @@ pub const EXPLAIN_DERIVE_UNDERSCORE: &'static str =
982986
pub const EXPLAIN_PLACEMENT_IN: &'static str =
983987
"placement-in expression syntax is experimental and subject to change.";
984988

989+
pub const CLOSURE_TO_FN_COERCION: &'static str =
990+
"non-capturing closure to fn coercion is experimental";
991+
985992
struct PostExpansionVisitor<'a> {
986993
context: &'a Context<'a>,
987994
}
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// Ensure that capturing closures are never coerced to fns
12+
// Especially interesting as non-capturing closures can be.
13+
14+
fn main() {
15+
let mut a = 0u8;
16+
let foo: fn(u8) -> u8 = |v: u8| { a += v; a };
17+
//~^ ERROR mismatched types
18+
let b = 0u8;
19+
let bar: fn() -> u8 = || { b };
20+
//~^ ERROR mismatched types
21+
let baz: fn() -> u8 = || { b } as fn() -> u8;
22+
//~^ ERROR mismatched types
23+
//~^^ ERROR non-scalar cast
24+
}

0 commit comments

Comments
 (0)