Skip to content

Commit a40714f

Browse files
committed
Implement isset/empty for ?-> (incomplete)
1 parent ad7e47c commit a40714f

File tree

3 files changed

+92
-34
lines changed

3 files changed

+92
-34
lines changed
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
--TEST--
2+
Test isset and empty on nullsafe property
3+
--FILE--
4+
<?php
5+
6+
class Foo {
7+
public $bar;
8+
}
9+
10+
// FIXME: These should not emit an "Undefined variable" warning
11+
var_dump(isset($foo?->bar));
12+
var_dump(empty($foo?->bar));
13+
14+
$foo = null;
15+
var_dump(isset($foo?->bar));
16+
var_dump(empty($foo?->bar));
17+
18+
$foo = new Foo();
19+
var_dump(isset($foo?->bar));
20+
var_dump(empty($foo?->bar));
21+
22+
$foo->bar = 'bar';
23+
var_dump(isset($foo?->bar));
24+
var_dump(empty($foo?->bar));
25+
26+
--EXPECTF--
27+
Warning: Undefined variable $foo in %s011.php on line 8
28+
NULL
29+
30+
Warning: Undefined variable $foo in %s011.php on line 9
31+
NULL
32+
NULL
33+
NULL
34+
bool(false)
35+
bool(true)
36+
bool(true)
37+
bool(false)

Zend/zend_compile.c

Lines changed: 54 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ typedef struct _zend_loop_var {
5959
uint32_t try_catch_offset;
6060
} zend_loop_var;
6161

