~sircmpwn/hare

06c599619fc71d3363b753f83900029c2ec40f30 — Drew DeVault 3 months ago 12611cc
all: 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
@@ 167,7 167,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
@@ 205,7 205,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


@@ 214,7 214,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/harec/gen.ha => cmd/harec/gen.ha +0 -1
@@ 75,7 75,6 @@ fn gen_expr(ctx: *context, expr: *unit::expr) value = {
		unit::constant_expr => return gen_expr_const(ctx, expr),
		* => abort(), // TODO
	};
	abort(); // Unreachable
};

fn gen_expr_const(ctx: *context, expr: *unit::expr) value = {

M cmd/haredoc/html.ha => cmd/haredoc/html.ha +3 -3
@@ 478,11 478,11 @@ fn struct_union_html(
	let members = match (t.repr) {
		t: ast::struct_type => {
			z += fmt::fprint(out, "<span class='keyword'>struct</span> {")?;
			t: []ast::struct_member;
			yield t: []ast::struct_member;
		},
		t: ast::union_type => {
			z += fmt::fprint(out, "<span class='keyword'>union</span> {")?;
			t: []ast::struct_member;
			yield t: []ast::struct_member;
		},
	};



@@ 634,7 634,7 @@ fn type_html(
		t: ast::union_type => z += struct_union_html(out, indent, _type, brief)?,
	};

	z;
	return z;
};

fn prototype_html(

M cmd/haredoc/sort.ha => cmd/haredoc/sort.ha +6 -6
@@ 38,7 38,7 @@ fn sort_decls(decls: []ast::decl) summary = {
							// XXX: Kind of bad
							let new: []ast::decl_type = [];
							append(new, t[j]);
							new;
							yield new;
						},
						docs = decl.docs,
					});


@@ 52,7 52,7 @@ fn sort_decls(decls: []ast::decl) summary = {
							// XXX: Kind of bad
							let new: []ast::decl_const = [];
							append(new, c[j]);
							new;
							yield new;
						},
						docs = decl.docs,
					});


@@ 66,7 66,7 @@ fn sort_decls(decls: []ast::decl) summary = {
							// XXX: Kind of bad
							let new: []ast::decl_global = [];
							append(new, g[j]);
							new;
							yield new;
						},
						docs = decl.docs,
					});


@@ 98,14 98,14 @@ fn decl_ident(decl: ast::decl) ast::ident = match (decl.decl) {
	f: ast::decl_func => f.ident,
	t: []ast::decl_type => {
		assert(len(t) == 1);
		t[0].ident;
		yield t[0].ident;
	},
	c: []ast::decl_const => {
		assert(len(c) == 1);
		c[0].ident;
		yield c[0].ident;
	},
	g: []ast::decl_global => {
		assert(len(g) == 1);
		g[0].ident;
		yield g[0].ident;
	},
};

M cmd/haredoc/tty.ha => cmd/haredoc/tty.ha +3 -3
@@ 166,12 166,12 @@ fn struct_union_type_tty(
		st: ast::struct_type => {
			z += fmt::fprint(out,
				"\x1b[36m" "struct" "\x1b[0m" " {")?;
			st: []ast::struct_member;
			yield st: []ast::struct_member;
		},
		ut: ast::union_type => {
			z += fmt::fprint(out,
				"\x1b[36m" "union" "\x1b[0m" " {")?;
			ut: []ast::struct_member;
			yield ut: []ast::struct_member;
		},
	};



@@ 190,7 190,7 @@ fn struct_union_type_tty(
			se: ast::struct_embedded => type_tty(out, indent, *se)?,
			sa: ast::struct_alias => unparse::ident(out, sa)?,
			sf: ast::struct_field => {
				fmt::fprintf(out, "{}: ", sf.name)?
				yield fmt::fprintf(out, "{}: ", sf.name)?
					+ type_tty(out, indent, *sf._type)?;
			},
		};

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 +1 -1
@@ 67,7 67,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 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 +9 -9
@@ 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);


@@ 84,14 84,14 @@ export fn scan(par: *parser) (token | void | error) = {
				};
				let el = scan_element(par)?;
				par.state = state::ATTRS;
				el;
				yield el;
			},
			* => {
				if (par.state == state::ROOT) {
					return syntaxerr;
				};
				bufio::unreadrune(par.in, rn);
				scan_content(par)?;
				yield scan_content(par)?;
			},
		},
		state::ATTRS => {


@@ 106,7 106,7 @@ export fn scan(par: *parser) (token | void | error) = {
				return syntaxerr;
			};
			bufio::unreadrune(par.in, rn);
			scan_attr(par)?;
			yield scan_attr(par)?;
		},
	};
};


@@ 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)?;
				},
				* => rn,
			};


@@ 269,7 269,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);
		},
	};
};


