diff --git a/bin/precompile.pike b/bin/precompile.pike
index a178626820380048758f0f3b3f38cb2e7095bbe8..8e18bd13ccd1e5db63964969e11b6da257a4f3a7 100644
--- a/bin/precompile.pike
+++ b/bin/precompile.pike
@@ -75,11 +75,35 @@ constant precompile_api_version = "2";
  * disabled by putting ", ..." last in the argument list.
  *
  * Magic types in function argument lists:
+ *
  *   bignum    A native int or bignum object. The C storage type is
  *             struct svalue.
+ *
  *   longest   A native int or bignum object which is range checked and stored
  *             in a LONGEST.
  *
+ *   zero      If used like "zero|Something" then a zero value is allowed in
+ *             addition to Something (which is how it works by default in pike
+ *             code). This will not cause an svalue to be constructed on the C
+ *             level, rather 0/NULL is assigned to the C type.
+ *
+ *             Note that this is almost the same as how void works in
+ *             "void|Something", except that the argument still is required.
+ *
+ *             "zero" interacts with "void" in a special way: If you write
+ *             "void|int" then the C storage will be an svalue to make it
+ *             possible to tell a missing argument from a zero integer. If you
+ *             write "void|zero|int" then it's assumed that you don't care
+ *             about that, and so the C storage will be an INT_TYPE which will
+ *             be assigned zero both if the argument is zero and when it's left
+ *             out. This applies to the int and float runtime types, but not
+ *             for the special case "void|zero".
+ *
+ * Note: If a function argument accepts more than one runtime type, e.g.
+ * "int|string", then no type checking at all is performed on the svalue. That
+ * is not a bug - the user code have to do a type check later anyway, and it's
+ * more efficient to add the error handling there.
+ *
  * Currently, the following attributes are understood:
  *   efun;          makes this function a global constant (no value)
  *   flags;         ID_STATIC | ID_NOMASK etc.
@@ -376,6 +400,20 @@ class PikeType
       }
     }
 
+  static array(PikeType) strip_zero_alt()
+  /* Assumes this is an '|' node. Returns args without any 'zero'
+   * alternatives. */
+  {
+    if (!args) return 0;
+    array(PikeType) a = ({});
+    foreach (args, PikeType arg)
+      if (arg->basetype() != "zero") a += ({arg});
+    if (!sizeof (a) && sizeof (args))
+      // Make sure we don't strip away the entire type.
+      a = ({args[0]});
+    return a;
+  }
+
   /*
    * return the 'one-word' description of this type
    */
@@ -387,11 +425,27 @@ class PikeType
 	case "CTYPE":
 	  return args[1]->basetype();
 
