Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:122255 Return-Path: Delivered-To: mailing list internals@lists.php.net Received: (qmail 53775 invoked from network); 25 Jan 2024 11:28:29 -0000 Received: from unknown (HELO php-smtp4.php.net) (45.112.84.5) by pb1.pair.com with SMTP; 25 Jan 2024 11:28:29 -0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=php.net; s=mail; t=1706182153; bh=nsuJ2l5812ouMZT/v34ZQ1HVywkZyKbLwnRedD8BJ6k=; h=References:In-Reply-To:From:Date:Subject:To:From; b=QJwJyN760nfDF+yesolK3jTW/WIA54LIlCYxtVxyFgGYmuKlvc6ymEMsNwKiggaId KtfQ9D9LsOrGnY9Zpws6t0h0tdtUrjb10HeQX0jh+MwtQtwfXDyPaeYGU2HIDwoFID PCwDleSlFoRvhGoMsXZexjsaXKw5uE5TPUGL+aRiONccPteZ2cAccmq8aKdhYCwAei RLgEphQMCZfaW3yDehJkImqEVwIbXomjCxi0a1SdZd8AJ1f4uYkANb3fhDu6j8EU9e /ciGvreum34ZAWTxbaGy2Wym1cg2ZZ+HU3D2ltywL+vO7QOl47Fh8iS2UvfR2WHkJf NWHpoX4hI3ejQ== Received: from php-smtp4.php.net (localhost [127.0.0.1]) by php-smtp4.php.net (Postfix) with ESMTP id CA04F180048 for ; Thu, 25 Jan 2024 03:29:12 -0800 (PST) X-Spam-Checker-Version: SpamAssassin 4.0.0 (2022-12-13) on php-smtp4.php.net X-Spam-Level: X-Spam-Status: No, score=-3.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,DMARC_PASS,FREEMAIL_FROM, RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS autolearn=no autolearn_force=no version=4.0.0 X-Spam-Virus: No X-Envelope-From: Received: from mail-yb1-f172.google.com (mail-yb1-f172.google.com [209.85.219.172]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by php-smtp4.php.net (Postfix) with ESMTPS for ; Thu, 25 Jan 2024 03:29:12 -0800 (PST) Received: by mail-yb1-f172.google.com with SMTP id 3f1490d57ef6-dc223463ee4so6007872276.2 for ; Thu, 25 Jan 2024 03:28:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1706182106; x=1706786906; darn=lists.php.net; h=content-transfer-encoding:to:subject:message-id:date:from :in-reply-to:references:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=xQOou6kBrFQjZEcxYQBQ2RihMZp5HVyfJxBbdWyJ4Tw=; b=FAh/PL9KM4RZPdc2BQorKmj+LjvKB1MBrbeW+wVXVDine4TCmC2TnaXvjjwzpOlfp8 z96bscPFhWG1bEZX+eyb2ba5oAaDRuZ2qgsGkcltURGZdCesPuUF2hGF29KBJdn1JKR+ 78IkzjzA7qYxYXGvd1bRgv9mkIGS6T57019bpPJY8aQx8ufyzwG4s86o4A4/v7oJYyEB UpugGxzqOnBwoKlgVOIsq9nsGoL7qdxvO/npch0Rv5h4lbhLwwdoMJhqZXHDumkEILP3 skGoxppItPvJ4wAjuqEZjk2l6Tp1BcQ9g55wc/qxqB3m3av4Y5YjoZWo8Ip/PCukuY75 +n+w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1706182106; x=1706786906; h=content-transfer-encoding:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=xQOou6kBrFQjZEcxYQBQ2RihMZp5HVyfJxBbdWyJ4Tw=; b=KoyI5bw8WawjOFb4tp+Ad6RnyvXu1478cKZd2Fhxh7fGSg9QUZYpWpx0SRYAFjfScR 1S5jL+TAZEz2mhtQNuXmq/q0uokMM5cBzxqqOjCp0k7XUEwh2GNVPjXnErCLN+avKrHy EGK2UamCzR01uoswVruQfZAs8JtLaYW7A4FwBMw0iNA+XM0J5RVNEB9NsJKWifVypE1o iMiI1934oVJsP3URJ33pk0AiefLpyvGgSiVF+qtRNWnapI3vG3rOd2sEWv5H+ecU3t2B eH/x2JlhqqKkwtbJ0SQbv2zwBJtxbR72EoANEucDKvRXicbyNAmMQ7UYxe7XVb6S2Co8 mj3Q== X-Gm-Message-State: AOJu0Yw33aPnxJze510gxpXW8XNfQTkUkLVSaZBXxnXC7P2/LVJi5j9z NRWjdaV5+b3pFpYw3Xt4AOXPD2EoHR6nghwoFPuPu7IJm6gQLckdC4+ZOuf7HkD/Xx7YUOYEBEn uRjMgn8Ev5Kjo46SHPdjit3qO2tUABtzVjeA= X-Google-Smtp-Source: AGHT+IHrlmQoO4rtLUXtPx0s/KkM9+TL2cLUFmd6w4xqwL5hajTuokz/cmMk3xfw/l6hN1+63idgVRcunv+Ht2T/24o= X-Received: by 2002:a25:a287:0:b0:dc2:26db:e806 with SMTP id c7-20020a25a287000000b00dc226dbe806mr663171ybi.83.1706182106479; Thu, 25 Jan 2024 03:28:26 -0800 (PST) MIME-Version: 1.0 References: In-Reply-To: Date: Thu, 25 Jan 2024 12:28:15 +0100 Message-ID: To: internals Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Subject: Re: [PHP-DEV] Discussion: making continue and break into an expression From: tovilo.ilija@gmail.com (Ilija Tovilo) Hi Robert On Thu, Jan 25, 2024 at 10:16=E2=80=AFAM Robert Landers wrote: > > Now that throwing is an expression, it allows for some very concise > programming. What are your thoughts on making a break/continue into an > expression as well? > > Instead of: > > while(true) { > ... > if(is_null($arr['var'])) continue; > if($something) continue; else break; > ... > } > > You could write > > while(true) { > ... > $arr['var'] ?? continue; > $something ? continue : break; > ... > } This leads to very similar issues as break/continue inside blocks. See: https://wiki.php.net/rfc/match_blocks#technical_implications_of_control_sta= tements I'll try to explain. The VM works with temporary variables. For the expression foo() + bar() two temporary variables for the result of foo() and bar() will be created, which are then used for the + operation. Normally, + will consume both operands, i.e. use and then free them. However, with break/continue etc. being expressions, it would become possible to skip over the consuming instructions. do { echo foo() + break; } while (true); echo 'Done'; Pseudo opcodes: 0000 V1 =3D CALL foo 0001 JMP 0005 0002 V2 =3D ADD V1 false ; false is here represents a bottom value that will never actually be used 0003 ECHO V2 0004 JMP 0000 0005 ECHO 'Done' Since JMP will skip over the ADD instruction, V1 remains unused. A similar problem already exists for break/continue in foreach itself. foreach ($foos as $foo) { foreach ($bars as $bar) { break 2; } } foreach holds a copy of $bars (in case it gets modified) that normally gets cleaned up when the loop ends. With break over multiple loop-boundaries, we can completely skip over this freeing mechanism. PHP solves this by inserting an explicit FE_FREE instruction before the break 2, which itself is essentially just a JMP to the end of the outer loop. Hopefully it's now more evident why this is a problem: while (true) { foo() && break; } foo() returns a value that would normally be consumed by the && operation. However, with break, we may skip over the && operation entirely. As such, the break itself becomes responsible for freeing these values. This requires significant changes in the compiler to track variables that are currently "live" (i.e. haven't been consumed yet), and emitting FREE opcodes for them as needed. I've implemented this for match blocks here: https://github.com/php/php-src/compare/master...iluuu1994:php-src:match-blo= cks-var-tracking However, note that due to complexity, I've decided to disallow using break/continue and the likes in such contexts to avoid this issue completely, which isn't possible for what you are suggesting. There's another related issue. foo(bar(), break); Function calls in PHP consist of multiple instructions, namely an INIT_CALL, 0-n SEND and a DO_CALL opcode. INIT_CALL creates a stack frame, SEND pushes arguments onto the stack frame, and DO_CALL starts the execution of the function and frees both arguments and stack frame when the function ends. If prior to a SEND opcode we break, we skip over the DO_CALL, so the stack frame needs to be freed manually. The patch linked above solves this by inserting CLEAN_UNFINISHED_CALLS opcodes that do as the name suggests. This mechanism is already used for exceptions. This should work for you, but was insufficient for match blocks, for reasons I won't get into here. All this to say: Don't expect the implementation here to be trivial. Regards, Ilija