-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[Sema] Report use of implicitly imported decls in inlinable code #59799
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
[Sema] Report use of implicitly imported decls in inlinable code #59799
Conversation
Renaming isImportedImplementationOnly to getRestrictedImportKind will allow us to support different kind of restrictive imports in the next commits.
This warning will be used more generally so let's make bring back the diagnostic in line with the error version.
Implicitly imported decls may end up in inlinable code and break the module API. This have been known to lead to deserialization crash and could in theory break the generated swiftinterfaces files. Let's explicitly check for such a case, keeping it to a warning until Swift 6 where we can make it an error. rdar://95816286
@swift-ci Please smoke test |
lib/AST/Module.cpp
Outdated
return RestrictedImportKind::None; | ||
|
||
// Special case the __ObjC module. This module is implicitly imported but | ||
// represents any clang module so we can consider it public. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm having trouble following this part because I thought the __ObjC
module is the one that gets implicitly imported as a result of having a bridging header. If that's true, I would think that it is harmful to reference decls from __ObjC
in public decls and inlinable code, at least for library evolution enabled modules. Am I thinking about this wrong?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I though frameworks aren't meant to have bridging headers at all, especially not if they're built with library evolution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I experimented with it and it looks like there's an automatic @_exported import __ObjC
emitted in the swiftinterface file for a module with a bridging header, so __ObjC
is implicitly publicly imported. Using these decls in API should be fine in theory. This appeared to work even without that special case, I believe the import of a bridging header __ObjC
is usually recognized by the check a few lines above: if (imports.isImportedBy(module, desc.module.importedModule))
I actually added this special case to support the how the C++ interop represents C++ namespaces in Swift. They synthesize an enum in the __ObjC
module and extend it in each imported module adding decls to the namespace. This lead to this diagnostics reporting references to __ObjC
as errors in the Swift code in the compiler for each use of a decl under a namespace. There's a few more issues here, the first one being that extension members can be referenced without being imported so the C++ interop solution suffers greatly from the very bug this PR tries to limit. As a proper alternative to this special case, maybe the C++ interop should instead declare the import more cleanly as we already do for a module with a bridging header.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eventually I'd like to fix that, though. I think the reason it's discouraged is what I'm talking about here, but if we got the diagnostics correct and changed the semantics for library evolution modules to have the bridging header be implicitly implementation only imported I think we we could allow it. Many framework developers need something like a bridging header but have to resort to creating a custom clang module to import implementation only instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Not sure how the comment order got mixed up, but my last comment was in response to Slava's).
How would the compiler resolve the import __ObjC
when encountering it in a swiftinterface in an SDK? It seems like that would rely on the corresponding clang header being available at some well known location relative to the swiftmodule. In an Xcode project a bridging header is usually just a project header, though. And if you didn't make it a project header, I think the build system would need to know to treat it specially and synthesize a module definition file to install with it or something.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know how building that swiftinterface works in practice, like Slava I'm used to Xcode (I believe it's Xcode) rejecting frameworks with bridging-headers. Thinking about it, the use case with the C++ interop in the compiler was for a mixed source module, I'll have to take a look at how it behaves in such a case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll remove the special case for now to keep the check as strict as it was before, even if it may raise more warnings now as it's executed on more files.
Maybe instead of "implicitly imported" you can be more explicit (hah!) and say "defined in an extension in a transitively-imported module" or something even more specific? Otherwise someone who encounters this diagnostic will have a hard time figuring out what's going on. |
@slavapestov What do you think of |
d628734
to
e7b1434
Compare
@swift-ci Please smoke test |
Changed the diagnostic to the more direct |
@swift-ci Please smoke test |
…nitions The test swift-class-instantiation-module-interface triggered crashes in this logic because some decls don't have a owning module. It appears to be the synthesized templates that are at cause here.
The synthesized decls for C++ templates don't have an owning module, this broke a workaround for imported decls with multiple definitions. We can skip those decls in the workaround as they wouldn't help to establish that there's a visible import of any one of the multiple definitions. @swift-ci Please smoke test |
@swift-ci Please smoke test macOS |
@swift-ci Please smoke test Linux |
Implicitly imported decls references from inlinable code can break the module API. This has been known to lead to deserialization crash and could break the generated swiftinterfaces files. Let's explicitly check for such a case, keeping it to a warning until Swift language version 6 where it's an error.
This should address cases crashing the compiler, but it will require more work. Mainly, lookup should not allow any reference to non-imported decls, since this is source breaking we could also report them as a warning in implementation details by adding a similar check to
diagnoseDeclAvailability
. On the performance side, because we're using more ofgetRestrictedImportKind
we may want to cache its result at this point.rdar://95816286