Skip to content

Commit 07d308a

Browse files
committed
Tweak lineno compilation
Fixes GH-18985 Closes GH-22833 zend_compile_expr() and zend_compile_var() now restore CG(zend_lineno) after compilation of the node has finished. This makes the compiled lineno more predictable. Note that there are many cases left that are incorrect. For example: <?php return foo(); L0004 0000 INIT_FCALL_BY_NAME 0 string("foo") L0004 0001 T0 = DO_FCALL_BY_NAME L0004 0002 RETURN T0 L0007 0003 RETURN int(1) The zend_ast_create_n() functions set lineno to the lineno of the first non-null child. In this case that's foo(), even though it's not on the same line as return. What we'd need instead is backing up CG(zend_lineno) after the return keyword in the parser so we can restore it later. However, this would be a rather invasive change, given the same issue exists for many ast kinds, so a better solution might just be to re-commit e528762. I don't feel comfortable backporting this as a bug fix, but we might address the issue in a simpler, less comprehensive manner for older branches.
1 parent 1da60a2 commit 07d308a

7 files changed

Lines changed: 79 additions & 16 deletions

File tree

NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ PHP NEWS
66
. Implemented partial function application RFC. (Arnaud)
77
. Fixed bug GH-22263 (reset typed property default on every unserialize
88
failure path). (David Carlier)
9+
. Fixed bug GH-18985 (Wrong line numbers for match with constant arms).
10+
(ilutov)
911

