adding minimal enums

This commit is contained in:
Jakub Doka 2024-11-17 16:25:39 +01:00
parent 397b2a4b1b
commit 12b9d43754
No known key found for this signature in database
GPG key ID: C6E9A89936B8C143
7 changed files with 522 additions and 155 deletions

View file

@ -51,6 +51,7 @@ main := fn(): uint {
}
```
#### floating_point_arithmetic
```hb
main := fn(): f32 {
@ -113,6 +114,21 @@ main := fn(): uint {
}
```
#### hex_octal_binary_literals
```hb
main := fn(): uint {
hex := 0xFF
decimal := 255
octal := 0o377
binary := 0b11111111
if hex == decimal & octal == decimal & binary == decimal {
return 0
}
return 1
}
```
#### loops
```hb
main := fn(): uint {
@ -158,6 +174,60 @@ drop := fn(a: uint): void {
}
```
#### structs
```hb
Ty := struct {
// comment
a: uint,
}
Ty2 := struct {
ty: Ty,
c: uint,
}
useless := struct {}
main := fn(): uint {
// `packed` structs have no padding (all fields are alighred to 1)
if @sizeof(packed struct {a: u8, b: u16}) != 3 {
return 9001
}
finst := Ty2.{ty: .{a: 4}, c: 3}
inst := odher_pass(finst)
if inst.c == 3 {
return pass(&inst.ty)
}
return 0
}
pass := fn(t: ^Ty): uint {
return t.a
}
odher_pass := fn(t: Ty2): Ty2 {
return t
}
```
#### enums
```hb
Enum := enum {A, B, C}
some_enum := fn(): Enum return .A
main := fn(): uint {
e := some_enum()
match e {
.A => return 0,
_ => return 100,
}
}
```
#### nullable_types
```hb
main := fn(): uint {
@ -215,74 +285,6 @@ new_bar := fn(a: ?^uint): ?Bar return .(a, 1)
decide := fn(): bool return !false
```
#### structs
```hb
Ty := struct {
// comment
a: uint,
}
Ty2 := struct {
ty: Ty,
c: uint,
}
useless := struct {}
main := fn(): uint {
// `packed` structs have no padding (all fields are alighred to 1)
if @sizeof(packed struct {a: u8, b: u16}) != 3 {
return 9001
}
finst := Ty2.{ty: .{a: 4}, c: 3}
inst := odher_pass(finst)
if inst.c == 3 {
return pass(&inst.ty)
}
return 0
}
pass := fn(t: ^Ty): uint {
return t.a
}
odher_pass := fn(t: Ty2): Ty2 {
return t
}
```
#### hex_octal_binary_literals
```hb
main := fn(): uint {
hex := 0xFF
decimal := 255
octal := 0o377
binary := 0b11111111
if hex == decimal & octal == decimal & binary == decimal {
return 0
}
return 1
}
```
#### wrong_dead_code_elimination
```hb
Color := struct {b: u8}
main := fn(): void {
color := Color.(0)
n := @as(u8, 1)
loop {
if color.b == 255 | color.b == 0 {
n = -n
}
color.b += n
}
}
```
#### inline_return_stack
```hb
$fun := fn(): [uint; 3] {
@ -649,6 +651,21 @@ main := fn(): uint {
### Purely Testing Examples
#### wrong_dead_code_elimination
```hb
Color := struct {b: u8}
main := fn(): void {
color := Color.(0)
n := @as(u8, 1)
loop {
if color.b == 255 | color.b == 0 {
n = -n
}
color.b += n
}
}
```
#### different_function_destinations
```hb
Stru := struct {a: uint, b: uint}

View file

@ -1,7 +1,7 @@
use {
crate::{
lexer::{self, Lexer, TokenKind},
parser::{self, CommentOr, CtorField, Expr, Poser, Radix, StructField},
parser::{self, CommentOr, CtorField, EnumField, Expr, Poser, Radix, StructField},
},
core::fmt::{self},
};
@ -28,50 +28,44 @@ pub fn display_radix(radix: Radix, mut value: u64, buf: &mut [u8; 64]) -> &str {
#[repr(u8)]
enum TokenGroup {
Blank = 0,
Comment = 1,
Keyword = 2,
Identifier = 3,
Directive = 4,
Number = 5,
String = 6,
Op = 7,
Assign = 8,
Paren = 9,
Bracket = 10,
Colon = 11,
Comma = 12,
Dot = 13,
Ctor = 14,
Blank,
Comment,
Keyword,
Identifier,
Directive,
Number,
String,
Op,
Assign,
Paren,
Bracket,
Colon,
Comma,
Dot,
Ctor,
}
fn token_group(kind: TokenKind) -> TokenGroup {
use crate::lexer::TokenKind::*;
use {crate::lexer::TokenKind::*, TokenGroup as TG};
match kind {
// unused/unimplemented
| BSlash | Pound | Eof | Ct => TokenGroup::Blank,
| Comment => TokenGroup::Comment,
| Directive => TokenGroup::Directive,
| Colon => TokenGroup::Colon,
| Semi | Comma => TokenGroup::Comma,
| Dot => TokenGroup::Dot,
| Ctor | Tupl => TokenGroup::Ctor,
| LParen | RParen => TokenGroup::Paren,
| LBrace | RBrace | LBrack | RBrack => TokenGroup::Bracket,
| Number | Float => TokenGroup::Number,
| Under | CtIdent | Ident => TokenGroup::Identifier,
| Tick | Tilde | Que
| Not | Mod | Band | Bor | Xor
| Mul | Add | Sub | Div
| Shl | Shr | Or | And
| Lt | Gt | Eq | Le | Ge | Ne => TokenGroup::Op,
| Decl | Assign
| BorAss | XorAss | BandAss
| AddAss | SubAss | MulAss | DivAss | ModAss
| ShrAss | ShlAss => TokenGroup::Assign,
| DQuote | Quote => TokenGroup::String,
| Return | If | Else | Loop | Break | Continue | Fn | Idk | Die
| Struct | Packed | True | False | Null => TokenGroup::Keyword,
BSlash | Pound | Eof | Ct => TG::Blank,
Comment => TG::Comment,
Directive => TG::Directive,
Colon => TG::Colon,
Semi | Comma => TG::Comma,
Dot => TG::Dot,
Ctor | Tupl | TArrow => TG::Ctor,
LParen | RParen => TG::Paren,
LBrace | RBrace | LBrack | RBrack => TG::Bracket,
Number | Float => TG::Number,
Under | CtIdent | Ident => TG::Identifier,
Tick | Tilde | Que | Not | Mod | Band | Bor | Xor | Mul | Add | Sub | Div | Shl | Shr
| Or | And | Lt | Gt | Eq | Le | Ge | Ne => TG::Op,
Decl | Assign | BorAss | XorAss | BandAss | AddAss | SubAss | MulAss | DivAss | ModAss
| ShrAss | ShlAss => TG::Assign,
DQuote | Quote => TG::String,
Return | If | Else | Loop | Break | Continue | Fn | Idk | Die | Struct | Packed | True
| False | Null | Match | Enum => TG::Keyword,
}
}
@ -295,7 +289,7 @@ impl<'a> Formatter<'a> {
f.write_str("packed ")?;
}
write!(f, "struct {{")?;
f.write_str("struct {")?;
self.fmt_list_low(f, trailing_comma, "}", ",", fields, |s, field, f| {
match field {
CommentOr::Or(StructField { name, ty, .. }) => {
@ -311,6 +305,21 @@ impl<'a> Formatter<'a> {
Ok(field.or().is_some())
})
}
Expr::Enum { variants, trailing_comma, .. } => {
f.write_str("enum {")?;
self.fmt_list_low(f, trailing_comma, "}", ",", variants, |_, var, f| {
match var {
CommentOr::Or(EnumField { name, .. }) => {
f.write_str(name)?;
}
CommentOr::Comment { literal, .. } => {
f.write_str(literal)?;
f.write_str("\n")?;
}
}
Ok(var.or().is_some())
})
}
Expr::Ctor { ty, fields, trailing_comma, .. } => {
if let Some(ty) = ty {
self.fmt_paren(ty, f, unary)?;
@ -385,6 +394,16 @@ impl<'a> Formatter<'a> {
}
Ok(())
}
Expr::Match { value, branches, .. } => {
f.write_str("match ")?;
self.fmt(value, f)?;
f.write_str(" {")?;
self.fmt_list(f, true, "}", ",", branches, |s, br, f| {
s.fmt(&br.pat, f)?;
f.write_str(" => ")?;
s.fmt(&br.body, f)
})
}
Expr::Loop { body, .. } => {
f.write_str("loop ")?;
self.fmt(body, f)

View file

@ -121,23 +121,9 @@ pub enum TokenKind {
Ct,
Return,
If,
Else,
Loop,
Break,
Continue,
Fn,
Struct,
Packed,
True,
False,
Null,
Idk,
Die,
Ctor,
Tupl,
TArrow,
Or,
And,
@ -147,8 +133,26 @@ pub enum TokenKind {
BSlash = b'\\',
RBrack = b']',
Xor = b'^',
Tick = b'`',
Under = b'_',
Tick = b'`',
Return,
If,
Match,
Else,
Loop,
Break,
Continue,
Fn,
Struct,
Packed,
Enum,
True,
False,
Null,
Idk,
Die,
// Unused = a-z
LBrace = b'{',
Bor = b'|',
@ -300,6 +304,7 @@ gen_token_kind! {
#[keywords]
Return = b"return",
If = b"if",
Match = b"match",
Else = b"else",
Loop = b"loop",
Break = b"break",
@ -307,6 +312,7 @@ gen_token_kind! {
Fn = b"fn",
Struct = b"struct",
Packed = b"packed",
Enum = b"enum",
True = b"true",
False = b"false",
Null = b"null",
@ -316,6 +322,7 @@ gen_token_kind! {
#[punkt]
Ctor = ".{",
Tupl = ".(",
TArrow = "=>",
// #define OP: each `#[prec]` delimeters a level of precedence from lowest to highest
#[ops]
#[prec]
@ -514,6 +521,7 @@ impl<'a> Lexer<'a> {
}
b'.' if self.advance_if(b'{') => T::Ctor,
b'.' if self.advance_if(b'(') => T::Tupl,
b'=' if self.advance_if(b'>') => T::TArrow,
b'&' if self.advance_if(b'&') => T::And,
b'|' if self.advance_if(b'|') => T::Or,
b'$' if self.advance_if(b':') => T::Ct,

View file

@ -352,6 +352,11 @@ pub mod ty {
debug_assert_ne!(st.pos, Pos::MAX);
crate::SymKey::Struct(st.file, st.pos, st.captures)
}
Kind::Enum(e) => {
let en = &ctx.enums[e];
debug_assert_ne!(en.pos, Pos::MAX);
crate::SymKey::Enum(en.file, en.pos)
}
Kind::Ptr(p) => crate::SymKey::Pointer(&ctx.ptrs[p]),
Kind::Opt(p) => crate::SymKey::Optional(&ctx.opts[p]),
Kind::Func(f) => {
@ -485,7 +490,7 @@ pub mod ty {
{
Loc::Reg
}
Kind::Ptr(_) | Kind::Builtin(_) => Loc::Reg,
Kind::Ptr(_) | Kind::Enum(_) | Kind::Builtin(_) => Loc::Reg,
Kind::Struct(_) if tys.size_of(*self) == 0 => Loc::Reg,
Kind::Struct(_) | Kind::Slice(_) | Kind::Opt(_) => Loc::Stack,
Kind::Func(_) | Kind::Global(_) | Kind::Module(_) | Kind::Const(_) => {
@ -640,6 +645,7 @@ pub mod ty {
pub enum Kind {
Builtin,
Struct,
Enum,
Ptr,
Slice,
Opt,
@ -720,7 +726,7 @@ pub mod ty {
f.write_str("[")?;
idx.fmt(f)?;
f.write_str("]{")?;
for (i, &super::Field { name, ty }) in
for (i, &super::StructField { name, ty }) in
self.tys.struct_fields(idx).iter().enumerate()
{
if i != 0 {
@ -736,6 +742,12 @@ pub mod ty {
f.write_str(file.ident_str(record.name))
}
}
TK::Enum(idx) => {
let enm = &self.tys.ins.enums[idx];
debug_assert!(!enm.name.is_null());
let file = &self.files[enm.file.index()];
f.write_str(file.ident_str(enm.name))
}
TK::Func(idx) => {
f.write_str("fn")?;
idx.fmt(f)
@ -773,6 +785,7 @@ pub enum SymKey<'a> {
Pointer(&'a Ptr),
Optional(&'a Opt),
Struct(Module, Pos, ty::Tuple),
Enum(Module, Pos),
FuncInst(ty::Func, ty::Tuple),
Decl(Module, Ident),
Array(&'a Array),
@ -852,7 +865,19 @@ impl Reloc {
}
}
struct Field {
struct EnumField {
name: Ident,
}
#[derive(Default)]
struct Enum {
name: Ident,
pos: Pos,
file: Module,
field_start: u32,
}
struct StructField {
name: Ident,
ty: ty::Id,
}
@ -955,18 +980,21 @@ impl IdentInterner {
#[derive(Default)]
struct TypesTmp {
fields: Vec<Field>,
struct_fields: Vec<StructField>,
enum_fields: Vec<EnumField>,
args: Vec<ty::Id>,
}
#[derive(Default)]
pub struct TypeIns {
args: Vec<ty::Id>,
fields: Vec<Field>,
struct_fields: Vec<StructField>,
enum_fields: Vec<EnumField>,
funcs: EntVec<ty::Func, Func>,
globals: EntVec<ty::Global, Global>,
consts: EntVec<ty::Const, Const>,
structs: EntVec<ty::Struct, Struct>,
enums: EntVec<ty::Enum, Enum>,
ptrs: EntVec<ty::Ptr, Ptr>,
opts: EntVec<ty::Opt, Opt>,
slices: EntVec<ty::Slice, Array>,
@ -1149,11 +1177,11 @@ trait TypeParser {
return ty;
}
let prev_tmp = self.tys().tmp.fields.len();
let prev_tmp = self.tys().tmp.struct_fields.len();
for field in fields.iter().filter_map(CommentOr::or) {
let ty = self.parse_ty(file, &field.ty, None, files);
let field = Field { name: self.tys().names.intern(field.name), ty };
self.tys().tmp.fields.push(field);
let field = StructField { name: self.tys().names.intern(field.name), ty };
self.tys().tmp.struct_fields.push(field);
}
let tys = self.tys();
@ -1164,13 +1192,43 @@ trait TypeParser {
file,
pos,
name: name.unwrap_or_default(),
field_start: tys.ins.fields.len() as _,
field_start: tys.ins.struct_fields.len() as _,
explicit_alignment: packed.then_some(1),
..Default::default()
})
.into();
tys.ins.fields.extend(tys.tmp.fields.drain(prev_tmp..));
tys.ins.struct_fields.extend(tys.tmp.struct_fields.drain(prev_tmp..));
tys.syms.insert(sym, ty, &tys.ins);
ty
}
Expr::Enum { pos, variants, .. } => {
let sym = SymKey::Enum(file, pos);
let tys = self.tys();
if let Some(&ty) = tys.syms.get(sym, &tys.ins) {
return ty;
}
let prev_tmp = self.tys().tmp.enum_fields.len();
for field in variants.iter().filter_map(CommentOr::or) {
let field = EnumField { name: self.tys().names.intern(field.name) };
self.tys().tmp.enum_fields.push(field);
}
let tys = self.tys();
let ty = tys
.ins
.enums
.push(Enum {
file,
pos,
name: name.unwrap_or_default(),
field_start: tys.ins.enum_fields.len() as _,
})
.into();
tys.ins.enum_fields.extend(tys.tmp.enum_fields.drain(prev_tmp..));
tys.syms.insert(sym, ty, &tys.ins);
ty
@ -1211,13 +1269,6 @@ trait TypeParser {
}
impl Types {
fn struct_field_range(&self, strct: ty::Struct) -> Range<usize> {
let start = self.ins.structs[strct].field_start as usize;
let end =
self.ins.structs.next(strct).map_or(self.ins.fields.len(), |s| s.field_start as usize);
start..end
}
fn pack_args(&mut self, arg_base: usize) -> Option<ty::Tuple> {
let base = self.ins.args.len();
self.ins.args.extend(self.tmp.args.drain(arg_base..));
@ -1233,8 +1284,29 @@ impl Types {
ty::Tuple::new(sp, len)
}
fn struct_fields(&self, strct: ty::Struct) -> &[Field] {
&self.ins.fields[self.struct_field_range(strct)]
fn struct_fields(&self, strct: ty::Struct) -> &[StructField] {
&self.ins.struct_fields[self.struct_field_range(strct)]
}
fn struct_field_range(&self, strct: ty::Struct) -> Range<usize> {
let start = self.ins.structs[strct].field_start as usize;
let end = self
.ins
.structs
.next(strct)
.map_or(self.ins.struct_fields.len(), |s| s.field_start as usize);
start..end
}
fn enum_fields(&self, enm: ty::Enum) -> &[EnumField] {
&self.ins.enum_fields[self.enum_field_range(enm)]
}
fn enum_field_range(&self, enm: ty::Enum) -> Range<usize> {
let start = self.ins.enums[enm].field_start as usize;
let end =
self.ins.enums.next(enm).map_or(self.ins.enum_fields.len(), |s| s.field_start as usize);
start..end
}
fn parama(&self, ret: impl Into<ty::Id>) -> (Option<PLoc>, ParamAlloc) {
@ -1285,6 +1357,7 @@ impl Types {
self.ins.structs[stru].size.set(oiter.offset);
oiter.offset
}
ty::Kind::Enum(enm) => (self.enum_field_range(enm).len().ilog2() + 7) / 8,
ty::Kind::Opt(opt) => {
let base = self.ins.opts[opt].base;
if self.nieche_of(base).is_some() {
@ -1308,7 +1381,7 @@ impl Types {
|| {
self.struct_fields(stru)
.iter()
.map(|&Field { ty, .. }| self.align_of(ty))
.map(|&StructField { ty, .. }| self.align_of(ty))
.max()
.unwrap_or(1)
},
@ -1380,11 +1453,11 @@ impl Types {
self.ins.args.clear();
self.ins.globals.clear();
self.ins.structs.clear();
self.ins.fields.clear();
self.ins.struct_fields.clear();
self.ins.ptrs.clear();
self.ins.slices.clear();
debug_assert_eq!(self.tmp.fields.len(), 0);
debug_assert_eq!(self.tmp.struct_fields.len(), 0);
debug_assert_eq!(self.tmp.args.len(), 0);
debug_assert_eq!(self.tasks.len(), 0);
@ -1416,9 +1489,9 @@ impl OffsetIter {
.map(|(f, off)| (off, f.ty))
}
fn next<'a>(&mut self, tys: &'a Types) -> Option<(&'a Field, Offset)> {
fn next<'a>(&mut self, tys: &'a Types) -> Option<(&'a StructField, Offset)> {
let stru = &tys.ins.structs[self.strct];
let field = &tys.ins.fields[self.fields.next()?];
let field = &tys.ins.struct_fields[self.fields.next()?];
let align = stru.explicit_alignment.map_or_else(|| tys.align_of(field.ty), |a| a as u32);
self.offset = (self.offset + align - 1) & !(align - 1);
@ -1433,7 +1506,7 @@ impl OffsetIter {
Some((field.ty, off))
}
fn into_iter(mut self, tys: &Types) -> impl Iterator<Item = (&Field, Offset)> {
fn into_iter(mut self, tys: &Types) -> impl Iterator<Item = (&StructField, Offset)> {
core::iter::from_fn(move || self.next(tys))
}
}

View file

@ -397,6 +397,22 @@ impl<'a, 'b> Parser<'a, 'b> {
},
trailing_comma: core::mem::take(&mut self.trailing_sep),
},
T::Enum => E::Enum {
pos,
variants: {
self.expect_advance(T::LBrace)?;
self.collect_list(T::Comma, T::RBrace, |s| {
let tok = s.token;
Some(if s.advance_if(T::Comment) {
CommentOr::Comment { literal: s.tok_str(tok), pos: tok.start }
} else {
let name = s.expect_advance(T::Ident)?;
CommentOr::Or(EnumField { pos: name.start, name: s.tok_str(name) })
})
})
},
trailing_comma: core::mem::take(&mut self.trailing_sep),
},
T::Ident | T::CtIdent => {
let (id, is_first) = self.resolve_ident(token);
E::Ident {
@ -413,6 +429,20 @@ impl<'a, 'b> Parser<'a, 'b> {
then: self.ptr_expr()?,
else_: self.advance_if(T::Else).then(|| self.ptr_expr()).trans()?,
},
T::Match => E::Match {
pos,
value: self.ptr_expr()?,
branches: {
self.expect_advance(T::LBrace)?;
self.collect_list(T::Comma, T::RBrace, |s| {
Some(MatchBranch {
pat: s.expr()?,
pos: s.expect_advance(T::TArrow)?.start,
body: s.expr()?,
})
})
},
},
T::Loop => E::Loop { pos, body: self.ptr_expr()? },
T::Break => E::Break { pos },
T::Continue => E::Continue { pos },
@ -459,14 +489,18 @@ impl<'a, 'b> Parser<'a, 'b> {
pos
},
},
T::Band | T::Mul | T::Xor | T::Sub | T::Que | T::Not => E::UnOp {
T::Band | T::Mul | T::Xor | T::Sub | T::Que | T::Not | T::Dot => E::UnOp {
pos,
op: token.kind,
val: {
let prev_ident_stack = self.ctx.idents.len();
let expr = self.ptr_unit_expr()?;
if token.kind == T::Band {
self.flag_idents(*expr, idfl::REFERENCED);
}
if token.kind == T::Dot {
self.ctx.idents.truncate(prev_ident_stack);
}
expr
},
},
@ -858,6 +892,11 @@ generate_expr! {
then: &'a Self,
else_: Option<&'a Self>,
},
Match {
pos: Pos,
value: &'a Self,
branches: &'a [MatchBranch<'a>],
},
/// `'loop' Expr`
Loop {
pos: Pos,
@ -877,6 +916,12 @@ generate_expr! {
trailing_comma: bool,
packed: bool,
},
/// `'enum' LIST('{', ',', '}', Ident)`
Enum {
pos: Pos,
variants: &'a [CommentOr<'a, EnumField<'a>>],
trailing_comma: bool,
},
/// `[Expr] LIST('.{', ',', '}', Ident [':' Expr])`
Ctor {
pos: Pos,
@ -992,6 +1037,31 @@ impl Expr<'_> {
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct MatchBranch<'a> {
pub pat: Expr<'a>,
pub pos: Pos,
pub body: Expr<'a>,
}
impl Poser for MatchBranch<'_> {
fn posi(&self) -> Pos {
self.pat.pos()
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct EnumField<'a> {
pub pos: Pos,
pub name: &'a str,
}
impl Poser for EnumField<'_> {
fn posi(&self) -> Pos {
self.pos
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct StructField<'a> {
pub pos: Pos,

View file

@ -10,7 +10,7 @@ use {
parser::{
self,
idfl::{self},
CtorField, Expr, Pos,
CtorField, Expr, MatchBranch, Pos,
},
ty::{self, Arg, ArrayLen, Loc, Module, Tuple},
utils::{BitSet, Ent, Vc},
@ -2944,6 +2944,41 @@ impl<'a> Codegen<'a> {
vl.ty = base;
Some(vl)
}
Expr::UnOp { pos, op: TokenKind::Dot, val: &Expr::Ident { id, .. } } => {
inference!(ty, ctx, self, pos, "enum type", "<EnumTy>.Variant");
let ty::Kind::Enum(e) = ty.expand() else {
self.report(
pos,
fa!("expected inferred type to be enum but got '{}'", self.ty_display(ty)),
);
return Value::NEVER;
};
let intrnd = self.tys.names.project(self.file().ident_str(id));
let Some(index) =
self.tys.enum_fields(e).iter().position(|f| Some(f.name) == intrnd)
else {
let field_list = self
.tys
.enum_fields(e)
.iter()
.map(|f| self.tys.names.ident_str(f.name))
.intersperse("', '")
.collect::<String>();
self.report(
pos,
fa!(
"the '{}' does not have this variant, \
but it does have '{field_list}'",
self.ty_display(ty)
),
);
return Value::NEVER;
};
Some(self.ci.nodes.new_const_lit(ty, index as i64))
}
Expr::UnOp { pos, op: op @ TokenKind::Sub, val } => {
let val =
self.expr_ctx(val, Ctx::default().with_ty(ctx.ty.unwrap_or(ty::Id::INT)))?;
@ -3569,6 +3604,17 @@ impl<'a> Codegen<'a> {
for stmt in stmts {
ret = ret.and(self.expr(stmt));
if let Some(mut id) = ret {
if id.ty != ty::Id::VOID {
self.report(
stmt.pos(),
fa!(
"statements need to evaluate to 'void', \
but this statements evaluates '{}', \
use '_ = <stmt>' to discard the value if its intentional",
self.ty_display(id.ty)
),
);
}
self.assert_ty(stmt.pos(), &mut id, ty::Id::VOID, "statement");
} else {
break;
@ -3885,6 +3931,119 @@ impl<'a> Codegen<'a> {
Some(Value::VOID)
}
Expr::Match { pos, value, branches } => {
let value = self.expr(value)?;
let ty::Kind::Enum(e) = value.ty.expand() else {
self.report(
pos,
fa!(
"match operates on enums (for now), '{}' is not an enum",
self.ty_display(value.ty)
),
);
return Value::NEVER;
};
let mut covered_values = vec![Pos::MAX; self.tys.enum_field_range(e).len()];
let mut scopes = vec![];
let mut else_branch = None;
for &MatchBranch { pat, pos: bp, body } in branches {
if let Expr::Wildcard { .. } = pat {
else_branch = Some(body);
continue;
}
let pat_val = self.eval_const(self.ci.file, &pat, value.ty);
if covered_values[pat_val as usize] != Pos::MAX {
self.report(bp, "duplicate branch");
self.report(covered_values[pat_val as usize], "first branch declared here");
continue;
}
covered_values[pat_val as usize] = bp;
let pat_val = self.ci.nodes.new_const(value.ty, pat_val as i64);
let cnd = self.ci.nodes.new_node(
ty::Id::BOOL,
Kind::BinOp { op: TokenKind::Eq },
[VOID, value.id, pat_val],
self.tys,
);
let if_node = self.ci.nodes.new_node(
ty::Id::VOID,
Kind::If,
[self.ci.ctrl.get(), cnd],
self.tys,
);
let cached_scope = self.ci.scope.dup(&mut self.ci.nodes);
self.ci.ctrl.set(
self.ci.nodes.new_node(ty::Id::VOID, Kind::Then, [if_node], self.tys),
&mut self.ci.nodes,
);
let ctrl = self.expr(&body).map_or(Nid::MAX, |_| self.ci.ctrl.get());
scopes.push((ctrl, mem::replace(&mut self.ci.scope, cached_scope)));
self.ci.ctrl.set(
self.ci.nodes.new_node(ty::Id::VOID, Kind::Else, [if_node], self.tys),
&mut self.ci.nodes,
);
}
let mut rcntrl = if let Some(ebr) = else_branch {
self.expr(&ebr).map_or(Nid::MAX, |_| self.ci.ctrl.get())
} else {
let missing_branches = covered_values
.into_iter()
.zip(self.tys.enum_fields(e))
.filter(|&(f, _)| f == Pos::MAX)
.map(|(_, f)| self.tys.names.ident_str(f.name))
.intersperse("', '")
.collect::<String>();
if !missing_branches.is_empty() {
self.report(
pos,
fa!("not all cases covered, missing '{missing_branches}'"),
);
}
self.ci.ctrl.get()
};
for (lcntrl, mut then_scope) in scopes.into_iter().rev() {
if lcntrl == Nid::MAX && rcntrl == Nid::MAX {
then_scope.clear(&mut self.ci.nodes);
return None;
} else if lcntrl == Nid::MAX {
then_scope.clear(&mut self.ci.nodes);
return Some(Value::VOID);
} else if rcntrl == Nid::MAX {
self.ci.scope.clear(&mut self.ci.nodes);
self.ci.scope = then_scope;
self.ci.ctrl.set(lcntrl, &mut self.ci.nodes);
return Some(Value::VOID);
}
rcntrl = self.ci.nodes.new_node(
ty::Id::VOID,
Kind::Region,
[lcntrl, rcntrl],
self.tys,
);
self.ci.ctrl.set(rcntrl, &mut self.ci.nodes);
self.ci.nodes.merge_scopes(
&mut self.ci.loops,
&self.ci.ctrl,
&mut self.ci.scope,
&mut then_scope,
self.tys,
);
then_scope.clear(&mut self.ci.nodes);
}
Some(Value::VOID)
}
ref e => {
self.report_unhandled_ast(e, "bruh");
Value::NEVER
@ -4953,11 +5112,12 @@ mod tests {
comments;
if_statements;
variables;
hex_octal_binary_literals;
loops;
pointers;
nullable_types;
structs;
hex_octal_binary_literals;
enums;
nullable_types;
struct_operators;
global_variables;
constants;

View file

@ -0,0 +1,20 @@
main:
ADDI64 r254, r254, -16d
ST r31, r254, 0a, 16h
JAL r31, r0, :some_enum
CP r32, r1
ANDI r32, r32, 255d
JNE r32, r0, :0
CP r1, r0
JMP :1
0: LI64 r32, 100d
CP r1, r32
1: LD r31, r254, 0a, 16h
ADDI64 r254, r254, 16d
JALA r0, r31, 0a
some_enum:
CP r1, r0
JALA r0, r31, 0a
code size: 128
ret: 0
status: Ok(())