]> git.lizzy.rs Git - rust.git/commitdiff
libsyntax/librustc: Allow calling variadic foreign functions.
authorLuqman Aden <laden@csclub.uwaterloo.ca>
Fri, 25 Oct 2013 05:56:34 +0000 (01:56 -0400)
committerLuqman Aden <laden@csclub.uwaterloo.ca>
Tue, 5 Nov 2013 04:53:11 +0000 (23:53 -0500)
28 files changed:
src/librustc/lib/llvm.rs
src/librustc/metadata/tydecode.rs
src/librustc/metadata/tyencode.rs
src/librustc/middle/trans/callee.rs
src/librustc/middle/trans/foreign.rs
src/librustc/middle/trans/reflect.rs
src/librustc/middle/trans/type_.rs
src/librustc/middle/ty.rs
src/librustc/middle/typeck/astconv.rs
src/librustc/middle/typeck/check/mod.rs
src/librustc/middle/typeck/collect.rs
src/librustc/middle/typeck/infer/combine.rs
src/librustc/middle/typeck/infer/test.rs
src/librustc/middle/typeck/mod.rs
src/librustc/util/ppaux.rs
src/libstd/reflect.rs
src/libstd/repr.rs
src/libstd/unstable/intrinsics.rs
src/libsyntax/ast.rs
src/libsyntax/ext/build.rs
src/libsyntax/fold.rs
src/libsyntax/parse/lexer.rs
src/libsyntax/parse/mod.rs
src/libsyntax/parse/parser.rs
src/libsyntax/parse/token.rs
src/libsyntax/print/pprust.rs
src/test/run-pass/reflect-visit-data.rs
src/test/run-pass/reflect-visit-type.rs

index e152d009050c858dbe74144e746842c9171b576c..043b3dcc7aa57a078b06198a579040dcc67def28 100644 (file)
@@ -29,6 +29,7 @@
 
 // Consts for the LLVM CallConv type, pre-cast to uint.
 
+#[deriving(Eq)]
 pub enum CallConv {
     CCallConv = 0,
     FastCallConv = 8,
index 62de991ce9631fe010762bdcf0cf9db907fba175..b365e7a48795e631e6109a7cf7b4b9a5967d1e4f 100644 (file)
@@ -526,10 +526,17 @@ fn parse_sig(st: &mut PState, conv: conv_did) -> ty::FnSig {
         inputs.push(parse_ty(st, |x,y| conv(x,y)));
     }
     st.pos += 1u; // eat the ']'
+    let variadic = if peek(st) == 'A' {
+        st.pos += 1; // eat the 'A'
+        true
+    } else { false };
     let ret_ty = parse_ty(st, conv);
-    ty::FnSig {bound_lifetime_names: opt_vec::Empty, // FIXME(#4846)
-               inputs: inputs,
-               output: ret_ty}
+    ty::FnSig {
+        bound_lifetime_names: opt_vec::Empty, // FIXME(#4846)
+        inputs: inputs,
+        output: ret_ty,
+        variadic: variadic
+    }
 }
 
 // Rust metadata parsing
index 7fb33c881156b17ff0d334e63e276d0ed3fed942..5397bf0e768d8d237ea32c3e48d5d8d1c874a4ff 100644 (file)
@@ -371,6 +371,9 @@ fn enc_fn_sig(w: @mut MemWriter, cx: @ctxt, fsig: &ty::FnSig) {
         enc_ty(w, cx, *ty);
     }
     mywrite!(w, "]");
+    if fsig.variadic {
+        mywrite!(w, "A");
+    }
     enc_ty(w, cx, fsig.output);
 }
 
index 7a3a67b35ee1ece8563123138f2cd25995e2ffe2..ecb53efbad788b29480a3d824ad236d4852c9806 100644 (file)
@@ -750,8 +750,12 @@ pub fn trans_call_inner(in_cx: @mut Block,
             let mut llargs = ~[];
             bcx = trans_args(bcx, args, callee_ty,
                              autoref_arg, &mut llargs);
+            let arg_tys = match args {
+                ArgExprs(a) => a.iter().map(|x| expr_ty(bcx, *x)).collect(),
+                ArgVals(_) => fail!("expected arg exprs.")
+            };
             bcx = foreign::trans_native_call(bcx, callee_ty,
-                                             llfn, opt_llretslot.unwrap(), llargs);
+                                             llfn, opt_llretslot.unwrap(), llargs, arg_tys);
         }
 
         // If the caller doesn't care about the result of this fn call,
