Skip to content

Implement basic variance support #4185

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 11 commits into from
Closed

Implement basic variance support #4185

wants to merge 11 commits into from

Conversation

nikic
Copy link
Member

@nikic nikic commented May 22, 2019

@dstogov My proposal for "minimal" variance support, with only enough to make the non-circular cases work.

  • This is based on Avoid early-binding on unresolved types #4137 to avoid early-binding issues.
  • For preloading, we now require that all used method argument/return types are available for the class to be linked. For anything else linking will be delayed until runtime. This is a conservative solution, and we might try to make it more precise (we don't always need loaded types to check inheritance).
  • Anything involving circular dependencies will not work with this patch (at least after Forbid use of not fully linked classes #4184 fixes some loopholes), regardless of whether the classes are in the same file, autoloaded or preloaded. Again, this is something we may improve in the future.

@nikic
Copy link
Member Author

nikic commented May 22, 2019

I've included #4184 in here as well now, because it requires some additional handling (some things work by accident without it).

nikic added 7 commits May 23, 2019 10:56
Fixes bug #76451, and more importantly lays necessary groundwork for
covariant/contravariant types. Bug #76451 is just an edge case, but
once covariance is introduced this will become a common problem instead.
The autoload case is supposed to work, but doesn't actually :(
@nikic
Copy link
Member Author

nikic commented May 23, 2019

Rebased over #4184.

// Illegal: A::parent is ill-defined
class A {
public function method(parent $x) {}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this allowed at all?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had originally added a compile-time check for this in 7.4, but reverted it again in deb44d4 because it broke Mockery, which is used by Laravel. Maybe we can do this for PHP 8.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be a good idea to add this into deprecations for PHP-7.4

nikic added 2 commits May 23, 2019 15:16
Which should make it more obvious if an inheritance check fails due to
a cycle.
Copy link
Member

@dstogov dstogov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general the PR is OK, but it is a subject for future optimizations and reduction of implementation limitations (For example, the second example from RFC and Zend/tests/type_declarations/variance/class_order_autoload.phpt might be supported).

@@ -6392,6 +6394,7 @@ zend_op *zend_compile_class_decl(zend_ast *ast, zend_bool toplevel) /* {{{ */
&& !(CG(compiler_options) & ZEND_COMPILE_PRELOAD) /* delay inheritance till preloading */
&& ((parent_ce->type != ZEND_INTERNAL_CLASS) || !(CG(compiler_options) & ZEND_COMPILE_IGNORE_INTERNAL_CLASSES))
&& ((parent_ce->type != ZEND_USER_CLASS) || !(CG(compiler_options) & ZEND_COMPILE_IGNORE_OTHER_FILES) || (parent_ce->info.user.filename == ce->info.user.filename))
&& zend_can_early_bind(ce, parent_ce)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should try to reduce overhead of zend_can_early_bind(), but we can do it later.

@@ -3491,6 +3529,10 @@ static void preload_link(void)
if (!found) continue;
}

if (!preload_all_types_known(ce)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is seriously more restricted than necessary, but we try to improve it later. Please add TODO comments.

zend_class_entry *fe_ce = lookup_class(fe, fe_class_name);
if (!fe_ce) {
*unresolved_class = fe_class_name;
return INHERITANCE_UNRESOLVED;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is not necessary. Any class should work, so we may avoid class loading.

<?php
interface A {
        function m(): object;
}
interface B extends A {
        function m(): unknown;
}

@nikic
Copy link
Member Author

nikic commented May 24, 2019

Merged into 7.4 as afec3a9 and 49a3b03.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants