Rollup merge of #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 #39817
This commit is contained in:
commit
c3075f3d96
22 changed files with 275 additions and 3 deletions
|
@ -715,6 +715,7 @@ impl<'a, 'gcx, 'tcx> ExprUseVisitor<'a, 'gcx, 'tcx> {
|
||||||
adjustment::Adjust::NeverToAny |
|
adjustment::Adjust::NeverToAny |
|
||||||
adjustment::Adjust::ReifyFnPointer |
|
adjustment::Adjust::ReifyFnPointer |
|
||||||
adjustment::Adjust::UnsafeFnPointer |
|
adjustment::Adjust::UnsafeFnPointer |
|
||||||
|
adjustment::Adjust::ClosureFnPointer |
|
||||||
adjustment::Adjust::MutToConstPointer => {
|
adjustment::Adjust::MutToConstPointer => {
|
||||||
// Creating a closure/fn-pointer or unsizing consumes
|
// Creating a closure/fn-pointer or unsizing consumes
|
||||||
// the input and stores it into the resulting rvalue.
|
// the input and stores it into the resulting rvalue.
|
||||||
|
|
|
@ -464,6 +464,7 @@ impl<'a, 'gcx, 'tcx> MemCategorizationContext<'a, 'gcx, 'tcx> {
|
||||||
adjustment::Adjust::NeverToAny |
|
adjustment::Adjust::NeverToAny |
|
||||||
adjustment::Adjust::ReifyFnPointer |
|
adjustment::Adjust::ReifyFnPointer |
|
||||||
adjustment::Adjust::UnsafeFnPointer |
|
adjustment::Adjust::UnsafeFnPointer |
|
||||||
|
adjustment::Adjust::ClosureFnPointer |
|
||||||
adjustment::Adjust::MutToConstPointer |
|
adjustment::Adjust::MutToConstPointer |
|
||||||
adjustment::Adjust::DerefRef {..} => {
|
adjustment::Adjust::DerefRef {..} => {
|
||||||
debug!("cat_expr({:?}): {:?}",
|
debug!("cat_expr({:?}): {:?}",
|
||||||
|
|
|
@ -1022,6 +1022,9 @@ pub enum CastKind {
|
||||||
/// Convert unique, zero-sized type for a fn to fn()
|
/// Convert unique, zero-sized type for a fn to fn()
|
||||||
ReifyFnPointer,
|
ReifyFnPointer,
|
||||||
|
|
||||||
|
/// Convert non capturing closure to fn()
|
||||||
|
ClosureFnPointer,
|
||||||
|
|
||||||
/// Convert safe fn() to unsafe fn()
|
/// Convert safe fn() to unsafe fn()
|
||||||
UnsafeFnPointer,
|
UnsafeFnPointer,
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,9 @@ pub enum Adjust<'tcx> {
|
||||||
/// Go from a safe fn pointer to an unsafe fn pointer.
|
/// Go from a safe fn pointer to an unsafe fn pointer.
|
||||||
UnsafeFnPointer,
|
UnsafeFnPointer,
|
||||||
|
|
||||||
|
// Go from a non-capturing closure to an fn pointer.
|
||||||
|
ClosureFnPointer,
|
||||||
|
|
||||||
/// Go from a mut raw pointer to a const raw pointer.
|
/// Go from a mut raw pointer to a const raw pointer.
|
||||||
MutToConstPointer,
|
MutToConstPointer,
|
||||||
|
|
||||||
|
@ -120,6 +123,7 @@ impl<'tcx> Adjustment<'tcx> {
|
||||||
|
|
||||||
Adjust::ReifyFnPointer |
|
Adjust::ReifyFnPointer |
|
||||||
Adjust::UnsafeFnPointer |
|
Adjust::UnsafeFnPointer |
|
||||||
|
Adjust::ClosureFnPointer |
|
||||||
Adjust::MutToConstPointer |
|
Adjust::MutToConstPointer |
|
||||||
Adjust::DerefRef {..} => false,
|
Adjust::DerefRef {..} => false,
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,6 +99,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
ExprKind::Use { .. } |
|
ExprKind::Use { .. } |
|
||||||
ExprKind::NeverToAny { .. } |
|
ExprKind::NeverToAny { .. } |
|
||||||
ExprKind::ReifyFnPointer { .. } |
|
ExprKind::ReifyFnPointer { .. } |
|
||||||
|
ExprKind::ClosureFnPointer { .. } |
|
||||||
ExprKind::UnsafeFnPointer { .. } |
|
ExprKind::UnsafeFnPointer { .. } |
|
||||||
ExprKind::Unsize { .. } |
|
ExprKind::Unsize { .. } |
|
||||||
ExprKind::Repeat { .. } |
|
ExprKind::Repeat { .. } |
|
||||||
|
|
|
@ -112,6 +112,10 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
let source = unpack!(block = this.as_operand(block, source));
|
let source = unpack!(block = this.as_operand(block, source));
|
||||||
block.and(Rvalue::Cast(CastKind::UnsafeFnPointer, source, expr.ty))
|
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 } => {
|
ExprKind::Unsize { source } => {
|
||||||
let source = unpack!(block = this.as_operand(block, source));
|
let source = unpack!(block = this.as_operand(block, source));
|
||||||
block.and(Rvalue::Cast(CastKind::Unsize, source, expr.ty))
|
block.and(Rvalue::Cast(CastKind::Unsize, source, expr.ty))
|
||||||
|
|
|
@ -70,6 +70,7 @@ impl Category {
|
||||||
ExprKind::Cast { .. } |
|
ExprKind::Cast { .. } |
|
||||||
ExprKind::Use { .. } |
|
ExprKind::Use { .. } |
|
||||||
ExprKind::ReifyFnPointer { .. } |
|
ExprKind::ReifyFnPointer { .. } |
|
||||||
|
ExprKind::ClosureFnPointer { .. } |
|
||||||
ExprKind::UnsafeFnPointer { .. } |
|
ExprKind::UnsafeFnPointer { .. } |
|
||||||
ExprKind::Unsize { .. } |
|
ExprKind::Unsize { .. } |
|
||||||
ExprKind::Repeat { .. } |
|
ExprKind::Repeat { .. } |
|
||||||
|
|
|
@ -244,6 +244,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> {
|
||||||
ExprKind::Cast { .. } |
|
ExprKind::Cast { .. } |
|
||||||
ExprKind::Use { .. } |
|
ExprKind::Use { .. } |
|
||||||
ExprKind::ReifyFnPointer { .. } |
|
ExprKind::ReifyFnPointer { .. } |
|
||||||
|
ExprKind::ClosureFnPointer { .. } |
|
||||||
ExprKind::UnsafeFnPointer { .. } |
|
ExprKind::UnsafeFnPointer { .. } |
|
||||||
ExprKind::Unsize { .. } |
|
ExprKind::Unsize { .. } |
|
||||||
ExprKind::Repeat { .. } |
|
ExprKind::Repeat { .. } |
|
||||||
|
|
|
@ -60,6 +60,15 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
|
||||||
kind: ExprKind::UnsafeFnPointer { source: expr.to_ref() },
|
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)) => {
|
Some((ty::adjustment::Adjust::NeverToAny, adjusted_ty)) => {
|
||||||
expr = Expr {
|
expr = Expr {
|
||||||
temp_lifetime: temp_lifetime,
|
temp_lifetime: temp_lifetime,
|
||||||
|
|
|
@ -152,6 +152,9 @@ pub enum ExprKind<'tcx> {
|
||||||
ReifyFnPointer {
|
ReifyFnPointer {
|
||||||
source: ExprRef<'tcx>,
|
source: ExprRef<'tcx>,
|
||||||
},
|
},
|
||||||
|
ClosureFnPointer {
|
||||||
|
source: ExprRef<'tcx>,
|
||||||
|
},
|
||||||
UnsafeFnPointer {
|
UnsafeFnPointer {
|
||||||
source: ExprRef<'tcx>,
|
source: ExprRef<'tcx>,
|
||||||
},
|
},
|
||||||
|
|
|
@ -619,6 +619,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
|
||||||
Rvalue::CheckedBinaryOp(..) |
|
Rvalue::CheckedBinaryOp(..) |
|
||||||
Rvalue::Cast(CastKind::ReifyFnPointer, ..) |
|
Rvalue::Cast(CastKind::ReifyFnPointer, ..) |
|
||||||
Rvalue::Cast(CastKind::UnsafeFnPointer, ..) |
|
Rvalue::Cast(CastKind::UnsafeFnPointer, ..) |
|
||||||
|
Rvalue::Cast(CastKind::ClosureFnPointer, ..) |
|
||||||
Rvalue::Cast(CastKind::Unsize, ..) => {}
|
Rvalue::Cast(CastKind::Unsize, ..) => {}
|
||||||
|
|
||||||
Rvalue::Len(_) => {
|
Rvalue::Len(_) => {
|
||||||
|
|
|
@ -447,6 +447,7 @@ fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Exp
|
||||||
Some(Adjust::NeverToAny) |
|
Some(Adjust::NeverToAny) |
|
||||||
Some(Adjust::ReifyFnPointer) |
|
Some(Adjust::ReifyFnPointer) |
|
||||||
Some(Adjust::UnsafeFnPointer) |
|
Some(Adjust::UnsafeFnPointer) |
|
||||||
|
Some(Adjust::ClosureFnPointer) |
|
||||||
Some(Adjust::MutToConstPointer) => {}
|
Some(Adjust::MutToConstPointer) => {}
|
||||||
|
|
||||||
Some(Adjust::DerefRef { autoderefs, .. }) => {
|
Some(Adjust::DerefRef { autoderefs, .. }) => {
|
||||||
|
|
|
@ -1188,7 +1188,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
assert!(fn_ty.kind() == llvm::TypeKind::Function,
|
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();
|
let param_tys = fn_ty.func_params();
|
||||||
|
|
||||||
|
|
|
@ -489,6 +489,20 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
|
||||||
self.output);
|
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(..) => {
|
mir::Rvalue::Box(..) => {
|
||||||
let exchange_malloc_fn_def_id =
|
let exchange_malloc_fn_def_id =
|
||||||
self.scx
|
self.scx
|
||||||
|
|
|
@ -20,7 +20,7 @@ use rustc::mir;
|
||||||
use rustc::mir::tcx::LvalueTy;
|
use rustc::mir::tcx::LvalueTy;
|
||||||
use rustc::ty::{self, layout, Ty, TyCtxt, TypeFoldable};
|
use rustc::ty::{self, layout, Ty, TyCtxt, TypeFoldable};
|
||||||
use rustc::ty::cast::{CastTy, IntTy};
|
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 rustc_data_structures::indexed_vec::{Idx, IndexVec};
|
||||||
use {abi, adt, base, Disr, machine};
|
use {abi, adt, base, Disr, machine};
|
||||||
use callee::Callee;
|
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 => {
|
mir::CastKind::UnsafeFnPointer => {
|
||||||
// this is a no-op at the LLVM level
|
// this is a no-op at the LLVM level
|
||||||
operand.llval
|
operand.llval
|
||||||
|
|
|
@ -12,6 +12,7 @@ use llvm::{self, ValueRef};
|
||||||
use rustc::ty::{self, Ty};
|
use rustc::ty::{self, Ty};
|
||||||
use rustc::ty::cast::{CastTy, IntTy};
|
use rustc::ty::cast::{CastTy, IntTy};
|
||||||
use rustc::ty::layout::Layout;
|
use rustc::ty::layout::Layout;
|
||||||
|
use rustc::ty::subst::Kind;
|
||||||
use rustc::mir::tcx::LvalueTy;
|
use rustc::mir::tcx::LvalueTy;
|
||||||
use rustc::mir;
|
use rustc::mir;
|
||||||
use middle::lang_items::ExchangeMallocFnLangItem;
|
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 => {
|
mir::CastKind::UnsafeFnPointer => {
|
||||||
// this is a no-op at the LLVM level
|
// this is a no-op at the LLVM level
|
||||||
operand.val
|
operand.val
|
||||||
|
|
|
@ -63,13 +63,17 @@
|
||||||
use check::FnCtxt;
|
use check::FnCtxt;
|
||||||
|
|
||||||
use rustc::hir;
|
use rustc::hir;
|
||||||
|
use rustc::hir::def_id::DefId;
|
||||||
use rustc::infer::{Coercion, InferOk, TypeTrace};
|
use rustc::infer::{Coercion, InferOk, TypeTrace};
|
||||||
use rustc::traits::{self, ObligationCause, ObligationCauseCode};
|
use rustc::traits::{self, ObligationCause, ObligationCauseCode};
|
||||||
use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow};
|
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::fold::TypeFoldable;
|
||||||
use rustc::ty::error::TypeError;
|
use rustc::ty::error::TypeError;
|
||||||
use rustc::ty::relate::RelateResult;
|
use rustc::ty::relate::RelateResult;
|
||||||
|
use syntax::abi;
|
||||||
|
use syntax::feature_gate;
|
||||||
use util::common::indent;
|
use util::common::indent;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
@ -196,6 +200,11 @@ impl<'f, 'gcx, 'tcx> Coerce<'f, 'gcx, 'tcx> {
|
||||||
// unsafe qualifier.
|
// unsafe qualifier.
|
||||||
self.coerce_from_fn_pointer(a, a_f, b)
|
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.
|
// Otherwise, just use unification rules.
|
||||||
self.unify_and_identity(a, b)
|
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,
|
fn coerce_unsafe_ptr(&self,
|
||||||
a: Ty<'tcx>,
|
a: Ty<'tcx>,
|
||||||
b: Ty<'tcx>,
|
b: Ty<'tcx>,
|
||||||
|
|
|
@ -412,6 +412,10 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
|
||||||
adjustment::Adjust::MutToConstPointer
|
adjustment::Adjust::MutToConstPointer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
adjustment::Adjust::ClosureFnPointer => {
|
||||||
|
adjustment::Adjust::ClosureFnPointer
|
||||||
|
}
|
||||||
|
|
||||||
adjustment::Adjust::UnsafeFnPointer => {
|
adjustment::Adjust::UnsafeFnPointer => {
|
||||||
adjustment::Adjust::UnsafeFnPointer
|
adjustment::Adjust::UnsafeFnPointer
|
||||||
}
|
}
|
||||||
|
|
|
@ -326,6 +326,10 @@ declare_features! (
|
||||||
// `extern "msp430-interrupt" fn()`
|
// `extern "msp430-interrupt" fn()`
|
||||||
(active, abi_msp430_interrupt, "1.16.0", Some(38487)),
|
(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
|
// Used to identify crates that contain sanitizer runtimes
|
||||||
// rustc internal
|
// rustc internal
|
||||||
(active, sanitizer_runtime, "1.17.0", None),
|
(active, sanitizer_runtime, "1.17.0", None),
|
||||||
|
@ -982,6 +986,9 @@ pub const EXPLAIN_DERIVE_UNDERSCORE: &'static str =
|
||||||
pub const EXPLAIN_PLACEMENT_IN: &'static str =
|
pub const EXPLAIN_PLACEMENT_IN: &'static str =
|
||||||
"placement-in expression syntax is experimental and subject to change.";
|
"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> {
|
struct PostExpansionVisitor<'a> {
|
||||||
context: &'a Context<'a>,
|
context: &'a Context<'a>,
|
||||||
}
|
}
|
||||||
|
|
24
src/test/compile-fail/closure-no-fn.rs
Normal file
24
src/test/compile-fail/closure-no-fn.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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
|
||||||
|
}
|
45
src/test/compile-fail/feature-gate-closure_to_fn_coercion.rs
Normal file
45
src/test/compile-fail/feature-gate-closure_to_fn_coercion.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
41
src/test/run-pass/closure-to-fn-coercion.rs
Normal file
41
src/test/run-pass/closure-to-fn-coercion.rs
Normal file
|
@ -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 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue