Type Checker
Noname uses a simple type checker to ensure that all types are consistent in the program.
For example, in code like:
#![allow(unused)] fn main() { let x = y + z; }
the type checker will ensure that y
and z
are both field elements (because the operation +
is used).
And in code like:
#![allow(unused)] fn main() { assert_eq(a, b); }
the type checker will ensure that a
and b
are of the same types, since they are being compared.
Type inference
The type checker can do some simple type inference. For example, in the following code:
#![allow(unused)] fn main() { let x = y + z; }
the type of x
is inferred to be the same as the type of y
and z
.
Inference is willingly kept naive, as more type inference would lead to less readable code.
Scope
The type checker must be aware of scopes, as it keeps track of the type of variables and functions that are local to each scope.
For example:
#![allow(unused)] fn main() { let x = 2; for i in 0..2 { let y = x + 1; // x exists inside of this for loop } let z = 1 + y; // BAD: y doesn't exist outside of the for loop }
To do this, each function is passed an Environment which contains a list of all variables along with their type information.
#![allow(unused)] fn main() { pub struct Environment { /// created by the type checker, gives a type to every external variable pub var_types: HashMap<String, TypeInfo>, // ... /// the functions present in the scope /// contains at least the set of builtin functions (like assert_eq) pub functions: HashMap<String, FuncInScope>, /// stores the imported modules pub modules: HashMap<String, ImportedModule>, } }
So that shadowing is disallowed, even in different scopes, there is only one variable that is stored, but the scope is stored in TypeInfo
and matched against the current scope to see if the current scope is the same or a direct child.
An environment is unique to a function, as it is important that different functions can use the same variable names.
Some notes:
- Currently the notion of module is quite shaky. It is used mostly for
crypto::poseidon
at the moment. functions
is mostly used for builtins likeassert_eq
modules
is mostly used for the functions instd::crypto
, which only containscrypto::poseidon
atm.
more:
- I think Environment mixes things
- we should be able to create a new Environment whenever we parse a new function, so the functions/modules should be part of another (AvailableToAllScopes)
- variables is something that has nothing to do with the “Type” Environment and should be moved elsewhere no? GateCreationEnv?
- there needs to be a namespace in the typeinfo