add labeled loops, labeled break, labeled continue. remove goto

closes #346
closes #630

regression: translate-c can no longer translate switch statements.
after #629 we can ressurect and modify the code to utilize arbitrarily
returning from blocks.
This commit is contained in:
Andrew Kelley 2017-12-20 22:55:24 -05:00
parent d686113bd2
commit 8bc523219c
15 changed files with 345 additions and 694 deletions

View File

@ -5847,11 +5847,9 @@ ParamDeclList = "(" list(ParamDecl, ",") ")"
ParamDecl = option("noalias" | "comptime") option(Symbol ":") (TypeExpr | "...")
Block = "{" many(Statement) option(Expression) "}"
Block = option(Symbol ":") "{" many(Statement) option(Expression) "}"
Statement = Label | LocalVarDecl ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";"
Label = Symbol ":"
Statement = LocalVarDecl ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";"
TypeExpr = PrefixOpExpression | "var"
@ -5891,13 +5889,13 @@ SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Sy
SwitchItem = Expression | (Expression "..." Expression)
ForExpression(body) = option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body))
ForExpression(body) = option(Symbol ":") option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body))
BoolOrExpression = BoolAndExpression "or" BoolOrExpression | BoolAndExpression
ReturnExpression = option("%") "return" option(Expression)
BreakExpression = "break" option(Expression)
BreakExpression = "break" option(":" Symbol) option(Expression)
Defer(body) = option("%") "defer" body
@ -5907,7 +5905,7 @@ TryExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|")
TestExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body option("else" BlockExpression(body))
WhileExpression(body) = option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body))
WhileExpression(body) = option(Symbol ":") option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body))
BoolAndExpression = ComparisonExpression "and" BoolAndExpression | ComparisonExpression
@ -5955,15 +5953,13 @@ StructLiteralField = "." Symbol "=" Expression
PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "%" | "%%" | "??" | "-%"
PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl
PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl | ("continue" option(":" Symbol))
ArrayType : "[" option(Expression) "]" option("align" "(" Expression option(":" Integer ":" Integer) ")")) option("const") option("volatile") TypeExpr
GotoExpression = "goto" Symbol
GroupedExpression = "(" Expression ")"
KeywordLiteral = "true" | "false" | "null" | "continue" | "undefined" | "error" | "this" | "unreachable"
KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable"
ContainerDecl = option("extern" | "packed")
("struct" option(GroupedExpression) | "union" option("enum" option(GroupedExpression) | GroupedExpression) | ("enum" option(GroupedExpression)))

View File

