diff options
| author | Andrew Kelley <superjoe30@gmail.com> | 2016-05-03 15:06:34 -0700 |
|---|---|---|
| committer | Andrew Kelley <superjoe30@gmail.com> | 2016-05-03 15:06:34 -0700 |
| commit | 7f589c0cab105a79ff6d71233538dca8a39f8152 (patch) | |
| tree | cd3a1eb6b05578bbddbe76feab6ffb63d76f5610 /src | |
| parent | 9ccd0ba9611d7828f42bffca919c7ad3177cbbe1 (diff) | |
| download | zig-7f589c0cab105a79ff6d71233538dca8a39f8152.tar.gz zig-7f589c0cab105a79ff6d71233538dca8a39f8152.zip | |
support maybe destructuring into a pointer variable
Diffstat (limited to 'src')
| -rw-r--r-- | src/all_types.hpp | 3 | ||||
| -rw-r--r-- | src/analyze.cpp | 18 | ||||
| -rw-r--r-- | src/codegen.cpp | 117 | ||||
| -rw-r--r-- | src/parser.cpp | 17 |
4 files changed, 122 insertions, 33 deletions
diff --git a/src/all_types.hpp b/src/all_types.hpp index 0227ddf535..c0f97edbb7 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -479,6 +479,7 @@ struct AstNodeIfVarExpr { AstNodeVariableDeclaration var_decl; AstNode *then_block; AstNode *else_node; // null, block node, or other if expr node + bool var_is_ptr; // populated by semantic analyzer TypeTableEntry *type; @@ -502,6 +503,7 @@ struct AstNodeForExpr { AstNode *elem_node; // always a symbol AstNode *index_node; // always a symbol, might be null AstNode *body; + bool elem_is_ptr; // populated by semantic analyzer bool contains_break; @@ -509,7 +511,6 @@ struct AstNodeForExpr { Expr resolved_expr; VariableTableEntry *elem_var; VariableTableEntry *index_var; - bool elem_is_ptr; }; struct AstNodeSwitchExpr { diff --git a/src/analyze.cpp b/src/analyze.cpp index 4d286fe03c..0958e11453 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -41,7 +41,7 @@ static TopLevelDecl *get_as_top_level_decl(AstNode *node); static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *source_node, AstNodeVariableDeclaration *variable_declaration, - bool expr_is_maybe, AstNode *decl_node); + bool expr_is_maybe, AstNode *decl_node, bool var_is_ptr); static void scan_decls(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node); static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry); @@ -1582,7 +1582,7 @@ static void resolve_top_level_decl(CodeGen *g, AstNode *node, bool pointer_only) { AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration; VariableTableEntry *var = analyze_variable_declaration_raw(g, import, import->block_context, - node, variable_declaration, false, node); + node, variable_declaration, false, node, false); g->global_vars.append(var); break; @@ -3503,7 +3503,7 @@ static TypeTableEntry *analyze_unwrap_error_expr(CodeGen *g, ImportTableEntry *i static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *source_node, AstNodeVariableDeclaration *variable_declaration, - bool expr_is_maybe, AstNode *decl_node) + bool expr_is_maybe, AstNode *decl_node, bool var_is_ptr) { bool is_const = variable_declaration->is_const; bool is_export = (variable_declaration->top_level_decl.visib_mod == VisibModExport); @@ -3528,7 +3528,12 @@ static VariableTableEntry *analyze_variable_declaration_raw(CodeGen *g, ImportTa // ignore the poison value } else if (expr_is_maybe) { if (implicit_type->id == TypeTableEntryIdMaybe) { - implicit_type = implicit_type->data.maybe.child_type; + if (var_is_ptr) { + // TODO if the expression is constant, can't get pointer to it + implicit_type = get_pointer_to_type(g, implicit_type->data.maybe.child_type, false); + } else { + implicit_type = implicit_type->data.maybe.child_type; + } } else { add_node_error(g, variable_declaration->expr, buf_sprintf("expected maybe type")); implicit_type = g->builtin_types.entry_invalid; @@ -3575,7 +3580,8 @@ static VariableTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableE BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration; - return analyze_variable_declaration_raw(g, import, context, node, variable_declaration, false, nullptr); + return analyze_variable_declaration_raw(g, import, context, node, variable_declaration, + false, nullptr, false); } static TypeTableEntry *analyze_null_literal_expr(CodeGen *g, ImportTableEntry *import, @@ -3945,7 +3951,7 @@ static TypeTableEntry *analyze_if_var_expr(CodeGen *g, ImportTableEntry *import, BlockContext *child_context = new_block_context(node, parent_context); analyze_variable_declaration_raw(g, import, child_context, node, &node->data.if_var_expr.var_decl, true, - nullptr); + nullptr, node->data.if_var_expr.var_is_ptr); VariableTableEntry *var = node->data.if_var_expr.var_decl.variable; if (var->type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; diff --git a/src/codegen.cpp b/src/codegen.cpp index 5d97d00616..1112c7947f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -212,7 +212,7 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *expr_node); static LLVMValueRef gen_lvalue(CodeGen *g, AstNode *expr_node, AstNode *node, TypeTableEntry **out_type_entry); static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node, bool is_lvalue); static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVariableDeclaration *var_decl, - bool unwrap_maybe, LLVMValueRef *init_val, TypeTableEntry **init_val_type); + bool unwrap_maybe, LLVMValueRef *init_val, TypeTableEntry **init_val_type, bool var_is_ptr); static LLVMValueRef gen_assign_raw(CodeGen *g, AstNode *source_node, BinOpType bin_op, LLVMValueRef target_ref, LLVMValueRef value, TypeTableEntry *op1_type, TypeTableEntry *op2_type); @@ -2102,21 +2102,32 @@ static LLVMValueRef gen_if_bool_expr(CodeGen *g, AstNode *node) { } } +static void gen_var_debug_decl(CodeGen *g, VariableTableEntry *var) { + BlockContext *block_context = var->block_context; + AstNode *source_node = var->decl_node; + LLVMZigDILocation *debug_loc = LLVMZigGetDebugLoc(source_node->line + 1, source_node->column + 1, + block_context->di_scope); + LLVMZigInsertDeclareAtEnd(g->dbuilder, var->value_ref, var->di_loc_var, debug_loc, + LLVMGetInsertBlock(g->builder)); +} + static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeIfVarExpr); assert(node->data.if_var_expr.var_decl.expr); - LLVMValueRef init_val; - TypeTableEntry *expr_type; - gen_var_decl_raw(g, node, &node->data.if_var_expr.var_decl, true, &init_val, &expr_type); + AstNodeVariableDeclaration *var_decl = &node->data.if_var_expr.var_decl; + VariableTableEntry *variable = var_decl->variable; // test if value is the maybe state - assert(expr_type->id == TypeTableEntryIdMaybe); + TypeTableEntry *expr_type = get_expr_type(var_decl->expr); TypeTableEntry *child_type = expr_type->data.maybe.child_type; + + LLVMValueRef init_val = gen_expr(g, var_decl->expr); + LLVMValueRef cond_value; - if (child_type->id == TypeTableEntryIdPointer || - child_type->id == TypeTableEntryIdFn) - { + bool maybe_is_ptr = child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn; + if (maybe_is_ptr) { + set_debug_source_node(g, node); cond_value = LLVMBuildICmp(g->builder, LLVMIntNE, init_val, LLVMConstNull(child_type->type_ref), ""); } else { set_debug_source_node(g, node); @@ -2124,11 +2135,80 @@ static LLVMValueRef gen_if_var_expr(CodeGen *g, AstNode *node) { cond_value = LLVMBuildLoad(g->builder, maybe_field_ptr, ""); } - LLVMValueRef return_value = gen_if_bool_expr_raw(g, node, cond_value, - node->data.if_var_expr.then_block, - node->data.if_var_expr.else_node); + AstNode *then_node = node->data.if_var_expr.then_block; + AstNode *else_node = node->data.if_var_expr.else_node; + + TypeTableEntry *then_type = get_expr_type(then_node); + TypeTableEntry *else_type = get_expr_type(else_node); + + bool use_then_value = type_has_bits(then_type); + bool use_else_value = type_has_bits(else_type); + + LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeThen"); + LLVMBasicBlockRef else_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeElse"); + + LLVMBasicBlockRef endif_block; + bool then_endif_reachable = then_type->id != TypeTableEntryIdUnreachable; + bool else_endif_reachable = else_type->id != TypeTableEntryIdUnreachable; + if (then_endif_reachable || else_endif_reachable) { + endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "MaybeEndIf"); + } + + set_debug_source_node(g, node); + LLVMBuildCondBr(g->builder, cond_value, then_block, else_block); + + LLVMPositionBuilderAtEnd(g->builder, then_block); + if (node->data.if_var_expr.var_is_ptr) { + LLVMValueRef payload_ptr; + if (maybe_is_ptr) { + zig_panic("TODO"); + } else { + payload_ptr = LLVMBuildStructGEP(g->builder, init_val, 0, ""); + } + LLVMBuildStore(g->builder, payload_ptr, variable->value_ref); + } else { + LLVMValueRef payload_val; + if (maybe_is_ptr) { + payload_val = init_val; + } else { + LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, init_val, 0, ""); + payload_val = get_handle_value(g, node, payload_ptr, child_type); + } + gen_assign_raw(g, node, BinOpTypeAssign, variable->value_ref, payload_val, + variable->type, child_type); + } + gen_var_debug_decl(g, variable); + + LLVMValueRef then_expr_result = gen_expr(g, then_node); + if (then_endif_reachable) { + LLVMBuildBr(g->builder, endif_block); + } + LLVMBasicBlockRef after_then_block = LLVMGetInsertBlock(g->builder); + - return return_value; + LLVMPositionBuilderAtEnd(g->builder, else_block); + LLVMValueRef else_expr_result = gen_expr(g, else_node); + if (else_endif_reachable) { + LLVMBuildBr(g->builder, endif_block); + } + LLVMBasicBlockRef after_else_block = LLVMGetInsertBlock(g->builder); + + if (then_endif_reachable || else_endif_reachable) { + LLVMPositionBuilderAtEnd(g->builder, endif_block); + if (use_then_value && use_else_value) { + LLVMValueRef phi = LLVMBuildPhi(g->builder, LLVMTypeOf(then_expr_result), ""); + LLVMValueRef incoming_values[2] = {then_expr_result, else_expr_result}; + LLVMBasicBlockRef incoming_blocks[2] = {after_then_block, after_else_block}; + LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2); + return phi; + } else if (use_then_value) { + return then_expr_result; + } else if (use_else_value) { + return else_expr_result; + } + } + + return nullptr; } static LLVMValueRef gen_block(CodeGen *g, AstNode *block_node, TypeTableEntry *implicit_return_type) { @@ -2456,15 +2536,6 @@ static LLVMValueRef gen_while_expr(CodeGen *g, AstNode *node) { return nullptr; } -static void gen_var_debug_decl(CodeGen *g, VariableTableEntry *var) { - BlockContext *block_context = var->block_context; - AstNode *source_node = var->decl_node; - LLVMZigDILocation *debug_loc = LLVMZigGetDebugLoc(source_node->line + 1, source_node->column + 1, - block_context->di_scope); - LLVMZigInsertDeclareAtEnd(g->dbuilder, var->value_ref, var->di_loc_var, debug_loc, - LLVMGetInsertBlock(g->builder)); -} - static LLVMValueRef gen_for_expr(CodeGen *g, AstNode *node) { assert(node->type == NodeTypeForExpr); assert(node->data.for_expr.array_expr); @@ -2564,7 +2635,7 @@ static LLVMValueRef gen_continue(CodeGen *g, AstNode *node) { } static LLVMValueRef gen_var_decl_raw(CodeGen *g, AstNode *source_node, AstNodeVariableDeclaration *var_decl, - bool unwrap_maybe, LLVMValueRef *init_value, TypeTableEntry **expr_type) + bool unwrap_maybe, LLVMValueRef *init_value, TypeTableEntry **expr_type, bool var_is_ptr) { VariableTableEntry *variable = var_decl->variable; @@ -2683,7 +2754,7 @@ static LLVMValueRef gen_var_decl_expr(CodeGen *g, AstNode *node) { LLVMValueRef init_val; TypeTableEntry *init_val_type; - return gen_var_decl_raw(g, node, &node->data.variable_declaration, false, &init_val, &init_val_type); + return gen_var_decl_raw(g, node, &node->data.variable_declaration, false, &init_val, &init_val_type, false); } static LLVMValueRef gen_symbol(CodeGen *g, AstNode *node) { diff --git a/src/parser.cpp b/src/parser.cpp index 04434f82e6..2c25526ec6 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1755,7 +1755,7 @@ static AstNode *ast_parse_else(ParseContext *pc, int *token_index, bool mandator /* IfExpression : IfVarExpression | IfBoolExpression IfBoolExpression : token(If) token(LParen) Expression token(RParen) Expression option(Else) -IfVarExpression : token(If) token(LParen) (token(Const) | token(Var)) token(Symbol) option(Expression) Token(MaybeAssign) Expression token(RParen) Expression Option(Else) +IfVarExpression = "if" "(" ("const" | "var") option("*") "Symbol" option(":" TypeExpr) "?=" Expression ")" Expression Option(Else) */ static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool mandatory) { Token *if_tok = &pc->tokens->at(*token_index); @@ -1776,8 +1776,19 @@ static AstNode *ast_parse_if_expr(ParseContext *pc, int *token_index, bool manda node->data.if_var_expr.var_decl.is_const = (token->id == TokenIdKeywordConst); *token_index += 1; - Token *name_token = ast_eat_token(pc, token_index, TokenIdSymbol); - ast_buf_from_token(pc, name_token, &node->data.if_var_expr.var_decl.symbol); + Token *star_or_symbol = &pc->tokens->at(*token_index); + if (star_or_symbol->id == TokenIdStar) { + *token_index += 1; + node->data.if_var_expr.var_is_ptr = true; + Token *name_token = ast_eat_token(pc, token_index, TokenIdSymbol); + ast_buf_from_token(pc, name_token, &node->data.if_var_expr.var_decl.symbol); + } else if (star_or_symbol->id == TokenIdSymbol) { + *token_index += 1; + ast_buf_from_token(pc, star_or_symbol, &node->data.if_var_expr.var_decl.symbol); + } else { + ast_invalid_token_error(pc, star_or_symbol); + } + Token *eq_or_colon = &pc->tokens->at(*token_index); if (eq_or_colon->id == TokenIdMaybeAssign) { |
