From 1b9b32288369dbd13a9d721d6765ddc30d29e8c1 Mon Sep 17 00:00:00 2001 From: est31 Date: Wed, 22 Feb 2017 01:24:16 +0100 Subject: [PATCH 1/4] Implement non-capturing closure to fn coercion --- src/librustc/middle/expr_use_visitor.rs | 1 + src/librustc/middle/mem_categorization.rs | 1 + src/librustc/mir/mod.rs | 3 ++ src/librustc/ty/adjustment.rs | 4 ++ src/librustc_mir/build/expr/as_lvalue.rs | 1 + src/librustc_mir/build/expr/as_rvalue.rs | 4 ++ src/librustc_mir/build/expr/category.rs | 1 + src/librustc_mir/build/expr/into.rs | 1 + src/librustc_mir/hair/cx/expr.rs | 9 ++++ src/librustc_mir/hair/mod.rs | 3 ++ src/librustc_mir/transform/qualify_consts.rs | 1 + src/librustc_passes/consts.rs | 1 + src/librustc_trans/builder.rs | 2 +- src/librustc_trans/collector.rs | 14 +++++ src/librustc_trans/mir/constant.rs | 29 ++++++++++ src/librustc_trans/mir/rvalue.rs | 31 +++++++++++ src/librustc_typeck/check/coercion.rs | 57 +++++++++++++++++++- src/librustc_typeck/check/writeback.rs | 4 ++ src/test/run-pass/closure-to-fn-coercion.rs | 41 ++++++++++++++ 19 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 src/test/run-pass/closure-to-fn-coercion.rs 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..e7582061b5419 100644 --- a/src/librustc_trans/mir/constant.rs +++ b/src/librustc_trans/mir/constant.rs @@ -578,6 +578,35 @@ 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 = Substs::for_item(tcx, + call_once, + |_, _| {bug!()}, + |def, _| { match def.index { + 0 => operand.ty.clone(), + 1 => input.skip_binder(), + _ => bug!(), + } } + ); + + 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..95bb1dd7e0f34 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::Substs; use rustc::mir::tcx::LvalueTy; use rustc::mir; use middle::lang_items::ExchangeMallocFnLangItem; @@ -190,6 +191,36 @@ 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 = Substs::for_item(bcx.tcx(), + call_once, + |_, _| {bug!()}, + |def, _| { match def.index { + 0 => operand.ty.clone(), + 1 => input.skip_binder(), + _ => bug!(), + } } + ); + + 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..e9ac0a58d3644 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, TypeVariants, TypeAndMut, + Ty, ClosureSubsts}; use rustc::ty::fold::TypeFoldable; use rustc::ty::error::TypeError; use rustc::ty::relate::RelateResult; +use syntax::ast::NodeId; +use syntax::abi; 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,52 @@ 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 :NodeId = 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()) => { + // 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.input(0).map_bound(|v| { + let params_iter = match v.sty { + TypeVariants::TyTuple(params, _) => { + params.into_iter().cloned() + } + _ => bug!(), + }; + self.tcx.mk_fn_sig(params_iter, + sig.output().skip_binder(), + sig.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/test/run-pass/closure-to-fn-coercion.rs b/src/test/run-pass/closure-to-fn-coercion.rs new file mode 100644 index 0000000000000..c4d0bbdd0704c --- /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); +} From f753a6ef0252689930fbfdf39859bf80c0388955 Mon Sep 17 00:00:00 2001 From: est31 Date: Thu, 23 Feb 2017 21:35:12 +0100 Subject: [PATCH 2/4] Feature gate --- src/librustc_typeck/check/coercion.rs | 9 +++++++++ src/libsyntax/feature_gate.rs | 7 +++++++ src/test/run-pass/closure-to-fn-coercion.rs | 2 +- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index e9ac0a58d3644..ab498b453dde1 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -74,6 +74,7 @@ use rustc::ty::error::TypeError; use rustc::ty::relate::RelateResult; use syntax::ast::NodeId; use syntax::abi; +use syntax::feature_gate; use util::common::indent; use std::cell::RefCell; @@ -575,6 +576,14 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> { let node_id_a :NodeId = 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 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/run-pass/closure-to-fn-coercion.rs b/src/test/run-pass/closure-to-fn-coercion.rs index c4d0bbdd0704c..f3f0736041b54 100644 --- a/src/test/run-pass/closure-to-fn-coercion.rs +++ b/src/test/run-pass/closure-to-fn-coercion.rs @@ -10,7 +10,7 @@ // ignore-stage0: new feature, remove this when SNAP -// #![feature(closure_to_fn_coercion)] +#![feature(closure_to_fn_coercion)] const FOO :fn(u8) -> u8 = |v: u8| { v }; From 21c38988f49629d1a055734e46ee739bafe6540d Mon Sep 17 00:00:00 2001 From: est31 Date: Thu, 23 Feb 2017 22:31:21 +0100 Subject: [PATCH 3/4] Two more tests --- src/test/compile-fail/closure-no-fn.rs | 24 ++++++++++ .../feature-gate-closure_to_fn_coercion.rs | 45 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 src/test/compile-fail/closure-no-fn.rs create mode 100644 src/test/compile-fail/feature-gate-closure_to_fn_coercion.rs 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..6ad65523833a9 --- /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..20ed24f025b89 --- /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 + } + +} + + + From 77f131da1afcaf5582259e7a15048105fea9591d Mon Sep 17 00:00:00 2001 From: est31 Date: Fri, 24 Feb 2017 00:32:21 +0100 Subject: [PATCH 4/4] Review changes * use more convenient mk_substs function * remove type annotations * use map_bound one level farther outside * style improvements --- src/librustc_trans/mir/constant.rs | 14 +++----------- src/librustc_trans/mir/rvalue.rs | 14 +++----------- src/librustc_typeck/check/coercion.rs | 15 +++++++-------- src/test/compile-fail/closure-no-fn.rs | 6 +++--- .../feature-gate-closure_to_fn_coercion.rs | 4 ++-- src/test/run-pass/closure-to-fn-coercion.rs | 16 ++++++++-------- 6 files changed, 26 insertions(+), 43 deletions(-) diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs index e7582061b5419..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; @@ -589,16 +589,8 @@ impl<'a, 'tcx> MirConstContext<'a, 'tcx> { .unwrap().def_id; // Now create its substs [Closure, Tuple] let input = tcx.closure_type(def_id, substs).sig.input(0); - let substs = Substs::for_item(tcx, - call_once, - |_, _| {bug!()}, - |def, _| { match def.index { - 0 => operand.ty.clone(), - 1 => input.skip_binder(), - _ => bug!(), - } } - ); - + 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) } diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs index 95bb1dd7e0f34..6f6d81a25350f 100644 --- a/src/librustc_trans/mir/rvalue.rs +++ b/src/librustc_trans/mir/rvalue.rs @@ -12,7 +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::Substs; +use rustc::ty::subst::Kind; use rustc::mir::tcx::LvalueTy; use rustc::mir; use middle::lang_items::ExchangeMallocFnLangItem; @@ -202,16 +202,8 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> { .unwrap().def_id; // Now create its substs [Closure, Tuple] let input = bcx.tcx().closure_type(def_id, substs).sig.input(0); - let substs = Substs::for_item(bcx.tcx(), - call_once, - |_, _| {bug!()}, - |def, _| { match def.index { - 0 => operand.ty.clone(), - 1 => input.skip_binder(), - _ => bug!(), - } } - ); - + 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)) diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index ab498b453dde1..424e3143929f4 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -67,12 +67,11 @@ 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, TypeVariants, TypeAndMut, +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::ast::NodeId; use syntax::abi; use syntax::feature_gate; use util::common::indent; @@ -573,7 +572,7 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> { let b = self.shallow_resolve(b); - let node_id_a :NodeId = self.tcx.hir.as_local_node_id(def_id_a).unwrap(); + 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 { @@ -589,16 +588,16 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> { // to // `fn(arg0,arg1,...) -> _` let sig = self.closure_type(def_id_a, substs_a).sig; - let converted_sig = sig.input(0).map_bound(|v| { - let params_iter = match v.sty { - TypeVariants::TyTuple(params, _) => { + 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, - sig.output().skip_binder(), - sig.variadic()) + s.output(), + s.variadic) }); let fn_ty = self.tcx.mk_bare_fn(ty::BareFnTy { unsafety: hir::Unsafety::Normal, diff --git a/src/test/compile-fail/closure-no-fn.rs b/src/test/compile-fail/closure-no-fn.rs index 6ad65523833a9..fe179e8a48f16 100644 --- a/src/test/compile-fail/closure-no-fn.rs +++ b/src/test/compile-fail/closure-no-fn.rs @@ -13,12 +13,12 @@ fn main() { let mut a = 0u8; - let foo :fn(u8) -> u8 = |v: u8| { a += v; a }; + let foo: fn(u8) -> u8 = |v: u8| { a += v; a }; //~^ ERROR mismatched types let b = 0u8; - let bar :fn() -> u8 = || { b }; + let bar: fn() -> u8 = || { b }; //~^ ERROR mismatched types - let baz :fn() -> u8 = || { b } as fn() -> u8; + 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 index 20ed24f025b89..d074a35628ea8 100644 --- a/src/test/compile-fail/feature-gate-closure_to_fn_coercion.rs +++ b/src/test/compile-fail/feature-gate-closure_to_fn_coercion.rs @@ -13,7 +13,7 @@ #[cfg(a)] mod a { - const FOO :fn(u8) -> u8 = |v: u8| { v }; + const FOO: fn(u8) -> u8 = |v: u8| { v }; //[a]~^ ERROR non-capturing closure to fn coercion is experimental //[a]~^^ ERROR mismatched types @@ -34,7 +34,7 @@ mod b { fn foo() { // Items assert_eq!(func_specific()(), 42); - let foo :fn(u8) -> u8 = |v: u8| { v }; + 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 index f3f0736041b54..13d1d6aa13900 100644 --- a/src/test/run-pass/closure-to-fn-coercion.rs +++ b/src/test/run-pass/closure-to-fn-coercion.rs @@ -12,7 +12,7 @@ #![feature(closure_to_fn_coercion)] -const FOO :fn(u8) -> u8 = |v: u8| { v }; +const FOO: fn(u8) -> u8 = |v: u8| { v }; const BAR: [fn(&mut u32); 5] = [ |_: &mut u32| {}, @@ -28,14 +28,14 @@ fn func_specific() -> (fn() -> u32) { fn main() { // Items assert_eq!(func_specific()(), 42); - let foo :fn(u8) -> u8 = |v: u8| { v }; + 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); + 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); }