brandonLi8 / solis

9 stars 0 forks source link

Name mangling #45

Open brandonLi8 opened 1 year ago

brandonLi8 commented 1 year ago

Consider the following program:

let a = 1
if true {
  let a = 2;
  print(a) // 2
}
print(a) // 1

One approach to have this behavior is to remove the "redeclare" check in type checker. In the then_block, the a is added to identifier_types in the ir. However, im thinking that redceclaring might have problems in the register allocation, as a variable in a if block would share the conflicts of whatever it previously had prematurely. Additionally, without name mangling, both as would be assigned to the same location and this the result would not even be correct.

brandonLi8 commented 1 year ago

I started working on this and I think I realized that maybe redeclared variables shouldn't be allowed at all. The best way to do it for now could be to just do it the "java" model, where

let a = 1
if true {
  let a = 2; // ERROR: redeclare in the same scope (
}

In java, if statements are sort of a psuedo scope where anything it creates doesnt live past the if statement, but it is the same scope in the sense that It cant create a local a.

However, this will need to be revisited later on when functions are implemented (since they do have their own entire scope)

let a: int = 0
let b: int = 9

function f() {
   let a: int = 1; // entirely local a
   a + b // 10
}

In this case, the f() will by default inherit the global scope (so b=9, a=0), but allow it to have a local a. We will have to mangle the a within the f() to be different from the global a

Here is my progress for name_mangler:

``` // Copyright © 2022-2023 Brandon Li. All rights reserved. //! Name mangling is the process of making every variable name unique. It is needed to ensure that variables in //! different scopes are placed in different locations at run time. See `https://github.com/brandonLi8/solis/issues/45`. //! It is performed in the same pass as the IR translation, and works in conjunction with the translator. //! //! The `translator` traverses down the AST tree, and flattens by post-operating on the sub results. Before operating on //! the sub-results, the mangler operates on the expression. use File; use std::ops::Range; use error_messages::compilation_error; use std::collections::HashMap; /// Name Mangler for each scope of the program. pub struct Mangler<'a> { /// Maps pre-mangled name to (the most recent) post-mangled name. mangled_names: HashMap, /// The number of mangled names. mangled_names_count: u32, /// The original Solis input file, for error messaging purposes. pub file: &'a File, } impl<'a> Mangler<'a> { /// Mangler constructor. /// * file: the original Solis file pub fn new(file: &'a File) -> Self { Mangler { file, mangled_names: HashMap::new(), mangled_names_count: 0 } } /// Constructs a Mangler from another Mangler, with the mangled_names cloned pub fn inherited(mangler: &Mangler<'a>) -> Self { Mangler { file: mangler.file, mangled_names: mangler.mangled_names.clone(), mangled_names_count: 0 } } /// Converts a pre-mangled name to a post-mangled name for a variable. pub fn mangle(&mut self, id: &String) -> String { let name = format!("{id}_mangle{}", self.mangled_names_count); self.mangled_names_count += 1; self.mangled_names.insert(id.to_string(), name.to_string()); name } /// Gets the post-mangled name of a variable. If the variable has not been declared, a `compilation_error` is /// created. pub fn get_name(&self, id: &String, position: &Range) -> String { let name = self.mangled_names.get(id); if name.is_none() { compilation_error(self.file, position, &format!("Undeclared variable `{id}`")) } name.unwrap().clone() } } ```