62+
#define ZEND_SHORT_CIRCUITING_SCOPE_ISSET (1 << 0)
63+
#define ZEND_SHORT_CIRCUITING_SCOPE_EMPTY (1 << 1)
64+
65+
typedef struct _zend_short_circuiting_scope {
66+
zend_stack jump_opnums;
67+
zend_bool skip;
68+
} zend_short_circuiting_scope;
69+
6270
static inline uint32_t zend_alloc_cache_slots(unsigned count) {
6371
if (count == 0) {
6472
return (uint32_t) -1;
@@ -368,7 +376,7 @@ void zend_init_compiler_data_structures(void) /* {{{ */
368376
{
369377
zend_stack_init(&CG(loop_var_stack), sizeof(zend_loop_var));
370378
zend_stack_init(&CG(delayed_oplines_stack), sizeof(zend_op));
371-
zend_stack_init(&CG(short_circuiting_labels), sizeof(zend_stack*));
379+
zend_stack_init(&CG(short_circuiting_scopes), sizeof(zend_short_circuiting_scope));
372380
CG(active_class_entry) = NULL;
373381
CG(in_compilation) = 0;
374382
CG(skip_shebang) = 0;
@@ -422,7 +430,7 @@ void shutdown_compiler(void) /* {{{ */
422430
{
423431
zend_stack_destroy(&CG(loop_var_stack));
424432
zend_stack_destroy(&CG(delayed_oplines_stack));
425-
zend_stack_destroy(&CG(short_circuiting_labels));
433+
zend_stack_destroy(&CG(short_circuiting_scopes));
426434
zend_hash_destroy(&CG(filenames_table));
427435
zend_arena_destroy(CG(arena));
428436

@@ -2225,23 +2233,30 @@ static zend_op *zend_delayed_compile_end(uint32_t offset) /* {{{ */
22252233
}
22262234
/* }}} */
22272235

2228-
zend_bool zend_should_start_short_circuiting()
2236+
static zend_bool zend_should_begin_short_circuiting_scope()
22292237
{
22302238
return !CG(short_circuiting);
22312239
}
22322240

2233-
void zend_start_short_circuiting()
2241+
static zend_short_circuiting_scope *zend_current_short_circuiting_scope()
2242+
{
2243+
return zend_stack_top(&CG(short_circuiting_scopes));
2244+
}
2245+
2246+
static void zend_begin_short_circuiting_scope()
22342247
{
22352248
CG(short_circuiting) = 1;
22362249

2237-
zend_stack *labels = emalloc(sizeof(zend_stack));
2238-
zend_stack_init(labels, sizeof(uint32_t));
2239-
zend_stack_push(&CG(short_circuiting_labels), &labels);
2250+
zend_short_circuiting_scope scope;
2251+
zend_stack_init(&scope.jump_opnums, sizeof(uint32_t));
2252+
scope.skip = 0;
2253+
zend_stack_push(&CG(short_circuiting_scopes), &scope);
22402254
}
22412255

2242-
void zend_end_short_circuiting(znode *result)
2256+
static void zend_end_short_circuiting_scope(znode *result)
22432257
{
2244-
zend_stack *labels = *(zend_stack **)zend_stack_top(&CG(short_circuiting_labels));
2258+
zend_short_circuiting_scope *scope = zend_current_short_circuiting_scope();
2259+
zend_stack *labels = &scope->jump_opnums;
22452260
zend_bool contains_short_circuiting_operators = !zend_stack_is_empty(labels);
22462261

22472262
uint32_t end_label = 0;
@@ -2266,9 +2281,8 @@ void zend_end_short_circuiting(znode *result)
22662281

22672282
CG(short_circuiting) = 0;
22682283

2269-
zend_stack_del_top(&CG(short_circuiting_labels));
2284+
zend_stack_del_top(&CG(short_circuiting_scopes));
22702285
zend_stack_destroy(labels);
2271-
efree(labels);
22722286
}
22732287

22742288
#define ZEND_MEMOIZE_NONE 0
@@ -2759,9 +2773,10 @@ static zend_op *zend_delayed_compile_prop(znode *result, zend_ast *ast, uint32_t
27592773

27602774
uint32_t jmp_call = zend_emit_cond_jump(ZEND_JMPZ, &typecheck_node, 0);
27612775

2762-
zend_stack *short_circuiting_labels = *(zend_stack **)zend_stack_top(&CG(short_circuiting_labels));
2776+
zend_short_circuiting_scope *short_circuiting_scope = zend_current_short_circuiting_scope();
2777+
zend_stack *short_circuiting_scopes = &short_circuiting_scope->jump_opnums;
27632778
uint32_t jmp_end = zend_emit_jump(0);
2764-
zend_stack_push(short_circuiting_labels, &jmp_end);
2779+
zend_stack_push(short_circuiting_scopes, &jmp_end);
27652780
zend_update_jump_target_to_next(jmp_call);
27662781
}
27672782

@@ -3010,9 +3025,9 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
30103025
zend_op *opline;
30113026
uint32_t offset;
30123027

3013-
zend_bool start_short_circuiting = zend_should_start_short_circuiting();
3028+
zend_bool start_short_circuiting = zend_should_begin_short_circuiting_scope();
30143029
if (start_short_circuiting) {
3015-
zend_start_short_circuiting();
3030+
zend_begin_short_circuiting_scope();
30163031
}
30173032

30183033
if (is_this_fetch(var_ast)) {
@@ -3123,7 +3138,7 @@ void zend_compile_assign(znode *result, zend_ast *ast) /* {{{ */
31233138
}
31243139

31253140
if (start_short_circuiting) {
3126-
zend_end_short_circuiting(result);
3141+
zend_end_short_circuiting_scope(result);
31273142
}
31283143
}
31293144
/* }}} */
@@ -3202,7 +3217,7 @@ void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */
32023217
zend_ast *var_ast = ast->child[0];
32033218
zend_ast *expr_ast = ast->child[1];
32043219
uint32_t opcode = ast->attr;
3205-
zend_bool start_short_circuiting = zend_should_start_short_circuiting();
3220+
zend_bool start_short_circuiting = zend_should_begin_short_circuiting_scope();
32063221

32073222
znode var_node, expr_node;
32083223
zend_op *opline;
@@ -3211,7 +3226,7 @@ void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */
32113226
zend_ensure_writable_variable(var_ast);
32123227

32133228
if (start_short_circuiting) {
3214-
zend_start_short_circuiting();
3229+
zend_begin_short_circuiting_scope();
32153230
}
32163231

32173232
switch (var_ast->kind) {
@@ -3271,7 +3286,7 @@ void zend_compile_compound_assign(znode *result, zend_ast *ast) /* {{{ */
32713286
}
32723287

32733288
if (start_short_circuiting) {
3274-
zend_end_short_circuiting(result);
3289+
zend_end_short_circuiting_scope(result);
32753290
}
32763291
}
32773292
/* }}} */
@@ -4240,9 +4255,10 @@ void zend_compile_method_call(znode *result, zend_ast *ast, uint32_t type) /* {{
42404255

42414256
uint32_t jmp_call = zend_emit_cond_jump(ZEND_JMPZ, &typecheck_node, 0);
42424257

4243-
zend_stack *short_circuiting_labels = *(zend_stack **)zend_stack_top(&CG(short_circuiting_labels));
4258+
zend_short_circuiting_scope *short_circuiting_scope = zend_current_short_circuiting_scope();
4259+
zend_stack *short_circuiting_scopes = &short_circuiting_scope->jump_opnums;
42444260
uint32_t jmp_end = zend_emit_jump(0);
4245-
zend_stack_push(short_circuiting_labels, &jmp_end);
4261+
zend_stack_push(short_circuiting_scopes, &jmp_end);
42464262
zend_update_jump_target_to_next(jmp_call);
42474263
}
42484264

@@ -7797,9 +7813,9 @@ void zend_compile_post_incdec(znode *result, zend_ast *ast) /* {{{ */
77977813

77987814
zend_ensure_writable_variable(var_ast);
77997815

7800-
zend_bool start_short_circuiting = zend_should_start_short_circuiting();
7816+
zend_bool start_short_circuiting = zend_should_begin_short_circuiting_scope();
78017817
if (start_short_circuiting) {
7802-
zend_start_short_circuiting();
7818+
zend_begin_short_circuiting_scope();
78037819
}
78047820

78057821
if (var_ast->kind == ZEND_AST_PROP || var_ast->kind == ZEND_AST_NULLSAFE_PROP) {
@@ -7818,7 +7834,7 @@ void zend_compile_post_incdec(znode *result, zend_ast *ast) /* {{{ */
78187834
}
78197835

78207836
if (start_short_circuiting) {
7821-
zend_end_short_circuiting(result);
7837+
zend_end_short_circuiting_scope(result);
78227838
}
78237839
}
78247840
/* }}} */
@@ -7830,9 +7846,9 @@ void zend_compile_pre_incdec(znode *result, zend_ast *ast) /* {{{ */
78307846

