Skip to content

Commit 8bed5b2

Browse files
committed
Fix GH-8661: Nullsafe in coalesce triggers undefined variable warning
1 parent 171ebb1 commit 8bed5b2

File tree

7 files changed

+68
-28
lines changed

7 files changed

+68
-28
lines changed

Zend/Optimizer/sccp.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1563,7 +1563,7 @@ static void sccp_visit_instr(scdf_ctx *scdf, zend_op *opline, zend_ssa_op *ssa_o
15631563
SET_RESULT(result, op1);
15641564
break;
15651565
case ZEND_JMP_NULL:
1566-
switch (opline->extended_value) {
1566+
switch (opline->extended_value & ZEND_SHORT_CIRCUITING_CHAIN_MASK) {
15671567
case ZEND_SHORT_CIRCUITING_CHAIN_EXPR:
15681568
ZVAL_NULL(&zv);
15691569
break;

Zend/Optimizer/zend_inference.c

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2460,16 +2460,19 @@ static zend_always_inline zend_result _zend_update_type_info(
24602460
COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->result_def);
24612461
break;
24622462
case ZEND_JMP_NULL:
2463-
if (opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EXPR) {
2463+
{
2464+
uint32_t short_circuiting_type = opline->extended_value & ZEND_SHORT_CIRCUITING_CHAIN_MASK;
2465+
if (short_circuiting_type == ZEND_SHORT_CIRCUITING_CHAIN_EXPR) {
24642466
tmp = MAY_BE_NULL;
2465-
} else if (opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) {
2467+
} else if (short_circuiting_type == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) {
24662468
tmp = MAY_BE_FALSE;
24672469
} else {
2468-
ZEND_ASSERT(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY);
2470+
ZEND_ASSERT(short_circuiting_type == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY);
24692471
tmp = MAY_BE_TRUE;
24702472
}
24712473
UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
24722474
break;
2475+
}
24732476
case ZEND_ASSIGN_OP:
24742477
case ZEND_ASSIGN_DIM_OP:
24752478
case ZEND_ASSIGN_OBJ_OP:
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
GH-8661: Nullsafe in coalesce triggers undefined variable error
3+
--FILE--
4+
<?php
5+
6+
var_dump($a?->foo ?? null);
7+
8+
?>
9+
--EXPECT--
10+
NULL

Zend/zend_compile.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2264,21 +2264,24 @@ static void zend_short_circuiting_commit(uint32_t checkpoint, znode *result, zen
22642264
zend_op *opline = &CG(active_op_array)->opcodes[opnum];
22652265
opline->op2.opline_num = get_next_op_number();
22662266
SET_NODE(opline->result, result);
2267-
opline->extended_value =
2267+
opline->extended_value |=
22682268
ast->kind == ZEND_AST_ISSET ? ZEND_SHORT_CIRCUITING_CHAIN_ISSET :
22692269
ast->kind == ZEND_AST_EMPTY ? ZEND_SHORT_CIRCUITING_CHAIN_EMPTY :
22702270
ZEND_SHORT_CIRCUITING_CHAIN_EXPR;
22712271
zend_stack_del_top(&CG(short_circuiting_opnums));
22722272
}
22732273
}
22742274

2275-
static void zend_emit_jmp_null(znode *obj_node)
2275+
static void zend_emit_jmp_null(znode *obj_node, uint32_t bp_type)
22762276
{
22772277
uint32_t jmp_null_opnum = get_next_op_number();
22782278
zend_op *opline = zend_emit_op(NULL, ZEND_JMP_NULL, obj_node, NULL);
22792279
if (opline->op1_type == IS_CONST) {
22802280
Z_TRY_ADDREF_P(CT_CONSTANT(opline->op1));
22812281
}
2282+
if (bp_type == BP_VAR_IS) {
2283+
opline->extended_value |= ZEND_JMP_NULL_BP_VAR_IS;
2284+
}
22822285
zend_stack_push(&CG(short_circuiting_opnums), &jmp_null_opnum);
22832286
}
22842287

@@ -2853,7 +2856,7 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t
28532856
}
28542857
}
28552858
}
2856-
zend_emit_jmp_null(&obj_node);
2859+
zend_emit_jmp_null(&obj_node, type);
28572860
}
28582861
}
28592862

@@ -4464,7 +4467,7 @@ static void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type
44644467
zend_short_circuiting_mark_inner(obj_ast);
44654468
zend_compile_expr(&obj_node, obj_ast);
44664469
if (nullsafe) {
4467-
zend_emit_jmp_null(&obj_node);
4470+
zend_emit_jmp_null(&obj_node, type);
44684471
}
44694472
}
44704473

