Skip to content

Commit 36cc5a4

Browse files
committed
initial import
1 parent d7f5f60 commit 36cc5a4

9 files changed

+8282
-7877
lines changed

Zend/zend_API.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2103,6 +2103,10 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio
21032103
internal_function->num_args = 0;
21042104
internal_function->required_num_args = 0;
21052105
}
2106+
2107+
/* Temporarily, don't use this */
2108+
internal_function->return_hint.used = 0;
2109+
21062110
if (ptr->flags & ZEND_ACC_ABSTRACT) {
21072111
if (scope) {
21082112
/* This is a class that must be abstract itself. Here we set the check info. */

Zend/zend_compile.c

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1862,6 +1862,31 @@ void zend_do_end_function_declaration(const znode *function_token TSRMLS_DC) /*
18621862
}
18631863
/* }}} */
18641864

1865+
/* {{{ */
1866+
void zend_do_function_return_hint(const znode *return_hint, zend_bool allow_null TSRMLS_DC) {
1867+
if (return_hint->op_type != IS_UNUSED) {
1868+
CG(active_op_array)->return_hint.used = 1;
1869+
CG(active_op_array)->return_hint.allow_null = allow_null;
1870+
1871+
if (return_hint->op_type == IS_CONST) {
1872+
if (Z_TYPE(return_hint->u.constant) == IS_STRING) {
1873+
CG(active_op_array)->return_hint.type = IS_OBJECT;
1874+
CG(active_op_array)->return_hint.class_name_len = Z_STRLEN(return_hint->u.constant);
1875+
CG(active_op_array)->return_hint.class_name = zend_new_interned_string
1876+
(Z_STRVAL(return_hint->u.constant), Z_STRLEN(return_hint->u.constant)+1, 1 TSRMLS_CC);
1877+
} else {
1878+
CG(active_op_array)->return_hint.type = Z_TYPE(return_hint->u.constant);
1879+
}
1880+
} else {
1881+
printf("non constant return hint\n");
1882+
CG(active_op_array)->return_hint.type = IS_OBJECT;
1883+
CG(active_op_array)->return_hint.class_name_len = Z_STRLEN(return_hint->u.constant);
1884+
CG(active_op_array)->return_hint.class_name = zend_new_interned_string
1885+
(Z_STRVAL(return_hint->u.constant), Z_STRLEN(return_hint->u.constant)+1, 1 TSRMLS_CC);
1886+
}
1887+
}
1888+
} /* }}} */
1889+
18651890
void zend_do_receive_param(zend_uchar op, znode *varname, const znode *initialization, znode *class_type, zend_uchar pass_by_reference, zend_bool is_variadic TSRMLS_DC) /* {{{ */
18661891
{
18671892
zend_op *opline;
@@ -2806,6 +2831,20 @@ void zend_do_return(znode *expr, int do_end_vparse TSRMLS_DC) /* {{{ */
28062831

28072832
start_op_number = get_next_op_number(CG(active_op_array));
28082833

2834+
if (CG(active_op_array)->return_hint.used && expr) {
2835+
if (expr->op_type == IS_UNUSED && !CG(active_op_array)->return_hint.allow_null) {
2836+
zend_error(E_COMPILE_ERROR, "return hint error in %s", CG(active_op_array)->function_name);
2837+
} else {
2838+
if (expr->op_type == IS_CONST) {
2839+
if (Z_TYPE(expr->u.constant) & ~IS_CONSTANT_TYPE_MASK != CG(active_op_array)->return_hint.type) {
2840+
zend_error(E_COMPILE_ERROR, "return type mismatch");
2841+
}
2842+
}
2843+
2844+
/* work out if we can do anything else */
2845+
}
2846+
}
2847+
28092848
#ifdef ZTS
28102849
zend_stack_apply_with_argument(&CG(switch_cond_stack), ZEND_STACK_APPLY_TOPDOWN, (int (*)(void *element, void *)) generate_free_switch_expr TSRMLS_CC);
28112850
zend_stack_apply_with_argument(&CG(foreach_copy_stack), ZEND_STACK_APPLY_TOPDOWN, (int (*)(void *element, void *)) generate_free_foreach_copy TSRMLS_CC);
@@ -3512,7 +3551,38 @@ static char * zend_get_function_declaration(zend_function *fptr TSRMLS_DC) /* {{
35123551
REALLOC_BUF_IF_EXCEED(buf, offset, length, 32);
35133552
}
35143553
}
3554+
35153555
*(offset++) = ')';
3556+
3557+
if (fptr->common.return_hint.used) {
3558+
REALLOC_BUF_IF_EXCEED(buf, offset, length, 4);
3559+
*(offset++) = ' ';
3560+
*(offset++) = ':';
3561+
*(offset++) = ' ';
3562+
3563+
if (fptr->common.return_hint.allow_null) {
3564+
*(offset++) = '?';
3565+
}
3566+
3567+
switch (fptr->common.return_hint.type) {
3568+
case IS_OBJECT:
3569+
REALLOC_BUF_IF_EXCEED(buf, offset, length, fptr->common.return_hint.class_name_len);
3570+
memcpy(offset, fptr->common.return_hint.class_name, fptr->common.return_hint.class_name_len);
3571+
offset += fptr->common.return_hint.class_name_len;
3572+
break;
3573+
3574+
default: {
3575+
char *type = zend_get_type_by_const(fptr->common.return_hint.type);
3576+
if (type) {
3577+
size_t type_len = strlen(type);
3578+
REALLOC_BUF_IF_EXCEED(buf, offset, length, type_len);
3579+
memcpy(offset, type, type_len);
3580+
offset += type_len;
3581+
}
3582+
}
3583+
}
3584+
}
3585+
35163586
*offset = '\0';
35173587

35183588
return buf;
@@ -3588,6 +3658,51 @@ static void do_inheritance_check_on_method(zend_function *child, zend_function *
35883658
efree(method_prototype);
35893659
}
35903660
}
3661+
3662+
if (parent->common.return_hint.used) {
3663+
3664+
if (!child->common.return_hint.used) {
3665+
char *method_prototype = zend_get_function_declaration(parent TSRMLS_CC);
3666+
zend_error(E_COMPILE_ERROR, "Delcaration of %s::%s should be compatible with %s, return type missing", ZEND_FN_SCOPE_NAME(child), child->common.function_name, method_prototype);
3667+
efree(method_prototype);
3668+
return;
3669+
}
3670+
3671+
if (child->common.return_hint.type != parent->common.return_hint.type) {
3672+
char *method_prototype = zend_get_function_declaration(parent TSRMLS_CC);
3673+
zend_error(E_COMPILE_ERROR, "Delcaration of %s::%s should be compatible with %s, return type mismatch", ZEND_FN_SCOPE_NAME(child), child->common.function_name, method_prototype);
3674+
efree(method_prototype);
3675+
return;
3676+
}
3677+
3678+
if (child->common.return_hint.type == IS_OBJECT) {
3679+
zend_class_entry **pce = NULL, **cce = NULL;
3680+
3681+
if (zend_lookup_class(child->common.return_hint.class_name, child->common.return_hint.class_name_len, &cce TSRMLS_CC) != SUCCESS) {
3682+
char *method_prototype = zend_get_function_declaration(parent TSRMLS_CC);
3683+
zend_error(E_COMPILE_ERROR, "Delcaration of %s::%s declares return type %s, which could not be found",
3684+
ZEND_FN_SCOPE_NAME(child), child->common.function_name, child->common.return_hint.class_name);
3685+
efree(method_prototype);
3686+
return;
3687+
}
3688+
3689+
if (zend_lookup_class(parent->common.return_hint.class_name, parent->common.return_hint.class_name_len, &pce TSRMLS_CC) != SUCCESS) {
3690+
char *method_prototype = zend_get_function_declaration(parent TSRMLS_CC);
3691+
zend_error(E_COMPILE_ERROR, "Delcaration of %s::%s declares return type %s, which could not be found",
3692+
ZEND_FN_SCOPE_NAME(parent), parent->common.function_name, parent->common.return_hint.class_name);
3693+
efree(method_prototype);
3694+
return;
3695+
}
3696+
3697+
if (!instanceof_function(*cce, *pce TSRMLS_CC)) {
3698+
char *method_prototype = zend_get_function_declaration(parent TSRMLS_CC);
3699+
zend_error(E_COMPILE_ERROR, "Delcaration of %s::%s should be compatible with %s, return type mismatch",
3700+
ZEND_FN_SCOPE_NAME(child), child->common.function_name, method_prototype);
3701+
efree(method_prototype);
3702+
return;
3703+
}
3704+
}
3705+
}
35913706
}
35923707
/* }}} */
35933708

Zend/zend_compile.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,14 @@ typedef struct _zend_compiled_variable {
264264
ulong hash_value;
265265
} zend_compiled_variable;
266266

267+
typedef struct _zend_return_hint {
268+
zend_uchar type;
269+
const char *class_name;
270+
zend_uint class_name_len;
271+
zend_bool allow_null;
272+
zend_bool used;
273+
} zend_return_hint;
274+
267275
struct _zend_op_array {
268276
/* Common elements */
269277
zend_uchar type;
@@ -274,8 +282,9 @@ struct _zend_op_array {
274282
zend_uint num_args;
275283
zend_uint required_num_args;
276284
zend_arg_info *arg_info;
285+
zend_return_hint return_hint;
277286
/* END of common elements */
278-
287+
279288
zend_uint *refcount;
280289

281290
zend_op *opcodes;
@@ -331,6 +340,7 @@ typedef struct _zend_internal_function {
331340
zend_uint num_args;
332341
zend_uint required_num_args;
333342
zend_arg_info *arg_info;
343+
zend_return_hint return_hint;
334344
/* END of common elements */
335345

336346
void (*handler)(INTERNAL_FUNCTION_PARAMETERS);
@@ -351,6 +361,7 @@ typedef union _zend_function {
351361
zend_uint num_args;
352362
zend_uint required_num_args;
353363
zend_arg_info *arg_info;
364+
zend_return_hint return_hint;
354365
} common;
355366

356367
zend_op_array op_array;
@@ -515,6 +526,7 @@ void zend_do_add_variable(znode *result, const znode *op1, const znode *op2 TSRM
515526
int zend_do_verify_access_types(const znode *current_access_type, const znode *new_modifier);
516527
void zend_do_begin_function_declaration(znode *function_token, znode *function_name, int is_method, int return_reference, znode *fn_flags_znode TSRMLS_DC);
517528
void zend_do_end_function_declaration(const znode *function_token TSRMLS_DC);
529+
void zend_do_function_return_hint(const znode *return_hint, zend_bool allow_null TSRMLS_DC);
518530
void zend_do_receive_param(zend_uchar op, znode *varname, const znode *initialization, znode *class_type, zend_bool pass_by_reference, zend_bool is_variadic TSRMLS_DC);
519531
int zend_do_begin_function_call(znode *function_name, zend_bool check_namespace TSRMLS_DC);
520532
void zend_do_begin_method_call(znode *left_bracket TSRMLS_DC);

Zend/zend_language_parser.y

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -410,9 +410,21 @@ is_variadic:
410410
| T_ELLIPSIS { $$.op_type = 1; }
411411
;
412412

413+
function_return_type:
414+
T_ARRAY { $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_ARRAY; }
415+
| T_CALLABLE { $$.op_type = IS_CONST; Z_TYPE($$.u.constant)=IS_CALLABLE; }
416+
| fully_qualified_class_name { $$ = $1; }
417+
;
418+
419+
function_return_hint:
420+
/* empty */ { $$.op_type = IS_UNUSED; }
421+
| ':' function_return_type { zend_do_function_return_hint(&$2, 0 TSRMLS_CC); }
422+
| ':' '?' function_return_type { zend_do_function_return_hint(&$3, 1 TSRMLS_CC); }
423+
;
424+
413425
unticked_function_declaration_statement:
414426
function is_reference T_STRING { zend_do_begin_function_declaration(&$1, &$3, 0, $2.op_type, NULL TSRMLS_CC); }
415-
'(' parameter_list ')'
427+
'(' parameter_list ')' function_return_hint
416428
'{' inner_statement_list '}' { zend_do_end_function_declaration(&$1 TSRMLS_CC); }
417429
;
418430

@@ -629,8 +641,8 @@ class_statement:
629641
| class_constant_declaration ';'
630642
| trait_use_statement
631643
| method_modifiers function is_reference T_STRING { zend_do_begin_function_declaration(&$2, &$4, 1, $3.op_type, &$1 TSRMLS_CC); }
632-
'(' parameter_list ')'
633-
method_body { zend_do_abstract_method(&$4, &$1, &$9 TSRMLS_CC); zend_do_end_function_declaration(&$2 TSRMLS_CC); }
644+
'(' parameter_list ')' function_return_hint
645+
method_body { zend_do_abstract_method(&$4, &$1, &$10 TSRMLS_CC); zend_do_end_function_declaration(&$2 TSRMLS_CC); }
634646
;
635647

636648
trait_use_statement:

0 commit comments

Comments
 (0)