Skip to content

Commit fe15e41

Browse files
authored
Merge pull request #758 from wado-lang/claude/implement-match-constant-pattern-QgUqG
Fix expected_type propagation for tuple-to-Array coercion in blocks
2 parents 8b221e4 + 79f2035 commit fe15e41

File tree

6 files changed

+1747
-25
lines changed

6 files changed

+1747
-25
lines changed

wado-compiler/src/resolver/expr.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ impl<H: CompilerHost> Resolver<'_, H> {
4747
Expr::FieldAccess(field_access) => self.resolve_field_access(field_access, ctx),
4848
Expr::Index(index) => self.resolve_index(index, ctx),
4949
Expr::Block(block) => {
50-
let tir_block = self.resolve_block(block, ctx, None);
50+
let tir_block = self.resolve_block(block, ctx, expected_type);
5151
let type_id = tir_block
5252
.stmts
5353
.last()
@@ -76,17 +76,19 @@ impl<H: CompilerHost> Resolver<'_, H> {
7676
ctx.active_labels.push(lb.label.clone());
7777

7878
ctx.enter_scope();
79-
let tir_block = self.resolve_block(&lb.block, ctx, None);
79+
let tir_block = self.resolve_block(&lb.block, ctx, expected_type);
8080
ctx.exit_scope();
8181

8282
ctx.active_labels.pop();
8383
let target = ctx.labeled_block_targets.pop().unwrap();
8484

85-
let result_type = if target.break_types.is_empty() {
86-
TypeTable::UNIT
87-
} else {
85+
let result_type = if !target.break_types.is_empty() {
8886
// TODO: type unification for multiple breaks
8987
target.break_types[0]
88+
} else if let Some(ty) = expected_type {
89+
ty
90+
} else {
91+
TypeTable::UNIT
9092
};
9193

9294
TirExpr::new(

wado-compiler/src/resolver/stmt.rs

Lines changed: 82 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,30 @@ impl<H: CompilerHost> Resolver<'_, H> {
3636
let len = block.stmts.len();
3737
let mut stmts = Vec::new();
3838
for (i, s) in block.stmts.iter().enumerate() {
39-
// Propagate expected type to the last expression for coercion
40-
if expected_type.is_some()
41-
&& i == len - 1
42-
&& let Stmt::Expr(expr_stmt) = s
43-
{
44-
let expr = self.resolve_expr(&expr_stmt.expr, ctx, expected_type);
45-
stmts.push(TirStmt::new(TirStmtKind::Expr(expr), expr_stmt.span));
46-
continue;
39+
// Propagate expected type to the last expression/statement for coercion
40+
if expected_type.is_some() && i == len - 1 {
41+
if let Stmt::Expr(expr_stmt) = s {
42+
let expr = self.resolve_expr(&expr_stmt.expr, ctx, expected_type);
43+
stmts.push(TirStmt::new(TirStmtKind::Expr(expr), expr_stmt.span));
44+
continue;
45+
}
46+
if let Stmt::If(if_stmt) = s {
47+
stmts.extend(self.resolve_if_stmt_with_expected(if_stmt, ctx, expected_type));
48+
continue;
49+
}
50+
if let Stmt::Match(match_expr) = s {
51+
let tir = self.resolve_match_expr(match_expr, ctx, expected_type);
52+
stmts.push(TirStmt::new(TirStmtKind::Expr(tir), match_expr.span));
53+
continue;
54+
}
55+
if let Stmt::LabeledBlock(labeled_block) = s {
56+
stmts.push(self.resolve_labeled_block_with_expected(
57+
labeled_block,
58+
ctx,
59+
expected_type,
60+
));
61+
continue;
62+
}
4763
}
4864
stmts.extend(self.resolve_stmt(s, ctx));
4965
}
@@ -85,10 +101,19 @@ impl<H: CompilerHost> Resolver<'_, H> {
85101
&mut self,
86102
labeled_block: &ast::LabeledBlockStmt,
87103
ctx: &mut FunctionContext,
104+
) -> TirStmt {
105+
self.resolve_labeled_block_with_expected(labeled_block, ctx, None)
106+
}
107+
108+
fn resolve_labeled_block_with_expected(
109+
&mut self,
110+
labeled_block: &ast::LabeledBlockStmt,
111+
ctx: &mut FunctionContext,
112+
expected_type: Option<TypeId>,
88113
) -> TirStmt {
89114
ctx.active_labels.push(labeled_block.label.clone());
90115
// resolve_block already handles scope entry/exit
91-
let block = self.resolve_block(&labeled_block.block, ctx, None);
116+
let block = self.resolve_block(&labeled_block.block, ctx, expected_type);
92117
ctx.active_labels.pop();
93118

94119
TirStmt::new(
@@ -811,6 +836,54 @@ impl<H: CompilerHost> Resolver<'_, H> {
811836
}
812837
}
813838

839+
/// Like `resolve_if_stmt` but propagates `expected_type` to blocks for coercion.
840+
/// Used when an if statement is the last statement in a block that needs type coercion
841+
/// (e.g., match arm returning Array<T> from an if-else with tuple literals).
842+
fn resolve_if_stmt_with_expected(
843+
&mut self,
844+
if_stmt: &IfStmt,
845+
ctx: &mut FunctionContext,
846+
expected_type: Option<TypeId>,
847+
) -> Vec<TirStmt> {
848+
match &if_stmt.condition {
849+
ast::Condition::Expr(expr) => {
850+
let condition = self.resolve_expr(expr, ctx, Some(TypeTable::BOOL));
851+
let then_block = self.resolve_block(&if_stmt.then_block, ctx, expected_type);
852+
let else_block = if_stmt
853+
.else_block
854+
.as_ref()
855+
.map(|b| self.resolve_block(b, ctx, expected_type));
856+
vec![TirStmt::new(
857+
TirStmtKind::If {
858+
condition,
859+
then_block,
860+
else_block,
861+
},
862+
if_stmt.span,
863+
)]
864+
}
865+
ast::Condition::LetChain { elements, .. } => {
866+
let else_block = if_stmt
867+
.else_block
868+
.as_ref()
869+
.map(|b| self.resolve_block(b, ctx, expected_type));
870+
871+
ctx.enter_scope();
872+
let stmts = self.resolve_let_chain_stmts(
873+
elements,
874+
&if_stmt.then_block,
875+
else_block.as_ref(),
876+
ctx,
877+
expected_type,
878+
if_stmt.span,
879+
);
880+
ctx.exit_scope();
881+
882+
stmts
883+
}
884+
}
885+
}
886+
814887
/// Resolve a let-chain condition into a nested sequence of IfLet/If TIR statements.
815888
///
816889
/// Each element of the chain adds one nesting level: a `Let` element becomes an `IfLet`

0 commit comments

Comments
 (0)