78317847
zend_ensure_writable_variable(var_ast);
78327848

7833-
zend_bool start_short_circuiting = zend_should_start_short_circuiting();
7849+
zend_bool start_short_circuiting = zend_should_begin_short_circuiting_scope();
78347850
if (start_short_circuiting) {
7835-
zend_start_short_circuiting();
7851+
zend_begin_short_circuiting_scope();
78367852
}
78377853

78387854
if (var_ast->kind == ZEND_AST_PROP || var_ast->kind == ZEND_AST_NULLSAFE_PROP) {
@@ -7853,7 +7869,7 @@ void zend_compile_pre_incdec(znode *result, zend_ast *ast) /* {{{ */
78537869
}
78547870

78557871
if (start_short_circuiting) {
7856-
zend_end_short_circuiting(result);
7872+
zend_end_short_circuiting_scope(result);
78577873
}
78587874
}
78597875
/* }}} */
@@ -8003,10 +8019,10 @@ void zend_compile_assign_coalesce(znode *result, zend_ast *ast) /* {{{ */
80038019
zend_op *opline;
80048020
uint32_t coalesce_opnum;
80058021
zend_bool need_frees = 0;
8006-
zend_bool start_short_circuiting = zend_should_start_short_circuiting();
8022+
zend_bool start_short_circuiting = zend_should_begin_short_circuiting_scope();
80078023

80088024
if (start_short_circuiting) {
8009-
zend_start_short_circuiting();
8025+
zend_begin_short_circuiting_scope();
80108026
}
80118027

80128028
/* Remember expressions compiled during the initial BP_VAR_IS lookup,
@@ -8097,7 +8113,7 @@ void zend_compile_assign_coalesce(znode *result, zend_ast *ast) /* {{{ */
80978113
CG(memoize_mode) = orig_memoize_mode;
80988114

80998115
if (start_short_circuiting) {
8100-
zend_end_short_circuiting(result);
8116+
zend_end_short_circuiting_scope(result);
81018117
}
81028118
}
81038119
/* }}} */
@@ -8243,6 +8259,9 @@ void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */
82438259

82448260
ZEND_ASSERT(ast->kind == ZEND_AST_ISSET || ast->kind == ZEND_AST_EMPTY);
82458261

8262+
zend_begin_short_circuiting_scope();
8263+
zend_current_short_circuiting_scope()->skip = 1;
8264+
82468265
if (!zend_is_variable(var_ast)) {
82478266
if (ast->kind == ZEND_AST_EMPTY) {
82488267
/* empty(expr) can be transformed to !expr */
@@ -8288,6 +8307,8 @@ void zend_compile_isset_or_empty(znode *result, zend_ast *ast) /* {{{ */
82888307
if (!(ast->kind == ZEND_AST_ISSET)) {
82898308
opline->extended_value |= ZEND_ISEMPTY;
82908309
}
8310+
8311+
zend_end_short_circuiting_scope(result);
82918312
}
82928313
/* }}} */
82938314

@@ -9187,16 +9208,16 @@ zend_op *zend_compile_var_inner(znode *result, zend_ast *ast, uint32_t type, int
91879208

91889209
zend_op *zend_compile_var(znode *result, zend_ast *ast, uint32_t type, int by_ref, zend_bool allow_starting_short_circuiting) /* {{{ */
91899210
{
9190-
zend_bool start_short_circuiting = allow_starting_short_circuiting && zend_should_start_short_circuiting();
9211+
zend_bool start_short_circuiting = allow_starting_short_circuiting && zend_should_begin_short_circuiting_scope();
91919212

91929213
if (start_short_circuiting) {
9193-
zend_start_short_circuiting();
9214+
zend_begin_short_circuiting_scope();
91949215
}
91959216

91969217
zend_op *opcode = zend_compile_var_inner(result, ast, type, by_ref);
91979218

91989219
if (start_short_circuiting) {
9199-
zend_end_short_circuiting(result);
9220+
zend_end_short_circuiting_scope(result);
92009221
}
92019222

92029223
return opcode;

Zend/zend_globals.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ struct _zend_compiler_globals {
131131
uint32_t rtd_key_counter;
132132

133133
zend_bool short_circuiting;
134-
zend_stack short_circuiting_labels;
134+
zend_stack short_circuiting_scopes;
135135
};
136136

137137

0 commit comments

Comments
 (0)