Commit 852679c369

Andrew Kelley <andrew@ziglang.org>
2019-09-09 22:44:23
fix a var decl in scope preventing for loop spills
1 parent a399346
Changed files (2)
src
test
stage1
behavior
src/analyze.cpp
@@ -5701,23 +5701,30 @@ static ZigType *get_async_fn_type(CodeGen *g, ZigType *orig_fn_type) {
 //      (await y) + x
 static void mark_suspension_point(Scope *scope) {
     ScopeExpr *child_expr_scope = (scope->id == ScopeIdExpr) ? reinterpret_cast<ScopeExpr *>(scope) : nullptr;
+    bool looking_for_exprs = true;
     for (;;) {
         scope = scope->parent;
         switch (scope->id) {
-            case ScopeIdDefer:
             case ScopeIdDeferExpr:
             case ScopeIdDecls:
             case ScopeIdFnDef:
             case ScopeIdCompTime:
-            case ScopeIdVarDecl:
             case ScopeIdCImport:
             case ScopeIdSuspend:
             case ScopeIdTypeOf:
                 return;
+            case ScopeIdVarDecl:
+            case ScopeIdDefer:
+                looking_for_exprs = false;
+                continue;
             case ScopeIdLoop:
             case ScopeIdRuntime:
                 continue;
             case ScopeIdExpr: {
+                if (!looking_for_exprs) {
+                    // Now we're only looking for a block, to see if it's in a loop (see the case ScopeIdBlock)
+                    continue;
+                }
                 ScopeExpr *parent_expr_scope = reinterpret_cast<ScopeExpr *>(scope);
                 if (child_expr_scope != nullptr) {
                     for (size_t i = 0; parent_expr_scope->children_ptr[i] != child_expr_scope; i += 1) {
test/stage1/behavior/async_fn.zig
@@ -1233,3 +1233,38 @@ test "spill target expr in a for loop" {
     resume S.global_frame;
 }
 
+test "spill target expr in a for loop, with a var decl in the loop body" {
+    const S = struct {
+        var global_frame: anyframe = undefined;
+
+        fn doTheTest() void {
+            var foo = Foo{
+                .slice = [_]i32{1, 2},
+            };
+            expect(atest(&foo) == 3);
+        }
+
+        const Foo = struct {
+            slice: []i32,
+        };
+
+        fn atest(foo: *Foo) i32 {
+            var sum: i32 = 0;
+            for (foo.slice) |x| {
+                // Previously this var decl would prevent spills. This test makes sure
+                // the for loop spills still happen even though there is a VarDecl in scope
+                // before the suspend.
+                var anything = true;
+                _ = anything;
+                suspend {
+                    global_frame = @frame();
+                }
+                sum += x;
+            }
+            return sum;
+        }
+    };
+    _ = async S.doTheTest();
+    resume S.global_frame;
+    resume S.global_frame;
+}