From 6a2ede5a6eb17d6b86e6636457710a3583376fa3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 5 Feb 2016 23:20:34 -0700 Subject: [PATCH] parsing code for defer and more * disable goto and label support see #44 * refactor the way block contexts work --- doc/langref.md | 4 +- src/all_types.hpp | 60 +++++--- src/analyze.cpp | 362 ++++++++++++++++++++++----------------------- src/analyze.hpp | 2 - src/ast_render.cpp | 12 ++ src/codegen.cpp | 159 ++++++++++---------- src/parser.cpp | 35 +++-- src/tokenizer.cpp | 4 + src/tokenizer.hpp | 1 + std/std.zig | 2 + test/run_tests.cpp | 35 ----- 11 files changed, 347 insertions(+), 329 deletions(-) diff --git a/doc/langref.md b/doc/langref.md index 1aa570b59f..746c2f5843 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -51,7 +51,7 @@ Expression = BlockExpression | NonBlockExpression TypeExpr = PrefixOpExpression -NonBlockExpression = ReturnExpression | AssignmentExpression +NonBlockExpression = ReturnExpression | AssignmentExpression | DeferExpression AsmExpression = "asm" option("volatile") "(" "String" option(AsmOutput) ")" @@ -91,6 +91,8 @@ BoolOrExpression = BoolAndExpression "||" BoolOrExpression | BoolAndExpression ReturnExpression = option("%" | "?") "return" option(Expression) +DeferExpression = option("%" | "?") "defer" option(Expression) + IfExpression = IfVarExpression | IfBoolExpression IfBoolExpression = "if" "(" Expression ")" Expression option(Else) diff --git a/src/all_types.hpp b/src/all_types.hpp index 7e7accdc0e..167efe60fb 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -24,7 +24,6 @@ struct TypeTableEntry; struct VariableTableEntry; struct ErrorTableEntry; struct BuiltinFnEntry; -struct LabelTableEntry; struct TypeStructField; struct CodeGen; struct ConstExprValue; @@ -118,6 +117,7 @@ enum NodeType { NodeTypeBlock, NodeTypeDirective, NodeTypeReturnExpr, + NodeTypeDeferExpr, NodeTypeVariableDeclaration, NodeTypeTypeDecl, NodeTypeErrorValueDecl, @@ -235,6 +235,16 @@ struct AstNodeReturnExpr { Expr resolved_expr; }; +struct AstNodeDeferExpr { + ReturnKind kind; + AstNode *expr; + + // populated by semantic analyzer: + Expr resolved_expr; + int index_in_block; + LLVMBasicBlockRef basic_block; +}; + struct AstNodeVariableDeclaration { Buf symbol; bool is_const; @@ -477,7 +487,6 @@ struct AstNodeWhileExpr { bool contains_break; bool contains_continue; Expr resolved_expr; - BlockContext *block_context; }; struct AstNodeForExpr { @@ -522,7 +531,6 @@ struct AstNodeLabel { Buf name; // populated by semantic analyzer - LabelTableEntry *label_entry; Expr resolved_expr; }; @@ -530,7 +538,6 @@ struct AstNodeGoto { Buf name; // populated by semantic analyzer - LabelTableEntry *label_entry; Expr resolved_expr; }; @@ -732,6 +739,7 @@ struct AstNode { AstNodeParamDecl param_decl; AstNodeBlock block; AstNodeReturnExpr return_expr; + AstNodeDeferExpr defer_expr; AstNodeVariableDeclaration variable_declaration; AstNodeTypeDecl type_decl; AstNodeErrorValueDecl error_value_decl; @@ -967,13 +975,8 @@ struct ImportTableEntry { // reminder: hash tables must be initialized before use HashMap fn_table; -}; - -struct LabelTableEntry { - AstNode *label_node; - LLVMBasicBlockRef basic_block; - bool used; - bool entered_from_fallthrough; + HashMap type_table; + HashMap error_table; }; struct FnTableEntry { @@ -992,8 +995,9 @@ struct FnTableEntry { bool is_test; uint32_t ref_count; // if this is 0 we don't have to codegen it - // reminder: hash tables must be initialized before use - HashMap label_table; + ZigList cast_alloca_list; + ZigList struct_val_expr_alloca_list; + ZigList variable_list; }; enum BuiltinFnId { @@ -1140,6 +1144,7 @@ struct VariableTableEntry { LLVMZigDILocalVariable *di_loc_var; int src_arg_index; int gen_arg_index; + BlockContext *block_context; }; struct ErrorTableEntry { @@ -1148,19 +1153,32 @@ struct ErrorTableEntry { AstNode *decl_node; }; +enum BlockExitPath { + BlockExitPathFallthrough, + BlockExitPathReturn, + BlockExitPathGoto, +}; + struct BlockContext { - AstNode *node; // either NodeTypeFnDef or NodeTypeBlock or NodeTypeRoot - FnTableEntry *fn_entry; // null at the module scope - BlockContext *parent; // null when this is the root + // One of: NodeTypeFnDef, NodeTypeBlock, NodeTypeRoot, NodeTypeDeferExpr, NodeTypeVariableDeclaration + AstNode *node; + + // any variables that are introduced by this scope HashMap variable_table; - HashMap type_table; - HashMap error_table; - ZigList cast_alloca_list; - ZigList struct_val_expr_alloca_list; - ZigList variable_list; + + // if the block is inside a function, this is the function it is in: + FnTableEntry *fn_entry; + + // if the block has a parent, this is it + BlockContext *parent; + + // if break or continue is valid in this context, this is the loop node that + // it would pertain to AstNode *parent_loop_node; + LLVMZigDIScope *di_scope; Buf *c_import_buf; + bool block_exit_paths[3]; // one for each BlockExitPath }; enum CIntType { diff --git a/src/analyze.cpp b/src/analyze.cpp index 2d05c6499a..f5c9b5eba4 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -57,6 +57,7 @@ static AstNode *first_executing_node(AstNode *node) { case NodeTypeBlock: case NodeTypeDirective: case NodeTypeReturnExpr: + case NodeTypeDeferExpr: case NodeTypeVariableDeclaration: case NodeTypeTypeDecl: case NodeTypeErrorValueDecl: @@ -865,23 +866,6 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t } } -static void preview_function_labels(CodeGen *g, AstNode *node, FnTableEntry *fn_table_entry) { - assert(node->type == NodeTypeBlock); - - for (int i = 0; i < node->data.block.statements.length; i += 1) { - AstNode *label_node = node->data.block.statements.at(i); - if (label_node->type != NodeTypeLabel) - continue; - - LabelTableEntry *label_entry = allocate(1); - label_entry->label_node = label_node; - Buf *name = &label_node->data.label.name; - fn_table_entry->label_table.put(name, label_entry); - - label_node->data.label.label_entry = label_entry; - } -} - static void resolve_enum_type(CodeGen *g, ImportTableEntry *import, TypeTableEntry *enum_type) { // if you change this logic you likely must also change similar logic in parseh.cpp assert(enum_type->id == TypeTableEntryIdEnum); @@ -1233,7 +1217,6 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, fn_table_entry->fn_def_node = fn_def_node; fn_table_entry->internal_linkage = !is_c_compat; fn_table_entry->is_extern = is_extern; - fn_table_entry->label_table.init(8); fn_table_entry->member_of_struct = struct_type; fn_table_entry->ref_count = (proto_node->data.fn_proto.visib_mod == VisibModExport) ? 1 : 0; @@ -1272,10 +1255,6 @@ static void preview_fn_proto(CodeGen *g, ImportTableEntry *import, proto_node->data.fn_proto.fn_table_entry = fn_table_entry; resolve_function_proto(g, proto_node, fn_table_entry, import); - if (fn_def_node) { - preview_function_labels(g, fn_def_node->data.fn_def.body, fn_table_entry); - } - if (is_pub && !struct_type) { for (int i = 0; i < import->importers.length; i += 1) { ImporterInfo importer = import->importers.at(i); @@ -1317,13 +1296,13 @@ static void resolve_error_value_decl(CodeGen *g, ImportTableEntry *import, AstNo ErrorTableEntry *err = node->data.error_value_decl.err; - import->block_context->error_table.put(&err->name, err); + import->error_table.put(&err->name, err); bool is_pub = (node->data.error_value_decl.visib_mod != VisibModPrivate); if (is_pub) { for (int i = 0; i < import->importers.length; i += 1) { ImporterInfo importer = import->importers.at(i); - importer.import->block_context->error_table.put(&err->name, err); + importer.import->error_table.put(&err->name, err); } } } @@ -1347,6 +1326,8 @@ static void resolve_c_import_decl(CodeGen *g, ImportTableEntry *parent_import, A ImportTableEntry *child_import = allocate(1); child_import->fn_table.init(32); + child_import->type_table.init(8); + child_import->error_table.init(8); child_import->c_import_node = node; ZigList errors = {0}; @@ -1442,19 +1423,19 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode } } - import->block_context->type_table.put(decl_name, entry); + import->type_table.put(decl_name, entry); bool is_pub = (node->data.type_decl.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->type_table.maybe_get(&entry->name); + auto table_entry = importer.import->type_table.maybe_get(&entry->name); if (table_entry) { add_node_error(g, importer.source_node, buf_sprintf("import of type '%s' overrides existing definition", buf_ptr(&entry->name))); } else { - importer.import->block_context->type_table.put(&entry->name, entry); + importer.import->type_table.put(&entry->name, entry); } } } @@ -1475,6 +1456,7 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeParamDecl: case NodeTypeFnDecl: case NodeTypeReturnExpr: + case NodeTypeDeferExpr: case NodeTypeRoot: case NodeTypeBlock: case NodeTypeBinOpExpr: @@ -1953,9 +1935,7 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) { BlockContext *context = allocate(1); context->node = node; context->parent = parent; - context->variable_table.init(8); - context->type_table.init(8); - context->error_table.init(8); + context->variable_table.init(4); if (parent) { context->parent_loop_node = parent->parent_loop_node; @@ -1976,10 +1956,10 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent) { return context; } -static VariableTableEntry *find_local_variable(BlockContext *context, Buf *name) { - while (context && context->fn_entry) { +static VariableTableEntry *find_variable(BlockContext *context, Buf *name, bool local_only) { + while (context && (!local_only || context->fn_entry)) { auto entry = context->variable_table.maybe_get(name); - if (entry != nullptr) + if (entry) return entry->value; context = context->parent; @@ -1987,26 +1967,12 @@ static VariableTableEntry *find_local_variable(BlockContext *context, Buf *name) return nullptr; } -VariableTableEntry *find_variable(BlockContext *context, Buf *name) { - while (context) { - auto entry = context->variable_table.maybe_get(name); - if (entry != nullptr) - return entry->value; - - context = context->parent; - } - return nullptr; -} - -TypeTableEntry *find_container(BlockContext *context, Buf *name) { - while (context) { - auto entry = context->type_table.maybe_get(name); - if (entry != nullptr) - return entry->value; - - context = context->parent; - } - return nullptr; +static TypeTableEntry *find_container(ImportTableEntry *import, Buf *name) { + auto entry = import->type_table.maybe_get(name); + if (entry) + return entry->value; + else + return nullptr; } static TypeEnumField *get_enum_field(TypeTableEntry *enum_type, Buf *name) { @@ -2034,7 +2000,7 @@ static TypeTableEntry *analyze_enum_value_expr(CodeGen *g, ImportTableEntry *imp StructValExprCodeGen *codegen = &field_access_node->data.field_access_expr.resolved_struct_val_expr; codegen->type_entry = enum_type; codegen->source_node = field_access_node; - context->struct_val_expr_alloca_list.append(codegen); + context->fn_entry->struct_val_expr_alloca_list.append(codegen); Expr *expr = get_resolved_expr(field_access_node); expr->const_val.ok = false; @@ -2099,7 +2065,6 @@ static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry StructValExprCodeGen *codegen = &container_init_expr->resolved_struct_val_expr; codegen->type_entry = container_type; codegen->source_node = node; - context->struct_val_expr_alloca_list.append(codegen); int expr_field_count = container_init_expr->entries.length; @@ -2150,6 +2115,9 @@ static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry const_val->ok = false; } } + if (!const_val->ok) { + context->fn_entry->struct_val_expr_alloca_list.append(codegen); + } } for (int i = 0; i < actual_field_count; i += 1) { @@ -2192,7 +2160,9 @@ static TypeTableEntry *analyze_container_init_expr(CodeGen *g, ImportTableEntry StructValExprCodeGen *codegen = &container_init_expr->resolved_struct_val_expr; codegen->type_entry = fixed_size_array_type; codegen->source_node = node; - context->struct_val_expr_alloca_list.append(codegen); + if (!const_val->ok) { + context->fn_entry->struct_val_expr_alloca_list.append(codegen); + } return fixed_size_array_type; } else if (container_type->id == TypeTableEntryIdArray) { @@ -2339,7 +2309,7 @@ static TypeTableEntry *analyze_slice_expr(CodeGen *g, ImportTableEntry *import, if (return_type->id != TypeTableEntryIdInvalid) { node->data.slice_expr.resolved_struct_val_expr.type_entry = return_type; node->data.slice_expr.resolved_struct_val_expr.source_node = node; - context->struct_val_expr_alloca_list.append(&node->data.slice_expr.resolved_struct_val_expr); + context->fn_entry->struct_val_expr_alloca_list.append(&node->data.slice_expr.resolved_struct_val_expr); } analyze_expression(g, import, context, g->builtin_types.entry_isize, node->data.slice_expr.start); @@ -2518,7 +2488,7 @@ static TypeTableEntry *resolve_expr_const_val_as_bignum_op(CodeGen *g, AstNode * static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node, Buf *err_name) { - auto err_table_entry = import->block_context->error_table.maybe_get(err_name); + auto err_table_entry = import->error_table.maybe_get(err_name); if (err_table_entry) { return resolve_expr_const_val_as_err(g, node, err_table_entry->value); @@ -2545,7 +2515,7 @@ static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, return resolve_expr_const_val_as_type(g, node, primitive_table_entry->value); } - VariableTableEntry *var = find_variable(context, variable_name); + VariableTableEntry *var = find_variable(context, variable_name, false); if (var) { node->data.symbol_expr.variable = var; if (var->is_const) { @@ -2561,7 +2531,7 @@ static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, return var->type; } - TypeTableEntry *container_type = find_container(context, variable_name); + TypeTableEntry *container_type = find_container(import, variable_name); if (container_type) { return resolve_expr_const_val_as_type(g, node, container_type); } @@ -2641,7 +2611,7 @@ static TypeTableEntry *analyze_lvalue(CodeGen *g, ImportTableEntry *import, Bloc if (purpose == LValPurposeAddressOf) { expected_rhs_type = analyze_symbol_expr(g, import, block_context, nullptr, lhs_node); } else { - VariableTableEntry *var = find_variable(block_context, name); + VariableTableEntry *var = find_variable(block_context, name, false); if (var) { if (var->is_const) { add_node_error(g, lhs_node, buf_sprintf("cannot assign to constant")); @@ -3011,21 +2981,18 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, } // 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) +static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, ImportTableEntry *import, + BlockContext *context, Buf *name, TypeTableEntry *type_entry, bool is_const) { VariableTableEntry *variable_entry = allocate(1); variable_entry->type = type_entry; + variable_entry->block_context = context; if (name) { buf_init_from_buf(&variable_entry->name, name); VariableTableEntry *existing_var; - if (context->fn_entry) { - existing_var = find_local_variable(context, name); - } else { - existing_var = find_variable(context, name); - } + existing_var = find_variable(context, name, context->fn_entry != nullptr); if (existing_var) { add_node_error(g, source_node, buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); @@ -3036,7 +3003,7 @@ static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, Block if (primitive_table_entry) { type = primitive_table_entry->value; } else { - type = find_container(context, name); + type = find_container(import, name); } if (type) { add_node_error(g, source_node, buf_sprintf("variable shadows type '%s'", buf_ptr(&type->name))); @@ -3045,10 +3012,11 @@ static VariableTableEntry *add_local_var(CodeGen *g, AstNode *source_node, Block } 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); + } + if (context->fn_entry) { + context->fn_entry->variable_list.append(variable_entry); } variable_entry->is_const = is_const; @@ -3075,7 +3043,7 @@ static TypeTableEntry *analyze_unwrap_error_expr(CodeGen *g, ImportTableEntry *i child_context = new_block_context(node, parent_context); var_node->block_context = child_context; Buf *var_name = &var_node->data.symbol_expr.symbol; - node->data.unwrap_err_expr.var = add_local_var(g, var_node, child_context, var_name, + node->data.unwrap_err_expr.var = add_local_var(g, var_node, import, child_context, var_name, g->builtin_types.entry_pure_error, true); } else { child_context = parent_context; @@ -3153,7 +3121,7 @@ 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 *var = add_local_var(g, source_node, context, + VariableTableEntry *var = add_local_var(g, source_node, import, context, &variable_declaration->symbol, type, is_const); variable_declaration->variable = var; @@ -3199,7 +3167,7 @@ static TypeTableEntry *analyze_null_literal_expr(CodeGen *g, ImportTableEntry *i node->data.null_literal.resolved_struct_val_expr.type_entry = expected_type; node->data.null_literal.resolved_struct_val_expr.source_node = node; - block_context->struct_val_expr_alloca_list.append(&node->data.null_literal.resolved_struct_val_expr); + block_context->fn_entry->struct_val_expr_alloca_list.append(&node->data.null_literal.resolved_struct_val_expr); return resolve_expr_const_val_as_null(g, node, expected_type); } @@ -3303,7 +3271,6 @@ static TypeTableEntry *analyze_while_expr(CodeGen *g, ImportTableEntry *import, 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); @@ -3359,16 +3326,16 @@ static TypeTableEntry *analyze_for_expr(CodeGen *g, ImportTableEntry *import, Bl AstNode *elem_var_node = node->data.for_expr.elem_node; elem_var_node->block_context = child_context; 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); + node->data.for_expr.elem_var = add_local_var(g, elem_var_node, import, 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; index_var_node->block_context = child_context; - node->data.for_expr.index_var = add_local_var(g, index_var_node, child_context, index_var_name, + node->data.for_expr.index_var = add_local_var(g, index_var_node, import, child_context, index_var_name, g->builtin_types.entry_isize, true); } else { - node->data.for_expr.index_var = add_local_var(g, node, child_context, nullptr, + node->data.for_expr.index_var = add_local_var(g, node, import, child_context, nullptr, g->builtin_types.entry_isize, true); } @@ -3617,6 +3584,21 @@ static void eval_const_expr_implicit_cast(CodeGen *g, AstNode *node, AstNode *ex } } +static TypeTableEntry *resolve_cast(CodeGen *g, BlockContext *context, AstNode *node, + AstNode *expr_node, TypeTableEntry *wanted_type, CastOp op, bool need_alloca) +{ + node->data.fn_call_expr.cast_op = op; + eval_const_expr_implicit_cast(g, node, expr_node); + if (need_alloca) { + if (context->fn_entry) { + context->fn_entry->cast_alloca_list.append(node); + } else { + assert(get_resolved_expr(node)->const_val.ok); + } + } + return wanted_type; +} + static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node) { @@ -3642,27 +3624,21 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B // explicit match or non-const to const if (types_match_const_cast_only(wanted_type, actual_type)) { - node->data.fn_call_expr.cast_op = CastOpNoop; - eval_const_expr_implicit_cast(g, node, expr_node); - return wanted_type; + return resolve_cast(g, context, node, expr_node, wanted_type, CastOpNoop, false); } // explicit cast from bool to int if (wanted_type->id == TypeTableEntryIdInt && actual_type->id == TypeTableEntryIdBool) { - node->data.fn_call_expr.cast_op = CastOpBoolToInt; - eval_const_expr_implicit_cast(g, node, expr_node); - return wanted_type; + return resolve_cast(g, context, node, expr_node, wanted_type, CastOpBoolToInt, false); } // explicit cast from pointer to isize or usize if ((wanted_type == g->builtin_types.entry_isize || wanted_type == g->builtin_types.entry_usize) && actual_type->id == TypeTableEntryIdPointer) { - node->data.fn_call_expr.cast_op = CastOpPtrToInt; - eval_const_expr_implicit_cast(g, node, expr_node); - return wanted_type; + return resolve_cast(g, context, node, expr_node, wanted_type, CastOpPtrToInt, false); } @@ -3670,9 +3646,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B if (wanted_type->id == TypeTableEntryIdPointer && (actual_type == g->builtin_types.entry_isize || actual_type == g->builtin_types.entry_usize)) { - node->data.fn_call_expr.cast_op = CastOpIntToPtr; - eval_const_expr_implicit_cast(g, node, expr_node); - return wanted_type; + return resolve_cast(g, context, node, expr_node, wanted_type, CastOpIntToPtr, false); } // explicit widening or shortening cast @@ -3681,27 +3655,21 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B (wanted_type->id == TypeTableEntryIdFloat && actual_type->id == TypeTableEntryIdFloat)) { - node->data.fn_call_expr.cast_op = CastOpWidenOrShorten; - eval_const_expr_implicit_cast(g, node, expr_node); - return wanted_type; + return resolve_cast(g, context, node, expr_node, wanted_type, CastOpWidenOrShorten, false); } // explicit cast from int to float if (wanted_type->id == TypeTableEntryIdFloat && actual_type->id == TypeTableEntryIdInt) { - node->data.fn_call_expr.cast_op = CastOpIntToFloat; - eval_const_expr_implicit_cast(g, node, expr_node); - return wanted_type; + return resolve_cast(g, context, node, expr_node, wanted_type, CastOpIntToFloat, false); } // explicit cast from float to int if (wanted_type->id == TypeTableEntryIdInt && actual_type->id == TypeTableEntryIdFloat) { - node->data.fn_call_expr.cast_op = CastOpFloatToInt; - eval_const_expr_implicit_cast(g, node, expr_node); - return wanted_type; + return resolve_cast(g, context, node, expr_node, wanted_type, CastOpFloatToInt, false); } // explicit cast from fixed size array to unknown size array @@ -3712,36 +3680,25 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B wanted_type->data.structure.fields[0].type_entry->data.pointer.child_type, actual_type->data.array.child_type)) { - node->data.fn_call_expr.cast_op = CastOpToUnknownSizeArray; - context->cast_alloca_list.append(node); - eval_const_expr_implicit_cast(g, node, expr_node); - return wanted_type; + return resolve_cast(g, context, node, expr_node, wanted_type, CastOpToUnknownSizeArray, true); } // explicit cast from pointer to another pointer if (actual_type->id == TypeTableEntryIdPointer && wanted_type->id == TypeTableEntryIdPointer) { - node->data.fn_call_expr.cast_op = CastOpPointerReinterpret; - eval_const_expr_implicit_cast(g, node, expr_node); - return wanted_type; + return resolve_cast(g, context, node, expr_node, wanted_type, CastOpPointerReinterpret, false); } // explicit cast from child type of maybe type to maybe type if (wanted_type->id == TypeTableEntryIdMaybe) { if (types_match_const_cast_only(wanted_type->data.maybe.child_type, actual_type)) { - node->data.fn_call_expr.cast_op = CastOpMaybeWrap; - context->cast_alloca_list.append(node); - eval_const_expr_implicit_cast(g, node, expr_node); - return wanted_type; + return resolve_cast(g, context, node, expr_node, wanted_type, CastOpMaybeWrap, true); } else if (actual_type->id == TypeTableEntryIdNumLitInt || actual_type->id == TypeTableEntryIdNumLitFloat) { if (num_lit_fits_in_other_type(g, expr_node, wanted_type->data.maybe.child_type)) { - node->data.fn_call_expr.cast_op = CastOpMaybeWrap; - context->cast_alloca_list.append(node); - eval_const_expr_implicit_cast(g, node, expr_node); - return wanted_type; + return resolve_cast(g, context, node, expr_node, wanted_type, CastOpMaybeWrap, true); } else { return g->builtin_types.entry_invalid; } @@ -3751,18 +3708,12 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B // explicit cast from child type of error type to error type if (wanted_type->id == TypeTableEntryIdErrorUnion) { if (types_match_const_cast_only(wanted_type->data.error.child_type, actual_type)) { - node->data.fn_call_expr.cast_op = CastOpErrorWrap; - context->cast_alloca_list.append(node); - eval_const_expr_implicit_cast(g, node, expr_node); - return wanted_type; + return resolve_cast(g, context, node, expr_node, wanted_type, CastOpErrorWrap, true); } else if (actual_type->id == TypeTableEntryIdNumLitInt || actual_type->id == TypeTableEntryIdNumLitFloat) { if (num_lit_fits_in_other_type(g, expr_node, wanted_type->data.error.child_type)) { - node->data.fn_call_expr.cast_op = CastOpErrorWrap; - context->cast_alloca_list.append(node); - eval_const_expr_implicit_cast(g, node, expr_node); - return wanted_type; + return resolve_cast(g, context, node, expr_node, wanted_type, CastOpErrorWrap, true); } else { return g->builtin_types.entry_invalid; } @@ -3773,9 +3724,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B if (wanted_type->id == TypeTableEntryIdErrorUnion && actual_type->id == TypeTableEntryIdPureError) { - node->data.fn_call_expr.cast_op = CastOpPureErrorWrap; - eval_const_expr_implicit_cast(g, node, expr_node); - return wanted_type; + return resolve_cast(g, context, node, expr_node, wanted_type, CastOpPureErrorWrap, false); } // explicit cast from number literal to another type @@ -3783,21 +3732,21 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B actual_type->id == TypeTableEntryIdNumLitInt) { if (num_lit_fits_in_other_type(g, expr_node, wanted_type)) { + CastOp op; if ((actual_type->id == TypeTableEntryIdNumLitFloat && wanted_type->id == TypeTableEntryIdFloat) || (actual_type->id == TypeTableEntryIdNumLitInt && wanted_type->id == TypeTableEntryIdInt)) { - node->data.fn_call_expr.cast_op = CastOpNoop; + op = CastOpNoop; } else if (wanted_type->id == TypeTableEntryIdInt) { - node->data.fn_call_expr.cast_op = CastOpFloatToInt; + op = CastOpFloatToInt; } else if (wanted_type->id == TypeTableEntryIdFloat) { - node->data.fn_call_expr.cast_op = CastOpIntToFloat; + op = CastOpIntToFloat; } else { zig_unreachable(); } - eval_const_expr_implicit_cast(g, node, expr_node); - return wanted_type; + return resolve_cast(g, context, node, expr_node, wanted_type, op, false); } else { return g->builtin_types.entry_invalid; } @@ -3815,9 +3764,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B if (bignum_fits_in_bits(&bn, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) { - node->data.fn_call_expr.cast_op = CastOpErrToInt; - eval_const_expr_implicit_cast(g, node, expr_node); - return wanted_type; + return resolve_cast(g, context, node, expr_node, wanted_type, CastOpErrToInt, false); } else { add_node_error(g, node, buf_sprintf("too many error values to fit in '%s'", buf_ptr(&wanted_type->name))); @@ -4176,7 +4123,7 @@ static TypeTableEntry *analyze_fn_call_ptr(CodeGen *g, ImportTableEntry *import, } if (handle_is_ptr(return_type)) { - context->cast_alloca_list.append(node); + context->fn_entry->cast_alloca_list.append(node); } return return_type; @@ -4572,8 +4519,8 @@ static TypeTableEntry *analyze_switch_expr(CodeGen *g, ImportTableEntry *import, assert(var_node->type == NodeTypeSymbol); Buf *var_name = &var_node->data.symbol_expr.symbol; var_node->block_context = child_context; - prong_node->data.switch_prong.var = add_local_var(g, var_node, child_context, var_name, - var_type, true); + prong_node->data.switch_prong.var = add_local_var(g, var_node, import, + child_context, var_name, var_type, true); prong_node->data.switch_prong.var_is_target_expr = var_is_target_expr; } @@ -4598,6 +4545,9 @@ static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, normalize_parent_ptrs(node); } + // TODO follow the blocks to their parents, loop over all of them, set them all to true + context->block_exit_paths[BlockExitPathReturn] = true; + TypeTableEntry *expected_return_type = get_return_type(context); switch (node->data.return_expr.kind) { @@ -4632,6 +4582,71 @@ static TypeTableEntry *analyze_return_expr(CodeGen *g, ImportTableEntry *import, } } +static void validate_voided_expr(CodeGen *g, AstNode *source_node, TypeTableEntry *type_entry) { + if (type_entry->id == TypeTableEntryIdMetaType) { + add_node_error(g, first_executing_node(source_node), buf_sprintf("expected expression, found type")); + } else if (type_entry->id == TypeTableEntryIdErrorUnion) { + add_node_error(g, first_executing_node(source_node), buf_sprintf("statement ignores error value")); + } +} + +static TypeTableEntry *analyze_defer_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, + TypeTableEntry *expected_type, AstNode *node) +{ + if (!context->fn_entry) { + add_node_error(g, node, buf_sprintf("defer expression outside function definition")); + return g->builtin_types.entry_invalid; + } + + if (!node->data.defer_expr.expr) { + add_node_error(g, node, buf_sprintf("defer expects an expression")); + return g->builtin_types.entry_void; + } + + + switch (node->data.defer_expr.kind) { + case ReturnKindUnconditional: + { + TypeTableEntry *resolved_type = analyze_expression(g, import, context, nullptr, + node->data.defer_expr.expr); + validate_voided_expr(g, node->data.defer_expr.expr, resolved_type); + zig_panic("TODO"); + + //node->data.defer_expr.index_in_block = context->defer_list.length; + //context->defer_list.append(node); + return g->builtin_types.entry_void; + } + case ReturnKindError: + { + TypeTableEntry *resolved_type = analyze_expression(g, import, context, nullptr, + node->data.defer_expr.expr); + if (resolved_type->id == TypeTableEntryIdInvalid) { + // OK + } else if (resolved_type->id == TypeTableEntryIdErrorUnion) { + // OK + } else { + add_node_error(g, node->data.defer_expr.expr, + buf_sprintf("expected error type, got '%s'", buf_ptr(&resolved_type->name))); + } + return g->builtin_types.entry_void; + } + case ReturnKindMaybe: + { + TypeTableEntry *resolved_type = analyze_expression(g, import, context, nullptr, + node->data.defer_expr.expr); + if (resolved_type->id == TypeTableEntryIdInvalid) { + // OK + } else if (resolved_type->id == TypeTableEntryIdMaybe) { + // OK + } else { + add_node_error(g, node->data.defer_expr.expr, + buf_sprintf("expected maybe type, got '%s'", buf_ptr(&resolved_type->name))); + } + return g->builtin_types.entry_void; + } + } +} + static TypeTableEntry *analyze_string_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { @@ -4652,10 +4667,8 @@ static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import, for (int i = 0; i < node->data.block.statements.length; i += 1) { AstNode *child = node->data.block.statements.at(i); if (child->type == NodeTypeLabel) { - child->block_context = child_context; - LabelTableEntry *label_entry = child->data.label.label_entry; - assert(label_entry); - label_entry->entered_from_fallthrough = (return_type->id != TypeTableEntryIdUnreachable); + add_node_error(g, child, + buf_sprintf("label and goto not supported yet, see https://github.com/andrewrk/zig/issues/44")); return_type = g->builtin_types.entry_void; continue; } @@ -4673,11 +4686,7 @@ static TypeTableEntry *analyze_block_expr(CodeGen *g, ImportTableEntry *import, TypeTableEntry *passed_expected_type = is_last ? expected_type : nullptr; return_type = analyze_expression(g, import, child_context, passed_expected_type, child); if (!is_last) { - if (return_type->id == TypeTableEntryIdMetaType) { - add_node_error(g, child, buf_sprintf("expected expression, found type")); - } else if (return_type->id == TypeTableEntryIdErrorUnion) { - add_node_error(g, child, buf_sprintf("statement ignores error value")); - } + validate_voided_expr(g, child, return_type); } } return return_type; @@ -4700,7 +4709,7 @@ static TypeTableEntry *analyze_asm_expr(CodeGen *g, ImportTableEntry *import, Bl } } else { Buf *variable_name = &asm_output->variable_name; - VariableTableEntry *var = find_variable(context, variable_name); + VariableTableEntry *var = find_variable(context, variable_name, false); if (var) { asm_output->variable = var; return var->type; @@ -4719,6 +4728,13 @@ static TypeTableEntry *analyze_asm_expr(CodeGen *g, ImportTableEntry *import, Bl return return_type; } +static TypeTableEntry *analyze_goto(CodeGen *g, ImportTableEntry *import, BlockContext *context, + TypeTableEntry *expected_type, AstNode *node) +{ + add_node_error(g, node, buf_sprintf("goto is broken, see https://github.com/andrewrk/zig/issues/44")); + return g->builtin_types.entry_unreachable; +} + // When you call analyze_expression, the node you pass might no longer be the child node // you thought it was due to implicit casting rewriting the AST. static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context, @@ -4734,24 +4750,16 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, case NodeTypeReturnExpr: return_type = analyze_return_expr(g, import, context, expected_type, node); break; + case NodeTypeDeferExpr: + return_type = analyze_defer_expr(g, import, context, expected_type, node); + break; case NodeTypeVariableDeclaration: analyze_variable_declaration(g, import, context, expected_type, node); return_type = g->builtin_types.entry_void; break; case NodeTypeGoto: - { - FnTableEntry *fn_table_entry = get_context_fn_entry(context); - auto table_entry = fn_table_entry->label_table.maybe_get(&node->data.goto_expr.name); - if (table_entry) { - node->data.goto_expr.label_entry = table_entry->value; - table_entry->value->used = true; - } else { - add_node_error(g, node, - buf_sprintf("use of undeclared label '%s'", buf_ptr(&node->data.goto_expr.name))); - } - return_type = g->builtin_types.entry_unreachable; - break; - } + analyze_goto(g, import, context, expected_type, node); + break; case NodeTypeBreak: return_type = analyze_break_expr(g, import, context, expected_type, node); break; @@ -4909,7 +4917,7 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo add_node_error(g, param_decl_node, buf_sprintf("missing parameter name")); } - VariableTableEntry *var = add_local_var(g, param_decl_node, context, ¶m_decl->name, type, true); + VariableTableEntry *var = add_local_var(g, param_decl_node, import, context, ¶m_decl->name, type, true); var->src_arg_index = i; param_decl_node->data.param_decl.variable = var; @@ -4920,22 +4928,6 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo TypeTableEntry *block_return_type = analyze_expression(g, import, context, expected_type, node->data.fn_def.body); node->data.fn_def.implicit_return_type = block_return_type; - - { - auto it = fn_table_entry->label_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; - - LabelTableEntry *label_entry = entry->value; - if (!label_entry->used) { - add_node_error(g, label_entry->label_node, - buf_sprintf("label '%s' defined but not used", - buf_ptr(&label_entry->label_node->data.label.name))); - } - } - } } static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode *node) { @@ -4964,6 +4956,7 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeParamDecl: case NodeTypeFnDecl: case NodeTypeReturnExpr: + case NodeTypeDeferExpr: case NodeTypeRoot: case NodeTypeBlock: case NodeTypeBinOpExpr: @@ -5028,7 +5021,7 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode Buf *name = &node->data.symbol_expr.symbol; auto table_entry = g->primitive_type_table.maybe_get(name); if (!table_entry) { - table_entry = import->block_context->type_table.maybe_get(name); + table_entry = import->type_table.maybe_get(name); } if (!table_entry || !type_is_complete(table_entry->value)) { decl_node->deps.put(name, node); @@ -5046,6 +5039,9 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeReturnExpr: collect_expr_decl_deps(g, import, node->data.return_expr.expr, decl_node); break; + case NodeTypeDeferExpr: + collect_expr_decl_deps(g, import, node->data.defer_expr.expr, decl_node); + break; case NodeTypePrefixOpExpr: collect_expr_decl_deps(g, import, node->data.prefix_op_expr.primary_expr, decl_node); break; @@ -5196,7 +5192,7 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast Buf *name = &node->data.struct_decl.name; auto table_entry = g->primitive_type_table.maybe_get(name); if (!table_entry) { - table_entry = import->block_context->type_table.maybe_get(name); + table_entry = import->type_table.maybe_get(name); } if (table_entry) { node->data.struct_decl.type_entry = table_entry->value; @@ -5210,20 +5206,20 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast node->data.struct_decl.kind, node, buf_ptr(name)); } - import->block_context->type_table.put(&entry->name, entry); + import->type_table.put(&entry->name, entry); node->data.struct_decl.type_entry = entry; bool is_pub = (node->data.struct_decl.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->type_table.maybe_get(&entry->name); + auto table_entry = importer.import->type_table.maybe_get(&entry->name); if (table_entry) { add_node_error(g, importer.source_node, buf_sprintf("import of type '%s' overrides existing definition", buf_ptr(&entry->name))); } else { - importer.import->block_context->type_table.put(&entry->name, entry); + importer.import->type_table.put(&entry->name, entry); } } } @@ -5365,6 +5361,7 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast case NodeTypeParamDecl: case NodeTypeFnDecl: case NodeTypeReturnExpr: + case NodeTypeDeferExpr: case NodeTypeBlock: case NodeTypeBinOpExpr: case NodeTypeUnwrapErrorExpr: @@ -5555,6 +5552,8 @@ Expr *get_resolved_expr(AstNode *node) { switch (node->type) { case NodeTypeReturnExpr: return &node->data.return_expr.resolved_expr; + case NodeTypeDeferExpr: + return &node->data.defer_expr.resolved_expr; case NodeTypeBinOpExpr: return &node->data.bin_op_expr.resolved_expr; case NodeTypeUnwrapErrorExpr: @@ -5653,6 +5652,7 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) { return &node->data.type_decl.top_level_decl; case NodeTypeNumberLiteral: case NodeTypeReturnExpr: + case NodeTypeDeferExpr: case NodeTypeBinOpExpr: case NodeTypeUnwrapErrorExpr: case NodeTypePrefixOpExpr: diff --git a/src/analyze.hpp b/src/analyze.hpp index 9aae7eeba3..01f11c6d86 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -14,8 +14,6 @@ void semantic_analyze(CodeGen *g); ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg); TypeTableEntry *new_type_table_entry(TypeTableEntryId id); TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool is_const); -VariableTableEntry *find_variable(BlockContext *context, Buf *name); -TypeTableEntry *find_container(BlockContext *context, Buf *name); BlockContext *new_block_context(AstNode *node, BlockContext *parent); Expr *get_resolved_expr(AstNode *node); TopLevelDecl *get_resolved_top_level_decl(AstNode *node); diff --git a/src/ast_render.cpp b/src/ast_render.cpp index c4268d01c5..917c61c51c 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -122,6 +122,8 @@ static const char *node_type_str(NodeType node_type) { return "Directive"; case NodeTypeReturnExpr: return "ReturnExpr"; + case NodeTypeDeferExpr: + return "DeferExpr"; case NodeTypeVariableDeclaration: return "VariableDeclaration"; case NodeTypeTypeDecl: @@ -259,6 +261,14 @@ void ast_print(FILE *f, AstNode *node, int indent) { ast_print(f, node->data.return_expr.expr, indent + 2); break; } + case NodeTypeDeferExpr: + { + const char *prefix_str = return_prefix_str(node->data.defer_expr.kind); + fprintf(f, "%s%s\n", prefix_str, node_type_str(node->type)); + if (node->data.defer_expr.expr) + ast_print(f, node->data.defer_expr.expr, indent + 2); + break; + } case NodeTypeVariableDeclaration: { Buf *name_buf = &node->data.variable_declaration.symbol; @@ -620,6 +630,8 @@ static void render_node(AstRender *ar, AstNode *node) { break; case NodeTypeReturnExpr: zig_panic("TODO"); + case NodeTypeDeferExpr: + zig_panic("TODO"); case NodeTypeVariableDeclaration: { const char *pub_str = visib_mod_string(node->data.variable_declaration.visib_mod); diff --git a/src/codegen.cpp b/src/codegen.cpp index b607aa41fe..089fee96e6 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1669,6 +1669,15 @@ static LLVMValueRef gen_return_expr(CodeGen *g, AstNode *node) { } } +static LLVMValueRef gen_defer_expr(CodeGen *g, AstNode *node) { + assert(node->type == NodeTypeDeferExpr); + + zig_panic("TODO"); + //node->block_context->cur_defer_index = node->data.defer_expr.index_in_block; + + return nullptr; +} + static LLVMValueRef gen_if_bool_expr_raw(CodeGen *g, AstNode *source_node, LLVMValueRef cond_value, AstNode *then_node, AstNode *else_node) { @@ -1794,6 +1803,21 @@ static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) { static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *implicit_return_type) { assert(block_node->type == NodeTypeBlock); + /* TODO + BlockContext *block_context = block_node->data.block.block_context; + if (block_context->defer_list.length > 0) { + LLVMBasicBlockRef exit_scope_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DeferExitScope"); + + for (int i = 0; i < block_context->defer_list.length; i += 1) { + AstNode *defer_node = block_context->defer_list.at(i); + defer_node->data.defer_expr.basic_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DeferExpr"); + LLVMPositionBuilderAtEnd(g->builder, body_block); + } + + LLVMPositionBuilderAtEnd(g->builder, ?); + } + */ + LLVMValueRef return_value; for (int i = 0; i < block_node->data.block.statements.length; i += 1) { AstNode *statement_node = block_node->data.block.statements.at(i); @@ -2451,6 +2475,8 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { return gen_unwrap_err_expr(g, node); case NodeTypeReturnExpr: return gen_return_expr(g, node); + case NodeTypeDeferExpr: + return gen_defer_expr(g, node); case NodeTypeVariableDeclaration: return gen_var_decl_expr(g, node); case NodeTypePrefixOpExpr: @@ -2478,24 +2504,13 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { case NodeTypeBlock: return gen_block(g, node, nullptr); case NodeTypeGoto: - add_debug_source_node(g, node); - return LLVMBuildBr(g->builder, node->data.goto_expr.label_entry->basic_block); + zig_unreachable(); case NodeTypeBreak: return gen_break(g, node); case NodeTypeContinue: return gen_continue(g, node); case NodeTypeLabel: - { - LabelTableEntry *label_entry = node->data.label.label_entry; - assert(label_entry); - LLVMBasicBlockRef basic_block = label_entry->basic_block; - if (label_entry->entered_from_fallthrough) { - add_debug_source_node(g, node); - LLVMBuildBr(g->builder, basic_block); - } - LLVMPositionBuilderAtEnd(g->builder, basic_block); - return nullptr; - } + zig_unreachable(); case NodeTypeContainerInitExpr: return gen_container_init_expr(g, node); case NodeTypeSwitchExpr: @@ -2532,19 +2547,6 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { zig_unreachable(); } -static void build_label_blocks(CodeGen *g, AstNode *block_node) { - assert(block_node->type == NodeTypeBlock); - for (int i = 0; i < block_node->data.block.statements.length; i += 1) { - AstNode *label_node = block_node->data.block.statements.at(i); - if (label_node->type != NodeTypeLabel) - continue; - - Buf *name = &label_node->data.label.name; - label_node->data.label.label_entry->basic_block = LLVMAppendBasicBlock( - g->cur_fn->fn_value, buf_ptr(name)); - } -} - static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *const_val) { assert(const_val->ok); @@ -2977,11 +2979,7 @@ static void do_code_gen(CodeGen *g) { LLVMPositionBuilderAtEnd(g->builder, entry_block); - AstNode *body_node = fn_def_node->data.fn_def.body; - build_label_blocks(g, body_node); - - // Set up debug info for blocks and variables and - // allocate all local variables + // Set up debug info for blocks for (int bc_i = 0; bc_i < fn_table_entry->all_block_contexts.length; bc_i += 1) { BlockContext *block_context = fn_table_entry->all_block_contexts.at(bc_i); @@ -2994,59 +2992,45 @@ static void do_code_gen(CodeGen *g) { block_context->di_scope = LLVMZigLexicalBlockToScope(di_block); } - for (int var_i = 0; var_i < block_context->variable_list.length; var_i += 1) { - VariableTableEntry *var = block_context->variable_list.at(var_i); - if (!type_has_bits(var->type)) { - continue; - } + } - unsigned tag; - unsigned arg_no; - TypeTableEntry *gen_type; - if (block_context->node->type == NodeTypeFnDef) { - tag = LLVMZigTag_DW_arg_variable(); - arg_no = var->gen_arg_index + 1; + // create debug variable declarations for variables and allocate all local variables + for (int var_i = 0; var_i < fn_table_entry->variable_list.length; var_i += 1) { + VariableTableEntry *var = fn_table_entry->variable_list.at(var_i); - var->is_ptr = false; - assert(var->gen_arg_index >= 0); - var->value_ref = LLVMGetParam(fn, var->gen_arg_index); - - gen_type = fn_table_entry->type_entry->data.fn.gen_param_info[var->src_arg_index].type; - } else { - tag = LLVMZigTag_DW_auto_variable(); - arg_no = 0; - - add_debug_source_node(g, var->decl_node); - var->value_ref = LLVMBuildAlloca(g->builder, var->type->type_ref, buf_ptr(&var->name)); - uint64_t align_bytes = LLVMABISizeOfType(g->target_data_ref, var->type->type_ref); - LLVMSetAlignment(var->value_ref, align_bytes); - - gen_type = var->type; - } - - var->di_loc_var = LLVMZigCreateLocalVariable(g->dbuilder, tag, - block_context->di_scope, buf_ptr(&var->name), - import->di_file, var->decl_node->line + 1, - gen_type->di_type, !g->strip_debug_symbols, 0, arg_no); + if (!type_has_bits(var->type)) { + continue; } - // allocate structs which are the result of casts - for (int cea_i = 0; cea_i < block_context->cast_alloca_list.length; cea_i += 1) { - AstNode *fn_call_node = block_context->cast_alloca_list.at(cea_i); - add_debug_source_node(g, fn_call_node); - Expr *expr = &fn_call_node->data.fn_call_expr.resolved_expr; - fn_call_node->data.fn_call_expr.tmp_ptr = LLVMBuildAlloca(g->builder, - expr->type_entry->type_ref, ""); + unsigned tag; + unsigned arg_no; + TypeTableEntry *gen_type; + if (var->block_context->node->type == NodeTypeFnDef) { + tag = LLVMZigTag_DW_arg_variable(); + arg_no = var->gen_arg_index + 1; + + var->is_ptr = false; + assert(var->gen_arg_index >= 0); + var->value_ref = LLVMGetParam(fn, var->gen_arg_index); + + gen_type = fn_table_entry->type_entry->data.fn.gen_param_info[var->src_arg_index].type; + } else { + tag = LLVMZigTag_DW_auto_variable(); + arg_no = 0; + + add_debug_source_node(g, var->decl_node); + var->value_ref = LLVMBuildAlloca(g->builder, var->type->type_ref, buf_ptr(&var->name)); + uint64_t align_bytes = LLVMABISizeOfType(g->target_data_ref, var->type->type_ref); + LLVMSetAlignment(var->value_ref, align_bytes); + + gen_type = var->type; } - // allocate structs which are struct value expressions - for (int alloca_i = 0; alloca_i < block_context->struct_val_expr_alloca_list.length; alloca_i += 1) { - StructValExprCodeGen *struct_val_expr_node = block_context->struct_val_expr_alloca_list.at(alloca_i); - add_debug_source_node(g, struct_val_expr_node->source_node); - struct_val_expr_node->ptr = LLVMBuildAlloca(g->builder, - struct_val_expr_node->type_entry->type_ref, ""); - } + var->di_loc_var = LLVMZigCreateLocalVariable(g->dbuilder, tag, + var->block_context->di_scope, buf_ptr(&var->name), + import->di_file, var->decl_node->line + 1, + gen_type->di_type, !g->strip_debug_symbols, 0, arg_no); } // create debug variable declarations for parameters @@ -3069,6 +3053,23 @@ static void do_code_gen(CodeGen *g) { entry_block); } + // allocate structs which are the result of casts + for (int cea_i = 0; cea_i < fn_table_entry->cast_alloca_list.length; cea_i += 1) { + AstNode *fn_call_node = fn_table_entry->cast_alloca_list.at(cea_i); + add_debug_source_node(g, fn_call_node); + Expr *expr = &fn_call_node->data.fn_call_expr.resolved_expr; + fn_call_node->data.fn_call_expr.tmp_ptr = LLVMBuildAlloca(g->builder, + expr->type_entry->type_ref, ""); + } + + // allocate structs which are struct value expressions + for (int alloca_i = 0; alloca_i < fn_table_entry->struct_val_expr_alloca_list.length; alloca_i += 1) { + StructValExprCodeGen *struct_val_expr_node = fn_table_entry->struct_val_expr_alloca_list.at(alloca_i); + add_debug_source_node(g, struct_val_expr_node->source_node); + struct_val_expr_node->ptr = LLVMBuildAlloca(g->builder, + struct_val_expr_node->type_entry->type_ref, ""); + } + TypeTableEntry *implicit_return_type = fn_def_node->data.fn_def.implicit_return_type; gen_block(g, fn_def_node->data.fn_def.body, implicit_return_type); @@ -3524,6 +3525,8 @@ void codegen_parseh(CodeGen *g, Buf *src_dirname, Buf *src_basename, Buf *source import->source_code = source_code; import->path = full_path; import->fn_table.init(32); + import->type_table.init(8); + import->error_table.init(8); g->root_import = import; init(g, full_path); @@ -3614,6 +3617,8 @@ static ImportTableEntry *codegen_add_code(CodeGen *g, Buf *abs_full_path, import_entry->line_offsets = tokenization.line_offsets; import_entry->path = full_path; import_entry->fn_table.init(32); + import_entry->type_table.init(8); + import_entry->error_table.init(8); import_entry->root = ast_parse(source_code, tokenization.tokens, import_entry, g->err_color, &g->next_node_index); diff --git a/src/parser.cpp b/src/parser.cpp index 78481f61f0..8be98e6e29 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1635,20 +1635,24 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool manda /* ReturnExpression : option("%" | "?") "return" option(Expression) +DeferExpression = option("%" | "?") "defer" option(Expression) */ -static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool mandatory) { +static AstNode *ast_parse_return_or_defer_expr(ParseContext *pc, int *token_index) { Token *token = &pc->tokens->at(*token_index); + NodeType node_type; ReturnKind kind; if (token->id == TokenIdPercent) { Token *next_token = &pc->tokens->at(*token_index + 1); if (next_token->id == TokenIdKeywordReturn) { kind = ReturnKindError; + node_type = NodeTypeReturnExpr; + *token_index += 2; + } else if (next_token->id == TokenIdKeywordDefer) { + kind = ReturnKindError; + node_type = NodeTypeDeferExpr; *token_index += 2; - } else if (mandatory) { - ast_expect_token(pc, next_token, TokenIdKeywordReturn); - zig_unreachable(); } else { return nullptr; } @@ -1656,24 +1660,28 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, int *token_index, bool m Token *next_token = &pc->tokens->at(*token_index + 1); if (next_token->id == TokenIdKeywordReturn) { kind = ReturnKindMaybe; + node_type = NodeTypeReturnExpr; + *token_index += 2; + } else if (next_token->id == TokenIdKeywordDefer) { + kind = ReturnKindMaybe; + node_type = NodeTypeDeferExpr; *token_index += 2; - } else if (mandatory) { - ast_expect_token(pc, next_token, TokenIdKeywordReturn); - zig_unreachable(); } else { return nullptr; } } else if (token->id == TokenIdKeywordReturn) { kind = ReturnKindUnconditional; + node_type = NodeTypeReturnExpr; + *token_index += 1; + } else if (token->id == TokenIdKeywordDefer) { + kind = ReturnKindUnconditional; + node_type = NodeTypeDeferExpr; *token_index += 1; - } else if (mandatory) { - ast_expect_token(pc, token, TokenIdKeywordReturn); - zig_unreachable(); } else { return nullptr; } - AstNode *node = ast_create_node(pc, NodeTypeReturnExpr, token); + AstNode *node = ast_create_node(pc, node_type, token); node->data.return_expr.kind = kind; node->data.return_expr.expr = ast_parse_expression(pc, token_index, false); @@ -2060,7 +2068,7 @@ NonBlockExpression : ReturnExpression | AssignmentExpression static AstNode *ast_parse_non_block_expr(ParseContext *pc, int *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); - AstNode *return_expr = ast_parse_return_expr(pc, token_index, false); + AstNode *return_expr = ast_parse_return_or_defer_expr(pc, token_index); if (return_expr) return return_expr; @@ -2695,6 +2703,9 @@ void normalize_parent_ptrs(AstNode *node) { case NodeTypeReturnExpr: set_field(&node->data.return_expr.expr); break; + case NodeTypeDeferExpr: + set_field(&node->data.defer_expr.expr); + break; case NodeTypeVariableDeclaration: set_list_fields(node->data.variable_declaration.directives); set_field(&node->data.variable_declaration.type); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index aacb5cbee8..f6d7ec6f22 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -102,6 +102,7 @@ const char * zig_keywords[] = { "pub", "export", "import", "c_import", "if", "else", "goto", "asm", "volatile", "struct", "enum", "while", "for", "continue", "break", "null", "noalias", "switch", "undefined", "error", "type", "inline", + "defer", }; bool is_zig_keyword(Buf *buf) { @@ -275,6 +276,8 @@ static void end_token(Tokenize *t) { t->cur_tok->id = TokenIdKeywordType; } else if (mem_eql_str(token_mem, token_len, "inline")) { t->cur_tok->id = TokenIdKeywordInline; + } else if (mem_eql_str(token_mem, token_len, "defer")) { + t->cur_tok->id = TokenIdKeywordDefer; } t->cur_tok = nullptr; @@ -1090,6 +1093,7 @@ const char * token_name(TokenId id) { case TokenIdKeywordError: return "error"; case TokenIdKeywordType: return "type"; case TokenIdKeywordInline: return "inline"; + case TokenIdKeywordDefer: return "defer"; case TokenIdLParen: return "("; case TokenIdRParen: return ")"; case TokenIdComma: return ","; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 5fd59a572c..de0010d0b9 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -42,6 +42,7 @@ enum TokenId { TokenIdKeywordError, TokenIdKeywordType, TokenIdKeywordInline, + TokenIdKeywordDefer, TokenIdLParen, TokenIdRParen, TokenIdComma, diff --git a/std/std.zig b/std/std.zig index 3fad1bb0ce..32b1647bb7 100644 --- a/std/std.zig +++ b/std/std.zig @@ -50,6 +50,8 @@ pub struct OutStream { fd: isize, buffer: [buffer_size]u8, index: isize, + // TODO remove this. let the user flush at will. + // for stderr the user can use printf buffered: bool, pub fn print_str(os: &OutStream, str: []const u8) -> %isize { diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 7c70528d3b..27cb25e998 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -259,25 +259,6 @@ pub fn main(args: [][]u8) -> %void { } )SOURCE", "pass\n"); - add_simple_case("goto", R"SOURCE( -import "std.zig"; - -fn loop(a : i32) { - if (a == 0) { - goto done; - } - %%stdout.printf("loop\n"); - loop(a - 1); - -done: - return; -} - -pub fn main(args: [][]u8) -> %void { - loop(3); -} - )SOURCE", "loop\nloop\nloop\n"); - add_simple_case("local variables", R"SOURCE( import "std.zig"; @@ -1613,16 +1594,6 @@ fn a() { ".tmp_source.zig:3:5: error: use of undeclared identifier 'b'", ".tmp_source.zig:4:5: error: use of undeclared identifier 'c'"); - add_compile_fail_case("goto cause unreachable code", R"SOURCE( -fn a() { - goto done; - b(); -done: - return; -} -fn b() {} - )SOURCE", 1, ".tmp_source.zig:4:5: error: unreachable code"); - add_compile_fail_case("parameter redeclaration", R"SOURCE( fn f(a : i32, a : i32) { } @@ -1670,12 +1641,6 @@ fn f() { fn f(a : unreachable) {} )SOURCE", 1, ".tmp_source.zig:2:10: error: parameter of type 'unreachable' not allowed"); - add_compile_fail_case("unused label", R"SOURCE( -fn f() { -a_label: -} - )SOURCE", 1, ".tmp_source.zig:3:1: error: label 'a_label' defined but not used"); - add_compile_fail_case("bad assignment target", R"SOURCE( fn f() { 3 = 3;