Commit 7f589c0cab

Andrew Kelley <superjoe30@gmail.com>
2016-05-04 00:06:34
support maybe destructuring into a pointer variable
1 parent 9ccd0ba
doc/langref.md
@@ -93,7 +93,7 @@ IfExpression = IfVarExpression | IfBoolExpression
 
 IfBoolExpression = "if" "(" Expression ")" Expression option(Else)
 
-IfVarExpression = "if" "(" ("const" | "var") "Symbol" option(":" TypeExpr) "?=" Expression ")" Expression Option(Else)
+IfVarExpression = "if" "(" ("const" | "var") option("*") "Symbol" option(":" TypeExpr) "?=" Expression ")" Expression Option(Else)
 
 Else = "else" Expression
 
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 {
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;
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) {
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) {
test/self_hosted.zig
@@ -1409,3 +1409,36 @@ fn string_escapes() {
     assert(str.eql("\\", "\x5c"));
     assert(str.eql("\u1234\u0069", "\xe1\x88\xb4\x69"));
 }
+
+#attribute("test")
+fn if_var_maybe_pointer() {
+    assert(should_be_a_plus_1(Particle {.a = 14, .b = 1, .c = 1, .d = 1}) == 15);
+}
+#static_eval_enable(false)
+fn should_be_a_plus_1(p: Particle) -> u64 {
+    var maybe_particle: ?Particle = p;
+    if (const *particle ?= maybe_particle) {
+        particle.a += 1;
+    }
+    if (const particle ?= maybe_particle) {
+        return particle.a;
+    }
+    return 0;
+}
+struct Particle {
+    a: u64,
+    b: u64,
+    c: u64,
+    d: u64,
+}
+
+#attribute("test")
+fn assign_to_if_var_ptr() {
+    var maybe_bool: ?bool = true;
+
+    if (const *b ?= maybe_bool) {
+        *b = false;
+    }
+
+    assert(??maybe_bool == false);
+}