~sircmpwn/hare unlisted

b055679aabff4765d74656215baa8601e95f1a7d — Drew DeVault 3 months ago dd4942e
yield WIP

Signed-off-by: Drew DeVault <sir@cmpwn.com>
M ascii/ctype.ha => ascii/ctype.ha +2 -2
@@ 79,7 79,7 @@ export fn isascii(c: rune) bool = c: u32 <= 0o177;
// if it was not a lowercase letter (or was not ASCII).
export fn toupper(c: rune) rune = {
	return if (islower(c)) {
		(c: u32 - ('a': u32) + ('A': u32)): rune;
		yield (c: u32 - ('a': u32) + ('A': u32)): rune;
	} else c;
};



@@ 87,7 87,7 @@ export fn toupper(c: rune) rune = {
// if it was not an uppercase letter (or was not ASCII).
export fn tolower(c: rune) rune = {
	return if (isupper(c)) {
		(c: u32 - ('A': u32) + ('a': u32)): rune;
		yield (c: u32 - ('A': u32) + ('a': u32)): rune;
	} else c;
};


M bufio/buffered.ha => bufio/buffered.ha +1 -1
@@ 164,7 164,7 @@ fn buffered_read(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = {
				if (s.ravail == 0) {
					return io::EOF;
				};
				0z;
				yield 0z;
			},
			z: size => z,
		};

M bufio/memstream.ha => bufio/memstream.ha +2 -2
@@ 145,9 145,9 @@ fn read(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = {
		return io::EOF;
	};
	const n = if (len(s.buf) - s.pos < len(buf)) {
		len(s.buf) - s.pos;
		yield len(s.buf) - s.pos;
	} else {
		len(buf);
		yield len(buf);
	};
	assert(s.pos + n <= len(s.buf));
	buf[..n] = s.buf[s.pos..s.pos + n];

M cmd/hare/schedule.ha => cmd/hare/schedule.ha +2 -2
@@ 206,7 206,7 @@ fn sched_hare_object(
				path, fs::strerror(err)),
		};
		append(harec.cmd, "-t", path::join(path, td));
		path::join(path, name);
		yield path::join(path, name);
	} else {
		// XXX: This is probably kind of dumb
		// It would be better to apply any defines which affect this


@@ 215,7 215,7 @@ fn sched_hare_object(
			append(harec.cmd, "-D", plan.context.defines[i]);
		};

		mkfile(plan, "o"); // TODO: Should exes go in the cache?
		yield mkfile(plan, "o"); // TODO: Should exes go in the cache?
	};

	for (let i = 0z; i < len(ver.inputs); i += 1) {

M cmd/hare/subcmds.ha => cmd/hare/subcmds.ha +37 -35
@@ 253,41 253,43 @@ fn sched_walk(plan: *plan, ident: ast::ident, link: *[]*task) void = {
	const path = module::identpath(ident);
	let it = os::iter(path)?;
	free(path);
	:loop for (true) match (fs::next(it)) {
		ent: fs::dirent => {
			if (ent.name == "." || ent.name == "..") {
				continue;
			};
			if (ent.ftype & fs::mode::DIR != fs::mode::DIR) {
				continue;
			};
			let d = utf8::decode(ent.name);
			match (utf8::next(&d)) {
				void => break,
				(utf8::more | utf8::invalid) => continue :loop,
				r: rune => if (!ascii::isalpha(r) && r != '_') {
					continue :loop;
				},
			};
			for (true) match (utf8::next(&d)) {
				void => break,
				(utf8::more | utf8::invalid) => continue :loop,
				r: rune => if (!ascii::isalnum(r) && r != '_') {
					continue :loop;
				},
			};
			let new = ast::ident_dup(ident);
			append(new, strings::dup(ent.name));
			sched_walk(plan, new, link);

			match (module::lookup(plan.context, new)) {
				ver: module::version =>
					if (len(ver.inputs) == 0) continue,
				module::error => continue,
			};
			sched_module(plan, new, link);
		},
		void => break,
	for (true) :loop {
		match (fs::next(it)) {
			ent: fs::dirent => {
				if (ent.name == "." || ent.name == "..") {
					continue;
				};
				if (ent.ftype & fs::mode::DIR != fs::mode::DIR) {
					continue;
				};
				let d = utf8::decode(ent.name);
				match (utf8::next(&d)) {
					void => break,
					(utf8::more | utf8::invalid) => continue :loop,
					r: rune => if (!ascii::isalpha(r) && r != '_') {
						continue :loop;
					},
				};
				for (true) match (utf8::next(&d)) {
					void => break,
					(utf8::more | utf8::invalid) => continue :loop,
					r: rune => if (!ascii::isalnum(r) && r != '_') {
						continue :loop;
					},
				};
				let new = ast::ident_dup(ident);
				append(new, strings::dup(ent.name));
				sched_walk(plan, new, link);

				match (module::lookup(plan.context, new)) {
					ver: module::version =>
						if (len(ver.inputs) == 0) continue,
					module::error => continue,
				};
				sched_module(plan, new, link);
			},
			void => break,
		};
	};
};


M compress/flate/inflate.ha => compress/flate/inflate.ha +2 -2
@@ 233,7 233,7 @@ fn uncompressed_read(d: *decompressor) (void | io::EOF | io::error) = {
	let z = match (io::read(d.in, buf)?) {
		z: size => {
			d.left -= z;
			z;
			yield z;
		},
		io::EOF => return wraperror(inflate_err::EOF),
	};


@@ 294,7 294,7 @@ fn dynamic(d: *decompressor) (void | io::EOF | io::error) = {
					return wraperror(inflate_err::TABLE);
				};
				n = lens[len(lens) - 1];
				3 + bits(d, 2)?;
				yield 3 + bits(d, 2)?;
			},
			17 => 3 + bits(d, 3)?,
			18 => 11 + bits(d, 7)?,

M compress/zlib/reader.ha => compress/zlib/reader.ha +3 -2
@@ 57,7 57,8 @@ fn verifysum(s: *reader) (io::EOF | io::error) = {
		};
	};

	return if (adler32::sum32(s.hash) == endian::begetu32(hash)) io::EOF
	return
		if (adler32::sum32(s.hash) == endian::begetu32(hash)) io::EOF
		else wraperror(decompress_err::CHECKSUM);
};



@@ 67,7 68,7 @@ fn read(s: *io::stream, buf: []u8) (size | io::EOF | io::error) = {
		io::EOF => return verifysum(s),
		z: size => buf = buf[..z],
	};
	hash::write(s.hash, buf);
	return hash::write(s.hash, buf);
};

