hphp/hack/doc/async/await-as-an-expression-spec.md
For a summary, see the await-as-an-expression docs.
The guiding principle of “unconditionally consumed with the statement” is to prevent computation from being thrown away. Since we could run the awaits before the statement, if they turn out to not be consumed, it will result in waste. We intentionally ignore the possibility of Exceptions being thrown for the definition of “conditional”.
For the position to be considered “unconditionally consumed” we require all parents of the await expression until the closest statement pass this check.
Valid positions:
(await $yes) ? (await $no) : (await $no)(await $yes)->foo(await $yes, await $yes)(await $yes)?->foo(await $no, await $no)(await $yes)?->(await $no)(<token>)(await $yes)(await $yes)->(await $yes)(await $yes)::(await $yes)(await $yes) is Int(await $yes) as Int(await $yes) ?as Intempty(await $yes)isset(await $yes)(await $yes){await $yes}“{await $yes}"Map { await $yes => await $yes } or Vector { await $yes }new (await $yes)(await $yes)shape('key'`` => await $yes)tuple(await $yes, await $yes)array(await $yes => await $yes)[await $yes => await $yes]darray[await $yes => await $yes]varray[await $yes]dict[await $yes => await $yes]keyset[await $yes]vec[await $yes]await $yes => await $yes(await $yes)[await $yes]{(await $yes)[await $yes]}yield (await $yes)await $yes, await $yes!: !(await $yes)~: ~(await $yes)+: +(await $yes)-: -(await $yes)@: @(await $yes)clone: clone (await $yes)print: print (await $yes)AND: (await $yes) AND (await $no)OR: (await $yes) OR (await $no)||: (await $yes) || (await $no)&&: (await $yes) && (await $no)?:: (await $yes) ?: (await $no)??: (await $yes) ?? (await $no)=: (await $no) = (await $yes)|=: (await $no) |= (await $yes)+=: (await $no) += (await $yes)*=: (await $no) *= (await $yes)**=: (await $no) **= (await $yes)/=: (await $no) /= (await $yes).=: (await $no) .= (await $yes)-=: (await $no) -= (await $yes)%=: (await $no) %= (await $yes)^=: (await $no) ^= (await $yes)&=: (await $no) &= (await $yes)<<=: (await $no) <<= (await $yes)>>=: (await $no) >>= (await $yes)??=: (await $no) ??= (await $no)+: (await $yes) + (await $yes)-: (await $yes) - (await $yes)*: (await $yes) * (await $yes)/: (await $yes) / (await $yes)**: (await $yes) ** (await $yes)===: (await $yes) === (await $yes)<: (await $yes) < (await $yes)>: (await $yes) > (await $yes)==: (await $yes) == (await $yes)%: (await $yes) % (await $yes).: (await $yes) . (await $yes)!=: (await $yes) != (await $yes)<>: (await $yes) <> (await $yes)!==: (await $yes) !== (await $yes)<=: (await $yes) <= (await $yes)<=>: (await $yes) <=> (await $yes)>=: (await $yes) >= (await $yes)&: (await $yes) & (await $yes)|: (await $yes) | (await $yes)<<: (await $yes) << (await $yes)>>: (await $yes) >> (await $yes)^: (await $yes) ^ (await $yes)|>): Since we disallow dependent awaits in a single statement (or concurrent block), we need to disallow awaits in pipe operators that de-sugar to nested awaits. The simple rule to disallow this: we treat the $$ as an await if the left side contains an await.
(await $x) |> (await $$) de-sugars into (await (await $x))$x |> (await $$) de-sugars into (await $x)(await $x) |> f($$) de-sugars into f(await $x)(await $x) |> (f($$) + await $y) de-sugars into f(await $x) + await $y(await $x) |> f($$) |> await g($$) de-sugars into await g(f(await $x))(await $x) |> $y ?? $$ de-sugars into $y ?? (``await $x)await $yes;return await $yes;unset($a[await $yes]);echo (await $yes);print (await $yes);if (await $yes) { await $yes_but_new_statement; }throw (await $yes);switch (await $yes) { ... }foreach ((await $yes) [await] as ... ) { ... }for (await $yes; await $no; await $no) { ... }