]> granicus.if.org Git - php/commitdiff
Fixed bug #78434
authorNikita Popov <nikita.ppv@gmail.com>
Fri, 3 Apr 2020 08:06:41 +0000 (10:06 +0200)
committerNikita Popov <nikita.ppv@gmail.com>
Thu, 9 Apr 2020 08:33:11 +0000 (10:33 +0200)
The DO_INIT flag, which will skip the first resume on a primed
generator, should always be set when starting to yield from a
new generator, not only when the yield from happens during priming.

NEWS
Zend/tests/generators/bug78434.phpt [new file with mode: 0644]
Zend/tests/generators/yield_from_multi_tree.phpt
Zend/zend_generators.c

diff --git a/NEWS b/NEWS
index 67d71eca64b5ea704f3c3562dfd05fdab361fa12..dd770268aefd2d916dfdbab846c91d31cae543d7 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -3,6 +3,9 @@ PHP                                                                        NEWS
 
 ?? ??? ????, PHP 7.4.6
 
+- Core:
+  . Fixed bug #78434 (Generator yields no items after valid() call). (Nikita)
+
 - DOM:
   . Fixed bug #78221 (DOMNode::normalize() doesn't remove empty text nodes).
     (cmb)
diff --git a/Zend/tests/generators/bug78434.phpt b/Zend/tests/generators/bug78434.phpt
new file mode 100644 (file)
index 0000000..dd9351e
--- /dev/null
@@ -0,0 +1,27 @@
+--TEST--
+Bug #78434: Generator skips first item after valid() call
+--FILE--
+<?php
+
+$function = function () {
+    yield 0;
+};
+
+$wrapper = function () use ($function) {
+    $generator = $function();
+    $generator->valid();
+    yield from $generator;
+
+    $generator = $function();
+    $generator->valid();
+    yield from $generator;
+};
+
+foreach ($wrapper() as $value) {
+    echo $value, "\n";
+}
+
+?>
+--EXPECT--
+0
+0
index 9bec1381496ffb19ad6a3a12d979d4682fbfb995..808d693eaec20461acdba0fd437b1dd6dbacccdf 100644 (file)
@@ -10,9 +10,6 @@ function from($levels) {
 }
 
 function gen($gen, $level) {
-       if ($level % 2) {
-               yield $gen->current();
-       }
        yield from $gen;
 }
 
index 7031e9a8105f8b521a8afb28b2c67b8be63cc13d..9d8546f5185b34b6bfdcc2fb0293424be595b459 100644 (file)
@@ -609,6 +609,7 @@ void zend_generator_yield_from(zend_generator *generator, zend_generator *from)
        generator->node.parent = from;
        zend_generator_get_current(generator);
        GC_DELREF(&from->std);
+       generator->flags |= ZEND_GENERATOR_DO_INIT;
 }
 
 ZEND_API zend_generator *zend_generator_update_current(zend_generator *generator, zend_generator *leaf)
@@ -785,6 +786,7 @@ try_again:
 
        if (UNEXPECTED((orig_generator->flags & ZEND_GENERATOR_DO_INIT) != 0 && !Z_ISUNDEF(generator->value))) {
                /* We must not advance Generator if we yield from a Generator being currently run */
+               orig_generator->flags &= ~ZEND_GENERATOR_DO_INIT;
                return;
        }
 
@@ -864,15 +866,15 @@ try_again:
                        goto try_again;
                }
        }
+
+       orig_generator->flags &= ~ZEND_GENERATOR_DO_INIT;
 }
 /* }}} */
 
 static inline void zend_generator_ensure_initialized(zend_generator *generator) /* {{{ */
 {
        if (UNEXPECTED(Z_TYPE(generator->value) == IS_UNDEF) && EXPECTED(generator->execute_data) && EXPECTED(generator->node.parent == NULL)) {
-               generator->flags |= ZEND_GENERATOR_DO_INIT;
                zend_generator_resume(generator);
-               generator->flags &= ~ZEND_GENERATOR_DO_INIT;
                generator->flags |= ZEND_GENERATOR_AT_FIRST_YIELD;
        }
 }