Add flow-based escape analysis of stack memory, reported as warnings

This commit is contained in:
gingerBill committed 2026-10-07 13:08:22 +01:00
1 parent 9a2321bf55
commit 8ea02cc733
4 files changed
+1839 -29

No files matched your search

+2
View File
@@ -2531,6 +2531,8 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
}
check_close_scope(ctx);
check_proc_escapes(type, body);
check_scope_usage(ctx->checker, ctx->scope, check_vet_flags(body));
add_deps_from_child_to_parent(decl);
+1826
View File
File diff suppressed because it is too large. Load diff
+9 -29
View File
@@ -2633,11 +2633,10 @@ gb_internal void check_if_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
check_close_scope(ctx);
}
// NOTE(bill): This is very basic escape analysis
// This needs to be improved tremendously, and a lot of it done during the
// middle-end (or LLVM side) to improve checks and error messages
void check_unsafe_return(Operand const &o, Type *type, Ast *expr) {
auto const unsafe_return_error = [](Operand const &o, char const *msg, Type *extra_type=nullptr) {
gb_internal bool check_unsafe_return(Operand const &o, Type *type, Ast *expr) {
bool reported = false;
auto const unsafe_return_error = [&reported](Operand const &o, char const *msg, Type *extra_type=nullptr) {
reported = true;
gbString s = expr_to_string(o.expr);
if (extra_type) {
gbString t = type_to_string(extra_type);
@@ -2650,13 +2649,13 @@ void check_unsafe_return(Operand const &o, Type *type, Ast *expr) {
};
if (type == nullptr || expr == nullptr) {
return;
return false;
}
if (expr->kind == Ast_CompoundLit && is_type_slice(type)) {
ast_node(cl, CompoundLit, expr);
if (cl->elems.count == 0) {
return;
return false;
}
unsafe_return_error(o, "a compound literal of a slice");
} else if (expr->kind == Ast_UnaryExpr && expr->UnaryExpr.op.kind == Token_And) {
@@ -2689,7 +2688,7 @@ void check_unsafe_return(Operand const &o, Type *type, Ast *expr) {
}
} else if (o.mode == Addressing_Constant && is_type_slice(type)) {
if (is_load_directive_call(o.expr)) {
return;
return false;
}
ERROR_BLOCK();
@@ -2702,11 +2701,12 @@ void check_unsafe_return(Operand const &o, Type *type, Ast *expr) {
ast_node(fv, FieldValue, elem);
Entity *e = entity_of_node(fv->field);
if (e != nullptr) {
check_unsafe_return(o, e->type, fv->value);
reported |= check_unsafe_return(o, e->type, fv->value);
}
}
}
}
return reported;
}
gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
@@ -2768,26 +2768,6 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
}
}
}
for (Operand &o : operands) {
if (o.expr == nullptr) {
continue;
}
Ast *expr = unparen_expr(o.expr);
while (expr->kind == Ast_CallExpr && expr->CallExpr.proc->tav.mode == Addressing_Type) {
if (expr->CallExpr.args.count != 1) {
break;
}
Ast *arg = expr->CallExpr.args[0];
if (arg->kind == Ast_FieldValue || !are_types_identical(arg->tav.type, expr->tav.type)) {
break;
}
expr = unparen_expr(arg);
}
check_unsafe_return(o, o.type, expr);
}
}
gb_internal void check_for_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
+2
View File
@@ -13,6 +13,7 @@ gb_internal void check_expr_or_type(CheckerContext *c, Operand *operand, Ast *ex
gb_internal void add_comparison_procedures_for_fields(CheckerContext *c, Type *t);
gb_internal Type *check_type(CheckerContext *ctx, Ast *e);
gb_internal void check_procedure_later(Checker *c, ProcInfo *info);
gb_internal void check_proc_escapes(Type *type, Ast *body);
gb_internal bool is_operand_value(Operand o) {
switch (o.mode) {
@@ -4907,6 +4908,7 @@ gb_internal DECL_ATTRIBUTE_PROC(asm_decl_attribute) {
#include "name_canonicalization.cpp"
#include "check_decl.cpp"
#include "check_stmt.cpp"
#include "check_escape.cpp"