Commit a9acc8cb45

Andrew Kelley <superjoe30@gmail.com>
2016-12-28 09:47:02
IR: error for returning from defer expression
also fix peer type resolution for pure error mixed with error union
1 parent dc26dec
src/all_types.hpp
@@ -344,7 +344,7 @@ struct AstNodeDefer {
 
     // temporary data used in IR generation
     Scope *child_scope;
-    Scope *parent_scope;
+    Scope *expr_scope;
 };
 
 struct AstNodeVariableDeclaration {
@@ -1280,6 +1280,7 @@ enum ScopeId {
     ScopeIdDecls,
     ScopeIdBlock,
     ScopeIdDefer,
+    ScopeIdDeferExpr,
     ScopeIdVarDecl,
     ScopeIdCImport,
     ScopeIdLoop,
@@ -1321,11 +1322,21 @@ struct ScopeBlock {
 };
 
 // This scope is created from every defer expression.
+// It's the code following the defer statement.
 // NodeTypeDefer
 struct ScopeDefer {
     Scope base;
 };
 
+// This scope is created from every defer expression.
+// It's the parent of the defer expression itself.
+// NodeTypeDefer
+struct ScopeDeferExpr {
+    Scope base;
+
+    bool reported_err;
+};
+
 // This scope is created for every variable declaration inside an IrExecutable
 // NodeTypeVariableDeclaration, NodeTypeParamDecl
 struct ScopeVarDecl {
src/analyze.cpp
@@ -163,6 +163,13 @@ ScopeDefer *create_defer_scope(AstNode *node, Scope *parent) {
     return scope;
 }
 
+ScopeDeferExpr *create_defer_expr_scope(AstNode *node, Scope *parent) {
+    assert(node->type == NodeTypeDefer);
+    ScopeDeferExpr *scope = allocate<ScopeDeferExpr>(1);
+    init_scope(&scope->base, ScopeIdDeferExpr, node, parent);
+    return scope;
+}
+
 Scope *create_var_scope(AstNode *node, Scope *parent, VariableTableEntry *var) {
     ScopeVarDecl *scope = allocate<ScopeVarDecl>(1);
     init_scope(&scope->base, ScopeIdVarDecl, node, parent);
@@ -2183,6 +2190,7 @@ FnTableEntry *scope_get_fn_if_root(Scope *scope) {
                 return nullptr;
             case ScopeIdDecls:
             case ScopeIdDefer:
+            case ScopeIdDeferExpr:
             case ScopeIdVarDecl:
             case ScopeIdCImport:
             case ScopeIdLoop:
src/analyze.hpp
@@ -84,6 +84,7 @@ void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *
 
 ScopeBlock *create_block_scope(AstNode *node, Scope *parent);
 ScopeDefer *create_defer_scope(AstNode *node, Scope *parent);
+ScopeDeferExpr *create_defer_expr_scope(AstNode *node, Scope *parent);
 Scope *create_var_scope(AstNode *node, Scope *parent, VariableTableEntry *var);
 ScopeCImport *create_cimport_scope(AstNode *node, Scope *parent);
 Scope *create_loop_scope(AstNode *node, Scope *parent);
src/codegen.cpp
@@ -325,6 +325,8 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) {
             scope->di_scope = ZigLLVMLexicalBlockToScope(di_block);
             return scope->di_scope;
         }
+        case ScopeIdDeferExpr:
+            return get_di_scope(g, scope->parent);
     }
     zig_unreachable();
 }
src/ir.cpp
@@ -1980,7 +1980,7 @@ static void ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o
                 (gen_maybe_defers && defer_kind == ReturnKindMaybe))
             {
                 AstNode *defer_expr_node = defer_node->data.defer.expr;
-                ir_gen_node(irb, defer_expr_node, defer_node->data.defer.parent_scope);
+                ir_gen_node(irb, defer_expr_node, defer_node->data.defer.expr_scope);
             }
 
         }
@@ -1994,6 +1994,18 @@ static void ir_set_cursor_at_end(IrBuilder *irb, IrBasicBlock *basic_block) {
     irb->current_basic_block = basic_block;
 }
 
+static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) {
+    while (scope) {
+        if (scope->id == ScopeIdDeferExpr)
+            return (ScopeDeferExpr *)scope;
+        if (scope->id == ScopeIdFnDef)
+            return nullptr;
+
+        scope = scope->parent;
+    }
+    return nullptr;
+}
+
 static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LValPurpose lval) {
     assert(node->type == NodeTypeReturnExpr);
 
@@ -2003,6 +2015,15 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
         return irb->codegen->invalid_instruction;
     }
 
+    ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(scope);
+    if (scope_defer_expr) {
+        if (!scope_defer_expr->reported_err) {
+            add_node_error(irb->codegen, node, buf_sprintf("cannot return from defer expression"));
+            scope_defer_expr->reported_err = true;
+        }
+        return irb->codegen->invalid_instruction;
+    }
+
     Scope *outer_scope = fn_entry->child_scope;
 
     AstNode *expr_node = node->data.return_expr.expr;
@@ -2593,6 +2614,8 @@ static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node,
         return irb->codegen->invalid_instruction;
     }
 
+    // TODO put a variable of same name with invalid type in global scope
+    // so that future references to this same name will find a variable with an invalid type
     add_node_error(irb->codegen, node, buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name)));
     return irb->codegen->invalid_instruction;
 }
@@ -4012,9 +4035,11 @@ static IrInstruction *ir_gen_error_type(IrBuilder *irb, Scope *scope, AstNode *n
 static IrInstruction *ir_gen_defer(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
     assert(node->type == NodeTypeDefer);
 
-    ScopeDefer *defer_scope = create_defer_scope(node, parent_scope);
-    node->data.defer.child_scope = &defer_scope->base;
-    node->data.defer.parent_scope = parent_scope;
+    ScopeDefer *defer_child_scope = create_defer_scope(node, parent_scope);
+    node->data.defer.child_scope = &defer_child_scope->base;
+
+    ScopeDeferExpr *defer_expr_scope = create_defer_expr_scope(node, parent_scope);
+    node->data.defer.expr_scope = &defer_expr_scope->base;
 
     return ir_build_const_void(irb, parent_scope, node);
 }
@@ -4665,6 +4690,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
             ir_add_error_node(ira, source_node,
                 buf_sprintf("unable to make error union out of null literal"));
             return ira->codegen->builtin_types.entry_invalid;
+        } else if (prev_inst->value.type->id == TypeTableEntryIdErrorUnion) {
+            return prev_inst->value.type;
         } else {
             return get_error_type(ira->codegen, prev_inst->value.type);
         }
@@ -4675,6 +4702,8 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
             ir_add_error_node(ira, source_node,
                 buf_sprintf("unable to make maybe out of number literal"));
             return ira->codegen->builtin_types.entry_invalid;
+        } else if (prev_inst->value.type->id == TypeTableEntryIdMaybe) {
+            return prev_inst->value.type;
         } else {
             return get_maybe_type(ira->codegen, prev_inst->value.type);
         }
@@ -9955,6 +9984,7 @@ static TypeTableEntry *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira,
 static TypeTableEntry *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira,
     IrInstructionUnwrapErrPayload *instruction)
 {
+    assert(instruction->value->other);
     IrInstruction *value = instruction->value->other;
     if (value->value.type->id == TypeTableEntryIdInvalid)
         return ira->codegen->builtin_types.entry_invalid;