Skip to content

Commit

Permalink
defer is broken when placed before a $foreach #1912
Browse files Browse the repository at this point in the history
  • Loading branch information
lerno committed Jan 31, 2025
1 parent 7dd9256 commit 9092def
Show file tree
Hide file tree
Showing 15 changed files with 432 additions and 104 deletions.
16 changes: 15 additions & 1 deletion lib/std/collections/list.c3
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,20 @@ struct List (Printable)
Type *entries;
}

<*
@param initial_capacity "The initial capacity to reserve"
@param [&inout] allocator "The allocator to use, defaults to the heap allocator"
*>
fn List* List.init(&self, usz initial_capacity = 16, Allocator allocator)
{
self.allocator = allocator;
self.size = 0;
self.capacity = 0;
self.entries = null;
self.reserve(initial_capacity);
return self;
}

<*
@param initial_capacity "The initial capacity to reserve"
@param [&inout] allocator "The allocator to use, defaults to the heap allocator"
Expand All @@ -40,7 +54,7 @@ fn List* List.new_init(&self, usz initial_capacity = 16, Allocator allocator = a
*>
fn List* List.temp_init(&self, usz initial_capacity = 16)
{
return self.new_init(initial_capacity, allocator::temp()) @inline;
return self.init(initial_capacity, allocator::temp()) @inline;
}

<*
Expand Down
1 change: 1 addition & 0 deletions releasenotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
- Missing error when placing a single statement for-body on a new row #1892.
- Fix bug where in dead code, only the first statement would be turned into a nop.
- Remove unused $inline argument to mem::copy.
- defer is broken when placed before a $foreach #1912.

### Stdlib changes
- Added '%h' and '%H' for printing out binary data in hexadecimal using the formatter.
Expand Down
8 changes: 4 additions & 4 deletions resources/examples/contextfree/boolerr.c3
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import libc;
import std::io;
import std::collections::maybe;

def MaybeString = Maybe(<String>);
def MaybeHead = Maybe(<Head>);
def new_head_val = maybe::value(<Head>);
def new_string_val = maybe::value(<String>);
def MaybeString = Maybe<[String]>;
def MaybeHead = Maybe<[Head]>;
def new_head_val = maybe::value<[Head]>;
def new_string_val = maybe::value<[String]>;

fault TitleResult
{
Expand Down
9 changes: 9 additions & 0 deletions src/compiler/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,15 @@ bool ast_is_compile_time(Ast *ast)
return expr_is_runtime_const(ast->return_stmt.expr);
case AST_EXPR_STMT:
return expr_is_runtime_const(ast->expr_stmt);
case AST_CT_COMPOUND_STMT:
{
AstId current = ast->ct_compound_stmt;
while (current)
{
if (!ast_is_compile_time(ast_next(&current))) return false;
}
return true;
}
case AST_COMPOUND_STMT:
{
AstId current = ast->compound_stmt.first_stmt;
Expand Down
1 change: 1 addition & 0 deletions src/compiler/c_codegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,7 @@ static void c_emit_stmt(GenContext *c, Ast *stmt)
break;
case AST_CT_ECHO_STMT:
break;
case AST_CT_COMPOUND_STMT:
case AST_CT_ELSE_STMT:
break;
case AST_CT_FOREACH_STMT:
Expand Down
1 change: 1 addition & 0 deletions src/compiler/compiler_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -1488,6 +1488,7 @@ typedef struct Ast_
AstAssertStmt assert_stmt; // 16
AstCaseStmt case_stmt; // 32
AstCompoundStmt compound_stmt; // 12
AstId ct_compound_stmt;
AstContinueBreakStmt contbreak_stmt;// 24
AstContractStmt contract_stmt; // 32
AstDocFault contract_fault; // 24
Expand Down
3 changes: 3 additions & 0 deletions src/compiler/copying.c
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,9 @@ Ast *ast_copy_deep(CopyStruct *c, Ast *source)
MACRO_COPY_EXPRID(ast->ct_switch_stmt.cond);
MACRO_COPY_AST_LIST(ast->ct_switch_stmt.body);
break;
case AST_CT_COMPOUND_STMT:
MACRO_COPY_ASTID(ast->ct_compound_stmt);
break;
case AST_DECLARE_STMT:
MACRO_COPY_DECL(ast->declare_stmt);
break;
Expand Down
13 changes: 7 additions & 6 deletions src/compiler/enums.h
Original file line number Diff line number Diff line change
Expand Up @@ -189,15 +189,19 @@ typedef enum
typedef enum
{
AST_POISONED,
AST_ASM_STMT,
AST_ASM_BLOCK_STMT,
AST_ASM_LABEL,
AST_ASM_STMT,
AST_ASSERT_STMT,
AST_BLOCK_EXIT_STMT,
AST_BREAK_STMT,
AST_CASE_STMT,
AST_COMPOUND_STMT,
AST_CONTINUE_STMT,
AST_CONTRACT,
AST_CONTRACT_FAULT,
AST_CT_ASSERT,
AST_CT_COMPOUND_STMT,
AST_CT_ECHO_STMT,
AST_CT_ELSE_STMT,
AST_CT_FOREACH_STMT,
Expand All @@ -209,17 +213,14 @@ typedef enum
AST_DEFAULT_STMT,
AST_DEFER_STMT,
AST_EXPR_STMT,
AST_FOR_STMT,
AST_FOREACH_STMT,
AST_FOR_STMT,
AST_IF_CATCH_SWITCH_STMT,
AST_IF_STMT,
AST_NEXTCASE_STMT,
AST_NOP_STMT,
AST_RETURN_STMT,
AST_BLOCK_EXIT_STMT,
AST_SWITCH_STMT,
AST_NEXTCASE_STMT,
AST_CONTRACT,
AST_CONTRACT_FAULT,
} AstKind;

typedef enum
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/llvm_codegen_stmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ void llvm_emit_compound_stmt(GenContext *c, Ast *ast)
if (old_block) c->debug.block_stack = old_block;
}


void llvm_emit_local_static(GenContext *c, Decl *decl, BEValue *value)
{
// In defers we might already have generated this variable.
Expand Down Expand Up @@ -1690,6 +1691,9 @@ void llvm_emit_stmt(GenContext *c, Ast *ast)
case AST_BLOCK_EXIT_STMT:
llvm_emit_block_exit_return(c, ast);
break;
case AST_CT_COMPOUND_STMT:
llvm_emit_statement_chain(c, ast->ct_compound_stmt);
break;
case AST_COMPOUND_STMT:
llvm_emit_compound_stmt(c, ast);
break;
Expand Down
8 changes: 4 additions & 4 deletions src/compiler/parse_stmt.c
Original file line number Diff line number Diff line change
Expand Up @@ -1100,9 +1100,9 @@ static inline Ast* parse_ct_foreach_stmt(ParseContext *c)
TRY_CONSUME_OR_RET(TOKEN_COLON, "Expected ':'.", poisoned_ast);
ASSIGN_EXPRID_OR_RET(ast->ct_foreach_stmt.expr, parse_expr(c), poisoned_ast);
CONSUME_OR_RET(TOKEN_RPAREN, poisoned_ast);
Ast *body = new_ast(AST_COMPOUND_STMT, ast->span);
Ast *body = new_ast(AST_CT_COMPOUND_STMT, ast->span);
ast->ct_foreach_stmt.body = astid(body);
AstId *current = &body->compound_stmt.first_stmt;
AstId *current = &body->ct_compound_stmt;
while (!try_consume(c, TOKEN_CT_ENDFOREACH))
{
ASSIGN_AST_OR_RET(Ast *stmt, parse_stmt(c), poisoned_ast);
Expand Down Expand Up @@ -1140,9 +1140,9 @@ static inline Ast* parse_ct_for_stmt(ParseContext *c)

CONSUME_OR_RET(TOKEN_RPAREN, poisoned_ast);

Ast *body = new_ast(AST_COMPOUND_STMT, ast->span);
Ast *body = new_ast(AST_CT_COMPOUND_STMT, ast->span);
ast->for_stmt.body = astid(body);
AstId *current = &body->compound_stmt.first_stmt;
AstId *current = &body->ct_compound_stmt;
while (!try_consume(c, TOKEN_CT_ENDFOR))
{
ASSIGN_AST_OR_RET(Ast *stmt, parse_stmt(c), poisoned_ast);
Expand Down
136 changes: 73 additions & 63 deletions src/compiler/sema_decls.c
Original file line number Diff line number Diff line change
Expand Up @@ -3763,6 +3763,78 @@ INLINE bool sema_analyse_macro_body(SemaContext *context, Decl **body_parameters
}
return true;
}

static inline bool sema_check_body_const(SemaContext *context, Ast *body)
{
while (body)
{
switch (body->ast_kind)
{
case AST_CT_ASSERT:
case AST_CT_ECHO_STMT:
case AST_CT_FOREACH_STMT:
case AST_CT_FOR_STMT:
case AST_CT_IF_STMT:
case AST_CT_SWITCH_STMT:
case AST_CT_ELSE_STMT:
case AST_DECLARE_STMT:
case AST_DECLS_STMT:
case AST_NOP_STMT:
body = astptrzero(body->next);
continue;
case AST_CT_COMPOUND_STMT:
if (!sema_check_body_const(context, body)) return false;
body = astptrzero(body->next);
continue;
case AST_RETURN_STMT:
if (!body->return_stmt.expr) RETURN_SEMA_ERROR(body, "The 'return' in an `@const` must provide a value, e.g. 'return Foo.typeid;'");
if (body->next) RETURN_SEMA_ERROR(body, "There should not be any statements after 'return'.");
body = NULL;
continue;
case AST_EXPR_STMT:
// For some cases we KNOW it's not correct.
switch (body->expr_stmt->expr_kind)
{
case EXPR_IDENTIFIER:
case EXPR_UNRESOLVED_IDENTIFIER:
case EXPR_LAMBDA:
case EXPR_FORCE_UNWRAP:
case EXPR_ASM:
case EXPR_EXPR_BLOCK:
case EXPR_TERNARY:
case EXPR_RETHROW:
break;
default:
body = astptrzero(body->next);
continue;
}
FALLTHROUGH;
case AST_POISONED:
case AST_ASM_STMT:
case AST_ASM_LABEL:
case AST_ASM_BLOCK_STMT:
case AST_ASSERT_STMT:
case AST_BREAK_STMT:
case AST_CASE_STMT:
case AST_COMPOUND_STMT:
case AST_CONTINUE_STMT:
case AST_DEFAULT_STMT:
case AST_DEFER_STMT:
case AST_FOR_STMT:
case AST_FOREACH_STMT:
case AST_IF_CATCH_SWITCH_STMT:
case AST_IF_STMT:
case AST_BLOCK_EXIT_STMT:
case AST_SWITCH_STMT:
case AST_NEXTCASE_STMT:
case AST_CONTRACT:
case AST_CONTRACT_FAULT:
RETURN_SEMA_ERROR(body, "Only 'return' and compile time statements are allowed in an '@const' macro.");
}
UNREACHABLE
}
return true;
}
static inline bool sema_analyse_macro(SemaContext *context, Decl *decl, bool *erase_decl)
{
decl->func_decl.unit = context->unit;
Expand Down Expand Up @@ -3800,69 +3872,7 @@ static inline bool sema_analyse_macro(SemaContext *context, Decl *decl, bool *er
ASSERT(body->ast_kind == AST_COMPOUND_STMT);
body = astptrzero(body->compound_stmt.first_stmt);
if (!body) RETURN_SEMA_ERROR(decl, "'@const' macros cannot have an empty body.");
while (body)
{
switch (body->ast_kind)
{
case AST_CT_ASSERT:
case AST_CT_ECHO_STMT:
case AST_CT_FOREACH_STMT:
case AST_CT_FOR_STMT:
case AST_CT_IF_STMT:
case AST_CT_SWITCH_STMT:
case AST_CT_ELSE_STMT:
case AST_DECLARE_STMT:
case AST_DECLS_STMT:
case AST_NOP_STMT:
body = astptrzero(body->next);
continue;
case AST_RETURN_STMT:
if (!body->return_stmt.expr) RETURN_SEMA_ERROR(body, "The 'return' in an `@const` must provide a value, e.g. 'return Foo.typeid;'");
if (body->next) RETURN_SEMA_ERROR(body, "There should not be any statements after 'return'.");
body = NULL;
continue;
case AST_EXPR_STMT:
// For some cases we KNOW it's not correct.
switch (body->expr_stmt->expr_kind)
{
case EXPR_IDENTIFIER:
case EXPR_UNRESOLVED_IDENTIFIER:
case EXPR_LAMBDA:
case EXPR_FORCE_UNWRAP:
case EXPR_ASM:
case EXPR_EXPR_BLOCK:
case EXPR_TERNARY:
case EXPR_RETHROW:
break;
default:
body = astptrzero(body->next);
continue;
}
FALLTHROUGH;
case AST_POISONED:
case AST_ASM_STMT:
case AST_ASM_LABEL:
case AST_ASM_BLOCK_STMT:
case AST_ASSERT_STMT:
case AST_BREAK_STMT:
case AST_CASE_STMT:
case AST_COMPOUND_STMT:
case AST_CONTINUE_STMT:
case AST_DEFAULT_STMT:
case AST_DEFER_STMT:
case AST_FOR_STMT:
case AST_FOREACH_STMT:
case AST_IF_CATCH_SWITCH_STMT:
case AST_IF_STMT:
case AST_BLOCK_EXIT_STMT:
case AST_SWITCH_STMT:
case AST_NEXTCASE_STMT:
case AST_CONTRACT:
case AST_CONTRACT_FAULT:
RETURN_SEMA_ERROR(body, "Only 'return' and compile time statements are allowed in an '@const' macro.");
}
UNREACHABLE
}
sema_check_body_const(context, body);
}
decl->type = type_void;
return true;
Expand Down
3 changes: 3 additions & 0 deletions src/compiler/sema_liveness.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ static void sema_trace_stmt_liveness(Ast *ast)
case AST_NOP_STMT:
case AST_ASM_LABEL:
return;
case AST_CT_COMPOUND_STMT:
sema_trace_stmt_chain_liveness(ast->ct_compound_stmt);
return;
case AST_COMPOUND_STMT:
sema_trace_stmt_chain_liveness(ast->compound_stmt.first_stmt);
return;
Expand Down
Loading

0 comments on commit 9092def

Please sign in to comment.