rue_compiler/compile/
block.rs

1use 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}