Coverage Report

Created: 2025-09-05 15:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/home/noah/src/ruchy/src/middleend/mir/types.rs
Line
Count
Source
1
//! MIR type definitions
2
3
use std::collections::HashMap;
4
use std::fmt;
5
6
/// A MIR program consists of functions
7
#[derive(Debug, Clone)]
8
pub struct Program {
9
    /// Global functions in the program
10
    pub functions: HashMap<String, Function>,
11
    /// Entry point function name
12
    pub entry: String,
13
}
14
15
/// A function in MIR representation
16
#[derive(Debug, Clone)]
17
pub struct Function {
18
    /// Function name
19
    pub name: String,
20
    /// Parameters (as local variables)
21
    pub params: Vec<Local>,
22
    /// Return type
23
    pub return_ty: Type,
24
    /// Local variables (including parameters)
25
    pub locals: Vec<LocalDecl>,
26
    /// Basic blocks making up the function body
27
    pub blocks: Vec<BasicBlock>,
28
    /// Entry block index
29
    pub entry_block: BlockId,
30
}
31
32
/// A basic block is a sequence of statements with a single entry and exit
33
#[derive(Debug, Clone)]
34
pub struct BasicBlock {
35
    /// Block identifier
36
    pub id: BlockId,
37
    /// Statements in this block (no control flow)
38
    pub statements: Vec<Statement>,
39
    /// Terminator - how control leaves this block
40
    pub terminator: Terminator,
41
}
42
43
/// Block identifier
44
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
45
pub struct BlockId(pub usize);
46
47
/// Local variable identifier
48
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
49
pub struct Local(pub usize);
50
51
/// Local variable declaration
52
#[derive(Debug, Clone)]
53
pub struct LocalDecl {
54
    /// Variable identifier
55
    pub id: Local,
56
    /// Variable type
57
    pub ty: Type,
58
    /// Is this mutable?
59
    pub mutable: bool,
60
    /// Optional name for debugging
61
    pub name: Option<String>,
62
}
63
64
/// MIR types (simplified from AST types)
65
#[derive(Debug, Clone, PartialEq, Eq)]
66
pub enum Type {
67
    /// Unit type
68
    Unit,
69
    /// Boolean
70
    Bool,
71
    /// Signed integers
72
    I8,
73
    I16,
74
    I32,
75
    I64,
76
    I128,
77
    /// Unsigned integers
78
    U8,
79
    U16,
80
    U32,
81
    U64,
82
    U128,
83
    /// Floating point
84
    F32,
85
    F64,
86
    /// String
87
    String,
88
    /// Reference (borrowed)
89
    Ref(Box<Type>, Mutability),
90
    /// Array with known size
91
    Array(Box<Type>, usize),
92
    /// Dynamic vector
93
    Vec(Box<Type>),
94
    /// Tuple
95
    Tuple(Vec<Type>),
96
    /// Function pointer
97
    FnPtr(Vec<Type>, Box<Type>),
98
    /// User-defined type
99
    UserType(String),
100
}
101
102
/// Mutability of references
103
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
104
pub enum Mutability {
105
    Immutable,
106
    Mutable,
107
}
108
109
/// A statement that doesn't affect control flow
110
#[derive(Debug, Clone)]
111
pub enum Statement {
112
    /// Assign an rvalue to a place
113
    Assign(Place, Rvalue),
114
    /// Mark a local as live (for storage)
115
    StorageLive(Local),
116
    /// Mark a local as dead (storage can be reclaimed)
117
    StorageDead(Local),
118
    /// No operation
119
    Nop,
120
}
121
122
/// A place where a value can be stored
123
#[derive(Debug, Clone)]
124
pub enum Place {
125
    /// Local variable
126
    Local(Local),
127
    /// Field projection (e.g., struct.field)
128
    Field(Box<Place>, FieldIdx),
129
    /// Array/slice index
130
    Index(Box<Place>, Box<Place>),
131
    /// Dereference
132
    Deref(Box<Place>),
133
}
134
135
/// Field index in a struct/tuple
136
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
137
pub struct FieldIdx(pub usize);
138
139
/// Right-hand side of an assignment
140
#[derive(Debug, Clone)]
141
pub enum Rvalue {
142
    /// Use a value from a place
143
    Use(Operand),
144
    /// Binary operation
145
    BinaryOp(BinOp, Operand, Operand),
146
    /// Unary operation
147
    UnaryOp(UnOp, Operand),
148
    /// Create a reference
149
    Ref(Mutability, Place),
150
    /// Create an aggregate (struct, tuple, array)
151
    Aggregate(AggregateKind, Vec<Operand>),
152
    /// Function call
153
    Call(Operand, Vec<Operand>),
154
    /// Cast between types
155
    Cast(CastKind, Operand, Type),
156
}
157
158
/// An operand (value that can be used)
159
#[derive(Debug, Clone)]
160
pub enum Operand {
161
    /// Copy value from a place
162
    Copy(Place),
163
    /// Move value from a place
164
    Move(Place),
165
    /// Constant value
166
    Constant(Constant),
167
}
168
169
/// Constant values
170
#[derive(Debug, Clone)]
171
pub enum Constant {
172
    /// Unit value
173
    Unit,
174
    /// Boolean
175
    Bool(bool),
176
    /// Integer
177
    Int(i128, Type),
178
    /// Unsigned integer
179
    Uint(u128, Type),
180
    /// Float
181
    Float(f64, Type),
182
    /// String literal
183
    String(String),
184
    /// Character literal
185
    Char(char),
186
}
187
188
/// Binary operations
189
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
190
pub enum BinOp {
191
    // Arithmetic
192
    Add,
193
    Sub,
194
    Mul,
195
    Div,
196
    Rem,
197
    Pow,
198
    // Bitwise
199
    BitAnd,
200
    BitOr,
201
    BitXor,
202
    Shl,
203
    Shr,
204
    // Comparison
205
    Eq,
206
    Ne,
207
    Lt,
208
    Le,
209
    Gt,
210
    Ge,
211
    // Logical (short-circuiting is handled by control flow)
212
    And,
213
    Or,
214
    NullCoalesce,
215
}
216
217
/// Unary operations
218
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
219
pub enum UnOp {
220
    /// Negation (arithmetic)
221
    Neg,
222
    /// Logical not
223
    Not,
224
    /// Bitwise not
225
    BitNot,
226
    /// Reference (borrow)
227
    Ref,
228
}
229
230
/// Aggregate kinds
231
#[derive(Debug, Clone)]
232
pub enum AggregateKind {
233
    /// Tuple
234
    Tuple,
235
    /// Array
236
    Array(Type),
237
    /// Struct
238
    Struct(String),
239
}
240
241
/// Cast kinds
242
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
243
pub enum CastKind {
244
    /// Numeric cast (int to int, float to float, etc.)
245
    Numeric,
246
    /// Pointer to pointer
247
    Pointer,
248
    /// Unsizing (e.g., array to slice)
249
    Unsize,
250
}
251
252
/// How control flow leaves a basic block
253
#[derive(Debug, Clone)]
254
pub enum Terminator {
255
    /// Unconditional jump
256
    Goto(BlockId),
257
    /// Conditional branch
258
    If {
259
        condition: Operand,
260
        then_block: BlockId,
261
        else_block: BlockId,
262
    },
263
    /// Switch/match on a value
264
    Switch {
265
        discriminant: Operand,
266
        targets: Vec<(Constant, BlockId)>,
267
        default: Option<BlockId>,
268
    },
269
    /// Return from function
270
    Return(Option<Operand>),
271
    /// Call a function and continue
272
    Call {
273
        func: Operand,
274
        args: Vec<Operand>,
275
        destination: Option<(Place, BlockId)>,
276
    },
277
    /// Unreachable code (for exhaustiveness)
278
    Unreachable,
279
}
280
281
// Display implementations for debugging
282
impl fmt::Display for BlockId {
283
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
284
0
        write!(f, "bb{}", self.0)
285
0
    }