fn close(s: *io::stream) void = {

M crypto/sha256/sha256.ha => crypto/sha256/sha256.ha +1 -1
@@ 80,7 80,7 @@ fn write(st: *io::stream, buf: const []u8) (size | io::error) = {
	h.ln += n;
	if (h.nx > 0) {
		let n = if (len(b) > len(h.x) - h.nx) {
			len(h.x) - h.nx;
			yield len(h.x) - h.nx;
		} else len(b);
		h.x[h.nx..] = b[..n];
		h.nx += n;

M encoding/hex/hex.ha => encoding/hex/hex.ha +4 -0
@@ 9,12 9,15 @@ use strio;
// Returned when attempting to decode a string which contains invalid hex
// characters.
export type invalid = !void;
// XXX: REMOVE debugging printlns

// Encodes a byte slice as a hexadecimal string and writes it to a stream.
export fn encode(sink: *io::stream, b: []u8) (size | io::error) = {
	let z = 0z;
	for (let i = 0z; i < len(b); i += 1) {
		fmt::printfln("b[{}] ({:x}): {}", i, b[i], strio::string(sink));
		let s = strconv::u8tosb(b[i], strconv::base::HEX_LOWER);
		fmt::printfln("utosb: {}", s);
		if (len(s) == 1) {
			z += io::write(sink, ['0': u32: u8])?;
		};


@@ 35,6 38,7 @@ export fn encodestr(b: []u8) str = {
	let in: [_]u8 = [0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xF0, 0x0D];
	let s = encodestr(in);
	defer free(s);
	io::println("encoded", s);
	assert(s == "cafebabedeadf00d");
};


M fmt/fmt.ha => fmt/fmt.ha +5 -5
@@ 175,11 175,11 @@ export fn fprintf(
				continue;
			} else if (ascii::isdigit(r)) {
				strings::push(&iter, r);
				args[scan_uint(&iter)];
				yield args[scan_uint(&iter)];
			} else {
				strings::push(&iter, r);
				i += 1;
				args[i - 1];
				yield args[i - 1];
			};

			let mod = modifiers { base = strconv::base::DEC, ... };


@@ 245,11 245,11 @@ fn format_raw(
	b: bool => io::write(out, strings::toutf8(if (b) "true" else "false")),
	n: types::numeric => {
		let s = strconv::numerictosb(n, mod.base);
		io::write(out, strings::toutf8(s));
		yield io::write(out, strings::toutf8(s));
	},
	p: uintptr => {
		let s = strconv::uptrtosb(p, mod.base);
		io::write(out, strings::toutf8(s));
		yield io::write(out, strings::toutf8(s));
	},
	v: nullable *void => match (v) {
		v: *void => {


@@ 257,7 257,7 @@ fn format_raw(
				strconv::base::HEX_LOWER);
			let n = io::write(out, strings::toutf8("0x"))?;
			n += io::write(out, strings::toutf8(s))?;
			n;
			yield n;
		},
		null => format(out, "(null)", mod),
	},

M format/xml/parser.ha => format/xml/parser.ha +6 -6
@@ 73,7 73,7 @@ export fn scan(par: *parser) (token | void | error) = {
					io::EOF => return syntaxerr,
					rn: rune => {
						bufio::unreadrune(par.in, rn);
						rn;
						yield rn;
					},
				};
				bufio::unreadrune(par.in, rn);


@@ 138,7 138,7 @@ fn scan_attr(par: *parser) (token | error) = {
				'<' => return syntaxerr,
				'&' => {
					bufio::unreadrune(par.in, rn);
					scan_entity(par)?;
					yield scan_entity(par)?;
				},
				* => rn,
			};


@@ 228,7 228,7 @@ fn scan_content(par: *parser) (text | error) = {
				},
				'&', '%' => {
					bufio::unreadrune(par.in, rn);
					scan_entity(par)?;
					yield scan_entity(par)?;
				},
			};
			strio::appendrune(par.textbuf, rn)?;


@@ 268,7 268,7 @@ fn scan_entity(par: *parser) (rune | error) = {
		'%' => syntaxerr, // XXX: Deliberate omission: PEReference
		*   => {
			bufio::unreadrune(par.in, rn);
			scan_namedent(par);
			yield scan_namedent(par);
		},
	};
};


@@ 377,7 377,7 @@ fn prolog(par: *parser) (void | error) = {
		io::EOF => false,
		rn: rune => {
			bufio::unreadrune(par.in, rn);
			hadws && rn == 'e';
			yield hadws && rn == 'e';
		},
	};
	if (encoding) {


@@ 397,7 397,7 @@ fn prolog(par: *parser) (void | error) = {
		io::EOF => false,
		rn: rune => {
			bufio::unreadrune(par.in, rn);
			hadws && rn == 's';
			yield hadws && rn == 's';
		},
	};
	if (standalone) {

M fs/fs.ha => fs/fs.ha +1 -1
@@ 139,7 139,7 @@ export fn mksubdir(fs: *fs, path: str) (*fs | error) = {
	return match (fs.mksubdir) {
		null => {
			mkdir(fs, path)?;
			subdir(fs, path);
			yield subdir(fs, path);
		},
		f: *mksubdirfunc => f(fs, path),
	};

M getopt/getopts.ha => getopt/getopts.ha +3 -3
@@ 108,7 108,7 @@ export type help = (cmd_help | flag_help | parameter_help);
export fn parse(args: []str, help: help...) command = {
	let opts: []option = [];
	let i = 1z;
	:arg for (i < len(args); i += 1) {
	for (i < len(args); i += 1) :arg {
		const arg = args[i];
		if (len(arg) == 0 || arg == "-"
				|| !strings::has_prefix(arg, "-")) {


@@ 122,9 122,9 @@ export fn parse(args: []str, help: help...) command = {
		let d = utf8::decode(arg);
		assert(utf8::next(&d) as rune == '-');
		let next = utf8::next(&d);
		:flag for (next is rune; next = utf8::next(&d)) {
		for (next is rune; next = utf8::next(&d)) :flag {
			const r = next as rune;
			:help for (let j = 0z; j < len(help); j += 1) {
			for (let j = 0z; j < len(help); j += 1) :help {
				let p: parameter_help = match (help[j]) {
					cmd_help => continue :help,
					f: flag_help => if (r == f.0) {

M hare/lex/lex.ha => hare/lex/lex.ha +10 -9
@@ 271,7 271,7 @@ fn lex_name(lex: *lexer, loc: location, label: bool) (token | error) = {
			defer free(n);
			let tok = v: uintptr - &bmap[0]: uintptr;
			tok /= size(str): uintptr;
			(tok: ltok, void, loc);
			yield (tok: ltok, void, loc);
		},
	};
};


@@ 410,7 410,7 @@ fn lex_literal(lex: *lexer) (token | error) = {
				void => len(chars),
				suff: size => suff,
			};
			strings::fromutf8(chars[exp..end]);
			yield strings::fromutf8(chars[exp..end]);
		},
	};
	let exp = match (strconv::stoi(exp)) {


@@ 460,14 460,14 @@ fn lex_literal(lex: *lexer) (token | error) = {
			strconv::invalid => abort(),
			strconv::overflow => if (chars[0] != '-': u32: u8) {
				suff = ltok::LIT_U64;
				strconv::stou64b(val, base);
				yield strconv::stou64b(val, base);
			} else strconv::overflow,
		},
		ltok::LIT_I8, ltok::LIT_I16, ltok::LIT_I32, ltok::LIT_I64,
		ltok::LIT_INT => strconv::stoi64b(val, base),
		ltok::LIT_F32, ltok::LIT_F64, ltok::LIT_FCONST => {
			val = strings::fromutf8(chars[..floatend]);
			strconv::stof64(val);
			yield strconv::stof64(val);
		},
	};
	let val = match (val) {


@@ 475,13 475,13 @@ fn lex_literal(lex: *lexer) (token | error) = {
			for (let i = 0z; i < exp; i += 1) {
				val *= 10;
			};
			val;
			yield val;
		},
		val: i64 => {
			for (let i = 0z; i < exp; i += 1) {
				val *= 10;
			};
			val;
			yield val;
		},
		val: f64 => val,
		strconv::invalid => abort(), // Shouldn't be lexed in


@@ 528,9 528,10 @@ fn lex2(lex: *lexer) (token | error) = {
				':' => return (ltok::DOUBLE_COLON, void, first.1),
				* => {
					unget(lex, r);
					return if (is_name(r.0, false)) {
						lex_name(lex, first.1, true)?;
					} else (ltok::COLON, void, first.1);
					return if (is_name(r.0, false))
						lex_name(lex, first.1, true)?
					else
						(ltok::COLON, void, first.1);
				},
			},
			io::EOF => return (ltok::COLON, void, first.1),

M hare/module/context.ha => hare/module/context.ha +1 -1
@@ 45,7 45,7 @@ export fn context_init(tags: []tag, defs: []str, harepath: str) context = {
				};
				append(path, strings::dup("."));
				free(sl);
				path;
				yield path;
			},
		},
		cache: str = match (os::getenv("HARECACHE")) {

M hare/parse/decl.ha => hare/parse/decl.ha +2 -2
@@ 143,7 143,7 @@ fn decl_func(lexer: *lex::lexer) (ast::decl_func | error) = {
					len(params[i].name) > 0,
					"Expected parameter name in function declaration")?;
			};
			expression(lexer)?;
			yield expression(lexer)?;
		},
		ltok::SEMICOLON => lex::unlex(lexer, tok),
	};


@@ 172,7 172,7 @@ export fn decls(lexer: *lex::lexer) ([]ast::decl | error) = {
			void => false,
			lex::token => {
				comment = strings::dup(lex::comment(lexer));
				true;
				yield true;
			},
		};
		const toks = [ltok::CONST, ltok::LET, ltok::DEF, ltok::TYPE];

M hare/parse/expr.ha => hare/parse/expr.ha +21 -23
@@ 19,14 19,14 @@ export fn expression(lexer: *lex::lexer) (ast::expr | error) = {
		ltok::RSHIFTEQ, ltok::TIMESEQ,
	];

	const expr: ast::expr = if (indirect) {
	const expr: ast::expr = if (indirect) :outer {
		const expr = cast(lexer, void)?;
		// Disambiguate between
		// 	* unary-expression assignment-op expression
		// and
		// 	binary-expression
		if (peek(lexer, atoks...)? is lex::token) {
			expr;
			yield :outer, expr;
		} else return binarithm(lexer, ast::expr {
			start = loc,
			end = lex::prevloc(lexer),


@@ 99,11 99,11 @@ fn assert_expr(lexer: *lex::lexer, is_static: bool) (ast::expr | error) = {
			want(lexer, ltok::LPAREN)?;
			const msg: nullable *ast::expr =
				if (peek(lexer, ltok::RPAREN)? is lex::token) {
					null;
					yield null;
				} else alloc(expression(lexer)?);
			want(lexer, ltok::RPAREN)?;

			ast::assert_expr {
			yield ast::assert_expr {
				cond      = null,
				message   = msg,
				is_static = is_static,


@@ 115,11 115,11 @@ fn assert_expr(lexer: *lex::lexer, is_static: bool) (ast::expr | error) = {
				alloc(expression(lexer)?);
			const msg: nullable *ast::expr =
				if (try(lexer, ltok::COMMA)? is lex::token) {
					alloc(expression(lexer)?);
					yield alloc(expression(lexer)?);
				} else null;
			want(lexer, ltok::RPAREN)?;

			ast::assert_expr {
			yield ast::assert_expr {
				cond      = cond,
				message   = msg,
				is_static = is_static,


@@ 142,7 142,7 @@ fn alloc_expr(lexer: *lex::lexer) (ast::expr | error) = {
	const init = expression(lexer)?;
	const cap: nullable *ast::expr =
		if (try(lexer, ltok::COMMA)? is lex::token) {
			alloc(expression(lexer)?);
			yield alloc(expression(lexer)?);
		} else null;

	want(lexer, ltok::RPAREN)?;


@@ 209,15 209,13 @@ fn measurement(lexer: *lex::lexer) (ast::expr | error) = {
			want(lexer, ltok::LPAREN)?;
			let e = expression(lexer)?;
			want(lexer, ltok::RPAREN)?;

			alloc(e): ast::len_expr;
			yield alloc(e): ast::len_expr;
		},
		ltok::SIZE => {
			want(lexer, ltok::LPAREN)?;
			let ty = _type(lexer)?;
			want(lexer, ltok::RPAREN)?;

			alloc(ty): ast::size_expr;
			yield alloc(ty): ast::size_expr;
		},
		ltok::OFFSET => abort(), // TODO
	};


@@ 282,7 280,7 @@ fn binding(lexer: *lex::lexer, is_static: bool) (ast::expr | error) = {
		const name = want(lexer, ltok::NAME)?.1 as str;
		const btype: nullable *ast::_type =
			if (try(lexer, ltok::COLON)? is lex::token) {
				alloc(_type(lexer)?);
				yield alloc(_type(lexer)?);
			} else null;
		want(lexer, ltok::EQUAL)?;
		const init = alloc(expression(lexer)?);


@@ 330,7 328,7 @@ fn builtin(lexer: *lex::lexer) (ast::expr | error) = {
				void => return syntaxerr(tok.2,
					"Expected let, const, or assert"),
			};
			switch (tok.0) {
			yield switch (tok.0) {
				ltok::LET, ltok::CONST => binding(lexer, true),
				ltok::ABORT,
				ltok::ASSERT => assert_expr(lexer, true),


@@ 341,7 339,7 @@ fn builtin(lexer: *lex::lexer) (ast::expr | error) = {
		ltok::DEFER => {
			want(lexer, ltok::DEFER)?;
			let expr = alloc(expression(lexer)?);
			ast::expr {
			yield ast::expr {
				start = tok.2,
				end = lex::prevloc(lexer),
				expr = expr: ast::defer_expr,


@@ 442,7 440,7 @@ fn constant(lexer: *lex::lexer) (ast::expr | error) = {
fn control(lexer: *lex::lexer) (ast::expr | error) = {
	let tok = want(lexer, ltok::BREAK, ltok::CONTINUE, ltok::RETURN)?;
	let label = if (tok.0 == ltok::BREAK || tok.0 == ltok::CONTINUE) {
		match (try(lexer, ltok::LABEL)?) {
		yield match (try(lexer, ltok::LABEL)?) {
			tok: lex::token => tok.1 as str,
			void => "",
		};


@@ 501,7 499,7 @@ fn for_expr(lexer: *lex::lexer) (ast::expr | error) = {
	const label: ast::label = switch (tok.0) {
		ltok::LABEL => {
			want(lexer, ltok::FOR)?;
			tok.1 as str;
			yield tok.1 as str;
		},
		* => "",
	};


@@ 514,7 512,7 @@ fn for_expr(lexer: *lex::lexer) (ast::expr | error) = {
		lex::token => {
			const bindings = alloc(binding(lexer, false)?);
			want(lexer, ltok::SEMICOLON)?;
			bindings;
			yield bindings;
		},
	};



@@ 525,7 523,7 @@ fn for_expr(lexer: *lex::lexer) (ast::expr | error) = {
		void => null,
		lex::token => {
			want(lexer, ltok::SEMICOLON)?;
			alloc(expression(lexer)?);
			yield alloc(expression(lexer)?);
		},
	};



@@ 608,7 606,7 @@ fn indexing(lexer: *lex::lexer, lvalue: ast::expr) (ast::expr | error) = {
			object = alloc(lvalue),
			index = {
				assert(start != null && end == null);
				start: *ast::expr;
				yield start: *ast::expr;
			},
		},
	};


@@ 657,7 655,7 @@ fn plain_expression(lexer: *lex::lexer) (ast::expr | error) = {
				},
				lex::token => {
					let s = plain_struct(lexer, id)?;
					ast::expr {
					yield ast::expr {
						start = tok.2,
						end = lex::prevloc(lexer),
						expr = s,


@@ 777,7 775,7 @@ fn struct_field(
					const _type = alloc(_type(lexer)?);
					want(lexer, ltok::EQUAL)?;
					const init = alloc(expression(lexer)?);
					ast::struct_value {
					yield ast::struct_value {
						name = name,
						_type = _type,
						init = init,


@@ 892,7 890,7 @@ fn postfix_dot(
		let val = val as lex::value;
		synassert(lex::mkloc(lexer), val is i64,
			"Expected integer constant")?;
		ast::expr {
		return ast::expr {
			start = lvalue.start,
			end = lex::prevloc(lexer),
			expr = ast::access_tuple {


@@ 963,7 961,7 @@ fn match_case(lexer: *lex::lexer) (ast::match_case | error) = {
	let nt = switch (tok.0) {
		ltok::NULL => {
			want(lexer, ltok::NULL)?;
			("", ast::_type {
			yield ("", ast::_type {
				start = loc,
				end = lex::prevloc(lexer),
				flags = 0,

M hare/parse/parse.ha => hare/parse/parse.ha +3 -3
@@ 96,16 96,16 @@ fn nametype(lexer: *lex::lexer) ((str, ast::_type) | error) = {
			let name = tok.1 as str;
			want(lexer, ltok::NAME)?;
			tok = peek(lexer)? as lex::token;
			switch (tok.0) {
			yield switch (tok.0) {
				ltok::COLON => {
					want(lexer, ltok::COLON)?;
					(name, _type(lexer)?);
					yield (name, _type(lexer)?);
				},
				ltok::DOUBLE_COLON => {
					want(lexer, ltok::DOUBLE_COLON)?;
					let id = ident(lexer)?;
					insert(id[0], name);
					("", ast::_type {
					yield ("", ast::_type {
						start = start,
						end = lex::prevloc(lexer),
						flags = 0,

M hare/parse/type.ha => hare/parse/type.ha +5 -5
@@ 85,7 85,7 @@ fn primitive_type(lexer: *lex::lexer) (ast::_type | error) = {
		ltok::U32, ltok::U64, ltok::U64, ltok::U8, ltok::UINT,
		ltok::UINTPTR => {
			lex::unlex(lexer, tok);
			integer_type(lexer)?;
			yield integer_type(lexer)?;
		},
		ltok::RUNE => builtin_type::RUNE,
		ltok::STR => builtin_type::STR,


@@ 227,7 227,7 @@ fn struct_union_type(lexer: *lex::lexer) (ast::_type | error) = {
				want(lexer, ltok::LPAREN)?;
				let ex = expression(lexer)?;
				want(lexer, ltok::RPAREN)?;
				alloc(ex);
				yield alloc(ex);
			},
		};



@@ 299,7 299,7 @@ fn struct_embed_or_field(
			ltok::DOUBLE_COLON => {
				let id = ident(lexer)?;
				insert(id[0], name.1 as str);
				id;
				yield id;
			},
			* => abort(),
		},


@@ 348,7 348,7 @@ fn enum_type(lexer: *lex::lexer) (ast::_type | error) = {
		void => {
			let storage = integer_type(lexer)?;
			want(lexer, ltok::LBRACE)?;
			storage;
			yield storage;
		},
		lex::token => builtin_type::INT,
	};


@@ 416,7 416,7 @@ export fn _type(lexer: *lex::lexer) (ast::_type | error) = {
		ltok::LPAREN => {
			want(lexer, ltok::LPAREN)?;
			let t = _type(lexer)?;
			switch (want(lexer, ltok::BOR,
			yield switch (want(lexer, ltok::BOR,
					ltok::COMMA)?.0) {
				ltok::BOR => tagged_type(lexer, t, tok.2)?,
				ltok::COMMA => tuple_type(lexer, t, tok.2)?,

M hare/types/store.ha => hare/types/store.ha +29 -29
@@ 88,7 88,7 @@ fn fromast(store: *typestore, atype: *ast::_type) (_type | deferred | error) = {
		a: ast::alias_type => {
			// TODO: This is incomplete
			assert(!a.unwrap);
			alias {
			yield alias {
				id = ast::ident_dup(a.ident),
				_type = null,
			};


@@ 98,49 98,49 @@ fn fromast(store: *typestore, atype: *ast::_type) (_type | deferred | error) = {
			ast::builtin_type::BOOL => {
				sz = store.arch._int;
				align = store.arch._int;
				builtin::BOOL;
				yield builtin::BOOL;
			},
			ast::builtin_type::CHAR => {
				sz = 1; align = 1;
				builtin::CHAR;
				yield builtin::CHAR;
			},
			ast::builtin_type::F32 => {
				sz = 4; align = 4;
				builtin::F32;
				yield builtin::F32;
			},
			ast::builtin_type::F64 => {
				sz = 8; align = 8;
				builtin::F64;
				yield builtin::F64;
			},
			ast::builtin_type::I16 => {
				sz = 2; align = 2;
				builtin::I16;
				yield builtin::I16;
			},
			ast::builtin_type::I32 => {
				sz = 4; align = 4;
				builtin::I32;
				yield builtin::I32;
			},
			ast::builtin_type::I64 => {
				sz = 8; align = 8;
				builtin::I64;
				yield builtin::I64;
			},
			ast::builtin_type::I8 => {
				sz = 1; align = 1;
				builtin::I8;
				yield builtin::I8;
			},
			ast::builtin_type::INT => {
				sz = store.arch._int;
				align = store.arch._int;
				builtin::INT;
				yield builtin::INT;
			},
			ast::builtin_type::RUNE => {
				sz = 4; align = 4;
				builtin::RUNE;
				yield builtin::RUNE;
			},
			ast::builtin_type::SIZE => {
				sz = store.arch._size;
				align = store.arch._size;
				builtin::SIZE;
				yield builtin::SIZE;
			},
			ast::builtin_type::STR => {
				sz = store.arch._pointer;


@@ 150,37 150,37 @@ fn fromast(store: *typestore, atype: *ast::_type) (_type | deferred | error) = {
						store.arch._size
					else
						store.arch._pointer;
				builtin::STR;
				yield builtin::STR;
			},
			ast::builtin_type::U16 => {
				sz = 2; align = 2;
				builtin::U16;
				yield builtin::U16;
			},
			ast::builtin_type::U32 => {
				sz = 4; align = 4;
				builtin::U32;
				yield builtin::U32;
			},
			ast::builtin_type::U64 => {
				sz = 8; align = 8;
				builtin::U64;
				yield builtin::U64;
			},
			ast::builtin_type::U8 => {
				sz = 1; align = 1;
				builtin::U8;
				yield builtin::U8;
			},
			ast::builtin_type::UINT => {
				sz = store.arch._int;
				align = store.arch._int;
				builtin::UINT;
				yield builtin::UINT;
			},
			ast::builtin_type::UINTPTR => {
				sz = store.arch._pointer;
				align = store.arch._pointer;
				builtin::UINTPTR;
				yield builtin::UINTPTR;
			},
			ast::builtin_type::VOID => {
				sz = 0; align = 0;
				builtin::VOID;
				yield builtin::VOID;
			},
			ast::builtin_type::NULL => builtin::NULL,
			ast::builtin_type::ICONST,


@@ 190,7 190,7 @@ fn fromast(store: *typestore, atype: *ast::_type) (_type | deferred | error) = {
		p: ast::pointer_type => {
			sz = store.arch._pointer;
			align = store.arch._pointer;
			pointer {
			yield pointer {
				referent = lookup(store, p.referent)?,
				flags = p.flags: pointer_flags,
			};


@@ 207,7 207,7 @@ fn fromast(store: *typestore, atype: *ast::_type) (_type | deferred | error) = {
					align = field._type.align;
				};
			};
			st;
			yield st;
		},
		un: ast::union_type => {
			let st = struct_from_ast(store, un, true)?;


@@ 221,7 221,7 @@ fn fromast(store: *typestore, atype: *ast::_type) (_type | deferred | error) = {
					align = field._type.align;
				};
			};
			st;
			yield st;
		},
		ta: ast::tagged_type => {
			let ta = tagged_from_ast(store, ta)?;


@@ 238,7 238,7 @@ fn fromast(store: *typestore, atype: *ast::_type) (_type | deferred | error) = {
				align = store.arch._int;
			};
			sz += store.arch._int % align + store.arch._int;
			ta;
			yield ta;
		},
		tu: ast::tuple_type => {
			let tu = tuple_from_ast(store, tu)?;


@@ 252,13 252,13 @@ fn fromast(store: *typestore, atype: *ast::_type) (_type | deferred | error) = {
					align = value._type.align;
				};
			};
			tu;
			yield tu;
		},
		lt: ast::list_type => {
			let r = list_from_ast(store, &lt)?;
			sz = r.0;
			align = r.1;
			r.2;
			yield r.2;
		},
		et: ast::enum_type => abort(), // TODO
	};


@@ 309,14 309,14 @@ fn list_from_ast(
			align = if (store.arch._pointer > store.arch._size)
					store.arch._pointer
				else store.arch._size;
			memb: slice;
			yield memb: slice;
		},
		// Note: contextual length is handled by hare::unit when
		// initializing bindings. We treat it like unbounded here and
		// it's fixed up later on.
		(ast::len_unbounded | ast::len_contextual) => {
			align = memb.align;
			array {
			yield array {
				length = SIZE_UNDEFINED,
				member = memb,
			};


@@ 330,7 330,7 @@ fn list_from_ast(
			sz = memb.sz * length;
			assert(sz / length == memb.sz, "overflow");
			align = memb.align;
			array {
			yield array {
				length = length,
				member = memb,
			};

M hare/unparse/expr.ha => hare/unparse/expr.ha +31 -35
@@ 21,16 21,16 @@ export fn expr(
				z += fmt::fprintf(out, "[")?;
				z += expr(out, indent, *ix.index)?;
				z += fmt::fprintf(out, "]")?;
				z;
				yield z;
			},
			fi: ast::access_field => {
				let z = expr(out, indent, *fi.object)?;
				z + fmt::fprintf(out, ".{}", fi.field)?;
				yield z + fmt::fprintf(out, ".{}", fi.field)?;
			},
			tp: ast::access_tuple => {
				let z = expr(out, indent, *tp.object)?;
				z += fmt::fprintf(out, ".")?;
				z + expr(out, indent, *tp.value)?;
				yield z + expr(out, indent, *tp.value)?;
			},
		},
		e: ast::alloc_expr => {


@@ 44,7 44,7 @@ export fn expr(
				},
			};
			z += fmt::fprint(out, ")")?;
			z;
			yield z;
		},
		e: ast::append_expr => {
			let z = fmt::fprint(out, "append(")?;


@@ 65,17 65,15 @@ export fn expr(
					z += fmt::fprint(out, "...")?;
				},
			};
			z + fmt::fprint(out, ")")?;
			yield z + fmt::fprint(out, ")")?;
		},
		e: ast::assert_expr => {
			let z = fmt::fprint(
				out, if (e.is_static) "static " else "")?;
			// assert without a condition = abort
			z += match (e.cond) {
				e: *ast::expr => {
					fmt::fprint(out, "assert(")? +
						expr(out, indent, *e)?;
				},
				e: *ast::expr => fmt::fprint(out,
					"assert(")? + expr(out, indent, *e)?,
				null => fmt::fprint(out, "abort(")?,
			};
			z += match (e.message) {


@@ 87,11 85,11 @@ export fn expr(
						},
						null => void,
					};
					z + expr(out, indent, *m)?;
					yield z + expr(out, indent, *m)?;
				},
				null => 0,
			};
			z + fmt::fprint(out, ")")?;
			yield z + fmt::fprint(out, ")")?;
		},
		e: ast::assign_expr => {
			let z = 0z;


@@ 120,7 118,7 @@ export fn expr(
			};
			z += fmt::fprintf(out, " {} ", op)?;
			z += expr(out, indent, *e.value)?;
			z;
			yield z;
		},
		e: ast::binarithm_expr => {
			let z = expr(out, indent, *e.lvalue)?;


@@ 146,7 144,7 @@ export fn expr(
				ast::binarithm_op::BXOR		=> "^",
			})?;
			z += expr(out, indent, *e.rvalue)?;
			z;
			yield z;
		},
		e: ast::binding_expr => {
			let z = fmt::fprintf(out, "{}{}",


@@ 156,11 154,9 @@ export fn expr(
				let binding = e.bindings[i];
				z += match (binding._type) {
					null => fmt::fprint(out, binding.name)?,
					t: *ast::_type => {
						fmt::fprintf(out, "{}: ",
							binding.name)?
						+ _type(out, indent, *t)?;
					},
					t: *ast::_type => fmt::fprintf(out,
							"{}: ", binding.name)?
						+ _type(out, indent, *t)?,
				};
				z += fmt::fprint(out, " = ")?;
				z += expr(out, indent, *binding.init)?;


@@ 168,14 164,14 @@ export fn expr(
					z += fmt::fprint(out, ", ")?;
				};
			};
			z;
			yield z;
		},
		e: ast::break_expr => {
			let z = fmt::fprint(out, "break")?;
			if (e != "") {
				z += fmt::fprintf(out, " :{}", e)?;
			};
			z;
			yield z;
		},
		e: ast::call_expr => {
			let z = expr(out, indent, *e.lvalue)?;


@@ 190,7 186,7 @@ export fn expr(
				z += fmt::fprintf(out, "...")?;
			};
			z += fmt::fprintf(out, ")")?;
			z;
			yield z;
		},
		e: ast::cast_expr => {
			let z = expr(out, indent, *e.value)?;


@@ 201,7 197,7 @@ export fn expr(
			};
			z += fmt::fprintf(out, "{}", op)?;
			z += _type(out, indent, *e._type)?;
			z;
			yield z;
		},
		e: ast::constant_expr => constant(out, indent, e)?,
		e: ast::continue_expr => {


@@ 209,7 205,7 @@ export fn expr(
			if (e != "") {
				z += fmt::fprintf(out, " :{}", e)?;
			};
			z;
			yield z;
		},
		e: ast::defer_expr =>
			fmt::fprint(out, "defer ")? + expr(out, indent, *e)?,


@@ 234,7 230,7 @@ export fn expr(
					z += expr(out, indent, *e)?;
				},
			};
			z;
			yield z;
		},
		e: ast::list_expr => {
			let z = fmt::fprintf(out, "{{")?;


@@ 245,23 241,23 @@ export fn expr(
			};
			z += newline(out, indent)?;
			z += fmt::fprintf(out, "}}")?;
			z;
			yield z;
		},
		e: ast::match_expr => match_expr(out, indent, e)?,
		e: ast::len_expr => {
			let z = fmt::fprint(out, "len(")?;
			z += expr(out, indent, *e)?;
			z + fmt::fprint(out, ")")?;
			yield z + fmt::fprint(out, ")")?;
		},
		e: ast::size_expr => {
			let z = fmt::fprint(out, "size(")?;
			z += _type(out, indent, *e)?;
			z + fmt::fprint(out, ")")?;
			yield z + fmt::fprint(out, ")")?;
		},
		e: ast::offset_expr => abort(),
		e: ast::propagate_expr => {
			let z = expr(out, indent, *e.expr)?;
			z + fmt::fprintf(out, if (e.is_abort) "!" else "?")?;
			yield z + fmt::fprintf(out, if (e.is_abort) "!" else "?")?;
		},
		e: ast::return_expr => {
			let z = fmt::fprint(out, "return")?;


@@ 272,7 268,7 @@ export fn expr(
					z += expr(out, indent, *e)?;
				},
			};
			z;
			yield z;
		},
		e: ast::slice_expr => {
			let z = expr(out, indent, *e.object)?;


@@ 287,7 283,7 @@ export fn expr(
				e: *ast::expr => expr(out, indent, *e)?,
			};
			z += fmt::fprint(out, "]")?;
			z;
			yield z;
		},
		e: ast::switch_expr => switch_expr(out, indent, e)?,
		e: ast::unarithm_expr => {


@@ 300,7 296,7 @@ export fn expr(
				ast::unarithm_op::PLUS	=> "+",
			})?;
			z += expr(out, indent, *e.operand)?;
			z;
			yield z;
		},
	};
};


@@ 329,7 325,7 @@ fn constant(
					z += fmt::fprint(out, ", ")?;
				};
			};
			z + fmt::fprintf(out, "{}]",
			yield z + fmt::fprintf(out, "{}]",
				if (ac.expand) "..." else "")?;
		},
		sc: ast::struct_constant => struct_constant(out, indent, sc)?,


@@ 341,7 337,7 @@ fn constant(
					z += fmt::fprint(out, ", ")?;
				};
			};
			z + fmt::fprint(out, ")")?;
			yield z + fmt::fprint(out, ")")?;
		},
	};
};


@@ 353,9 349,9 @@ fn struct_constant(
) (size | io::error) = {
	let z = 0z;
	z += if (len(sc.alias) != 0) {
		ident(out, sc.alias)?;
		yield ident(out, sc.alias)?;
	} else {
		fmt::fprint(out, "struct")?;
		yield fmt::fprint(out, "struct")?;
	};
	z += fmt::fprint(out, " {")?;
	indent += 1;

M hare/unparse/type.ha => hare/unparse/type.ha +5 -6
@@ 66,11 66,11 @@ fn struct_union_type(
	let membs = match (t._type) {
		st: ast::struct_type => {
			z += fmt::fprint(out, "struct {")?;
			st: []ast::struct_member;
			yield st: []ast::struct_member;
		},
		ut: ast::union_type => {
			z += fmt::fprint(out, "union {")?;
			ut: []ast::struct_member;
			yield ut: []ast::struct_member;
		},
	};



@@ 88,10 88,9 @@ fn struct_union_type(
		z += match (membs[i].member) {
			se: ast::struct_embedded => _type(out, indent, *se)?,
			sa: ast::struct_alias => ident(out, sa)?,
			sf: ast::struct_field => {
				fmt::fprintf(out, "{}: ", sf.name)?
					+ _type(out, indent, *sf._type)?;
			},
			sf: ast::struct_field => fmt::fprintf(out,
					"{}: ", sf.name)?
				+ _type(out, indent, *sf._type)?,
		};

		z += fmt::fprint(out, ",")?;

M hash/crc16/crc16.ha => hash/crc16/crc16.ha +2 -2
@@ 162,9 162,9 @@ export fn memoize(polynomial: u16, buf: *[256]u16) void = {
		let value = i: u16;
		for (let z = 0z; z < 8; z += 1) {
			value = if (value & 0x1 == 1) {
				(value >> 1) ^ polynomial;
				yield (value >> 1) ^ polynomial;
			} else {
				value >> 1;
				yield value >> 1;
			};
		};
		buf[i] = value;

M hash/crc32/crc32.ha => hash/crc32/crc32.ha +2 -2
@@ 169,9 169,9 @@ export fn memoize(polynomial: u32, buf: *[256]u32) void = {
		let value = i: u32;
		for (let z = 0z; z < 8; z += 1) {
			value = if (value & 0x1 == 1) {
				(value >> 1) ^ polynomial;
				yield (value >> 1) ^ polynomial;
			} else {
				value >> 1;
				yield value >> 1;
			};
		};
		buf[i] = value;

M hash/crc64/crc64.ha => hash/crc64/crc64.ha +2 -2
@@ 201,9 201,9 @@ export fn memoize(polynomial: u64, buf: *[256]u64) void = {
		let value = i: u64;
		for (let z = 0z; z < 8; z += 1) {
			value = if (value & 0x1 == 1) {
				(value >> 1) ^ polynomial;
				yield (value >> 1) ^ polynomial;
			} else {
				value >> 1;
				yield value >> 1;
			};
		};
		buf[i] = value;

M io/limit.ha => io/limit.ha +2 -2
@@ 49,9 49,9 @@ fn limited_read(s: *stream, buf: []u8) (size | EOF | error) = {
fn limited_write(s: *stream, buf: const []u8) (size | error) = {
	let stream = s: *limited_stream;
	let slice = if (len(buf) > stream.limit) {
		buf[..stream.limit];
		yield buf[..stream.limit];
	} else {
		buf[..];
		yield buf[..];
	};
	stream.limit -= len(slice);
	return write(stream.source, slice);

M linux/io_uring/cqe.ha => linux/io_uring/cqe.ha +2 -4
@@ 17,7 17,7 @@ export fn wait(ring: *io_uring) (*cqe | error) = {
		err: error => err,
		cq: nullable *cqe => {
			assert(cq != null); // XXX: Correct?
			cq: *cqe;
			yield cq: *cqe;
		},
	};
};


@@ 34,9 34,7 @@ export fn result(cqe: *cqe) (int | error) = {
		rt::ETIME => errors::timeout,
		rt::ECANCELED => errors::cancelled,
		* => errors::errno(rt::wrap_errno(-cqe.res)),
	} else {
		cqe.res;
	};
	} else cqe.res: int;
};

// Gets the user data field of a [[cqe]]. See [[set_user]] for the corresponding

M linux/io_uring/setup.ha => linux/io_uring/setup.ha +1 -1
@@ 39,7 39,7 @@ export fn setup(entries: u32, params: *params) (io_uring | error) = {
	};

	cq.ring_ptr = if (uring.features & features::SINGLE_MMAP == features::SINGLE_MMAP) {
		sq.ring_ptr;
		yield sq.ring_ptr;
	} else match (rt::mmap(null, cq.ring_sz,
			rt::PROT_READ | rt::PROT_WRITE,
			rt::MAP_SHARED | rt::MAP_POPULATE,

M net/+linux/util.ha => net/+linux/util.ha +1 -2
@@ 10,7 10,6 @@ fn setsockopt(sockfd: int, option: int, value: bool) (void | rt::errno) = {
	let val: int = if (value) 1 else 0;
	rt::setsockopt(sockfd, rt::SOL_SOCKET, option,
		&val: *void, size(int): u32)?;
	return;
};

fn setfcntl(sockfd: int, flag: int) (void | rt::errno) = {


@@ 20,7 19,7 @@ fn setfcntl(sockfd: int, flag: int) (void | rt::errno) = {
};

fn wrap(ie: (int | rt::errno)) (int | io::error) = {
	match (ie) {
	return match (ie) {
		i: int => i,
		err: rt::errno => errors::errno(err),
	};

M net/ip/+test.ha => net/ip/+test.ha +1 -1
@@ 9,7 9,7 @@ fn ip_test(s: str, expected: (addr|invalid)) void = {
	} else {
		assert(expected is addr);
		assert(equal(pr as addr, expected as addr));
		pr as addr;
		yield pr as addr;
	};
	let fmted = string(ip);
	let iprp = parse(fmted);

M net/ip/ip.ha => net/ip/ip.ha +2 -2
@@ 40,14 40,14 @@ export fn equal(l: addr, r: addr) bool = {
				return false;
			};
			let r = r as addr4;
			bytes::equal(l, r);
			yield bytes::equal(l, r);
		},
		l: addr6 => {
			if (!(r is addr6)) {
				return false;
			};
			let r = r as addr6;
			bytes::equal(l, r);
			yield bytes::equal(l, r);
		},
	};
};

M rt/+linux/+aarch64.ha => rt/+linux/+aarch64.ha +1 -1
@@ 7,7 7,7 @@ export fn clone(
	child_tid: nullable *int,
	tls: u64,
) (int | void | errno) = {
	return match (wrap_return(syscall5(SYS_clone,
	return match (check(syscall5(SYS_clone,
			flags: u64,
			stack: uintptr: u64,
			parent_tid: uintptr: u64,

M rt/+linux/+x86_64.ha => rt/+linux/+x86_64.ha +1 -1
@@ 7,7 7,7 @@ export fn clone(
	child_tid: nullable *int,
	tls: u64,
) (int | void | errno) = {
	return match (wrap_return(syscall5(SYS_clone,
	return match (check(syscall5(SYS_clone,
			flags: u64,
			stack: uintptr: u64,
			parent_tid: uintptr: u64,

M rt/+linux/errno.ha => rt/+linux/errno.ha +1 -1
@@ 6,7 6,7 @@ export fn wrap_errno(err: int) errno = err: errno;

// Checks the return value from a Linux syscall and, if found to be in error,
// returns the appropriate error. Otherwise, returns the original value.
fn wrap_return(r: u64) (errno | u64) = {
fn check(r: u64) (errno | u64) = {
	if (r > -4096: u64) {
		return (-(r: i64)): int: errno;
	};

M rt/+linux/segmalloc.ha => rt/+linux/segmalloc.ha +1 -1
@@ 5,7 5,7 @@ fn segmalloc(n: size) nullable *void = {
			MAP_PRIVATE | MAP_ANON, -1, 0)) {
		err: errno => {
			assert(err == ENOMEM: errno);
			null;
			yield null;
		},
		p: *void => p,
	};

M rt/+linux/stat.ha => rt/+linux/stat.ha +1 -2
@@ 12,10 12,9 @@ fn fstatat_statx(
	statbuf: *stx,
) (void | errno) = {
	let path = kpath(path)?;
	wrap_return(syscall5(SYS_statx,
	check(syscall5(SYS_statx,
		dirfd: u64, path: uintptr: u64, flags: u64,
		mask: u64, statbuf: uintptr: u64))?;
	return;
};

export fn fstatat(

M rt/+linux/syscalls.ha => rt/+linux/syscalls.ha +76 -102
@@ 21,7 21,7 @@ fn kpath(path: path) (*const char | errno) = {
				length: size,
				capacity: size,
			};
			ptr.buf[..ptr.length];
			yield ptr.buf[..ptr.length];
		},
		b: []u8 => b,
	};


@@ 34,18 34,18 @@ fn kpath(path: path) (*const char | errno) = {
};

export fn read(fd: int, buf: *void, count: size) (size | errno) = {
	return wrap_return(syscall3(SYS_read,
	return check(syscall3(SYS_read,
		fd: u64, buf: uintptr: u64, count: u64))?: size;
};

export fn write(fd: int, buf: *const void, count: size) (size | errno) = {
	return wrap_return(syscall3(SYS_write,
	return check(syscall3(SYS_write,
		fd: u64, buf: uintptr: u64, count: u64))?: size;
};

export fn open(path: path, flags: int, mode: uint) (int | errno) = {
	let path = kpath(path)?;
	return wrap_return(syscall4(SYS_openat, AT_FDCWD: u64,
	return check(syscall4(SYS_openat, AT_FDCWD: u64,
		path: uintptr: u64, flags: u64, mode: u64))?: int;
};



@@ 54,7 54,7 @@ fn openat(
	path: *const char,
	flags: int,
	mode: uint,
) (int | errno) = wrap_return(syscall4(SYS_openat, dirfd: u64,
) (int | errno) = check(syscall4(SYS_openat, dirfd: u64,
	path: uintptr: u64, flags: u64, mode: u64))?: int;

export fn openat2(


@@ 69,16 69,14 @@ export fn openat2(

export fn unlink(path: path) (void | errno) = {
	let path = kpath(path)?;
	wrap_return(syscall3(SYS_unlinkat,
	check(syscall3(SYS_unlinkat,
		AT_FDCWD: u64, path: uintptr: u64, 0u64))?;
	return;
};

export fn unlinkat(dirfd: int, path: path, flags: int) (void | errno) = {
	let path = kpath(path)?;
	wrap_return(syscall3(SYS_unlinkat,
	check(syscall3(SYS_unlinkat,
		dirfd: u64, path: uintptr: u64, flags: u64))?;
	return;
};

export fn mknodat(


@@ 88,83 86,72 @@ export fn mknodat(
	dev: dev_t,
) (void | errno) = {
	let path = kpath(path)?;
	wrap_return(syscall3(SYS_mknodat,
	check(syscall3(SYS_mknodat,
		path: uintptr: u64, mode: u64, dev: u64))?;
	return;
};

export fn chmod(path: path, mode: uint) (void | errno) = {
	let path = kpath(path)?;
	wrap_return(syscall4(SYS_fchmodat,
	check(syscall4(SYS_fchmodat,
		AT_FDCWD: u64, path: uintptr: u64, mode: u64, 0))?;
	return;
};

export fn fchmodat(dirfd: int, path: path, mode: uint, flags: int) (void | errno) = {
	let path = kpath(path)?;
	wrap_return(syscall4(SYS_fchmodat,
	check(syscall4(SYS_fchmodat,
		dirfd: u64, path: uintptr: u64, mode: u64, flags: u64))?;
	return;
};

export fn chown(path: path, uid: uint, gid: uint) (void | errno) = {
	let path = kpath(path)?;
	wrap_return(syscall5(SYS_fchownat,
	check(syscall5(SYS_fchownat,
		AT_FDCWD: u64, path: uintptr: u64, uid: u32, gid: u32, 0))?;
	return;
};

export fn fchownat(dirfd: int, path: path, uid: uint, gid: uint, flags: int) (void | errno) = {
	let path = kpath(path)?;
	wrap_return(syscall5(SYS_fchownat,
	check(syscall5(SYS_fchownat,
		dirfd: u64, path: uintptr: u64, uid: u32, gid: u32, flags: u64))?;
	return;
};

export fn dup(fd: int) (int | errno) = {
	return wrap_return(syscall1(SYS_dup, fd: u64))?: int;
	return check(syscall1(SYS_dup, fd: u64))?: int;
};

export fn close(fd: int) (void | errno) = {
	wrap_return(syscall1(SYS_close, fd: u64))?;
	return;
	check(syscall1(SYS_close, fd: u64))?;
};

export fn chdir(path: path) (void | errno) = {
	let path = kpath(path)?;
	wrap_return(syscall1(SYS_chdir, path: uintptr: u64))?;
	return;
	check(syscall1(SYS_chdir, path: uintptr: u64))?;
};

export fn fchdir(fd: int) (void | errno) = {
	wrap_return(syscall1(SYS_fchdir, fd: u64))?;
	return;
	check(syscall1(SYS_fchdir, fd: u64))?;
};

export fn chroot(path: path) (void | errno) = {
	let path = kpath(path)?;
	wrap_return(syscall1(SYS_chroot, path: uintptr: u64))?;
	return;
	check(syscall1(SYS_chroot, path: uintptr: u64))?;
};

export fn mkdir(path: path, mode: uint) (void | errno) = {
	let path = kpath(path)?;
	wrap_return(syscall3(SYS_mkdirat, AT_FDCWD: u64,
	check(syscall3(SYS_mkdirat, AT_FDCWD: u64,
		path: uintptr: u64, mode: u64))?;
	return;
};

export fn mkdirat(dirfd: int, path: path, mode: uint) (void | errno) = {
	let path = kpath(path)?;
	wrap_return(syscall3(SYS_mkdirat,
	check(syscall3(SYS_mkdirat,
		dirfd: u64, path: uintptr: u64, mode: u64))?;
	return;
};

export fn execveat(dirfd: int, path: path, argv: *[*]nullable *const char,
		envp: *[*]nullable *const char, flags: int) errno = {
	let path = kpath(path)?;
	return match (wrap_return(syscall5(SYS_execveat, dirfd: u64,
	return match (check(syscall5(SYS_execveat, dirfd: u64,
			path: uintptr: u64, argv: uintptr: u64,
			envp: uintptr: u64, flags: u64))) {
		err: errno => err,


@@ 184,7 171,7 @@ export fn wait4(
	options: int,
	rusage: *rusage,
) (int | errno) = {
	return wrap_return(syscall4(SYS_wait4,
	return check(syscall4(SYS_wait4,
		pid: u64, wstatus: uintptr: u64,
		options: u64, rusage: uintptr: u64))?: int;
};


@@ 194,19 181,17 @@ export fn sendfile(
	in: int,
	offs: nullable *size,
	count: size,
) (size | errno) = wrap_return(syscall4(SYS_sendfile,
) (size | errno) = check(syscall4(SYS_sendfile,
	out: u64, in: u64, offs: uintptr: u64, count: u64))?: size;
	
export @noreturn fn exit(status: int) void = syscall1(SYS_exit, status: u64);

export fn kill(pid: int, signal: int) (void | errno) = {
	wrap_return(syscall2(SYS_kill, pid: u64, signal: u64))?;
	return;
	check(syscall2(SYS_kill, pid: u64, signal: u64))?;
};

export fn pipe2(pipefd: *[2]int, flags: int) (void | errno) = {
	wrap_return(syscall2(SYS_pipe2, pipefd: uintptr: u64, flags: u64))?;
	return;
	check(syscall2(SYS_pipe2, pipefd: uintptr: u64, flags: u64))?;
};

export fn mmap(


@@ 219,7 204,7 @@ export fn mmap(
) (*void | errno) = {
	let r = syscall6(SYS_mmap, addr: uintptr: u64,
		length: u64, prot: u64, flags: u64, fd: u64, offs: u64);
	match (wrap_return(r)) {
	return match (check(r)) {
		err: errno => {
			// XXX: Type promotion would simplify this
			return if (r: int == -EPERM


@@ 227,7 212,7 @@ export fn mmap(
					&& (flags & MAP_ANON) > 0
					&& (flags & MAP_FIXED) == 0) {
				 // Fix up incorrect EPERM from kernel:
				wrap_errno(ENOMEM);
				yield wrap_errno(ENOMEM);
			} else err;
		},
		n: u64 => n: uintptr: *void,


@@ 235,26 220,24 @@ export fn mmap(
};

export fn munmap(addr: *void, length: size) (void | errno) = {
	wrap_return(syscall2(SYS_munmap,
	check(syscall2(SYS_munmap,
		addr: uintptr: u64, length: u64))?;
	return;
};
	

export fn mprotect(addr: *void, length: size, prot: uint) (void | errno) = {
	wrap_return(syscall3(SYS_mprotect,
	check(syscall3(SYS_mprotect,
		addr: uintptr: u64, length: u64, prot: u64))?;
	return;
};
	

export fn lseek(fd: int, off: i64, whence: uint) (i64 | errno) = {
	return wrap_return(syscall3(SYS_lseek,
	return check(syscall3(SYS_lseek,
		fd: u64, off: u64, whence: u64))?: i64;
};

fn faccessat1(dirfd: int, path: *const char, mode: int) (bool | errno) = {
	return match (wrap_return(syscall3(SYS_faccessat, dirfd: u64,
	return match (check(syscall3(SYS_faccessat, dirfd: u64,
			path: uintptr: u64, mode: u64))) {
		err: errno => switch (err) {
			EACCES => false,


@@ 262,7 245,7 @@ fn faccessat1(dirfd: int, path: *const char, mode: int) (bool | errno) = {
		},
		n: u64 => {
			assert(n == 0);
			true;
			yield true;
		},
	};
};


@@ 277,7 260,7 @@ export fn faccessat(
	flags: int,
) (bool | errno) = {
	let path = kpath(path)?;
	return match (wrap_return(syscall4(SYS_faccessat2, dirfd: u64,
	return match (check(syscall4(SYS_faccessat2, dirfd: u64,
			path: uintptr: u64, mode: u64, flags: u64))) {
		err: errno => switch (err) {
			EACCES => false,


@@ 288,13 271,13 @@ export fn faccessat(
		},
		n: u64 => {
			assert(n == 0);
			true;
			yield true;
		},
	};
};

export fn getdents64(dirfd: int, dirp: *void, count: size) (size | errno) = {
	return wrap_return(syscall3(SYS_getdents64, dirfd: u64,
	return check(syscall3(SYS_getdents64, dirfd: u64,
		dirp: uintptr: u64, count: u64))?: size;
};



@@ 308,7 291,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) {
	return check(match (arg) {
		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),


@@ 318,33 301,29 @@ export fn fcntl(fd: int, cmd: int, arg: fcntl_arg) (int | errno) = {
};

export fn getrandom(buf: *void, bufln: size, flags: uint) (size | errno) = {
	return wrap_return(syscall3(SYS_getrandom,
	return check(syscall3(SYS_getrandom,
		buf: uintptr: u64, bufln: u64, flags: u64))?: size;
};

// TODO: Implement me with VDSO
export fn clock_gettime(clock_id: int, tp: *timespec) (void | errno) = {
	wrap_return(syscall2(SYS_clock_gettime,
	check(syscall2(SYS_clock_gettime,
		clock_id: u64, tp: uintptr: u64))?;
	return;
};

export fn nanosleep(req: *const timespec, rem: *timespec) (void | errno) = {
	wrap_return(syscall2(SYS_nanosleep,
	check(syscall2(SYS_nanosleep,
		req: uintptr: u64, rem: uintptr: u64))?;
	return;
};

export fn uname(uts: *utsname) (void | errno) = {
	wrap_return(syscall1(SYS_uname, uts: uintptr: u64))?;
	return;
	check(syscall1(SYS_uname, uts: uintptr: u64))?;
};

// The return value is statically allocated and must be duplicated before
// calling getcwd again.
export fn getcwd() (*const char | errno) = {
	static let pathbuf: [PATH_MAX + 1]u8 = [0...];
	wrap_return(syscall2(SYS_getcwd,
	check(syscall2(SYS_getcwd,
		&pathbuf: *[*]u8: uintptr: u64,
		PATH_MAX + 1))?;
	return &pathbuf: *const char;


@@ 356,7 335,7 @@ export fn ppoll(
	timeout: const nullable *timespec,
	sigmask: const nullable *sigset,
) (int | errno) = {
	return wrap_return(syscall4(SYS_ppoll, fds: uintptr: u64, nfds: u64,
	return check(syscall4(SYS_ppoll, fds: uintptr: u64, nfds: u64,
		timeout: uintptr: u64, sigmask: uintptr: u64))?: int;
};



@@ 369,7 348,7 @@ export fn poll(fds: *pollfd, nfds: nfds_t, timeout: int) (int | errno) = {
};

export fn epoll_create1(flags: int) (int | errno) = {
	return wrap_return(syscall1(SYS_epoll_create1, flags: u64))?: int;
	return check(syscall1(SYS_epoll_create1, flags: u64))?: int;
};

export fn epoll_create(size_: int) (int | errno) = {


@@ 382,7 361,7 @@ export fn epoll_ctl(
	fd: int,
	event: nullable *epoll_event
) (int | errno) = {
	return wrap_return(syscall4(SYS_epoll_ctl,
	return check(syscall4(SYS_epoll_ctl,
		epfd: u64, op: u64, fd: u64, event: uintptr: u64))?: int;
};



@@ 393,7 372,7 @@ export fn epoll_pwait(
	timeout: int,
	sigmask: nullable *sigset
) (int | errno) = {
	return wrap_return(syscall6(SYS_epoll_pwait,
	return check(syscall6(SYS_epoll_pwait,
		epfd: u64, events: uintptr: u64,
		maxevents: u64, timeout: u64,
		sigmask: uintptr: u64, size(sigset): u64))?: int;


@@ 409,7 388,7 @@ export fn epoll_wait(
};

export fn timerfd_create(clock_id: int, flags: int) (int | errno) = {
	return wrap_return(syscall2(SYS_timerfd_create,
	return check(syscall2(SYS_timerfd_create,
		clock_id: u64, flags: u64))?: int;
};



@@ 419,18 398,18 @@ export fn timerfd_settime(
	new_value: *const itimerspec,
	old_value: nullable *itimerspec
) (int | errno) = {
	return wrap_return(syscall4(SYS_timerfd_settime,
	return check(syscall4(SYS_timerfd_settime,
		fd: u64, flags: u64,
		new_value: uintptr: u64, old_value: uintptr: u64))?: int;
};

export fn timerfd_gettime(fd: int, curr_value: *itimerspec) (int | errno) = {
	return wrap_return(syscall2(SYS_timerfd_gettime,
	return check(syscall2(SYS_timerfd_gettime,
		fd: u64, curr_value: uintptr: u64))?: int;
};

export fn signalfd(fd: int, mask: *const sigset, flags: int) (int | errno) = {
	return wrap_return(syscall4(SYS_signalfd4,
	return check(syscall4(SYS_signalfd4,
		fd: u64, mask: uintptr: u64, size(sigset): u64,
		flags: u64))?: int;
};


@@ 440,7 419,7 @@ export fn sigprocmask(
	set: *const sigset,
	old: nullable *sigset
) (int | errno) = {
	return wrap_return(syscall4(SYS_rt_sigprocmask,
	return check(syscall4(SYS_rt_sigprocmask,
		how: u64, set: uintptr: u64, old: uintptr: u64,
		size(sigset): u64))?: int;
};


@@ 457,40 436,40 @@ export fn sigaction(
	real_act.sa_flags |= SA_RESTORER;
	let restore_fn = if ((act.sa_flags & SA_SIGINFO) != 0) &restore_si else &restore;
	real_act.sa_restorer = &restore;
	return wrap_return(syscall4(SYS_rt_sigaction,
	return check(syscall4(SYS_rt_sigaction,
		signum: u64, &real_act: uintptr: u64, old: uintptr: u64,
		size(sigset): u64))?: int;
};

export fn socket(domain: int, type_: int, protocol: int) (int | errno) = {
	return wrap_return(syscall3(SYS_socket,
	return check(syscall3(SYS_socket,
		domain: u64, type_: u64, protocol: u64))?: int;
};

export fn connect(sockfd: int, addr: *const sockaddr, addrlen: u32) (int | errno) = {
	return wrap_return(syscall3(SYS_connect,
	return check(syscall3(SYS_connect,
		sockfd: u64, addr: uintptr: u64, addrlen: u64))?: int;
};

export fn bind(sockfd: int, addr: *const sockaddr, addrlen: u32) (int | errno) = {
	return wrap_return(syscall3(SYS_bind,
	return check(syscall3(SYS_bind,
		sockfd: u64, addr: uintptr: u64, addrlen: u64))?: int;
};

export fn listen(sockfd: int, backlog: u32) (int | errno) = {
	return wrap_return(syscall2(SYS_listen,
	return check(syscall2(SYS_listen,
		sockfd: u64, backlog: u64))?: int;
};

export fn accept(sockfd: int, addr: nullable *sockaddr, addrlen: nullable *u32) (int | errno) = {
	return wrap_return(syscall3(SYS_accept,
	return check(syscall3(SYS_accept,
		sockfd: u64, addr: uintptr: u64, addrlen: uintptr: u64))?: int;
};

export fn recvfrom(sockfd: int, buf: *void, len_: size, flags: int,
	src_addr: nullable *sockaddr, addrlen: nullable *u32
) (size | errno) = {
	return wrap_return(syscall6(SYS_recvfrom,
	return check(syscall6(SYS_recvfrom,
		sockfd: u64, buf: uintptr: u64, len_: u64, flags: u64,
		src_addr: uintptr: u64, addrlen: uintptr: u64))?: size;
};


@@ 498,7 477,7 @@ export fn recvfrom(sockfd: int, buf: *void, len_: size, flags: int,
export fn sendto(sockfd: int, buf: *void, len_: size, flags: int,
	dest_addr: nullable *sockaddr, addrlen: nullable *u32
) (size | errno) = {
	return wrap_return(syscall6(SYS_sendto,
	return check(syscall6(SYS_sendto,
		sockfd: u64, buf: uintptr: u64, len_: u64, flags: u64,
		dest_addr: uintptr: u64, addrlen: uintptr: u64))?: size;
};


@@ 512,94 491,89 @@ export fn send(sockfd: int, buf: *void, len_: size, flags: int) (size | errno) =
};

export fn getsockopt(sockfd: int, level: int, optname: int, optval: nullable *void, optlen: nullable *u32) (int | errno) = {
	return wrap_return(syscall5(SYS_getsockopt,
	return check(syscall5(SYS_getsockopt,
		sockfd: u64, level: u64, optname: u64,
		optval: uintptr: u64, optlen: uintptr: u64))?: int;
};

export fn setsockopt(sockfd: int, level: int, optname: int, optval: *void, optlen: u32) (int | errno) = {
	return wrap_return(syscall5(SYS_setsockopt,
	return check(syscall5(SYS_setsockopt,
		sockfd: u64, level: u64, optname: u64,
		optval: uintptr: u64, optlen: u64))?: int;
};

export fn ioctl(fd: int, req: u64, arg: nullable *void) (int | errno) = {
	return wrap_return(syscall3(SYS_ioctl, fd: u64,
	return check(syscall3(SYS_ioctl, fd: u64,
		req, arg: uintptr: u64))?: int;
};

export fn getsockname(sockfd: int, addr: nullable *sockaddr, addrlen: nullable *u32) (int | errno) = {
	return wrap_return(syscall3(SYS_getsockname,
	return check(syscall3(SYS_getsockname,
		sockfd: u64, addr: uintptr: u64, addrlen: uintptr: u64))?: int;
};

export fn getpeername(sockfd: int, addr: nullable *sockaddr, addrlen: nullable *u32) (int | errno) = {
	return wrap_return(syscall3(SYS_getpeername,
	return check(syscall3(SYS_getpeername,
		sockfd: u64, addr: uintptr: u64, addrlen: uintptr: u64))?: int;
};

export fn readv(fd: int, iov: *const iovec, iovcnt: int) (int | errno) = {
	return wrap_return(syscall3(SYS_readv,
	return check(syscall3(SYS_readv,
		fd: u64, iov: uintptr: u64, iovcnt: u64))?: int;
};

export fn writev(fd: int, iov: *const iovec, iovcnt: int) (int | errno) = {
	return wrap_return(syscall3(SYS_writev,
	return check(syscall3(SYS_writev,
		fd: u64, iov: uintptr: u64, iovcnt: u64))?: int;
};

export fn sendmsg(fd: int, msg: *const msghdr, flags: int) (int | errno) = {
	return wrap_return(syscall3(SYS_sendmsg,
	return check(syscall3(SYS_sendmsg,
		fd: u64, msg: uintptr: u64, flags: u64))?: int;
};

export fn recvmsg(fd: int, msg: *const msghdr, flags: int) (int | errno) = {
	return wrap_return(syscall3(SYS_recvmsg,
	return check(syscall3(SYS_recvmsg,
		fd: u64, msg: uintptr: u64, flags: u64))?: int;
};

export fn umask(mode: mode_t) (mode_t | errno) = {
	return wrap_return(syscall1(SYS_umask, mode: u64))?: mode_t;
	return check(syscall1(SYS_umask, mode: u64))?: mode_t;
};

export fn setresuid(uid: uid_t, euid: uid_t, suid: uid_t) (void | errno) = {
	wrap_return(syscall3(SYS_setresuid, uid: u64, euid: u64, suid: u64))?;
	return;
	check(syscall3(SYS_setresuid, uid: u64, euid: u64, suid: u64))?;
};

export fn setresgid(gid: gid_t, egid: gid_t, sgid: gid_t) (void | errno) = {
	wrap_return(syscall3(SYS_setresgid, gid: u64, egid: u64, sgid: u64))?;
	return;
	check(syscall3(SYS_setresgid, gid: u64, egid: u64, sgid: u64))?;
};

export fn getresuid(uid: *uid_t, euid: *uid_t, suid: *uid_t) (void | errno) = {
	wrap_return(syscall3(SYS_getresuid,
	check(syscall3(SYS_getresuid,
		uid: uintptr: u64,
		euid: uintptr: u64,
		suid: uintptr: u64))?;
	return;
};

export fn getresgid(gid: *gid_t, egid: *gid_t, sgid: *gid_t) (void | errno) = {
	wrap_return(syscall3(SYS_getresgid,
	check(syscall3(SYS_getresgid,
		gid: uintptr: u64,
		egid: uintptr: u64,
		sgid: uintptr: u64))?;
	return;
};

export fn getpriority(which: int, who: id_t) (int | errno) = {
	return wrap_return(syscall2(SYS_setpriority,
	return check(syscall2(SYS_setpriority,
		which: u64, who: u64))?: int;
};

export fn setpriority(which: int, who: id_t, prio: int) (void | errno) = {
	wrap_return(syscall3(SYS_setpriority, which: u64, who: u64, prio: u64))?;
	return;
	check(syscall3(SYS_setpriority, which: u64, who: u64, prio: u64))?;
};

export fn io_uring_setup(entries: u32, params: *void) (int | errno) = {
	return wrap_return(syscall2(SYS_io_uring_setup,
	return check(syscall2(SYS_io_uring_setup,
		entries: u64, params: uintptr: u64))?: int;
};



@@ 608,7 582,7 @@ export fn io_uring_register(
	opcode: uint,
	arg: nullable *void,
	nr_args: uint,
) (int | errno) = wrap_return(syscall4(SYS_io_uring_register,
) (int | errno) = check(syscall4(SYS_io_uring_register,
	fd: u64, opcode: u64, arg: uintptr: u64, nr_args: u64))?: int;

export fn io_uring_enter(


@@ 618,7 592,7 @@ export fn io_uring_enter(
	flags: uint,
	sig: const nullable *sigset,
) (uint | errno) = {
	return wrap_return(syscall5(SYS_io_uring_enter,
	return check(syscall5(SYS_io_uring_enter,
		fd: u64, to_submit: u64, min_complete: u64,
		flags: u64, sig: uintptr: u64))?: uint;
};

M rt/ensure.ha => rt/ensure.ha +9 -0
@@ 4,9 4,17 @@ export type slice = struct {
	capacity: size,
};

// XXX REMOVE ME
fn io::println(items: str...) void;
fn strconv::ztos(z: size) str;

export fn ensure(s: *slice, membsz: size) void = {
	io::println("ensure; membsz", strconv::ztos(membsz));
	let cap = s.capacity;
	io::println("ensure; cap", strconv::ztos(cap));
	io::println("ensure; length", strconv::ztos(s.length));
	if (cap >= s.length) {
		io::println("ensure; early return");
		return;
	};
	for (cap < s.length) {


@@ 21,6 29,7 @@ export fn ensure(s: *slice, membsz: size) void = {
	const data = realloc(s.data, s.capacity * membsz);
	assert(data != null || s.capacity * membsz == 0);
	s.data = data;
	io::println("ensure; re-allocated");
};

export fn unensure(s: *slice, membsz: size) void = {

M rt/malloc.ha => rt/malloc.ha +22 -3
@@ 44,12 44,23 @@ fn size2bin(s: size) size = {
	return (s + (WORD * (ALIGN - 1) - 1)) / (WORD * ALIGN);
};

// XXX REMOVE ME
fn io::println(items: str...) void;
fn strconv::ztos(z: size) str;

// Allocates n bytes of memory and returns a pointer to them, or null if there
// is insufficient memory.
export fn malloc(n: size) nullable *void = {
	return if (n == 0) null
	let p = if (n == 0) null
		else if (n > bin2size(len(bins) - 1)) malloc_large(n)
		else malloc_small(n);
	io::println("malloc", strconv::ztos(p: uintptr: size));
	io::println("of size", strconv::ztos(n));
	io::println("actual", strconv::ztos((p: *[*]size)[-1]));
	return p;
	//return if (n == 0) null
	//	else if (n > bin2size(len(bins) - 1)) malloc_large(n)
	//	else malloc_small(n);
};

fn malloc_large(n: size) nullable *void = {


@@ 74,7 85,7 @@ fn malloc_small(n: size) nullable *void = {
	return if (p != null) {
		let q = *(p: **void);
		bins[b] = q;
		p;
		yield p;
	} else null;
};



@@ 107,12 118,15 @@ fn list_from_block(s: size, p: uintptr) nullable *void = {

// Frees a pointer previously allocated with [[malloc]].
export @symbol("rt.free") fn free_(_p: nullable *void) void = {
	io::println("free", strconv::ztos(_p: uintptr: size));
	if (_p != null) {
		let p = _p: *void;
		let bsize = (p: uintptr - size(size): uintptr): *[1]size;
		let s = bsize[0];
		if (s <= bin2size(len(bins) - 1)) free_small(p, s)
		else free_large(p, s);
		io::println("of size", strconv::ztos(s));
		//free_large(p, s);
	};
};



@@ 135,10 149,13 @@ fn free_small(p: *void, s: size) void = {
// in-place. Returns null if there is insufficient memory to support the
// request.
export fn realloc(_p: nullable *void, n: size) nullable *void = {
	io::println("realloc", strconv::ztos(n));
	if (n == 0) {
		io::println("n == 0");
		free_(_p);
		return null;
	} else if (_p == null) {
		io::println("malloc short circuit");
		return malloc(n);
	};



@@ 146,16 163,18 @@ export fn realloc(_p: nullable *void, n: size) nullable *void = {
	let bsize = (p: uintptr - size(size): uintptr): *size;
	let s = *bsize;
	if (s >= n) {
		io::println("existing allocation is sufficient");
		return p;
	};

	if (n < bin2size(len(bins) - 1) && size2bin(n) == size2bin(s)) {
		//io::println("existing bin is sufficient");
		return p;
	};

	let new = malloc(n);
	if (new != null) {
		memcpy(new: *void, p, s);
		memcpy(new: *void, p, n);
		free(p);
	};


M strconv/ftos.ha => strconv/ftos.ha +3 -3
@@ 73,7 73,7 @@ fn mulshiftall64(m: u64, mul: (u64, u64), j: i32, mm_shift: u32) (u64, u64, u64)
		const lo3 = lo - mul.0;
		const mid3 = mid - mul.1 - ibool(lo3 > lo);
		const hi3 = hi - ibool(mid3 > mid);
		u128rshift(mid3, hi3, (j - 64 - 1): u32);
		yield u128rshift(mid3, hi3, (j - 64 - 1): u32);
	} else {
		const lo3 = lo + lo;
		const mid3 = mid + mid + ibool(lo3 < lo);


@@ 81,7 81,7 @@ fn mulshiftall64(m: u64, mul: (u64, u64), j: i32, mm_shift: u32) (u64, u64, u64)
		const lo4 = lo3 - mul.0;
		const mid4 = mid3 - mul.1 - ibool(lo4 > lo3);
		const hi4 = hi3 - ibool(mid4 > mid3);
		u128rshift(mid4, hi4, (j - 64): u32);
		yield u128rshift(mid4, hi4, (j - 64): u32);
	};
	const v_rounded = u128rshift(mid, hi, (j - 64 - 1): u32);
	return (v_plus, v_rounded, v_minus);


@@ 434,7 434,7 @@ fn encode(buf: []u8, v: decf64) size = {
		let ex = if (exp < 0) {
			buf[h] = '-': u32: u8;
			h += 1;
			-exp;
			yield -exp;
		} else exp;
		const elen = declen(ex: u64);
		let k = h + elen: i32 - 1;

M strconv/stof.ha => strconv/stof.ha +19 -14
@@ 245,10 245,10 @@ fn parse(s: str) (decimal | invalid) = {
	const buf = strings::toutf8(s);
	d.negative = if (buf[0] == '+': u32: u8) {
		i += 1;
		false;
		yield false;
	} else if (buf[0] == '-': u32: u8) {
		i += 1;
		true;
		yield true;
	} else false;
	let sawdot = false, sawdigits = false;
	let nd: u32 = 0, dp: i32 = 0;


@@ 278,12 278,12 @@ fn parse(s: str) (decimal | invalid) = {
	if (i < len(s) && (buf[i] == 'e': u32: u8 || buf[i] == 'E': u32: u8)) {
		i += 1;
		if (i >= len(s)) return i: invalid;
		const expsign: int = if (buf[i] == '+': u32: u8) {
		let expsign: int = if (buf[i] == '+': u32: u8) {
			i += 1;
			1;
			yield 1;
		} else if (buf[i] == '-': u32: u8) {
			i += 1;
			-1;
			yield -1;
		} else 1;
		if (i >= len(s) || !ascii::isdigit(buf[i]: u32: rune)) return i: invalid;
		let e: int = 0;


@@ 311,19 311,24 @@ fn floatbits(d: decimal, mbits: u8, ebits: u8, ebias: i16) (u64 | overflow) = {
		return overflow;
	};
	for (d.decimal_point > 0) {
		const n: int = if (d.decimal_point: uint >= len(powtab))
			maxshift: int
			else powtab[d.decimal_point];
		const n: int =
			if (d.decimal_point: uint >= len(powtab))
				maxshift: int
			else powtab[d.decimal_point]: int;
		decimal_shift(&d, -n);
		e += n;
	};
	for (d.decimal_point <= 0) {
		const n: int = if (d.decimal_point == 0) {
			if (d.digits[0] >= 5) break; 
			if (d.digits[0] < 2) 2 else 1;
		} else if (-d.decimal_point >= len(powtab): int)
			maxshift: int
		else powtab[-d.decimal_point];
		let n: int = if (d.decimal_point == 0) {
			if (d.digits[0] >= 5) {
				break; 
			};
			yield if (d.digits[0] < 2) 2 else 1;
		} else if (-d.decimal_point >= len(powtab): int) {
			yield maxshift: int;
		} else {
			yield powtab[-d.decimal_point]: int;
		};
		decimal_shift(&d, n);
		e -= n;
	};

M strconv/utos.ha => strconv/utos.ha +1 -1
@@ 17,7 17,7 @@ export fn u64tosb(u: u64, b: base) const str = {
	];
	const lut = if (b != base::HEX_LOWER) &lut_upper else {
		b = base::HEX_UPPER;
		&lut_lower;
		yield &lut_lower;
	};

	let s = types::string { data = &buf, ... };

M unix/tty/+linux/winsize.ha => unix/tty/+linux/winsize.ha +4 -6
@@ 12,12 12,10 @@ export fn winsize(tty: *io::stream) (ttysize | error) = {
	};
	let wsz = rt::winsize { ... };
	return match (rt::ioctl(fd, rt::TIOCGWINSZ, &wsz: *void)) {
		e: rt::errno => {
			switch (e: int) {
				rt::EBADFD => errors::invalid,
				rt::ENOTTY => errors::unsupported,
				* => abort("unreachable"),
			};
		e: rt::errno => switch (e: int) {
			rt::EBADFD => errors::invalid,
			rt::ENOTTY => errors::unsupported,
			* => abort("unreachable"),
		},
		int => ttysize {
			rows    = wsz.ws_row,