Skip to content

[ClangImporter] Fix IUO ordering bug #70106

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

Merged
merged 1 commit into from
Jan 6, 2024

Conversation

beccadax
Copy link
Contributor

The IUO-ness of imported declarations is not actually computed by IsImplicitlyUnwrappedOptionalRequest. Instead, ClangImporter manually sets the bit to true after the declaration’s type is imported and expects IsImplicitlyUnwrappedOptionalRequest to always set it to false for all other imported declarations.

Normally, declaration types are imported greedily as soon as the declaration is created. However, a ClangImporter refactoring in #61026 deferred the import of a VarDecl’s type, and therefore the setting of its IUO bit, until the first time InterfaceTypeRequest is evaluated.

It turns out that there is nothing to guarantee that InterfaceTypeRequest will be evaluated before IsImplicitlyUnwrappedOptionalRequest, so if isImplicitlyUnwrappedOptional() was fetched before getInterfaceType() was called, it would return an incorrect result. The only known client that accesses the information in this order is the API digester, but in theory any part of the compiler could fall into this trap.

Force the evaluation of InterfaceTypeRequest during IsImplicitlyUnwrappedOptionalRequest when necessary to compute the IUO bit for an imported VarDecl, and add a test to prove that this fixes the observed bug in the API digester.

Cherry-picked out of #69928.

The IUO-ness of imported declarations is not actually computed by IsImplicitlyUnwrappedOptionalRequest. Instead, ClangImporter manually sets the bit to `true` after the declaration’s type is imported and expects IsImplicitlyUnwrappedOptionalRequest to always set it to `false` for all other imported declarations.

Normally, declaration types are imported greedily as soon as the declaration is created. However, a ClangImporter refactoring in swiftlang#61026 deferred the import of a VarDecl’s type, and therefore the setting of its IUO bit, until the first time InterfaceTypeRequest is evaluated.

It turns out that there is nothing to guarantee that InterfaceTypeRequest will be evaluated before IsImplicitlyUnwrappedOptionalRequest, so if isImplicitlyUnwrappedOptional() was fetched before getInterfaceType() was called, it would return an incorrect result. The only known client that accesses the information in this order is the API digester, but in theory any part of the compiler could fall into this trap.

Force the evaluation of InterfaceTypeRequest during IsImplicitlyUnwrappedOptionalRequest when necessary to compute the IUO bit for an imported VarDecl, and add a test to prove that this fixes the observed bug in the API digester.
@beccadax beccadax requested a review from zoecarver November 30, 2023 00:31
@beccadax
Copy link
Contributor Author

@zoecarver If you can think of a better fix, suggestions welcome.

@beccadax
Copy link
Contributor Author

@swift-ci please test

@beccadax beccadax marked this pull request as ready for review November 30, 2023 00:33
@slavapestov
Copy link
Contributor

The IUO-ness of imported declarations is not actually computed by IsImplicitlyUnwrappedOptionalRequest. Instead, ClangImporter manually sets the bit to true after the declaration’s type is imported and expects IsImplicitlyUnwrappedOptionalRequest to always set it to false for all other imported declarations.

It sounds like we were a bit sloppy with the original request conversion here. Would it be too difficult to fix this the right way? I know it's not your fault it's like this.

It turns out that there is nothing to guarantee that InterfaceTypeRequest will be evaluated before IsImplicitlyUnwrappedOptionalRequest,

This is true of all requests, which is why the evaluation function should not have observable side effects like this.

@beccadax
Copy link
Contributor Author

This is true of all requests, which is why the evaluation function should not have observable side effects like this.

True. ClangImporter has a bad—well, I guess really it's just old—habit of manually computing and pre-populating values that are normally computed by a request that only works for Swift code; that's usually acceptable because it sets the values right after it creates the AST nodes, but here some of that work was deferred, so it became possible to execute other code before the field was populated.

It sounds like we were a bit sloppy with the original request conversion here. Would it be too difficult to fix this the right way? I know it's not your fault it's like this.

The tricky thing is that, because of how importType() works, we can't compute the IUO-ness of a decl without also computing enough information to determine its interface type. But perhaps we should just add an ImportedDeclInterfaceTypeAndIsImplicitlyUnwrappedRequest that computes and caches both pieces of information, and that both InterfaceTypeRequest and IsImplicitlyUnwrappedOptionalRequest defer to for decls with a clang node. It'll be a little strange to have two requests that both write to the same cache, but it seems better than adding another DenseMap to the importer or repeating work unnecessarily.

I don't love this, though, so if anyone has a better idea, I'd love to hear it.

Copy link
Contributor

@slavapestov slavapestov left a comment

Choose a reason for hiding this comment

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

The tricky thing is that, because of how importType() works, we can't compute the IUO-ness of a decl without also computing enough information to determine its interface type.

Looking at usages of OTK_ImplicitlyUnwrappedOptional in ImportType.cpp, it might be theoretically possible to untangle them with some minimal amount of duplication. After all, ImplicitlyUnwrappedOptionalRequest also does this to Swift AST in some sense, because it looks at TypeReprs just like InterfaceTypeRequest ultimately looks at TypeReprs.

It might even make sense to just always have InterfaceTypeRequest return a type/IUO pair.

But both of those are much larger changes than what I was imagining, so your narrow fix to force the request for its side effect is fine. Hopefully we can sort it out eventually!

@slavapestov
Copy link
Contributor

... ok, one last idea: since the importType() lazyness only applies to VarDecls, it might not be too bad to figure out how to extract just that part?

@beccadax
Copy link
Contributor Author

beccadax commented Dec 2, 2023

I've looked at the IUO logic in ImportType.cpp and it seems difficult to disentangle from the SwiftTypeConverter. (The main problem is the ImportHint, which is selected by visiting the clang type nodes and which is frequently used to disable IUOs.)

I think I'm going to go with what I have for now.

@beccadax
Copy link
Contributor Author

beccadax commented Dec 2, 2023

@swift-ci please test and merge

@beccadax
Copy link
Contributor Author

beccadax commented Dec 7, 2023

@swift-ci smoke test

@beccadax
Copy link
Contributor Author

beccadax commented Dec 8, 2023

@swift-ci smoke test and merge

@beccadax
Copy link
Contributor Author

beccadax commented Dec 8, 2023

@swift-ci smoke test and merge

1 similar comment
@beccadax
Copy link
Contributor Author

beccadax commented Dec 8, 2023

@swift-ci smoke test and merge

@beccadax
Copy link
Contributor Author

beccadax commented Dec 8, 2023

One more try.

@swift-ci smoke test and merge

@beccadax
Copy link
Contributor Author

beccadax commented Dec 8, 2023

@swift-ci please clean smoke test macOS platform

@beccadax
Copy link
Contributor Author

beccadax commented Dec 8, 2023

@swift-ci please clean smoke test Linux platform

@beccadax
Copy link
Contributor Author

beccadax commented Jan 5, 2024

@swift-ci smoke test

@beccadax beccadax enabled auto-merge January 6, 2024 00:36
@beccadax beccadax merged commit 46f8dfa into swiftlang:main Jan 6, 2024
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