@@ 378,7 378,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) {


@@ 398,7 398,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
@@ 167,7 167,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 hare/lex/lex.ha => hare/lex/lex.ha +7 -7
@@ 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


@@ 529,7 529,7 @@ fn lex2(lex: *lexer) (token | error) = {
				* => {
					unget(lex, r);
					return if (is_name(r.0, false)) {
						lex_name(lex, first.1, true)?;
						yield lex_name(lex, first.1, true)?;
					} else (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 -21
@@ 25,8 25,8 @@ export fn expression(lexer: *lex::lexer) (ast::expr | error) = {
		// 	* unary-expression assignment-op expression
		// and
		// 	binary-expression
		if (peek(lexer, atoks...)? is lex::token) {
			expr;
		yield if (peek(lexer, atoks...)? is lex::token) {
			yield 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)?;


@@ 210,14 210,14 @@ fn measurement(lexer: *lex::lexer) (ast::expr | error) = {
			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 282,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 330,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 341,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 442,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 => "",
		};


@@ 482,7 482,7 @@ fn compound_expr(lexer: *lex::lexer) (ast::expr | error) = {
	const label = switch (start.0) {
		ltok::LABEL => {
			want(lexer, ltok::LBRACE)?;
			start.1 as str;
			yield start.1 as str;
		},
		* => "",
	};


@@ 517,7 517,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;
		},
	};



@@ 528,7 528,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)?);
		},
	};



@@ 610,7 610,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;
			},
		},
	};


@@ 659,7 659,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,


@@ 779,7 779,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,


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


@@ 965,7 965,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
@@ 107,7 107,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),
				secondary = null,
			};


@@ 117,49 117,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;


@@ 169,37 169,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,


@@ 209,7 209,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,
			};


@@ 226,7 226,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)?;


@@ 240,7 240,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)?;


@@ 257,7 257,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)?;


@@ 271,13 271,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
	};


@@ 331,14 331,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,
			};


