Combine variance obligation processing and error reporting

I don't believe there's a reason anymore to first resolve all
delayed variance obligations, and then report errors if any are
left.  We can report errors directly when resolving obligations.
This commit is contained in:
Nikita Popov 2021-11-08 10:32:47 +01:00
parent 904933e918
commit b969ce3b98

View file

@ -2441,8 +2441,7 @@ static void add_property_compatibility_obligation(
static void resolve_delayed_variance_obligations(zend_class_entry *ce);
static int check_variance_obligation(zval *zv) {
variance_obligation *obligation = Z_PTR_P(zv);
static void check_variance_obligation(variance_obligation *obligation) {
if (obligation->type == OBLIGATION_DEPENDENCY) {
zend_class_entry *dependency_ce = obligation->dependency_ce;
if (dependency_ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE) {
@ -2453,18 +2452,11 @@ static int check_variance_obligation(zval *zv) {
resolve_delayed_variance_obligations(dependency_ce);
CG(current_linking_class) = orig_linking_class;
}
if (!(dependency_ce->ce_flags & ZEND_ACC_LINKED)) {
return ZEND_HASH_APPLY_KEEP;
}
} else if (obligation->type == OBLIGATION_COMPATIBILITY) {
inheritance_status status = zend_do_perform_implementation_check(
&obligation->child_fn, obligation->child_scope,
&obligation->parent_fn, obligation->parent_scope);
if (UNEXPECTED(status != INHERITANCE_SUCCESS)) {
if (EXPECTED(status == INHERITANCE_UNRESOLVED)) {
return ZEND_HASH_APPLY_KEEP;
}
ZEND_ASSERT(status == INHERITANCE_ERROR);
emit_incompatible_method_error(
&obligation->child_fn, obligation->child_scope,
&obligation->parent_fn, obligation->parent_scope, status);
@ -2475,14 +2467,9 @@ static int check_variance_obligation(zval *zv) {
inheritance_status status =
property_types_compatible(obligation->parent_prop, obligation->child_prop);
if (status != INHERITANCE_SUCCESS) {
if (status == INHERITANCE_UNRESOLVED) {
return ZEND_HASH_APPLY_KEEP;
}
ZEND_ASSERT(status == INHERITANCE_ERROR);
emit_incompatible_property_error(obligation->child_prop, obligation->parent_prop);
}
}
return ZEND_HASH_APPLY_REMOVE;
}
static void load_delayed_classes(zend_class_entry *ce) {
@ -2521,44 +2508,11 @@ static void resolve_delayed_variance_obligations(zend_class_entry *ce) {
obligations = zend_hash_index_find_ptr(all_obligations, num_key);
ZEND_ASSERT(obligations != NULL);
zend_hash_apply(obligations, check_variance_obligation);
if (zend_hash_num_elements(obligations) == 0) {
ce->ce_flags &= ~ZEND_ACC_UNRESOLVED_VARIANCE;
ce->ce_flags |= ZEND_ACC_LINKED;
zend_hash_index_del(all_obligations, num_key);
}
}
static void report_variance_errors(zend_class_entry *ce) {
HashTable *all_obligations = CG(delayed_variance_obligations), *obligations;
variance_obligation *obligation;
zend_ulong num_key = (zend_ulong) (uintptr_t) ce;
ZEND_ASSERT(all_obligations != NULL);
obligations = zend_hash_index_find_ptr(all_obligations, num_key);
ZEND_ASSERT(obligations != NULL);
ZEND_HASH_FOREACH_PTR(obligations, obligation) {
if (obligation->type == OBLIGATION_COMPATIBILITY) {
/* Just used to populate the delayed_autoloads table,
* which will be used when printing the "unresolved" error. */
inheritance_status status = zend_do_perform_implementation_check(
&obligation->child_fn, obligation->child_scope,
&obligation->parent_fn, obligation->parent_scope);
ZEND_ASSERT(status == INHERITANCE_UNRESOLVED);
emit_incompatible_method_error(
&obligation->child_fn, obligation->child_scope,
&obligation->parent_fn, obligation->parent_scope, status);
} else if (obligation->type == OBLIGATION_PROPERTY_COMPATIBILITY) {
emit_incompatible_property_error(obligation->child_prop, obligation->parent_prop);
} else {
/* Report variance errors of the dependency, which prevent it from being linked. */
report_variance_errors(obligation->dependency_ce);
}
check_variance_obligation(obligation);
} ZEND_HASH_FOREACH_END();
/* Only warnings were thrown above -- that means that there are incompatibilities, but only
* ones that we permit. Mark all classes with open obligations as fully linked. */
ce->ce_flags &= ~ZEND_ACC_UNRESOLVED_VARIANCE;
ce->ce_flags |= ZEND_ACC_LINKED;
zend_hash_index_del(all_obligations, num_key);
@ -2931,10 +2885,6 @@ ZEND_API zend_class_entry *zend_do_link_class(zend_class_entry *ce, zend_string
load_delayed_classes(ce);
if (ce->ce_flags & ZEND_ACC_UNRESOLVED_VARIANCE) {
resolve_delayed_variance_obligations(ce);
if (!(ce->ce_flags & ZEND_ACC_LINKED)) {
CG(current_linking_class) = orig_linking_class;
report_variance_errors(ce);
}
}
if (ce->ce_flags & ZEND_ACC_CACHEABLE) {
ce->ce_flags &= ~ZEND_ACC_CACHEABLE;