286
}
287
288
impl fmt::Display for Local {
289
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
290
0
        write!(f, "__{}", self.0)
291
0
    }
292
}
293
294
impl fmt::Display for Type {
295
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
296
0
        match self {
297
0
            Type::Unit => write!(f, "()"),
298
0
            Type::Bool => write!(f, "bool"),
299
0
            Type::I8 => write!(f, "i8"),
300
0
            Type::I16 => write!(f, "i16"),
301
0
            Type::I32 => write!(f, "i32"),
302
0
            Type::I64 => write!(f, "i64"),
303
0
            Type::I128 => write!(f, "i128"),
304
0
            Type::U8 => write!(f, "u8"),
305
0
            Type::U16 => write!(f, "u16"),
306
0
            Type::U32 => write!(f, "u32"),
307
0
            Type::U64 => write!(f, "u64"),
308
0
            Type::U128 => write!(f, "u128"),
309
0
            Type::F32 => write!(f, "f32"),
310
0
            Type::F64 => write!(f, "f64"),
311
0
            Type::String => write!(f, "String"),
312
0
            Type::Ref(ty, Mutability::Immutable) => write!(f, "&{ty}"),
313
0
            Type::Ref(ty, Mutability::Mutable) => write!(f, "&mut {ty}"),
314
0
            Type::Array(ty, size) => write!(f, "[{ty}; {size}]"),
315
0
            Type::Vec(ty) => write!(f, "Vec<{ty}>"),
316
0
            Type::Tuple(tys) => {
317
0
                write!(f, "(")?;
318
0
                for (i, ty) in tys.iter().enumerate() {
319
0
                    if i > 0 {
320
0
                        write!(f, ", ")?;
321
0
                    }
322
0
                    write!(f, "{ty}")?;
323
                }
324
0
                write!(f, ")")
325
            }
326
0
            Type::FnPtr(params, ret) => {
327
0
                write!(f, "fn(")?;
328
0
                for (i, param) in params.iter().enumerate() {
329
0
                    if i > 0 {
330
0
                        write!(f, ", ")?;
331
0
                    }
332
0
                    write!(f, "{param}")?;
333
                }
334
0
                write!(f, ") -> {ret}")
335
            }
336
0
            Type::UserType(name) => write!(f, "{name}"),
337
        }
338
0
    }
