| author | |
| committer | |
| log | fbbef140130e8da13f1d58b884ec3a0225965531 |
| tree | dc54b1f5a9e3bc793986067b67de336320a2ee29 |
| parent | f0a43cfda9bcfcbefb24cac3ef01c5c745022c58 |
See #519 files changed, 344 insertions(+), 105 deletions(-)
doc/langref.md+3-1| ... | ... | @@ -90,10 +90,12 @@ AssignmentExpression : UnwrapMaybeExpression AssignmentOperator UnwrapMaybeExpre |
| 90 | 90 | |
| 91 | 91 | 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) |
| 92 | 92 | |
| 93 | BlockExpression : IfExpression | Block | WhileExpression | |
| 93 | BlockExpression : IfExpression | Block | WhileExpression | ForExpression | |
| 94 | 94 | |
| 95 | 95 | WhileExpression : token(While) token(LParen) Expression token(RParen) Expression |
| 96 | 96 | |
| 97 | ForExpression : token(For) token(LParen) Symbol token(Comma) Expression option(token(Comma) token(Symbol)) token(RParen) Expression | |
| 98 | ||
| 97 | 99 | BoolOrExpression : BoolAndExpression token(BoolOr) BoolOrExpression | BoolAndExpression |
| 98 | 100 | |
| 99 | 101 | ReturnExpression : token(Return) option(Expression) |
src/all_types.hpp+20-2| ... | ... | @@ -138,6 +138,7 @@ enum NodeType { |
| 138 | 138 | NodeTypeIfBoolExpr, |
| 139 | 139 | NodeTypeIfVarExpr, |
| 140 | 140 | NodeTypeWhileExpr, |
| 141 | NodeTypeForExpr, | |
| 141 | 142 | NodeTypeLabel, |
| 142 | 143 | NodeTypeGoto, |
| 143 | 144 | NodeTypeBreak, |
| ... | ... | @@ -393,6 +394,21 @@ struct AstNodeWhileExpr { |
| 393 | 394 | bool condition_always_true; |
| 394 | 395 | bool contains_break; |
| 395 | 396 | Expr resolved_expr; |
| 397 | BlockContext *block_context; | |
| 398 | }; | |
| 399 | ||
| 400 | struct AstNodeForExpr { | |
| 401 | AstNode *elem_node; // always a symbol | |
| 402 | AstNode *array_expr; | |
| 403 | AstNode *index_node; // always a symbol, might be null | |
| 404 | AstNode *body; | |
| 405 | ||
| 406 | // populated by semantic analyzer | |
| 407 | bool contains_break; | |
| 408 | Expr resolved_expr; | |
| 409 | BlockContext *block_context; | |
| 410 | VariableTableEntry *elem_var; | |
| 411 | VariableTableEntry *index_var; | |
| 396 | 412 | }; |
| 397 | 413 | |
| 398 | 414 | struct AstNodeLabel { |
| ... | ... | @@ -605,6 +621,7 @@ struct AstNode { |
| 605 | 621 | AstNodeIfBoolExpr if_bool_expr; |
| 606 | 622 | AstNodeIfVarExpr if_var_expr; |
| 607 | 623 | AstNodeWhileExpr while_expr; |
| 624 | AstNodeForExpr for_expr; | |
| 608 | 625 | AstNodeLabel label; |
| 609 | 626 | AstNodeGoto goto_expr; |
| 610 | 627 | AstNodeAsmExpr asm_expr; |
| ... | ... | @@ -916,7 +933,8 @@ struct VariableTableEntry { |
| 916 | 933 | bool is_ptr; // if true, value_ref is a pointer |
| 917 | 934 | AstNode *decl_node; |
| 918 | 935 | LLVMZigDILocalVariable *di_loc_var; |
| 919 | int arg_index; | |
| 936 | int src_arg_index; | |
| 937 | int gen_arg_index; | |
| 920 | 938 | }; |
| 921 | 939 | |
| 922 | 940 | struct BlockContext { |
| ... | ... | @@ -927,8 +945,8 @@ struct BlockContext { |
| 927 | 945 | HashMap<Buf *, TypeTableEntry *, buf_hash, buf_eql_buf> type_table; |
| 928 | 946 | ZigList<Cast *> cast_expr_alloca_list; |
| 929 | 947 | ZigList<StructValExprCodeGen *> struct_val_expr_alloca_list; |
| 948 | ZigList<VariableTableEntry *> variable_list; | |
| 930 | 949 | AstNode *parent_loop_node; |
| 931 | AstNode *next_child_parent_loop_node; | |
| 932 | 950 | LLVMZigDIScope *di_scope; |
| 933 | 951 | }; |
| 934 | 952 |
src/analyze.cpp+124-56| ... | ... | @@ -60,6 +60,7 @@ static AstNode *first_executing_node(AstNode *node) { |
| 60 | 60 | case NodeTypeStructField: |
| 61 | 61 | case NodeTypeStructValueField: |
| 62 | 62 | case NodeTypeWhileExpr: |
| 63 | case NodeTypeForExpr: | |
| 63 | 64 | case NodeTypeContainerInitExpr: |
| 64 | 65 | case NodeTypeArrayType: |
| 65 | 66 | return node; |
| ... | ... | @@ -867,6 +868,7 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode |
| 867 | 868 | case NodeTypeIfBoolExpr: |
| 868 | 869 | case NodeTypeIfVarExpr: |
| 869 | 870 | case NodeTypeWhileExpr: |
| 871 | case NodeTypeForExpr: | |
| 870 | 872 | case NodeTypeLabel: |
| 871 | 873 | case NodeTypeGoto: |
| 872 | 874 | case NodeTypeBreak: |
| ... | ... | @@ -1175,12 +1177,7 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) { |
| 1175 | 1177 | context->type_table.init(8); |
| 1176 | 1178 | |
| 1177 | 1179 | if (parent) { |
| 1178 | if (parent->next_child_parent_loop_node) { | |
| 1179 | context->parent_loop_node = parent->next_child_parent_loop_node; | |
| 1180 | parent->next_child_parent_loop_node = nullptr; | |
| 1181 | } else { | |
| 1182 | context->parent_loop_node = parent->parent_loop_node; | |
| 1183 | } | |
| 1180 | context->parent_loop_node = parent->parent_loop_node; | |
| 1184 | 1181 | } |
| 1185 | 1182 | |
| 1186 | 1183 | if (node && node->type == NodeTypeFnDef) { |
| ... | ... | @@ -1986,6 +1983,36 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, |
| 1986 | 1983 | zig_unreachable(); |
| 1987 | 1984 | } |
| 1988 | 1985 | |
| 1986 | // Set name to nullptr to make the variable anonymous (not visible to programmer). | |
| 1987 | static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, BlockContext *context, | |
| 1988 | Buf *name, TypeTableEntry *type_entry, bool is_const) | |
| 1989 | { | |
| 1990 | VariableTableEntry *variable_entry = allocate<VariableTableEntry>(1); | |
| 1991 | variable_entry->type = type_entry; | |
| 1992 | ||
| 1993 | if (name) { | |
| 1994 | buf_init_from_buf(&variable_entry->name, name); | |
| 1995 | VariableTableEntry *existing_var = find_local_variable(context, name); | |
| 1996 | ||
| 1997 | if (existing_var) { | |
| 1998 | add_node_error(g, source_node, buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); | |
| 1999 | variable_entry->type = g->builtin_types.entry_invalid; | |
| 2000 | } | |
| 2001 | ||
| 2002 | context->variable_table.put(&variable_entry->name, variable_entry); | |
| 2003 | context->variable_list.append(variable_entry); | |
| 2004 | } else { | |
| 2005 | buf_init_from_str(&variable_entry->name, "_anon"); | |
| 2006 | context->variable_list.append(variable_entry); | |
| 2007 | } | |
| 2008 | ||
| 2009 | variable_entry->is_const = is_const; | |
| 2010 | variable_entry->is_ptr = true; | |
| 2011 | variable_entry->decl_node = source_node; | |
| 2012 | ||
| 2013 | return variable_entry; | |
| 2014 | } | |
| 2015 | ||
| 1989 | 2016 | static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import, |
| 1990 | 2017 | BlockContext *context, AstNode *source_node, |
| 1991 | 2018 | AstNodeVariableDeclaration *variable_declaration, |
| ... | ... | @@ -2037,38 +2064,26 @@ static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTa |
| 2037 | 2064 | TypeTableEntry *type = explicit_type != nullptr ? explicit_type : implicit_type; |
| 2038 | 2065 | assert(type != nullptr); // should have been caught by the parser |
| 2039 | 2066 | |
| 2040 | VariableTableEntry *existing_variable = find_local_variable(context, &variable_declaration->symbol); | |
| 2041 | if (existing_variable) { | |
| 2042 | add_node_error(g, source_node, | |
| 2043 | buf_sprintf("redeclaration of variable '%s'", buf_ptr(&variable_declaration->symbol))); | |
| 2044 | } else { | |
| 2045 | VariableTableEntry *variable_entry = allocate<VariableTableEntry>(1); | |
| 2046 | buf_init_from_buf(&variable_entry->name, &variable_declaration->symbol); | |
| 2047 | variable_entry->type = type; | |
| 2048 | variable_entry->is_const = variable_declaration->is_const; | |
| 2049 | variable_entry->is_ptr = true; | |
| 2050 | variable_entry->decl_node = source_node; | |
| 2051 | context->variable_table.put(&variable_entry->name, variable_entry); | |
| 2067 | VariableTableEntry *var = add_local_var(g, source_node, context, | |
| 2068 | &variable_declaration->symbol, type, variable_declaration->is_const); | |
| 2052 | 2069 | |
| 2053 | bool is_pub = (variable_declaration->visib_mod != VisibModPrivate); | |
| 2054 | if (is_pub) { | |
| 2055 | for (int i = 0; i < import->importers.length; i += 1) { | |
| 2056 | ImporterInfo importer = import->importers.at(i); | |
| 2057 | auto table_entry = importer.import->block_context->variable_table.maybe_get(&variable_entry->name); | |
| 2058 | if (table_entry) { | |
| 2059 | add_node_error(g, importer.source_node, | |
| 2060 | buf_sprintf("import of variable '%s' overrides existing definition", | |
| 2061 | buf_ptr(&variable_entry->name))); | |
| 2062 | } else { | |
| 2063 | importer.import->block_context->variable_table.put(&variable_entry->name, variable_entry); | |
| 2064 | } | |
| 2070 | ||
| 2071 | bool is_pub = (variable_declaration->visib_mod != VisibModPrivate); | |
| 2072 | if (is_pub) { | |
| 2073 | for (int i = 0; i < import->importers.length; i += 1) { | |
| 2074 | ImporterInfo importer = import->importers.at(i); | |
| 2075 | auto table_entry = importer.import->block_context->variable_table.maybe_get(&var->name); | |
| 2076 | if (table_entry) { | |
| 2077 | add_node_error(g, importer.source_node, | |
| 2078 | buf_sprintf("import of variable '%s' overrides existing definition", | |
| 2079 | buf_ptr(&var->name))); | |
| 2080 | } else { | |
| 2081 | importer.import->block_context->variable_table.put(&var->name, var); | |
| 2065 | 2082 | } |
| 2066 | 2083 | } |
| 2067 | ||
| 2068 | ||
| 2069 | return variable_entry; | |
| 2070 | 2084 | } |
| 2071 | return nullptr; | |
| 2085 | ||
| 2086 | return var; | |
| 2072 | 2087 | } |
| 2073 | 2088 | |
| 2074 | 2089 | static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableEntry *import, |
| ... | ... | @@ -2172,11 +2187,15 @@ static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, |
| 2172 | 2187 | |
| 2173 | 2188 | AstNode *condition_node = node->data.while_expr.condition; |
| 2174 | 2189 | AstNode *while_body_node = node->data.while_expr.body; |
| 2190 | ||
| 2175 | 2191 | TypeTableEntry *condition_type = analyze_expression(g, import, context, |
| 2176 | 2192 | g->builtin_types.entry_bool, condition_node); |
| 2177 | 2193 | |
| 2178 | context->next_child_parent_loop_node = node; | |
| 2179 | analyze_expression(g, import, context, g->builtin_types.entry_void, while_body_node); | |
| 2194 | BlockContext *child_context = new_block_context(node, context); | |
| 2195 | child_context->parent_loop_node = node; | |
| 2196 | node->data.while_expr.block_context = child_context; | |
| 2197 | ||
| 2198 | analyze_expression(g, import, child_context, g->builtin_types.entry_void, while_body_node); | |
| 2180 | 2199 | |
| 2181 | 2200 | |
| 2182 | 2201 | TypeTableEntry *expr_return_type = g->builtin_types.entry_void; |
| ... | ... | @@ -2200,6 +2219,54 @@ static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, |
| 2200 | 2219 | return expr_return_type; |
| 2201 | 2220 | } |
| 2202 | 2221 | |
| 2222 | static TypeTableEntry *analyze_for_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | |
| 2223 | TypeTableEntry *expected_type, AstNode *node) | |
| 2224 | { | |
| 2225 | assert(node->type == NodeTypeForExpr); | |
| 2226 | ||
| 2227 | AstNode *array_node = node->data.for_expr.array_expr; | |
| 2228 | TypeTableEntry *array_type = analyze_expression(g, import, context, nullptr, array_node); | |
| 2229 | TypeTableEntry *child_type; | |
| 2230 | if (array_type->id == TypeTableEntryIdInvalid) { | |
| 2231 | child_type = array_type; | |
| 2232 | } else if (array_type->id == TypeTableEntryIdArray) { | |
| 2233 | child_type = array_type->data.array.child_type; | |
| 2234 | } else if (array_type->id == TypeTableEntryIdStruct && | |
| 2235 | array_type->data.structure.is_unknown_size_array) | |
| 2236 | { | |
| 2237 | TypeTableEntry *pointer_type = array_type->data.structure.fields[0].type_entry; | |
| 2238 | assert(pointer_type->id == TypeTableEntryIdPointer); | |
| 2239 | child_type = pointer_type->data.pointer.child_type; | |
| 2240 | } else { | |
| 2241 | add_node_error(g, node, | |
| 2242 | buf_sprintf("iteration over non array type '%s'", buf_ptr(&array_type->name))); | |
| 2243 | child_type = g->builtin_types.entry_invalid; | |
| 2244 | } | |
| 2245 | ||
| 2246 | BlockContext *child_context = new_block_context(node, context); | |
| 2247 | node->data.for_expr.block_context = child_context; | |
| 2248 | ||
| 2249 | AstNode *elem_var_node = node->data.for_expr.elem_node; | |
| 2250 | Buf *elem_var_name = &elem_var_node->data.symbol_expr.symbol; | |
| 2251 | node->data.for_expr.elem_var = add_local_var(g, elem_var_node, child_context, elem_var_name, child_type, true); | |
| 2252 | ||
| 2253 | AstNode *index_var_node = node->data.for_expr.index_node; | |
| 2254 | if (index_var_node) { | |
| 2255 | Buf *index_var_name = &index_var_node->data.symbol_expr.symbol; | |
| 2256 | node->data.for_expr.index_var = add_local_var(g, index_var_node, child_context, index_var_name, | |
| 2257 | g->builtin_types.entry_usize, true); | |
| 2258 | } else { | |
| 2259 | node->data.for_expr.index_var = add_local_var(g, node, child_context, nullptr, | |
| 2260 | g->builtin_types.entry_usize, true); | |
| 2261 | } | |
| 2262 | ||
| 2263 | AstNode *for_body_node = node->data.for_expr.body; | |
| 2264 | analyze_expression(g, import, child_context, g->builtin_types.entry_void, for_body_node); | |
| 2265 | ||
| 2266 | ||
| 2267 | return g->builtin_types.entry_void; | |
| 2268 | } | |
| 2269 | ||
| 2203 | 2270 | static TypeTableEntry *analyze_break_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 2204 | 2271 | TypeTableEntry *expected_type, AstNode *node) |
| 2205 | 2272 | { |
| ... | ... | @@ -3018,6 +3085,9 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, |
| 3018 | 3085 | case NodeTypeWhileExpr: |
| 3019 | 3086 | return_type = analyze_while_expr(g, import, context, expected_type, node); |
| 3020 | 3087 | break; |
| 3088 | case NodeTypeForExpr: | |
| 3089 | return_type = analyze_for_expr(g, import, context, expected_type, node); | |
| 3090 | break; | |
| 3021 | 3091 | case NodeTypeArrayType: |
| 3022 | 3092 | return_type = analyze_array_type(g, import, context, expected_type, node); |
| 3023 | 3093 | break; |
| ... | ... | @@ -3081,6 +3151,7 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo |
| 3081 | 3151 | |
| 3082 | 3152 | AstNodeFnProto *fn_proto = &fn_proto_node->data.fn_proto; |
| 3083 | 3153 | bool is_exported = (fn_proto->visib_mod == VisibModExport); |
| 3154 | int gen_arg_index = 0; | |
| 3084 | 3155 | for (int i = 0; i < fn_proto->params.length; i += 1) { |
| 3085 | 3156 | AstNode *param_decl_node = fn_proto->params.at(i); |
| 3086 | 3157 | assert(param_decl_node->type == NodeTypeParamDecl); |
| ... | ... | @@ -3099,28 +3170,15 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo |
| 3099 | 3170 | buf_sprintf("byvalue struct parameters not yet supported on exported functions")); |
| 3100 | 3171 | } |
| 3101 | 3172 | |
| 3102 | VariableTableEntry *variable_entry = allocate<VariableTableEntry>(1); | |
| 3103 | buf_init_from_buf(&variable_entry->name, &param_decl->name); | |
| 3104 | variable_entry->type = type; | |
| 3105 | variable_entry->is_const = true; | |
| 3106 | variable_entry->decl_node = param_decl_node; | |
| 3107 | variable_entry->arg_index = i; | |
| 3173 | VariableTableEntry *var = add_local_var(g, param_decl_node, context, &param_decl->name, type, true); | |
| 3174 | var->src_arg_index = i; | |
| 3175 | param_decl_node->data.param_decl.variable = var; | |
| 3108 | 3176 | |
| 3109 | param_decl_node->data.param_decl.variable = variable_entry; | |
| 3110 | ||
| 3111 | VariableTableEntry *existing_entry = find_local_variable(context, &variable_entry->name); | |
| 3112 | if (!existing_entry) { | |
| 3113 | // unique definition | |
| 3114 | context->variable_table.put(&variable_entry->name, variable_entry); | |
| 3177 | if (type->size_in_bits > 0) { | |
| 3178 | var->gen_arg_index = gen_arg_index; | |
| 3179 | gen_arg_index += 1; | |
| 3115 | 3180 | } else { |
| 3116 | add_node_error(g, node, | |
| 3117 | buf_sprintf("redeclaration of parameter '%s'.", buf_ptr(&existing_entry->name))); | |
| 3118 | if (existing_entry->type == variable_entry->type) { | |
| 3119 | // types agree, so the type is probably good enough for the rest of analysis | |
| 3120 | } else { | |
| 3121 | // types disagree. don't trust either one of them. | |
| 3122 | existing_entry->type = g->builtin_types.entry_invalid;; | |
| 3123 | } | |
| 3181 | var->gen_arg_index = -1; | |
| 3124 | 3182 | } |
| 3125 | 3183 | } |
| 3126 | 3184 | |
| ... | ... | @@ -3187,6 +3245,7 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode |
| 3187 | 3245 | case NodeTypeIfBoolExpr: |
| 3188 | 3246 | case NodeTypeIfVarExpr: |
| 3189 | 3247 | case NodeTypeWhileExpr: |
| 3248 | case NodeTypeForExpr: | |
| 3190 | 3249 | case NodeTypeLabel: |
| 3191 | 3250 | case NodeTypeGoto: |
| 3192 | 3251 | case NodeTypeBreak: |
| ... | ... | @@ -3281,6 +3340,10 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode |
| 3281 | 3340 | collect_expr_decl_deps(g, import, node->data.while_expr.condition, decl_node); |
| 3282 | 3341 | collect_expr_decl_deps(g, import, node->data.while_expr.body, decl_node); |
| 3283 | 3342 | break; |
| 3343 | case NodeTypeForExpr: | |
| 3344 | collect_expr_decl_deps(g, import, node->data.for_expr.array_expr, decl_node); | |
| 3345 | collect_expr_decl_deps(g, import, node->data.for_expr.body, decl_node); | |
| 3346 | break; | |
| 3284 | 3347 | case NodeTypeBlock: |
| 3285 | 3348 | for (int i = 0; i < node->data.block.statements.length; i += 1) { |
| 3286 | 3349 | AstNode *stmt = node->data.block.statements.at(i); |
| ... | ... | @@ -3505,6 +3568,7 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast |
| 3505 | 3568 | case NodeTypeIfBoolExpr: |
| 3506 | 3569 | case NodeTypeIfVarExpr: |
| 3507 | 3570 | case NodeTypeWhileExpr: |
| 3571 | case NodeTypeForExpr: | |
| 3508 | 3572 | case NodeTypeLabel: |
| 3509 | 3573 | case NodeTypeGoto: |
| 3510 | 3574 | case NodeTypeBreak: |
| ... | ... | @@ -3681,6 +3745,8 @@ Expr *get_resolved_expr(AstNode *node) { |
| 3681 | 3745 | return &node->data.if_var_expr.resolved_expr; |
| 3682 | 3746 | case NodeTypeWhileExpr: |
| 3683 | 3747 | return &node->data.while_expr.resolved_expr; |
| 3748 | case NodeTypeForExpr: | |
| 3749 | return &node->data.for_expr.resolved_expr; | |
| 3684 | 3750 | case NodeTypeAsmExpr: |
| 3685 | 3751 | return &node->data.asm_expr.resolved_expr; |
| 3686 | 3752 | case NodeTypeContainerInitExpr: |
| ... | ... | @@ -3743,6 +3809,7 @@ NumLitCodeGen *get_resolved_num_lit(AstNode *node) { |
| 3743 | 3809 | case NodeTypeIfBoolExpr: |
| 3744 | 3810 | case NodeTypeIfVarExpr: |
| 3745 | 3811 | case NodeTypeWhileExpr: |
| 3812 | case NodeTypeForExpr: | |
| 3746 | 3813 | case NodeTypeAsmExpr: |
| 3747 | 3814 | case NodeTypeContainerInitExpr: |
| 3748 | 3815 | case NodeTypeRoot: |
| ... | ... | @@ -3793,6 +3860,7 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) { |
| 3793 | 3860 | case NodeTypeIfBoolExpr: |
| 3794 | 3861 | case NodeTypeIfVarExpr: |
| 3795 | 3862 | case NodeTypeWhileExpr: |
| 3863 | case NodeTypeForExpr: | |
| 3796 | 3864 | case NodeTypeAsmExpr: |
| 3797 | 3865 | case NodeTypeContainerInitExpr: |
| 3798 | 3866 | case NodeTypeRoot: |
src/codegen.cpp+108-40| ... | ... | @@ -526,41 +526,35 @@ static LLVMValueRef gen_array_base_ptr(CodeGen *g, AstNode *node) { |
| 526 | 526 | return array_ptr; |
| 527 | 527 | } |
| 528 | 528 | |
| 529 | static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) { | |
| 530 | assert(node->type == NodeTypeArrayAccessExpr); | |
| 531 | ||
| 532 | AstNode *array_expr_node = node->data.array_access_expr.array_ref_expr; | |
| 533 | TypeTableEntry *type_entry = get_expr_type(array_expr_node); | |
| 534 | ||
| 535 | LLVMValueRef array_ptr = gen_array_base_ptr(g, array_expr_node); | |
| 536 | ||
| 537 | LLVMValueRef subscript_value = gen_expr(g, node->data.array_access_expr.subscript); | |
| 529 | static LLVMValueRef gen_array_elem_ptr(CodeGen *g, AstNode *source_node, LLVMValueRef array_ptr, | |
| 530 | TypeTableEntry *array_type, LLVMValueRef subscript_value) | |
| 531 | { | |
| 538 | 532 | assert(subscript_value); |
| 539 | 533 | |
| 540 | if (type_entry->size_in_bits == 0) { | |
| 534 | if (array_type->size_in_bits == 0) { | |
| 541 | 535 | return nullptr; |
| 542 | 536 | } |
| 543 | 537 | |
| 544 | if (type_entry->id == TypeTableEntryIdArray) { | |
| 538 | if (array_type->id == TypeTableEntryIdArray) { | |
| 545 | 539 | LLVMValueRef indices[] = { |
| 546 | 540 | LLVMConstNull(g->builtin_types.entry_usize->type_ref), |
| 547 | 541 | subscript_value |
| 548 | 542 | }; |
| 549 | add_debug_source_node(g, node); | |
| 543 | add_debug_source_node(g, source_node); | |
| 550 | 544 | return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); |
| 551 | } else if (type_entry->id == TypeTableEntryIdPointer) { | |
| 545 | } else if (array_type->id == TypeTableEntryIdPointer) { | |
| 552 | 546 | assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); |
| 553 | 547 | LLVMValueRef indices[] = { |
| 554 | 548 | subscript_value |
| 555 | 549 | }; |
| 556 | add_debug_source_node(g, node); | |
| 550 | add_debug_source_node(g, source_node); | |
| 557 | 551 | return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 1, ""); |
| 558 | } else if (type_entry->id == TypeTableEntryIdStruct) { | |
| 559 | assert(type_entry->data.structure.is_unknown_size_array); | |
| 552 | } else if (array_type->id == TypeTableEntryIdStruct) { | |
| 553 | assert(array_type->data.structure.is_unknown_size_array); | |
| 560 | 554 | assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); |
| 561 | 555 | assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); |
| 562 | 556 | |
| 563 | add_debug_source_node(g, node); | |
| 557 | add_debug_source_node(g, source_node); | |
| 564 | 558 | LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, 0, ""); |
| 565 | 559 | LLVMValueRef ptr = LLVMBuildLoad(g->builder, ptr_ptr, ""); |
| 566 | 560 | return LLVMBuildInBoundsGEP(g->builder, ptr, &subscript_value, 1, ""); |
| ... | ... | @@ -569,6 +563,19 @@ static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) { |
| 569 | 563 | } |
| 570 | 564 | } |
| 571 | 565 | |
| 566 | static LLVMValueRef gen_array_ptr(CodeGen *g, AstNode *node) { | |
| 567 | assert(node->type == NodeTypeArrayAccessExpr); | |
| 568 | ||
| 569 | AstNode *array_expr_node = node->data.array_access_expr.array_ref_expr; | |
| 570 | TypeTableEntry *array_type = get_expr_type(array_expr_node); | |
| 571 | ||
| 572 | LLVMValueRef array_ptr = gen_array_base_ptr(g, array_expr_node); | |
| 573 | ||
| 574 | LLVMValueRef subscript_value = gen_expr(g, node->data.array_access_expr.subscript); | |
| 575 | ||
| 576 | return gen_array_elem_ptr(g, node, array_ptr, array_type, subscript_value); | |
| 577 | } | |
| 578 | ||
| 572 | 579 | static LLVMValueRef gen_field_ptr(CodeGen *g, AstNode *node, TypeTableEntry **out_type_entry) { |
| 573 | 580 | assert(node->type == NodeTypeFieldAccessExpr); |
| 574 | 581 | |
| ... | ... | @@ -1695,10 +1702,13 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) { |
| 1695 | 1702 | assert(node->data.while_expr.condition); |
| 1696 | 1703 | assert(node->data.while_expr.body); |
| 1697 | 1704 | |
| 1705 | BlockContext *old_block_context = g->cur_block_context; | |
| 1706 | ||
| 1698 | 1707 | bool condition_always_true = node->data.while_expr.condition_always_true; |
| 1699 | 1708 | bool contains_break = node->data.while_expr.contains_break; |
| 1700 | 1709 | if (condition_always_true) { |
| 1701 | 1710 | // generate a forever loop |
| 1711 | g->cur_block_context = node->data.while_expr.block_context; | |
| 1702 | 1712 | |
| 1703 | 1713 | LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "WhileBody"); |
| 1704 | 1714 | LLVMBasicBlockRef end_block = nullptr; |
| ... | ... | @@ -1735,6 +1745,7 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) { |
| 1735 | 1745 | LLVMBuildBr(g->builder, cond_block); |
| 1736 | 1746 | |
| 1737 | 1747 | LLVMPositionBuilderAtEnd(g->builder, cond_block); |
| 1748 | g->cur_block_context = old_block_context; | |
| 1738 | 1749 | LLVMValueRef cond_val = gen_expr(g, node->data.while_expr.condition); |
| 1739 | 1750 | add_debug_source_node(g, node->data.while_expr.condition); |
| 1740 | 1751 | LLVMBuildCondBr(g->builder, cond_val, body_block, end_block); |
| ... | ... | @@ -1742,6 +1753,7 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) { |
| 1742 | 1753 | LLVMPositionBuilderAtEnd(g->builder, body_block); |
| 1743 | 1754 | g->break_block_stack.append(end_block); |
| 1744 | 1755 | g->continue_block_stack.append(cond_block); |
| 1756 | g->cur_block_context = node->data.while_expr.block_context; | |
| 1745 | 1757 | gen_expr(g, node->data.while_expr.body); |
| 1746 | 1758 | g->break_block_stack.pop(); |
| 1747 | 1759 | g->continue_block_stack.pop(); |
| ... | ... | @@ -1753,6 +1765,77 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) { |
| 1753 | 1765 | LLVMPositionBuilderAtEnd(g->builder, end_block); |
| 1754 | 1766 | } |
| 1755 | 1767 | |
| 1768 | g->cur_block_context = old_block_context; | |
| 1769 | return nullptr; | |
| 1770 | } | |
| 1771 | ||
| 1772 | static LLVMValueRef gen_for_expr(CodeGen *g, AstNode *node) { | |
| 1773 | assert(node->type == NodeTypeForExpr); | |
| 1774 | assert(node->data.for_expr.array_expr); | |
| 1775 | assert(node->data.for_expr.body); | |
| 1776 | ||
| 1777 | VariableTableEntry *elem_var = node->data.for_expr.elem_var; | |
| 1778 | assert(elem_var); | |
| 1779 | ||
| 1780 | TypeTableEntry *array_type = get_expr_type(node->data.for_expr.array_expr); | |
| 1781 | ||
| 1782 | VariableTableEntry *index_var = node->data.for_expr.index_var; | |
| 1783 | assert(index_var); | |
| 1784 | LLVMValueRef index_ptr = index_var->value_ref; | |
| 1785 | LLVMValueRef one_const = LLVMConstInt(g->builtin_types.entry_usize->type_ref, 1, false); | |
| 1786 | ||
| 1787 | BlockContext *old_block_context = g->cur_block_context; | |
| 1788 | ||
| 1789 | LLVMBasicBlockRef cond_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForCond"); | |
| 1790 | LLVMBasicBlockRef body_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForBody"); | |
| 1791 | LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "ForEnd"); | |
| 1792 | ||
| 1793 | LLVMValueRef array_val = gen_expr(g, node->data.for_expr.array_expr); | |
| 1794 | add_debug_source_node(g, node); | |
| 1795 | LLVMBuildStore(g->builder, LLVMConstNull(index_var->type->type_ref), index_ptr); | |
| 1796 | LLVMValueRef len_val; | |
| 1797 | TypeTableEntry *child_type; | |
| 1798 | if (array_type->id == TypeTableEntryIdArray) { | |
| 1799 | len_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, | |
| 1800 | array_type->data.array.len, false); | |
| 1801 | child_type = array_type->data.array.child_type; | |
| 1802 | } else if (array_type->id == TypeTableEntryIdStruct) { | |
| 1803 | assert(array_type->data.structure.is_unknown_size_array); | |
| 1804 | TypeTableEntry *child_ptr_type = array_type->data.structure.fields[0].type_entry; | |
| 1805 | assert(child_ptr_type->id == TypeTableEntryIdPointer); | |
| 1806 | child_type = child_ptr_type->data.pointer.child_type; | |
| 1807 | LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, array_val, 1, ""); | |
| 1808 | len_val = LLVMBuildLoad(g->builder, len_field_ptr, ""); | |
| 1809 | } else { | |
| 1810 | zig_unreachable(); | |
| 1811 | } | |
| 1812 | LLVMBuildBr(g->builder, cond_block); | |
| 1813 | ||
| 1814 | LLVMPositionBuilderAtEnd(g->builder, cond_block); | |
| 1815 | LLVMValueRef index_val = LLVMBuildLoad(g->builder, index_ptr, ""); | |
| 1816 | LLVMValueRef cond = LLVMBuildICmp(g->builder, LLVMIntSLT, index_val, len_val, ""); | |
| 1817 | LLVMBuildCondBr(g->builder, cond, body_block, end_block); | |
| 1818 | ||
| 1819 | LLVMPositionBuilderAtEnd(g->builder, body_block); | |
| 1820 | LLVMValueRef elem_ptr = gen_array_elem_ptr(g, node, array_val, array_type, index_val); | |
| 1821 | LLVMValueRef elem_val = handle_is_ptr(child_type) ? elem_ptr : LLVMBuildLoad(g->builder, elem_ptr, ""); | |
| 1822 | gen_assign_raw(g, node, BinOpTypeAssign, elem_var->value_ref, elem_val, | |
| 1823 | elem_var->type, child_type); | |
| 1824 | g->break_block_stack.append(end_block); | |
| 1825 | g->continue_block_stack.append(cond_block); | |
| 1826 | g->cur_block_context = node->data.for_expr.block_context; | |
| 1827 | gen_expr(g, node->data.for_expr.body); | |
| 1828 | g->break_block_stack.pop(); | |
| 1829 | g->continue_block_stack.pop(); | |
| 1830 | if (get_expr_type(node->data.for_expr.body)->id != TypeTableEntryIdUnreachable) { | |
| 1831 | add_debug_source_node(g, node); | |
| 1832 | LLVMValueRef new_index_val = LLVMBuildAdd(g->builder, index_val, one_const, ""); | |
| 1833 | LLVMBuildStore(g->builder, new_index_val, index_ptr); | |
| 1834 | LLVMBuildBr(g->builder, cond_block); | |
| 1835 | } | |
| 1836 | ||
| 1837 | LLVMPositionBuilderAtEnd(g->builder, end_block); | |
| 1838 | g->cur_block_context = old_block_context; | |
| 1756 | 1839 | return nullptr; |
| 1757 | 1840 | } |
| 1758 | 1841 | |
| ... | ... | @@ -1935,6 +2018,8 @@ static LLVMValueRef gen_expr_no_cast(CodeGen *g, AstNode *node) { |
| 1935 | 2018 | return gen_if_var_expr(g, node); |
| 1936 | 2019 | case NodeTypeWhileExpr: |
| 1937 | 2020 | return gen_while_expr(g, node); |
| 2021 | case NodeTypeForExpr: | |
| 2022 | return gen_for_expr(g, node); | |
| 1938 | 2023 | case NodeTypeAsmExpr: |
| 1939 | 2024 | return gen_asm_expr(g, node); |
| 1940 | 2025 | case NodeTypeNumberLiteral: |
| ... | ... | @@ -2177,22 +2262,6 @@ static void do_code_gen(CodeGen *g) { |
| 2177 | 2262 | |
| 2178 | 2263 | fn_def_node->data.fn_def.block_context->di_scope = LLVMZigSubprogramToScope(subprogram); |
| 2179 | 2264 | |
| 2180 | int non_void_param_count = count_non_void_params(g, &fn_proto->params); | |
| 2181 | assert(non_void_param_count == (int)LLVMCountParams(fn)); | |
| 2182 | LLVMValueRef *params = allocate<LLVMValueRef>(non_void_param_count); | |
| 2183 | LLVMGetParams(fn, params); | |
| 2184 | ||
| 2185 | int non_void_index = 0; | |
| 2186 | for (int param_i = 0; param_i < fn_proto->params.length; param_i += 1) { | |
| 2187 | AstNode *param_decl = fn_proto->params.at(param_i); | |
| 2188 | assert(param_decl->type == NodeTypeParamDecl); | |
| 2189 | if (is_param_decl_type_void(g, param_decl)) | |
| 2190 | continue; | |
| 2191 | VariableTableEntry *parameter_variable = fn_def_node->data.fn_def.block_context->variable_table.get(&param_decl->data.param_decl.name); | |
| 2192 | parameter_variable->value_ref = params[non_void_index]; | |
| 2193 | non_void_index += 1; | |
| 2194 | } | |
| 2195 | ||
| 2196 | 2265 | AstNode *body_node = fn_def_node->data.fn_def.body; |
| 2197 | 2266 | build_label_blocks(g, body_node); |
| 2198 | 2267 | |
| ... | ... | @@ -2212,13 +2281,9 @@ static void do_code_gen(CodeGen *g) { |
| 2212 | 2281 | |
| 2213 | 2282 | g->cur_block_context = block_context; |
| 2214 | 2283 | |
| 2215 | auto it = block_context->variable_table.entry_iterator(); | |
| 2216 | for (;;) { | |
| 2217 | auto *entry = it.next(); | |
| 2218 | if (!entry) | |
| 2219 | break; | |
| 2284 | for (int var_i = 0; var_i < block_context->variable_list.length; var_i += 1) { | |
| 2285 | VariableTableEntry *var = block_context->variable_list.at(var_i); | |
| 2220 | 2286 | |
| 2221 | VariableTableEntry *var = entry->value; | |
| 2222 | 2287 | if (var->type->size_in_bits == 0) { |
| 2223 | 2288 | continue; |
| 2224 | 2289 | } |
| ... | ... | @@ -2227,7 +2292,10 @@ static void do_code_gen(CodeGen *g) { |
| 2227 | 2292 | unsigned arg_no; |
| 2228 | 2293 | if (block_context->node->type == NodeTypeFnDef) { |
| 2229 | 2294 | tag = LLVMZigTag_DW_arg_variable(); |
| 2230 | arg_no = var->arg_index + 1; | |
| 2295 | arg_no = var->gen_arg_index + 1; | |
| 2296 | ||
| 2297 | var->is_ptr = false; | |
| 2298 | var->value_ref = LLVMGetParam(fn, var->gen_arg_index); | |
| 2231 | 2299 | } else { |
| 2232 | 2300 | tag = LLVMZigTag_DW_auto_variable(); |
| 2233 | 2301 | arg_no = 0; |
src/parser.cpp+57-1| ... | ... | @@ -121,6 +121,8 @@ const char *node_type_str(NodeType node_type) { |
| 121 | 121 | return "IfVarExpr"; |
| 122 | 122 | case NodeTypeWhileExpr: |
| 123 | 123 | return "WhileExpr"; |
| 124 | case NodeTypeForExpr: | |
| 125 | return "ForExpr"; | |
| 124 | 126 | case NodeTypeLabel: |
| 125 | 127 | return "Label"; |
| 126 | 128 | case NodeTypeGoto: |
| ... | ... | @@ -331,6 +333,15 @@ void ast_print(AstNode *node, int indent) { |
| 331 | 333 | ast_print(node->data.while_expr.condition, indent + 2); |
| 332 | 334 | ast_print(node->data.while_expr.body, indent + 2); |
| 333 | 335 | break; |
| 336 | case NodeTypeForExpr: | |
| 337 | fprintf(stderr, "%s\n", node_type_str(node->type)); | |
| 338 | ast_print(node->data.for_expr.elem_node, indent + 2); | |
| 339 | ast_print(node->data.for_expr.array_expr, indent + 2); | |
| 340 | if (node->data.for_expr.index_node) { | |
| 341 | ast_print(node->data.for_expr.index_node, indent + 2); | |
| 342 | } | |
| 343 | ast_print(node->data.for_expr.body, indent + 2); | |
| 344 | break; | |
| 334 | 345 | case NodeTypeLabel: |
| 335 | 346 | fprintf(stderr, "%s '%s'\n", node_type_str(node->type), buf_ptr(&node->data.label.name)); |
| 336 | 347 | break; |
| ... | ... | @@ -2114,8 +2125,49 @@ static AstNode *ast_parse_while_expr(ParseContext *pc, int *token_index, bool ma |
| 2114 | 2125 | return node; |
| 2115 | 2126 | } |
| 2116 | 2127 | |
| 2128 | static AstNode *ast_parse_symbol(ParseContext *pc, int *token_index) { | |
| 2129 | Token *token = ast_eat_token(pc, token_index, TokenIdSymbol); | |
| 2130 | AstNode *node = ast_create_node(pc, NodeTypeSymbol, token); | |
| 2131 | ast_buf_from_token(pc, token, &node->data.symbol_expr.symbol); | |
| 2132 | return node; | |
| 2133 | } | |
| 2134 | ||
| 2117 | 2135 | /* |
| 2118 | BlockExpression : IfExpression | Block | WhileExpression | |
| 2136 | ForExpression : token(For) token(LParen) Symbol token(Comma) Expression option(token(Comma) token(Symbol)) token(RParen) Expression | |
| 2137 | */ | |
| 2138 | static AstNode *ast_parse_for_expr(ParseContext *pc, int *token_index, bool mandatory) { | |
| 2139 | Token *token = &pc->tokens->at(*token_index); | |
| 2140 | ||
| 2141 | if (token->id != TokenIdKeywordFor) { | |
| 2142 | if (mandatory) { | |
| 2143 | ast_invalid_token_error(pc, token); | |
| 2144 | } else { | |
| 2145 | return nullptr; | |
| 2146 | } | |
| 2147 | } | |
| 2148 | *token_index += 1; | |
| 2149 | ||
| 2150 | AstNode *node = ast_create_node(pc, NodeTypeForExpr, token); | |
| 2151 | ||
| 2152 | ast_eat_token(pc, token_index, TokenIdLParen); | |
| 2153 | node->data.for_expr.elem_node = ast_parse_symbol(pc, token_index); | |
| 2154 | ast_eat_token(pc, token_index, TokenIdComma); | |
| 2155 | node->data.for_expr.array_expr = ast_parse_expression(pc, token_index, true); | |
| 2156 | ||
| 2157 | Token *comma = &pc->tokens->at(*token_index); | |
| 2158 | if (comma->id == TokenIdComma) { | |
| 2159 | *token_index += 1; | |
| 2160 | node->data.for_expr.index_node = ast_parse_symbol(pc, token_index); | |
| 2161 | } | |
| 2162 | ||
| 2163 | ast_eat_token(pc, token_index, TokenIdRParen); | |
| 2164 | ||
| 2165 | node->data.for_expr.body = ast_parse_expression(pc, token_index, true); | |
| 2166 | return node; | |
| 2167 | } | |
| 2168 | ||
| 2169 | /* | |
| 2170 | BlockExpression : IfExpression | Block | WhileExpression | ForExpression | |
| 2119 | 2171 | */ |
| 2120 | 2172 | static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool mandatory) { |
| 2121 | 2173 | Token *token = &pc->tokens->at(*token_index); |
| ... | ... | @@ -2132,6 +2184,10 @@ static AstNode *ast_parse_block_expr(ParseContext *pc, int *token_index, bool ma |
| 2132 | 2184 | if (while_expr) |
| 2133 | 2185 | return while_expr; |
| 2134 | 2186 | |
| 2187 | AstNode *for_expr = ast_parse_for_expr(pc, token_index, false); | |
| 2188 | if (for_expr) | |
| 2189 | return for_expr; | |
| 2190 | ||
| 2135 | 2191 | if (mandatory) |
| 2136 | 2192 | ast_invalid_token_error(pc, token); |
| 2137 | 2193 |
src/tokenizer.cpp+3| ... | ... | @@ -231,6 +231,8 @@ static void end_token(Tokenize *t) { |
| 231 | 231 | t->cur_tok->id = TokenIdKeywordStruct; |
| 232 | 232 | } else if (mem_eql_str(token_mem, token_len, "enum")) { |
| 233 | 233 | t->cur_tok->id = TokenIdKeywordEnum; |
| 234 | } else if (mem_eql_str(token_mem, token_len, "for")) { | |
| 235 | t->cur_tok->id = TokenIdKeywordFor; | |
| 234 | 236 | } else if (mem_eql_str(token_mem, token_len, "while")) { |
| 235 | 237 | t->cur_tok->id = TokenIdKeywordWhile; |
| 236 | 238 | } else if (mem_eql_str(token_mem, token_len, "continue")) { |
| ... | ... | @@ -1028,6 +1030,7 @@ const char * token_name(TokenId id) { |
| 1028 | 1030 | case TokenIdKeywordStruct: return "struct"; |
| 1029 | 1031 | case TokenIdKeywordEnum: return "enum"; |
| 1030 | 1032 | case TokenIdKeywordWhile: return "while"; |
| 1033 | case TokenIdKeywordFor: return "for"; | |
| 1031 | 1034 | case TokenIdKeywordContinue: return "continue"; |
| 1032 | 1035 | case TokenIdKeywordBreak: return "break"; |
| 1033 | 1036 | case TokenIdKeywordNull: return "null"; |
src/tokenizer.hpp+1| ... | ... | @@ -31,6 +31,7 @@ enum TokenId { |
| 31 | 31 | TokenIdKeywordStruct, |
| 32 | 32 | TokenIdKeywordEnum, |
| 33 | 33 | TokenIdKeywordWhile, |
| 34 | TokenIdKeywordFor, | |
| 34 | 35 | TokenIdKeywordContinue, |
| 35 | 36 | TokenIdKeywordBreak, |
| 36 | 37 | TokenIdKeywordNull, |
std/bootstrap.zig+1-4| ... | ... | @@ -25,12 +25,9 @@ fn strlen(ptr: &u8) usize => { |
| 25 | 25 | |
| 26 | 26 | fn call_main() unreachable => { |
| 27 | 27 | var args: [argc][]u8; |
| 28 | var i : @typeof(argc) = 0; | |
| 29 | // TODO for in loop over the array | |
| 30 | while (i < argc) { | |
| 28 | for (arg, args, i) { | |
| 31 | 29 | const ptr = argv[i]; |
| 32 | 30 | args[i] = ptr[0...strlen(ptr)]; |
| 33 | i += 1; | |
| 34 | 31 | } |
| 35 | 32 | exit(main(args)) |
| 36 | 33 | } |
test/run_tests.cpp+27-1| ... | ... | @@ -1136,6 +1136,32 @@ pub fn main(args: [][]u8) i32 => { |
| 1136 | 1136 | return 0; |
| 1137 | 1137 | } |
| 1138 | 1138 | )SOURCE", "hello\nthis\nis\nmy\nthing\n"); |
| 1139 | ||
| 1140 | add_simple_case("for loops", R"SOURCE( | |
| 1141 | import "std.zig"; | |
| 1142 | ||
| 1143 | pub fn main(args: [][]u8) i32 => { | |
| 1144 | const array = []u8 {9, 8, 7, 6}; | |
| 1145 | for (item, array) { | |
| 1146 | print_u64(item); | |
| 1147 | print_str("\n"); | |
| 1148 | } | |
| 1149 | for (item, array, index) { | |
| 1150 | print_u64(index); | |
| 1151 | print_str("\n"); | |
| 1152 | } | |
| 1153 | const unknown_size: []u8 = array; | |
| 1154 | for (item, unknown_size) { | |
| 1155 | print_u64(item); | |
| 1156 | print_str("\n"); | |
| 1157 | } | |
| 1158 | for (item, unknown_size, index) { | |
| 1159 | print_u64(index); | |
| 1160 | print_str("\n"); | |
| 1161 | } | |
| 1162 | return 0; | |
| 1163 | } | |
| 1164 | )SOURCE", "9\n8\n7\n6\n0\n1\n2\n3\n9\n8\n7\n6\n0\n1\n2\n3\n"); | |
| 1139 | 1165 | } |
| 1140 | 1166 | |
| 1141 | 1167 | |
| ... | ... | @@ -1226,7 +1252,7 @@ fn b() => {} |
| 1226 | 1252 | add_compile_fail_case("parameter redeclaration", R"SOURCE( |
| 1227 | 1253 | fn f(a : i32, a : i32) => { |
| 1228 | 1254 | } |
| 1229 | )SOURCE", 1, ".tmp_source.zig:2:1: error: redeclaration of parameter 'a'"); | |
| 1255 | )SOURCE", 1, ".tmp_source.zig:2:15: error: redeclaration of variable 'a'"); | |
| 1230 | 1256 | |
| 1231 | 1257 | add_compile_fail_case("local variable redeclaration", R"SOURCE( |
| 1232 | 1258 | fn f() => { |