Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type checking analysis pass #378

Open
nblei opened this issue Sep 1, 2023 · 6 comments
Open

Type checking analysis pass #378

nblei opened this issue Sep 1, 2023 · 6 comments
Labels
enhancement New feature or request

Comments

@nblei
Copy link
Contributor

nblei commented Sep 1, 2023

Dalance,

I would like to add a type checking pass which has a primary goal of evaluating the size (in bits) and packedness of each datatype.

The way I see this working is that a graph whose nodes are types, and whose edges represent dependencies.

For example,

type k = logic<2>;
struct m {
  a: k,
}

Would contain three nodes and two edges:
(logic<2>) -> (k) -> (m).

In addition to allowing us to identify the packed size of m, this also allows us to do dependency analysis. The type-graph should be a DAG. If a cycle exists, we have a problem. However, currently

package p {
    struct m {
        a: logic,
        n: n,
    }
    struct n {
        b: logic,
        m: m,
    }
}

passes on this mutually recurssive, but not inductive package p.

Any thoughts on this approach?

@nblei
Copy link
Contributor Author

nblei commented Sep 2, 2023

Another note:

Currently, veryl allows declaration of variably lengthed logic vectors:

module foo
{
    var a: u32;
    var _b: logic<a>;
}

produces

module veryl_testcase_foo;
    int unsigned         a ;
    logic        [a-1:0] _b;
endmodule

I think this is likely because the struct Type's width and array fields:

pub struct Type {
    pub modifier: Vec<TypeModifier>,
    pub kind: TypeKind,
    pub width: Vec<syntax_tree::Expression>,
    pub array: Vec<syntax_tree::Expression>,
}

These are arbitrary expressions, and no checking is done to determine if they are evaluable, either directly into an explicit constant natural number, or parametrically.

@dalance
Copy link
Collaborator

dalance commented Sep 4, 2023

A primitive type evaluation is implemented at https://github.com/dalance/veryl/blob/master/crates/analyzer/src/handlers/check_enum.rs.
It evaluates the size of enum members and checks whether the size is over the enum size.

I implemented it as a checker, but I think it should be more generic framework.
For example,

  • Create SymbolTable ( already implemented )
  • Create Type DAG from the SymbolTable
  • Evaluate size of each entry of the DAG
  • Check relationship between each entry like enum, union

About cycle of DAG, I think it should be reported as error at creating DAG phase.
Variably lengthed logic may be checked at evaluating phase too.

The current implementation of evaluator https://github.com/dalance/veryl/blob/master/crates/analyzer/src/evaluator.rs can handle fixed value only.
It is required to extend to support parameterized value.

@dalance dalance added the enhancement New feature or request label Sep 4, 2023
@nblei
Copy link
Contributor Author

nblei commented Sep 4, 2023

How is the symbol table currently used? greping the analyzer crate shows that symbol_table::get_all() is only used in analyzer::check_symbol_table, and symbol_table::get is only used in the symbol_table crate's test module.

It looks like to use the symbol table in a separate pass, I would need to implement VerylGrammarTrait functions for all syntactical structures which can push to SymbolPath and Namespace. However, this is something very easy to mistake.

Perhaps a trait VerylGrammarContextTrait : VerylGrammarTrat is called for. This subtrait would implement default functions which push/pop symbol path / namespace as required.

Does this make sense?

@dalance
Copy link
Collaborator

dalance commented Sep 5, 2023

The symbol table is used at some analyzer handlers and language server.
For example,

https://github.com/dalance/veryl/blob/9571195d062834f9c0b365e06344660c45c31191/crates/analyzer/src/handlers/check_function.rs#L39
https://github.com/dalance/veryl/blob/9571195d062834f9c0b365e06344660c45c31191/crates/analyzer/src/handlers/check_instance.rs#L45

It looks like to use the symbol table in a separate pass, I would need to implement VerylGrammarTrait functions for all syntactical structures which can push to SymbolPath and Namespace. However, this is something very easy to mistake.

To avoid re-implementation of pushing/poping SymbolPath and Namespace, I implemented namespace_table.
https://github.com/dalance/veryl/blob/master/crates/analyzer/src/namespace_table.rs

It holds pairs of TokenId, and Namespace which is belonged by the token.
It is created at creating symbol table, and namespace can be resolved without push/pop after the creation.
symbol_table::resolve internally calls the namespace_table like below:

https://github.com/dalance/veryl/blob/9571195d062834f9c0b365e06344660c45c31191/crates/analyzer/src/symbol_table.rs#L130-L135

@nblei
Copy link
Contributor Author

nblei commented Sep 13, 2023

Hey @dalance , any changes you'd like me to make to #383?

@dalance
Copy link
Collaborator

dalance commented Sep 13, 2023

Sorry for the late reply.
I'ii check it by this weekend.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants