Skip to content

Commit b53ed2b

Browse files
committed
Make Generator Yield Type Inference Inside Bodies more real
1 parent ad8d5f9 commit b53ed2b

1 file changed

Lines changed: 16 additions & 12 deletions

File tree

example.php

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,21 +1215,23 @@ public function foreachGeneratorParam(\Generator $pencils): void
12151215
//
12161216
// Generator<TKey, TValue, TSend, TReturn>
12171217
//
1218-
// - `yield $expr` produces TValue to the consumer
1219-
// - `$var = yield $expr` assigns TSend (the sent value) to $var
1220-
// - Reverse yield inference: when `yield $var` appears and $var has no
1221-
// prior assignment, PHPantom infers $var as TValue from the @return type
1218+
// - `yield $expr` produces TValue to the consumer. The yielded variable
1219+
// keeps its own type (from its assignment), not the Generator annotation.
1220+
// - `$var = yield $expr` assigns TSend (the sent value) to $var. The yield
1221+
// expression evaluates to whatever was passed via ->send().
12221222

12231223
class GeneratorYieldDemo
12241224
{
12251225
/** @return \Generator<int, Pen> */
12261226
public function findAll(): \Generator
12271227
{
1228-
// Reverse yield inference: $pen has no assignment, so the LSP
1229-
// infers it as Pen from @return Generator<int, Pen> (TValue)
1228+
// The type of $pen comes from `new Pen(...)`, not from the @return.
1229+
// Completion on $pen-> works because the assignment is known.
1230+
$pen = new Pen('blue');
12301231
yield $pen;
12311232
$pen->write(); // resolves to Pen
12321233

1234+
$anotherPen = new Pen('red');
12331235
yield 0 => $anotherPen;
12341236
$anotherPen->color(); // key => value yields also work
12351237
}
@@ -1238,9 +1240,9 @@ public function findAll(): \Generator
12381240
public function yieldInsideControlFlow(): \Generator
12391241
{
12401242
if (true) {
1241-
// Reverse inference works inside control flow blocks too
1243+
$pen = new Pen('green');
12421244
yield $pen;
1243-
$pen->write(); // resolves to Pen
1245+
$pen->write(); // resolves inside control flow blocks
12441246
}
12451247
}
12461248

@@ -1249,15 +1251,15 @@ public function chainingThroughYieldInferred(): \Generator
12491251
{
12501252
$pen = new Pen('black');
12511253
yield $pen;
1252-
$pen->rename('Bold')->color(); // chains through inferred type
1254+
$pen->rename('Bold')->color(); // chains through yielded variable
12531255
}
12541256

12551257
/** @return \Generator<int, string, Pencil, void> */
12561258
public function coroutine(): \Generator
12571259
{
12581260
// TSend inference: $var = yield gets the 3rd Generator type param.
12591261
// yield produces 'ready' (TValue = string) to the consumer;
1260-
// the yield expression evaluates to whatever was ->send()'d (TSend = Pencil)
1262+
// the yield expression evaluates to whatever was ->send()'d (TSend = Pencil).
12611263
$pencil = yield 'ready';
12621264
$pencil->sketch(); // resolves to Pencil (TSend)
12631265
}
@@ -4034,9 +4036,11 @@ function runDemoAssertions(): void
40344036
}
40354037

40364038
// ── Generator yield inference (GeneratorYieldDemo) ───────────────────
4037-
// findAll() uses reverse yield inference (unassigned vars) so it yields
4038-
// null at runtime. Only chainingThroughYieldInferred() yields real Pens.
40394039
$yieldDemo = new GeneratorYieldDemo();
4040+
foreach ($yieldDemo->findAll() as $yieldedPen) {
4041+
assert($yieldedPen instanceof Pen, 'GeneratorYieldDemo::findAll() must yield Pen');
4042+
break;
4043+
}
40404044
foreach ($yieldDemo->chainingThroughYieldInferred() as $chainPen) {
40414045
assert($chainPen instanceof Pen, 'chainingThroughYieldInferred() must yield Pen');
40424046
break;

0 commit comments

Comments
 (0)