提交 8405770b 编写于 作者: O Oliver Schneider

Rustup to rustc 1.17.0-nightly (be760566 2017-02-28)

上级 8878a403
use std::error::Error;
use std::fmt;
use rustc::mir;
use rustc::ty::{BareFnTy, Ty, FnSig, layout};
use syntax::abi::Abi;
use rustc::ty::{PolyFnSig, Ty, layout};
use memory::{Pointer, Function};
use rustc_const_math::ConstMathErr;
use syntax::codemap::Span;
#[derive(Clone, Debug)]
pub enum EvalError<'tcx> {
FunctionPointerTyMismatch(Abi, &'tcx FnSig<'tcx>, &'tcx BareFnTy<'tcx>),
FunctionPointerTyMismatch(PolyFnSig<'tcx>, PolyFnSig<'tcx>),
NoMirFor(String),
UnterminatedCString(Pointer),
DanglingPointerDeref,
......@@ -151,8 +150,8 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
ptr.offset, ptr.offset + size, ptr.alloc_id, allocation_size)
},
EvalError::NoMirFor(ref func) => write!(f, "no mir for `{}`", func),
EvalError::FunctionPointerTyMismatch(abi, sig, got) =>
write!(f, "tried to call a function with abi {:?} and sig {:?} through a function pointer of type {:?}", abi, sig, got),
EvalError::FunctionPointerTyMismatch(sig, got) =>
write!(f, "tried to call a function with sig {} through a function pointer of type {}", sig.skip_binder(), got.skip_binder()),
EvalError::ArrayIndexOutOfBounds(span, len, index) =>
write!(f, "index out of bounds: the len is {} but the index is {} at {:?}", len, index, span),
EvalError::Math(span, ref err) =>
......
......@@ -181,8 +181,6 @@ pub(super) fn const_to_value(&mut self, const_val: &ConstVal) -> EvalResult<'tcx
Float(ConstFloat::F32(f)) => PrimVal::from_f32(f),
Float(ConstFloat::F64(f)) => PrimVal::from_f64(f),
Float(ConstFloat::FInfer { .. }) =>
bug!("uninferred constants only exist before typeck"),
Bool(b) => PrimVal::from_bool(b),
Char(c) => PrimVal::from_char(c),
......@@ -196,7 +194,7 @@ pub(super) fn const_to_value(&mut self, const_val: &ConstVal) -> EvalResult<'tcx
Struct(_) => unimplemented!(),
Tuple(_) => unimplemented!(),
Function(_) => unimplemented!(),
Function(_, _) => unimplemented!(),
Array(_) => unimplemented!(),
Repeat(_, _) => unimplemented!(),
};
......@@ -457,7 +455,10 @@ pub(super) fn eval_rvalue_into_lvalue(
General { discr, ref variants, .. } => {
if let mir::AggregateKind::Adt(adt_def, variant, _, _) = *kind {
let discr_val = adt_def.variants[variant].disr_val;
let discr_val = adt_def.discriminants(self.tcx)
.nth(variant)
.expect("broken mir: Adt variant id invalid")
.to_u128_unchecked();
let discr_size = discr.size().bytes();
if variants[variant].packed {
let ptr = self.force_allocation(dest)?.to_ptr_and_extra().0;
......@@ -530,7 +531,10 @@ pub(super) fn eval_rvalue_into_lvalue(
CEnum { .. } => {
assert_eq!(operands.len(), 0);
if let mir::AggregateKind::Adt(adt_def, variant, _, _) = *kind {
let n = adt_def.variants[variant].disr_val;
let n = adt_def.discriminants(self.tcx)
.nth(variant)
.expect("broken mir: Adt variant index invalid")
.to_u128_unchecked();
self.write_primval(dest, PrimVal::Bytes(n), dest_ty)?;
} else {
bug!("tried to assign {:?} to Layout::CEnum", kind);
......@@ -640,9 +644,8 @@ pub(super) fn eval_rvalue_into_lvalue(
}
ReifyFnPointer => match self.operand_ty(operand).sty {
ty::TyFnDef(def_id, substs, fn_ty) => {
let fn_ty = self.tcx.erase_regions(&fn_ty);
let fn_ptr = self.memory.create_fn_ptr(self.tcx,def_id, substs, fn_ty);
ty::TyFnDef(def_id, substs, sig) => {
let fn_ptr = self.memory.create_fn_ptr(def_id, substs, sig);
self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?;
},
ref other => bug!("reify fn pointer on {:?}", other),
......@@ -658,8 +661,8 @@ pub(super) fn eval_rvalue_into_lvalue(
ClosureFnPointer => match self.operand_ty(operand).sty {
ty::TyClosure(def_id, substs) => {
let fn_ty = self.tcx.closure_type(def_id, substs);
let fn_ptr = self.memory.create_fn_ptr_from_noncapture_closure(self.tcx, def_id, substs, fn_ty);
let fn_ty = self.tcx.closure_type(def_id);
let fn_ptr = self.memory.create_fn_ptr_from_noncapture_closure(def_id, substs, fn_ty);
self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?;
},
ref other => bug!("reify fn pointer on {:?}", other),
......@@ -673,7 +676,7 @@ pub(super) fn eval_rvalue_into_lvalue(
let ptr = self.force_allocation(lval)?.to_ptr();
let discr_val = self.read_discriminant_value(ptr, ty)?;
if let ty::TyAdt(adt_def, _) = ty.sty {
if adt_def.variants.iter().all(|v| discr_val != v.disr_val) {
if adt_def.discriminants(self.tcx).all(|v| discr_val != v.to_u128_unchecked()) {
return Err(EvalError::InvalidDiscriminant);
}
} else {
......
......@@ -3,12 +3,10 @@
use std::{fmt, iter, ptr, mem, io};
use rustc::hir::def_id::DefId;
use rustc::ty::{self, BareFnTy, ClosureTy, ClosureSubsts, TyCtxt};
use rustc::ty::{self, PolyFnSig, ClosureSubsts};
use rustc::ty::subst::Substs;
use rustc::ty::layout::{self, TargetDataLayout};
use syntax::abi::Abi;
use error::{EvalError, EvalResult};
use value::PrimVal;
......@@ -109,8 +107,7 @@ pub fn never_ptr() -> Self {
pub struct FunctionDefinition<'tcx> {
pub def_id: DefId,
pub substs: &'tcx Substs<'tcx>,
pub abi: Abi,
pub sig: &'tcx ty::FnSig<'tcx>,
pub sig: PolyFnSig<'tcx>,
}
/// Either a concrete function, or a glue function
......@@ -127,7 +124,7 @@ pub enum Function<'tcx> {
DropGlue(ty::Ty<'tcx>),
/// Glue required to treat the ptr part of a fat pointer
/// as a function pointer
FnPtrAsTraitObject(&'tcx ty::FnSig<'tcx>),
FnPtrAsTraitObject(PolyFnSig<'tcx>),
/// Glue for Closures
Closure(FunctionDefinition<'tcx>),
/// Glue for noncapturing closures casted to function pointers
......@@ -217,67 +214,43 @@ pub fn allocations(&self) -> ::std::collections::hash_map::Iter<AllocId, Allocat
self.alloc_map.iter()
}
pub fn create_closure_ptr(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: ClosureSubsts<'tcx>, fn_ty: ClosureTy<'tcx>) -> Pointer {
// FIXME: this is a hack
let fn_ty = tcx.mk_bare_fn(ty::BareFnTy {
unsafety: fn_ty.unsafety,
abi: fn_ty.abi,
sig: fn_ty.sig,
});
pub fn create_closure_ptr(&mut self, def_id: DefId, substs: ClosureSubsts<'tcx>, sig: PolyFnSig<'tcx>) -> Pointer {
self.create_fn_alloc(Function::Closure(FunctionDefinition {
def_id,
substs: substs.substs,
abi: fn_ty.abi,
// FIXME: why doesn't this compile?
//sig: tcx.erase_late_bound_regions(&fn_ty.sig),
sig: fn_ty.sig.skip_binder(),
sig,
}))
}
pub fn create_fn_ptr_from_noncapture_closure(&mut self, tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: ClosureSubsts<'tcx>, fn_ty: ClosureTy<'tcx>) -> Pointer {
// FIXME: this is a hack
let fn_ty = tcx.mk_bare_fn(ty::BareFnTy {
unsafety: fn_ty.unsafety,
abi: fn_ty.abi,
sig: fn_ty.sig,
});
pub fn create_fn_ptr_from_noncapture_closure(&mut self, def_id: DefId, substs: ClosureSubsts<'tcx>, sig: PolyFnSig<'tcx>) -> Pointer {
self.create_fn_alloc(Function::NonCaptureClosureAsFnPtr(FunctionDefinition {
def_id,
substs: substs.substs,
abi: Abi::Rust, // adjust abi
// FIXME: why doesn't this compile?
//sig: tcx.erase_late_bound_regions(&fn_ty.sig),
sig: fn_ty.sig.skip_binder(),
sig,
}))
}
pub fn create_fn_as_trait_glue(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer {
pub fn create_fn_as_trait_glue(&mut self, def_id: DefId, substs: &'tcx Substs, sig: PolyFnSig<'tcx>) -> Pointer {
self.create_fn_alloc(Function::FnDefAsTraitObject(FunctionDefinition {
def_id,
substs,
abi: fn_ty.abi,
// FIXME: why doesn't this compile?
//sig: tcx.erase_late_bound_regions(&fn_ty.sig),
sig: fn_ty.sig.skip_binder(),
sig,
}))
}
pub fn create_fn_ptr_as_trait_glue(&mut self, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer {
self.create_fn_alloc(Function::FnPtrAsTraitObject(fn_ty.sig.skip_binder()))
pub fn create_fn_ptr_as_trait_glue(&mut self, sig: PolyFnSig<'tcx>) -> Pointer {
self.create_fn_alloc(Function::FnPtrAsTraitObject(sig))
}
pub fn create_drop_glue(&mut self, ty: ty::Ty<'tcx>) -> Pointer {
self.create_fn_alloc(Function::DropGlue(ty))
}
pub fn create_fn_ptr(&mut self, _tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, substs: &'tcx Substs<'tcx>, fn_ty: &'tcx BareFnTy<'tcx>) -> Pointer {
pub fn create_fn_ptr(&mut self, def_id: DefId, substs: &'tcx Substs, sig: PolyFnSig<'tcx>) -> Pointer {
self.create_fn_alloc(Function::Concrete(FunctionDefinition {
def_id,
substs,
abi: fn_ty.abi,
// FIXME: why doesn't this compile?
//sig: tcx.erase_late_bound_regions(&fn_ty.sig),
sig: fn_ty.sig.skip_binder(),
sig,
}))
}
......@@ -623,12 +596,7 @@ pub fn leak_report(&self) -> usize {
fn dump_fn_def<'tcx>(fn_def: FunctionDefinition<'tcx>) -> String {
let name = ty::tls::with(|tcx| tcx.item_path_str(fn_def.def_id));
let abi = if fn_def.abi == Abi::Rust {
format!("")
} else {
format!("extern {} ", fn_def.abi)
};
format!("function pointer: {}: {}{}", name, abi, fn_def.sig)
format!("function pointer: {}: {}", name, fn_def.sig.skip_binder())
}
/// Byte accessors
......
......@@ -97,7 +97,7 @@ pub fn drop(
let (adt_ptr, extra) = lval.to_ptr_and_extra();
// run drop impl before the fields' drop impls
if let Some(drop_def_id) = adt_def.destructor() {
if let Some(drop_def_id) = adt_def.destructor(self.tcx) {
let trait_ref = ty::Binder(ty::TraitRef {
def_id: self.tcx.lang_items.drop_trait().unwrap(),
substs: self.tcx.mk_substs_trait(ty, &[]),
......@@ -121,7 +121,7 @@ pub fn drop(
Layout::General { .. } => {
let discr_val = self.read_discriminant_value(adt_ptr, ty)? as u128;
let ptr = self.force_allocation(lval)?.to_ptr();
match adt_def.variants.iter().position(|v| discr_val == v.disr_val) {
match adt_def.discriminants(self.tcx).position(|v| discr_val == v.to_u128_unchecked()) {
Some(i) => {
lval = Lvalue::Ptr {
ptr,
......
......@@ -3,8 +3,10 @@
use rustc::ty::layout::Layout;
use rustc::ty::subst::Substs;
use rustc::ty::{self, Ty};
use rustc_const_math::ConstInt;
use syntax::codemap::Span;
use syntax::attr;
use syntax::abi::Abi;
use error::{EvalError, EvalResult};
use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited};
......@@ -62,11 +64,9 @@ pub(super) fn eval_terminator(
let func_ty = self.operand_ty(func);
let fn_def = match func_ty.sty {
ty::TyFnPtr(bare_fn_ty) => {
ty::TyFnPtr(bare_sig) => {
let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr()?;
let fn_def = self.memory.get_fn(fn_ptr.alloc_id)?;
let bare_sig = self.tcx.erase_late_bound_regions_and_normalize(&bare_fn_ty.sig);
let bare_sig = self.tcx.erase_regions(&bare_sig);
match fn_def {
Function::Concrete(fn_def) => {
// transmuting function pointers in miri is fine as long as the number of
......@@ -74,21 +74,21 @@ pub(super) fn eval_terminator(
// FIXME: also check the size of the arguments' type and the return type
// Didn't get it to work, since that triggers an assertion in rustc which
// checks whether the type has escaping regions
if fn_def.abi != bare_fn_ty.abi ||
fn_def.sig.variadic != bare_sig.variadic ||
fn_def.sig.inputs().len() != bare_sig.inputs().len() {
return Err(EvalError::FunctionPointerTyMismatch(fn_def.abi, fn_def.sig, bare_fn_ty));
if fn_def.sig.abi() != bare_sig.abi() ||
fn_def.sig.variadic() != bare_sig.variadic() ||
fn_def.sig.inputs().skip_binder().len() != bare_sig.inputs().skip_binder().len() {
return Err(EvalError::FunctionPointerTyMismatch(fn_def.sig, bare_sig));
}
},
Function::NonCaptureClosureAsFnPtr(fn_def) => {
if fn_def.abi != bare_fn_ty.abi ||
fn_def.sig.variadic != bare_sig.variadic ||
fn_def.sig.inputs().len() != 1 {
return Err(EvalError::FunctionPointerTyMismatch(fn_def.abi, fn_def.sig, bare_fn_ty));
assert_eq!(fn_def.sig.abi(), Abi::RustCall);
if fn_def.sig.variadic() != bare_sig.variadic() ||
fn_def.sig.inputs().skip_binder().len() != 1 {
return Err(EvalError::FunctionPointerTyMismatch(fn_def.sig, bare_sig));
}
if let ty::TyTuple(fields, _) = fn_def.sig.inputs()[0].sty {
if fields.len() != bare_sig.inputs().len() {
return Err(EvalError::FunctionPointerTyMismatch(fn_def.abi, fn_def.sig, bare_fn_ty));
if let ty::TyTuple(fields, _) = fn_def.sig.inputs().skip_binder()[0].sty {
if fields.len() != bare_sig.inputs().skip_binder().len() {
return Err(EvalError::FunctionPointerTyMismatch(fn_def.sig, bare_sig));
}
}
},
......@@ -99,8 +99,7 @@ pub(super) fn eval_terminator(
ty::TyFnDef(def_id, substs, fn_ty) => Function::Concrete(FunctionDefinition {
def_id,
substs,
abi: fn_ty.abi,
sig: fn_ty.sig.skip_binder(),
sig: fn_ty,
}),
_ => {
......@@ -165,8 +164,8 @@ fn eval_fn_call(
use syntax::abi::Abi;
match fn_def {
// Intrinsics can only be addressed directly
Function::Concrete(FunctionDefinition { def_id, substs, abi: Abi::RustIntrinsic, sig }) => {
let ty = sig.output();
Function::Concrete(FunctionDefinition { def_id, substs, sig }) if sig.abi() == Abi::RustIntrinsic => {
let ty = *sig.output().skip_binder();
let layout = self.type_layout(ty)?;
let (ret, target) = match destination {
Some(dest) if is_inhabited(self.tcx, ty) => dest,
......@@ -177,8 +176,8 @@ fn eval_fn_call(
Ok(())
},
// C functions can only be addressed directly
Function::Concrete(FunctionDefinition { def_id, abi: Abi::C, sig, ..}) => {
let ty = sig.output();
Function::Concrete(FunctionDefinition { def_id, sig, ..}) if sig.abi() == Abi::C => {
let ty = *sig.output().skip_binder();
let (ret, target) = destination.unwrap();
self.call_c_abi(def_id, arg_operands, ret, ty)?;
self.dump_local(ret);
......@@ -186,8 +185,7 @@ fn eval_fn_call(
Ok(())
},
Function::DropGlue(_) => Err(EvalError::ManuallyCalledDropGlue),
Function::Concrete(FunctionDefinition { def_id, abi: Abi::RustCall, sig, substs }) |
Function::Concrete(FunctionDefinition { def_id, abi: Abi::Rust, sig, substs }) => {
Function::Concrete(FunctionDefinition { def_id, sig, substs }) if sig.abi() == Abi::Rust || sig.abi() == Abi::RustCall => {
let mut args = Vec::new();
for arg in arg_operands {
let arg_val = self.eval_operand(arg)?;
......@@ -204,20 +202,20 @@ fn eval_fn_call(
};
// FIXME(eddyb) Detect ADT constructors more efficiently.
if let Some(adt_def) = sig.output().ty_adt_def() {
if let Some(v) = adt_def.variants.iter().find(|v| resolved_def_id == v.did) {
if let Some(adt_def) = sig.output().skip_binder().ty_adt_def() {
let dids = adt_def.variants.iter().map(|v| v.did);
let discrs = adt_def.discriminants(self.tcx).map(ConstInt::to_u128_unchecked);
if let Some((_, disr_val)) = dids.zip(discrs).find(|&(did, _)| resolved_def_id == did) {
let (lvalue, target) = destination.expect("tuple struct constructors can't diverge");
let dest_ty = self.tcx.item_type(adt_def.did);
let dest_layout = self.type_layout(dest_ty)?;
trace!("layout({:?}) = {:#?}", dest_ty, dest_layout);
match *dest_layout {
Layout::Univariant { .. } => {
let disr_val = v.disr_val;
assert_eq!(disr_val, 0);
self.assign_fields(lvalue, dest_ty, args)?;
},
Layout::General { discr, ref variants, .. } => {
let disr_val = v.disr_val;
let discr_size = discr.size().bytes();
self.assign_discr_and_fields(
lvalue,
......@@ -230,7 +228,6 @@ fn eval_fn_call(
)?;
},
Layout::StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => {
let disr_val = v.disr_val;
if nndiscr as u128 == disr_val {
self.assign_fields(lvalue, dest_ty, args)?;
} else {
......@@ -268,7 +265,7 @@ fn eval_fn_call(
span,
)
},
Function::NonCaptureClosureAsFnPtr(FunctionDefinition { def_id, abi: Abi::Rust, substs, sig }) => {
Function::NonCaptureClosureAsFnPtr(FunctionDefinition { def_id, substs, sig }) if sig.abi() == Abi::RustCall => {
let mut args = Vec::new();
for arg in arg_operands {
let arg_val = self.eval_operand(arg)?;
......@@ -277,7 +274,7 @@ fn eval_fn_call(
}
args.insert(0, (
Value::ByVal(PrimVal::Undef),
sig.inputs()[0],
sig.inputs().skip_binder()[0],
));
self.eval_fn_call_inner(
def_id,
......
......@@ -113,7 +113,7 @@ pub(crate) fn trait_method(
match self.memory.get_fn(fn_ptr.alloc_id)? {
Function::FnDefAsTraitObject(fn_def) => {
trace!("sig: {:#?}", fn_def.sig);
assert!(fn_def.abi != abi::Abi::RustCall);
assert!(fn_def.sig.abi() != abi::Abi::RustCall);
assert_eq!(args.len(), 2);
// a function item turned into a closure trait object
// the first arg is just there to give use the vtable
......@@ -126,14 +126,14 @@ pub(crate) fn trait_method(
trace!("sig: {:#?}", fn_def.sig);
args[0] = (
Value::ByVal(PrimVal::Ptr(self_ptr)),
fn_def.sig.inputs()[0],
fn_def.sig.inputs().skip_binder()[0],
);
Ok((fn_def.def_id, fn_def.substs, Vec::new()))
},
Function::NonCaptureClosureAsFnPtr(fn_def) => {
args.insert(0, (
Value::ByVal(PrimVal::Undef),
fn_def.sig.inputs()[0],
fn_def.sig.inputs().skip_binder()[0],
));
Ok((fn_def.def_id, fn_def.substs, Vec::new()))
}
......@@ -155,7 +155,7 @@ pub(crate) fn trait_method(
Function::NonCaptureClosureAsFnPtr(fn_def) => {
args.insert(0, (
Value::ByVal(PrimVal::Undef),
fn_def.sig.inputs()[0],
fn_def.sig.inputs().skip_binder()[0],
));
fn_def
},
......@@ -220,7 +220,7 @@ pub fn get_vtable(&mut self, trait_ref: ty::PolyTraitRef<'tcx>) -> EvalResult<'t
_ => bug!("bad function type: {}", fn_ty),
};
let fn_ty = self.tcx.erase_regions(&fn_ty);
self.memory.create_fn_ptr(self.tcx, mth.method.def_id, mth.substs, fn_ty)
self.memory.create_fn_ptr(mth.method.def_id, mth.substs, fn_ty)
}))
.collect::<Vec<_>>()
.into_iter()
......@@ -233,15 +233,15 @@ pub fn get_vtable(&mut self, trait_ref: ty::PolyTraitRef<'tcx>) -> EvalResult<'t
..
}
) => {
let closure_type = self.tcx.closure_type(closure_def_id, substs);
vec![Some(self.memory.create_closure_ptr(self.tcx, closure_def_id, substs, closure_type))].into_iter()
let closure_type = self.tcx.closure_type(closure_def_id);
vec![Some(self.memory.create_closure_ptr(closure_def_id, substs, closure_type))].into_iter()
}
// turn a function definition into a Fn trait object
traits::VtableFnPointer(traits::VtableFnPointerData { fn_ty, .. }) => {
match fn_ty.sty {
ty::TyFnDef(did, substs, bare_fn_ty) => {
vec![Some(self.memory.create_fn_as_trait_glue(self.tcx, did, substs, bare_fn_ty))].into_iter()
vec![Some(self.memory.create_fn_as_trait_glue(did, substs, bare_fn_ty))].into_iter()
},
ty::TyFnPtr(bare_fn_ty) => {
vec![Some(self.memory.create_fn_ptr_as_trait_glue(bare_fn_ty))].into_iter()
......@@ -275,13 +275,13 @@ pub fn get_vtable(&mut self, trait_ref: ty::PolyTraitRef<'tcx>) -> EvalResult<'t
// in case there is no drop function to be called, this still needs to be initialized
self.memory.write_usize(vtable, 0)?;
if let ty::TyAdt(adt_def, substs) = trait_ref.self_ty().sty {
if let Some(drop_def_id) = adt_def.destructor() {
if let Some(drop_def_id) = adt_def.destructor(self.tcx) {
let fn_ty = match self.tcx.item_type(drop_def_id).sty {
ty::TyFnDef(_, _, fn_ty) => self.tcx.erase_regions(&fn_ty),
_ => bug!("drop method is not a TyFnDef"),
};
// The real type is taken from the self argument in `fn drop(&mut self)`
let real_ty = match fn_ty.sig.skip_binder().inputs()[0].sty {
let real_ty = match fn_ty.inputs().skip_binder()[0].sty {
ty::TyRef(_, mt) => self.monomorphize(mt.ty, substs),
_ => bug!("first argument of Drop::drop must be &mut T"),
};
......
......@@ -5,5 +5,5 @@ fn f() {}
std::mem::transmute::<fn(), fn(i32)>(f)
};
g(42) //~ ERROR tried to call a function with abi Rust and sig
g(42) //~ ERROR tried to call a function with sig fn() through a function pointer of type fn(i32)
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册