~sircmpwn/hare

d97c115d356eb2b9d3669f1a4cafe5974ee9a180 — Alexey Yerin 7 months ago 9c8cf7b
Update match statements according to spec update
M ascii/strcmp.ha => ascii/strcmp.ha +8 -8
@@ 8,14 8,14 @@ export fn strcmp(a: str, b: str) (int | void) = {
	let a = strings::iter(a), b = strings::iter(b);
	for (true) {
		let ra = match (strings::next(&a)) {
			void => return match (strings::next(&b)) {
				void => 0,
				rune => -1,
			_: void => return match (strings::next(&b)) {
				_: void => 0,
				_: rune => -1,
			},
			r: rune => r,
		};
		let rb = match (strings::next(&b)) {
			void => return 1,
			_: void => return 1,
			r: rune => r,
		};
		if (!isascii(ra) || !isascii(rb)) {


@@ 33,14 33,14 @@ export fn strcasecmp(a: str, b: str) (int | void) = {
	let a = strings::iter(a), b = strings::iter(b);
	for (true) {
		let ra = match (strings::next(&a)) {
			void => return match (strings::next(&b)) {
				void => 0,
				rune => -1,
			_: void => return match (strings::next(&b)) {
				_: void => 0,
				_: rune => -1,
			},
			r: rune => r,
		};
		let rb = match (strings::next(&b)) {
			void => return 1,
			_: void => return 1,
			r: rune => r,
		};
		if (!isascii(ra) || !isascii(rb)) {

M bufio/scanner.ha => bufio/scanner.ha +5 -5
@@ 10,7 10,7 @@ export fn scanbyte(stream: *io::stream) (u8 | io::EOF | io::error) = {

	return match (io::read(stream, buf)?) {
		read: size => if (read > 0) buf[0] else io::EOF,
		io::EOF    => io::EOF,
		e: io::EOF => e,
	};
};



@@ 27,7 27,7 @@ export fn scantok(stream: *io::stream, delim: u8) ([]u8 | io::EOF | io::error) =
				};
				append(buf, res);
			},
			io::EOF => {
			_: io::EOF => {
				if (len(buf) == 0) {
					return io::EOF;
				};


@@ 68,9 68,9 @@ export fn scanrune(stream: *io::stream) (rune | utf8::invalid | io::EOF | io::er

	let dec = utf8::decode(b[..sz]);
	return match (utf8::next(&dec)) {
		r: rune             => r,
		utf8::invalid       => utf8::invalid,
		(void | utf8::more) => io::EOF,
		r: rune => r,
		_: utf8::invalid => utf8::invalid,
		_: (void | utf8::more) => io::EOF,
	};
};


M bytes/tokenize.ha => bytes/tokenize.ha +13 -13
@@ 32,7 32,7 @@ export fn next_token(s: *tokenizer) ([]u8 | void) = match (peek_token(s)) {
		s.p = types::SIZE_MAX;
		return b;
	},
	void => void,
	_: void => void,
};

// Same as next_token(), but does not advance the cursor


@@ 43,7 43,7 @@ export fn peek_token(s: *tokenizer) ([]u8 | void) = {
	if (s.p > len(s.s)) {
		s.p = match (index(s.s, s.d)) {
			i: size => i,
			void => len(s.s),
			_: void => len(s.s),
		};
	};
	return s.s[..s.p];


@@ 73,7 73,7 @@ export fn remaining_tokens(s: *tokenizer) []u8 = {
	assert(equal(peek_token(&t) as []u8, peek_token(&t) as []u8));
	match (next_token(&t)) {
		b: []u8 => assert(equal([4, 5], b)),
		void    => abort(),
		_: void => abort(),
	};

	assert(peek_token(&t) is void);


@@ 85,20 85,20 @@ export fn remaining_tokens(s: *tokenizer) []u8 = {
	assert(equal(peek_token(&t) as []u8, peek_token(&t) as []u8));
	match (next_token(&t)) {
		b: []u8 => assert(equal([], b)),
		void    => abort(),
		_: void => abort(),
	};

	assert(equal(peek_token(&t) as []u8, peek_token(&t) as []u8));
	match (next_token(&t)) {
		b: []u8 => assert(equal([1], b)),
		void    => abort(),
		_: void => abort(),
	};

	//assert(equal(peek_token(&t) as []u8, peek_token(&t) as []u8));
	//assert(false);
	match (next_token(&t)) {
		b: []u8 => assert(equal([], b)),
		void    => abort(),
		_: void => abort(),
	};

	assert(peek_token(&t) is void);


@@ 109,17 109,17 @@ export fn remaining_tokens(s: *tokenizer) []u8 = {

	match (next_token(&t)) {
		b: []u8 => assert(equal([1, 1], b)),
		void    => abort(),
		_: void => abort(),
	};

	match (next_token(&t)) {
		b: []u8 => assert(equal([1], b)),
		void    => abort(),
		_: void => abort(),
	};

	match (next_token(&t)) {
		b: []u8 => assert(equal([2], b)),
		void    => abort(),
		_: void => abort(),
	};

	assert(next_token(&t) is void);


@@ 129,12 129,12 @@ export fn remaining_tokens(s: *tokenizer) []u8 = {

	match (next_token(&t)) {
		b: []u8 => assert(equal([], b)),
		void    => abort(),
		_: void => abort(),
	};

	match (next_token(&t)) {
		b: []u8 => assert(equal([], b)),
		void    => abort(),
		_: void => abort(),
	};

	assert(peek_token(&t) is void);


@@ 145,12 145,12 @@ export fn remaining_tokens(s: *tokenizer) []u8 = {

	match (next_token(&t)) {
		b: []u8 => assert(equal([], b)),
		void    => abort(),
		_: void => abort(),
	};

	match (next_token(&t)) {
		b: []u8 => assert(equal([1], b)),
		void    => abort(),
		_: void => abort(),
	};

	assert(equal(remaining_tokens(&t), [2, 3, 4]));

M cmd/hare/plan.ha => cmd/hare/plan.ha +2 -2
@@ 118,7 118,7 @@ fn plan_execute(plan: *plan, verbose: bool) void = {
				task.cmd[0], exec::strerror(err)),
			err: exec::exit_status! => fmt::fatal("Error: {}: {}",
				task.cmd[0], exec::exitstr(err)),
			void => void,
			_: void => void,
		};

		task.status = status::COMPLETE;


@@ 140,7 140,7 @@ fn update_cache(plan: *plan, mod: modcache) void = {
		err: module::error => fmt::fatal(
			"Error updating module cache: {}",
			module::strerror(err)),
		void => void,
		_: void => void,
	};
};


M cmd/hare/subcmds.ha => cmd/hare/subcmds.ha +9 -9
@@ 18,7 18,7 @@ fn default_tags() []module::tag = {

fn addtags(tags: []module::tag, in: str) ([]module::tag | void) = {
	let in = match (module::parsetags(in)) {
		void => return void,
		_: void => return void,
		t: []module::tag => t,
	};
	defer free(in);


@@ 32,7 32,7 @@ fn deltags(tags: []module::tag, in: str) ([]module::tag | void) = {
		return [];
	};
	let in = match (module::parsetags(in)) {
		void => return void,
		_: void => return void,
		t: []module::tag => t,
	};
	defer free(in);


@@ 88,11 88,11 @@ fn build(args: []str) void = {
			'o' => output = opt.1,
			't' => abort(), // TODO
			'T' => tags = match (addtags(tags, opt.1)) {
				void => fmt::fatal("Error parsing tags"),
				_: void => fmt::fatal("Error parsing tags"),
				t: []module::tag => t,
			},
			'X' => tags = match (deltags(tags, opt.1)) {
				void => fmt::fatal("Error parsing tags"),
				_: void => fmt::fatal("Error parsing tags"),
				t: []module::tag => t,
			},
			*   => abort(),


@@ 193,11 193,11 @@ fn run(args: []str) void = {
			'l' => abort(), // TODO
			't' => abort(), // TODO
			'T' => tags = match (addtags(tags, opt.1)) {
				void => fmt::fatal("Error parsing tags"),
				_: void => fmt::fatal("Error parsing tags"),
				t: []module::tag => t,
			},
			'X' => tags = match (deltags(tags, opt.1)) {
				void => fmt::fatal("Error parsing tags"),
				_: void => fmt::fatal("Error parsing tags"),
				t: []module::tag => t,
			},
			*   => abort(),


@@ 277,11 277,11 @@ fn test(args: []str) void = {
			'l' => abort(), // TODO
			't' => abort(), // TODO
			'T' => tags = match (addtags(tags, opt.1)) {
				void => fmt::fatal("Error parsing tags"),
				_: void => fmt::fatal("Error parsing tags"),
				t: []module::tag => t,
			},
			'X' => tags = match (deltags(tags, opt.1)) {
				void => fmt::fatal("Error parsing tags"),
				_: void => fmt::fatal("Error parsing tags"),
				t: []module::tag => t,
			},
			*   => abort(),


@@ 343,7 343,7 @@ fn version(args: []str) void = {
	fmt::println();

	match (os::getenv("HAREPATH")) {
		void => fmt::printfln("HAREPATH\t{}", HAREPATH),
		_: void => fmt::printfln("HAREPATH\t{}", HAREPATH),
		s: str => fmt::printfln("HAREPATH\t{}\t(from environment)", s),
	};
	if (len(args) > 1 && args[1] == "-v") {

M cmd/haredoc/hare.ha => cmd/haredoc/hare.ha +1 -1
@@ 27,7 27,7 @@ fn details_hare(decl: ast::decl) (void | error) = {
		s: str => if (len(s) != 0) {
			fmt::printfln("//{}", s)?;
		},
		void => break,
		_: void => break,
	};

	unparse_hare(os::stdout, decl)?;

M cmd/haredoc/html.ha => cmd/haredoc/html.ha +4 -4
@@ 59,9 59,9 @@ fn tocentry(decl: ast::decl) (void | error) = {
	fmt::println("<li>");
	fmt::printf("{} ",
		match (decl.decl) {
			ast::decl_func => "fn",
			[]ast::decl_type => "type",
			[]ast::decl_global => "let",
			_: ast::decl_func => "fn",
			_: []ast::decl_type => "type",
			_: []ast::decl_global => "let",
		});
	fmt::printf("<a href='#");
	unparse::ident(os::stdout, decl_ident(decl))?;


@@ 116,7 116,7 @@ fn details(decl: ast::decl) (void | error) = {
		s: str => if (len(s) != 0) {
			html::escape(os::stdout, s)?;
		},
		void => break,
		_: void => break,
	};

	fmt::println("</section>")?;

M cmd/haredoc/main.ha => cmd/haredoc/main.ha +1 -1
@@ 59,7 59,7 @@ export fn main() void = {
	for (let i = 0z; i < len(unit); i += 1) {
		let summary = sort_decls(unit[i].decls);
		match (emit(summary, fmt, template)) {
			void => void,
			_: void => void,
			err: error => fmt::fatal("Error: {}", strerror(err)),
		};
	};

M compress/flate/inflate.ha => compress/flate/inflate.ha +5 -5
@@ 358,12 358,12 @@ fn read(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = {
	for (n < len(buf)) {
		if (len(s.buf) == 0) {
			if (s.left == 0) match (next(s)?) {
				void => void,
				io::EOF => return if (n == 0) io::EOF else n,
				_: void => void,
				_: io::EOF => return if (n == 0) io::EOF else n,
			};
			match (s.state(s)?) {
				void => void,
				io::EOF => return if (n == 0) io::EOF else n,
				_: void => void,
				_: io::EOF => return if (n == 0) io::EOF else n,
			};
		};
		const toread =


@@ 413,7 413,7 @@ fn close(s: *io::stream) void = {
	let s = inflate(ins);
	defer io::close(s);
	match (io::copy(outs, s)) {
		size => void,
		_: size => void,
		e: io::error => {
			fmt::errorln(io::strerror(e));
			abort();

M compress/zlib/reader.ha => compress/zlib/reader.ha +1 -1
@@ 127,7 127,7 @@ export fn decompress(s: *io::stream) (*io::stream | io::error) = {
			},
		};
		match (io::copy(out, d)) {
			size => void,
			_: size => void,
			e: io::error => {
				fmt::errorfln("vector {}: {}", i, io::strerror(e));
				abort();

M crypto/random/random.ha => crypto/random/random.ha +1 -1
@@ 27,7 27,7 @@ export let stream: *io::stream = &_stream;
	let buf: [4096]u8 = [0...];
	let test: []u8 = [];
	match (io::read(stream, buf[..])) {
		(io::error | io::EOF) => abort(),
		_: (io::error | io::EOF) => abort(),
		n: size => test = buf[..n],
	};


M dirs/xdg.ha => dirs/xdg.ha +1 -1
@@ 19,7 19,7 @@ fn lookup(prog: str, var: str, default: str) str = {
				},
			};
		},
		void => void,
		_: void => void,
	};

	let home = os::getenv("HOME") as str;

M encoding/hex/hex.ha => encoding/hex/hex.ha +1 -1
@@ 48,7 48,7 @@ export fn decode(s: str) ([]u8 | invalid) = {
	for (let i = 0z; i < len(s) / 2; i += 1) {
		let oct = strings::fromutf8_unsafe(s[i * 2..i * 2 + 2]);
		let u = match (strconv::stou8b(oct, 16)) {
			(strconv::invalid | strconv::overflow) => return invalid,
			_: (strconv::invalid | strconv::overflow) => return invalid,
			u: u8 => u,
		};
		append(buf, u);

M encoding/utf8/decode.ha => encoding/utf8/decode.ha +6 -6
@@ 96,7 96,7 @@ export fn prev(d: *decoder) (rune | void | more | invalid) = {
	let decoder = decode(input);
	for (let i = 0z; i < len(expected); i += 1) {
		match (next(&decoder)) {
			(invalid | more | void) => abort(),
			_: (invalid | more | void) => abort(),
			r: rune => assert(r == expected[i]),
		};
	};


@@ 104,7 104,7 @@ export fn prev(d: *decoder) (rune | void | more | invalid) = {
	assert(decoder.offs == len(decoder.src));
	for (let i = 0z; i < len(expected); i += 1) {
		match (prev(&decoder)) {
			(invalid | more | void) => abort(),
			_: (invalid | more | void) => abort(),
			r: rune => assert(r == expected[len(expected) - i - 1]),
		};
	};


@@ 131,10 131,10 @@ export fn valid(src: (str | []u8)) bool = {
	let decoder = decode(src);
	for (true) {
		match (next(&decoder)) {
			void    => return true,
			invalid => return false,
			more    => return false,
			rune    => void,
			_: void    => return true,
			_: invalid => return false,
			_: more    => return false,
			_: rune    => void,
		};
	};
	abort();

M fmt/fmt.ha => fmt/fmt.ha +11 -11
@@ 198,13 198,13 @@ export fn fprintf(
	let iter = strings::iter(fmt);
	for (true) {
		let r: rune = match (strings::next(&iter)) {
			void => break,
			_: void => break,
			r: rune => r,
		};

		if (r == '{') {
			r = match (strings::next(&iter)) {
				void => abort("Invalid format string (unterminated '{')"),
				_: void => abort("Invalid format string (unterminated '{')"),
				r: rune => r,
			};



@@ 222,7 222,7 @@ export fn fprintf(

			let mod = modifiers { base = strconv::base::DEC, ... };
			r = match (strings::next(&iter)) {
				void => abort("Invalid format string (unterminated '{')"),
				_: void => abort("Invalid format string (unterminated '{')"),
				r: rune => r,
			};
			switch (r) {


@@ 234,7 234,7 @@ export fn fprintf(
			n += format(s, arg, &mod)?;
		} else if (r == '}') {
			match (strings::next(&iter)) {
				void => abort("Invalid format string (hanging '}')"),
				_: void => abort("Invalid format string (hanging '}')"),
				r: rune => assert(r == '}', "Invalid format string (hanging '}')"),
			};



@@ 307,7 307,7 @@ fn scan_uint(iter: *strings::iterator) uint = {
	defer free(num);
	for (true) {
		let r = match (strings::next(iter)) {
			void => abort("Invalid format string (unterminated '{')"),
			_: void => abort("Invalid format string (unterminated '{')"),
			r: rune => r,
		};



@@ 316,7 316,7 @@ fn scan_uint(iter: *strings::iterator) uint = {
		} else {
			strings::push(iter, r);
			match (strconv::stou(strings::fromutf8(num))) {
				(strconv::invalid | strconv::overflow) =>
				_: (strconv::invalid | strconv::overflow) =>
					abort("Invalid format string (invalid index)"),
				u: uint => return u,
			};


@@ 330,7 330,7 @@ fn scan_modifier_flags(iter: *strings::iterator, mod: *modifiers) void = {

	for (true) {
		let r = match (strings::next(iter)) {
			void => abort("Invalid format string (unterminated '{')"),
			_: void => abort("Invalid format string (unterminated '{')"),
			r: rune => r,
		};



@@ 363,7 363,7 @@ fn scan_modifier_flags(iter: *strings::iterator, mod: *modifiers) void = {

fn scan_modifier_width(iter: *strings::iterator, mod: *modifiers) void = {
	let r = match (strings::next(iter)) {
		void => abort("Invalid format string (unterminated '{')"),
		_: void => abort("Invalid format string (unterminated '{')"),
		r: rune => r,
	};



@@ 377,7 377,7 @@ fn scan_modifier_width(iter: *strings::iterator, mod: *modifiers) void = {

fn scan_modifier_precision(iter: *strings::iterator, mod: *modifiers) void = {
	let r = match (strings::next(iter)) {
		void => abort("Invalid format string (unterminated '{')"),
		_: void => abort("Invalid format string (unterminated '{')"),
		r: rune => r,
	};



@@ 390,7 390,7 @@ fn scan_modifier_precision(iter: *strings::iterator, mod: *modifiers) void = {

fn scan_modifier_base(iter: *strings::iterator, mod: *modifiers) void = {
	let r = match (strings::next(iter)) {
		void => abort("Invalid format string (unterminated '{')"),
		_: void => abort("Invalid format string (unterminated '{')"),
		r: rune => r,
	};



@@ 411,7 411,7 @@ fn scan_modifiers(iter: *strings::iterator, mod: *modifiers) void = {

	// eat '}'
	let terminated = match (strings::next(iter)) {
		void => false,
		_: void => false,
		r: rune => r == '}',
	};
	assert(terminated, "Invalid format string (unterminated '{')");

M format/html/escape.ha => format/html/escape.ha +1 -1
@@ 9,7 9,7 @@ export fn escape(out: *io::stream, in: str) (size | io::error) = {
	let z = 0z;
	let iter = strings::iter(in);
	for (true) match (strings::next(&iter)) {
		void => break,
		_: void => break,
		rn: rune => z += io::write(out, switch (rn) {
			'&' => strings::toutf8("&amp;"),
			'<' => strings::toutf8("&lt;"),

M format/xml/+test.ha => format/xml/+test.ha +2 -2
@@ 88,8 88,8 @@ fn xmltest(input: str, expected: []token, error: bool) void = {
	for (let i = 0z; i < len(expected); i += 1) {
		let tok = match (scan(parser)) {
			tok: token => tok,
			void => abort("Expected token, got void"),
			syntaxerr => abort("Expected token, got syntax error"),
			_: void => abort("Expected token, got void"),
			_: syntaxerr => abort("Expected token, got syntax error"),
		};
		match (tok) {
			el: elementstart => {

M format/xml/parser.ha => format/xml/parser.ha +4 -4
@@ 301,7 301,7 @@ fn scan_charref(par: *parser) (rune | error) = {
	};
	return match (strconv::stou32b(strio::string(par.entbuf), base)) {
		u: u32 => u: rune,
		(strconv::invalid | strconv::overflow) => syntaxerr,
		_: (strconv::invalid | strconv::overflow) => syntaxerr,
	};
};



@@ 387,7 387,7 @@ fn prolog(par: *parser) (void | error) = {
		};
		// XXX: Deliberate omission: all values other than utf-8
		match (ascii::strcasecmp(attr.1, "utf-8")) {
			void => return utf8::invalid,
			_: void => return utf8::invalid,
			n: int => if (n != 0) return utf8::invalid,
		};
	};


@@ 407,7 407,7 @@ fn prolog(par: *parser) (void | error) = {
		};
		// XXX: Deliberate omission: non-standalone documents
		match (ascii::strcasecmp(attr.1, "yes")) {
			void => return syntaxerr,
			_: void => return syntaxerr,
			n: int => if (n != 0) return syntaxerr,
		};
	};


@@ 448,7 448,7 @@ fn want(par: *parser, tok: (rune | str | whitespace)...) (bool | error) = {
			let iter = strings::iter(x);
			for (true) match (strings::next(&iter)) {
				rn: rune => want(par, rn)?,
				void => break,
				_: void => break,
			};
		},
		ws: whitespace => {

M fs/fs.ha => fs/fs.ha +3 -3
@@ 87,9 87,9 @@ export fn mkdirs(fs: *fs, path: str) (void | error) = {
	let parent = path::dirname(path);
	if (path != parent) {
		match (mkdirs(fs, parent)) {
			errors::exists => void,
			_: errors::exists => void,
			err: error => return err,
			void => void,
			_: void => void,
		};
	};
	return mkdir(fs, path);


@@ 122,7 122,7 @@ export fn rmdirall(fs: *fs, path: str) (void | error) = {
				* => remove(fs, p)?,
			};
		},
		void => break,
		_: void => break,
	};
	if (path != "") {
		return rmdir(fs, path);

M fs/mem/+test.ha => fs/mem/+test.ha +2 -2
@@ 56,7 56,7 @@ use strconv;
	defer free(it);
	let count = 0z;
	for (true) match (it.next(it)) {
		void => break,
		_: void => break,
		d: fs::dirent => count += 1,
	};
	assert(count == 6);


@@ 132,7 132,7 @@ use strconv;
	defer free(it);
	let count = 0z;
	for (true) match (it.next(it)) {
		void => break,
		_: void => break,
		d: fs::dirent => count += 1,
	};
	assert(count == limit);

M fs/mem/util.ha => fs/mem/util.ha +1 -1
@@ 102,7 102,7 @@ fn find_rec(dir: *inode, name: str, it: *path::iterator) (*inode | fs::error) = 
		ino: *inode => {
			if (name == ino.name) {
				return match (path::next(it)) {
					void => ino,
					_: void => ino,
					name: str => find_rec(ino, name, it),
				};
			};

M fs/util.ha => fs/util.ha +1 -1
@@ 87,7 87,7 @@ export fn readdir(fs: *fs, path: str) ([]dirent | error) = {
	for (true) {
		match (next(i)) {
			d: dirent => append(ents, dirent_dup(&d)),
			void => break,
			_: void => break,
		};
	};
	return ents;

M getopt/getopts.ha => getopt/getopts.ha +6 -6
@@ 164,9 164,9 @@ export fn parse(args: []str, help: help...) command = {
			os::exit(1);
		};
		match (next) {
			rune => abort(), // Unreachable
			void => void,
			(utf8::more | utf8::invalid) => {
			_: rune => abort(), // Unreachable
			_: void => void,
			_: (utf8::more | utf8::invalid) => {
				errmsg(args[9], "invalid UTF-8 in arguments",
					void, help);
				os::exit(1);


@@ 233,8 233,8 @@ export fn printhelp(s: *io::stream, name: str, help: []help) void = {
	printusage(s, name, help);

	for (let i = 0z; i < len(help); i += 1) match (help[i]) {
		cmd_help => void,
		(flag_help | parameter_help) => {
		_: cmd_help => void,
		_: (flag_help | parameter_help) => {
			// Only print this if there are flags to show
			fmt::fprint(s, "\n");
			break;


@@ 255,7 255,7 @@ export fn printhelp(s: *io::stream, name: str, help: []help) void = {
fn errmsg(name: str, err: str, opt: (rune | void), help: []help) void = {
	fmt::errorfln("{}: {}{}", name, err, match (opt) {
		r: rune => r,
		void => "",
		_: void => "",
	});
	printusage(os::stderr, name, help);
};

M hare/ast/expr.ha => hare/ast/expr.ha +1 -1
@@ 327,7 327,7 @@ export fn expr_free(e: (expr | nullable *expr)) void = match (e) {
			expr_free(c._type);
		},
		c: constant_expr => match(c) {
			(void | lex::value) => void,
			_: (void | lex::value) => void,
			a: array_constant => {
				for (let i = 0z; i < len(a.values); i += 1) {
					expr_free(a.values[i]);

M hare/lex/+test.ha => hare/lex/+test.ha +1 -1
@@ 38,7 38,7 @@ fn vassert(expected: value, actual: value) void = match (expected) {
	expected: i64 => assert(actual as i64 == expected),
	expected: u64 => assert(actual as u64 == expected),
	expected: f64 => assert(actual as f64 == expected),
	void => assert(actual is void),
	_: void => assert(actual is void),
};

fn lextest(in: str, expected: []token) void = {

M hare/lex/lex.ha => hare/lex/lex.ha +8 -8
@@ 71,7 71,7 @@ export fn lex(lex: *lexer) (token | error) = {
			lex.un = void;
			return tok;
		},
		void => void,
		_: void => void,
	};

	let loc = location { ... };


@@ 122,7 122,7 @@ fn is_name(r: rune, num: bool) bool =
fn ncmp(a: const *void, b: const *void) int = {
	let a = a: const *str, b = b: const *str;
	return match (ascii::strcmp(*a, *b)) {
		void => abort("non-ascii name"), // TODO: Bubble me up
		_: void => abort("non-ascii name"), // TODO: Bubble me up
		i: int => i,
	};
};


@@ 196,7 196,7 @@ fn lex_string(lex: *lexer, loc: location) (token | error) = {
fn lex_rn_str(lex: *lexer, loc: location) (token | error) = {
	let r = match (next(lex)) {
		r: rune => r,
		(io::EOF | io::error) => abort(),
		_: (io::EOF | io::error) => abort(),
	};
	switch (r) {
		'\"' => return lex_string(lex, loc),


@@ 222,7 222,7 @@ fn lex_name(lex: *lexer, loc: location, keyword: bool) (token | error) = {
			assert(is_name(r, false));
			strio::appendrune(buf, r);
		},
		(io::EOF | io::error) => abort(),
		_: (io::EOF | io::error) => abort(),
	};

	for (true) match (next(lex)?) {


@@ 366,10 366,10 @@ fn lex_literal(lex: *lexer, loc: location) (token | error) = {
	if (end == 0) end = len(chars);

	let exp = match (exp) {
		void => "0",
		_: void => "0",
		exp: size => {
			let end = match (suff) {
				void => len(chars),
				_: void => len(chars),
				suff: size => suff,
			};
			strings::fromutf8(chars[exp..end]);


@@ 384,7 384,7 @@ fn lex_literal(lex: *lexer, loc: location) (token | error) = {

	let suff = match (suff) {
		suff: size => strings::fromutf8(chars[suff..]),
		void => "",
		_: void => "",
	};
	let suff = if (suff == "u8") ltok::LIT_U8
		else if (suff == "u16") ltok::LIT_U16


@@ 647,7 647,7 @@ export fn unlex(lex: *lexer, tok: token) void = {

fn next(lex: *lexer) (rune | io::EOF | io::error) = {
	match (lex.rb[0]) {
		void => void,
		_: void => void,
		r: (rune | io::EOF) => {
			lex.rb[0] = lex.rb[1];
			lex.rb[1] = void;

M hare/module/context.ha => hare/module/context.ha +2 -2
@@ 32,7 32,7 @@ export fn context_init(tags: []tag, defs: []str, harepath: str) context = {
		tags = tags,
		defines = defs,
		paths: []str = match (os::getenv("HAREPATH")) {
			void => {
			_: void => {
				let path: []str = alloc([
					strings::dup(harepath),
					dirs::data("hare"),


@@ 51,7 51,7 @@ export fn context_init(tags: []tag, defs: []str, harepath: str) context = {
			},
		},
		cache: str = match (os::getenv("HARECACHE")) {
			void => dirs::cache("hare"),
			_: void => dirs::cache("hare"),
			s: str => strings::dup(s),
		},
		...

M hare/module/manifest.ha => hare/module/manifest.ha +16 -16
@@ 57,7 57,7 @@ export fn manifest_load(ctx: *context, ident: ast::ident) (manifest | error) = {
	defer unlock(ctx.fs, cachedir, l);

	let file = match (fs::open(ctx.fs, mpath, fs::flags::RDONLY)) {
		errors::noentry => return manifest,
		_: errors::noentry => return manifest,
		err: fs::error => return err,
		file: *io::stream => file,
	};


@@ 72,7 72,7 @@ export fn manifest_load(ctx: *context, ident: ast::ident) (manifest | error) = {
			io::EOF => break,
			line: []u8 => match (strings::try_fromutf8(line)) {
				// Treat an invalid manifest as empty
				utf8::invalid => return manifest,
				_: utf8::invalid => return manifest,
				s: str => s,
			},
		};


@@ 84,13 84,13 @@ export fn manifest_load(ctx: *context, ident: ast::ident) (manifest | error) = {

		let tok = strings::tokenize(line, " ");
		let kind = match (strings::next_token(&tok)) {
			void => continue,
			_: void => continue,
			s: str => s,
		};

		if (kind == "version") {
			let ver = match (strings::next_token(&tok)) {
				void => return manifest,
				_: void => return manifest,
				s: str => s,
			};
			match (strconv::stoi(ver)) {


@@ 101,13 101,13 @@ export fn manifest_load(ctx: *context, ident: ast::ident) (manifest | error) = {
			};
		} else if (kind == "input") {
			let hash = match (strings::next_token(&tok)) {
				void => return manifest, s: str => s,
				_: void => return manifest, s: str => s,
			}, path = match (strings::next_token(&tok)) {
				void => return manifest, s: str => s,
				_: void => return manifest, s: str => s,
			}, inode = match (strings::next_token(&tok)) {
				void => return manifest, s: str => s,
				_: void => return manifest, s: str => s,
			}, mtime = match (strings::next_token(&tok)) {
				void => return manifest, s: str => s,
				_: void => return manifest, s: str => s,
			};

			let hash = match (hex::decode(hash)) {


@@ 125,7 125,7 @@ export fn manifest_load(ctx: *context, ident: ast::ident) (manifest | error) = {

			let parsed = parse_name(path);
			let ftype = match (type_for_ext(path)) {
				void => return manifest,
				_: void => return manifest,
				ft: filetype => ft,
			};



@@ 143,7 143,7 @@ export fn manifest_load(ctx: *context, ident: ast::ident) (manifest | error) = {
			});
		} else if (kind == "module") {
			let modhash = match (strings::next_token(&tok)) {
				void => return manifest, s: str => s,
				_: void => return manifest, s: str => s,
			};
			let modhash = match (hex::decode(modhash)) {
				* => return manifest,


@@ 153,7 153,7 @@ export fn manifest_load(ctx: *context, ident: ast::ident) (manifest | error) = {
			let minputs: []input = [];
			for (true) {
				let hash = match (strings::next_token(&tok)) {
					void => break,
					_: void => break,
					s: str => s,
				};
				let hash = match (hex::decode(hash)) {


@@ 179,7 179,7 @@ export fn manifest_load(ctx: *context, ident: ast::ident) (manifest | error) = {

		// Check for extra tokens
		match (strings::next_token(&tok)) {
			void => void,
			_: void => void,
			s: str => return manifest,
		};
	};


@@ 281,8 281,8 @@ fn lock(fs: *fs::fs, cachedir: str) (*io::stream | error) = {
	// XXX: I wonder if this should be some generic function in fs or
	// something
	match (os::mkdirs(cachedir)) {
		errors::exists => void,
		void => void,
		_: errors::exists => void,
		_: void => void,
		e: fs::error => return e,
	};
	let lockpath = path::join(cachedir, "manifest.lock");


@@ 292,7 292,7 @@ fn lock(fs: *fs::fs, cachedir: str) (*io::stream | error) = {
	for (true) {
		match (fs::create(fs, lockpath, 0o644, fs::flags::EXCL)) {
			fd: *io::stream => return fd,
			(errors::busy | errors::exists) => void,
			_: (errors::busy | errors::exists) => void,
			err: fs::error => return err,
		};
		if (!logged) {


@@ 309,7 309,7 @@ fn unlock(fs: *fs::fs, cachedir: str, s: *io::stream) void = {
	let lockpath = path::join(cachedir, "manifest.lock");
	defer free(lockpath);
	match (fs::remove(fs, lockpath)) {
		void => void,
		_: void => void,
		err: fs::error => abort("Error removing module lock"),
	};
};

M hare/module/scan.ha => hare/module/scan.ha +6 -6
@@ 22,12 22,12 @@ export fn scan(ctx: *context, path: str) (version | error) = {
	let sha = sha256::sha256();
	//defer! hash::close(sha);
	let iter = match (fs::iter(ctx.fs, path)) {
		fs::wrongtype => {
		_: fs::wrongtype => {
			// Single file case
			let inputs: []input = [];
			let deps: []ast::ident = [];
			let ft = match (type_for_ext(path)) {
				void => return module_not_found,
				_: void => return module_not_found,
				ft: filetype => ft,
			};
			let st = fs::stat(ctx.fs, path)?;


@@ 77,7 77,7 @@ fn parse_name(name: str) (str, str, []tag) = {
		else p: size;
	let tags = strings::sub(base, i, strings::end);
	let tags = match (parsetags(tags)) {
		void => return (base, ext, []),
		_: void => return (base, ext, []),
		t: []tag => t,
	};
	let base = strings::sub(base, 0, i);


@@ 112,7 112,7 @@ fn scan_directory(

	for (true) {
		let ent = match (fs::next(iter)) {
			void => break,
			_: void => break,
			ent: fs::dirent => ent,
		};



@@ 337,7 337,7 @@ export fn parsetags(in: str) ([]tag | void) = {
	for (true) {
		let t = tag { ... };
		let m = match (strings::next(&iter)) {
			void => break,
			_: void => break,
			r: rune => r,
		};
		t.mode = switch (m) {


@@ 347,7 347,7 @@ export fn parsetags(in: str) ([]tag | void) = {
		};
		let buf = strio::dynamic();
		for (true) match (strings::next(&iter)) {
			void => break,
			_: void => break,
			r: rune => {
				if (ascii::isalnum(r) || r == '_') {
					strio::appendrune(buf, r);

M hare/parse/decl.ha => hare/parse/decl.ha +10 -10
@@ 11,13 11,13 @@ fn attr_symbol(lexer: *lex::lexer) (str | error) = {
	let s = t.1 as str;
	let d = strings::iter(s);
	match (strings::next(&d)) {
		void => void,
		_: void => void,
		r: rune => synassert(t.2,
			ascii::isalpha(r) || r == '.' || r == '_',
			"Invalid symbol")?,
	};
	for (true) match (strings::next(&d)) {
		void => break,
		_: void => break,
		r: rune => synassert(t.2,
			ascii::isalnum(r) || r == '$' || r == '.' || r == '_',
			"Invalid symbol")?,


@@ 34,8 34,8 @@ fn decl_global(
	for (true) {
		let symbol = if (tok == ltok::CONST || tok == ltok::LET) {
			match (try(lexer, ltok::ATTR_SYMBOL)?) {
				void => "",
				lex::token => attr_symbol(lexer)?,
				_: void => "",
				_: lex::token => attr_symbol(lexer)?,
			};
		} else "";
		let ident = ident(lexer)?;


@@ 55,7 55,7 @@ fn decl_global(
			init = init,
		});
		match (btok) {
			void => break,
			_: void => break,
			* => void,
		};
	};


@@ 74,7 74,7 @@ fn decl_type(lexer: *lex::lexer) ([]ast::decl_type | error) = {
			_type = _type,
		});
		match (btok) {
			void => break,
			_: void => break,
			* => void,
		};
	};


@@ 88,7 88,7 @@ fn decl_func(lexer: *lex::lexer) (ast::decl_func | error) = {
		ltok::ATTR_NORETURN, ltok::ATTR_SYMBOL
	];
	for (true) match (try(lexer, attrs...)?) {
		void => break,
		_: void => break,
		t: lex::token => switch (t.0) {
			ltok::ATTR_FINI => attr = ast::fndecl_attrs::FINI,
			ltok::ATTR_INIT => attr = ast::fndecl_attrs::INIT,


@@ 144,8 144,8 @@ export fn decls(lexer: *lex::lexer) ([]ast::decl | error) = {
		if (peek(lexer, ltok::EOF)? is lex::token) break;
		let comment = "";
		let exported = match (try(lexer, ltok::EXPORT)?) {
			void => false,
			lex::token => {
			_: void => false,
			_: lex::token => {
				comment = strings::dup(lex::comment(lexer));
				true;
			},


@@ 156,7 156,7 @@ export fn decls(lexer: *lex::lexer) ([]ast::decl | error) = {
			comment = strings::dup(lex::comment(lexer));
		};
		let decl = match (next) {
			void => decl_func(lexer)?,
			_: void => decl_func(lexer)?,
			t: lex::token =>
				if (t.0 == ltok::TYPE) decl_type(lexer)?
				else decl_global(lexer, t.0)?,

M hare/parse/expr.ha => hare/parse/expr.ha +44 -44
@@ 6,8 6,8 @@ use strings;
// Parses an expression.
export fn expression(lexer: *lex::lexer) (ast::expr | error) = {
	const indirect = match (try(lexer, ltok::TIMES)?) {
		void => false,
		lex::token => true,
		_: void => false,
		_: lex::token => true,
	};

	// All assignment-op tokens


@@ 33,7 33,7 @@ export fn expression(lexer: *lex::lexer) (ast::expr | error) = {
			ltok::SWITCH, ltok::IF, ltok::LABEL, ltok::FOR,
			ltok::BREAK, ltok::CONTINUE, ltok::RETURN, ltok::LET,
			ltok::CONST)?) {
		void => binarithm(lexer, void, 0)?,
		_: void => binarithm(lexer, void, 0)?,
		tok: lex::token => switch (tok.0) {
			ltok::LBRACE => expression_list(lexer)?,
			ltok::MATCH => abort(), // TODO


@@ 204,7 204,7 @@ fn binarithm(
	// Precedence climbing parser
	// https://en.wikipedia.org/wiki/Operator-precedence_parser
	let lvalue = match (lvalue) {
		void => cast(lexer, void)?,
		_: void => cast(lexer, void)?,
		expr: ast::expr => expr,
	};



@@ 254,8 254,8 @@ fn binding(lexer: *lex::lexer, is_static: bool) (ast::expr | error) = {
			init = init,
		});
		match (try(lexer, ltok::COMMA)?) {
			void => break,
			lex::token => void,
			_: void => break,
			_: lex::token => void,
		};
	};



@@ 271,7 271,7 @@ fn builtin(lexer: *lex::lexer) (ast::expr | error) = {
			ltok::DELETE, ltok::ABORT, ltok::ASSERT, ltok::STATIC,
			ltok::SIZE, ltok::LEN, ltok::OFFSET, ltok::DEFER)?) {
		tok: lex::token => tok,
		void => return postfix(lexer, void),
		_: void => return postfix(lexer, void),
	};
	return switch (tok.0) {
		ltok::ALLOC => alloc_expr(lexer),


@@ 285,7 285,7 @@ fn builtin(lexer: *lex::lexer) (ast::expr | error) = {
					ltok::ABORT, ltok::ASSERT)?) {
				tok: lex::token => tok,
				// TODO: The following is lame
				void => return syntaxerr(tok.2,
				_: void => return syntaxerr(tok.2,
					"Expected let, const, or assert"),
			};
			switch (tok.0) {


@@ 310,20 310,20 @@ fn call(lexer: *lex::lexer, lvalue: ast::expr) (ast::expr | error) = {

	for (true) {
		match (try(lexer, ltok::RPAREN)?) {
			lex::token => break,
			void => void,
			_: lex::token => break,
			_: void => void,
		};

		append(args, alloc(expression(lexer)?));

		match (try(lexer, ltok::ELLIPSIS)?) {
			lex::token => {
			_: lex::token => {
				variadic = true;
				try(lexer, ltok::COMMA)?;
				want(lexer, ltok::RPAREN)?;
				break;
			},
			void => void,
			_: void => void,
		};

		switch (want(lexer, ltok::COMMA, ltok::RPAREN)?.0) {


@@ 341,12 341,12 @@ fn call(lexer: *lex::lexer, lvalue: ast::expr) (ast::expr | error) = {

fn cast(lexer: *lex::lexer, lvalue: (ast::expr | void)) (ast::expr | error) = {
	const lvalue = match (lvalue) {
		void => unarithm(lexer)?,
		_: void => unarithm(lexer)?,
		e: ast::expr => e,
	};
	const tok = match (try(lexer, ltok::COLON,
			ltok::AS, ltok::IS)?) {
		void => return lvalue,
		_: void => return lvalue,
		tok: lex::token => tok.0,
	};
	const kind = switch (tok) {


@@ 383,14 383,14 @@ fn control(lexer: *lex::lexer) (ast::expr | error) = {
	let label = if (tok.0 == ltok::BREAK || tok.0 == ltok::CONTINUE) {
		match (try(lexer, ltok::LABEL)?) {
			tok: lex::token => tok.1 as str,
			void => "",
			_: void => "",
		};
	} else "";
	return switch (tok.0) {
		ltok::BREAK => label: ast::break_expr,
		ltok::CONTINUE => label: ast::continue_expr,
		ltok::RETURN => match (peek(lexer, ltok::COMMA, ltok::SEMICOLON)?) {
			void => alloc(expression(lexer)?): ast::return_expr,
			_: void => alloc(expression(lexer)?): ast::return_expr,
			lex::token => null: ast::return_expr,
		},
	};


@@ 412,8 412,8 @@ fn expression_list(lexer: *lex::lexer) (ast::expr | error) = {
	want(lexer, ltok::LBRACE)?;
	for (let more = true; more) {
		const item = match (peek(lexer, ltok::RBRACE)?) {
			lex::token => break,
			void => expression(lexer)?,
			_: lex::token => break,
			_: void => expression(lexer)?,
		};
		append(items, alloc(item));
		want(lexer, ltok::SEMICOLON)?;


@@ 437,8 437,8 @@ fn for_expr(lexer: *lex::lexer) (ast::expr | error) = {

	const bindings: nullable *ast::expr = match (peek(
			lexer, ltok::LET, ltok::CONST)?) {
		void => null,
		lex::token => {
		_: void => null,
		_: lex::token => {
			const bindings = alloc(binding(lexer, false)?);
			want(lexer, ltok::SEMICOLON)?;
			bindings;


@@ 449,8 449,8 @@ fn for_expr(lexer: *lex::lexer) (ast::expr | error) = {

	const afterthought: nullable *ast::expr = match (peek(
			lexer, ltok::SEMICOLON)) {
		void => null,
		lex::token => {
		_: void => null,
		_: lex::token => {
			want(lexer, ltok::SEMICOLON)?;
			alloc(expression(lexer)?);
		},


@@ 482,8 482,8 @@ fn if_expr(lexer: *lex::lexer) (ast::expr | error) = {
	want(lexer, ltok::RPAREN)?;
	const tbranch = alloc(expression(lexer)?);
	const fbranch: nullable *ast::expr = match (try(lexer, ltok::ELSE)?) {
		void => null,
		lex::token => alloc(expression(lexer)?),
		_: void => null,
		_: lex::token => alloc(expression(lexer)?),
	};
	return ast::if_expr {
		cond = cond,


@@ 497,16 497,16 @@ fn indexing(lexer: *lex::lexer, lvalue: ast::expr) (ast::expr | error) = {
	let start: nullable *ast::expr = null, end: nullable *ast::expr = null;

	match (try(lexer, ltok::SLICE)?) {
		void => start = alloc(expression(lexer)?),
		lex::token => is_slice = true,
		_: void => start = alloc(expression(lexer)?),
		_: lex::token => is_slice = true,
	};
	if (!is_slice) match (try(lexer, ltok::SLICE)?) {
		void => void,
		lex::token => is_slice = true,
		_: void => void,
		_: lex::token => is_slice = true,
	};
	if (is_slice) match (peek(lexer, ltok::RBRACKET)?) {
		lex::token => void,
		void => end = alloc(expression(lexer)?),
		_: lex::token => void,
		_: void => end = alloc(expression(lexer)?),
	};

	want(lexer, ltok::RBRACKET)?;


@@ 552,8 552,8 @@ fn plain_expression(lexer: *lex::lexer) (ast::expr | error) = {
		ltok::NAME => {
			let id = ident(lexer)?;
			return match (peek(lexer, ltok::LBRACE)?) {
				void => id: ast::access_identifier,
				lex::token => plain_struct(lexer, id)?,
				_: void => id: ast::access_identifier,
				_: lex::token => plain_struct(lexer, id)?,
			};
		},
		* => syntaxerr(mkloc(lexer),


@@ 569,14 569,14 @@ fn plain_array(lexer: *lex::lexer) (ast::expr | error) = {
	let expand = false;
	for (true) {
		match (try(lexer, ltok::RBRACKET)?) {
			lex::token => break,
			void => void,
			_: lex::token => break,
			_: void => void,
		};

		append(values, alloc(expression(lexer)?));

		match (try(lexer, ltok::COMMA, ltok::ELLIPSIS)?) {
			void => {
			_: void => {
				want(lexer, ltok::RBRACKET)?;
				break;
			},


@@ 651,7 651,7 @@ fn struct_field(
			const tok = match (try(lexer, ltok::COLON,
					ltok::DOUBLE_COLON, ltok::EQUAL)?) {
				tok: lex::token => tok,
				void => {
				_: void => {
					let id: ast::ident = alloc([name]);
					const expr = plain_struct(lexer, id)?;
					const expr = expr as ast::constant_expr;


@@ 704,18 704,18 @@ fn plain_tuple(lexer: *lex::lexer, ex: ast::expr) (ast::expr | error) = {

	for (true) {
		match (try(lexer, ltok::RPAREN)?) {
			lex::token => break,
			void => void,
			_: lex::token => break,
			_: void => void,
		};

		append(values, alloc(expression(lexer)?));

		match (try(lexer, ltok::COMMA)?) {
			void => {
			_: void => {
				want(lexer, ltok::RPAREN)?;
				break;
			},
			lex::token => void,
			_: lex::token => void,
		};
	};



@@ 725,7 725,7 @@ fn plain_tuple(lexer: *lex::lexer, ex: ast::expr) (ast::expr | error) = {

fn postfix(lexer: *lex::lexer, lvalue: (ast::expr | void)) (ast::expr | error) = {
	let lvalue = match (lvalue) {
		void => plain_expression(lexer)?,
		_: void => plain_expression(lexer)?,
		ex: ast::expr => ex,
	};



@@ 738,7 738,7 @@ fn postfix(lexer: *lex::lexer, lvalue: (ast::expr | void)) (ast::expr | error) =
			ltok::QUESTION => alloc(lvalue): ast::propagate_expr,
			* => abort(),
		},
		void => return lvalue,
		_: void => return lvalue,
	};

	return postfix(lexer, lvalue);


@@ 750,7 750,7 @@ fn postfix_dot(lexer: *lex::lexer, lvalue: ast::expr) (ast::expr | error) = {
			object = alloc(lvalue),
			field = tok.1 as str,
		},
		void => {
		_: void => {
			let con = constant(lexer)?;
			let val = con as ast::constant_expr;
			synassert(mkloc(lexer), val is lex::value,


@@ 820,7 820,7 @@ fn unarithm(lexer: *lex::lexer) (ast::expr | error) = {
	const tok = match (try(lexer,
			ltok::PLUS, ltok::MINUS, ltok::BNOT,
			ltok::LNOT, ltok::TIMES, ltok::BAND)) {
		void => return builtin(lexer),
		_: void => return builtin(lexer),
		tok: lex::token => tok.0,
	};
	const op = switch (tok) {

M hare/parse/ident.ha => hare/parse/ident.ha +2 -2
@@ 8,12 8,12 @@ fn ident_trailing(lexer: *lex::lexer) ((ast::ident, bool) | error) = {
	for (true) {
		let name = match (try(lexer, ltok::NAME)?) {
			t: lex::token => t.1 as str,
			void => return (ident: ast::ident, true),
			_: void => return (ident: ast::ident, true),
		};
		append(ident, name);
		z += len(name);
		match (try(lexer, ltok::DOUBLE_COLON)?) {
			void => break,
			_: void => break,
			* => void, // Grab the next ident
		};
		z += 1;

M hare/parse/import.ha => hare/parse/import.ha +2 -2
@@ 8,7 8,7 @@ fn name_list(lexer: *lex::lexer) ([]str | error) = {
		append(names, want(lexer, ltok::NAME)?.1 as str);
		switch (want(lexer, ltok::COMMA, ltok::RBRACE)?.0) {
			ltok::COMMA => match (try(lexer, ltok::RBRACE)?) {
				void => void,
				_: void => void,
				* => return names,
			},
			ltok::RBRACE => return names,


@@ 23,7 23,7 @@ export fn imports(lexer: *lex::lexer) ([]ast::import | error) = {
	let imports: []ast::import = [];
	for (true) {
		match (try(lexer, ltok::USE)?) {
			void => break,
			_: void => break,
			* => void,
		};


M hare/parse/type.ha => hare/parse/type.ha +22 -22
@@ 10,8 10,8 @@ fn prototype(lexer: *lex::lexer) (ast::func_type | error) = {
	for (try(lexer, ltok::RPAREN)? is void) {
		let loc = mkloc(lexer);
		match (try(lexer, ltok::ELLIPSIS)?) {
			void => void,
			lex::token => {
			_: void => void,
			_: lex::token => {
				synassert(loc, len(params) > 0,
					"Expected at least one non-variadic parameter for C-style variadism")?;
				variadism = ast::variadism::C;


@@ 21,8 21,8 @@ fn prototype(lexer: *lex::lexer) (ast::func_type | error) = {
			},
		};
		let name = match (try(lexer, ltok::UNDERSCORE)?) {
			void => want(lexer, ltok::NAME)?.1 as str,
			lex::token => "",
			_: void => want(lexer, ltok::NAME)?.1 as str,
			_: lex::token => "",
		};
		want(lexer, ltok::COLON);
		append(params, ast::func_param {


@@ 31,8 31,8 @@ fn prototype(lexer: *lex::lexer) (ast::func_type | error) = {
			_type = alloc(_type(lexer)?),
		});
		match (try(lexer, ltok::ELLIPSIS)?) {
			void => void,
			lex::token => {
			_: void => void,
			_: lex::token => {
				variadism = ast::variadism::HARE;
				try(lexer, ltok::COMMA)?;
				want(lexer, ltok::RPAREN)?;


@@ 40,11 40,11 @@ fn prototype(lexer: *lex::lexer) (ast::func_type | error) = {
			},
		};
		match (try(lexer, ltok::COMMA)?) {
			void => {
			_: void => {
				want(lexer, ltok::RPAREN)?;
				break;
			},
			lex::token => void,
			_: lex::token => void,
		};
	};
	let t = _type(lexer)?;


@@ 108,7 108,7 @@ fn primitive_type(lexer: *lex::lexer) (ast::_type | error) = {
fn alias_type(lexer: *lex::lexer) (ast::_type | error) = {
	let loc = mkloc(lexer);
	let unwrap = match (try(lexer, ltok::ELLIPSIS)?) {
		void => false,
		_: void => false,
		* => true,
	};
	return ast::_type {


@@ 124,7 124,7 @@ fn alias_type(lexer: *lex::lexer) (ast::_type | error) = {
fn pointer_type(lexer: *lex::lexer) (ast::_type | error) = {
	let loc = mkloc(lexer);
	let flags = match (try(lexer, ltok::NULLABLE)?) {
		void => 0: ast::pointer_flags,
		_: void => 0: ast::pointer_flags,
		* => ast::pointer_flags::NULLABLE,
	};
	want(lexer, ltok::TIMES)?;


@@ 145,11 145,11 @@ fn tagged_type(lexer: *lex::lexer, first: ast::_type) (ast::_type | error) = {
	for (try(lexer, ltok::RPAREN)? is void) {
		append(tagged, alloc(_type(lexer)?));
		match (try(lexer, ltok::BOR)?) {
			void => {
			_: void => {
				want(lexer, ltok::RPAREN)?;
				break;
			},
			lex::token => void,
			_: lex::token => void,
		};
	};
	return ast::_type {


@@ 166,11 166,11 @@ fn tuple_type(lexer: *lex::lexer, first: ast::_type) (ast::_type | error) = {
	for (try(lexer, ltok::RPAREN)? is void) {
		append(tuple, alloc(_type(lexer)?));
		match (try(lexer, ltok::COMMA)?) {
			void => {
			_: void => {
				want(lexer, ltok::RPAREN)?;
				break;
			},
			lex::token => void,
			_: lex::token => void,
		};
	};
	return ast::_type {


@@ 183,7 183,7 @@ fn tuple_type(lexer: *lex::lexer, first: ast::_type) (ast::_type | error) = {
fn fn_type(lexer: *lex::lexer) (ast::_type | error) = {
	let loc = mkloc(lexer);
	let attrs = match (try(lexer, ltok::ATTR_NORETURN)?) {
		void => 0: ast::func_attrs,
		_: void => 0: ast::func_attrs,
		* => ast::func_attrs::NORETURN,
	};
	want(lexer, ltok::FN)?;


@@ 209,8 209,8 @@ fn struct_union_type(lexer: *lex::lexer) (ast::_type | error) = {
		};

		let offs: nullable *ast::expr = match (try(lexer, ltok::ATTR_OFFSET)?) {
			void => null,
			lex::token => {
			_: void => null,
			_: lex::token => {
				want(lexer, ltok::LPAREN)?;
				let ex = expression(lexer)?;
				want(lexer, ltok::RPAREN)?;


@@ 270,7 270,7 @@ fn struct_embed_or_field(
	let name = want(lexer, ltok::NAME)?;

	let id: ast::ident = match (try(lexer, ltok::COLON, ltok::DOUBLE_COLON)?) {
		void => alloc([name.1 as str]),
		_: void => alloc([name.1 as str]),
		tok: lex::token => switch (tok.0) {
			ltok::COLON => {
				let field = ast::struct_field {


@@ 307,7 307,7 @@ fn array_slice_type(lexer: *lex::lexer) (ast::_type | error) = {

	let length = match (try(lexer,
			ltok::UNDERSCORE, ltok::TIMES, ltok::RBRACKET)?) {
		void => alloc(expression(lexer)?),
		_: void => alloc(expression(lexer)?),
		tok: lex::token => switch (tok.0) {
			ltok::UNDERSCORE => ast::len_contextual,
			ltok::TIMES => ast::len_unbounded,


@@ 335,7 335,7 @@ fn enum_type(lexer: *lex::lexer) (ast::_type | error) = {
	let start = want(lexer, ltok::ENUM)?;

	const storage = match (try(lexer, ltok::LBRACE)?) {
		void => {
		_: void => {
			let storage = integer_type(lexer)?;
			want(lexer, ltok::LBRACE)?;
			storage;


@@ 382,7 382,7 @@ fn enum_type(lexer: *lex::lexer) (ast::_type | error) = {
// Parses a type
export fn _type(lexer: *lex::lexer) (ast::_type | error) = {
	let flags: ast::type_flags = match (try(lexer, ltok::CONST)?) {
		void => 0,
		_: void => 0,
		* => ast::type_flags::CONST,
	};
	let tok = peek(lexer)? as lex::token;


@@ 415,7 415,7 @@ export fn _type(lexer: *lex::lexer) (ast::_type | error) = {
	};

	match (try(lexer, ltok::LNOT)?) {
		void => void,
		_: void => void,
		* => flags |= ast::type_flags::ERROR,
	};


M hare/unparse/decl.ha => hare/unparse/decl.ha +1 -1
@@ 59,7 59,7 @@ export fn decl(out: *io::stream, d: ast::decl) (size | io::error) = {
			n += prototype(out, 0,
				f.prototype._type as ast::func_type)?;
			match (f.body) {
				void => void,
				_: void => void,
				e: ast::expr => {
					n += fmt::fprint(out, " = ")?;
					n += expr(out, 0, e)?;

M hare/unparse/expr.ha => hare/unparse/expr.ha +4 -4
@@ 100,7 100,7 @@ export fn expr(
			};
			z += expr(out, indent, *e.object)?;
			const op = match (e.op) {
				void => "=",
				_: void => "=",
				op: ast::binarithm_op => switch (op) {
					ast::binarithm_op::BAND => "&=",
					ast::binarithm_op::BOR => "|=",


@@ 308,11 308,11 @@ fn constant(
	e: ast::constant_expr,
) (size | io::error) = {
	return match (e) {
		void => fmt::fprint(out, "void"),
		ast::_null => fmt::fprint(out, "null"),
		_: void => fmt::fprint(out, "void"),
		_: ast::_null => fmt::fprint(out, "null"),
		b: bool => fmt::fprint(out, b),
		v: lex::value => fmt::fprint(out, match (v) {
			void => abort(),
			_: void => abort(),
			v: (i64 | u64 | f64) => v,
			// TODO: Escape these:
			s: str => return fmt::fprintf(out, "\"{}\"", s),

M io/copy.ha => io/copy.ha +1 -1
@@ 23,7 23,7 @@ export fn copy(dest: *stream, src: *stream) (error | size) = {
				w += r;
				i += r;
			},
			EOF => break,
			_: EOF => break,
		};
	};
	return w;

M net/+linux/util.ha => net/+linux/util.ha +1 -1
@@ 60,7 60,7 @@ fn from_native(addr: rt::sockaddr) (ip::addr | unix::addr) = {
export fn peeraddr(stream: *io::stream) ((ip::addr, u16) | void) = {
	let fd = match (os::streamfd(stream, true)) {
		fd: int => fd,
		void => return,
		_: void => return,
	};
	let sn = rt::sockaddr {...};
	let sz = size(rt::sockaddr): u32;

M net/ip/ip.ha => net/ip/ip.ha +3 -3
@@ 87,7 87,7 @@ fn parsev6(st: str) (addr6 | invalid) = {
	for (i < 16) {
		let s = match (strings::next_token(&tok)) {
			s: str => s,
			void => break,
			_: void => break,
		};
		if (s == "") {
			if (ells != -1) {


@@ 280,7 280,7 @@ fn fmtmask(s: *io::stream, mask: addr) (io::error | size) = {
	match (masklen(slice)) {
		// format as hex, if zero runs are not contiguous
		// (like golang does)
		void => {
		_: void => {
			for (let i = 0z; i < len(slice); i += 1) {
				ret += fmt::fprintf(s, "{:x}", slice[i])?;
			};


@@ 322,6 322,6 @@ export fn string(item: (...addr | subnet)) str = {
fn wanttoken(tok: *strings::tokenizer) (str | invalid) = {
	return match (strings::next_token(tok)) {
		s: str => s,
		void => invalid
		_: void => invalid
	};
};

M os/+linux/dirfdfs.ha => os/+linux/dirfdfs.ha +8 -8
@@ 209,7 209,7 @@ fn fs_remove(fs: *fs::fs, path: str) (void | fs::error) = {
	let fs = fs: *os_filesystem;
	match (rt::unlinkat(fs.dirfd, path, 0)) {
		err: rt::errno => return errno_to_fs(err),
		void => void,
		_: void => void,
	};
};



@@ 218,7 218,7 @@ fn fs_stat(fs: *fs::fs, path: str) (fs::filestat | fs::error) = {
	let st = rt::st { ... };
	match (rt::fstatat(fs.dirfd, path, &st, rt::AT_SYMLINK_NOFOLLOW)) {
		err: rt::errno => return errno_to_fs(err),
		void => void,
		_: void => void,
	};
	return fs::filestat {
		mask = fs::stat_mask::UID


@@ 268,7 268,7 @@ fn fs_rmdir(fs: *fs::fs, path: str) (void | fs::error) = {
	let fs = fs: *os_filesystem;
	match (rt::unlinkat(fs.dirfd, path, rt::AT_REMOVEDIR)) {
		err: rt::errno => return errno_to_fs(err),
		void => void,
		_: void => void,
	};
};



@@ 276,7 276,7 @@ fn fs_mkdir(fs: *fs::fs, path: str) (void | fs::error) = {
	let fs = fs: *os_filesystem;
	return match (rt::mkdirat(fs.dirfd, path, 0o755)) {
		err: rt::errno => errno_to_fs(err),
		void => void,
		_: void => void,
	};
};



@@ 284,7 284,7 @@ fn fs_chmod(fs: *fs::fs, path: str, mode: fs::mode) (void | fs::error) = {
	let fs = fs: *os_filesystem;
	return match (rt::fchmodat(fs.dirfd, path, mode: uint)) {
		err: rt::errno => return errno_to_fs(err),
		void => void,
		_: void => void,
	};
};



@@ 292,7 292,7 @@ fn fs_chown(fs: *fs::fs, path: str, uid: uint, gid: uint) (void | fs::error) = {
	let fs = fs: *os_filesystem;
	return match (rt::fchownat(fs.dirfd, path, uid, gid)) {
		err: rt::errno => return errno_to_fs(err),
		void => void,
		_: void => void,
	};
};



@@ 315,13 315,13 @@ fn fs_resolve(fs: *fs::fs, path: str) str = {
	if (!path::abs(path)) {
		let iter = path::iter(getcwd());
		for (true) match (path::next(&iter)) {
			void => break,
			_: void => break,
			p: str => resolve_part(&parts, p),
		};
	};
	let iter = path::iter(path);
	for (true) match (path::next(&iter)) {
		void => break,
		_: void => break,
		p: str => resolve_part(&parts, p),
	};
	return path::join(parts...);

M os/+linux/environ.ha => os/+linux/environ.ha +2 -2
@@ 38,7 38,7 @@ export fn getenv(name: const str) (str | void) = {
	for (let i = 0z; rt::envp[i] != null; i += 1) {
		const item = rt::envp[i]: *[*]u8;
		const eq: size = match (bytes::index(item[..], '=': u32: u8)) {
			void => abort("Environment violates System-V invariants"),
			_: void => abort("Environment violates System-V invariants"),
			i: size => i,
		};
		if (bytes::equal(name_b, item[..eq])) {


@@ 52,7 52,7 @@ export fn getenv(name: const str) (str | void) = {
// unset.
export fn tryenv(name: const str, default: str) str = match (getenv(name)) {
	s: str => s,
	void => default,
	_: void => default,
};

let envp: []str = [];

M os/+linux/fs.ha => os/+linux/fs.ha +3 -3
@@ 28,14 28,14 @@ export fn chdir(target: (*fs::fs | str)) (void | fs::error) = {
			let fs = fs: *os_filesystem;
			return match (rt::fchdir(fs.dirfd)) {
				err: rt::errno => errors::errno(err),
				void => void,
				_: void => void,
			};
		},
		s: str  => s,
	};
	return match (rt::chdir(path)) {
		err: rt::errno => errors::errno(err),
		void => void,
		_: void => void,
	};
};



@@ 46,6 46,6 @@ export fn chdir(target: (*fs::fs | str)) (void | fs::error) = {
export fn chroot(target: str) (void | fs::error) = {
	return match (rt::chroot(target)) {
		err: rt::errno => errors::errno(err),
		void => void,
		_: void => void,
	};
};

M os/exec/cmd.ha => os/exec/cmd.ha +4 -4
@@ 24,7 24,7 @@ export fn cmd(name: str, args: str...) (command | error) = {
				err: errors::opaque => return nocmd,
				p: platform_cmd => p,
			} else match (lookup(name)) {
				void => return nocmd,
				_: void => return nocmd,
				p: platform_cmd => p,
			},
		argv = alloc([], len(args) + 1z),


@@ 80,7 80,7 @@ export fn clearenv(cmd: *command) void = {
export fn setenv(cmd: *command, key: str, value: str) void = {
	let iter = strings::iter(key);
	for (let i = 0z; true; i += 1) match (strings::next(&iter)) {
		void => break,
		_: void => break,
		r: rune => if (i == 0) assert(r == '_' || ascii::isalpha(r),
			"Invalid environment variable")
		else assert(r == '_' || ascii::isalnum(r),


@@ 101,13 101,13 @@ export fn setenv(cmd: *command, key: str, value: str) void = {

fn lookup(name: str) (platform_cmd | void) = {
	const path = match (os::getenv("PATH")) {
		void => return,
		_: void => return,
		s: str => s,
	};
	let tok = strings::tokenize(path, ":");
	for (true) {
		const item = match (strings::next_token(&tok)) {
			void => break,
			_: void => break,
			s: str => s,
		};
		let path = strings::concat(item, "/", name);

M os/exec/exec+linux.ha => os/exec/exec+linux.ha +2 -2
@@ 55,7 55,7 @@ fn platform_start(cmd: *command) (errors::opaque | process) = {
	let pipe: [2]int = [0...];
	match (rt::pipe2(&pipe, rt::O_CLOEXEC)) {
		err: rt::errno => return errors::errno(err),
		void => void,
		_: void => void,
	};

	match (rt::clone(null, 0, null, null, 0)) {


@@ 72,7 72,7 @@ fn platform_start(cmd: *command) (errors::opaque | process) = {
				},
			};
		},
		void => {
		_: void => {
			rt::close(pipe[0]);
			let err = platform_exec(cmd);
			let err = &err.data: *rt::errno;

M path/iter.ha => path/iter.ha +1 -1
@@ 42,7 42,7 @@ export fn next(iter: *iterator) (str | void) = {
	};
	return match (bytes::next_token(&iter.tok)) {
		b: []u8 => strings::fromutf8_unsafe(b),
		void => void,
		_: void => void,
	};
};


M path/names.ha => path/names.ha +3 -3
@@ 9,7 9,7 @@ use strings;
export fn dirname(path: str) str = {
	let b = strings::toutf8(path);
	let i = match (bytes::rindex(b, PATHSEP)) {
		void => return path,
		_: void => return path,
		z: size => z,
	};
	if (i == 0) {


@@ 33,7 33,7 @@ export fn dirname(path: str) str = {
export fn basename(path: str) str = {
	let b = strings::toutf8(path);
	let i = match (bytes::rindex(b, PATHSEP)) {
		void => return path,
		_: void => return path,
		z: size => if (z + 1 < len(b)) z + 1z else 0z,
	};
	return strings::fromutf8_unsafe(b[i..]);


@@ 63,7 63,7 @@ export fn extension(p: str) (str, str) = {
	};
	let b = strings::toutf8(p);
	let i = match (bytes::index(b, '.': u32: u8)) {
		void => return (p, ""),
		_: void => return (p, ""),
		z: size => z,
	};
	let e = b[i..];

M rt/+linux/segmalloc.ha => rt/+linux/segmalloc.ha +1 -1
@@ 15,7 15,7 @@ fn segmalloc(n: size) nullable *void = {
fn segfree(p: *void, s: size) void = {
	match (munmap(p, s)) {
		err: errno => abort("munmap failed"),
		void => void,
		_: void => void,
	};
};


M rt/+linux/syscalls.ha => rt/+linux/syscalls.ha +2 -2
@@ 156,7 156,7 @@ export fn execveat(dirfd: int, path: path, argv: *[*]nullable *const char,
			path: uintptr: u64, argv: uintptr: u64,
			envp: uintptr: u64, flags: u64))) {
		err: errno => err,
		u64 => abort("unreachable"),
		_: u64 => abort("unreachable"),
	};
};



@@ 297,7 297,7 @@ export type fcntl_arg = (void | int | *st_flock | *f_owner_ex | *u64);
export fn fcntl(fd: int, cmd: int, arg: fcntl_arg) (int | errno) = {
	let _fd = fd: u64, _cmd = cmd: u64;
	return wrap_return(match (arg) {
		void => syscall2(SYS_fcntl, _fd, _cmd),
		_: void => syscall2(SYS_fcntl, _fd, _cmd),
		i: int => syscall3(SYS_fcntl, _fd, _cmd, i: u64),
		l: *st_flock => syscall3(SYS_fcntl, _fd, _cmd, l: uintptr: u64),
		o: *f_owner_ex => syscall3(SYS_fcntl, _fd, _cmd, o: uintptr: u64),

M strconv/stou.ha => strconv/stou.ha +2 -2
@@ 27,12 27,12 @@ export fn stou64b(s: str, base: uint) (u64 | invalid | overflow) = {
	let iter = strings::iter(s);
	for (true) {
		let r: rune = match (strings::next(&iter)) {
			void => break,
			_: void => break,
			r: rune => r,
		};

		let digit = match (rune_to_integer(r)) {
			void => return (iter.dec.offs - 1): invalid,
			_: void => return (iter.dec.offs - 1): invalid,
			d: u64 => d,
		};


M strings/index.ha => strings/index.ha +1 -1
@@ 14,7 14,7 @@ fn index_rune(s: str, r: rune) (size | void) = {
	let iter = iter(s);
	for (let i = 0z; true; i += 1) match (next(&iter)) {
		n: rune => if (r == n) return i,
		void => break,
		_: void => break,
	};
};


M strings/iter.ha => strings/iter.ha +8 -8
@@ 35,12 35,12 @@ export fn next(iter: *iterator) (rune | void) = {
			iter.push = void;
			return r;
		},
		void => void,
		_: void => void,
	};
	return match (utf8::next(&iter.dec)) {
		r: rune => r,
		void    => void,
		(utf8::more | utf8::invalid) =>
		_: void => void,
		_: (utf8::more | utf8::invalid) =>
			abort("Invalid UTF-8 string (this should not happen)"),
	};
};


@@ 51,8 51,8 @@ export fn prev(iter: *iterator) (rune | void) = {
	assert(iter.push is void);
	return match (utf8::prev(&iter.dec)) {
		r: rune => r,
		void => void,
		(utf8::more | utf8::invalid) =>
		_: void => void,
		_: (utf8::more | utf8::invalid) =>
			abort("Invalid UTF-8 string (this should not happen)"),
	};
};


@@ 80,7 80,7 @@ export fn iter_str(iter: *iterator) str = {
	for (let i = 0z; i < len(expected1); i += 1) {
		match (next(&s)) {
			r: rune => assert(r == expected1[i]),
			void    => abort(),
			_: void => abort(),
		};
	};
	assert(iter_str(&s) == "にちは");


@@ 89,7 89,7 @@ export fn iter_str(iter: *iterator) str = {
	for (let i = 0z; i < len(expected2); i += 1) {
		match (next(&s)) {
			r: rune => assert(r == expected2[i]),
			void => abort(),
			_: void => abort(),
		};
	};
	assert(next(&s) is void);


@@ 103,7 103,7 @@ export fn iter_str(iter: *iterator) str = {
	for (let i = 0z; i< len(expected3); i += 1) {
		match (prev(&s)) {
			r: rune => assert(r == expected3[i]),
			void => abort(),
			_: void => abort(),
		};
	};
	assert(prev(&s) is void);

M strings/sub.ha => strings/sub.ha +3 -3
@@ 6,7 6,7 @@ fn utf8_byte_len_bounded(iter: *iterator, end: size) size = {
	let pos = 0z;
	for (let i = 0z; i < end; i += 1) {
		let r: rune = match (strings::next(iter)) {
			void => break,
			_: void => break,
			r: rune => r,
		};



@@ 19,7 19,7 @@ fn utf8_byte_len_unbounded(iter: *iterator) size = {
	let pos = 0z;
	for (true) {
		let r: rune = match (strings::next(iter)) {
			void => break,
			_: void => break,
			r: rune => r,
		};



@@ 41,7 41,7 @@ export fn sub(s: str, start: size, end: (size | end)) str = {
	let starti = utf8_byte_len_bounded(&iter, start);
	let endi = match (end) {
		sz: size => starti + utf8_byte_len_bounded(&iter, sz - start),
		end => starti + utf8_byte_len_unbounded(&iter),
		_: end => starti + utf8_byte_len_unbounded(&iter),
	};
	let bytes = toutf8(s);
	return fromutf8_unsafe(bytes[starti..endi]);

M strings/tokenize.ha => strings/tokenize.ha +11 -11
@@ 19,7 19,7 @@ export fn tokenize(s: str, delim: str) tokenizer =
export fn next_token(s: *tokenizer) (str | void) = {
	return match (bytes::next_token(s)) {
		b: []u8 => fromutf8(b),
		void => void,
		_: void => void,
	};
};



@@ 27,7 27,7 @@ export fn next_token(s: *tokenizer) (str | void) = {
export fn peek_token(s: *tokenizer) (str | void) = {
	return match (bytes::peek_token(s)) {
		b: []u8 => fromutf8(b),
		void => void,
		_: void => void,
	};
};



@@ 40,24 40,24 @@ export fn remaining_tokens(s: *tokenizer) str = {
@test fn tokenize() void = {
	let tok = tokenize("Hello, my name is drew", " ");
	match (next_token(&tok)) {
		s: str => assert(s == "Hello,"),
		void   => abort(),
		s: str  => assert(s == "Hello,"),
		_: void => abort(),
	};

	match (next_token(&tok)) {
		s: str => assert(s == "my"),
		void   => abort(),
		s: str  => assert(s == "my"),
		_: void => abort(),
	};

	match (peek_token(&tok)) {
		s: str => assert(s == "name"),
		void   => abort(),
		s: str  => assert(s == "name"),
		_: void => abort(),
	};


	match (next_token(&tok)) {
		s: str => assert(s == "name"),
		void   => abort(),
		s: str  => assert(s == "name"),
		_: void => abort(),
	};

	assert(remaining_tokens(&tok) == "is drew");


@@ 90,7 90,7 @@ export fn splitN(in: str, delim: str, n: size) []str = {
	for (let i = 0z; i < n - 1z; i += 1) {
		match (next_token(&tok)) {
			s: str => append(toks, s),
			void => return toks,
			_: void => return toks,
		};
	};
	append(toks, remaining_tokens(&tok));

M temp/+linux.ha => temp/+linux.ha +1 -1
@@ 67,7 67,7 @@ export fn dir() str = {
	let path = path::join(get_tmpdir(), name);
	match (os::mkdir(path)) {
		err: fs::error => abort("Could not create temp directory"),
		void => void,
		_: void => void,
	};
	return path;
};

M time/+linux/functions.ha => time/+linux/functions.ha +3 -3
@@ 26,7 26,7 @@ export fn sleep(n: duration) void = {
	for (true) {
		let res = rt::timespec { ... };
		match (rt::nanosleep(req, &res)) {
			void => return,
			_: void => return,
			err: rt::errno => switch (err) {
				rt::EINTR => {
					req = &res;


@@ 85,14 85,14 @@ fn now_vdso(clock: clock, tp: *rt::timespec) (void | rt::errno) = {
export fn now(clock: clock) instant = {
	let tp = rt::timespec { ... };
	let err = match (now_vdso(clock, &tp)) {
		void => return timespec_to_instant(tp),
		_: void => return timespec_to_instant(tp),
		err: rt::errno => err
	};
	if (err != rt::wrap_errno(rt::ENOSYS)) {
		abort("Unexpected error from clock_gettime");
	};
	return match (rt::clock_gettime(clock, &tp)) {
		void => timespec_to_instant(tp),
		_: void => timespec_to_instant(tp),
		err: rt::errno => abort("Unexpected error from clock_gettime"),
	};
};

M unix/nice+linux.ha => unix/nice+linux.ha +1 -1
@@ 17,7 17,7 @@ export fn nice(inc: int) (void | errors::opaque) = {
		prio = -20;
	};
	return match (rt::setpriority(rt::PRIO_PROCESS, 0, prio)) {
		void => void,
		_: void => void,
		err: rt::errno => errors::errno(err),
	};
};