diff --git a/doc/langref.md b/doc/langref.md index add823a1d8..4a9ed1703c 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -90,10 +90,12 @@ AssignmentExpression : UnwrapMaybeExpression AssignmentOperator UnwrapMaybeExpre AssignmentOperator : token(Eq) | token(TimesEq) | token(DivEq) | token(ModEq) | token(PlusEq) | token(MinusEq) | token(BitShiftLeftEq) | token(BitShiftRightEq) | token(BitAndEq) | token(BitXorEq) | token(BitOrEq) | token(BoolAndEq) | token(BoolOrEq) -BlockExpression : IfExpression | Block | WhileExpression +BlockExpression : IfExpression | Block | WhileExpression | ForExpression WhileExpression : token(While) token(LParen) Expression token(RParen) Expression +ForExpression : token(For) token(LParen) Symbol token(Comma) Expression option(token(Comma) token(Symbol)) token(RParen) Expression + BoolOrExpression : BoolAndExpression token(BoolOr) BoolOrExpression | BoolAndExpression ReturnExpression : token(Return) option(Expression) diff --git a/src/all_types.hpp b/src/all_types.hpp index 385cc41e0d..c5f6d31099 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -138,6 +138,7 @@ enum NodeType { NodeTypeIfBoolExpr, NodeTypeIfVarExpr, NodeTypeWhileExpr, + NodeTypeForExpr, NodeTypeLabel, NodeTypeGoto, NodeTypeBreak, @@ -393,6 +394,21 @@ struct AstNodeWhileExpr { bool condition_always_true; bool contains_break; Expr resolved_expr; + BlockContext *block_context; +}; + +struct AstNodeForExpr { + AstNode *elem_node; // always a symbol + AstNode *array_expr; + AstNode *index_node; // always a symbol, might be null + AstNode *body; + + // populated by semantic analyzer + bool contains_break; + Expr resolved_expr; + BlockContext *block_context; + VariableTableEntry *elem_var; + VariableTableEntry *index_var; }; struct AstNodeLabel { @@ -605,6 +621,7 @@ struct AstNode { AstNodeIfBoolExpr if_bool_expr; AstNodeIfVarExpr if_var_expr; AstNodeWhileExpr while_expr; + AstNodeForExpr for_expr; AstNodeLabel label; AstNodeGoto goto_expr; AstNodeAsmExpr asm_expr; @@ -916,7 +933,8 @@ struct VariableTableEntry { bool is_ptr; // if true, value_ref is a pointer AstNode *decl_node; LLVMZigDILocalVariable *di_loc_var; - int arg_index; + int src_arg_index; + int gen_arg_index; }; struct BlockContext { @@ -927,8 +945,8 @@ struct BlockContext { HashMap type_table; ZigList cast_expr_alloca_list; ZigList struct_val_expr_alloca_list; + ZigList variable_list; AstNode *parent_loop_node; - AstNode *next_child_parent_loop_node; LLVMZigDIScope *di_scope; }; diff --git a/src/analyze.cpp b/src/analyze.cpp index 2763b0f687..a1e0421133 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -60,6 +60,7 @@ static AstNode *first_executing_node(AstNode *node) { case NodeTypeStructField: case NodeTypeStructValueField: case NodeTypeWhileExpr: + case NodeTypeForExpr: case NodeTypeContainerInitExpr: case NodeTypeArrayType: return node; @@ -867,6 +868,7 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeIfBoolExpr: case NodeTypeIfVarExpr: case NodeTypeWhileExpr: + case NodeTypeForExpr: case NodeTypeLabel: case NodeTypeGoto: case NodeTypeBreak: @@ -1175,12 +1177,7 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) { context->type_table.init(8); if (parent) { - if (parent->next_child_parent_loop_node) { - context->parent_loop_node = parent->next_child_parent_loop_node; - parent->next_child_parent_loop_node = nullptr; - } else { - context->parent_loop_node = parent->parent_loop_node; - } + context->parent_loop_node = parent->parent_loop_node; } if (node && node->type == NodeTypeFnDef) { @@ -1986,6 +1983,36 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, zig_unreachable(); } +// Set name to nullptr to make the variable anonymous (not visible to programmer). +static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, BlockContext *context, + Buf *name, TypeTableEntry *type_entry, bool is_const) +{ + VariableTableEntry *variable_entry = allocate(1); + variable_entry->type = type_entry; + + if (name) { + buf_init_from_buf(&variable_entry->name, name); + VariableTableEntry *existing_var = find_local_variable(context, name); + + if (existing_var) { + add_node_error(g, source_node, buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); + variable_entry->type = g->builtin_types.entry_invalid; + } + + context->variable_table.put(&variable_entry->name, variable_entry); + context->variable_list.append(variable_entry); + } else { + buf_init_from_str(&variable_entry->name, "_anon"); + context->variable_list.append(variable_entry); + } + + variable_entry->is_const = is_const; + variable_entry->is_ptr = true; + variable_entry->decl_node = source_node; + + return variable_entry; +} + static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *source_node, AstNodeVariableDeclaration *variable_declaration, @@ -2037,38 +2064,26 @@ static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTa TypeTableEntry *type = explicit_type != nullptr ? explicit_type : implicit_type; assert(type != nullptr); // should have been caught by the parser - VariableTableEntry *existing_variable = find_local_variable(context, &variable_declaration->symbol); - if (existing_variable) { - add_node_error(g, source_node, - buf_sprintf("redeclaration of variable '%s'", buf_ptr(&variable_declaration->symbol))); - } else { - VariableTableEntry *variable_entry = allocate(1); - buf_init_from_buf(&variable_entry->name, &variable_declaration->symbol); - variable_entry->type = type; - variable_entry->is_const = variable_declaration->is_const; - variable_entry->is_ptr = true; - variable_entry->decl_node = source_node; - context->variable_table.put(&variable_entry->name, variable_entry); + VariableTableEntry *var = add_local_var(g, source_node, context, + &variable_declaration->symbol, type, variable_declaration->is_const); - bool is_pub = (variable_declaration->visib_mod != VisibModPrivate); - if (is_pub) { - for (int i = 0; i < import->importers.length; i += 1) { - ImporterInfo importer = import->importers.at(i); - auto table_entry = importer.import->block_context->variable_table.maybe_get(&variable_entry->name); - if (table_entry) { - add_node_error(g, importer.source_node, - buf_sprintf("import of variable '%s' overrides existing definition", - buf_ptr(&variable_entry->name))); - } else { - importer.import->block_context->variable_table.put(&variable_entry->name, variable_entry); - } + + bool is_pub = (variable_declaration->visib_mod != VisibModPrivate); + if (is_pub) { + for (int i = 0; i < import->importers.length; i += 1) { + ImporterInfo importer = import->importers.at(i); + auto table_entry = importer.import->block_context->variable_table.maybe_get(&var->name); + if (table_entry) { + add_node_error(g, importer.source_node, + buf_sprintf("import of variable '%s' overrides existing definition", + buf_ptr(&var->name))); + } else { + importer.import->block_context->variable_table.put(&var->name, var); } } - - - return variable_entry; } - return nullptr; + + return var; } static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableEntry *import, @@ -2172,11 +2187,15 @@ static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, AstNode *condition_node = node->data.while_expr.condition; AstNode *while_body_node = node->data.while_expr.body; + TypeTableEntry *condition_type = analyze_expression(g, import, context, g->builtin_types.entry_bool, condition_node); - context->next_child_parent_loop_node = node; - analyze_expression(g, import, context, g->builtin_types.entry_void, while_body_node); + BlockContext *child_context = new_block_context(node, context); + child_context->parent_loop_node = node; + node->data.while_expr.block_context = child_context; + + analyze_expression(g, import, child_context, g->builtin_types.entry_void, while_body_node); TypeTableEntry *expr_return_type = g->builtin_types.entry_void; @@ -2200,6 +2219,54 @@ static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, return expr_return_type; } +static TypeTableEntry *analyze_for_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, + TypeTableEntry *expected_type, AstNode *node) +{ + assert(node->type == NodeTypeForExpr); + + AstNode *array_node = node->data.for_expr.array_expr; + TypeTableEntry *array_type = analyze_expression(g, import, context, nullptr, array_node); + TypeTableEntry *child_type; + if (array_type->id == TypeTableEntryIdInvalid) { + child_type = array_type; + } else if (array_type->id == TypeTableEntryIdArray) { + child_type = array_type->data.array.child_type; + } else if (array_type->id == TypeTableEntryIdStruct && + array_type->data.structure.is_unknown_size_array) + { + TypeTableEntry *pointer_type = array_type->data.structure.fields[0].type_entry; + assert(pointer_type->id == TypeTableEntryIdPointer); + child_type = pointer_type->data.pointer.child_type; + } else { + add_node_error(g, node, + buf_sprintf("iteration over non array type '%s'", buf_ptr(&array_type->name))); + child_type = g->builtin_types.entry_invalid; + } + + BlockContext *child_context = new_block_context(node, context); + node->data.for_expr.block_context = child_context; + + AstNode *elem_var_node = node->data.for_expr.elem_node; + Buf *elem_var_name = &elem_var_node->data.symbol_expr.symbol; + node->data.for_expr.elem_var = add_local_var(g, elem_var_node, child_context, elem_var_name, child_type, true); + + AstNode *index_var_node = node->data.for_expr.index_node; + if (index_var_node) { + Buf *index_var_name = &index_var_node->data.symbol_expr.symbol; + node->data.for_expr.index_var = add_local_var(g, index_var_node, child_context, index_var_name, + g->builtin_types.entry_usize, true); + } else { + node->data.for_expr.index_var = add_local_var(g, node, child_context, nullptr, + g->builtin_types.entry_usize, true); + } + + AstNode *for_body_node = node->data.for_expr.body; + analyze_expression(g, import, child_context, g->builtin_types.entry_void, for_body_node); + + + return g->builtin_types.entry_void; +} + static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { @@ -3018,6 +3085,9 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, case NodeTypeWhileExpr: return_type = analyze_while_expr(g, import, context, expected_type, node); break; + case NodeTypeForExpr: + return_type = analyze_for_expr(g, import, context, expected_type, node); + break; case NodeTypeArrayType: return_type = analyze_array_type(g, import, context, expected_type, node); break; @@ -3081,6 +3151,7 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo AstNodeFnProto *fn_proto = &fn_proto_node->data.fn_proto; bool is_exported = (fn_proto->visib_mod == VisibModExport); + int gen_arg_index = 0; for (int i = 0; i < fn_proto->params.length; i += 1) { AstNode *param_decl_node = fn_proto->params.at(i); assert(param_decl_node->type == NodeTypeParamDecl); @@ -3099,28 +3170,15 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo buf_sprintf("byvalue struct parameters not yet supported on exported functions")); } - VariableTableEntry *variable_entry = allocate(1); - buf_init_from_buf(&variable_entry->name, ¶m_decl->name); - variable_entry->type = type; - variable_entry->is_const = true; - variable_entry->decl_node = param_decl_node; - variable_entry->arg_index = i; + VariableTableEntry *var = add_local_var(g, param_decl_node, context, ¶m_decl->name, type, true); + var->src_arg_index = i; + param_decl_node->data.param_decl.variable = var; - param_decl_node->data.param_decl.variable = variable_entry; - - VariableTableEntry *existing_entry = find_local_variable(context, &variable_entry->name); - if (!existing_entry) { - // unique definition - context->variable_table.put(&variable_entry->name, variable_entry); + if (type->size_in_bits > 0) { + var->gen_arg_index = gen_arg_index; + gen_arg_index += 1; } else { - add_node_error(g, node, - buf_sprintf("redeclaration of parameter '%s'.", buf_ptr(&existing_entry->name))); - if (existing_entry->type == variable_entry->type) { - // types agree, so the type is probably good enough for the rest of analysis - } else { - // types disagree. don't trust either one of them. - existing_entry->type = g->builtin_types.entry_invalid;; - } + var->gen_arg_index = -1; } } @@ -3187,6 +3245,7 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeIfBoolExpr: case NodeTypeIfVarExpr: case NodeTypeWhileExpr: + case NodeTypeForExpr: case NodeTypeLabel: case NodeTypeGoto: case NodeTypeBreak: @@ -3281,6 +3340,10 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode collect_expr_decl_deps(g, import, node->data.while_expr.condition, decl_node); collect_expr_decl_deps(g, import, node->data.while_expr.body, decl_node); break; + case NodeTypeForExpr: + collect_expr_decl_deps(g, import, node->data.for_expr.array_expr, decl_node); + collect_expr_decl_deps(g, import, node->data.for_expr.body, decl_node); + break; case NodeTypeBlock: for (int i = 0; i < node->data.block.statements.length; i += 1) { AstNode *stmt = node->data.block.statements.at(i); @@ -3505,6 +3568,7 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast case NodeTypeIfBoolExpr: case NodeTypeIfVarExpr: case NodeTypeWhileExpr: + case NodeTypeForExpr: case NodeTypeLabel: case NodeTypeGoto: case NodeTypeBreak: @@ -3681,6 +3745,8 @@ Expr *get_resolved_expr(AstNode *node) { return &node->data.if_var_expr.resolved_expr; case NodeTypeWhileExpr: return &node->data.while_expr.resolved_expr; + case NodeTypeForExpr: + return &node->data.for_expr.resolved_expr; case NodeTypeAsmExpr: return &node->data.asm_expr.resolved_expr; case NodeTypeContainerInitExpr: @@ -3743,6 +3809,7 @@ NumLitCodeGen *get_resolved_num_lit(AstNode *node) { case NodeTypeIfBoolExpr: case NodeTypeIfVarExpr: case NodeTypeWhileExpr: + case NodeTypeForExpr: case NodeTypeAsmExpr: case NodeTypeContainerInitExpr: case NodeTypeRoot: @@ -3793,6 +3860,7 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) { case NodeTypeIfBoolExpr: case NodeTypeIfVarExpr: case NodeTypeWhileExpr: + case NodeTypeForExpr: case NodeTypeAsmExpr: case NodeTypeContainerInitExpr: case NodeTypeRoot: diff --git a/src/codegen.cpp b/src/codegen.cpp index c6c03175a7..fce29f5d3e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -526,41 +526,35 @@ static LLVMValueRef gen_array_base_ptr(CodeGen *g, AstNode *node) { return array_ptr; } -static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) { - assert(node->type == NodeTypeArrayAccessExpr); - - AstNode *array_expr_node = node->data.array_access_expr.array_ref_expr; - TypeTableEntry *type_entry = get_expr_type(array_expr_node); - - LLVMValueRef array_ptr = gen_array_base_ptr(g, array_expr_node); - - LLVMValueRef subscript_value = gen_expr(g, node->data.array_access_expr.subscript); +static LLVMValueRef gen_array_elem_ptr(CodeGen *g, AstNode *source_node, LLVMValueRef array_ptr, + TypeTableEntry *array_type, LLVMValueRef subscript_value) +{ assert(subscript_value); - if (type_entry->size_in_bits == 0) { + if (array_type->size_in_bits == 0) { return nullptr; } - if (type_entry->id == TypeTableEntryIdArray) { + if (array_type->id == TypeTableEntryIdArray) { LLVMValueRef indices[] = { LLVMConstNull(g->builtin_types.entry_usize->type_ref), subscript_value }; - add_debug_source_node(g, node); + add_debug_source_node(g, source_node); return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); - } else if (type_entry->id == TypeTableEntryIdPointer) { + } else if (array_type->id == TypeTableEntryIdPointer) { assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); LLVMValueRef indices[] = { subscript_value }; - add_debug_source_node(g, node); + add_debug_source_node(g, source_node); return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 1, ""); - } else if (type_entry->id == TypeTableEntryIdStruct) { - assert(type_entry->data.structure.is_unknown_size_array); + } else if (array_type->id == TypeTableEntryIdStruct) { + assert(array_type->data.structure.is_unknown_size_array); assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); - add_debug_source_node(g, node); + add_debug_source_node(g, source_node); LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, 0, ""); LLVMValueRef ptr = LLVMBuildLoad(g->builder, ptr_ptr, ""); return LLVMBuildInBoundsGEP(g->builder, ptr, &subscript_value, 1, ""); @@ -569,6 +563,19 @@ static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) { } } +static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) { + assert(node->type == NodeTypeArrayAccessExpr); + + AstNode *array_expr_node = node->data.array_access_expr.array_ref_expr; + TypeTableEntry *array_type = get_expr_type(array_expr_node); + + LLVMValueRef array_ptr = gen_array_base_ptr(g, array_expr_node); + + LLVMValueRef subscript_value = gen_expr(g, node->data.array_access_expr.subscript); + + return gen_array_elem_ptr(g, node, array_ptr, array_type, subscript_value); +} + static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **out_type_entry) { assert(node->type == NodeTypeFieldAccessExpr); @@ -1695,10 +1702,13 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) { assert(node->data.while_expr.condition); assert(node->data.while_expr.body); + BlockContext *old_block_context = g->cur_block_context; + bool condition_always_true = node->data.while_expr.condition_always_true; bool contains_break = node->data.while_expr.contains_break; if (condition_always_true) { // generate a forever loop + g->cur_block_context = node->data.while_expr.block_context; LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileBody"); LLVMBasicBlockRef end_block = nullptr; @@ -1735,6 +1745,7 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) { LLVMBuildBr(g->builder, cond_block); LLVMPositionBuilderAtEnd(g->builder, cond_block); + g->cur_block_context = old_block_context; LLVMValueRef cond_val = gen_expr(g, node->data.while_expr.condition); add_debug_source_node(g, node->data.while_expr.condition); LLVMBuildCondBr(g->builder, cond_val, body_block, end_block); @@ -1742,6 +1753,7 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) { LLVMPositionBuilderAtEnd(g->builder, body_block); g->break_block_stack.append(end_block); g->continue_block_stack.append(cond_block); + g->cur_block_context = node->data.while_expr.block_context; gen_expr(g, node->data.while_expr.body); g->break_block_stack.pop(); g->continue_block_stack.pop(); @@ -1753,6 +1765,77 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) { LLVMPositionBuilderAtEnd(g->builder, end_block); } + g->cur_block_context = old_block_context; + return nullptr; +} + +static LLVMValueRef gen_for_expr(CodeGen *g, AstNode *node) { + assert(node->type == NodeTypeForExpr); + assert(node->data.for_expr.array_expr); + assert(node->data.for_expr.body); + + VariableTableEntry *elem_var = node->data.for_expr.elem_var; + assert(elem_var); + + TypeTableEntry *array_type = get_expr_type(node->data.for_expr.array_expr); + + VariableTableEntry *index_var = node->data.for_expr.index_var; + assert(index_var); + LLVMValueRef index_ptr = index_var->value_ref; + LLVMValueRef one_const = LLVMConstInt(g->builtin_types.entry_usize->type_ref, 1, false); + + BlockContext *old_block_context = g->cur_block_context; + + LLVMBasicBlockRef cond_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForCond"); + LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForBody"); + LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForEnd"); + + LLVMValueRef array_val = gen_expr(g, node->data.for_expr.array_expr); + add_debug_source_node(g, node); + LLVMBuildStore(g->builder, LLVMConstNull(index_var->type->type_ref), index_ptr); + LLVMValueRef len_val; + TypeTableEntry *child_type; + if (array_type->id == TypeTableEntryIdArray) { + len_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, + array_type->data.array.len, false); + child_type = array_type->data.array.child_type; + } else if (array_type->id == TypeTableEntryIdStruct) { + assert(array_type->data.structure.is_unknown_size_array); + TypeTableEntry *child_ptr_type = array_type->data.structure.fields[0].type_entry; + assert(child_ptr_type->id == TypeTableEntryIdPointer); + child_type = child_ptr_type->data.pointer.child_type; + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, array_val, 1, ""); + len_val = LLVMBuildLoad(g->builder, len_field_ptr, ""); + } else { + zig_unreachable(); + } + LLVMBuildBr(g->builder, cond_block); + + LLVMPositionBuilderAtEnd(g->builder, cond_block); + LLVMValueRef index_val = LLVMBuildLoad(g->builder, index_ptr, ""); + LLVMValueRef cond = LLVMBuildICmp(g->builder, LLVMIntSLT, index_val, len_val, ""); + LLVMBuildCondBr(g->builder, cond, body_block, end_block); + + LLVMPositionBuilderAtEnd(g->builder, body_block); + LLVMValueRef elem_ptr = gen_array_elem_ptr(g, node, array_val, array_type, index_val); + LLVMValueRef elem_val = handle_is_ptr(child_type) ? elem_ptr : LLVMBuildLoad(g->builder, elem_ptr, ""); + gen_assign_raw(g, node, BinOpTypeAssign, elem_var->value_ref, elem_val, + elem_var->type, child_type); + g->break_block_stack.append(end_block); + g->continue_block_stack.append(cond_block); + g->cur_block_context = node->data.for_expr.block_context; + gen_expr(g, node->data.for_expr.body); + g->break_block_stack.pop(); + g->continue_block_stack.pop(); + if (get_expr_type(node->data.for_expr.body)->id != TypeTableEntryIdUnreachable) { + add_debug_source_node(g, node); + LLVMValueRef new_index_val = LLVMBuildAdd(g->builder, index_val, one_const, ""); + LLVMBuildStore(g->builder, new_index_val, index_ptr); + LLVMBuildBr(g->builder, cond_block); + } + + LLVMPositionBuilderAtEnd(g->builder, end_block); + g->cur_block_context = old_block_context; return nullptr; } @@ -1935,6 +2018,8 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { return gen_if_var_expr(g, node); case NodeTypeWhileExpr: return gen_while_expr(g, node); + case NodeTypeForExpr: + return gen_for_expr(g, node); case NodeTypeAsmExpr: return gen_asm_expr(g, node); case NodeTypeNumberLiteral: @@ -2177,22 +2262,6 @@ static void do_code_gen(CodeGen *g) { fn_def_node->data.fn_def.block_context->di_scope = LLVMZigSubprogramToScope(subprogram); - int non_void_param_count = count_non_void_params(g, &fn_proto->params); - assert(non_void_param_count == (int)LLVMCountParams(fn)); - LLVMValueRef *params = allocate(non_void_param_count); - LLVMGetParams(fn, params); - - int non_void_index = 0; - for (int param_i = 0; param_i < fn_proto->params.length; param_i += 1) { - AstNode *param_decl = fn_proto->params.at(param_i); - assert(param_decl->type == NodeTypeParamDecl); - if (is_param_decl_type_void(g, param_decl)) - continue; - VariableTableEntry *parameter_variable = fn_def_node->data.fn_def.block_context->variable_table.get(¶m_decl->data.param_decl.name); - parameter_variable->value_ref = params[non_void_index]; - non_void_index += 1; - } - AstNode *body_node = fn_def_node->data.fn_def.body; build_label_blocks(g, body_node); @@ -2212,13 +2281,9 @@ static void do_code_gen(CodeGen *g) { g->cur_block_context = block_context; - auto it = block_context->variable_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; + for (int var_i = 0; var_i < block_context->variable_list.length; var_i += 1) { + VariableTableEntry *var = block_context->variable_list.at(var_i); - VariableTableEntry *var = entry->value; if (var->type->size_in_bits == 0) { continue; } @@ -2227,7 +2292,10 @@ static void do_code_gen(CodeGen *g) { unsigned arg_no; if (block_context->node->type == NodeTypeFnDef) { tag = LLVMZigTag_DW_arg_variable(); - arg_no = var->arg_index + 1; + arg_no = var->gen_arg_index + 1; + + var->is_ptr = false; + var->value_ref = LLVMGetParam(fn, var->gen_arg_index); } else { tag = LLVMZigTag_DW_auto_variable(); arg_no = 0; diff --git a/src/parser.cpp b/src/parser.cpp index 882db56b6c..6d86c53518 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -121,6 +121,8 @@ const char *node_type_str(NodeType node_type) { return "IfVarExpr"; case NodeTypeWhileExpr: return "WhileExpr"; + case NodeTypeForExpr: + return "ForExpr"; case NodeTypeLabel: return "Label"; case NodeTypeGoto: @@ -331,6 +333,15 @@ void ast_print(AstNode *node, int indent) { ast_print(node->data.while_expr.condition, indent + 2); ast_print(node->data.while_expr.body, indent + 2); break; + case NodeTypeForExpr: + fprintf(stderr, "%s\n", node_type_str(node->type)); + ast_print(node->data.for_expr.elem_node, indent + 2); + ast_print(node->data.for_expr.array_expr, indent + 2); + if (node->data.for_expr.index_node) { + ast_print(node->data.for_expr.index_node, indent + 2); + } + ast_print(node->data.for_expr.body, indent + 2); + break; case NodeTypeLabel: fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.label.name)); break; @@ -2114,8 +2125,49 @@ static AstNode *ast_parse_while_expr(ParseContext *pc, int *token_index, bool ma return node; } +static AstNode *ast_parse_symbol(ParseContext *pc, int *token_index) { + Token *token = ast_eat_token(pc, token_index, TokenIdSymbol); + AstNode *node = ast_create_node(pc, NodeTypeSymbol, token); + ast_buf_from_token(pc, token, &node->data.symbol_expr.symbol); + return node; +} + /* -BlockExpression : IfExpression | Block | WhileExpression +ForExpression : token(For) token(LParen) Symbol token(Comma) Expression option(token(Comma) token(Symbol)) token(RParen) Expression +*/ +static AstNode *ast_parse_for_expr(ParseContext *pc, int *token_index, bool mandatory) { + Token *token = &pc->tokens->at(*token_index); + + if (token->id != TokenIdKeywordFor) { + if (mandatory) { + ast_invalid_token_error(pc, token); + } else { + return nullptr; + } + } + *token_index += 1; + + AstNode *node = ast_create_node(pc, NodeTypeForExpr, token); + + ast_eat_token(pc, token_index, TokenIdLParen); + node->data.for_expr.elem_node = ast_parse_symbol(pc, token_index); + ast_eat_token(pc, token_index, TokenIdComma); + node->data.for_expr.array_expr = ast_parse_expression(pc, token_index, true); + + Token *comma = &pc->tokens->at(*token_index); + if (comma->id == TokenIdComma) { + *token_index += 1; + node->data.for_expr.index_node = ast_parse_symbol(pc, token_index); + } + + ast_eat_token(pc, token_index, TokenIdRParen); + + node->data.for_expr.body = ast_parse_expression(pc, token_index, true); + return node; +} + +/* +BlockExpression : IfExpression | Block | WhileExpression | ForExpression */ static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); @@ -2132,6 +2184,10 @@ static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool ma if (while_expr) return while_expr; + AstNode *for_expr = ast_parse_for_expr(pc, token_index, false); + if (for_expr) + return for_expr; + if (mandatory) ast_invalid_token_error(pc, token); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 2172c0ccbc..00de1219fb 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -231,6 +231,8 @@ static void end_token(Tokenize *t) { t->cur_tok->id = TokenIdKeywordStruct; } else if (mem_eql_str(token_mem, token_len, "enum")) { t->cur_tok->id = TokenIdKeywordEnum; + } else if (mem_eql_str(token_mem, token_len, "for")) { + t->cur_tok->id = TokenIdKeywordFor; } else if (mem_eql_str(token_mem, token_len, "while")) { t->cur_tok->id = TokenIdKeywordWhile; } else if (mem_eql_str(token_mem, token_len, "continue")) { @@ -1028,6 +1030,7 @@ const char * token_name(TokenId id) { case TokenIdKeywordStruct: return "struct"; case TokenIdKeywordEnum: return "enum"; case TokenIdKeywordWhile: return "while"; + case TokenIdKeywordFor: return "for"; case TokenIdKeywordContinue: return "continue"; case TokenIdKeywordBreak: return "break"; case TokenIdKeywordNull: return "null"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index a7f0ddc506..cd9f3cd804 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -31,6 +31,7 @@ enum TokenId { TokenIdKeywordStruct, TokenIdKeywordEnum, TokenIdKeywordWhile, + TokenIdKeywordFor, TokenIdKeywordContinue, TokenIdKeywordBreak, TokenIdKeywordNull, diff --git a/std/bootstrap.zig b/std/bootstrap.zig index 3368539aaa..222b15dae7 100644 --- a/std/bootstrap.zig +++ b/std/bootstrap.zig @@ -25,12 +25,9 @@ fn strlen(ptr: &u8) usize => { fn call_main() unreachable => { var args: [argc][]u8; - var i : @typeof(argc) = 0; - // TODO for in loop over the array - while (i < argc) { + for (arg, args, i) { const ptr = argv[i]; args[i] = ptr[0...strlen(ptr)]; - i += 1; } exit(main(args)) } diff --git a/test/run_tests.cpp b/test/run_tests.cpp index f416beb311..f7bb20038e 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -1136,6 +1136,32 @@ pub fn main(args: [][]u8) i32 => { return 0; } )SOURCE", "hello\nthis\nis\nmy\nthing\n"); + + add_simple_case("for loops", R"SOURCE( +import "std.zig"; + +pub fn main(args: [][]u8) i32 => { + const array = []u8 {9, 8, 7, 6}; + for (item, array) { + print_u64(item); + print_str("\n"); + } + for (item, array, index) { + print_u64(index); + print_str("\n"); + } + const unknown_size: []u8 = array; + for (item, unknown_size) { + print_u64(item); + print_str("\n"); + } + for (item, unknown_size, index) { + print_u64(index); + print_str("\n"); + } + return 0; +} + )SOURCE", "9\n8\n7\n6\n0\n1\n2\n3\n9\n8\n7\n6\n0\n1\n2\n3\n"); } @@ -1226,7 +1252,7 @@ fn b() => {} add_compile_fail_case("parameter redeclaration", R"SOURCE( fn f(a : i32, a : i32) => { } - )SOURCE", 1, ".tmp_source.zig:2:1: error: redeclaration of parameter 'a'"); + )SOURCE", 1, ".tmp_source.zig:2:15: error: redeclaration of variable 'a'"); add_compile_fail_case("local variable redeclaration", R"SOURCE( fn f() => {