mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
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.
148 lines
4.7 KiB
C++
148 lines
4.7 KiB
C++
/*
|
|
This file is part of TON Blockchain Library.
|
|
|
|
TON Blockchain Library is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation, either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
TON Blockchain Library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "symtable.h"
|
|
#include "compiler-state.h"
|
|
#include "platform-utils.h"
|
|
#include "generics-helpers.h"
|
|
|
|
namespace tolk {
|
|
|
|
std::string FunctionData::as_human_readable() const {
|
|
if (!genericTs) {
|
|
return name; // if it's generic instantiation like `f<int>`, its name is "f<int>", not "f"
|
|
}
|
|
return name + genericTs->as_human_readable();
|
|
}
|
|
|
|
bool FunctionData::does_need_codegen() const {
|
|
// when a function is declared, but not referenced from code in any way, don't generate its body
|
|
if (!is_really_used() && G.settings.remove_unused_functions) {
|
|
return false;
|
|
}
|
|
// functions with asm body don't need code generation
|
|
// (even if used as non-call: `var a = beginCell;` inserts TVM continuation inline)
|
|
if (is_asm_function() || is_builtin_function()) {
|
|
return false;
|
|
}
|
|
// when a function is referenced like `var a = some_fn;` (or in some other non-call way), its continuation should exist
|
|
if (is_used_as_noncall()) {
|
|
return true;
|
|
}
|
|
// generic functions also don't need code generation, only generic instantiations do
|
|
if (is_generic_function()) {
|
|
return false;
|
|
}
|
|
// currently, there is no inlining, all functions are codegenerated
|
|
// (but actually, unused ones are later removed by Fift)
|
|
// in the future, we may want to implement a true AST inlining for "simple" functions
|
|
return true;
|
|
}
|
|
|
|
void FunctionData::assign_resolved_type(TypePtr declared_return_type) {
|
|
this->declared_return_type = declared_return_type;
|
|
}
|
|
|
|
void FunctionData::assign_inferred_type(TypePtr inferred_return_type, TypePtr inferred_full_type) {
|
|
this->inferred_return_type = inferred_return_type;
|
|
this->inferred_full_type = inferred_full_type;
|
|
}
|
|
|
|
void FunctionData::assign_is_used_as_noncall() {
|
|
this->flags |= flagUsedAsNonCall;
|
|
}
|
|
|
|
void FunctionData::assign_is_implicit_return() {
|
|
this->flags |= flagImplicitReturn;
|
|
}
|
|
|
|
void FunctionData::assign_is_type_inferring_done() {
|
|
this->flags |= flagTypeInferringDone;
|
|
}
|
|
|
|
void FunctionData::assign_is_really_used() {
|
|
this->flags |= flagReallyUsed;
|
|
}
|
|
|
|
void FunctionData::assign_arg_order(std::vector<int>&& arg_order) {
|
|
this->arg_order = std::move(arg_order);
|
|
}
|
|
|
|
void GlobalVarData::assign_resolved_type(TypePtr declared_type) {
|
|
this->declared_type = declared_type;
|
|
}
|
|
|
|
void GlobalVarData::assign_is_really_used() {
|
|
this->flags |= flagReallyUsed;
|
|
}
|
|
|
|
void GlobalConstData::assign_resolved_type(TypePtr declared_type) {
|
|
this->declared_type = declared_type;
|
|
}
|
|
|
|
void LocalVarData::assign_ir_idx(std::vector<int>&& ir_idx) {
|
|
this->ir_idx = std::move(ir_idx);
|
|
}
|
|
|
|
void LocalVarData::assign_resolved_type(TypePtr declared_type) {
|
|
this->declared_type = declared_type;
|
|
}
|
|
|
|
void LocalVarData::assign_inferred_type(TypePtr inferred_type) {
|
|
this->declared_type = inferred_type;
|
|
}
|
|
|
|
GNU_ATTRIBUTE_NORETURN GNU_ATTRIBUTE_COLD
|
|
static void fire_error_redefinition_of_symbol(SrcLocation loc, const Symbol* previous) {
|
|
SrcLocation prev_loc = previous->loc;
|
|
if (prev_loc.is_stdlib()) {
|
|
throw ParseError(loc, "redefinition of a symbol from stdlib");
|
|
}
|
|
if (prev_loc.is_defined()) {
|
|
throw ParseError(loc, "redefinition of symbol, previous was at: " + prev_loc.to_string());
|
|
}
|
|
throw ParseError(loc, "redefinition of built-in symbol");
|
|
}
|
|
|
|
void GlobalSymbolTable::add_function(FunctionPtr f_sym) {
|
|
auto key = key_hash(f_sym->name);
|
|
auto [it, inserted] = entries.emplace(key, f_sym);
|
|
if (!inserted) {
|
|
fire_error_redefinition_of_symbol(f_sym->loc, it->second);
|
|
}
|
|
}
|
|
|
|
void GlobalSymbolTable::add_global_var(GlobalVarPtr g_sym) {
|
|
auto key = key_hash(g_sym->name);
|
|
auto [it, inserted] = entries.emplace(key, g_sym);
|
|
if (!inserted) {
|
|
fire_error_redefinition_of_symbol(g_sym->loc, it->second);
|
|
}
|
|
}
|
|
|
|
void GlobalSymbolTable::add_global_const(GlobalConstPtr c_sym) {
|
|
auto key = key_hash(c_sym->name);
|
|
auto [it, inserted] = entries.emplace(key, c_sym);
|
|
if (!inserted) {
|
|
fire_error_redefinition_of_symbol(c_sym->loc, it->second);
|
|
}
|
|
}
|
|
|
|
const Symbol* lookup_global_symbol(std::string_view name) {
|
|
return G.symtable.lookup(name);
|
|
}
|
|
|
|
} // namespace tolk
|