Skip to content

Avoid early-binding on unresolved types #4137

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 1 commit into from

Conversation

nikic
Copy link
Member

@nikic nikic commented May 9, 2019

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 core problem is that the inheritance code currently simply uses zend_lookup_class when checking whether two types (that are not string equal) are the same due to aliasing (or in the future: compatible through variance) and treats non-existing classes as an error. This has two problems:

  • If the class is early bound and one of the types is not found, this is treated as an error. Instead no early binding should be performed, as the types may be defined at runtime.
  • Again during early binding, types outside the current file will be used, thus introducing an illegal state dependence in opcache.

This PR solves the problem by

  • Introducing an UNRESOLVED state when performing a method compatibility check. This indicates that necessary types couldn't be loaded. At runtime this is the same as an error, while at compile-time it means that early binding cannot be performed.
  • Introducing the necessary checks to limit class loading to the local file at compile time.
  • Before attempting to perform early-binding with a parent class, do a pass over the methods to check that there are no UNRESOLVED incompatibilities. This is rather ugly, but I couldn't think of a better way to do it. It would be great if we could just try to run inheritance and abort if we encounter UNRESOLVED, but as inheritance is destructive, we need to detect this condition before we run it.

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.
@nikic
Copy link
Member Author

nikic commented May 9, 2019

@dstogov Please have a look at this. Once this issue is resolved, the actual covariance implementation will be simple.

@KalleZ KalleZ requested a review from dstogov May 11, 2019 20:06
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.

This patch affects preloading. Fixing it may disclose other problems.
make test TESTS="--preload Zend/tests/bug76451.phpt"

More optimal solution (without double-checks) would be great.

I'm not sure, if covariant/contravariant types may work without run-time checks, and how this patch would help them.

if ((ce = zend_lookup_class_ex(Z_STR_P(parent_name), Z_STR_P(parent_name + 1), 0)) != NULL) {
do_bind_class(RT_CONSTANT(&op_array->opcodes[opline_num], op_array->opcodes[opline_num].op1), ce);
zend_class_entry *ce = zend_hash_find_ptr(EG(class_table), Z_STR_P(lcname + 1));
zend_class_entry *parent_ce = zend_lookup_class_ex(Z_STR_P(parent_name), Z_STR_P(parent_name + 1), 0);
Copy link
Member

Choose a reason for hiding this comment

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

Do we need this lookup. if "ce" is NULL? We didn't do it before.

) {
continue;
}
if (zend_do_perform_implementation_check(func, parent_func) == 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.

In case we need this only for methods with "class" type hints, we may set special fn_flag to avoid duplicate check for others. We may also reuse the same flag for classes to completely eliminate this loop. (At least we may reuse ZEND_ACC_HAS_TYPE_HINTS & ZEND_ACC_HAS_RETURN_TYPE for methods).

Copy link
Member

Choose a reason for hiding this comment

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

On the other hand, if we check everything in zend_can_early_bind() and everything is fine (INHERITANCE_SUCCESS), we may avoid the same checks in do_bind_class(). If zend_do_perform_implementation_check() returns INHERITANCE_ERROR, we may throw error immediately.

@nikic
Copy link
Member Author

nikic commented May 24, 2019

Part of #4185 now.

@nikic nikic closed this May 24, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants