Skip to content

Commit 6e1eca0

Browse files
authored
Merge pull request #611 from shiika-lang/match-expr
Match expr
2 parents 2199fff + eac936a commit 6e1eca0

11 files changed

Lines changed: 171 additions & 15 deletions

File tree

lib/shiika_ffi/src/core_class/bool.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ extern "C" {
44

55
#[repr(C)]
66
#[derive(Debug)]
7-
pub struct SkBool(*const ShiikaBool);
7+
pub struct SkBool(pub *const ShiikaBool);
88

99
unsafe impl Send for SkBool {}
1010

1111
#[repr(C)]
1212
#[derive(Debug)]
13-
struct ShiikaBool {
13+
pub struct ShiikaBool {
1414
vtable: *const u8,
1515
class_obj: *const u8,
1616
value: bool,

lib/shiika_ffi/src/core_class/object.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::core_class::SkClass;
22

33
#[repr(C)]
44
#[derive(Debug)]
5-
pub struct SkObject(*const ShiikaObject);
5+
pub struct SkObject(pub *const ShiikaObject);
66

77
unsafe impl Send for SkObject {}
88

@@ -19,7 +19,7 @@ impl SkObject {
1919

2020
#[repr(C)]
2121
#[derive(Debug)]
22-
struct ShiikaObject {
22+
pub struct ShiikaObject {
2323
vtable: *const u8,
2424
class_obj: SkClass,
2525
}

lib/skc_async_experiment/src/codegen/compile.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,17 @@ impl<'run, 'ictx: 'run> CodeGen<'run, 'ictx> {
6060
};
6161

6262
let _ = self.compile_expr(&mut ctx, &f.body_stmts).unwrap();
63+
// If the function body fell through without a terminator (e.g., a
64+
// continuation chapter for a Never-returning async call), emit
65+
// `unreachable` so LLVM stays valid.
66+
if self
67+
.builder
68+
.get_insert_block()
69+
.and_then(|bb| bb.get_terminator())
70+
.is_none()
71+
{
72+
self.builder.build_unreachable().unwrap();
73+
}
6374
}
6475

6576
fn compile_value_expr(

lib/skc_async_experiment/src/mir_lowering/async_splitter.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,15 +288,23 @@ impl<'a> Compiler<'a> {
288288
args: Vec<mir::TypedExpr>,
289289
async_result_ty: mir::Ty,
290290
) -> Result<mir::TypedExpr> {
291+
// If the call never returns (e.g., Object#panic), the continuation
292+
// chapter is unreachable. Synthesize one with a placeholder param
293+
// type so the verifier (which forbids Never params) is satisfied.
294+
let chapter_param_ty = if async_result_ty == mir::Ty::raw("Never") {
295+
mir::Ty::raw("Object")
296+
} else {
297+
async_result_ty.clone()
298+
};
291299
// Change chapter here
292300
let next_chapter_name = chapter_func_name(&self.orig_func.name, self.chapters.len());
293301
let last_chapter = self.chapters.last_mut();
294302
let terminator = mir::Expr::return_(modify_async_call(fexpr, args, next_chapter_name));
295303
last_chapter.stmts.push(terminator);
296-
last_chapter.async_result_ty = Some(async_result_ty.clone());
304+
last_chapter.async_result_ty = Some(chapter_param_ty.clone());
297305
self.chapters.add(Chapter::new_async_call_receiver(
298306
chapter_func_name(&self.orig_func.name, self.chapters.len()),
299-
async_result_ty.clone(),
307+
chapter_param_ty,
300308
));
301309

302310
Ok(arg_ref_async_result(async_result_ty))

lib/skc_async_experiment/src/mirgen.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod constants;
22
mod lambda;
3+
mod pattern_match;
34
mod prepare_asyncness;
45
mod wtables;
56
use crate::build;
@@ -555,9 +556,10 @@ impl<'a> Compiler<'a> {
555556
self.convert_expr(*then_exprs),
556557
self.convert_expr(*else_exprs),
557558
),
558-
HirExpressionBase::HirMatchExpression { .. } => {
559-
todo!("Handle match expression")
560-
}
559+
HirExpressionBase::HirMatchExpression {
560+
cond_assign_expr,
561+
clauses,
562+
} => self.convert_match_expr(*cond_assign_expr, clauses),
561563
HirExpressionBase::HirWhileExpression {
562564
cond_expr,
563565
body_exprs,
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//! Lowering of `HirMatchExpression` to MIR.
2+
//!
3+
//! The HIR provides a scrutinee assignment plus a list of `MatchClause`s,
4+
//! each made of a sequence of `Test`/`Bind` components and a body. We lower
5+
//! to a chain of nested `If`s; each clause's components fold into a single
6+
//! Bool-typed test chain that interleaves binds (as `LVarDecl`) and tests
7+
//! (as `If(t, rest, false)`), so no expression is duplicated.
8+
use crate::mir;
9+
use skc_hir::pattern_match::{Component, MatchClause};
10+
use skc_hir::{HirExpression, HirExpressionBase};
11+
12+
impl<'a> super::Compiler<'a> {
13+
pub(super) fn convert_match_expr(
14+
&mut self,
15+
cond_assign_expr: HirExpression,
16+
clauses: Vec<MatchClause>,
17+
) -> mir::TypedExpr {
18+
// The cond_assign_expr is always a HirLVarAssign($exprN, scrutinee)
19+
// produced by skc_ast2hir. Convert it to LVarDecl so insert_allocs
20+
// emits the alloca for the temp.
21+
let scrutinee_decl = match cond_assign_expr.node {
22+
HirExpressionBase::HirLVarAssign { name, rhs } => {
23+
let mir_rhs = self.convert_expr(*rhs);
24+
mir::Expr::lvar_decl(name, mir_rhs, false)
25+
}
26+
other => panic!(
27+
"[BUG] expected HirLVarAssign as match cond_assign_expr, got {:?}",
28+
other
29+
),
30+
};
31+
let body = self.build_match_clauses(clauses);
32+
mir::Expr::exprs(vec![scrutinee_decl, body])
33+
}
34+
35+
fn build_match_clauses(&mut self, clauses: Vec<MatchClause>) -> mir::TypedExpr {
36+
// skc_ast2hir always appends a final unconditional clause whose body
37+
// panics. Recurse so it sits at the innermost else.
38+
let mut iter = clauses.into_iter();
39+
let first = iter.next().expect("[BUG] match expression with no clauses");
40+
if iter.len() == 0 {
41+
// Just the panic clause; emit its body unconditionally.
42+
return self.convert_expr(first.body_hir);
43+
}
44+
let rest: Vec<_> = iter.collect();
45+
let cond = self.build_test_chain(first.components);
46+
let then_ = self.convert_expr(first.body_hir);
47+
let else_ = self.build_match_clauses(rest);
48+
mir::Expr::if_(cond, then_, else_)
49+
}
50+
51+
fn build_test_chain(&mut self, components: Vec<Component>) -> mir::TypedExpr {
52+
let mut iter = components.into_iter();
53+
match iter.next() {
54+
None => mir::Expr::pseudo_var(mir::PseudoVar::True),
55+
Some(Component::Test(t)) => {
56+
let mir_t = self.convert_expr(t);
57+
let rest = self.build_test_chain(iter.collect());
58+
mir::Expr::if_(mir_t, rest, mir::Expr::pseudo_var(mir::PseudoVar::False))
59+
}
60+
Some(Component::Bind(name, e)) => {
61+
let mir_e = self.convert_expr(e);
62+
let decl = mir::Expr::lvar_decl(name, mir_e, false);
63+
let rest = self.build_test_chain(iter.collect());
64+
mir::Expr::exprs(vec![decl, rest])
65+
}
66+
}
67+
}
68+
}

packages/core/ext/exports.json5

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
["Class", "_specialize1(tyargs: Array<Class>) -> Class", true],
1616
["Class", "_type_argument(nth: Int) -> Class", true],
1717
["Class", "erasure_class -> Class", true],
18+
["Class", "==(other: Class) -> Bool", true],
1819
["Meta:Class", "_new(name: String, vtable: Object, wtable: Object, meta_cls: Metaclass, erasure_cls: Class) -> Class", false],
1920
["Int", "+(other: Int) -> Int", false],
2021
["Int", "-(other: Int) -> Int", false],
@@ -30,13 +31,15 @@
3031
["Int", "<=(other: Int) -> Bool", false],
3132
["Int", ">(other: Int) -> Bool", false],
3233
["Int", ">=(other: Int) -> Bool", false],
33-
["Int", "==(other: Int) -> Bool", false],
34+
["Int", "==(other: Int) -> Bool", true],
3435
//["Int", "to_f -> Float", false],
3536
["Metaclass", "_new(name: String, vtable: Object, wtable: Object, meta_cls: Metaclass, erasure_cls: Class) -> Metaclass", false],
3637
["Object", "sleep_sec(sec: Int)", true],
3738
["Object", "print(n: Int)", true],
3839
["Object", "puts(s: String)", true],
3940
["Object", "class() -> Class", true],
41+
["Object", "==(other: Object) -> Bool", true],
42+
["Object", "panic(s: String)", true],
4043
["String", "initialize(bytes: Shiika::Internal::Ptr, n_bytes: Shiika::Internal::Int64)", false],
4144
["Meta:Shiika::Internal", "p(value: Object, len: Int)", false],
4245
]

packages/core/ext/src/core_class/class.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
use shiika_ffi::core_class::class::{ShiikaClass, WitnessTable};
2-
use shiika_ffi::core_class::{SkArray, SkClass, SkInt, SkString};
2+
use shiika_ffi::core_class::{SkArray, SkBool, SkClass, SkInt, SkString};
33
use shiika_ffi_macro::{async_shiika_method, shiika_method};
44
use std::collections::HashMap;
55

6+
#[async_shiika_method("Class#==")]
7+
async fn class_eq(receiver: SkClass, other: SkClass) -> SkBool {
8+
(receiver.0 == other.0).into()
9+
}
10+
611
#[shiika_method("Meta:Class#_new")]
712
#[allow(non_snake_case)]
813
pub extern "C" fn meta_class__new(

packages/core/ext/src/core_class/int.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use shiika_ffi::core_class::{SkBool, SkInt};
2-
use shiika_ffi_macro::shiika_method;
2+
use shiika_ffi_macro::{async_shiika_method, shiika_method};
33

44
#[shiika_method("Int#+")]
55
pub extern "C" fn int_add(receiver: SkInt, other: SkInt) -> SkInt {
@@ -73,8 +73,8 @@ pub extern "C" fn int_ge(receiver: SkInt, other: SkInt) -> SkBool {
7373
(receiver.val() >= other.val()).into()
7474
}
7575

76-
#[shiika_method("Int#==")]
77-
pub extern "C" fn int_eq(receiver: SkInt, other: SkInt) -> SkBool {
76+
#[async_shiika_method("Int#==")]
77+
async fn int_eq(receiver: SkInt, other: SkInt) -> SkBool {
7878
(receiver.val() == other.val()).into()
7979
}
8080

packages/core/ext/src/core_class/object.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,21 @@
1-
use shiika_ffi::core_class::{SkClass, SkInt, SkObject, SkString};
1+
use shiika_ffi::core_class::{SkBool, SkClass, SkInt, SkObject, SkString};
22
use shiika_ffi_macro::async_shiika_method;
33
use std::time::Duration;
44
use tokio::io::{stdout, AsyncWriteExt};
55

6+
#[async_shiika_method("Object#==")]
7+
async fn object_eq(receiver: SkObject, other: SkObject) -> SkBool {
8+
(receiver.0 == other.0).into()
9+
}
10+
11+
#[async_shiika_method("Object#panic")]
12+
async fn object_panic(_receiver: SkObject, s: SkString) {
13+
let bytes = s.value();
14+
let msg = std::str::from_utf8(bytes).unwrap_or("<panic: invalid utf-8>");
15+
eprintln!("panic: {}", msg);
16+
std::process::exit(1);
17+
}
18+
619
#[async_shiika_method("Object#class")]
720
async fn object_class(receiver: SkObject) -> SkClass {
821
receiver.class()

0 commit comments

Comments
 (0)