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/environment.rs
Line
Count
Source
1
//! Type environment for type inference
2
3
use crate::middleend::types::{MonoType, TyVarGenerator, TypeScheme};
4
use std::collections::HashMap;
5
6
/// Type environment mapping identifiers to type schemes
7
#[derive(Debug, Clone)]
8
pub struct TypeEnv {
9
    bindings: HashMap<String, TypeScheme>,
10
}
11
12
impl TypeEnv {
13
    #[must_use]
14
55
    pub fn new() -> Self {
15
55
        TypeEnv {
16
55
            bindings: HashMap::new(),
17
55
        }
18
55
    }
19
20
    /// Create a standard environment with built-in functions
21
    #[must_use]
22
42
    pub fn standard() -> Self {
23
42
        let mut env = Self::new();
24
25
        // Arithmetic functions
26
42
        env.bind(
27
            "add",
28
42
            TypeScheme::mono(MonoType::Function(
29
42
                Box::new(MonoType::Int),
30
42
                Box::new(MonoType::Function(
31
42
                    Box::new(MonoType::Int),
32
42
                    Box::new(MonoType::Int),
33
42
                )),
34
42
            )),
35
        );
36
37
        // IO functions
38
42
        env.bind(
39
            "print",
40
42
            TypeScheme::mono(MonoType::Function(
41
42
                Box::new(MonoType::String),
42
42
                Box::new(MonoType::Unit),
43
42
            )),
44
        );
45
46
42
        env.bind(
47
            "println",
48
42
            TypeScheme::mono(MonoType::Function(
49
42
                Box::new(MonoType::String),
50
42
                Box::new(MonoType::Unit),
51
42
            )),
52
        );
53
54
        // Comparison functions
55
42
        env.bind(
56
            "eq",
57
42
            TypeScheme::mono(MonoType::Function(
58
42
                Box::new(MonoType::Int),
59
42
                Box::new(MonoType::Function(
60
42
                    Box::new(MonoType::Int),
61
42
                    Box::new(MonoType::Bool),
62
42
                )),
63
42
            )),
64
        );
65
66
42
        env
67
42
    }
68
69
    /// Bind a name to a type scheme
70
209
    pub fn bind(&mut self, name: impl Into<String>, scheme: TypeScheme) {
71
209
        self.bindings.insert(name.into(), scheme);
72
209
    }
73
74
    /// Look up a name in the environment
75
    #[must_use]
76
46
    pub fn lookup(&self, name: &str) -> Option<&TypeScheme> {
77
46
        self.bindings.get(name)
78
46
    }
79
80
    /// Extend the environment with a new binding (functional style)
81
    #[must_use]
82
31
    pub fn extend(&self, name: impl Into<String>, scheme: TypeScheme) -> Self {
83
31
        let mut new_env = self.clone();
84
31
        new_env.bind(name, scheme);
85
31
        new_env
86
31
    }
87
88
    /// Get free type variables in the environment
89
    #[must_use]
90
17
    pub fn free_vars(&self) -> Vec<crate::middleend::types::TyVar> {
91
17
        let mut vars = Vec::new();
92
52
        for scheme in 
self.bindings17
.
values17
() {
93
            // Only collect free variables not bound by the scheme
94
52
            let scheme_free = scheme.ty.free_vars();
95
59
            for 
var7
in scheme_free {
96
7
                if !scheme.vars.contains(&var) {
97
2
                    vars.push(var);
98
5
                }
99
            }
100
        }
101
17
        vars
102
17
    }
103
104
    /// Generalize a monomorphic type to a type scheme
105
    #[must_use]
106
15
    pub fn generalize(&self, ty: MonoType) -> TypeScheme {
107
15
        let ty_vars = ty.free_vars();
108
15
        let env_vars = self.free_vars();
109
110
        // Variables to generalize are those in ty but not in env
111
15
        let gen_vars: Vec<_> = ty_vars
112
15
            .into_iter()
113
15
            .filter(|v| !
env_vars11
.contains(v))
114
15
            .collect();
115
116
15
        TypeScheme { vars: gen_vars, ty }
117
15
    }
118
119
    /// Instantiate a type scheme with fresh variables
120
30
    pub fn instantiate(&self, scheme: &TypeScheme, gen: &mut TyVarGenerator) -> MonoType {
121
30
        scheme.instantiate(gen)
122
30
    }
123
}
124
125
impl Default for TypeEnv {
126
1
    fn default() -> Self {
127
1
        Self::new()
128
1
    }
129
}
130
131
#[cfg(test)]
132
#[allow(clippy::unwrap_used, clippy::panic)]
133
mod tests {
134
    use super::*;
135
    use crate::middleend::types::TyVar;
136
137
    #[test]
138
1
    fn test_env_lookup() {
139
1
        let mut env = TypeEnv::new();
140
1
        env.bind("x", TypeScheme::mono(MonoType::Int));
141
142
1
        assert!(env.lookup("x").is_some());
143
1
        assert!(env.lookup("y").is_none());
144
1
    }
145
146
    #[test]
147
1
    fn test_env_extend() {
148
1
        let env = TypeEnv::new();
149
1
        let env2 = env.extend("x", TypeScheme::mono(MonoType::Bool));
150
151
1
        assert!(env.lookup("x").is_none());
152
1
        assert!(env2.lookup("x").is_some());
153
1
    }
154
155
    #[test]
156
1
    fn test_generalization() {
157
1
        let env = TypeEnv::new();
158
1
        let var = TyVar(0);
159
160
        // A type with a free variable
161
1
        let ty = MonoType::Function(
162
1
            Box::new(MonoType::Var(var.clone())),
163
1
            Box::new(MonoType::Var(var.clone())),
164
1
        );
165
166
1
        let scheme = env.generalize(ty);
167
168
        // The variable should be generalized
169
1
        assert_eq!(scheme.vars.len(), 1);
170
1
        assert!(scheme.vars.contains(&var));
171
1
    }
172
173
    #[test]
174
1
    fn test_no_generalization_with_env_vars() {
175
1
        let mut env = TypeEnv::new();
176
1
        let var = TyVar(0);
177
178
        // Add a binding with the same variable to the environment
179
1
        env.bind("y", TypeScheme::mono(MonoType::Var(var.clone())));
180
181
        // Try to generalize a type with the same variable
182
1
        let ty = MonoType::Function(Box::new(MonoType::Var(var)), Box::new(MonoType::Int));
183
184
1
        let scheme = env.generalize(ty);
185
186
        // The variable should NOT be generalized (it's in the env)
187
1
        assert_eq!(scheme.vars.len(), 0);
188
1
    }
189
190
    #[test]
191
1
    fn test_standard_env() {
192
1
        let env = TypeEnv::standard();
193
194
1
        assert!(env.lookup("println").is_some());
195
1
        assert!(env.lookup("print").is_some());
196
1
        assert!(env.lookup("add").is_some());
197
1
        assert!(env.lookup("eq").is_some());
198
1
    }
199
200
    #[test]
201
1
    fn test_default_env() {
202
1
        let env = TypeEnv::default();
203
1
        assert!(env.lookup("nonexistent").is_none());
204
1
        assert_eq!(env.bindings.len(), 0);
205
1
    }
206
207
    #[test]
208
1
    fn test_multiple_bindings() {
209
1
        let mut env = TypeEnv::new();
210
1
        env.bind("x", TypeScheme::mono(MonoType::Int));
211
1
        env.bind("y", TypeScheme::mono(MonoType::Bool));
212
1
        env.bind("z", TypeScheme::mono(MonoType::String));
213
214
1
        assert!(env.lookup("x").is_some());
215
1
        assert!(env.lookup("y").is_some());
216
1
        assert!(env.lookup("z").is_some());
217
1
        assert!(env.lookup("w").is_none());
218
1
    }
219
220
    #[test]
221
1
    fn test_bind_overwrites() {
222
1
        let mut env = TypeEnv::new();
223
1
        env.bind("x", TypeScheme::mono(MonoType::Int));
224
1
        env.bind("x", TypeScheme::mono(MonoType::Bool));
225
226
1
        let scheme = env.lookup("x").unwrap();
227
1
        match &scheme.ty {
228
1
            MonoType::Bool => {}, // Expected
229
0
            _ => panic!("Expected Bool type after overwrite"),
230
        }
231
1
    }
232
233
    #[test]
234
1
    fn test_env_clone() {
235
1
        let mut env1 = TypeEnv::new();
236
1
        env1.bind("x", TypeScheme::mono(MonoType::Int));
237
238
1
        let env2 = env1.clone();
239
1
        assert!(env2.lookup("x").is_some());
240
1
    }
241
242
    #[test]
243
1
    fn test_free_vars_empty() {
244
1
        let env = TypeEnv::new();
245
1
        assert!(env.free_vars().is_empty());
246
1
    }
247
248
    #[test]
249
1
    fn test_free_vars_with_schemes() {
250
1
        let mut env = TypeEnv::new();
251
1
        let var1 = TyVar(1);
252
1
        let var2 = TyVar(2);
253
254
        // Add a scheme with a free variable
255
1
        let scheme1 = TypeScheme {
256
1
            vars: vec![],
257
1
            ty: MonoType::Var(var1.clone()),
258
1
        };
259
1
        env.bind("x", scheme1);
260
261
        // Add a scheme with a bound variable
262
1
        let scheme2 = TypeScheme {
263
1
            vars: vec![var2.clone()],
264
1
            ty: MonoType::Var(var2),
265
1
        };
266
1
        env.bind("y", scheme2);
267
268
1
        let free_vars = env.free_vars();
269
1
        assert!(free_vars.contains(&var1));
270
1
        assert!(!free_vars.contains(&TyVar(2))); // var2 is bound
271
1
    }
272
273
    #[test]
274
1
    fn test_generalize_empty_env() {
275
1
        let env = TypeEnv::new();
276
1
        let var = TyVar(5);
277
1
        let ty = MonoType::Var(var.clone());
278
279
1
        let scheme = env.generalize(ty);
280
1
        assert_eq!(scheme.vars.len(), 1);
281
1
        assert!(scheme.vars.contains(&var));
282
1
    }
283
284
    #[test]
285
1
    fn test_generalize_complex_type() {
286
1
        let env = TypeEnv::new();
287
1
        let var1 = TyVar(10);
288
1
        let var2 = TyVar(11);
289
290
1
        let ty = MonoType::Function(
291
1
            Box::new(MonoType::Var(var1.clone())),
292
1
            Box::new(MonoType::Function(
293
1
                Box::new(MonoType::Var(var2.clone())),
294
1
                Box::new(MonoType::Int),
295
1
            )),
296
1
        );
297
298
1
        let scheme = env.generalize(ty);
299
1
        assert_eq!(scheme.vars.len(), 2);
300
1
        assert!(scheme.vars.contains(&var1));
301
1
        assert!(scheme.vars.contains(&var2));
302
1
    }
303
304
    #[test]
305
1
    fn test_instantiate_scheme() {
306
1
        let env = TypeEnv::new();
307
1
        let mut gen = TyVarGenerator::new();
308
309
        // Create a polymorphic scheme: forall a. a -> a
310
1
        let var = TyVar(20);
311
1
        let scheme = TypeScheme {
312
1
            vars: vec![var.clone()],
313
1
            ty: MonoType::Function(
314
1
                Box::new(MonoType::Var(var.clone())),
315
1
                Box::new(MonoType::Var(var)),
316
1
            ),
317
1
        };
318
319
1
        let instance = env.instantiate(&scheme, &mut gen);
320
        
321
        // Should get fresh variables
322
1
        match instance {
323
1
            MonoType::Function(arg, ret) => {
324
1
                match (*arg, *ret) {
325
1
                    (MonoType::Var(v1), MonoType::Var(v2)) => {
326
1
                        assert_eq!(v1, v2); // Same fresh variable
327
1
                        assert_ne!(v1, TyVar(20)); // Different from original
328
                    }
329
0
                    _ => panic!("Expected function with variable types"),
330
                }
331
            }
332
0
            _ => panic!("Expected function type"),
333
        }
334
1
    }
335
336
    #[test]
337
1
    fn test_standard_env_function_types() {
338
1
        let env = TypeEnv::standard();
339
        
340
        // Test add function type
341
1
        let add_scheme = env.lookup("add").unwrap();
342
1
        match &add_scheme.ty {
343
1
            MonoType::Function(arg1, rest) => {
344
1
                assert!(
matches!0
(**arg1, MonoType::Int));
345
1
                match rest.as_ref() {
346
1
                    MonoType::Function(arg2, ret_type) => {
347
1
                        assert!(
matches!0
(**arg2, MonoType::Int));
348
1
                        assert!(
matches!0
(**ret_type, MonoType::Int));
349
                    }
350
0
                    _ => panic!("Expected curried function type"),
351
                }
352
            }
353
0
            _ => panic!("Expected function type for add"),
354
        }
355
356
        // Test print function type
357
1
        let print_scheme = env.lookup("print").unwrap();
358
1
        match &print_scheme.ty {
359
1
            MonoType::Function(arg, ret) => {
360
1
                assert!(
matches!0
(**arg, MonoType::String));
361
1
                assert!(
matches!0
(**ret, MonoType::Unit));
362
            }
363
0
            _ => panic!("Expected function type for print"),
364
        }
365
1
    }
366
}