Issue where a lambda wasn't correctly registered as external. #1408

This commit is contained in:
Christoffer Lerno
2024-09-03 23:25:47 +02:00
parent bbc199cda3
commit 59ff94c005
3 changed files with 49 additions and 36 deletions

View File

@@ -6,7 +6,7 @@
*None yet*
### Fixes
*None yet*
- Issue where a lambda wasn't correctly registered as external. #1408
### Stdlib changes
*None yet*

View File

@@ -8772,7 +8772,7 @@ static inline bool sema_expr_analyse_lambda(SemaContext *context, Type *target_t
decl->alignment = type_alloca_alignment(decl->type);
// We will actually compile this into any module using it (from a macro) by necessity,
// so we'll declare it as weak and externally visible.
if (context->compilation_unit != decl->unit) decl->is_external_visible = true;
unit_register_external_symbol(context, decl);
// Before function analysis, lambda evaluation is deferred
if (unit->module->stage < ANALYSIS_FUNCTIONS)

View File

@@ -24,7 +24,7 @@ static inline bool sema_analyse_return_stmt(SemaContext *context, Ast *statement
static inline bool sema_analyse_switch_stmt(SemaContext *context, Ast *statement);
static inline bool sema_return_optional_check_is_valid_in_scope(SemaContext *context, Expr *ret_expr);
static inline bool sema_defer_by_result(AstId defer_top, AstId defer_bottom);
static inline bool sema_defer_has_try_or_catch(AstId defer_top, AstId defer_bottom);
static inline bool sema_analyse_block_exit_stmt(SemaContext *context, Ast *statement);
static inline bool sema_analyse_defer_stmt_body(SemaContext *context, Ast *statement);
static inline bool sema_analyse_for_cond(SemaContext *context, ExprId *cond_ref, bool *infinite);
@@ -157,13 +157,9 @@ static inline bool sema_analyse_break_stmt(SemaContext *context, Ast *statement)
{
if (context_labels_exist_in_scope(context))
{
SEMA_ERROR(statement, "Unlabelled 'break' is not allowed here.");
RETURN_SEMA_ERROR(statement, "Unlabelled 'break' is not allowed here.");
}
else
{
SEMA_ERROR(statement, "There is no valid target for 'break', did you make a mistake?");
}
return false;
RETURN_SEMA_ERROR(statement, "There is no valid target for 'break', did you make a mistake?");
}
// Is jump, and set it as resolved.
@@ -221,8 +217,7 @@ static inline bool sema_analyse_continue_stmt(SemaContext *context, Ast *stateme
// If we have a plain continue and no continue label, we just failed.
if (!context->continue_target && !statement->contbreak_stmt.label.name)
{
SEMA_ERROR(statement, "'continue' is not allowed here.");
return false;
RETURN_SEMA_ERROR(statement, "'continue' is not allowed here.");
}
AstId defer_id;
@@ -233,11 +228,11 @@ static inline bool sema_analyse_continue_stmt(SemaContext *context, Ast *stateme
ASSIGN_DECL_OR_RET(Decl *target, sema_analyse_label(context, statement), false);
defer_id = target->label.defer;
parent = astptr(target->label.parent);
// Continue can only be used with "for" statements, skipping the "do { };" statement
if (!ast_supports_continue(parent))
{
SEMA_ERROR(statement, "'continue' may only be used with 'for', 'while' and 'do-while' statements.");
return false;
RETURN_SEMA_ERROR(statement, "'continue' may only be used with 'for', 'while' and 'do-while' statements.");
}
}
else
@@ -256,24 +251,37 @@ static inline bool sema_analyse_continue_stmt(SemaContext *context, Ast *stateme
return true;
}
/**
* If we have "if (catch x)", then we want to unwrap x in the else clause.
**/
static void sema_unwrappable_from_catch_in_else(SemaContext *c, Expr *cond)
{
assert(cond->expr_kind == EXPR_COND);
assert(cond->expr_kind == EXPR_COND && "Assumed cond");
Expr *last = VECLAST(cond->cond_expr);
assert(last);
// Dive into any cast, because it might have been cast into boolean.
while (last->expr_kind == EXPR_CAST)
{
last = exprptr(last->cast_expr.expr);
}
if (!last || last->expr_kind != EXPR_CATCH_UNWRAP) return;
// Skip any non-unwraps
if (last->expr_kind != EXPR_CATCH_UNWRAP) return;
// If we have "if (catch x)" then this will unwrap x in the
// else branch.
FOREACH(Expr *, expr, last->catch_unwrap_expr.exprs)
{
if (expr->expr_kind != EXPR_IDENTIFIER) continue;
Decl *decl = expr->identifier_expr.decl;
if (decl->decl_kind != DECL_VAR) continue;
assert(decl->type->type_kind == TYPE_OPTIONAL && "The variable should always be optional at this point.");
// Note that we could possibly have "if (catch x, x)" and in this case we'd
// unwrap twice, but that isn't really a problem.
// 5. Locals and globals may be unwrapped
switch (decl->var.kind)
{
@@ -284,14 +292,15 @@ static void sema_unwrappable_from_catch_in_else(SemaContext *c, Expr *cond)
default:
continue;
}
}
}
}
// --- Sema analyse stmts
/**
* Turn a "require" or "ensure" into a contract in the callee.
*/
static inline bool assert_create_from_contract(SemaContext *context, Ast *directive, AstId **asserts, SourceSpan evaluation_location)
{
directive = copy_ast_single(directive);
@@ -300,21 +309,21 @@ static inline bool assert_create_from_contract(SemaContext *context, Ast *direct
FOREACH(Expr *, expr, declexpr->expression_list)
{
if (expr->expr_kind == EXPR_DECL)
{
SEMA_ERROR(expr, "Only expressions are allowed.");
return false;
}
if (expr->expr_kind == EXPR_DECL) RETURN_SEMA_ERROR(expr, "Only expressions are allowed in contracts.");
CondResult result = COND_MISSING;
if (!sema_analyse_cond_expr(context, expr, &result)) return false;
const char *comment = directive->contract_stmt.contract.comment;
if (!comment) comment = directive->contract_stmt.contract.expr_string;
if (result == COND_TRUE) continue;
if (result == COND_FALSE)
switch (result)
{
sema_error_at(context, evaluation_location.a ? evaluation_location : expr->span, "%s", comment);
return false;
case COND_TRUE:
continue;
case COND_FALSE:
sema_error_at(context, evaluation_location.a ? evaluation_location : expr->span, "%s", comment);
return false;
case COND_MISSING:
break;
}
Ast *assert = new_ast(AST_ASSERT_STMT, expr->span);
assert->assert_stmt.is_ensure = true;
@@ -327,7 +336,8 @@ static inline bool assert_create_from_contract(SemaContext *context, Ast *direct
return true;
}
static inline bool sema_defer_by_result(AstId defer_top, AstId defer_bottom)
// Check whether a defer chain contains a try or a catch.
static inline bool sema_defer_has_try_or_catch(AstId defer_top, AstId defer_bottom)
{
AstId first = 0;
while (defer_bottom != defer_top)
@@ -339,17 +349,21 @@ static inline bool sema_defer_by_result(AstId defer_top, AstId defer_bottom)
return false;
}
// Print defers at return (from macro/block or from function)
static inline void sema_inline_return_defers(SemaContext *context, Ast *stmt, AstId defer_top, AstId defer_bottom)
{
// Store the cleanup defers, which will happen on try.
stmt->return_stmt.cleanup = context_get_defers(context, defer_top, defer_bottom, true);
if (stmt->return_stmt.expr && IS_OPTIONAL(stmt->return_stmt.expr) && sema_defer_by_result(context->active_scope.defer_last, context->block_return_defer))
// If we have an optional return, then we create a cleanup_fail
if (stmt->return_stmt.expr && IS_OPTIONAL(stmt->return_stmt.expr)
&& sema_defer_has_try_or_catch(context->active_scope.defer_last, context->block_return_defer))
{
stmt->return_stmt.cleanup_fail = context_get_defers(context, context->active_scope.defer_last, context->block_return_defer, false);
return;
}
else
{
stmt->return_stmt.cleanup_fail = stmt->return_stmt.cleanup ? astid(copy_ast_defer(astptr(stmt->return_stmt.cleanup))) : 0;
}
// Otherwise we make the cleanup fail be the same as the cleanup.
stmt->return_stmt.cleanup_fail = stmt->return_stmt.cleanup ? astid(copy_ast_defer(astptr(stmt->return_stmt.cleanup))) : 0;
}
static inline bool sema_return_optional_check_is_valid_in_scope(SemaContext *context, Expr *ret_expr)
@@ -537,8 +551,7 @@ static inline bool sema_analyse_return_stmt(SemaContext *context, Ast *statement
{
if (context->active_scope.in_defer)
{
SEMA_ERROR(statement, "Return is not allowed inside of a defer.");
return false;
RETURN_SEMA_ERROR(statement, "Return is not allowed inside of a defer.");
}
// This might be a return in a function block or a macro which must be treated differently.
@@ -546,6 +559,7 @@ static inline bool sema_analyse_return_stmt(SemaContext *context, Ast *statement
{
return sema_analyse_block_exit_stmt(context, statement);
}
// 1. We mark that the current scope ends with a jump.
context->active_scope.jump_end = true;
@@ -553,7 +567,6 @@ static inline bool sema_analyse_return_stmt(SemaContext *context, Ast *statement
assert(expected_rtype && "We should always have known type from a function return.");
Expr *return_expr = statement->return_stmt.expr;
if (return_expr)
{
if (!sema_analyse_expr_rhs(context, expected_rtype, return_expr, type_is_optional(expected_rtype), NULL, false)) return false;