@ -386,8 +386,6 @@ enum NodeType {
NodeTypeSwitchExpr,
NodeTypeSwitchProng,
NodeTypeSwitchRange,
NodeTypeLabel,
NodeTypeGoto,
NodeTypeCompTime,
NodeTypeBreak,
NodeTypeContinue,
@ -452,6 +450,7 @@ struct AstNodeParamDecl {
};
struct AstNodeBlock {
Buf *name;
ZigList<AstNode *> statements;
bool last_statement_is_result_expression;
};
@ -662,6 +661,7 @@ struct AstNodeTestExpr {
};
struct AstNodeWhileExpr {
Buf *name;
AstNode *condition;
Buf *var_symbol;
bool var_is_ptr;
@ -673,6 +673,7 @@ struct AstNodeWhileExpr {
};
struct AstNodeForExpr {
Buf *name;
AstNode *array_expr;
AstNode *elem_node; // always a symbol
AstNode *index_node; // always a symbol, might be null
@ -704,11 +705,6 @@ struct AstNodeLabel {
Buf *name;
};
struct AstNodeGoto {
Buf *name;
bool is_inline;
};
struct AstNodeCompTime {
AstNode *expr;
};
@ -836,11 +832,14 @@ struct AstNodeBoolLiteral {
};
struct AstNodeBreakExpr {
Buf *name;
AstNode *expr; // may be null
};
struct AstNodeContinueExpr {
Buf *name;
};
struct AstNodeUnreachableExpr {
};
@ -886,7 +885,6 @@ struct AstNode {
AstNodeSwitchProng switch_prong;
AstNodeSwitchRange switch_range;
AstNodeLabel label;
AstNodeGoto goto_expr;
AstNodeCompTime comptime_expr;
AstNodeAsmExpr asm_expr;
AstNodeFieldAccessExpr field_access_expr;
@ -1741,6 +1739,7 @@ struct ScopeCImport {
struct ScopeLoop {
Scope base;
Buf *name;
IrBasicBlock *break_block;
IrBasicBlock *continue_block;
IrInstruction *is_comptime;

View File

@ -144,9 +144,15 @@ ScopeCImport *create_cimport_scope(AstNode *node, Scope *parent) {
}
ScopeLoop *create_loop_scope(AstNode *node, Scope *parent) {
assert(node->type == NodeTypeWhileExpr || node->type == NodeTypeForExpr);
ScopeLoop *scope = allocate<ScopeLoop>(1);
init_scope(&scope->base, ScopeIdLoop, node, parent);
if (node->type == NodeTypeWhileExpr) {
scope->name = node->data.while_expr.name;
} else if (node->type == NodeTypeForExpr) {
scope->name = node->data.for_expr.name;
} else {
zig_unreachable();
}
return scope;
}
@ -2916,8 +2922,6 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
case NodeTypeSwitchExpr:
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
case NodeTypeLabel:
case NodeTypeGoto:
case NodeTypeBreak:
case NodeTypeContinue:
case NodeTypeUnreachable:

View File

@ -215,10 +215,6 @@ static const char *node_type_str(NodeType node_type) {
return "SwitchProng";
case NodeTypeSwitchRange:
return "SwitchRange";
case NodeTypeLabel:
return "Label";
case NodeTypeGoto:
return "Goto";
case NodeTypeCompTime:
return "CompTime";
case NodeTypeBreak:
@ -391,7 +387,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
switch (node->type) {
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
case NodeTypeLabel:
case NodeTypeStructValueField:
zig_unreachable();
case NodeTypeRoot:
@ -470,6 +465,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
break;
}
case NodeTypeBlock:
if (node->data.block.name != nullptr) {
fprintf(ar->f, "%s: ", buf_ptr(node->data.block.name));
}
if (node->data.block.statements.length == 0) {
fprintf(ar->f, "{}");
break;
@ -478,13 +476,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
ar->indent += ar->indent_size;
for (size_t i = 0; i < node->data.block.statements.length; i += 1) {
AstNode *statement = node->data.block.statements.at(i);
if (statement->type == NodeTypeLabel) {
ar->indent -= ar->indent_size;
print_indent(ar);
fprintf(ar->f, "%s:\n", buf_ptr(statement->data.label.name));
ar->indent += ar->indent_size;
continue;
}
print_indent(ar);
render_node_grouped(ar, statement);
if (!(i == node->data.block.statements.length - 1 &&
@ -515,6 +506,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
case NodeTypeBreak:
{
fprintf(ar->f, "break");
if (node->data.break_expr.name != nullptr) {
fprintf(ar->f, " :%s", buf_ptr(node->data.break_expr.name));
}
if (node->data.break_expr.expr) {
fprintf(ar->f, " ");
render_node_grouped(ar, node->data.break_expr.expr);
@ -828,6 +822,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
}
case NodeTypeWhileExpr:
{
if (node->data.while_expr.name != nullptr) {
fprintf(ar->f, "%s: ", buf_ptr(node->data.while_expr.name));
}
const char *inline_str = node->data.while_expr.is_inline ? "inline " : "";
fprintf(ar->f, "%swhile (", inline_str);
render_node_grouped(ar, node->data.while_expr.condition);
@ -957,11 +954,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
fprintf(ar->f, "}");
break;
}
case NodeTypeGoto:
{
fprintf(ar->f, "goto %s", buf_ptr(node->data.goto_expr.name));
break;
}
case NodeTypeCompTime:
{
fprintf(ar->f, "comptime ");
@ -970,6 +962,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
}
case NodeTypeForExpr:
{
if (node->data.for_expr.name != nullptr) {
fprintf(ar->f, "%s: ", buf_ptr(node->data.for_expr.name));
}
const char *inline_str = node->data.for_expr.is_inline ? "inline " : "";
fprintf(ar->f, "%sfor (", inline_str);
render_node_grouped(ar, node->data.for_expr.array_expr);
@ -995,6 +990,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
case NodeTypeContinue:
{
fprintf(ar->f, "continue");
if (node->data.continue_expr.name != nullptr) {
fprintf(ar->f, " :%s", buf_ptr(node->data.continue_expr.name));
}
break;
}
case NodeTypeUnreachable:

View File

@ -3511,29 +3511,6 @@ static VariableTableEntry *ir_create_var(IrBuilder *irb, AstNode *node, Scope *s
return var;
}
static LabelTableEntry *find_label(IrExecutable *exec, Scope *scope, Buf *name) {
while (scope) {
if (scope->id == ScopeIdBlock) {
ScopeBlock *block_scope = (ScopeBlock *)scope;
auto entry = block_scope->label_table.maybe_get(name);
if (entry)
return entry->value;
}
scope = scope->parent;
}
return nullptr;
}
static ScopeBlock *find_block_scope(IrExecutable *exec, Scope *scope) {
while (scope) {
if (scope->id == ScopeIdBlock)
return (ScopeBlock *)scope;
scope = scope->parent;
}
return nullptr;
}
static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node) {
assert(block_node->type == NodeTypeBlock);
@ -3557,38 +3534,6 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode
for (size_t i = 0; i < block_node->data.block.statements.length; i += 1) {
AstNode *statement_node = block_node->data.block.statements.at(i);
if (statement_node->type == NodeTypeLabel) {
Buf *label_name = statement_node->data.label.name;
IrBasicBlock *label_block = ir_build_basic_block(irb, child_scope, buf_ptr(label_name));
LabelTableEntry *label = allocate<LabelTableEntry>(1);
label->decl_node = statement_node;
label->bb = label_block;
irb->exec->all_labels.append(label);
LabelTableEntry *existing_label = find_label(irb->exec, child_scope, label_name);
if (existing_label) {
ErrorMsg *msg = add_node_error(irb->codegen, statement_node,
buf_sprintf("duplicate label name '%s'", buf_ptr(label_name)));
add_error_note(irb->codegen, msg, existing_label->decl_node, buf_sprintf("other label here"));
return irb->codegen->invalid_instruction;
} else {
ScopeBlock *scope_block = find_block_scope(irb->exec, child_scope);
scope_block->label_table.put(label_name, label);
}
if (!is_continuation_unreachable) {
// fall through into new labeled basic block
IrInstruction *is_comptime = ir_mark_gen(ir_build_const_bool(irb, child_scope, statement_node,
ir_should_inline(irb->exec, child_scope)));
ir_mark_gen(ir_build_br(irb, child_scope, statement_node, label_block, is_comptime));
}
ir_set_cursor_at_end(irb, label_block);
// a label is an entry point
is_continuation_unreachable = false;
continue;
}
IrInstruction *statement_value = ir_gen_node(irb, statement_node, child_scope);
is_continuation_unreachable = instr_is_unreachable(statement_value);
if (is_continuation_unreachable) {
@ -6000,22 +5945,6 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
}
static IrInstruction *ir_gen_goto(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeGoto);
// make a placeholder unreachable statement and a note to come back and
// replace the instruction with a branch instruction
IrGotoItem *goto_item = irb->exec->goto_list.add_one();
goto_item->bb = irb->current_basic_block;
goto_item->instruction_index = irb->current_basic_block->instruction_list.length;
goto_item->source_node = node;
goto_item->scope = scope;
// we don't know if we need to generate defer expressions yet
// we do that later when we find out which label we're jumping to.
return ir_build_unreachable(irb, scope, node);
}
static IrInstruction *ir_gen_comptime(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval) {
assert(node->type == NodeTypeCompTime);
@ -6033,16 +5962,28 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *
Scope *search_scope = break_scope;
ScopeLoop *loop_scope;
bool saw_any_loop_scope = false;
for (;;) {
if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) {
add_node_error(irb->codegen, node, buf_sprintf("break expression outside loop"));
return irb->codegen->invalid_instruction;
if (saw_any_loop_scope) {
add_node_error(irb->codegen, node, buf_sprintf("labeled loop not found: '%s'", buf_ptr(node->data.break_expr.name)));
return irb->codegen->invalid_instruction;
} else {
add_node_error(irb->codegen, node, buf_sprintf("break expression outside loop"));
return irb->codegen->invalid_instruction;
}
} else if (search_scope->id == ScopeIdDeferExpr) {
add_node_error(irb->codegen, node, buf_sprintf("cannot break out of defer expression"));
return irb->codegen->invalid_instruction;
} else if (search_scope->id == ScopeIdLoop) {
loop_scope = (ScopeLoop *)search_scope;
break;
ScopeLoop *this_loop_scope = (ScopeLoop *)search_scope;
saw_any_loop_scope = true;
if (node->data.break_expr.name == nullptr ||
(this_loop_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_loop_scope->name)))
{
loop_scope = this_loop_scope;
break;
}
}
search_scope = search_scope->parent;
}
@ -6081,16 +6022,28 @@ static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *continue_scope, Ast
Scope *search_scope = continue_scope;
ScopeLoop *loop_scope;
bool saw_any_loop_scope = false;
for (;;) {
if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) {
add_node_error(irb->codegen, node, buf_sprintf("continue expression outside loop"));
return irb->codegen->invalid_instruction;
if (saw_any_loop_scope) {
add_node_error(irb->codegen, node, buf_sprintf("labeled loop not found: '%s'", buf_ptr(node->data.continue_expr.name)));
return irb->codegen->invalid_instruction;
} else {
add_node_error(irb->codegen, node, buf_sprintf("continue expression outside loop"));
return irb->codegen->invalid_instruction;
}
} else if (search_scope->id == ScopeIdDeferExpr) {
add_node_error(irb->codegen, node, buf_sprintf("cannot continue out of defer expression"));
return irb->codegen->invalid_instruction;
} else if (search_scope->id == ScopeIdLoop) {
loop_scope = (ScopeLoop *)search_scope;
break;
ScopeLoop *this_loop_scope = (ScopeLoop *)search_scope;
saw_any_loop_scope = true;
if (node->data.continue_expr.name == nullptr ||
(this_loop_scope->name != nullptr && buf_eql_buf(node->data.continue_expr.name, this_loop_scope->name)))
{
loop_scope = this_loop_scope;
break;
}
}
search_scope = search_scope->parent;
}
@ -6332,7 +6285,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
case NodeTypeStructField:
case NodeTypeLabel:
case NodeTypeFnDef:
case NodeTypeFnDecl:
case NodeTypeErrorValueDecl:
@ -6396,8 +6348,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
return ir_lval_wrap(irb, scope, ir_gen_test_expr(irb, scope, node), lval);
case NodeTypeSwitchExpr:
return ir_lval_wrap(irb, scope, ir_gen_switch_expr(irb, scope, node), lval);
case NodeTypeGoto:
return ir_lval_wrap(irb, scope, ir_gen_goto(irb, scope, node), lval);
case NodeTypeCompTime:
return ir_gen_comptime(irb, scope, node, lval);
case NodeTypeErrorType:
@ -6432,70 +6382,6 @@ static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope) {
return ir_gen_node_extra(irb, node, scope, LVAL_NONE);
}
static bool ir_goto_pass2(IrBuilder *irb) {
for (size_t i = 0; i < irb->exec->goto_list.length; i += 1) {
IrGotoItem *goto_item = &irb->exec->goto_list.at(i);
AstNode *source_node = goto_item->source_node;
// Since a goto will always end a basic block, we move the "current instruction"
// index back to over the placeholder unreachable instruction and begin overwriting
irb->current_basic_block = goto_item->bb;
irb->current_basic_block->instruction_list.resize(goto_item->instruction_index);
Buf *label_name = source_node->data.goto_expr.name;
// Search up the scope until we find one of these things:
// * A block scope with the label in it => OK
// * A defer expression scope => error, error, cannot leave defer expression
// * Top level scope => error, didn't find label
LabelTableEntry *label;
Scope *search_scope = goto_item->scope;
for (;;) {
if (search_scope == nullptr) {
add_node_error(irb->codegen, source_node,
buf_sprintf("no label in scope named '%s'", buf_ptr(label_name)));
return false;
} else if (search_scope->id == ScopeIdBlock) {
ScopeBlock *block_scope = (ScopeBlock *)search_scope;
auto entry = block_scope->label_table.maybe_get(label_name);
if (entry) {
label = entry->value;
break;
}
} else if (search_scope->id == ScopeIdDeferExpr) {
add_node_error(irb->codegen, source_node,
buf_sprintf("cannot goto out of defer expression"));
return false;
}
search_scope = search_scope->parent;
}
label->used = true;
IrInstruction *is_comptime = ir_build_const_bool(irb, goto_item->scope, source_node,
ir_should_inline(irb->exec, goto_item->scope) || source_node->data.goto_expr.is_inline);
if (!ir_gen_defers_for_block(irb, goto_item->scope, label->bb->scope, false)) {
add_node_error(irb->codegen, source_node,
buf_sprintf("no label in scope named '%s'", buf_ptr(label_name)));
return false;
}
ir_build_br(irb, goto_item->scope, source_node, label->bb, is_comptime);
}
for (size_t i = 0; i < irb->exec->all_labels.length; i += 1) {
LabelTableEntry *label = irb->exec->all_labels.at(i);
if (!label->used) {
add_node_error(irb->codegen, label->decl_node,
buf_sprintf("label '%s' defined but not used",
buf_ptr(label->decl_node->data.label.name)));
return false;
}
}
return true;
}
static void invalidate_exec(IrExecutable *exec) {
if (exec->invalid)
return;
@ -6532,11 +6418,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
ir_mark_gen(ir_build_return(irb, scope, result->source_node, result));
}
if (!ir_goto_pass2(irb)) {
invalidate_exec(ir_executable);
return false;
}
return true;
}

View File

@ -632,27 +632,6 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m
return node;
}
/*
GotoExpression = "goto" Symbol
*/
static AstNode *ast_parse_goto_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
Token *goto_token = &pc->tokens->at(*token_index);
if (goto_token->id == TokenIdKeywordGoto) {
*token_index += 1;
} else if (mandatory) {
ast_expect_token(pc, goto_token, TokenIdKeywordGoto);
zig_unreachable();
} else {
return nullptr;
}
AstNode *node = ast_create_node(pc, NodeTypeGoto, goto_token);
Token *dest_symbol = ast_eat_token(pc, token_index, TokenIdSymbol);
node->data.goto_expr.name = token_buf(dest_symbol);
return node;
}
/*
CompTimeExpression(body) = "comptime" body
*/
@ -676,8 +655,8 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b
}
/*
PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl
KeywordLiteral = "true" | "false" | "null" | "continue" | "undefined" | "error" | "this" | "unreachable"
PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl | ("continue" option(":" Symbol))
KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable"
*/
static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
Token *token = &pc->tokens->at(*token_index);
@ -721,6 +700,12 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo
} else if (token->id == TokenIdKeywordContinue) {
AstNode *node = ast_create_node(pc, NodeTypeContinue, token);
*token_index += 1;
Token *maybe_colon_token = &pc->tokens->at(*token_index);
if (maybe_colon_token->id == TokenIdColon) {
*token_index += 1;
Token *name = ast_eat_token(pc, token_index, TokenIdSymbol);
node->data.continue_expr.name = token_buf(name);
}
return node;
} else if (token->id == TokenIdKeywordUndefined) {
AstNode *node = ast_create_node(pc, NodeTypeUndefinedLiteral, token);
@ -770,10 +755,6 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo
return node;
}
AstNode *goto_node = ast_parse_goto_expr(pc, token_index, false);
if (goto_node)
return goto_node;
AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false);
if (grouped_expr_node) {
return grouped_expr_node;
@ -1488,7 +1469,7 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, size_t *token_index) {
}
/*
BreakExpression : "break" option(Expression)
BreakExpression = "break" option(":" Symbol) option(Expression)
*/
static AstNode *ast_parse_break_expr(ParseContext *pc, size_t *token_index) {
Token *token = &pc->tokens->at(*token_index);
@ -1498,8 +1479,15 @@ static AstNode *ast_parse_break_expr(ParseContext *pc, size_t *token_index) {
} else {
return nullptr;
}
AstNode *node = ast_create_node(pc, NodeTypeBreak, token);
Token *maybe_colon_token = &pc->tokens->at(*token_index);
if (maybe_colon_token->id == TokenIdColon) {
*token_index += 1;
Token *name = ast_eat_token(pc, token_index, TokenIdSymbol);
node->data.break_expr.name = token_buf(name);
}
node->data.break_expr.expr = ast_parse_expression(pc, token_index, false);
return node;
@ -1678,35 +1666,53 @@ static AstNode *ast_parse_bool_or_expr(ParseContext *pc, size_t *token_index, bo
}
/*
WhileExpression(body) = option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body))
WhileExpression(body) = option(Symbol ":") option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body))
*/
static AstNode *ast_parse_while_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
Token *first_token = &pc->tokens->at(*token_index);
Token *while_token;
size_t orig_token_index = *token_index;
bool is_inline;
if (first_token->id == TokenIdKeywordInline) {
while_token = &pc->tokens->at(*token_index + 1);
if (while_token->id == TokenIdKeywordWhile) {
is_inline = true;
*token_index += 2;
Token *name_token = nullptr;
Token *token = &pc->tokens->at(*token_index);
if (token->id == TokenIdSymbol) {
*token_index += 1;
Token *colon_token = &pc->tokens->at(*token_index);
if (colon_token->id == TokenIdColon) {
*token_index += 1;
name_token = token;
token = &pc->tokens->at(*token_index);
} else if (mandatory) {
ast_expect_token(pc, while_token, TokenIdKeywordWhile);
ast_expect_token(pc, colon_token, TokenIdColon);
zig_unreachable();
} else {
*token_index = orig_token_index;
return nullptr;
}
} else if (first_token->id == TokenIdKeywordWhile) {
while_token = first_token;
is_inline = false;
}
bool is_inline = false;
if (token->id == TokenIdKeywordInline) {
is_inline = true;
*token_index += 1;
token = &pc->tokens->at(*token_index);
}
Token *while_token;
if (token->id == TokenIdKeywordWhile) {
while_token = token;
*token_index += 1;
} else if (mandatory) {
ast_expect_token(pc, first_token, TokenIdKeywordWhile);
ast_expect_token(pc, token, TokenIdKeywordWhile);
zig_unreachable();
} else {
*token_index = orig_token_index;
return nullptr;
}
AstNode *node = ast_create_node(pc, NodeTypeWhileExpr, while_token);
if (name_token != nullptr) {
node->data.while_expr.name = token_buf(name_token);
}
node->data.while_expr.is_inline = is_inline;
ast_eat_token(pc, token_index, TokenIdLParen);
@ -1766,36 +1772,53 @@ static AstNode *ast_parse_symbol(ParseContext *pc, size_t *token_index) {
}
/*
ForExpression(body) = option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body))
ForExpression(body) = option(Symbol ":") option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body))
*/
static AstNode *ast_parse_for_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
Token *first_token = &pc->tokens->at(*token_index);
Token *for_token;
size_t orig_token_index = *token_index;
bool is_inline;
if (first_token->id == TokenIdKeywordInline) {
is_inline = true;
for_token = &pc->tokens->at(*token_index + 1);
if (for_token->id == TokenIdKeywordFor) {
*token_index += 2;
Token *name_token = nullptr;
Token *token = &pc->tokens->at(*token_index);
if (token->id == TokenIdSymbol) {
*token_index += 1;
Token *colon_token = &pc->tokens->at(*token_index);
if (colon_token->id == TokenIdColon) {
*token_index += 1;
name_token = token;
token = &pc->tokens->at(*token_index);
} else if (mandatory) {
ast_expect_token(pc, first_token, TokenIdKeywordFor);
ast_expect_token(pc, colon_token, TokenIdColon);
zig_unreachable();
} else {
*token_index = orig_token_index;
return nullptr;
}
} else if (first_token->id == TokenIdKeywordFor) {
for_token = first_token;
is_inline = false;
}
bool is_inline = false;
if (token->id == TokenIdKeywordInline) {
is_inline = true;
*token_index += 1;
token = &pc->tokens->at(*token_index);
}
Token *for_token;
if (token->id == TokenIdKeywordFor) {
for_token = token;
*token_index += 1;
} else if (mandatory) {
ast_expect_token(pc, first_token, TokenIdKeywordFor);
ast_expect_token(pc, token, TokenIdKeywordFor);
zig_unreachable();
} else {
*token_index = orig_token_index;
return nullptr;
}
AstNode *node = ast_create_node(pc, NodeTypeForExpr, for_token);
if (name_token != nullptr) {
node->data.for_expr.name = token_buf(name_token);
}
node->data.for_expr.is_inline = is_inline;
ast_eat_token(pc, token_index, TokenIdLParen);
@ -2125,32 +2148,6 @@ static AstNode *ast_parse_expression(ParseContext *pc, size_t *token_index, bool
/*
Label: token(Symbol) token(Colon)
*/
static AstNode *ast_parse_label(ParseContext *pc, size_t *token_index, bool mandatory) {
Token *symbol_token = &pc->tokens->at(*token_index);
if (symbol_token->id != TokenIdSymbol) {
if (mandatory) {
ast_expect_token(pc, symbol_token, TokenIdSymbol);
} else {
return nullptr;
}
}
Token *colon_token = &pc->tokens->at(*token_index + 1);
if (colon_token->id != TokenIdColon) {
if (mandatory) {
ast_expect_token(pc, colon_token, TokenIdColon);
} else {
return nullptr;
}
}
*token_index += 2;
AstNode *node = ast_create_node(pc, NodeTypeLabel, symbol_token);
node->data.label.name = token_buf(symbol_token);
return node;
}
static bool statement_terminates_without_semicolon(AstNode *node) {
switch (node->type) {
case NodeTypeIfBoolExpr:
@ -2175,7 +2172,6 @@ static bool statement_terminates_without_semicolon(AstNode *node) {
return node->data.defer.expr->type == NodeTypeBlock;
case NodeTypeSwitchExpr:
case NodeTypeBlock:
case NodeTypeLabel:
return true;
default:
return false;
@ -2183,27 +2179,48 @@ static bool statement_terminates_without_semicolon(AstNode *node) {
}
/*
Block = "{" many(Statement) option(Expression) "}"
Block = option(Symbol ":") "{" many(Statement) option(Expression) "}"
Statement = Label | VariableDeclaration ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";" | ExportDecl
*/
static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mandatory) {
size_t orig_token_index = *token_index;
Token *name_token = nullptr;
Token *last_token = &pc->tokens->at(*token_index);
if (last_token->id == TokenIdSymbol) {
*token_index += 1;
Token *colon_token = &pc->tokens->at(*token_index);
if (colon_token->id == TokenIdColon) {
*token_index += 1;
name_token = last_token;
last_token = &pc->tokens->at(*token_index);
} else if (mandatory) {
ast_expect_token(pc, colon_token, TokenIdColon);
zig_unreachable();
} else {
*token_index = orig_token_index;
return nullptr;
}
}
if (last_token->id != TokenIdLBrace) {
if (mandatory) {
ast_expect_token(pc, last_token, TokenIdLBrace);
} else {
*token_index = orig_token_index;
return nullptr;
}
}
*token_index += 1;
AstNode *node = ast_create_node(pc, NodeTypeBlock, last_token);
if (name_token != nullptr) {
node->data.block.name = token_buf(name_token);
}
for (;;) {
AstNode *statement_node = ast_parse_label(pc, token_index, false);
if (!statement_node)
statement_node = ast_parse_local_var_decl(pc, token_index);
AstNode *statement_node = ast_parse_local_var_decl(pc, token_index);
if (!statement_node)
statement_node = ast_parse_defer_expr(pc, token_index);
if (!statement_node)
@ -2225,9 +2242,7 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand
}
}
node->data.block.last_statement_is_result_expression = statement_node && !(
statement_node->type == NodeTypeLabel ||
statement_node->type == NodeTypeDefer);
node->data.block.last_statement_is_result_expression = statement_node && statement_node->type != NodeTypeDefer;
last_token = &pc->tokens->at(*token_index);
if (last_token->id == TokenIdRBrace) {
@ -2860,12 +2875,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
visit_field(&node->data.switch_range.start, visit, context);
visit_field(&node->data.switch_range.end, visit, context);
break;
case NodeTypeLabel:
// none
break;
case NodeTypeGoto:
// none
break;
case NodeTypeCompTime:
visit_field(&node->data.comptime_expr.expr, visit, context);
break;

View File

@ -104,10 +104,8 @@ static TransScopeRoot *trans_scope_root_create(Context *c);
static TransScopeWhile *trans_scope_while_create(Context *c, TransScope *parent_scope);
static TransScopeBlock *trans_scope_block_create(Context *c, TransScope *parent_scope);
static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scope, Buf *wanted_name);
static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope);
static TransScopeBlock *trans_scope_block_find(TransScope *scope);
static TransScopeSwitch *trans_scope_switch_find(TransScope *scope);
static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl);
static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl);
@ -265,18 +263,6 @@ static AstNode *trans_create_node_addr_of(Context *c, bool is_const, bool is_vol
return node;
}
static AstNode *trans_create_node_goto(Context *c, Buf *label_name) {
AstNode *goto_node = trans_create_node(c, NodeTypeGoto);
goto_node->data.goto_expr.name = label_name;
return goto_node;
}
static AstNode *trans_create_node_label(Context *c, Buf *label_name) {
AstNode *label_node = trans_create_node(c, NodeTypeLabel);
label_node->data.label.name = label_name;
return label_node;
}
static AstNode *trans_create_node_bool(Context *c, bool value) {
AstNode *bool_node = trans_create_node(c, NodeTypeBoolLiteral);
bool_node->data.bool_literal.value = value;
@ -2379,145 +2365,6 @@ static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt
return while_scope->node;
}
static AstNode *trans_switch_stmt(Context *c, TransScope *parent_scope, const SwitchStmt *stmt) {
TransScopeBlock *block_scope = trans_scope_block_create(c, parent_scope);
TransScopeSwitch *switch_scope;
const DeclStmt *var_decl_stmt = stmt->getConditionVariableDeclStmt();
if (var_decl_stmt == nullptr) {
switch_scope = trans_scope_switch_create(c, &block_scope->base);
} else {
AstNode *vars_node;
TransScope *var_scope = trans_stmt(c, &block_scope->base, var_decl_stmt, &vars_node);
if (var_scope == nullptr)
return nullptr;
if (vars_node != nullptr)
block_scope->node->data.block.statements.append(vars_node);
switch_scope = trans_scope_switch_create(c, var_scope);
}
block_scope->node->data.block.statements.append(switch_scope->switch_node);
// TODO avoid name collisions
Buf *end_label_name = buf_create_from_str("end");
switch_scope->end_label_name = end_label_name;
const Expr *cond_expr = stmt->getCond();
assert(cond_expr != nullptr);
AstNode *expr_node = trans_expr(c, ResultUsedYes, &block_scope->base, cond_expr, TransRValue);
if (expr_node == nullptr)
return nullptr;
switch_scope->switch_node->data.switch_expr.expr = expr_node;
AstNode *body_node;
const Stmt *body_stmt = stmt->getBody();
if (body_stmt->getStmtClass() == Stmt::CompoundStmtClass) {
if (trans_compound_stmt_inline(c, &switch_scope->base, (const CompoundStmt *)body_stmt,
block_scope->node, nullptr))
{
return nullptr;
}
} else {
TransScope *body_scope = trans_stmt(c, &switch_scope->base, body_stmt, &body_node);
if (body_scope == nullptr)
return nullptr;
if (body_node != nullptr)
block_scope->node->data.block.statements.append(body_node);
}
if (!switch_scope->found_default && !stmt->isAllEnumCasesCovered()) {
AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng);
prong_node->data.switch_prong.expr = trans_create_node_goto(c, end_label_name);
switch_scope->switch_node->data.switch_expr.prongs.append(prong_node);
}
// This is necessary if the last switch case "falls through" the end of the switch block
block_scope->node->data.block.statements.append(trans_create_node_goto(c, end_label_name));
block_scope->node->data.block.statements.append(trans_create_node_label(c, end_label_name));
return block_scope->node;
}
static int trans_switch_case(Context *c, TransScope *parent_scope, const CaseStmt *stmt, AstNode **out_node,
TransScope **out_scope)
{
*out_node = nullptr;
if (stmt->getRHS() != nullptr) {
emit_warning(c, stmt->getLocStart(), "TODO support GNU switch case a ... b extension");
return ErrorUnexpected;
}
TransScopeSwitch *switch_scope = trans_scope_switch_find(parent_scope);
assert(switch_scope != nullptr);
Buf *label_name = buf_sprintf("case_%" PRIu32, switch_scope->case_index);
switch_scope->case_index += 1;
{
// Add the prong
AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng);
AstNode *item_node = trans_expr(c, ResultUsedYes, &switch_scope->base, stmt->getLHS(), TransRValue);
if (item_node == nullptr)
return ErrorUnexpected;
prong_node->data.switch_prong.items.append(item_node);
prong_node->data.switch_prong.expr = trans_create_node_goto(c, label_name);
switch_scope->switch_node->data.switch_expr.prongs.append(prong_node);
}
TransScopeBlock *scope_block = trans_scope_block_find(parent_scope);
scope_block->node->data.block.statements.append(trans_create_node_label(c, label_name));
AstNode *sub_stmt_node;
TransScope *new_scope = trans_stmt(c, parent_scope, stmt->getSubStmt(), &sub_stmt_node);
if (new_scope == nullptr)
return ErrorUnexpected;
if (sub_stmt_node != nullptr)
scope_block->node->data.block.statements.append(sub_stmt_node);
*out_scope = new_scope;
return ErrorNone;
}
static int trans_switch_default(Context *c, TransScope *parent_scope, const DefaultStmt *stmt, AstNode **out_node,
TransScope **out_scope)
{
*out_node = nullptr;
TransScopeSwitch *switch_scope = trans_scope_switch_find(parent_scope);
assert(switch_scope != nullptr);
Buf *label_name = buf_sprintf("default");
{
// Add the prong
AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng);
prong_node->data.switch_prong.expr = trans_create_node_goto(c, label_name);
switch_scope->switch_node->data.switch_expr.prongs.append(prong_node);
switch_scope->found_default = true;
}
TransScopeBlock *scope_block = trans_scope_block_find(parent_scope);
scope_block->node->data.block.statements.append(trans_create_node_label(c, label_name));
AstNode *sub_stmt_node;
TransScope *new_scope = trans_stmt(c, parent_scope, stmt->getSubStmt(), &sub_stmt_node);
if (new_scope == nullptr)
return ErrorUnexpected;
if (sub_stmt_node != nullptr)
scope_block->node->data.block.statements.append(sub_stmt_node);
*out_scope = new_scope;
return ErrorNone;
}
static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForStmt *stmt) {
AstNode *loop_block_node;
TransScopeWhile *while_scope;
@ -2595,8 +2442,7 @@ static AstNode *trans_break_stmt(Context *c, TransScope *scope, const BreakStmt
if (cur_scope->id == TransScopeIdWhile) {
return trans_create_node(c, NodeTypeBreak);
} else if (cur_scope->id == TransScopeIdSwitch) {
TransScopeSwitch *switch_scope = (TransScopeSwitch *)cur_scope;
return trans_create_node_goto(c, switch_scope->end_label_name);
zig_panic("TODO");
}
cur_scope = cur_scope->parent;
}
@ -2696,12 +2542,14 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt,
return wrap_stmt(out_node, out_child_scope, scope,
trans_expr(c, result_used, scope, ((const ParenExpr*)stmt)->getSubExpr(), lrvalue));
case Stmt::SwitchStmtClass:
return wrap_stmt(out_node, out_child_scope, scope,
trans_switch_stmt(c, scope, (const SwitchStmt *)stmt));
emit_warning(c, stmt->getLocStart(), "TODO handle C SwitchStmtClass");
return ErrorUnexpected;
case Stmt::CaseStmtClass:
return trans_switch_case(c, scope, (const CaseStmt *)stmt, out_node, out_child_scope);
emit_warning(c, stmt->getLocStart(), "TODO handle C CaseStmtClass");
return ErrorUnexpected;
case Stmt::DefaultStmtClass:
return trans_switch_default(c, scope, (const DefaultStmt *)stmt, out_node, out_child_scope);
emit_warning(c, stmt->getLocStart(), "TODO handle C DefaultStmtClass");
return ErrorUnexpected;
case Stmt::NoStmtClass:
emit_warning(c, stmt->getLocStart(), "TODO handle C NoStmtClass");
return ErrorUnexpected;
@ -3871,14 +3719,6 @@ static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scop
return result;
}
static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope) {
TransScopeSwitch *result = allocate<TransScopeSwitch>(1);
result->base.id = TransScopeIdSwitch;
result->base.parent = parent_scope;
result->switch_node = trans_create_node(c, NodeTypeSwitchExpr);
return result;
}
static TransScopeBlock *trans_scope_block_find(TransScope *scope) {
while (scope != nullptr) {
if (scope->id == TransScopeIdBlock) {
@ -3889,16 +3729,6 @@ static TransScopeBlock *trans_scope_block_find(TransScope *scope) {
return nullptr;
}
static TransScopeSwitch *trans_scope_switch_find(TransScope *scope) {
while (scope != nullptr) {
if (scope->id == TransScopeIdSwitch) {
return (TransScopeSwitch *)scope;
}
scope = scope->parent;
}
return nullptr;
}
static void render_aliases(Context *c) {
for (size_t i = 0; i < c->aliases.length; i += 1) {
Alias *alias = &c->aliases.at(i);

View File

@ -243,7 +243,7 @@ pub const Elf = struct {
var file_stream = io.FileInStream.init(elf.in_file);
const in = &file_stream.stream;
for (elf.section_headers) |*elf_section| {
section_loop: for (elf.section_headers) |*elf_section| {
if (elf_section.sh_type == SHT_NULL) continue;
const name_offset = elf.string_section.offset + elf_section.name;
@ -251,15 +251,13 @@ pub const Elf = struct {
for (name) |expected_c| {
const target_c = %return in.readByte();
if (target_c == 0 or expected_c != target_c) goto next_section;
if (target_c == 0 or expected_c != target_c) continue :section_loop;
}
{
const null_byte = %return in.readByte();
if (null_byte == 0) return elf_section;
}
next_section:
}
return null;

View File

@ -902,40 +902,41 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) -> %void {
/// this function recursively removes its entries and then tries again.
// TODO non-recursive implementation
pub fn deleteTree(allocator: &Allocator, full_path: []const u8) -> %void {
start_over:
// First, try deleting the item as a file. This way we don't follow sym links.
if (deleteFile(allocator, full_path)) {
return;
} else |err| {
if (err == error.FileNotFound)
start_over: while (true) {
// First, try deleting the item as a file. This way we don't follow sym links.
if (deleteFile(allocator, full_path)) {
return;
if (err != error.IsDir)
return err;
}
{
var dir = Dir.open(allocator, full_path) %% |err| {
} else |err| {
if (err == error.FileNotFound)
return;
if (err == error.NotDir)
goto start_over;
return err;
};
defer dir.close();
var full_entry_buf = ArrayList(u8).init(allocator);
defer full_entry_buf.deinit();
while (%return dir.next()) |entry| {
%return full_entry_buf.resize(full_path.len + entry.name.len + 1);
const full_entry_path = full_entry_buf.toSlice();
mem.copy(u8, full_entry_path, full_path);
full_entry_path[full_path.len] = '/';
mem.copy(u8, full_entry_path[full_path.len + 1..], entry.name);
%return deleteTree(allocator, full_entry_path);
if (err != error.IsDir)
return err;
}
{
var dir = Dir.open(allocator, full_path) %% |err| {
if (err == error.FileNotFound)
return;
if (err == error.NotDir)
continue :start_over;
return err;
};
defer dir.close();
var full_entry_buf = ArrayList(u8).init(allocator);
defer full_entry_buf.deinit();
while (%return dir.next()) |entry| {
%return full_entry_buf.resize(full_path.len + entry.name.len + 1);
const full_entry_path = full_entry_buf.toSlice();
mem.copy(u8, full_entry_path, full_path);
full_entry_path[full_path.len] = '/';
mem.copy(u8, full_entry_path[full_path.len + 1..], entry.name);
%return deleteTree(allocator, full_entry_path);
}
}
return deleteDir(allocator, full_path);
}
return deleteDir(allocator, full_path);
}
pub const Dir = struct {
@ -988,58 +989,59 @@ pub const Dir = struct {
/// Memory such as file names referenced in this returned entry becomes invalid
/// with subsequent calls to next, as well as when this ::Dir is deinitialized.
pub fn next(self: &Dir) -> %?Entry {
start_over:
if (self.index >= self.end_index) {
if (self.buf.len == 0) {
self.buf = %return self.allocator.alloc(u8, page_size);
}
while (true) {
const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len);
const err = linux.getErrno(result);
if (err > 0) {
switch (err) {
posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
posix.EINVAL => {
self.buf = %return self.allocator.realloc(u8, self.buf, self.buf.len * 2);
continue;
},
else => return unexpectedErrorPosix(err),
};
start_over: while (true) {
if (self.index >= self.end_index) {
if (self.buf.len == 0) {
self.buf = %return self.allocator.alloc(u8, page_size);
}
while (true) {
const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len);
const err = linux.getErrno(result);
if (err > 0) {
switch (err) {
posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
posix.EINVAL => {
self.buf = %return self.allocator.realloc(u8, self.buf, self.buf.len * 2);
continue;
},
else => return unexpectedErrorPosix(err),
};
}
if (result == 0)
return null;
self.index = 0;
self.end_index = result;
break;
}
if (result == 0)
return null;
self.index = 0;
self.end_index = result;
break;
}
const linux_entry = @ptrCast(& align(1) LinuxEntry, &self.buf[self.index]);
const next_index = self.index + linux_entry.d_reclen;
self.index = next_index;
const name = cstr.toSlice(&linux_entry.d_name);
// skip . and .. entries
if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
continue :start_over;
}
const type_char = self.buf[next_index - 1];
const entry_kind = switch (type_char) {
posix.DT_BLK => Entry.Kind.BlockDevice,
posix.DT_CHR => Entry.Kind.CharacterDevice,
posix.DT_DIR => Entry.Kind.Directory,
posix.DT_FIFO => Entry.Kind.NamedPipe,
posix.DT_LNK => Entry.Kind.SymLink,
posix.DT_REG => Entry.Kind.File,
posix.DT_SOCK => Entry.Kind.UnixDomainSocket,
else => Entry.Kind.Unknown,
};
return Entry {
.name = name,
.kind = entry_kind,
};
}
const linux_entry = @ptrCast(& align(1) LinuxEntry, &self.buf[self.index]);
const next_index = self.index + linux_entry.d_reclen;
self.index = next_index;
const name = cstr.toSlice(&linux_entry.d_name);
// skip . and .. entries
if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
goto start_over;
}
const type_char = self.buf[next_index - 1];
const entry_kind = switch (type_char) {
posix.DT_BLK => Entry.Kind.BlockDevice,
posix.DT_CHR => Entry.Kind.CharacterDevice,
posix.DT_DIR => Entry.Kind.Directory,
posix.DT_FIFO => Entry.Kind.NamedPipe,
posix.DT_LNK => Entry.Kind.SymLink,
posix.DT_REG => Entry.Kind.File,
posix.DT_SOCK => Entry.Kind.UnixDomainSocket,
else => Entry.Kind.Unknown,
};
return Entry {
.name = name,
.kind = entry_kind,
};
}
};

View File

@ -20,7 +20,6 @@ comptime {
_ = @import("cases/fn.zig");
_ = @import("cases/for.zig");
_ = @import("cases/generics.zig");
_ = @import("cases/goto.zig");
_ = @import("cases/if.zig");
_ = @import("cases/import.zig");
_ = @import("cases/incomplete_struct_param_tld.zig");

View File

@ -55,3 +55,37 @@ test "basic for loop" {
assert(mem.eql(u8, buffer[0..buf_index], expected_result));
}
test "break from outer for loop" {
testBreakOuter();
comptime testBreakOuter();
}
fn testBreakOuter() {
var array = "aoeu";
var count: usize = 0;
outer: for (array) |_| {
for (array) |_2| { // TODO shouldn't get error for redeclaring "_"
count += 1;
break :outer;
}
}
assert(count == 1);
}
test "continue outer for loop" {
testContinueOuter();
comptime testContinueOuter();
}
fn testContinueOuter() {
var array = "aoeu";
var counter: usize = 0;
outer: for (array) |_| {
for (array) |_2| { // TODO shouldn't get error for redeclaring "_"
counter += 1;
continue :outer;
}
}
assert(counter == array.len);
}

View File

@ -1,37 +0,0 @@
const assert = @import("std").debug.assert;
test "goto and labels" {
gotoLoop();
assert(goto_counter == 10);
}
fn gotoLoop() {
var i: i32 = 0;
goto cond;
loop:
i += 1;
cond:
if (!(i < 10)) goto end;
goto_counter += 1;
goto loop;
end:
}
var goto_counter: i32 = 0;
test "goto leave defer scope" {
testGotoLeaveDeferScope(true);
}
fn testGotoLeaveDeferScope(b: bool) {
var it_worked = false;
goto entry;
exit:
if (it_worked) {
return;
}
unreachable;
entry:
defer it_worked = true;
if (b) goto exit;
}

View File

@ -188,6 +188,33 @@ test "while on bool with else result follow break prong" {
assert(result == 10);
}
test "break from outer while loop" {
testBreakOuter();
comptime testBreakOuter();
}
fn testBreakOuter() {
outer: while (true) {
while (true) {
break :outer;
}
}
}
test "continue outer while loop" {
testContinueOuter();
comptime testContinueOuter();
}
fn testContinueOuter() {
var i: usize = 0;
outer: while (i < 10) : (i += 1) {
while (true) {
continue :outer;
}
}
}
fn returnNull() -> ?i32 { null }
fn returnMaybe(x: i32) -> ?i32 { x }
error YouWantedAnError;

View File

@ -1,6 +1,27 @@
const tests = @import("tests.zig");
pub fn addCases(cases: &tests.CompileErrorContext) {
cases.add("labeled break not found",
\\export fn entry() {
\\ blah: while (true) {
\\ while (true) {
\\ break :outer;
\\ }
\\ }
\\}
, ".tmp_source.zig:4:13: error: labeled loop not found: 'outer'");
cases.add("labeled continue not found",
\\export fn entry() {
\\ var i: usize = 0;
\\ blah: while (i < 10) : (i += 1) {
\\ while (true) {
\\ continue :outer;
\\ }
\\ }
\\}
, ".tmp_source.zig:5:13: error: labeled loop not found: 'outer'");
cases.add("attempt to use 0 bit type in extern fn",
\\extern fn foo(ptr: extern fn(&void));
\\
@ -833,26 +854,6 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\export fn entry() -> usize { @sizeOf(@typeOf(test1)) }
, ".tmp_source.zig:3:16: error: unable to evaluate constant expression");
cases.add("goto jumping into block",
\\export fn f() {
\\ {
\\a_label:
\\ }
\\ goto a_label;
\\}
, ".tmp_source.zig:5:5: error: no label in scope named 'a_label'");
cases.add("goto jumping past a defer",
\\fn f(b: bool) {
\\ if (b) goto label;
\\ defer derp();
\\label:
\\}
\\fn derp(){}
\\
\\export fn entry() -> usize { @sizeOf(@typeOf(f)) }
, ".tmp_source.zig:2:12: error: no label in scope named 'label'");
cases.add("assign null to non-nullable pointer",
\\const a: &u8 = null;
\\
@ -1854,16 +1855,6 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
,
".tmp_source.zig:4:13: error: cannot continue out of defer expression");
cases.add("cannot goto out of defer expression",
\\export fn foo() {
\\ defer {
\\ goto label;
\\ };
\\label:
\\}
,
".tmp_source.zig:3:9: error: cannot goto out of defer expression");
cases.add("calling a var args function only known at runtime",
\\var foos = []fn(...) { foo1, foo2 };
\\

View File

@ -1005,48 +1005,6 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\}
);
cases.add("switch statement",
\\int foo(int x) {
\\ switch (x) {
\\ case 1:
\\ x += 1;
\\ case 2:
\\ break;
\\ case 3:
\\ case 4:
\\ return x + 1;
\\ default:
\\ return 10;
\\ }
\\ return x + 13;
\\}
,
\\fn foo(_arg_x: c_int) -> c_int {
\\ var x = _arg_x;
\\ {
\\ switch (x) {
\\ 1 => goto case_0,
\\ 2 => goto case_1,
\\ 3 => goto case_2,
\\ 4 => goto case_3,
\\ else => goto default,
\\ };
\\ case_0:
\\ x += 1;
\\ case_1:
\\ goto end;
\\ case_2:
\\ case_3:
\\ return x + 1;
\\ default:
\\ return 10;
\\ goto end;
\\ end:
\\ };
\\ return x + 13;
\\}
);
cases.add("macros with field targets",
\\typedef unsigned int GLbitfield;
\\typedef void (*PFNGLCLEARPROC) (GLbitfield mask);
@ -1085,44 +1043,6 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\pub const OpenGLProcs = union_OpenGLProcs;
);
cases.add("switch statement with no default",
\\int foo(int x) {
\\ switch (x) {
\\ case 1:
\\ x += 1;
\\ case 2:
\\ break;
\\ case 3:
\\ case 4:
\\ return x + 1;
\\ }
\\ return x + 13;
\\}
,
\\fn foo(_arg_x: c_int) -> c_int {
\\ var x = _arg_x;
\\ {
\\ switch (x) {
\\ 1 => goto case_0,
\\ 2 => goto case_1,
\\ 3 => goto case_2,
\\ 4 => goto case_3,
\\ else => goto end,
\\ };
\\ case_0:
\\ x += 1;
\\ case_1:
\\ goto end;
\\ case_2:
\\ case_3:
\\ return x + 1;
\\ goto end;
\\ end:
\\ };
\\ return x + 13;
\\}
);
cases.add("variable name shadowing",
\\int foo(void) {
\\ int x = 1;