rue_compiler/compile/
block.rs1use log::debug;
2use rue_ast::{AstBlock, AstNode, AstStmt, AstStmtOrExpr};
3use rue_diagnostic::{DiagnosticKind, SrcLoc};
4use rue_hir::{
5 BindingSymbol, Block, Declaration, ExprStatement, Hir, IfStatement, Scope, Statement, Symbol,
6 Value,
7};
8use rue_types::TypeId;
9
10use crate::{Compiler, compile_expr, compile_type, create_binding};
11
12pub fn compile_block(
13 ctx: &mut Compiler,
14 block: &AstBlock,
15 is_expr: bool,
16 expected_type: Option<TypeId>,
17 require_return: bool,
18) -> Value {
19 let scope = ctx.alloc_scope(Scope::new());
20 ctx.push_scope(scope);
21
22 let mut statements = Vec::new();
23 let mut return_value = None;
24 let mut implicit_return = false;
25
26 for stmt in block.items() {
27 let stmt = match stmt {
28 AstStmtOrExpr::Stmt(stmt) => stmt,
29 AstStmtOrExpr::Expr(expr) => {
30 if !is_expr {
31 ctx.diagnostic(expr.syntax(), DiagnosticKind::UnexpectedImplicitReturn);
32 }
33
34 if return_value.is_none() {
35 return_value = Some(compile_expr(ctx, &expr, expected_type));
36 implicit_return = true;
37 }
38
39 continue;
40 }
41 };
42
43 let compiled = match stmt {
44 AstStmt::ExprStmt(stmt) => {
45 let value = if let Some(expr) = stmt.expr() {
46 compile_expr(ctx, &expr, None)
47 } else {
48 debug!("Unresolved expr stmt expr");
49 ctx.builtins().unresolved.clone()
50 };
51
52 let always_nil = ctx.is_castable(value.ty, ctx.builtins().types.nil);
53
54 if !always_nil {
55 ctx.diagnostic(stmt.syntax(), DiagnosticKind::UnusedStatementValue);
56 }
57
58 Statement::Expr(ExprStatement {
59 hir: value.hir,
60 always_nil,
61 })
62 }
63 AstStmt::LetStmt(stmt) => {
64 let symbol = ctx.alloc_symbol(Symbol::Unresolved);
65
66 ctx.push_declaration(Declaration::Symbol(symbol));
67
68 let expected_type = stmt.ty().map(|ty| compile_type(ctx, &ty));
69
70 let value = if let Some(expr) = stmt.value() {
71 compile_expr(ctx, &expr, expected_type)
72 } else {
73 debug!("Unresolved let binding value");
74 ctx.diagnostic(stmt.syntax(), DiagnosticKind::MissingLetValue);
75 ctx.builtins().unresolved.clone()
76 };
77
78 let ty = if let Some(expected_type) = expected_type {
79 if let Some(expr) = stmt.value() {
80 ctx.assign_type(expr.syntax(), value.ty, expected_type);
81 }
82 expected_type
83 } else {
84 value.ty
85 };
86
87 *ctx.symbol_mut(symbol) = Symbol::Binding(BindingSymbol {
88 name: None,
89 value: value.with_type(ty),
90 inline: stmt.inline().is_some(),
91 });
92
93 if let Some(binding) = stmt.binding() {
94 create_binding(ctx, symbol, &binding);
95 }
96
97 ctx.pop_declaration();
98
99 Statement::Let(symbol)
100 }
101 AstStmt::IfStmt(stmt) => {
102 let condition = if let Some(condition) = stmt.condition() {
103 let value = compile_expr(ctx, &condition, None);
104 ctx.check_condition(condition.syntax(), value.ty);
105 value
106 } else {
107 debug!("Unresolved if stmt condition");
108 ctx.builtins().unresolved.clone()
109 };
110
111 let then_block = if let Some(then_block) = stmt.then_block() {
112 if stmt.inline().is_some() {
113 compile_block(ctx, &then_block, false, expected_type, true)
114 } else {
115 let index = ctx.push_mappings(condition.then_map.clone());
116 let value = compile_block(ctx, &then_block, false, expected_type, true);
117 ctx.revert_mappings(index);
118 value
119 }
120 } else {
121 debug!("Unresolved if stmt then block");
122 ctx.builtins().unresolved.clone()
123 };
124
125 if stmt.inline().is_none() {
126 ctx.push_mappings(condition.else_map);
127 }
128
129 Statement::If(IfStatement {
130 condition: condition.hir,
131 then: then_block.hir,
132 inline: stmt.inline().is_some(),
133 })
134 }
135 AstStmt::ReturnStmt(stmt) => {
136 let value = if let Some(expr) = stmt.expr() {
137 compile_expr(ctx, &expr, expected_type)
138 } else {
139 ctx.builtins().nil.clone()
140 };
141
142 if is_expr {
143 ctx.diagnostic(stmt.syntax(), DiagnosticKind::UnexpectedExplicitReturn);
144 }
145
146 if return_value.is_none() {
147 return_value = Some(value.clone());
148 }
149
150 Statement::Return(value.hir)
151 }
152 AstStmt::AssertStmt(stmt) => {
153 let value = if let Some(expr) = stmt.expr() {
154 let value = compile_expr(ctx, &expr, None);
155 ctx.check_condition(expr.syntax(), value.ty);
156 value
157 } else {
158 debug!("Unresolved assert expr");
159 ctx.builtins().unresolved.clone()
160 };
161
162 ctx.push_mappings(value.then_map);
163
164 Statement::Assert(
165 value.hir,
166 SrcLoc::new(ctx.source().clone(), stmt.syntax().text_range().into()),
167 )
168 }
169 AstStmt::RaiseStmt(stmt) => {
170 let value = stmt.expr().map(|expr| compile_expr(ctx, &expr, None));
171
172 if return_value.is_none() {
173 return_value = Some(Value::new(
174 ctx.alloc_hir(Hir::Unresolved),
175 ctx.builtins().types.never,
176 ));
177 }
178
179 Statement::Raise(
180 value.map(|value| value.hir),
181 SrcLoc::new(ctx.source().clone(), stmt.syntax().text_range().into()),
182 )
183 }
184 };
185
186 statements.push(compiled);
187 }
188
189 if return_value.is_none() && require_return {
190 ctx.diagnostic(block.syntax(), DiagnosticKind::MissingReturn);
191 return_value = Some(Value::new(
192 ctx.alloc_hir(Hir::Unresolved),
193 ctx.builtins().types.unresolved,
194 ));
195 }
196
197 let hir = ctx.alloc_hir(Hir::Block(Block {
198 statements,
199 body: return_value
200 .as_ref()
201 .and_then(|value| implicit_return.then_some(value.hir)),
202 }));
203
204 ctx.pop_scope();
205
206 if let Some(return_value) = return_value {
207 return_value.with_hir(hir)
208 } else {
209 Value::new(hir, ctx.builtins().nil.ty)
210 }
211}