1012
- DOM:
1113
. Fixed bug GH-22825 (DOMElement::setAttribute() fails silently when the DTD

Zend/Optimizer/zend_optimizer.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1052,7 +1052,10 @@ static void zend_optimize(zend_op_array *op_array,
10521052
}
10531053

10541054
if (ctx->debug_level & ZEND_DUMP_BEFORE_OPTIMIZER) {
1055-
zend_dump_op_array(op_array, ZEND_DUMP_LIVE_RANGES, "before optimizer", NULL);
1055+
uint32_t additional_dump_flags = (ctx->debug_level & ZEND_DUMP_LINE_NUMBERS_PASSTHRU)
1056+
? ZEND_DUMP_LINE_NUMBERS
1057+
: 0;
1058+
zend_dump_op_array(op_array, ZEND_DUMP_LIVE_RANGES|additional_dump_flags, "before optimizer", NULL);
10561059
}
10571060

10581061
/* pass 1 (Simple local optimizations)

Zend/Optimizer/zend_optimizer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
#define ZEND_DUMP_DFA_SSA (1<<27)
8181
#define ZEND_DUMP_DFA_SSA_VARS (1<<28)
8282
#define ZEND_DUMP_SCCP (1<<29)
83+
#define ZEND_DUMP_LINE_NUMBERS_PASSTHRU (1<<30)
8384

8485
typedef struct _zend_script {
8586
zend_string *filename;

Zend/zend_compile.c

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7125,6 +7125,8 @@ static void zend_compile_match(znode *result, zend_ast *ast)
71257125
zend_ast *arm_ast = arms->child[i];
71267126
zend_ast *body_ast = arm_ast->child[1];
71277127

7128+
CG(zend_lineno) = zend_ast_get_lineno(arm_ast);
7129+
71287130
if (arm_ast->child[0] != NULL) {
71297131
zend_ast_list *conds = zend_ast_get_list(arm_ast->child[0]);
71307132

@@ -10728,6 +10730,8 @@ static void zend_compile_binary_op(znode *result, zend_ast *ast) /* {{{ */
1072810730
zend_compile_expr(&left_node, left_ast);
1072910731
zend_compile_expr(&right_node, right_ast);
1073010732

10733+
CG(zend_lineno) = ast->lineno;
10734+
1073110735
if (left_node.op_type == IS_CONST && right_node.op_type == IS_CONST) {
1073210736
if (zend_try_ct_eval_binary_op(&result->u.constant, opcode,
1073310737
&left_node.u.constant, &right_node.u.constant)
@@ -12335,9 +12339,6 @@ static void zend_compile_stmt(zend_ast *ast) /* {{{ */
1233512339

1233612340
static void zend_compile_expr_inner(znode *result, zend_ast *ast) /* {{{ */
1233712341
{
12338-
/* CG(zend_lineno) = ast->lineno; */
12339-
CG(zend_lineno) = zend_ast_get_lineno(ast);
12340-
1234112342
if (CG(memoize_mode) != ZEND_MEMOIZE_NONE) {
1234212343
zend_compile_memoized_expr(result, ast, BP_VAR_R);
1234312344
return;
@@ -12479,6 +12480,9 @@ static void zend_compile_expr(znode *result, zend_ast *ast)
1247912480
{
1248012481
zend_check_stack_limit();
1248112482

12483+
uint32_t prev_lineno = CG(zend_lineno);
12484+
CG(zend_lineno) = zend_ast_get_lineno(ast);
12485+
1248212486
uint32_t checkpoint = zend_short_circuiting_checkpoint();
1248312487
zend_compile_expr_inner(result, ast);
1248412488
zend_short_circuiting_commit(checkpoint, result, ast);
@@ -12488,12 +12492,12 @@ static void zend_compile_expr(znode *result, zend_ast *ast)
1248812492
ZEND_ASSERT(result->op_type != IS_VAR);
1248912493
}
1249012494
#endif
12495+
12496+
CG(zend_lineno) = prev_lineno;
1249112497
}
1249212498

1249312499
static zend_op *zend_compile_var_inner(znode *result, zend_ast *ast, uint32_t type, bool by_ref)
1249412500
{
12495-
CG(zend_lineno) = zend_ast_get_lineno(ast);
12496-
1249712501
if (CG(memoize_mode) != ZEND_MEMOIZE_NONE) {
1249812502
switch (ast->kind) {
1249912503
case ZEND_AST_CALL:
@@ -12554,6 +12558,9 @@ static zend_op *zend_compile_var(znode *result, zend_ast *ast, uint32_t type, bo
1255412558
{
1255512559
zend_check_stack_limit();
1255612560

12561+
uint32_t prev_lineno = CG(zend_lineno);
12562+
CG(zend_lineno) = zend_ast_get_lineno(ast);
12563+
1255712564
uint32_t checkpoint = zend_short_circuiting_checkpoint();
1255812565
zend_op *opcode = zend_compile_var_inner(result, ast, type, by_ref);
1255912566
zend_short_circuiting_commit(checkpoint, result, ast);
@@ -12567,32 +12574,47 @@ static zend_op *zend_compile_var(znode *result, zend_ast *ast, uint32_t type, bo
1256712574
ZEND_ASSERT(result->op_type != IS_VAR);
1256812575
}
1256912576
#endif
12577+
12578+
CG(zend_lineno) = prev_lineno;
12579+
1257012580
return opcode;
1257112581
}
1257212582

1257312583
static zend_op *zend_delayed_compile_var(znode *result, zend_ast *ast, uint32_t type, bool by_ref) /* {{{ */
1257412584
{
1257512585
zend_check_stack_limit();
1257612586

12587+
uint32_t prev_lineno = CG(zend_lineno);
12588+
CG(zend_lineno) = zend_ast_get_lineno(ast);
12589+
12590+
zend_op *opline;
1257712591
switch (ast->kind) {
1257812592
case ZEND_AST_VAR:
12579-
return zend_compile_simple_var(result, ast, type, true);
12593+
opline = zend_compile_simple_var(result, ast, type, true);
12594+
break;
1258012595
case ZEND_AST_DIM:
12581-
return zend_delayed_compile_dim(result, ast, type, by_ref);
12596+
opline = zend_delayed_compile_dim(result, ast, type, by_ref);
12597+
break;
1258212598
case ZEND_AST_PROP:
1258312599
case ZEND_AST_NULLSAFE_PROP:
1258412600
{
12585-
zend_op *opline = zend_delayed_compile_prop(result, ast, type);
12601+
opline = zend_delayed_compile_prop(result, ast, type);
1258612602
if (by_ref) {
1258712603
opline->extended_value |= ZEND_FETCH_REF;
1258812604
}
12589-
return opline;
12605+
break;
1259012606
}
1259112607
case ZEND_AST_STATIC_PROP:
12592-
return zend_compile_static_prop(result, ast, type, by_ref, true);
12608+
opline = zend_compile_static_prop(result, ast, type, by_ref, true);
12609+
break;
1259312610
default:
12594-
return zend_compile_var(result, ast, type, false);
12611+
opline = zend_compile_var(result, ast, type, false);
12612+
break;
1259512613
}
12614+
12615+
CG(zend_lineno) = prev_lineno;
12616+
12617+
return opline;
1259612618
}
1259712619
/* }}} */
1259812620

ext/opcache/tests/gh18985.phpt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
--TEST--
2+
GH-18985: Wrong lineno for multiline expressions
3+
--EXTENSIONS--
4+
opcache
5+
--INI--
6+
opcache.enable_cli=1
7+
opcache.opt_debug_level=0x40010000
8+
--FILE--
9+
<?php
10+
11+
echo match(15) {
12+
13 => "A",
13+
15 => "B",
14+
default => "C",
15+
};
16+
17+
?>
18+
--EXPECTF--
19+
$_main:
20+
; (lines=9, args=0, vars=0, tmps=%s)
21+
; (before optimizer)
22+
; %sgh18985.php:1-10
23+
; return [] RANGE[0..0]
24+
L0003 0000 MATCH int(15) 13: 0001, 15: 0003, default: 0005
25+
L0004 0001 T1 = QM_ASSIGN string("A")
26+
L0004 0002 JMP 0007
27+
L0005 0003 T1 = QM_ASSIGN string("B")
28+
L0005 0004 JMP 0007
29+
L0006 0005 T1 = QM_ASSIGN string("C")
30+
L0006 0006 JMP 0007
31+
L0003 0007 ECHO T1
32+
L0010 0008 RETURN int(1)
33+
LIVE RANGES:
34+
1: 0006 - 0007 (tmp/var)
35+
B

ext/opcache/tests/jit/shift_right_004.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ Warning: Undefined array key 0 in %sshift_right_004.php on line 7
3030

3131
Deprecated: Implicit conversion from float %f to int loses precision in %sshift_right_004.php on line 8
3232

33-
Warning: A non-numeric value encountered in %sshift_right_004.php on line 7
33+
Warning: A non-numeric value encountered in %sshift_right_004.php on line 6
3434

35-
Warning: A non-numeric value encountered in %sshift_right_004.php on line 7
35+
Warning: A non-numeric value encountered in %sshift_right_004.php on line 6
3636

3737
Fatal error: Uncaught ArithmeticError: Bit shift by negative number in %sshift_right_004.php:8
3838
Stack trace:

ext/opcache/tests/jit/switch_001.phpt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ foo();
1616
?>
1717
DONE
1818
--EXPECTF--
19-
Warning: Undefined variable $y in %sswitch_001.php on line 4
19+
Warning: Undefined variable $y in %sswitch_001.php on line 3
2020

21-
Warning: Undefined variable $y in %sswitch_001.php on line 5
21+
Warning: Undefined variable $y in %sswitch_001.php on line 3
2222
DONE

0 commit comments

Comments
 (0)