Skip to content

[Runtime] Fix infinite recursion in protocol conformance checking on class Sub: Super<Sub>. #38515

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

Conversation

mikeash
Copy link
Contributor

@mikeash mikeash commented Jul 20, 2021

Conformance checks now walk the superclass chain in two stages: stage 1 only walks superclasses that have already been instantiated. When there's a negative result and there's an uninstantiated superclass, then stage 2 will walk the uninstantiated superclasses.

The infinite recursion would occur in this scenario:

class Super<T: P> {}
class Sub: Super<Sub>, P {}

Instantiating the metadata for Super requires looking up the conformance for Sub: P. Conformance checking for Sub would instantiate the metadata for Super to check for a Super: P conformance.

The compiler does not allow the conformance to come from a superclass in this situation. This does not compile:

class Super<T: P>: P {}
class Sub: Super<Sub> {}

Therefore it's not necessary to look at Super when finding the conformance for Sub: P in this particular case. The trick is knowing when to skip Super.

We do need to instantiate Super in the general case, otherwise we can get false negatives. This was addressed in a80fe85, which walks the full superclass chain during conformance checks, even if the superclass has not yet been instantiated. Unfortunately, that causes this infinite recursion.

This fix modifies that fix to make superclass instantiation conditional. The result is the ability to choose between the old algorithm (which skipped uninstantiated superclasses, albeit somewhat by accident) and the new one. A small wrapper then runs the check with the old algorithm, and then only if the old algorithm fails and there is an uninstantiated superclass, it runs with the new one.

Uninstantiated superclasses are uncommon and transient (you only run into this while metadata is in the process of being constructed) so 99.9999% of the time we'll just run the first stage and be done, and performance should remain the same as before.

rdar://80532245

@mikeash mikeash force-pushed the fix-protocol-conformance-superclass-recursion branch 2 times, most recently from 93fcb02 to ca3c1d3 Compare July 22, 2021 15:19
@mikeash mikeash requested a review from rjmccall July 22, 2021 15:21
@mikeash mikeash force-pushed the fix-protocol-conformance-superclass-recursion branch from ca3c1d3 to 0397ab3 Compare July 22, 2021 15:30
@mikeash mikeash changed the title [WIP] Fix protocol conformance recursion when looking for superclasses. [Runtime] Fix infinite recursion in protocol conformance checking on class Sub: Super<Sub>. Jul 22, 2021
@mikeash mikeash marked this pull request as ready for review July 22, 2021 15:30
@mikeash
Copy link
Contributor Author

mikeash commented Jul 22, 2021

@swift-ci please test

…class Sub: Super<Sub>.

Conformance checks now walk the superclass chain in two stages: stage 1 only walks superclasses that have already been instantiated. When there's a negative result and there's an uninstantiated superclass, then stage 2 will walk the uninstantiated superclasses.

The infinite recursion would occur in this scenario:

    class Super<T: P> {}
    class Sub: Super<Sub>, P {}

Instantiating the metadata for Super requires looking up the conformance for Sub: P. Conformance checking for Sub would instantiate the metadata for Super to check for a Super: P conformance.

The compiler does not allow the conformance to come from a superclass in this situation. This does not compile:

    class Super<T: P>: P {}
    class Sub: Super<Sub> {}

Therefore it's not necessary to look at Super when finding the conformance for Sub: P in this particular case. The trick is knowing when to skip Super.

We do need to instantiate Super in the general case, otherwise we can get false negatives. This was addressed in a80fe85, which walks the full superclass chain during conformance checks, even if the superclass has not yet been instantiated. Unfortunately, that causes this infinite recursion.

This fix modifies that fix to make superclass instantiation conditional. The result is the ability to choose between the old algorithm (which skipped uninstantiated superclasses, albeit somewhat by accident) and the new one. A small wrapper then runs the check with the old algorithm, and then only if the old algorithm fails and there is an uninstantiated superclass, it runs with the new one.

Uninstantiated superclasses are uncommon and transient (you only run into this while metadata is in the process of being constructed) so 99.9999% of the time we'll just run the first stage and be done, and performance should remain the same as before.

rdar://80532245
@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 0397ab36c1be518660d1796eb62c3541e83bc1e1

@mikeash mikeash force-pushed the fix-protocol-conformance-superclass-recursion branch from 0397ab3 to b37252d Compare July 22, 2021 18:56
@mikeash
Copy link
Contributor Author

mikeash commented Jul 22, 2021

@swift-ci please test

@mikeash mikeash requested review from tbkka and al45tair July 22, 2021 19:56
@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - b37252d

@mikeash
Copy link
Contributor Author

mikeash commented Jul 22, 2021

@swift-ci please test linux platform

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - b37252d

@mikeash
Copy link
Contributor Author

mikeash commented Jul 22, 2021

@swift-ci please smoke test macOS platform

@mikeash
Copy link
Contributor Author

mikeash commented Jul 22, 2021

@swift-ci please smoke test OS X platform

Copy link
Contributor

@al45tair al45tair left a comment

Choose a reason for hiding this comment

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

LGTM.

@DougGregor DougGregor merged commit 7b791cf into swiftlang:main Jul 23, 2021
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.

4 participants