Skip to content

Commit 52e85af

Browse files
committed
Did I finally fix generic type binding?
1 parent ad0816d commit 52e85af

11 files changed

+181
-24
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
Cannot use generic type as a constraint
3+
--FILE--
4+
<?php
5+
6+
interface I<T1, T2 : T1> {
7+
public function foo(T $param): T;
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Cannot use generic parameter T1 to constrain generic parameter T2 in %s on line %d
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
Cannot use never as a constraint
3+
--FILE--
4+
<?php
5+
6+
interface I<T : never> {
7+
public function foo(T $param): T;
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Cannot use static, void, or never to constrain generic parameter T in %s on line %d
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
Cannot use void as a constraint
3+
--FILE--
4+
<?php
5+
6+
interface I<T : static> {
7+
public function foo(T $param): T;
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Cannot use static, void, or never to constrain generic parameter T in %s on line %d
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
--TEST--
2+
Cannot use void as a constraint
3+
--FILE--
4+
<?php
5+
6+
interface I<T : void> {
7+
public function foo(T $param): T;
8+
}
9+
10+
?>
11+
--EXPECTF--
12+
Fatal error: Cannot use static, void, or never to constrain generic parameter T in %s on line %d
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
Abstract generic type behaviour in extended interface 2 generic type to 1
3+
--FILE--
4+
<?php
5+
6+
interface I<T1, T2> {
7+
public function foo(T1 $param): T2;
8+
}
9+
10+
interface I2<S> extends I<S, S> {
11+
public function bar(int $o, S $param): S;
12+
}
13+
14+
class C implements I2<string> {
15+
public function foo(string $param): string {}
16+
public function bar(int $o, string $param): string {}
17+
}
18+
19+
?>
20+
DONE
21+
--EXPECT--
22+
DONE
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
--TEST--
2+
Abstract generic type behaviour in extended interface
3+
--FILE--
4+
<?php
5+
6+
interface IWG<T1> {
7+
public function foo(T1 $param): T1;
8+
}
9+
10+
interface I2<T2> extends IWG<T2> {
11+
public function bar(int $o, T2 $param): T2;
12+
}
13+
14+
class C implements I2<string> {
15+
public function foo(string $param): string {}
16+
public function bar(int $o, string $param): string {}
17+
}
18+
19+
?>
20+
DONE
21+
--EXPECT--
22+
DONE

Zend/tests/type_declarations/abstract_generics/extended_interface_new_abstract_generic_type2.phpt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
--TEST--
22
Abstract generic type behaviour in extended interface
3-
--XFAIL--
4-
Generic type is not properly bound (Declaration of C::foo(string $param): string must be compatible with I::foo(T $param): T)
53
--FILE--
64
<?php
75

Zend/zend_compile.c

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ void init_compiler(void) /* {{{ */
465465
CG(delayed_autoloads) = NULL;
466466
CG(unlinked_uses) = NULL;
467467
CG(current_linking_class) = NULL;
468-
CG(bound_associated_types) = NULL;
468+
CG(bound_generic_types) = NULL;
469469
}
470470
/* }}} */
471471

@@ -495,10 +495,10 @@ void shutdown_compiler(void) /* {{{ */
495495
}
496496
CG(current_linking_class) = NULL;
497497
/* This can happen during a fatal error */
498-
if (CG(bound_associated_types)) {
499-
zend_hash_destroy(CG(bound_associated_types));
500-
FREE_HASHTABLE(CG(bound_associated_types));
501-
CG(bound_associated_types) = NULL;
498+
if (CG(bound_generic_types)) {
499+
zend_hash_destroy(CG(bound_generic_types));
500+
FREE_HASHTABLE(CG(bound_generic_types));
501+
CG(bound_generic_types) = NULL;
502502
}
503503
}
504504
/* }}} */
@@ -9180,9 +9180,18 @@ static void zend_compile_generic_params(zend_ast *params_ast)
91809180
}
91819181

91829182
if (param_ast->child[1]) {
9183-
// TODO Need to free this
9183+
// TODO Need to free this?
91849184
constraint_type = zend_compile_typename(param_ast->child[1]);
9185-
// TODO Validate that void, static, never are not used in the constraint?
9185+
if (ZEND_TYPE_IS_ASSOCIATED(constraint_type)) {
9186+
zend_error_noreturn(E_COMPILE_ERROR,
9187+
"Cannot use generic parameter %s to constrain generic parameter %s",
9188+
ZSTR_VAL(ZEND_TYPE_NAME(constraint_type)), ZSTR_VAL(name));
9189+
}
9190+
if (ZEND_TYPE_FULL_MASK(constraint_type) & (MAY_BE_STATIC|MAY_BE_VOID|MAY_BE_NEVER)) {
9191+
zend_error_noreturn(E_COMPILE_ERROR,
9192+
"Cannot use static, void, or never to constrain generic parameter %s",
9193+
ZSTR_VAL(name));
9194+
}
91869195
}
91879196

91889197
generic_params[i].name = zend_string_copy(name);

0 commit comments

Comments
 (0)