Zend/zend_compile.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,10 +372,14 @@ typedef struct _zend_oparray_context {
372372
/* call through internal function handler. e.g. Closure::invoke() */
373373
#define ZEND_ACC_CALL_VIA_HANDLER ZEND_ACC_CALL_VIA_TRAMPOLINE
374374

375+
#define ZEND_SHORT_CIRCUITING_CHAIN_MASK 0x3
375376
#define ZEND_SHORT_CIRCUITING_CHAIN_EXPR 0
376377
#define ZEND_SHORT_CIRCUITING_CHAIN_ISSET 1
377378
#define ZEND_SHORT_CIRCUITING_CHAIN_EMPTY 2
378379

380+
// Must not clash with ZEND_SHORT_CIRCUITING_CHAIN_MASK
381+
#define ZEND_JMP_NULL_BP_VAR_IS 4
382+
379383
char *zend_visibility_string(uint32_t fn_flags);
380384

381385
typedef struct _zend_property_info {

Zend/zend_vm_def.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7574,19 +7574,23 @@ ZEND_VM_HOT_NOCONST_HANDLER(198, ZEND_JMP_NULL, CONST|TMP|VAR|CV, JMP_ADDR)
75747574
}
75757575

75767576
result = EX_VAR(opline->result.var);
7577-
if (EXPECTED(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EXPR)) {
7577+
uint32_t short_circuiting_type = opline->extended_value & ZEND_SHORT_CIRCUITING_CHAIN_MASK;
7578+
if (EXPECTED(short_circuiting_type == ZEND_SHORT_CIRCUITING_CHAIN_EXPR)) {
75787579
ZVAL_NULL(result);
7579-
if (OP1_TYPE == IS_CV && UNEXPECTED(Z_TYPE_P(val) == IS_UNDEF)) {
7580+
if (OP1_TYPE == IS_CV
7581+
&& UNEXPECTED(Z_TYPE_P(val) == IS_UNDEF)
7582+
&& (opline->extended_value & ZEND_JMP_NULL_BP_VAR_IS) == 0
7583+
) {
75807584
SAVE_OPLINE();
75817585
ZVAL_UNDEFINED_OP1();
75827586
if (UNEXPECTED(EG(exception) != NULL)) {
75837587
HANDLE_EXCEPTION();
75847588
}
75857589
}
7586-
} else if (opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) {
7590+
} else if (short_circuiting_type == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) {
75877591
ZVAL_FALSE(result);
75887592
} else {
7589-
ZEND_ASSERT(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY);
7593+
ZEND_ASSERT(short_circuiting_type == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY);
75907594
ZVAL_TRUE(result);
75917595
}
75927596

Zend/zend_vm_execute.h

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5244,19 +5244,23 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMP_NULL_SPEC_CON
52445244
}
52455245

52465246
result = EX_VAR(opline->result.var);
5247-
if (EXPECTED(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EXPR)) {
5247+
uint32_t short_circuiting_type = opline->extended_value & ZEND_SHORT_CIRCUITING_CHAIN_MASK;
5248+
if (EXPECTED(short_circuiting_type == ZEND_SHORT_CIRCUITING_CHAIN_EXPR)) {
52485249
ZVAL_NULL(result);
5249-
if (IS_CONST == IS_CV && UNEXPECTED(Z_TYPE_P(val) == IS_UNDEF)) {
5250+
if (IS_CONST == IS_CV
5251+
&& UNEXPECTED(Z_TYPE_P(val) == IS_UNDEF)
5252+
&& (opline->extended_value & ZEND_JMP_NULL_BP_VAR_IS) == 0
5253+
) {
52505254
SAVE_OPLINE();
52515255
ZVAL_UNDEFINED_OP1();
52525256
if (UNEXPECTED(EG(exception) != NULL)) {
52535257
HANDLE_EXCEPTION();
52545258
}
52555259
}
5256-
} else if (opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) {
5260+
} else if (short_circuiting_type == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) {
52575261
ZVAL_FALSE(result);
52585262
} else {
5259-
ZEND_ASSERT(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY);
5263+
ZEND_ASSERT(short_circuiting_type == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY);
52605264
ZVAL_TRUE(result);
52615265
}
52625266

@@ -19344,19 +19348,23 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMP_NULL_SPEC_TMP_
1934419348
}
1934519349

1934619350
result = EX_VAR(opline->result.var);
19347-
if (EXPECTED(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EXPR)) {
19351+
uint32_t short_circuiting_type = opline->extended_value & ZEND_SHORT_CIRCUITING_CHAIN_MASK;
19352+
if (EXPECTED(short_circuiting_type == ZEND_SHORT_CIRCUITING_CHAIN_EXPR)) {
1934819353
ZVAL_NULL(result);
19349-
if (IS_TMP_VAR == IS_CV && UNEXPECTED(Z_TYPE_P(val) == IS_UNDEF)) {
19354+
if (IS_TMP_VAR == IS_CV
19355+
&& UNEXPECTED(Z_TYPE_P(val) == IS_UNDEF)
19356+
&& (opline->extended_value & ZEND_JMP_NULL_BP_VAR_IS) == 0
19357+
) {
1935019358
SAVE_OPLINE();
1935119359
ZVAL_UNDEFINED_OP1();
1935219360
if (UNEXPECTED(EG(exception) != NULL)) {
1935319361
HANDLE_EXCEPTION();
1935419362
}
1935519363
}
19356-
} else if (opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) {
19364+
} else if (short_circuiting_type == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) {
1935719365
ZVAL_FALSE(result);
1935819366
} else {
19359-
ZEND_ASSERT(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY);
19367+
ZEND_ASSERT(short_circuiting_type == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY);
1936019368
ZVAL_TRUE(result);
1936119369
}
1936219370

@@ -22267,19 +22275,23 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMP_NULL_SPEC_VAR_
2226722275
}
2226822276

2226922277
result = EX_VAR(opline->result.var);
22270-
if (EXPECTED(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EXPR)) {
22278+
uint32_t short_circuiting_type = opline->extended_value & ZEND_SHORT_CIRCUITING_CHAIN_MASK;
22279+
if (EXPECTED(short_circuiting_type == ZEND_SHORT_CIRCUITING_CHAIN_EXPR)) {
2227122280
ZVAL_NULL(result);
22272-
if (IS_VAR == IS_CV && UNEXPECTED(Z_TYPE_P(val) == IS_UNDEF)) {
22281+
if (IS_VAR == IS_CV
22282+
&& UNEXPECTED(Z_TYPE_P(val) == IS_UNDEF)
22283+
&& (opline->extended_value & ZEND_JMP_NULL_BP_VAR_IS) == 0
22284+
) {
2227322285
SAVE_OPLINE();
2227422286
ZVAL_UNDEFINED_OP1();
2227522287
if (UNEXPECTED(EG(exception) != NULL)) {
2227622288
HANDLE_EXCEPTION();
2227722289
}
2227822290
}
22279-
} else if (opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) {
22291+
} else if (short_circuiting_type == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) {
2228022292
ZVAL_FALSE(result);
2228122293
} else {
22282-
ZEND_ASSERT(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY);
22294+
ZEND_ASSERT(short_circuiting_type == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY);
2228322295
ZVAL_TRUE(result);
2228422296
}
2228522297

@@ -39116,19 +39128,23 @@ static ZEND_VM_HOT ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_JMP_NULL_SPEC_CV_H
3911639128
}
3911739129

3911839130
result = EX_VAR(opline->result.var);
39119-
if (EXPECTED(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EXPR)) {
39131+
uint32_t short_circuiting_type = opline->extended_value & ZEND_SHORT_CIRCUITING_CHAIN_MASK;
39132+
if (EXPECTED(short_circuiting_type == ZEND_SHORT_CIRCUITING_CHAIN_EXPR)) {
3912039133
ZVAL_NULL(result);
39121-
if (IS_CV == IS_CV && UNEXPECTED(Z_TYPE_P(val) == IS_UNDEF)) {
39134+
if (IS_CV == IS_CV
39135+
&& UNEXPECTED(Z_TYPE_P(val) == IS_UNDEF)
39136+
&& (opline->extended_value & ZEND_JMP_NULL_BP_VAR_IS) == 0
39137+
) {
3912239138
SAVE_OPLINE();
3912339139
ZVAL_UNDEFINED_OP1();
3912439140
if (UNEXPECTED(EG(exception) != NULL)) {
3912539141
HANDLE_EXCEPTION();
3912639142
}
3912739143
}
39128-
} else if (opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) {
39144+
} else if (short_circuiting_type == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) {
3912939145
ZVAL_FALSE(result);
3913039146
} else {
39131-
ZEND_ASSERT(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY);
39147+
ZEND_ASSERT(short_circuiting_type == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY);
3913239148
ZVAL_TRUE(result);
3913339149
}
3913439150

0 commit comments

Comments
 (0)