-	case "|":
-	  array(string) tmp=args->basetype() - ({"void"});
-	  if(sizeof (tmp) == 1 || (sizeof (tmp) > 1 && `==(@tmp)))
-	    return tmp[0];
-	  return "mixed";
+	case "|": {
+	  // If there's only one type except "void" and "zero" then return it,
+	  // else return "mixed". Plus some special cases if we only got "void"
+	  // and "zero" in there.
+	  int got_zero;
+	  string single_type;
+	  foreach (args, PikeType arg) {
+	    string type = arg->basetype();
+	    if (type == "zero")
+	      got_zero = 1;
+	    else if (type != "void") {
+	      if (single_type && single_type != type)
+		return "mixed";
+	      else
+		single_type = type;
+	    }
+	  }
+	  if (single_type) return single_type;
+	  else if (got_zero) return "zero";
+	  else return "void";
+	}
 
 	case "=":
 	case "&":
@@ -429,6 +483,7 @@ class PikeType
       case "program":
       case "mixed":
       case "void":
+      case "zero":
 	return ret;
 
 	default: return "object";
@@ -509,6 +564,7 @@ class PikeType
       case "multiset":
       case "type":
       case "void":
+      case "zero":
 	return ({ ret });
 
 	case "bignum":
@@ -534,35 +590,43 @@ class PikeType
     }
 
   /*
-   * Return 1 if this type is or matches 'void'
+   * Check if this type matches 'void' and/or 'zero'.
    */
-  int may_be_void()
+  int may_be_void_or_zero (int(0..) may_be_void, int(0..) may_be_zero)
+  {
+    switch((string)t)
     {
-      switch((string)t)
-      {
-	case "void": return 1;
-	case "|":
-	  for(int e=0;e<sizeof(args);e++)
-	    if(args[e]->may_be_void())
-	      return 1;
-	  return 0;
+      case "zero": return may_be_zero;
+      case "void": return may_be_void;
+      case "|":
+	return max (0, @args->may_be_void_or_zero (may_be_void, may_be_zero));
 
-	case "=":
-	case "&":
-	  return args[-1]->may_be_void();
-      }
+      case "=":
+      case "&":
+	return args[-1]->may_be_void_or_zero (may_be_void, may_be_zero);
     }
+    return 0;
+  }
+
+  int may_be_void()		// Compat wrapper.
+  {
+    return may_be_void_or_zero (1, 0);
+  }
 
   string c_storage_type(int|void is_struct_entry)
     {
-      string btype = /*may_be_void() ? "mixed" : */realtype();
+      string btype = realtype();
       switch (btype)
       {
 	case "void": return "void";
-	case "int":
+	case "zero":
 	  return may_be_void()?"struct svalue *":"INT_TYPE";
+	case "int":
+	  return (may_be_void_or_zero (1, 2) == 1 ?
+		  "struct svalue *" : "INT_TYPE");
 	case "float":
-	  return may_be_void()?"struct svalue *":"FLOAT_TYPE";
+	  return (may_be_void_or_zero (1, 2) == 1 ?
+		  "struct svalue *" : "FLOAT_TYPE");
 	case "string": return "struct pike_string *";
 	  
 	case "array":
@@ -591,9 +655,10 @@ class PikeType
     }
 
   /*
-   * Make a C representation, like 'tInt'
+   * Make a C representation, like 'tInt'. Can also strip off an
+   * "|zero" alternative on the top level.
    */
-  string output_c_type()
+  string output_c_type (void|int ignore_zero_alt)
     {
       string ret=(string)t;
 //      werror("FOO: %O %O\n",t,args);
@@ -606,7 +671,9 @@ class PikeType
 	  return fiddle("tAnd",args->output_c_type());
 	  
 	case "|":
-	  return fiddle("tOr",args->output_c_type());
+	  array(PikeType) a = strip_zero_alt();
+	  if (sizeof (a) == 1) return a[0]->output_c_type();
+	  return fiddle("tOr",a->output_c_type());
 
 	case "!":
 	  return sprintf("tNot(%s)",args[0]->output_c_type());
@@ -644,7 +711,7 @@ class PikeType
 	case "=":
 	  return sprintf("tSetvar(%s,%s)",
 			 (string)args[0]->t,
-			 args[1]->output_c_type());
+			 args[1]->output_c_type (ignore_zero_alt));
 
 	case "0":
 	case "1":
@@ -757,13 +824,19 @@ class PikeType
     }
 
   /* 
-   * Copy and remove type assignments
+   * Copy and remove type assignments. Can also strip off an "|zero"
+   * alternative on the top level.
    */
-  PikeType copy_and_strip_type_assignments()
+  PikeType copy_and_strip_type_assignments (void|int ignore_zero_alt)
     {
       if("=" == (string)t)
-	return args[1]->copy_and_strip_type_assignments();
-      return PikeType(t, args && args->copy_and_strip_type_assignments());
+	return args[1]->copy_and_strip_type_assignments (ignore_zero_alt);
+      array(PikeType) a;
+      if (args && ignore_zero_alt && (string) t == "|")
+	a = strip_zero_alt();
+      else
+	a = args;
+      return PikeType(t, a && a->copy_and_strip_type_assignments (0));
     }
 
   string _sprintf(int how)
@@ -974,6 +1047,8 @@ class Argument
   string _realtype;
 
   int is_c_type() { return _is_c_type; }
+  int may_be_void_or_zero (int may_be_void, int may_be_zero)
+    { return type()->may_be_void_or_zero (may_be_void, may_be_zero); }
   int may_be_void() { return type()->may_be_void(); }
   int line() { return _line; }
   string name() { return _name; }
@@ -1001,7 +1076,7 @@ class Argument
     {
       if(_typename) return _typename;
       return _typename=
-	type()->copy_and_strip_type_assignments()->output_pike_type(0);
+	type()->copy_and_strip_type_assignments (1)->output_pike_type(0);
     }
 
   void create(array x)
@@ -2032,6 +2107,8 @@ class ParseBlock
 
 	  foreach(args, Argument arg)
 	  {
+	    int got_void_or_zero_check = 0;
+
 	    if (argnum == repeat_arg) {
 	      // Begin the argcnt loop.
 	      check_argbase = "+argcnt"+argbase;
@@ -2043,21 +2120,36 @@ class ParseBlock
 				 arg->line()) });
 	    }
 
-	    else if(arg->may_be_void())
-	    {
-	      if ((arg->basetype() == "int") || (arg->basetype() == "mixed")) {
-		ret+=({
-		  PC.Token(sprintf("if (args > %d) {",argnum)),
-		});
-	      } else {
-		ret+=({
-		  PC.Token(sprintf("if ((args > %d) && \n"
-				   "    ((Pike_sp[%d%s].type != PIKE_T_INT) ||\n"
-				   "     (Pike_sp[%d%s].u.integer))) {",
-				   argnum,
-				   argnum, check_argbase,
-				   argnum, check_argbase)),
-		});
+	    else {
+	      int void_or_zero = arg->may_be_void_or_zero (2, 1);
+	      if (void_or_zero == 2) {
+		if (!(<"int","mixed">)[arg->basetype()]) {
+		  ret+=({
+		    PC.Token(sprintf("if (args > %d &&"
+				     "    (Pike_sp[%d%s].type != PIKE_T_INT ||"
+				     "     Pike_sp[%d%s].u.integer)) {\n",
+				     argnum,
+				     argnum, check_argbase,
+				     argnum, check_argbase), arg->line()),
+		  });
+		} else {
+		  ret+=({
+		    PC.Token(sprintf("if (args > %d) {\n",argnum), arg->line()),
+		  });
+		}
+		got_void_or_zero_check = 1;
+	      }
+
+	      else if (void_or_zero == 1) {
+		if (!(<"int", "mixed">)[arg->basetype()]) {
+		  ret += ({
+		    PC.Token (sprintf ("if (Pike_sp[%d%s].type != PIKE_T_INT ||"
+				       "    Pike_sp[%d%s].u.integer) {\n",
+				       argnum, check_argbase,
+				       argnum, check_argbase), arg->line()),
+		  });
+		  got_void_or_zero_check = 1;
+		}
 	      }
 	    }
 
@@ -2147,16 +2239,17 @@ class ParseBlock
 	      {
 		case "INT_TYPE":
 		  ret+=({
-		    sprintf("%s=Pike_sp[%d%s].u.integer;\n",arg->name(),
-			    argnum,argbase)
+		    PC.Token (sprintf("%s=Pike_sp[%d%s].u.integer;\n",
+				      arg->name(), argnum,argbase),
+			      arg->line())
 		  });
 		  break;
 
 		case "FLOAT_TYPE":
 		  ret+=({
-		    sprintf("%s=Pike_sp[%d%s].u.float_number;\n",
-			    arg->name(),
-			    argnum,argbase)
+		    PC.Token (sprintf("%s=Pike_sp[%d%s].u.float_number;\n",
+				      arg->name(), argnum,argbase),
+			      arg->line())
 		  });
 		  break;
 
@@ -2232,9 +2325,17 @@ class ParseBlock
 		case "program":
 	      }
 
-	      if(arg->may_be_void())
+	      if (got_void_or_zero_check)
 	      {
-		ret+=({  PC.Token(sprintf("} else %s=0;\n", arg->name())) });
+		string zero_val;
+		switch (arg->c_type()) {
+		  case "INT_TYPE": zero_val = "0"; break;
+		  case "FLOAT_TYPE": zero_val = "0.0"; break;
+		  default: zero_val = "NULL"; break;
+		}
+		ret+=({  PC.Token(sprintf("} else %s = %s;\n",
+					  arg->name(), zero_val),
+				  arg->line()) });
 	      }
 	    }