1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
use erg_common::config::ErgConfig;

use crate::effectcheck::SideEffectChecker;
use crate::hir::*;
use crate::module::SharedCompilerResource;
// use crate::erg_common::traits::Stream;

/// Optimizes a `HIR`.
/// This should not be used in the context of sequential execution (e.g. REPL), since it assumes that the given code is all there is.
/// The optimizer determines the optimization level using `opt_level` in `cfg: ErgConfig`.
#[derive(Debug)]
pub struct HIROptimizer {
    cfg: ErgConfig,
    shared: SharedCompilerResource,
}

impl HIROptimizer {
    pub fn optimize(cfg: ErgConfig, shared: SharedCompilerResource, hir: HIR) -> HIR {
        let mut optimizer = HIROptimizer { cfg, shared };
        if optimizer.cfg.opt_level == 0 || optimizer.cfg.input.is_repl() {
            return hir;
        }
        optimizer.eliminate_dead_code(hir)
    }

    fn _fold_constants(&mut self, mut _hir: HIR) -> HIR {
        todo!()
    }

    fn eliminate_unused_variables(&mut self, mut hir: HIR) -> HIR {
        for chunk in hir.module.iter_mut() {
            self.eliminate_unused_def(chunk);
        }
        hir
    }

    fn eliminate_unused_def(&mut self, expr: &mut Expr) {
        match expr {
            Expr::Def(def) => {
                if def.sig.ident().is_discarded() || def.sig.vis().is_public() {
                    return;
                }
                if self
                    .shared
                    .index
                    .get_refs(&def.sig.ident().vi.def_loc)
                    .unwrap()
                    .referrers
                    .is_empty()
                    && SideEffectChecker::is_pure(expr)
                {
                    *expr = Expr::Dummy(Dummy::empty());
                }
            }
            Expr::Call(call) => {
                for arg in call.args.pos_args.iter_mut() {
                    self.eliminate_unused_def(&mut arg.expr);
                }
            }
            Expr::Code(block) | Expr::Compound(block) => {
                for chunk in block.iter_mut() {
                    self.eliminate_unused_def(chunk);
                }
            }
            Expr::Lambda(lambda) => {
                for chunk in lambda.body.iter_mut() {
                    self.eliminate_unused_def(chunk);
                }
            }
            _ => {}
        }
    }

    fn eliminate_dead_code(&mut self, hir: HIR) -> HIR {
        let hir = self.eliminate_discarded_variables(hir);
        self.eliminate_unused_variables(hir)
    }

    /// ```erg
    /// _ = 1
    /// (a, _) = (1, True)
    /// ```
    /// ↓
    /// ```erg
    /// a = 1
    /// ```
    fn eliminate_discarded_variables(&mut self, hir: HIR) -> HIR {
        hir
    }
}