## Coverage Report

#### Created: 2025-09-08 21: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
        fn test_program_creation() {
    347
            let program = Program {
    348
                functions: HashMap::new(),
    349
                entry: "main".to_string(),
    350
            };
    351
    352
            assert_eq!(program.entry, "main");
    353
            assert_eq!(program.functions.len(), 0);
    354
        }
    355
    356
    357
        #[test]
    358
        fn test_block_id() {
    359
            let id1 = BlockId(0);
    360
            let id2 = BlockId(1);
    361
            let id3 = BlockId(0);
    362
    363
            assert_eq!(id1, id3);
    364
            assert_ne!(id1, id2);
    365
            assert_eq!(format!("{id1:?}"), "BlockId(0)");
    366
        }
    367
    368
        #[test]
    369
        fn test_local_variable() {
    370
            let local1 = Local(0);
    371
            let local2 = Local(1);
    372
            let local3 = Local(0);
    373
    374
            assert_eq!(local1, local3);
    375
            assert_ne!(local1, local2);
    376
            assert_eq!(format!("{local1:?}"), "Local(0)");
    377
        }
    378
    379
        #[test]
    380
        fn test_type_variants() {
    381
            let types = vec![
    382
                Type::Unit,
    383
                Type::Bool,
    384
                Type::I32,
    385
                Type::I64,
    386
                Type::F32,
    387
                Type::F64,
    388
                Type::String,
    389
                Type::Array(Box::new(Type::I32), 10),
    390
                Type::Vec(Box::new(Type::F64)),
    391
                Type::Tuple(vec![Type::I32, Type::Bool]),
    392
            ];
    393
    394
            for ty in types {
    395
                assert!(!format!("{ty:?}").is_empty());
    396
            }
    397
        }
    398
    399
        #[test]
    400
        fn test_type_equality() {
    401
            assert_eq!(Type::I32, Type::I32);
    402
            assert_ne!(Type::I32, Type::I64);
    403
            assert_eq!(
    404
                Type::Array(Box::new(Type::U8), 5),
    405
                Type::Array(Box::new(Type::U8), 5)
    406
            );
    407
            assert_ne!(
    408
                Type::Array(Box::new(Type::U8), 5),
    409
                Type::Array(Box::new(Type::U8), 10)
    410
            );
    411
        }
    412
    }