From 82a9abbf624daaaca0c9761ee1bea6097472975c Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 21 Aug 2013 09:27:48 -0400 Subject: [PATCH] Change type of extern fns from `*u8` to `extern "ABI" fn` cc #3678 --- doc/rust.md | 32 +++++---- src/librustc/middle/trans/base.rs | 5 ++ src/librustc/middle/trans/builder.rs | 2 + src/librustc/middle/trans/expr.rs | 65 +++++++------------ src/librustc/middle/trans/llrepr.rs | 38 +++++++++++ src/librustc/middle/trans/mod.rs | 1 + src/librustc/middle/typeck/check/mod.rs | 16 ----- src/librustc/middle/typeck/collect.rs | 4 +- .../auxiliary/extern-crosscrate-source.rs | 3 +- ...xtern-no-call.rs => extern-cstack-lint.rs} | 12 +++- .../compile-fail/extern-wrong-value-type.rs | 5 +- src/test/run-pass/const-cast.rs | 2 +- src/test/run-pass/const-cross-crate-extern.rs | 7 +- src/test/run-pass/const-extern-function.rs | 10 +-- src/test/run-pass/extern-call-deep.rs | 3 +- src/test/run-pass/extern-call-deep2.rs | 3 +- src/test/run-pass/extern-call-direct.rs | 20 ++++++ ...extern-call.rs => extern-call-indirect.rs} | 3 +- src/test/run-pass/extern-call-scrub.rs | 3 +- .../extern-compare-with-return-type.rs | 32 +++++++++ src/test/run-pass/extern-stress.rs | 3 +- src/test/run-pass/extern-take-value.rs | 15 +++-- src/test/run-pass/extern-yield.rs | 3 +- src/test/run-pass/foreign-call-no-runtime.rs | 3 +- 24 files changed, 193 insertions(+), 97 deletions(-) create mode 100644 src/librustc/middle/trans/llrepr.rs rename src/test/compile-fail/{extern-no-call.rs => extern-cstack-lint.rs} (78%) create mode 100644 src/test/run-pass/extern-call-direct.rs rename src/test/run-pass/{extern-call.rs => extern-call-indirect.rs} (88%) create mode 100644 src/test/run-pass/extern-compare-with-return-type.rs diff --git a/doc/rust.md b/doc/rust.md index 16927d0de7a4a..b316f889ad1e4 100644 --- a/doc/rust.md +++ b/doc/rust.md @@ -1006,20 +1006,25 @@ code_. They are defined in the same way as any other Rust function, except that they have the `extern` modifier. ~~~ +// Declares an extern fn, the ABI defaults to "C" extern fn new_vec() -> ~[int] { ~[] } + +// Declares an extern fn with "stdcall" ABI +extern "stdcall" fn new_vec_stdcall() -> ~[int] { ~[] } ~~~ -Extern functions may not be called from Rust code, -but Rust code may take their value as a raw `u8` pointer. +Unlike normal functions, extern fns have an `extern "ABI" fn()`. +This is the same type as the functions declared in an extern +block. ~~~ # extern fn new_vec() -> ~[int] { ~[] } -let fptr: *u8 = new_vec; +let fptr: extern "C" fn() -> ~[int] = new_vec; ~~~ -The primary motivation for extern functions is -to create callbacks for foreign functions that expect to receive function -pointers. +Extern functions may be called from Rust code, but +caution must be taken with respect to the size of the stack +segment, just as when calling an extern function normally. ### Type definitions @@ -1384,14 +1389,13 @@ between the Rust ABI and the foreign ABI. A number of [attributes](#attributes) control the behavior of external blocks. -By default external blocks assume -that the library they are calling uses the standard C "cdecl" ABI. -Other ABIs may be specified using the `abi` attribute as in +By default external blocks assume that the library they are calling +uses the standard C "cdecl" ABI. Other ABIs may be specified using +an `abi` string, as shown here: ~~~{.xfail-test} // Interface to the Windows API -#[abi = "stdcall"] -extern { } +extern "stdcall" { } ~~~ The `link_name` attribute allows the name of the library to be specified. @@ -1407,6 +1411,12 @@ This is particularly useful for creating external blocks for libc, which tends to not follow standard library naming conventions and is linked to all Rust programs anyway. +The type of a function +declared in an extern block +is `extern "abi" fn(A1, ..., An) -> R`, +where `A1...An` are the declared types of its arguments +and `R` is the decalred return type. + ## Attributes ~~~~~~~~{.ebnf .gram} diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index 50b5140505ef3..cbd1ee1d5da48 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -52,6 +52,7 @@ use middle::trans::expr; use middle::trans::foreign; use middle::trans::glue; use middle::trans::inline; +use middle::trans::llrepr::LlvmRepr; use middle::trans::machine; use middle::trans::machine::{llalign_of_min, llsize_of}; use middle::trans::meth; @@ -1740,6 +1741,10 @@ pub fn copy_args_to_allocas(fcx: @mut FunctionContext, args: &[ast::arg], raw_llargs: &[ValueRef], arg_tys: &[ty::t]) -> @mut Block { + debug!("copy_args_to_allocas: raw_llargs=%s arg_tys=%s", + raw_llargs.llrepr(fcx.ccx), + arg_tys.repr(fcx.ccx.tcx)); + let _icx = push_ctxt("copy_args_to_allocas"); let mut bcx = bcx; diff --git a/src/librustc/middle/trans/builder.rs b/src/librustc/middle/trans/builder.rs index cba7a2253952f..9b910ab1ebc05 100644 --- a/src/librustc/middle/trans/builder.rs +++ b/src/librustc/middle/trans/builder.rs @@ -22,6 +22,7 @@ use std::hashmap::HashMap; use std::libc::{c_uint, c_ulonglong, c_char}; use std::vec; use syntax::codemap::span; +use std::ptr::is_not_null; pub struct Builder { llbuilder: BuilderRef, @@ -483,6 +484,7 @@ impl Builder { debug!("Store %s -> %s", self.ccx.tn.val_to_str(val), self.ccx.tn.val_to_str(ptr)); + assert!(is_not_null(self.llbuilder)); self.count_insn("store"); unsafe { llvm::LLVMBuildStore(self.llbuilder, val, ptr); diff --git a/src/librustc/middle/trans/expr.rs b/src/librustc/middle/trans/expr.rs index 725b525233e3d..0912e412fcf6e 100644 --- a/src/librustc/middle/trans/expr.rs +++ b/src/librustc/middle/trans/expr.rs @@ -824,56 +824,30 @@ fn trans_def_datum_unadjusted(bcx: @mut Block, { let _icx = push_ctxt("trans_def_datum_unadjusted"); - match def { + let fn_data = match def { ast::def_fn(did, _) | ast::def_static_method(did, None, _) => { - let fn_data = callee::trans_fn_ref(bcx, did, ref_expr.id); - return fn_data_to_datum(bcx, ref_expr, did, fn_data); + callee::trans_fn_ref(bcx, did, ref_expr.id) } ast::def_static_method(impl_did, Some(trait_did), _) => { - let fn_data = meth::trans_static_method_callee(bcx, impl_did, - trait_did, - ref_expr.id); - return fn_data_to_datum(bcx, ref_expr, impl_did, fn_data); + meth::trans_static_method_callee(bcx, impl_did, + trait_did, + ref_expr.id) } _ => { bcx.tcx().sess.span_bug(ref_expr.span, fmt!( "Non-DPS def %? referened by %s", def, bcx.node_id_to_str(ref_expr.id))); } - } + }; - fn fn_data_to_datum(bcx: @mut Block, - ref_expr: &ast::expr, - def_id: ast::def_id, - fn_data: callee::FnData) -> DatumBlock { - /*! - * - * Translates a reference to a top-level fn item into a rust - * value. This is just a fn pointer. - */ - - let is_extern = { - let fn_tpt = ty::lookup_item_type(bcx.tcx(), def_id); - ty::ty_fn_purity(fn_tpt.ty) == ast::extern_fn - }; - let (rust_ty, llval) = if is_extern { - let rust_ty = ty::mk_ptr( - bcx.tcx(), - ty::mt { - ty: ty::mk_mach_uint(ast::ty_u8), - mutbl: ast::m_imm - }); // *u8 - (rust_ty, PointerCast(bcx, fn_data.llfn, Type::i8p())) - } else { - let fn_ty = expr_ty(bcx, ref_expr); - (fn_ty, fn_data.llfn) - }; - return DatumBlock { - bcx: bcx, - datum: Datum {val: llval, - ty: rust_ty, - mode: ByValue} - }; + let fn_ty = expr_ty(bcx, ref_expr); + DatumBlock { + bcx: bcx, + datum: Datum { + val: fn_data.llfn, + ty: fn_ty, + mode: ByValue + } } } @@ -1657,6 +1631,7 @@ pub fn cast_type_kind(t: ty::t) -> cast_kind { ty::ty_float(*) => cast_float, ty::ty_ptr(*) => cast_pointer, ty::ty_rptr(*) => cast_pointer, + ty::ty_bare_fn(*) => cast_pointer, ty::ty_int(*) => cast_integral, ty::ty_uint(*) => cast_integral, ty::ty_bool => cast_integral, @@ -1719,10 +1694,16 @@ fn trans_imm_cast(bcx: @mut Block, expr: @ast::expr, val_ty(lldiscrim_a), lldiscrim_a, true), cast_float => SIToFP(bcx, lldiscrim_a, ll_t_out), - _ => ccx.sess.bug("translating unsupported cast.") + _ => ccx.sess.bug(fmt!("translating unsupported cast: \ + %s (%?) -> %s (%?)", + t_in.repr(ccx.tcx), k_in, + t_out.repr(ccx.tcx), k_out)) } } - _ => ccx.sess.bug("translating unsupported cast.") + _ => ccx.sess.bug(fmt!("translating unsupported cast: \ + %s (%?) -> %s (%?)", + t_in.repr(ccx.tcx), k_in, + t_out.repr(ccx.tcx), k_out)) }; return immediate_rvalue_bcx(bcx, newval, t_out); } diff --git a/src/librustc/middle/trans/llrepr.rs b/src/librustc/middle/trans/llrepr.rs new file mode 100644 index 0000000000000..43fd6625766ef --- /dev/null +++ b/src/librustc/middle/trans/llrepr.rs @@ -0,0 +1,38 @@ +// Copyright 2012 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. + +use middle::trans::context::CrateContext; +use middle::trans::type_::Type; +use lib::llvm::ValueRef; + +pub trait LlvmRepr { + fn llrepr(&self, ccx: &CrateContext) -> ~str; +} + +impl<'self, T:LlvmRepr> LlvmRepr for &'self [T] { + fn llrepr(&self, ccx: &CrateContext) -> ~str { + let reprs = self.map(|t| t.llrepr(ccx)); + fmt!("[%s]", reprs.connect(",")) + } +} + +impl LlvmRepr for Type { + fn llrepr(&self, ccx: &CrateContext) -> ~str { + ccx.tn.type_to_str(*self) + } +} + +impl LlvmRepr for ValueRef { + fn llrepr(&self, ccx: &CrateContext) -> ~str { + ccx.tn.val_to_str(*self) + } +} + + diff --git a/src/librustc/middle/trans/mod.rs b/src/librustc/middle/trans/mod.rs index cf6d465cb820c..008da7f7ff8f2 100644 --- a/src/librustc/middle/trans/mod.rs +++ b/src/librustc/middle/trans/mod.rs @@ -45,3 +45,4 @@ pub mod asm; pub mod type_; pub mod value; pub mod basic_block; +pub mod llrepr; diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index ad6bf69d55ad6..7d1b5f7e93f14 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -3099,22 +3099,6 @@ pub fn ty_param_bounds_and_ty_for_def(fcx: @mut FnCtxt, let typ = fcx.local_ty(sp, nid); return no_params(typ); } - ast::def_fn(_, ast::extern_fn) => { - // extern functions are just u8 pointers - return ty_param_bounds_and_ty { - generics: ty::Generics { - type_param_defs: @~[], - region_param: None - }, - ty: ty::mk_ptr( - fcx.ccx.tcx, - ty::mt { - ty: ty::mk_mach_uint(ast::ty_u8), - mutbl: ast::m_imm - }) - }; - } - ast::def_fn(id, _) | ast::def_static_method(id, _, _) | ast::def_static(id, _) | ast::def_variant(_, id) | ast::def_struct(id) => { diff --git a/src/librustc/middle/typeck/collect.rs b/src/librustc/middle/typeck/collect.rs index 7678a12b78a5f..6d577d382322a 100644 --- a/src/librustc/middle/typeck/collect.rs +++ b/src/librustc/middle/typeck/collect.rs @@ -1067,13 +1067,13 @@ pub fn ty_of_item(ccx: &CrateCtxt, it: &ast::item) tcx.tcache.insert(local_def(it.id), tpt); return tpt; } - ast::item_fn(ref decl, purity, _, ref generics, _) => { + ast::item_fn(ref decl, purity, abi, ref generics, _) => { assert!(rp.is_none()); let ty_generics = ty_generics(ccx, None, generics, 0); let tofd = astconv::ty_of_bare_fn(ccx, &empty_rscope, purity, - AbiSet::Rust(), + abi, &generics.lifetimes, decl); let tpt = ty_param_bounds_and_ty { diff --git a/src/test/auxiliary/extern-crosscrate-source.rs b/src/test/auxiliary/extern-crosscrate-source.rs index 03cb96a3729af..e34fb610e0605 100644 --- a/src/test/auxiliary/extern-crosscrate-source.rs +++ b/src/test/auxiliary/extern-crosscrate-source.rs @@ -19,7 +19,8 @@ pub mod rustrt { use std::libc; extern { - pub fn rust_dbg_call(cb: *u8, data: libc::uintptr_t) + pub fn rust_dbg_call(cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t, + data: libc::uintptr_t) -> libc::uintptr_t; } } diff --git a/src/test/compile-fail/extern-no-call.rs b/src/test/compile-fail/extern-cstack-lint.rs similarity index 78% rename from src/test/compile-fail/extern-no-call.rs rename to src/test/compile-fail/extern-cstack-lint.rs index 58649f3209bb1..8ccd92316e922 100644 --- a/src/test/compile-fail/extern-no-call.rs +++ b/src/test/compile-fail/extern-cstack-lint.rs @@ -8,10 +8,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// error-pattern:expected function but found `*u8` extern fn f() { } -fn main() { - f(); +extern fn call1() { + f(); // OK from another extern fn! } + +fn call2() { + f(); //~ ERROR invoking non-Rust fn +} + + +fn main() {} diff --git a/src/test/compile-fail/extern-wrong-value-type.rs b/src/test/compile-fail/extern-wrong-value-type.rs index fbb0f6e46a1c4..069c5a77c6c33 100644 --- a/src/test/compile-fail/extern-wrong-value-type.rs +++ b/src/test/compile-fail/extern-wrong-value-type.rs @@ -12,6 +12,7 @@ extern fn f() { } fn main() { - // extern functions are *u8 types - let _x: &fn() = f; //~ ERROR found `*u8` + // extern functions are extern "C" fn + let _x: extern "C" fn() = f; // OK + let _x: &fn() = f; //~ ERROR mismatched types } diff --git a/src/test/run-pass/const-cast.rs b/src/test/run-pass/const-cast.rs index 280fe44c3da3e..d511930c70fa7 100644 --- a/src/test/run-pass/const-cast.rs +++ b/src/test/run-pass/const-cast.rs @@ -12,7 +12,7 @@ use std::libc; extern fn foo() {} -static x: *u8 = foo; +static x: extern "C" fn() = foo; static y: *libc::c_void = x as *libc::c_void; static a: &'static int = &10; static b: *int = a as *int; diff --git a/src/test/run-pass/const-cross-crate-extern.rs b/src/test/run-pass/const-cross-crate-extern.rs index 5281c21762689..eb7ae3d6fec99 100644 --- a/src/test/run-pass/const-cross-crate-extern.rs +++ b/src/test/run-pass/const-cross-crate-extern.rs @@ -13,8 +13,11 @@ extern mod cci_const; use cci_const::bar; -static foo: *u8 = bar; +use std::cast::transmute; +static foo: extern "C" fn() = bar; pub fn main() { - assert_eq!(foo, cci_const::bar); + unsafe { + assert_eq!(foo, bar); + } } diff --git a/src/test/run-pass/const-extern-function.rs b/src/test/run-pass/const-extern-function.rs index 9a8104cb14f71..e07af4b0383fd 100644 --- a/src/test/run-pass/const-extern-function.rs +++ b/src/test/run-pass/const-extern-function.rs @@ -10,14 +10,16 @@ extern fn foopy() {} -static f: *u8 = foopy; +static f: extern "C" fn() = foopy; static s: S = S { f: foopy }; struct S { - f: *u8 + f: extern "C" fn() } pub fn main() { - assert_eq!(foopy, f); - assert_eq!(f, s.f); + unsafe { + assert_eq!(foopy, f); + assert_eq!(f, s.f); + } } diff --git a/src/test/run-pass/extern-call-deep.rs b/src/test/run-pass/extern-call-deep.rs index 27ad2fc46e07d..1153cb4177daa 100644 --- a/src/test/run-pass/extern-call-deep.rs +++ b/src/test/run-pass/extern-call-deep.rs @@ -14,7 +14,8 @@ mod rustrt { use std::libc; extern { - pub fn rust_dbg_call(cb: *u8, data: libc::uintptr_t) + pub fn rust_dbg_call(cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t, + data: libc::uintptr_t) -> libc::uintptr_t; } } diff --git a/src/test/run-pass/extern-call-deep2.rs b/src/test/run-pass/extern-call-deep2.rs index 6c90443636d39..a8fa9c2cef028 100644 --- a/src/test/run-pass/extern-call-deep2.rs +++ b/src/test/run-pass/extern-call-deep2.rs @@ -15,7 +15,8 @@ mod rustrt { use std::libc; extern { - pub fn rust_dbg_call(cb: *u8, data: libc::uintptr_t) + pub fn rust_dbg_call(cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t, + data: libc::uintptr_t) -> libc::uintptr_t; } } diff --git a/src/test/run-pass/extern-call-direct.rs b/src/test/run-pass/extern-call-direct.rs new file mode 100644 index 0000000000000..bc6ee63c0f457 --- /dev/null +++ b/src/test/run-pass/extern-call-direct.rs @@ -0,0 +1,20 @@ +// Copyright 2012 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. + +// Test direct calls to extern fns. + +extern fn f(x: uint) -> uint { x * 2 } + +fn main() { + #[fixed_stack_segment]; + + let x = f(22); + assert_eq!(x, 44); +} diff --git a/src/test/run-pass/extern-call.rs b/src/test/run-pass/extern-call-indirect.rs similarity index 88% rename from src/test/run-pass/extern-call.rs rename to src/test/run-pass/extern-call-indirect.rs index 939487df174db..9929cb447a6c7 100644 --- a/src/test/run-pass/extern-call.rs +++ b/src/test/run-pass/extern-call-indirect.rs @@ -14,7 +14,8 @@ mod rustrt { use std::libc; extern { - pub fn rust_dbg_call(cb: *u8, data: libc::uintptr_t) + pub fn rust_dbg_call(cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t, + data: libc::uintptr_t) -> libc::uintptr_t; } } diff --git a/src/test/run-pass/extern-call-scrub.rs b/src/test/run-pass/extern-call-scrub.rs index 5abd3c7b9d918..4388ef65e9865 100644 --- a/src/test/run-pass/extern-call-scrub.rs +++ b/src/test/run-pass/extern-call-scrub.rs @@ -19,7 +19,8 @@ mod rustrt { use std::libc; extern { - pub fn rust_dbg_call(cb: *u8, data: libc::uintptr_t) + pub fn rust_dbg_call(cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t, + data: libc::uintptr_t) -> libc::uintptr_t; } } diff --git a/src/test/run-pass/extern-compare-with-return-type.rs b/src/test/run-pass/extern-compare-with-return-type.rs new file mode 100644 index 0000000000000..41bf4a10b5d9e --- /dev/null +++ b/src/test/run-pass/extern-compare-with-return-type.rs @@ -0,0 +1,32 @@ +// Copyright 2013 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. + +// Tests that we can compare various kinds of extern fn signatures. + +extern fn voidret1() {} +extern fn voidret2() {} + +extern fn uintret() -> uint { 22 } + +extern fn uintvoidret(x: uint) {} + +extern fn uintuintuintuintret(x: uint, y: uint, z: uint) -> uint { x+y+z } + +fn main() { + assert_eq!(voidret1, voidret1); + assert!(voidret1 != voidret2); + + assert_eq!(uintret, uintret); + + assert_eq!(uintvoidret, uintvoidret); + + assert_eq!(uintuintuintuintret, uintuintuintuintret); +} + diff --git a/src/test/run-pass/extern-stress.rs b/src/test/run-pass/extern-stress.rs index 4eda3f34b6c15..2323567ccce43 100644 --- a/src/test/run-pass/extern-stress.rs +++ b/src/test/run-pass/extern-stress.rs @@ -18,7 +18,8 @@ mod rustrt { use std::libc; extern { - pub fn rust_dbg_call(cb: *u8, data: libc::uintptr_t) + pub fn rust_dbg_call(cb: extern "C" fn(libc::uintptr_t) -> libc::uintptr_t, + data: libc::uintptr_t) -> libc::uintptr_t; } } diff --git a/src/test/run-pass/extern-take-value.rs b/src/test/run-pass/extern-take-value.rs index 542686c9099f1..e636f511da8d3 100644 --- a/src/test/run-pass/extern-take-value.rs +++ b/src/test/run-pass/extern-take-value.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::cast::transmute; + extern fn f() { } @@ -15,11 +17,12 @@ extern fn g() { } pub fn main() { - // extern functions are *u8 types - let a: *u8 = f; - let b: *u8 = f; - let c: *u8 = g; + unsafe { + let a: extern "C" fn() = f; + let b: extern "C" fn() = f; + let c: extern "C" fn() = g; - assert_eq!(a, b); - assert!(a != c); + assert_eq!(a, b); + assert!(a != c); + } } diff --git a/src/test/run-pass/extern-yield.rs b/src/test/run-pass/extern-yield.rs index ce51aafa9d809..fd0807dffc84c 100644 --- a/src/test/run-pass/extern-yield.rs +++ b/src/test/run-pass/extern-yield.rs @@ -15,7 +15,8 @@ mod rustrt { use std::libc; extern { - pub fn rust_dbg_call(cb: *u8, data: libc::uintptr_t) + pub fn rust_dbg_call(cb: extern "C" fn (libc::uintptr_t) -> libc::uintptr_t, + data: libc::uintptr_t) -> libc::uintptr_t; } } diff --git a/src/test/run-pass/foreign-call-no-runtime.rs b/src/test/run-pass/foreign-call-no-runtime.rs index c8acdbf44781c..2fa4f191ed14d 100644 --- a/src/test/run-pass/foreign-call-no-runtime.rs +++ b/src/test/run-pass/foreign-call-no-runtime.rs @@ -2,7 +2,8 @@ use std::cast; use std::libc; use std::unstable::run_in_bare_thread; -externfn!(fn rust_dbg_call(cb: *u8, data: libc::uintptr_t) -> libc::uintptr_t) +externfn!(fn rust_dbg_call(cb: extern "C" fn(libc::uintptr_t), + data: libc::uintptr_t) -> libc::uintptr_t) pub fn main() { unsafe {