diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 2ca0069560cb7..a44679b0b3e0e 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -715,6 +715,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> { adjustment::Adjust::NeverToAny | adjustment::Adjust::ReifyFnPointer | adjustment::Adjust::UnsafeFnPointer | + adjustment::Adjust::ClosureFnPointer | adjustment::Adjust::MutToConstPointer => { // Creating a closure/fn-pointer or unsizing consumes // the input and stores it into the resulting rvalue. diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index 627753039bae3..b0c85e2ef4cd4 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -464,6 +464,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> { adjustment::Adjust::NeverToAny | adjustment::Adjust::ReifyFnPointer | adjustment::Adjust::UnsafeFnPointer | + adjustment::Adjust::ClosureFnPointer | adjustment::Adjust::MutToConstPointer | adjustment::Adjust::DerefRef {..} => { debug!("cat_expr({:?}): {:?}", diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index 3403cf0477450..d2a657e35b54a 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -1022,6 +1022,9 @@ pub enum CastKind { /// Convert unique, zero-sized type for a fn to fn() ReifyFnPointer, + /// Convert non capturing closure to fn() + ClosureFnPointer, + /// Convert safe fn() to unsafe fn() UnsafeFnPointer, diff --git a/src/librustc/ty/adjustment.rs b/src/librustc/ty/adjustment.rs index 333a5c74cb45c..34977822bc69d 100644 --- a/src/librustc/ty/adjustment.rs +++ b/src/librustc/ty/adjustment.rs @@ -33,6 +33,9 @@ pub enum Adjust<'tcx> { /// Go from a safe fn pointer to an unsafe fn pointer. UnsafeFnPointer, + // Go from a non-capturing closure to an fn pointer. + ClosureFnPointer, + /// Go from a mut raw pointer to a const raw pointer. MutToConstPointer, @@ -120,6 +123,7 @@ impl<'tcx> Adjustment<'tcx> { Adjust::ReifyFnPointer | Adjust::UnsafeFnPointer | + Adjust::ClosureFnPointer | Adjust::MutToConstPointer | Adjust::DerefRef {..} => false, } diff --git a/src/librustc_mir/build/expr/as_lvalue.rs b/src/librustc_mir/build/expr/as_lvalue.rs index 0487e277a338b..5abfe084f2258 100644 --- a/src/librustc_mir/build/expr/as_lvalue.rs +++ b/src/librustc_mir/build/expr/as_lvalue.rs @@ -99,6 +99,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ExprKind::Use { .. } | ExprKind::NeverToAny { .. } | ExprKind::ReifyFnPointer { .. } | + ExprKind::ClosureFnPointer { .. } | ExprKind::UnsafeFnPointer { .. } | ExprKind::Unsize { .. } | ExprKind::Repeat { .. } | diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index 7adcc0e730b15..7f5d9c36ecedf 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -112,6 +112,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let source = unpack!(block = this.as_operand(block, source)); block.and(Rvalue::Cast(CastKind::UnsafeFnPointer, source, expr.ty)) } + ExprKind::ClosureFnPointer { source } => { + let source = unpack!(block = this.as_operand(block, source)); + block.and(Rvalue::Cast(CastKind::ClosureFnPointer, source, expr.ty)) + } ExprKind::Unsize { source } => { let source = unpack!(block = this.as_operand(block, source)); block.and(Rvalue::Cast(CastKind::Unsize, source, expr.ty)) diff --git a/src/librustc_mir/build/expr/category.rs b/src/librustc_mir/build/expr/category.rs index 6e57c10964cb2..35173bb598c7c 100644 --- a/src/librustc_mir/build/expr/category.rs +++ b/src/librustc_mir/build/expr/category.rs @@ -70,6 +70,7 @@ impl Category { ExprKind::Cast { .. } | ExprKind::Use { .. } | ExprKind::ReifyFnPointer { .. } | + ExprKind::ClosureFnPointer { .. } | ExprKind::UnsafeFnPointer { .. } | ExprKind::Unsize { .. } | ExprKind::Repeat { .. } | diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index e66f2b4e2bfc0..d9f71e36e2118 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -244,6 +244,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { ExprKind::Cast { .. } | ExprKind::Use { .. } | ExprKind::ReifyFnPointer { .. } | + ExprKind::ClosureFnPointer { .. } | ExprKind::UnsafeFnPointer { .. } | ExprKind::Unsize { .. } | ExprKind::Repeat { .. } | diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 7eaf1fe139865..52e6446376830 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -60,6 +60,15 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { kind: ExprKind::UnsafeFnPointer { source: expr.to_ref() }, }; } + Some((ty::adjustment::Adjust::ClosureFnPointer, adjusted_ty)) => { + expr = Expr { + temp_lifetime: temp_lifetime, + temp_lifetime_was_shrunk: was_shrunk, + ty: adjusted_ty, + span: self.span, + kind: ExprKind::ClosureFnPointer { source: expr.to_ref() }, + }; + } Some((ty::adjustment::Adjust::NeverToAny, adjusted_ty)) => { expr = Expr { temp_lifetime: temp_lifetime, diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index 4ac67cfb2fca1..4ab45e14c99d5 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -152,6 +152,9 @@ pub enum ExprKind<'tcx> { ReifyFnPointer { source: ExprRef<'tcx>, }, + ClosureFnPointer { + source: ExprRef<'tcx>, + }, UnsafeFnPointer { source: ExprRef<'tcx>, }, diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs index 4459142cfb274..04e809ef9d839 100644 --- a/src/librustc_mir/transform/qualify_consts.rs +++ b/src/librustc_mir/transform/qualify_consts.rs @@ -619,6 +619,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> { Rvalue::CheckedBinaryOp(..) | Rvalue::Cast(CastKind::ReifyFnPointer, ..) | Rvalue::Cast(CastKind::UnsafeFnPointer, ..) | + Rvalue::Cast(CastKind::ClosureFnPointer, ..) | Rvalue::Cast(CastKind::Unsize, ..) => {} Rvalue::Len(_) => { diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index 0b55513f8318c..e3772a09968ec 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -447,6 +447,7 @@ fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Exp Some(Adjust::NeverToAny) | Some(Adjust::ReifyFnPointer) | Some(Adjust::UnsafeFnPointer) | + Some(Adjust::ClosureFnPointer) | Some(Adjust::MutToConstPointer) => {} Some(Adjust::DerefRef { autoderefs, .. }) => { diff --git a/src/librustc_trans/builder.rs b/src/librustc_trans/builder.rs index f64e581c1773e..66722f883d7a0 100644 --- a/src/librustc_trans/builder.rs +++ b/src/librustc_trans/builder.rs @@ -1181,7 +1181,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } assert!(fn_ty.kind() == llvm::TypeKind::Function, - "builder::{} not passed a function", typ); + "builder::{} not passed a function, but {:?}", typ, fn_ty); let param_tys = fn_ty.func_params(); diff --git a/src/librustc_trans/collector.rs b/src/librustc_trans/collector.rs index b5f948442b774..61b2c108fed79 100644 --- a/src/librustc_trans/collector.rs +++ b/src/librustc_trans/collector.rs @@ -489,6 +489,20 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { self.output); } } + mir::Rvalue::Cast(mir::CastKind::ClosureFnPointer, ref operand, _) => { + let source_ty = operand.ty(self.mir, self.scx.tcx()); + match source_ty.sty { + ty::TyClosure(def_id, substs) => { + let closure_trans_item = + create_fn_trans_item(self.scx, + def_id, + substs.substs, + self.param_substs); + self.output.push(closure_trans_item); + } + _ => bug!(), + } + } mir::Rvalue::Box(..) => { let exchange_malloc_fn_def_id = self.scx diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index 7e17ae5f1d389..e6cae2f9f3296 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -20,7 +20,7 @@ use rustc::mir; use rustc::mir::tcx::LvalueTy; use rustc::ty::{self, layout, Ty, TyCtxt, TypeFoldable}; use rustc::ty::cast::{CastTy, IntTy}; -use rustc::ty::subst::Substs; +use rustc::ty::subst::{Kind, Substs}; use rustc_data_structures::indexed_vec::{Idx, IndexVec}; use {abi, adt, base, Disr, machine}; use callee::Callee; @@ -578,6 +578,27 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { } } } + mir::CastKind::ClosureFnPointer => { + match operand.ty.sty { + ty::TyClosure(def_id, substs) => { + // Get the def_id for FnOnce::call_once + let fn_once = tcx.lang_items.fn_once_trait().unwrap(); + let call_once = tcx + .global_tcx().associated_items(fn_once) + .find(|it| it.kind == ty::AssociatedKind::Method) + .unwrap().def_id; + // Now create its substs [Closure, Tuple] + let input = tcx.closure_type(def_id, substs).sig.input(0); + let substs = tcx.mk_substs([operand.ty, input.skip_binder()] + .iter().cloned().map(Kind::from)); + Callee::def(self.ccx, call_once, substs) + .reify(self.ccx) + } + _ => { + bug!("{} cannot be cast to a fn ptr", operand.ty) + } + } + } mir::CastKind::UnsafeFnPointer => { // this is a no-op at the LLVM level operand.llval diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 7d4f542addbb1..6f6d81a25350f 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -12,6 +12,7 @@ use llvm::{self, ValueRef}; use rustc::ty::{self, Ty}; use rustc::ty::cast::{CastTy, IntTy}; use rustc::ty::layout::Layout; +use rustc::ty::subst::Kind; use rustc::mir::tcx::LvalueTy; use rustc::mir; use middle::lang_items::ExchangeMallocFnLangItem; @@ -190,6 +191,28 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { } } } + mir::CastKind::ClosureFnPointer => { + match operand.ty.sty { + ty::TyClosure(def_id, substs) => { + // Get the def_id for FnOnce::call_once + let fn_once = bcx.tcx().lang_items.fn_once_trait().unwrap(); + let call_once = bcx.tcx() + .global_tcx().associated_items(fn_once) + .find(|it| it.kind == ty::AssociatedKind::Method) + .unwrap().def_id; + // Now create its substs [Closure, Tuple] + let input = bcx.tcx().closure_type(def_id, substs).sig.input(0); + let substs = bcx.tcx().mk_substs([operand.ty, input.skip_binder()] + .iter().cloned().map(Kind::from)); + OperandValue::Immediate( + Callee::def(bcx.ccx, call_once, substs) + .reify(bcx.ccx)) + } + _ => { + bug!("{} cannot be cast to a fn ptr", operand.ty) + } + } + } mir::CastKind::UnsafeFnPointer => { // this is a no-op at the LLVM level operand.val diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 718c273785ae9..424e3143929f4 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -63,13 +63,17 @@ use check::FnCtxt; use rustc::hir; +use rustc::hir::def_id::DefId; use rustc::infer::{Coercion, InferOk, TypeTrace}; use rustc::traits::{self, ObligationCause, ObligationCauseCode}; use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow}; -use rustc::ty::{self, LvaluePreference, TypeAndMut, Ty}; +use rustc::ty::{self, LvaluePreference, TypeAndMut, + Ty, ClosureSubsts}; use rustc::ty::fold::TypeFoldable; use rustc::ty::error::TypeError; use rustc::ty::relate::RelateResult; +use syntax::abi; +use syntax::feature_gate; use util::common::indent; use std::cell::RefCell; @@ -196,6 +200,11 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> { // unsafe qualifier. self.coerce_from_fn_pointer(a, a_f, b) } + ty::TyClosure(def_id_a, substs_a) => { + // Non-capturing closures are coercible to + // function pointers + self.coerce_closure_to_fn(a, def_id_a, substs_a, b) + } _ => { // Otherwise, just use unification rules. self.unify_and_identity(a, b) @@ -551,6 +560,60 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> { } } + fn coerce_closure_to_fn(&self, + a: Ty<'tcx>, + def_id_a: DefId, + substs_a: ClosureSubsts<'tcx>, + b: Ty<'tcx>) + -> CoerceResult<'tcx> { + //! Attempts to coerce from the type of a non-capturing closure + //! into a function pointer. + //! + + let b = self.shallow_resolve(b); + + let node_id_a = self.tcx.hir.as_local_node_id(def_id_a).unwrap(); + match b.sty { + ty::TyFnPtr(_) if self.tcx.with_freevars(node_id_a, |v| v.is_empty()) => { + if !self.tcx.sess.features.borrow().closure_to_fn_coercion { + feature_gate::emit_feature_err(&self.tcx.sess.parse_sess, + "closure_to_fn_coercion", + self.cause.span, + feature_gate::GateIssue::Language, + feature_gate::CLOSURE_TO_FN_COERCION); + return self.unify_and_identity(a, b); + } + // We coerce the closure, which has fn type + // `extern "rust-call" fn((arg0,arg1,...)) -> _` + // to + // `fn(arg0,arg1,...) -> _` + let sig = self.closure_type(def_id_a, substs_a).sig; + let converted_sig = sig.map_bound(|s| { + let params_iter = match s.inputs()[0].sty { + ty::TyTuple(params, _) => { + params.into_iter().cloned() + } + _ => bug!(), + }; + self.tcx.mk_fn_sig(params_iter, + s.output(), + s.variadic) + }); + let fn_ty = self.tcx.mk_bare_fn(ty::BareFnTy { + unsafety: hir::Unsafety::Normal, + abi: abi::Abi::Rust, + sig: converted_sig, + }); + let pointer_ty = self.tcx.mk_fn_ptr(&fn_ty); + debug!("coerce_closure_to_fn(a={:?}, b={:?}, pty={:?})", + a, b, pointer_ty); + self.unify_and_identity(pointer_ty, b) + .map(|(ty, _)| (ty, Adjust::ClosureFnPointer)) + } + _ => self.unify_and_identity(a, b), + } + } + fn coerce_unsafe_ptr(&self, a: Ty<'tcx>, b: Ty<'tcx>, diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index a292227058379..a25e5f3f283dd 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -412,6 +412,10 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> { adjustment::Adjust::MutToConstPointer } + adjustment::Adjust::ClosureFnPointer => { + adjustment::Adjust::ClosureFnPointer + } + adjustment::Adjust::UnsafeFnPointer => { adjustment::Adjust::UnsafeFnPointer } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 1bed3e2784773..2d3fd78867a2d 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -323,6 +323,10 @@ declare_features! ( // `extern "msp430-interrupt" fn()` (active, abi_msp430_interrupt, "1.16.0", Some(38487)), + // Used to identify crates that contain sanitizer runtimes + // rustc internal + (active, closure_to_fn_coercion, "1.17.0", Some(39817)), + // Used to identify crates that contain sanitizer runtimes // rustc internal (active, sanitizer_runtime, "1.17.0", None), @@ -977,6 +981,9 @@ pub const EXPLAIN_DERIVE_UNDERSCORE: &'static str = pub const EXPLAIN_PLACEMENT_IN: &'static str = "placement-in expression syntax is experimental and subject to change."; +pub const CLOSURE_TO_FN_COERCION: &'static str = + "non-capturing closure to fn coercion is experimental"; + struct PostExpansionVisitor<'a> { context: &'a Context<'a>, } diff --git a/src/test/compile-fail/closure-no-fn.rs b/src/test/compile-fail/closure-no-fn.rs new file mode 100644 index 0000000000000..fe179e8a48f16 --- /dev/null +++ b/src/test/compile-fail/closure-no-fn.rs @@ -0,0 +1,24 @@ +// Copyright 2017 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. + +// Ensure that capturing closures are never coerced to fns +// Especially interesting as non-capturing closures can be. + +fn main() { + let mut a = 0u8; + let foo: fn(u8) -> u8 = |v: u8| { a += v; a }; + //~^ ERROR mismatched types + let b = 0u8; + let bar: fn() -> u8 = || { b }; + //~^ ERROR mismatched types + let baz: fn() -> u8 = || { b } as fn() -> u8; + //~^ ERROR mismatched types + //~^^ ERROR non-scalar cast +} diff --git a/src/test/compile-fail/feature-gate-closure_to_fn_coercion.rs b/src/test/compile-fail/feature-gate-closure_to_fn_coercion.rs new file mode 100644 index 0000000000000..d074a35628ea8 --- /dev/null +++ b/src/test/compile-fail/feature-gate-closure_to_fn_coercion.rs @@ -0,0 +1,45 @@ +// Copyright 2017 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. + +// ignore-stage0: new feature, remove this when SNAP +// revisions: a b + +#[cfg(a)] +mod a { + const FOO: fn(u8) -> u8 = |v: u8| { v }; + //[a]~^ ERROR non-capturing closure to fn coercion is experimental + //[a]~^^ ERROR mismatched types + + const BAR: [fn(&mut u32); 1] = [ + |v: &mut u32| *v += 1, + //[a]~^ ERROR non-capturing closure to fn coercion is experimental + //[a]~^^ ERROR mismatched types + ]; +} + +#[cfg(b)] +mod b { + fn func_specific() -> (fn() -> u32) { + || return 42 + //[b]~^ ERROR non-capturing closure to fn coercion is experimental + //[b]~^^ ERROR mismatched types + } + fn foo() { + // Items + assert_eq!(func_specific()(), 42); + let foo: fn(u8) -> u8 = |v: u8| { v }; + //[b]~^ ERROR non-capturing closure to fn coercion is experimental + //[b]~^^ ERROR mismatched types + } + +} + + + diff --git a/src/test/run-pass/closure-to-fn-coercion.rs b/src/test/run-pass/closure-to-fn-coercion.rs new file mode 100644 index 0000000000000..13d1d6aa13900 --- /dev/null +++ b/src/test/run-pass/closure-to-fn-coercion.rs @@ -0,0 +1,41 @@ +// Copyright 2017 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. + +// ignore-stage0: new feature, remove this when SNAP + +#![feature(closure_to_fn_coercion)] + +const FOO: fn(u8) -> u8 = |v: u8| { v }; + +const BAR: [fn(&mut u32); 5] = [ + |_: &mut u32| {}, + |v: &mut u32| *v += 1, + |v: &mut u32| *v += 2, + |v: &mut u32| *v += 3, + |v: &mut u32| *v += 4, +]; +fn func_specific() -> (fn() -> u32) { + || return 42 +} + +fn main() { + // Items + assert_eq!(func_specific()(), 42); + let foo: fn(u8) -> u8 = |v: u8| { v }; + assert_eq!(foo(31), 31); + // Constants + assert_eq!(FOO(31), 31); + let mut a: u32 = 0; + assert_eq!({ BAR[0](&mut a); a }, 0); + assert_eq!({ BAR[1](&mut a); a }, 1); + assert_eq!({ BAR[2](&mut a); a }, 3); + assert_eq!({ BAR[3](&mut a); a }, 6); + assert_eq!({ BAR[4](&mut a); a }, 10); +}