339
}
340
341
#[cfg(test)]
342
mod tests {
343
    use super::*;
344
    
345
    #[test]
346
1
    fn test_program_creation() {
347
1
        let program = Program {
348
1
            functions: HashMap::new(),
349
1
            entry: "main".to_string(),
350
1
        };
351
        
352
1
        assert_eq!(program.entry, "main");
353
1
        assert_eq!(program.functions.len(), 0);
354
1
    }
355
    
356
    
357
    #[test]
358
1
    fn test_block_id() {
359
1
        let id1 = BlockId(0);
360
1
        let id2 = BlockId(1);
361
1
        let id3 = BlockId(0);
362
        
363
1
        assert_eq!(id1, id3);
364
1
        assert_ne!(id1, id2);
365
1
        assert_eq!(format!("{id1:?}"), "BlockId(0)");
366
1
    }
367
    
368
    #[test]
369
1
    fn test_local_variable() {
370
1
        let local1 = Local(0);
371
1
        let local2 = Local(1);
372
1
        let local3 = Local(0);
373
        
374
1
        assert_eq!(local1, local3);
375
1
        assert_ne!(local1, local2);
376
1
        assert_eq!(format!("{local1:?}"), "Local(0)");
377
1
    }
378
    
379
    #[test]
380
1
    fn test_type_variants() {
381
1
        let types = vec![
382
1
            Type::Unit,
383
1
            Type::Bool,
384
1
            Type::I32,
385
1
            Type::I64,
386
1
            Type::F32,
387
1
            Type::F64,
388
1
            Type::String,
389
1
            Type::Array(Box::new(Type::I32), 10),
390
1
            Type::Vec(Box::new(Type::F64)),
391
1
            Type::Tuple(vec![Type::I32, Type::Bool]),
392
        ];
393
        
394
11
        for 
ty10
in types {
395
10
            assert!(!format!("{ty:?}").is_empty());
396
        }
397
1
    }
398
    
399
    #[test]
400
1
    fn test_type_equality() {
401
1
        assert_eq!(Type::I32, Type::I32);
402
1
        assert_ne!(Type::I32, Type::I64);
403
1
        assert_eq!(
404
1
            Type::Array(Box::new(Type::U8), 5),
405
1
            Type::Array(Box::new(Type::U8), 5)
406
        );
407
1
        assert_ne!(
408
1
            Type::Array(Box::new(Type::U8), 5),
409
1
            Type::Array(Box::new(Type::U8), 10)
410
        );
411
1
    }
412
}