Skip to content

Commit 5a855ee

Browse files
committed
Fix GH-8661: Nullsafe in coalesce triggers undefined variable warning
Closes GH-8690
1 parent 880803a commit 5a855ee

File tree

8 files changed

+70
-28
lines changed

8 files changed

+70
-28
lines changed

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ PHP NEWS
55
- Core:
66
. Fixed bug GH-8655 (Casting an object to array does not unwrap refcount=1
77
references). (Nicolas Grekas)
8+
. Fixed bug GH-8661 (Nullsafe in coalesce triggers undefined variable
9+
warning). (ilutov)
810

911
- MBString:
1012
. Backwards-compatible mappings for 0x5C/0x7E in Shift-JIS are restored,

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
@@ -2457,16 +2457,19 @@ static zend_always_inline zend_result _zend_update_type_info(
24572457
COPY_SSA_OBJ_TYPE(ssa_op->op1_use, ssa_op->result_def);
24582458
break;
24592459
case ZEND_JMP_NULL:
2460-
if (opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EXPR) {
2460+
{
2461+
uint32_t short_circuiting_type = opline->extended_value & ZEND_SHORT_CIRCUITING_CHAIN_MASK;
2462+
if (short_circuiting_type == ZEND_SHORT_CIRCUITING_CHAIN_EXPR) {
24612463
tmp = MAY_BE_NULL;
2462-
} else if (opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) {
2464+
} else if (short_circuiting_type == ZEND_SHORT_CIRCUITING_CHAIN_ISSET) {
24632465
tmp = MAY_BE_FALSE;
24642466
} else {
2465-
ZEND_ASSERT(opline->extended_value == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY);
2467+
ZEND_ASSERT(short_circuiting_type == ZEND_SHORT_CIRCUITING_CHAIN_EMPTY);
24662468
tmp = MAY_BE_TRUE;
24672469
}
24682470
UPDATE_SSA_TYPE(tmp, ssa_op->result_def);
24692471
break;
2472+
}
24702473
case ZEND_ASSIGN_OP:
24712474
case ZEND_ASSIGN_DIM_OP:
24722475
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
@@ -2261,21 +2261,24 @@ static void zend_short_circuiting_commit(uint32_t checkpoint, znode *result, zen
22612261
zend_op *opline = &CG(active_op_array)->opcodes[opnum];
22622262
opline->op2.opline_num = get_next_op_number();
22632263
SET_NODE(opline->result, result);
2264-
opline->extended_value =
2264+
opline->extended_value |=
22652265
ast->kind == ZEND_AST_ISSET ? ZEND_SHORT_CIRCUITING_CHAIN_ISSET :
22662266
ast->kind == ZEND_AST_EMPTY ? ZEND_SHORT_CIRCUITING_CHAIN_EMPTY :
22672267
ZEND_SHORT_CIRCUITING_CHAIN_EXPR;
22682268
zend_stack_del_top(&CG(short_circuiting_opnums));
22692269
}
22702270
}
22712271

2272-
static void zend_emit_jmp_null(znode *obj_node)
2272+
static void zend_emit_jmp_null(znode *obj_node, uint32_t bp_type)
22732273
{
22742274
uint32_t jmp_null_opnum = get_next_op_number();
22752275
zend_op *opline = zend_emit_op(NULL, ZEND_JMP_NULL, obj_node, NULL);
22762276
if (opline->op1_type == IS_CONST) {
22772277
Z_TRY_ADDREF_P(CT_CONSTANT(opline->op1));
22782278
}
2279+
if (bp_type == BP_VAR_IS) {
2280+
opline->extended_value |= ZEND_JMP_NULL_BP_VAR_IS;
2281+
}
22792282
zend_stack_push(&CG(short_circuiting_opnums), &jmp_null_opnum);
22802283
}
22812284

@@ -2850,7 +2853,7 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t
28502853
}
28512854
}
28522855
}
2853-
zend_emit_jmp_null(&obj_node);
2856+
zend_emit_jmp_null(&obj_node, type);
28542857
}
28552858
}
28562859

@@ -4461,7 +4464,7 @@ static void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type
44614464
zend_short_circuiting_mark_inner(obj_ast);
44624465
zend_compile_expr(&obj_node, obj_ast);
44634466
if (nullsafe) {
4464-
zend_emit_jmp_null(&obj_node);
4467+
zend_emit_jmp_null(&obj_node, type);
44654468
}
44664469
}
44674470

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
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)