@@ -789,6 +793,7 @@ pub fn trans_args(cx: @mut Block,
     let _icx = push_ctxt("trans_args");
     let mut temp_cleanups = ~[];
     let arg_tys = ty::ty_fn_args(fn_ty);
+    let variadic = ty::fn_is_variadic(fn_ty);
 
     let mut bcx = cx;
 
@@ -797,10 +802,17 @@ pub fn trans_args(cx: @mut Block,
     // to cast her view of the arguments to the caller's view.
     match args {
       ArgExprs(arg_exprs) => {
+        let num_formal_args = arg_tys.len();
         for (i, arg_expr) in arg_exprs.iter().enumerate() {
+            let arg_ty = if i >= num_formal_args {
+                assert!(variadic);
+                expr_ty_adjusted(cx, *arg_expr)
+            } else {
+                arg_tys[i]
+            };
             let arg_val = unpack_result!(bcx, {
                 trans_arg_expr(bcx,
-                               arg_tys[i],
+                               arg_ty,
                                ty::ByCopy,
                                *arg_expr,
                                &mut temp_cleanups,
index 7f8f1daebc48a020fcbcf00bb37e56a9c4d2b851..ef843b56f4bf0162a5e481abc0d8f4eccdea8ba2 100644 (file)
@@ -120,8 +120,7 @@ pub fn register_foreign_item_fn(ccx: @mut CrateContext,
     let cc = match llvm_calling_convention(ccx, abis) {
         Some(cc) => cc,
         None => {
-            // FIXME(#8357) We really ought to report a span here
-            ccx.sess.fatal(
+            ccx.sess.span_fatal(foreign_item.span,
                 format!("ABI `{}` has no suitable ABI \
                       for target architecture \
                       in module {}",
@@ -135,6 +134,12 @@ pub fn register_foreign_item_fn(ccx: @mut CrateContext,
     let lname = link_name(ccx, foreign_item);
     let tys = foreign_types_for_id(ccx, foreign_item.id);
 
+    // Make sure the calling convention is right for variadic functions
+    // (should've been caught if not in typeck)
+    if tys.fn_sig.variadic {
+        assert!(cc == lib::llvm::CCallConv);
+    }
+
     // Create the LLVM value for the C extern fn
     let llfn_ty = lltype_for_fn_from_foreign_types(&tys);
     let llfn = base::get_extern_fn(&mut ccx.externs, ccx.llmod,
@@ -148,7 +153,8 @@ pub fn trans_native_call(bcx: @mut Block,
                          callee_ty: ty::t,
                          llfn: ValueRef,
                          llretptr: ValueRef,
-                         llargs_rust: &[ValueRef]) -> @mut Block {
+                         llargs_rust: &[ValueRef],
+                         passed_arg_tys: ~[ty::t]) -> @mut Block {
     /*!
      * Prepares a call to a native function. This requires adapting
      * from the Rust argument passing rules to the native rules.
@@ -160,6 +166,10 @@ pub fn trans_native_call(bcx: @mut Block,
      * - `llretptr`: where to store the return value of the function
      * - `llargs_rust`: a list of the argument values, prepared
      *   as they would be if calling a Rust function
+     * - `passed_arg_tys`: Rust type for the arguments. Normally we
+     *   can derive these from callee_ty but in the case of variadic
+     *   functions passed_arg_tys will include the Rust type of all
+     *   the arguments including the ones not specified in the fn's signature.
      */
 
     let ccx = bcx.ccx();
@@ -176,7 +186,7 @@ pub fn trans_native_call(bcx: @mut Block,
         ty::ty_bare_fn(ref fn_ty) => (fn_ty.abis, fn_ty.sig.clone()),
         _ => ccx.sess.bug("trans_native_call called on non-function type")
     };
-    let llsig = foreign_signature(ccx, &fn_sig);
+    let llsig = foreign_signature(ccx, &fn_sig, passed_arg_tys);
     let ret_def = !ty::type_is_voidish(bcx.tcx(), fn_sig.output);
     let fn_type = cabi::compute_abi_info(ccx,
                                          llsig.llarg_tys,
@@ -208,7 +218,7 @@ pub fn trans_native_call(bcx: @mut Block,
         let mut llarg_rust = llarg_rust;
 
         // Does Rust pass this argument by pointer?
-        let rust_indirect = type_of::arg_is_indirect(ccx, fn_sig.inputs[i]);
+        let rust_indirect = type_of::arg_is_indirect(ccx, passed_arg_tys[i]);
 
         debug!("argument {}, llarg_rust={}, rust_indirect={}, arg_ty={}",
                i,
@@ -219,7 +229,7 @@ pub fn trans_native_call(bcx: @mut Block,
         // Ensure that we always have the Rust value indirectly,
         // because it makes bitcasting easier.
         if !rust_indirect {
-            let scratch = base::alloca(bcx, type_of::type_of(ccx, fn_sig.inputs[i]), "__arg");
+            let scratch = base::alloca(bcx, type_of::type_of(ccx, passed_arg_tys[i]), "__arg");
             Store(bcx, llarg_rust, scratch);
             llarg_rust = scratch;
         }
@@ -331,6 +341,20 @@ pub fn trans_foreign_mod(ccx: @mut CrateContext,
                          foreign_mod: &ast::foreign_mod) {
     let _icx = push_ctxt("foreign::trans_foreign_mod");
     for &foreign_item in foreign_mod.items.iter() {
+        match foreign_item.node {
+            ast::foreign_item_fn(*) => {
+                let (abis, mut path) = match ccx.tcx.items.get_copy(&foreign_item.id) {
+                    ast_map::node_foreign_item(_, abis, _, path) => (abis, (*path).clone()),
+                    _ => fail!("Unable to find foreign item in tcx.items table.")
+                };
+                if !(abis.is_rust() || abis.is_intrinsic()) {
+                    path.push(ast_map::path_name(foreign_item.ident));
+                    register_foreign_item_fn(ccx, abis, &path, foreign_item);
+                }
+            }
+            _ => ()
+        }
+
         let lname = link_name(ccx, foreign_item);
         ccx.item_symbols.insert(foreign_item.id, lname.to_owned());
     }
@@ -701,7 +725,7 @@ pub fn link_name(ccx: &CrateContext, i: @ast::foreign_item) -> @str {
     }
 }
 
-fn foreign_signature(ccx: &mut CrateContext, fn_sig: &ty::FnSig)
+fn foreign_signature(ccx: &mut CrateContext, fn_sig: &ty::FnSig, arg_tys: &[ty::t])
                      -> LlvmSignature {
     /*!
      * The ForeignSignature is the LLVM types of the arguments/return type
@@ -711,7 +735,7 @@ fn foreign_signature(ccx: &mut CrateContext, fn_sig: &ty::FnSig)
      * values by pointer like we do.
      */
 
-    let llarg_tys = fn_sig.inputs.map(|&arg| type_of(ccx, arg));
+    let llarg_tys = arg_tys.map(|&arg| type_of(ccx, arg));
     let llret_ty = type_of::type_of(ccx, fn_sig.output);
     LlvmSignature {
         llarg_tys: llarg_tys,
@@ -731,7 +755,7 @@ fn foreign_types_for_fn_ty(ccx: &mut CrateContext,
         ty::ty_bare_fn(ref fn_ty) => fn_ty.sig.clone(),
         _ => ccx.sess.bug("foreign_types_for_fn_ty called on non-function type")
     };
-    let llsig = foreign_signature(ccx, &fn_sig);
+    let llsig = foreign_signature(ccx, &fn_sig, fn_sig.inputs);
     let ret_def = !ty::type_is_voidish(ccx.tcx, fn_sig.output);
     let fn_ty = cabi::compute_abi_info(ccx,
                                        llsig.llarg_tys,
@@ -790,7 +814,11 @@ fn lltype_for_fn_from_foreign_types(tys: &ForeignTypes) -> Type {
         llargument_tys.push(llarg_ty);
     }
 
-    Type::func(llargument_tys, &llreturn_ty)
+    if tys.fn_sig.variadic {
+        Type::variadic_func(llargument_tys, &llreturn_ty)
+    } else {
+        Type::func(llargument_tys, &llreturn_ty)
+    }
 }
 
 pub fn lltype_for_foreign_fn(ccx: &mut CrateContext, ty: ty::t) -> Type {
index 7e875243fd0832b2aece2333eb740733ca0ce1e2..c56d88355495f4f08bfc4a860a0244d2f2a3c922 100644 (file)
@@ -375,6 +375,7 @@ pub fn visit_sig(&mut self, retval: uint, sig: &ty::FnSig) {
             self.visit("fn_input", extra);
         }
         let extra = ~[self.c_uint(retval),
+                      self.c_bool(sig.variadic),
                       self.c_tydesc(sig.output)];
         self.visit("fn_output", extra);
     }
index de41548895df2eade480ff90cdf1a0685422c028..9e95a9a6449acf8a83842ffe2cdafe80300ad0c8 100644 (file)
@@ -154,6 +154,12 @@ pub fn func(args: &[Type], ret: &Type) -> Type {
                                    args.len() as c_uint, False))
     }
 
+    pub fn variadic_func(args: &[Type], ret: &Type) -> Type {
+        let vec : &[TypeRef] = unsafe { cast::transmute(args) };
+        ty!(llvm::LLVMFunctionType(ret.to_ref(), vec::raw::to_ptr(vec),
+                                   args.len() as c_uint, True))
+    }
+
     pub fn func_pair(cx: &CrateContext, fn_ty: &Type) -> Type {
         Type::struct_([fn_ty.ptr_to(), Type::opaque_cbox_ptr(cx)], false)
     }
index c3c0c5ef20ff64fbfb06963e093e16bd2faba8ea..373395e91cc05142f2d148d12e6e200041d6d298 100644 (file)
@@ -433,12 +433,15 @@ pub struct ClosureTy {
  *
  * - `lifetimes` is the list of region names bound in this fn.
  * - `inputs` is the list of arguments and their modes.
- * - `output` is the return type. */
+ * - `output` is the return type.
+ * - `variadic` indicates whether this is a varidic function. (only true for foreign fns)
+ */
 #[deriving(Clone, Eq, IterBytes)]
 pub struct FnSig {
     bound_lifetime_names: OptVec<ast::Ident>,
     inputs: ~[t],
-    output: t
+    output: t,
+    variadic: bool
 }
 
 #[deriving(Clone, Eq, IterBytes)]
@@ -705,6 +708,7 @@ pub enum type_err {
     terr_float_mismatch(expected_found<ast::float_ty>),
     terr_traits(expected_found<ast::DefId>),
     terr_builtin_bounds(expected_found<BuiltinBounds>),
+    terr_variadic_mismatch(expected_found<bool>)
 }
 
 #[deriving(Eq, IterBytes)]
@@ -1251,7 +1255,8 @@ pub fn mk_ctor_fn(cx: ctxt, input_tys: &[ty::t], output: ty::t) -> t {
                    sig: FnSig {
                     bound_lifetime_names: opt_vec::Empty,
                     inputs: input_args,
-                    output: output
+                    output: output,
+                    variadic: false
                    }
                 })
 }
@@ -1338,7 +1343,8 @@ pub fn fold_sig(sig: &FnSig, fldop: &fn(t) -> t) -> FnSig {
     FnSig {
         bound_lifetime_names: sig.bound_lifetime_names.clone(),
         inputs: args,
-        output: fldop(sig.output)
+        output: fldop(sig.output),
+        variadic: sig.variadic
     }
 }
 
@@ -2816,6 +2822,16 @@ fn node_id_has_type_params(cx: ctxt, id: ast::NodeId) -> bool {
     cx.node_type_substs.contains_key(&id)
 }
 
+pub fn fn_is_variadic(fty: t) -> bool {
+    match get(fty).sty {
+        ty_bare_fn(ref f) => f.sig.variadic,
+        ty_closure(ref f) => f.sig.variadic,
+        ref s => {
+            fail!("fn_is_variadic() called on non-fn type: {:?}", s)
+        }
+    }
+}
+
 pub fn ty_fn_sig(fty: t) -> FnSig {
     match get(fty).sty {
         ty_bare_fn(ref f) => f.sig.clone(),
@@ -3579,6 +3595,11 @@ fn terr_vstore_kind_to_str(k: terr_vstore_kind) -> ~str {
                  values.expected.to_str(),
                  values.found.to_str())
         }
+        terr_variadic_mismatch(ref values) => {
+            format!("expected {} fn but found {} function",
+                    if values.expected { "variadic" } else { "non-variadic" },
+                    if values.found { "variadic" } else { "non-variadic" })
+        }
     }
 }
 
index dd0c6c12a69aacf3077fdf7f683562ef7ab956ae..45a6d709b04fb7fcac13ad26ef00608a7d1942c2 100644 (file)
@@ -396,6 +396,9 @@ fn check_path_args(tcx: ty::ctxt,
         ty::mk_tup(tcx, flds)
       }
       ast::ty_bare_fn(ref bf) => {
+          if bf.decl.variadic && !bf.abis.is_c() {
+            tcx.sess.span_err(ast_ty.span, "variadic function must have C calling convention");
+          }
           ty::mk_bare_fn(tcx, ty_of_bare_fn(this, rscope, bf.purity,
                                             bf.abis, &bf.lifetimes, &bf.decl))
       }
@@ -660,9 +663,12 @@ fn ty_of_method_or_bare_fn<AC:AstConv,RS:RegionScope + Clone + 'static>(
             ty::BareFnTy {
                 purity: purity,
                 abis: abi,
-                sig: ty::FnSig {bound_lifetime_names: bound_lifetime_names,
-                                inputs: input_tys,
-                                output: output_ty}
+                sig: ty::FnSig {
+                    bound_lifetime_names: bound_lifetime_names,
+                    inputs: input_tys,
+                    output: output_ty,
+                    variadic: decl.variadic
+                }
             });
 
     fn transform_self_ty<AC:AstConv,RS:RegionScope + Clone + 'static>(
@@ -770,9 +776,12 @@ pub fn ty_of_closure<AC:AstConv,RS:RegionScope + Clone + 'static>(
         onceness: onceness,
         region: bound_region,
         bounds: bounds,
-        sig: ty::FnSig {bound_lifetime_names: bound_lifetime_names,
-                        inputs: input_tys,
-                        output: output_ty}
+        sig: ty::FnSig {
+            bound_lifetime_names: bound_lifetime_names,
+            inputs: input_tys,
+            output: output_ty,
+            variadic: decl.variadic
+        }
     }
 }
 
index 011d39af7a8c876795e9695df39a1debbd7018f7..16f1052d9018770b1442210ed6b5a85f776b3254 100644 (file)
@@ -643,9 +643,17 @@ pub fn check_item(ccx: @mut CrateCtxt, it: @ast::item) {
             for item in m.items.iter() {
                 let tpt = ty::lookup_item_type(ccx.tcx, local_def(item.id));
                 if tpt.generics.has_type_params() {
-                    ccx.tcx.sess.span_err(
-                        item.span,
-                        format!("foreign items may not have type parameters"));
+                    ccx.tcx.sess.span_err(item.span, "foreign items may not have type parameters");
+                }
+
+                match item.node {
+                    ast::foreign_item_fn(ref fn_decl, _) => {
+                        if fn_decl.variadic && !m.abis.is_c() {
+                            ccx.tcx.sess.span_err(
+                                item.span, "variadic function must have C calling convention");
+                        }
+                    }
+                    _ => {}
                 }
             }
         }
@@ -1321,13 +1329,13 @@ fn check_method_argument_types(
         if ty::type_is_error(method_fn_ty) {
             let err_inputs = err_args(args.len());
             check_argument_types(fcx, sp, err_inputs, callee_expr,
-                                 args, sugar, deref_args);
+                                 args, sugar, deref_args, false);
             method_fn_ty
         } else {
             match ty::get(method_fn_ty).sty {
                 ty::ty_bare_fn(ref fty) => {
                     check_argument_types(fcx, sp, fty.sig.inputs, callee_expr,
-                                         args, sugar, deref_args);
+                                         args, sugar, deref_args, fty.sig.variadic);
                     fty.sig.output
                 }
                 _ => {
@@ -1339,15 +1347,14 @@ fn check_method_argument_types(
         }
     }
 
-    fn check_argument_types(
-        fcx: @mut FnCtxt,
-        sp: Span,
-        fn_inputs: &[ty::t],
-        callee_expr: @ast::Expr,
-        args: &[@ast::Expr],
-        sugar: ast::CallSugar,
-        deref_args: DerefArgs)
-    {
+    fn check_argument_types(fcx: @mut FnCtxt,
+                            sp: Span,
+                            fn_inputs: &[ty::t],
+                            callee_expr: @ast::Expr,
+                            args: &[@ast::Expr],
+                            sugar: ast::CallSugar,
+                            deref_args: DerefArgs,
+                            variadic: bool) {
         /*!
          *
          * Generic function that factors out common logic from
@@ -1362,6 +1369,19 @@ fn check_argument_types(
         let expected_arg_count = fn_inputs.len();
         let formal_tys = if expected_arg_count == supplied_arg_count {
             fn_inputs.map(|a| *a)
+        } else if variadic {
+            if supplied_arg_count >= expected_arg_count {
+                fn_inputs.map(|a| *a)
+            } else {
+                let msg = format!(
+                    "this function takes at least {0, plural, =1{# parameter} \
+                    other{# parameters}} but {1, plural, =1{# parameter was} \
+                    other{# parameters were}} supplied", expected_arg_count, supplied_arg_count);
+
+                tcx.sess.span_err(sp, msg);
+
+                err_args(supplied_arg_count)
+            }
         } else {
             let suffix = match sugar {
                 ast::NoSugar => "",
@@ -1370,19 +1390,15 @@ fn check_argument_types(
                 ast::ForSugar => " (including the closure passed by \
                                   the `for` keyword)"
             };
-            let msg = format!("this function takes {} parameter{} but \
-                            {} parameter{} supplied{}",
-                           expected_arg_count,
-                           if expected_arg_count == 1 {""}
-                           else {"s"},
-                           supplied_arg_count,
-                           if supplied_arg_count == 1 {" was"}
-                           else {"s were"},
-                           suffix);
+            let msg = format!(
+                "this function takes {0, plural, =1{# parameter} \
+                other{# parameters}} but {1, plural, =1{# parameter was} \
+                other{# parameters were}} supplied{2}",
+                expected_arg_count, supplied_arg_count, suffix);
 
             tcx.sess.span_err(sp, msg);
 
-            vec::from_elem(supplied_arg_count, ty::mk_err())
+            err_args(supplied_arg_count)
         };
 
         debug!("check_argument_types: formal_tys={:?}",
@@ -1406,7 +1422,15 @@ fn check_argument_types(
                 vtable::early_resolve_expr(callee_expr, fcx, true);
             }
 
-            for (i, arg) in args.iter().enumerate() {
+            // For variadic functions, we don't have a declared type for all of
+            // the arguments hence we only do our usual type checking with
+            // the arguments who's types we do know.
+            let t = if variadic {
+                expected_arg_count
+            } else {
+                supplied_arg_count
+            };
+            for (i, arg) in args.iter().take(t).enumerate() {
                 let is_block = match arg.node {
                     ast::ExprFnBlock(*) |
                     ast::ExprProc(*) |
@@ -1431,12 +1455,41 @@ fn check_argument_types(
                         DontDerefArgs => {}
                     }
 
-                    check_expr_coercable_to_type(
-                        fcx, *arg, formal_ty);
+                    check_expr_coercable_to_type(fcx, *arg, formal_ty);
 
                 }
             }
         }
+
+        // We also need to make sure we at least write the ty of the other
+        // arguments which we skipped above.
+        if variadic {
+            for arg in args.iter().skip(expected_arg_count) {
+                check_expr(fcx, *arg);
+
+                // There are a few types which get autopromoted when passed via varargs
+                // in C but we just error out instead and require explicit casts.
+                let arg_ty = structurally_resolved_type(fcx, arg.span, fcx.expr_ty(*arg));
+                match ty::get(arg_ty).sty {
+                    ty::ty_float(ast::ty_f32) => {
+                        fcx.type_error_message(arg.span,
+                                |t| format!("can't pass an {} to variadic function, \
+                                             cast to c_double", t), arg_ty, None);
+                    }
+                    ty::ty_int(ast::ty_i8) | ty::ty_int(ast::ty_i16) | ty::ty_bool => {
+                        fcx.type_error_message(arg.span,
+                                |t| format!("can't pass {} to variadic function, cast to c_int",
+                                            t), arg_ty, None);
+                    }
+                    ty::ty_uint(ast::ty_u8) | ty::ty_uint(ast::ty_u16) => {
+                        fcx.type_error_message(arg.span,
+                                |t| format!("can't pass {} to variadic function, cast to c_uint",
+                                            t), arg_ty, None);
+                    }
+                    _ => {}
+                }
+            }
+        }
     }
 
     fn err_args(len: uint) -> ~[ty::t] {
@@ -1505,7 +1558,8 @@ fn check_call(fcx: @mut FnCtxt,
         let error_fn_sig = FnSig {
             bound_lifetime_names: opt_vec::Empty,
             inputs: err_args(args.len()),
-            output: ty::mk_err()
+            output: ty::mk_err(),
+            variadic: false
         };
 
         let fn_sig = match *fn_sty {
@@ -1532,7 +1586,7 @@ fn check_call(fcx: @mut FnCtxt,
 
         // Call the generic checker.
         check_argument_types(fcx, call_expr.span, fn_sig.inputs, f,
-                             args, sugar, DontDerefArgs);
+                             args, sugar, DontDerefArgs, fn_sig.variadic);
 
         write_call(fcx, call_expr, fn_sig.output, sugar);
     }
@@ -1914,7 +1968,8 @@ fn check_expr_fn(fcx: @mut FnCtxt,
             fty_sig = FnSig {
                 bound_lifetime_names: opt_vec::Empty,
                 inputs: fn_ty.sig.inputs.map(|_| ty::mk_err()),
-                output: ty::mk_err()
+                output: ty::mk_err(),
+                variadic: false
             };
             ty::mk_err()
         } else {
@@ -3897,9 +3952,12 @@ fn param(ccx: @mut CrateCtxt, n: uint) -> ty::t {
     let fty = ty::mk_bare_fn(tcx, ty::BareFnTy {
         purity: ast::unsafe_fn,
         abis: AbiSet::Intrinsic(),
-        sig: FnSig {bound_lifetime_names: opt_vec::Empty,
-                    inputs: inputs,
-                    output: output}
+        sig: FnSig {
+            bound_lifetime_names: opt_vec::Empty,
+            inputs: inputs,
+            output: output,
+            variadic: false
+        }
     });
     let i_ty = ty::lookup_item_type(ccx.tcx, local_def(it.id));
     let i_n_tps = i_ty.generics.type_param_defs.len();
index 9c69e6fd85cc7d60cfb9985dd5c369c3e3e7ee87..353639051097a966b80ba764b6a32ee8fc3c28eb 100644 (file)
@@ -605,7 +605,8 @@ pub fn compare_impl_method(tcx: ty::ctxt,
                                            .bound_lifetime_names
                                            .clone(),
                                 inputs: trait_fn_args,
-                                output: trait_m.fty.sig.output
+                                output: trait_m.fty.sig.output,
+                                variadic: false
                             }
                         });
     let impl_fty =
@@ -620,7 +621,8 @@ pub fn compare_impl_method(tcx: ty::ctxt,
                                           .bound_lifetime_names
                                           .clone(),
                                     inputs: impl_fn_args,
-                                    output: impl_m.fty.sig.output
+                                    output: impl_m.fty.sig.output,
+                                    variadic: false
                             }
                         });
 
@@ -1291,9 +1293,12 @@ pub fn ty_of_foreign_fn_decl(ccx: &CrateCtxt,
         ty::BareFnTy {
             abis: abis,
             purity: ast::unsafe_fn,
-            sig: ty::FnSig {bound_lifetime_names: opt_vec::Empty,
-                            inputs: input_tys,
-                            output: output_ty}
+            sig: ty::FnSig {
+                bound_lifetime_names: opt_vec::Empty,
+                inputs: input_tys,
+                output: output_ty,
+                variadic: decl.variadic
+            }
         });
     let tpt = ty_param_bounds_and_ty {
         generics: ty_generics,
index 2d810f1da746fab196122fb83c2bb94460795b45..5d0f44ae7e3563fef8d0ec92583d2d81f1d14ce2 100644 (file)
@@ -414,8 +414,7 @@ pub fn eq_opt_regions<C:Combine>(
     }
 }
 
-pub fn super_fn_sigs<C:Combine>(
-    this: &C, a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
+pub fn super_fn_sigs<C:Combine>(this: &C, a: &ty::FnSig, b: &ty::FnSig) -> cres<ty::FnSig> {
 
     fn argvecs<C:Combine>(this: &C, a_args: &[ty::t], b_args: &[ty::t]) -> cres<~[ty::t]> {
         if a_args.len() == b_args.len() {
@@ -426,12 +425,19 @@ fn argvecs<C:Combine>(this: &C, a_args: &[ty::t], b_args: &[ty::t]) -> cres<~[ty
         }
     }
 
+    if a.variadic != b.variadic {
+        return Err(ty::terr_variadic_mismatch(expected_found(this, a.variadic, b.variadic)));
+    }
+
     do argvecs(this, a.inputs, b.inputs)
             .and_then |inputs| {
         do this.tys(a.output, b.output).and_then |output| {
-            Ok(FnSig {bound_lifetime_names: opt_vec::Empty, // FIXME(#4846)
-                      inputs: inputs.clone(),
-                      output: output})
+            Ok(FnSig {
+                bound_lifetime_names: opt_vec::Empty, // FIXME(#4846)
+                inputs: inputs.clone(),
+                output: output,
+                variadic: a.variadic
+            })
         }
     }
 }
index 48660ab64e591b541a0bad53009f7e0a039786e0..d7a00ebf0e940ad8f5437b48741a891dafa98a2c 100644 (file)
@@ -190,8 +190,11 @@ pub fn t_fn(&self, input_tys: &[ty::t], output_ty: ty::t) -> ty::t {
                           onceness: ast::Many,
                           region: ty::re_static,
                           bounds: @~[]},
-            sig: FnSig {inputs: inputs,
-                        output: output_ty}
+            sig: FnSig {
+                inputs: inputs,
+                output: output_ty,
+                variadic: false
+            }
         })
     }
 
index 10005bfb2cb68cae39342397f60e8c757e435d9a..75c7adfb03e405e764d2f9eae5368995e637d023 100644 (file)
@@ -356,7 +356,8 @@ fn check_main_fn_ty(ccx: &CrateCtxt,
                 sig: ty::FnSig {
                     bound_lifetime_names: opt_vec::Empty,
                     inputs: ~[],
-                    output: ty::mk_nil()
+                    output: ty::mk_nil(),
+                    variadic: false
                 }
             });
 
@@ -404,7 +405,8 @@ fn check_start_fn_ty(ccx: &CrateCtxt,
                         ty::mk_int(),
                         ty::mk_imm_ptr(tcx, ty::mk_imm_ptr(tcx, ty::mk_u8()))
                     ],
-                    output: ty::mk_int()
+                    output: ty::mk_int(),
+                    variadic: false
                 }
             });
 
index ab1ca420afab15b850f6ee0bf9353688864f7de6..006e14232f368e65fdf1c1d7f2bce5ac4eedb3ab 100644 (file)
@@ -410,7 +410,11 @@ fn push_sig_to_str(cx: ctxt,
         s.push_char(bra);
         let strs = sig.inputs.map(|a| fn_input_to_str(cx, *a));
         s.push_str(strs.connect(", "));
+        if sig.variadic {
+            s.push_str(", ...");
+        }
         s.push_char(ket);
+
         if ty::get(sig.output).sty != ty_nil {
             s.push_str(" -> ");
             if ty::type_is_bot(sig.output) {
index 19fa9abc0da55013e539ee3475b1bd39f60aef80..c841750d7ce7a093231a2ee274b9adf37a74c4b0 100644 (file)
@@ -382,11 +382,18 @@ fn visit_fn_input(&mut self, i: uint, mode: uint, inner: *TyDesc) -> bool {
         true
     }
 
+    #[cfg(stage0)]
     fn visit_fn_output(&mut self, retstyle: uint, inner: *TyDesc) -> bool {
         if ! self.inner.visit_fn_output(retstyle, inner) { return false; }
         true
     }
 
+    #[cfg(not(stage0))]
+    fn visit_fn_output(&mut self, retstyle: uint, variadic: bool, inner: *TyDesc) -> bool {
+        if ! self.inner.visit_fn_output(retstyle, variadic, inner) { return false; }
+        true
+    }
+
     fn visit_leave_fn(&mut self, purity: uint, proto: uint,
                       n_inputs: uint, retstyle: uint) -> bool {
         if ! self.inner.visit_leave_fn(purity, proto, n_inputs, retstyle) {
index dd68c57e37e9763f896ca75a10a7827aad31c9c2..97b42a1ebc1ca36094bd6b85834963c2b2f359ae 100644 (file)
@@ -572,6 +572,7 @@ fn visit_fn_input(&mut self, i: uint, _mode: uint, inner: *TyDesc) -> bool {
         true
     }
 
+    #[cfg(stage0)]
     fn visit_fn_output(&mut self, _retstyle: uint, inner: *TyDesc) -> bool {
         self.writer.write(")".as_bytes());
         let name = unsafe { (*inner).name };
@@ -582,6 +583,20 @@ fn visit_fn_output(&mut self, _retstyle: uint, inner: *TyDesc) -> bool {
         true
     }
 
+    #[cfg(not(stage0))]
+    fn visit_fn_output(&mut self, _retstyle: uint, variadic: bool, inner: *TyDesc) -> bool {
+        if variadic {
+            self.writer.write(", ...".as_bytes());
+        }
+        self.writer.write(")".as_bytes());
+        let name = unsafe { (*inner).name };
+        if name != "()" {
+            self.writer.write(" -> ".as_bytes());
+            self.writer.write(name.as_bytes());
+        }
+        true
+    }
+
     fn visit_leave_fn(&mut self, _purity: uint, _proto: uint,
                       _n_inputs: uint, _retstyle: uint) -> bool { true }
 
index c086e13d74024e37007b3b724789f21fcf744285..5189fdce115d1d7c02f6a85aebf66cbde9d6aebb 100644 (file)
@@ -160,7 +160,10 @@ fn visit_leave_enum(&mut self, n_variants: uint,
     fn visit_enter_fn(&mut self, purity: uint, proto: uint,
                       n_inputs: uint, retstyle: uint) -> bool;
     fn visit_fn_input(&mut self, i: uint, mode: uint, inner: *TyDesc) -> bool;
+    #[cfg(stage0)]
     fn visit_fn_output(&mut self, retstyle: uint, inner: *TyDesc) -> bool;
+    #[cfg(not(stage0))]
+    fn visit_fn_output(&mut self, retstyle: uint, variadic: bool, inner: *TyDesc) -> bool;
     fn visit_leave_fn(&mut self, purity: uint, proto: uint,
                       n_inputs: uint, retstyle: uint) -> bool;
 
index cf72455a83a06e03361937c7a4a470abc0ceea56..6aec7380f40156d4641521c81940191b0efd299d 100644 (file)
@@ -892,6 +892,7 @@ pub struct fn_decl {
     inputs: ~[arg],
     output: Ty,
     cf: ret_style,
+    variadic: bool
 }
 
 #[deriving(Clone, Eq, Encodable, Decodable, IterBytes)]
index 76d9f755d3c9f64a86e1f929721846420be6fe21..0a5e20fc2b29a3eae8ab603848dc06f3c655aab4 100644 (file)
@@ -728,6 +728,7 @@ fn fn_decl(&self, inputs: ~[ast::arg], output: ast::Ty) -> ast::fn_decl {
             inputs: inputs,
             output: output,
             cf: ast::return_val,
+            variadic: false
         }
     }
 
index d579e7dd2c7e4b264960205debe8b7f005aaefbd..388de29b45680c3fe470deeb67c9a84a695c9091 100644 (file)
@@ -99,6 +99,7 @@ fn fold_foreign_item(&self, ni: @foreign_item) -> @foreign_item {
                                                                       self)),
                                 output: self.fold_ty(&fdec.output),
                                 cf: fdec.cf,
+                                variadic: fdec.variadic
                             },
                             fold_generics(generics, self))
                     }
@@ -466,6 +467,7 @@ pub fn fold_fn_decl<T:ast_fold>(decl: &ast::fn_decl, fld: &T)
         inputs: decl.inputs.map(|x| fold_arg_(x, fld)), // bad copy
         output: fld.fold_ty(&decl.output),
         cf: decl.cf,
+        variadic: decl.variadic
     }
 }
 
index 7ac999c46a40017e5f9a5efda8dea962b727f1dd..49445312a12f6a7df91f1d0a7edbddca5204d2de 100644 (file)
@@ -664,12 +664,18 @@ fn binop(rdr: @mut StringReader, op: token::binop) -> token::Token {
       ';' => { bump(rdr); return token::SEMI; }
       ',' => { bump(rdr); return token::COMMA; }
       '.' => {
-        bump(rdr);
-        if rdr.curr == '.' && nextch(rdr) != '.' {
-            bump(rdr);
-            return token::DOTDOT;
-        }
-        return token::DOT;
+          bump(rdr);
+          return if rdr.curr == '.' {
+              bump(rdr);
+              if rdr.curr == '.' {
+                  bump(rdr);
+                  token::DOTDOTDOT
+              } else {
+                  token::DOTDOT
+              }
+          } else {
+              token::DOT
+          };
       }
       '(' => { bump(rdr); return token::LPAREN; }
       ')' => { bump(rdr); return token::RPAREN; }
index fbe711b5efe9e7cd5f88bf47669cfacf85ba14f5..6c81784b5dee48e09154ff74dc6049809261b853 100644 (file)
@@ -701,7 +701,8 @@ fn parser_done(p: Parser){
                                 output: ast::Ty{id: ast::DUMMY_NODE_ID,
                                                  node: ast::ty_nil,
                                                  span:sp(15,15)}, // not sure
-                                cf: ast::return_val
+                                cf: ast::return_val,
+                                variadic: false
                             },
                                     ast::impure_fn,
                                     abi::AbiSet::Rust(),
index 56254704e28d566fb7c37eccfb36267468747c59..7c98d8d1c85b344db905e616f2ef218032048cad 100644 (file)
@@ -863,7 +863,7 @@ pub fn parse_ty_bare_fn(&self) -> ty_ {
         let abis = opt_abis.unwrap_or(AbiSet::Rust());
         let purity = self.parse_unsafety();
         self.expect_keyword(keywords::Fn);
-        let (decl, lifetimes) = self.parse_ty_fn_decl();
+        let (decl, lifetimes) = self.parse_ty_fn_decl(true);
         return ty_bare_fn(@TyBareFn {
             abis: abis,
             purity: purity,
@@ -875,7 +875,7 @@ pub fn parse_ty_bare_fn(&self) -> ty_ {
     // Parses a procedure type (`proc`). The initial `proc` keyword must
     // already have been parsed.
     pub fn parse_proc_type(&self) -> ty_ {
-        let (decl, lifetimes) = self.parse_ty_fn_decl();
+        let (decl, lifetimes) = self.parse_ty_fn_decl(false);
         ty_closure(@TyClosure {
             sigil: OwnedSigil,
             region: None,
@@ -919,7 +919,7 @@ pub fn parse_ty_closure(&self,
                 // Old-style closure syntax (`fn(A)->B`).
                 self.expect_keyword(keywords::Fn);
                 let bounds = self.parse_optional_ty_param_bounds();
-                let (decl, lifetimes) = self.parse_ty_fn_decl();
+                let (decl, lifetimes) = self.parse_ty_fn_decl(false);
                 (sigil, decl, lifetimes, bounds)
             }
             None => {
@@ -960,6 +960,7 @@ pub fn parse_ty_closure(&self,
                     inputs: inputs,
                     output: output,
                     cf: return_style,
+                    variadic: false
                 };
 
                 (BorrowedSigil, decl, lifetimes, bounds)
@@ -994,7 +995,7 @@ pub fn parse_unsafety(&self) -> purity {
     }
 
     // parse a function type (following the 'fn')
-    pub fn parse_ty_fn_decl(&self) -> (fn_decl, OptVec<ast::Lifetime>) {
+    pub fn parse_ty_fn_decl(&self, allow_variadic: bool) -> (fn_decl, OptVec<ast::Lifetime>) {
         /*
 
         (fn) <'lt> (S) -> T
@@ -1013,17 +1014,13 @@ pub fn parse_ty_fn_decl(&self) -> (fn_decl, OptVec<ast::Lifetime>) {
             opt_vec::Empty
         };
 
-        let inputs = self.parse_unspanned_seq(
-            &token::LPAREN,
-            &token::RPAREN,
-            seq_sep_trailing_disallowed(token::COMMA),
-            |p| p.parse_arg_general(false)
-        );
+        let (inputs, variadic) = self.parse_fn_args(false, allow_variadic);
         let (ret_style, ret_ty) = self.parse_ret_ty();
         let decl = ast::fn_decl {
             inputs: inputs,
             output: ret_ty,
-            cf: ret_style
+            cf: ret_style,
+            variadic: variadic
         };
         (decl, lifetimes)
     }
@@ -2475,7 +2472,8 @@ pub fn parse_lambda_block_expr(&self) -> @Expr {
                               node: ty_infer,
                               span: *self.span
                           },
-                          cf: return_val
+                          cf: return_val,
+                          variadic: false
                       }
                   }
                 }
@@ -3526,21 +3524,63 @@ fn parse_generic_values_after_lt(&self) -> (OptVec<ast::Lifetime>, ~[Ty]) {
         (lifetimes, opt_vec::take_vec(result))
     }
 
-    // parse the argument list and result type of a function declaration
-    pub fn parse_fn_decl(&self) -> fn_decl {
-        let args: ~[arg] =
+    fn parse_fn_args(&self, named_args: bool, allow_variadic: bool) -> (~[arg], bool) {
+        let sp = *self.span;
+        let mut args: ~[Option<arg>] =
             self.parse_unspanned_seq(
                 &token::LPAREN,
                 &token::RPAREN,
                 seq_sep_trailing_disallowed(token::COMMA),
-                |p| p.parse_arg()
+                |p| {
+                    if *p.token == token::DOTDOTDOT {
+                        p.bump();
+                        if allow_variadic {
+                            if *p.token != token::RPAREN {
+                                p.span_fatal(*p.span,
+                                    "`...` must be last in argument list for variadic function");
+                            }
+                        } else {
+                            p.span_fatal(*p.span,
+                                         "only foreign functions are allowed to be variadic");
+                        }
+                        None
+                    } else {
+                        Some(p.parse_arg_general(named_args))
+                    }
+                }
             );
 
+        let variadic = match args.pop_opt() {
+            Some(None) => true,
+            Some(x) => {
+                // Need to put back that last arg
+                args.push(x);
+                false
+            }
+            None => false
+        };
+
+        if variadic && args.is_empty() {
+            self.span_err(sp,
+                          "variadic function must be declared with at least one named argument");
+        }
+
+        let args = args.move_iter().map(|x| x.unwrap()).collect();
+
+        (args, variadic)
+    }
+
+    // parse the argument list and result type of a function declaration
+    pub fn parse_fn_decl(&self, allow_variadic: bool) -> fn_decl {
+
+        let (args, variadic) = self.parse_fn_args(true, allow_variadic);
         let (ret_style, ret_ty) = self.parse_ret_ty();
+
         ast::fn_decl {
             inputs: args,
             output: ret_ty,
             cf: ret_style,
+            variadic: variadic
         }
     }
 
@@ -3729,7 +3769,8 @@ fn maybe_parse_borrowed_explicit_self(this: &Parser) -> ast::explicit_self_ {
         let fn_decl = ast::fn_decl {
             inputs: fn_inputs,
             output: ret_ty,
-            cf: ret_style
+            cf: ret_style,
+            variadic: false
         };
 
         (spanned(lo, hi, explicit_self), fn_decl)
@@ -3759,6 +3800,7 @@ fn parse_fn_block_decl(&self) -> fn_decl {
             inputs: inputs_captures,
             output: output,
             cf: return_val,
+            variadic: false
         }
     }
 
@@ -3784,6 +3826,7 @@ fn parse_proc_decl(&self) -> fn_decl {
             inputs: inputs,
             output: output,
             cf: return_val,
+            variadic: false
         }
     }
 
@@ -3808,7 +3851,7 @@ fn mk_item(&self, lo: BytePos, hi: BytePos, ident: Ident,
     // parse an item-position function declaration.
     fn parse_item_fn(&self, purity: purity, abis: AbiSet) -> item_info {
         let (ident, generics) = self.parse_fn_header();
-        let decl = self.parse_fn_decl();
+        let decl = self.parse_fn_decl(false);
         let (inner_attrs, body) = self.parse_inner_attrs_and_block();
         (ident,
          item_fn(decl, purity, abis, generics, body),
@@ -4239,7 +4282,7 @@ fn parse_item_foreign_fn(&self, vis: ast::visibility,
         }
 
         let (ident, generics) = self.parse_fn_header();
-        let decl = self.parse_fn_decl();
+        let decl = self.parse_fn_decl(true);
         let hi = self.span.hi;
         self.expect(&token::SEMI);
         @ast::foreign_item { ident: ident,
index 3d8fa1b672811e682bbc952db9f6e4588dac6c4f..63f4f97889c6dfd82ef6ed011222c0cf4f68cafc 100644 (file)
@@ -54,6 +54,7 @@ pub enum Token {
     AT,
     DOT,
     DOTDOT,
+    DOTDOTDOT,
     COMMA,
     SEMI,
     COLON,
@@ -147,6 +148,7 @@ pub fn to_str(input: @ident_interner, t: &Token) -> ~str {
       AT => ~"@",
       DOT => ~".",
       DOTDOT => ~"..",
+      DOTDOTDOT => ~"...",
       COMMA => ~",",
       SEMI => ~";",
       COLON => ~":",
index 0eb1045efe95fd1464816db9d680e65b4f52da9d..08c2ae88e4fabbf14966ff947bd6423bea63b150 100644 (file)
@@ -1774,6 +1774,9 @@ pub fn print_fn_args_and_ret(s: @ps, decl: &ast::fn_decl,
                              opt_explicit_self: Option<ast::explicit_self_>) {
     popen(s);
     print_fn_args(s, decl, opt_explicit_self);
+    if decl.variadic {
+        word(s.s, ", ...");
+    }
     pclose(s);
 
     maybe_print_comment(s, decl.output.span.lo);
@@ -2066,6 +2069,9 @@ pub fn print_ty_fn(s: @ps,
 
         opt_bounds.as_ref().map(|bounds| print_bounds(s, bounds, true));
     } else {
+        if decl.variadic {
+            word(s.s, ", ...");
+        }
         pclose(s);
     }
 
@@ -2408,7 +2414,8 @@ fn test_fun_to_str() {
             output: ast::Ty {id: 0,
                               node: ast::ty_nil,
                               span: codemap::dummy_sp()},
-            cf: ast::return_val
+            cf: ast::return_val,
+            variadic: false
         };
         let generics = ast_util::empty_generics();
         assert_eq!(&fun_to_str(&decl, ast::impure_fn, abba_ident,
index de8d9470f102716f0d7957d2f436db20b7b59fdc..42614f76539283df7ee882252f948d3f2b17e592 100644 (file)
@@ -366,8 +366,8 @@ fn visit_fn_input(&mut self, i: uint, mode: uint, inner: *TyDesc) -> bool {
         true
     }
 
-    fn visit_fn_output(&mut self, retstyle: uint, inner: *TyDesc) -> bool {
-        if ! self.inner.visit_fn_output(retstyle, inner) { return false; }
+    fn visit_fn_output(&mut self, retstyle: uint, variadic: bool, inner: *TyDesc) -> bool {
+        if ! self.inner.visit_fn_output(retstyle, variadic, inner) { return false; }
         true
     }
 
@@ -603,7 +603,7 @@ fn visit_enter_fn(&mut self, _purity: uint, _proto: uint,
     fn visit_fn_input(&mut self, _i: uint, _mode: uint, _inner: *TyDesc) -> bool {
         true
     }
-    fn visit_fn_output(&mut self, _retstyle: uint, _inner: *TyDesc) -> bool {
+    fn visit_fn_output(&mut self, _retstyle: uint, _variadic: bool, _inner: *TyDesc) -> bool {
         true
     }
     fn visit_leave_fn(&mut self, _purity: uint, _proto: uint,
index e77cb432c3ad0c7567a601a55b38b2df67740832..03722cd0fbf2c268645cc583f6effbbe7208ec42 100644 (file)
@@ -135,7 +135,7 @@ fn visit_leave_enum(&mut self,
     fn visit_enter_fn(&mut self, _purity: uint, _proto: uint,
                       _n_inputs: uint, _retstyle: uint) -> bool { true }
     fn visit_fn_input(&mut self, _i: uint, _mode: uint, _inner: *TyDesc) -> bool { true }
-    fn visit_fn_output(&mut self, _retstyle: uint, _inner: *TyDesc) -> bool { true }
+    fn visit_fn_output(&mut self, _retstyle: uint, _variadic: bool, _inner: *TyDesc) -> bool { true }
     fn visit_leave_fn(&mut self, _purity: uint, _proto: uint,
                       _n_inputs: uint, _retstyle: uint) -> bool { true }