1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

[Tolk] Smart casts and control flow graph

With the introduction of nullable types, we want the
compiler to be smart in cases like
> if (x == null) return;
> // x is int now
or
> if (x == null) x = 0;
> // x is int now

These are called smart casts: when the type of variable
at particular usage might differ from its declaration.

Implementing smart casts is very challenging. They are based
on building control-flow graph and handling every AST vertex
with care. Actually, I represent cfg not a as a "graph with
edges". Instead, it's a "structured DFS" for the AST:
1) at every point of inferring, we have "current flow facts"
2) when we see an `if (...)`, we create two derived contexts
3) after `if`, finalize them at the end and unify
4) if we detect unreachable code, we mark that context
In other words, we get the effect of a CFG but in a more direct
approach. That's enough for AST-level data-flow.

Smart casts work for local variables and tensor/tuple indices.
Compilation errors have been reworked and now are more friendly.
There are also compilation warnings for always true/false
conditions inside if, assert, etc.
This commit is contained in:
tolk-vm 2025-02-24 20:14:16 +03:00
parent f3e620f48c
commit 7bcb8b895f
No known key found for this signature in database
GPG key ID: 7905DD7FE0324B12
47 changed files with 3057 additions and 833 deletions

View file

@ -186,11 +186,14 @@ struct ASTNodeExpressionBase : ASTNodeBase {
TypePtr inferred_type = nullptr;
bool is_rvalue: 1 = false;
bool is_lvalue: 1 = false;
bool is_always_true: 1 = false; // inside `if`, `while`, ternary condition, `== null`, etc.
bool is_always_false: 1 = false; // (when expression is guaranteed to be always true or always false)
ASTNodeExpressionBase* mutate() const { return const_cast<ASTNodeExpressionBase*>(this); }
void assign_inferred_type(TypePtr type);
void assign_rvalue_true();
void assign_lvalue_true();
void assign_always_true_or_false(int flow_true_false_state);
ASTNodeExpressionBase(ASTNodeType type, SrcLocation loc) : ASTNodeBase(type, loc) {}
};
@ -734,10 +737,14 @@ template<>
// example: do while body is a sequence
struct Vertex<ast_sequence> final : ASTStatementVararg {
SrcLocation loc_end;
AnyV first_unreachable = nullptr;
const std::vector<AnyV>& get_items() const { return children; }
AnyV get_item(int i) const { return children.at(i); }
Vertex* mutate() const { return const_cast<Vertex*>(this); }
void assign_first_unreachable(AnyV first_unreachable);
Vertex(SrcLocation loc, SrcLocation loc_end, std::vector<AnyV> items)
: ASTStatementVararg(ast_sequence, loc, std::move(items))
, loc_end(loc_end) {}