@@ 352,7 352,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/unit/process.ha => hare/unit/process.ha +1 -1
@@ 28,7 28,7 @@ fn process_decl(
	decl: *ast::decl,
) (decl | error) = {
	// TODO: match on &decl.decl
	match (decl.decl) {
	return match (decl.decl) {
		co: []ast::decl_const => abort(), // TODO
		gl: []ast::decl_global => abort(), // TODO
		ty: []ast::decl_type => abort(), // TODO

M hare/unit/scan.ha => hare/unit/scan.ha +1 -1
@@ 25,7 25,7 @@ fn scan_decl(
	decl: *ast::decl,
) (void | types::deferred | error) = {
	// TODO: match on &decl.decl
	match (decl.decl) {
	return match (decl.decl) {
		co: []ast::decl_const => abort(), // TODO
		gl: []ast::decl_global => abort(), // TODO
		ty: []ast::decl_type => abort(), // TODO

M hare/unparse/expr.ha => hare/unparse/expr.ha +42 -30
@@ 21,16 21,18 @@ 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)?;
				z += fmt::fprintf(out, ".{}", fi.field)?;
				yield z;
			},
			tp: ast::access_tuple => {
				let z = expr(out, indent, *tp.object)?;
				z += fmt::fprintf(out, ".")?;
				z + expr(out, indent, *tp.value)?;
				z += expr(out, indent, *tp.value)?;
				yield z;
			},
		},
		e: ast::alloc_expr => {


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


@@ 65,7 67,8 @@ export fn expr(
					z += fmt::fprint(out, "...")?;
				},
			};
			z + fmt::fprint(out, ")")?;
			z += fmt::fprint(out, ")")?;
			yield z;
		},
		e: ast::assert_expr => {
			let z = fmt::fprint(


@@ 73,7 76,7 @@ export fn expr(
			// assert without a condition = abort
			z += match (e.cond) {
				e: *ast::expr => {
					fmt::fprint(out, "assert(")? +
					yield fmt::fprint(out, "assert(")? +
						expr(out, indent, *e)?;
				},
				null => fmt::fprint(out, "abort(")?,


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


@@ 120,7 125,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 151,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, "{}{}",


@@ 157,9 162,11 @@ export fn expr(
				z += match (binding._type) {
					null => fmt::fprint(out, binding.name)?,
					t: *ast::_type => {
						fmt::fprintf(out, "{}: ",
							binding.name)?
						+ _type(out, indent, *t)?;
						let z = 0z;
						z += fmt::fprintf(out, "{}: ",
							binding.name)?;
						z += _type(out, indent, *t)?;
						yield z;
					},
				};
				z += fmt::fprint(out, " = ")?;


@@ 168,14 175,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 197,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 208,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 216,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 241,7 @@ export fn expr(
					z += expr(out, indent, *e)?;
				},
			};
			z;
			yield z;
		},
		e: ast::compound_expr => {
			let z = 0z;


@@ 249,23 256,26 @@ 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, ")")?;
			z += fmt::fprint(out, ")")?;
			yield z;
		},
		e: ast::size_expr => {
			let z = fmt::fprint(out, "size(")?;
			z += _type(out, indent, *e)?;
			z + fmt::fprint(out, ")")?;
			z += fmt::fprint(out, ")")?;
			yield z;
		},
		ast::offset_expr => abort(),
		e: ast::propagate_expr => {
			let z = expr(out, indent, *e.expr)?;
			z + fmt::fprintf(out, if (e.is_abort) "!" else "?")?;
			z += fmt::fprintf(out, if (e.is_abort) "!" else "?")?;
			yield z;
		},
		e: ast::return_expr => {
			let z = fmt::fprint(out, "return")?;


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


@@ 291,7 301,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 => {


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


@@ 333,8 343,9 @@ fn constant(
					z += fmt::fprint(out, ", ")?;
				};
			};
			z + fmt::fprintf(out, "{}]",
			z += fmt::fprintf(out, "{}]",
				if (ac.expand) "..." else "")?;
			yield z;
		},
		sc: ast::struct_constant => struct_constant(out, indent, sc)?,
		tu: ast::tuple_constant => {


@@ 345,7 356,8 @@ fn constant(
					z += fmt::fprint(out, ", ")?;
				};
			};
			z + fmt::fprint(out, ")")?;
			z += fmt::fprint(out, ")")?;
			yield z;
		},
	};
};


@@ 357,9 369,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 +3 -3
@@ 66,11 66,11 @@ fn struct_union_type(
	let membs = match (t.repr) {
		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;
		},
	};



@@ 89,7 89,7 @@ fn struct_union_type(
			se: ast::struct_embedded => _type(out, indent, *se)?,
			sa: ast::struct_alias => ident(out, sa)?,
			sf: ast::struct_field => {
				fmt::fprintf(out, "{}: ", sf.name)?
				yield fmt::fprintf(out, "{}: ", sf.name)?
					+ _type(out, indent, *sf._type)?;
			},
		};

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 +1 -1
@@ 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;
		},
	};
};

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/dial/resolve.ha => net/dial/resolve.ha +1 -1
@@ 23,7 23,7 @@ export fn resolve(
		i: size => {
			const sub = strings::sub(addr, i + 1, strings::end);
			addr = strings::sub(addr, 0, i);
			match (strconv::stou16(sub)) {
			yield match (strconv::stou16(sub)) {
				u: u16 => u,
				* => return invalid_address,
			};

M net/dns/error.ha => net/dns/error.ha +1 -1
@@ 30,7 30,7 @@ export type error = !(format | server_failure | name_error

export fn strerror(err: error) const str = {
	static let buf: [64]u8 = [0...];
	match (err) {
	return match (err) {
		format => "The DNS message was poorly formatted",
		server_failure => "The name server was unable to process this query due to a problem with the name server",
		name_error => "The domain name referenced in the query does not exist",

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 +6 -5
@@ 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);
		},
	};
};


@@ 113,7 113,6 @@ fn parsev6(st: str) (addr6 | invalid) = {
			i += 4;
			break;
		};
		return invalid;
	};
	if (!(strings::next_token(&tok) is void)) {
		return invalid;


@@ 137,11 136,13 @@ fn parsev6(st: str) (addr6 | invalid) = {

// Parses an IP address.
export fn parse(s: str) (addr | invalid) = {
	match(parsev4(s)) {
	match (parsev4(s)) {
		v4: addr4 => return v4,
		invalid => void,
	};
	match(parsev6(s)) {
	match (parsev6(s)) {
		v6: addr6 => return v6,
		invalid => void,
	};
	return invalid;
};

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/syscalls.ha => rt/+linux/syscalls.ha +5 -5
@@ 19,7 19,7 @@ fn copy_kpath(path: path, buf: []u8) (*const char | errno) = {
				length: size,
				capacity: size,
			};
			ptr.buf[..ptr.length];
			yield ptr.buf[..ptr.length];
		},
		b: []u8 => b,
	};


@@ 240,7 240,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 (wrap_return(r)) {
		err: errno => {
			// XXX: Type promotion would simplify this
			return if (r: int == -EPERM


@@ 248,7 248,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,


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


@@ 309,7 309,7 @@ export fn faccessat(
		},
		n: u64 => {
			assert(n == 0);
			true;
			yield true;
		},
	};
};

M rt/malloc.ha => rt/malloc.ha +1 -1
@@ 74,7 74,7 @@ fn malloc_small(n: size) nullable *void = {
	return if (p != null) {
		let q = *(p: **void);
		bins[b] = q;
		p;
		yield p;
	} else null;
};


M strconv/ftos.ha => strconv/ftos.ha +3 -3
@@ 96,7 96,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);


@@ 104,7 104,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);


@@ 593,7 593,7 @@ fn encode_base10(buf: []u8, mantissa: u64, exponent: i32, digits: i32) 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 +1 -1
@@ 437,7 437,7 @@ fn floatbits(d: *decimal, f: *math::floatinfo) (u64 | overflow) = {
	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;
			yield if (d.digits[0] < 2) 2 else 1;
		} else if (-d.decimal_point >= len(powtab): i32)
			maxshift: int
		else powtab[-d.decimal_point];

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
@@ 11,12 11,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,