Skip to content

[SE-0364] Handle retroactive conformance for types and protocols from underlying modules #71302

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

tshortli
Copy link
Contributor

@tshortli tshortli commented Feb 1, 2024

SE-0364 was implemented to discourage "retroactive" conformances that might conflict with conformances that could be introduced by other modules in the future. These diagnostics should not apply to conformances that involve types and protocols imported from the underlying clang module of a Swift module since the two modules are assumed to be developed in tandem by the same owners, despite technically being separate modules from the perspective of the compiler.

The diagnostics implemented in #36068 were designed to take underlying clang modules into account. However, the implementation assumed that ModuleDecl::getUnderlyingModuleIfOverlay() would behave as expected when called on the Swift module being compiled. Unfortunately, it would always return nullptr and thus conformances involving the underlying clang module are being diagnosed unexpectedly.

The fix is to make ModuleDecl::getUnderlyingModuleIfOverlay() behave as expected when it is made up of SourceFiles.

Resolves rdar://121478556

@tshortli tshortli force-pushed the suppress-retroactive-warning-for-types-from-underlying-module branch 2 times, most recently from 4387480 to dc56003 Compare February 1, 2024 03:14
@tshortli tshortli marked this pull request as ready for review February 1, 2024 03:15
@tshortli tshortli force-pushed the suppress-retroactive-warning-for-types-from-underlying-module branch from dc56003 to c709291 Compare February 1, 2024 03:36
@tshortli
Copy link
Contributor Author

tshortli commented Feb 1, 2024

@swift-ci please test

… underlying modules.

SE-0364 was implemented to discourage "retroactive" conformances that might
conflict with conformances that could be introduced by other modules in the
future. These diagnostics should not apply to conformances that involve types
and protocols imported from the underlying clang module of a Swift module since
the two modules are assumed to be developed in tandem by the same owners,
despite technically being separate modules from the perspective of the
compiler.

The diagnostics implemented in swiftlang#36068 were
designed to take underlying clang modules into account. However, the
implementation assumed that `ModuleDecl::getUnderlyingModuleIfOverlay()` would
behave as expected when called on the Swift module being compiled.
Unfortunately, it would always return `nullptr` and thus conformances involving
the underlying clang module are being diagnosed unexpectedly.

The fix is to make `ModuleDecl::getUnderlyingModuleIfOverlay()` behave as
expected when it is made up of `SourceFile`s.

Resolves rdar://121478556
@tshortli tshortli force-pushed the suppress-retroactive-warning-for-types-from-underlying-module branch from c709291 to 8846f4e Compare February 1, 2024 18:44
@tshortli
Copy link
Contributor Author

tshortli commented Feb 1, 2024

@swift-ci please smoke test

Copy link
Contributor

@beccadax beccadax left a comment

Choose a reason for hiding this comment

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

To be clear, this is making it so that overlays (where you use an explicit @_exported import MyModule) are treated the same as mixed-language targets (where you use -import-underlying-module or -import-objc-header), right?

Comment on lines +699 to +703
ModuleDecl *getUnderlyingModuleIfOverlay() const override {
return ImportedUnderlyingModule;
}

const clang::Module *getUnderlyingClangModule() const override {
Copy link
Contributor

Choose a reason for hiding this comment

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

I was a little confused at first, but it looks like these are base class methods used by the existing diagnostic, so overriding them changes its behavior. Nice and clean. 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Right, there are existing ModuleDecl methods that call these methods that I've overridden, and I think it was an oversight that those ModuleDecl methods were not functional on the module instance representing the compiled module for the job.

Comment on lines +2569 to +2582

// Find and cache the import of the underlying module, if present.
auto parentModuleName = getParentModule()->getName();
for (auto import : imports) {
if (!import.options.contains(ImportFlags::Exported))
continue;

auto importedModule = import.module.importedModule;
if (importedModule->getName() == parentModuleName &&
importedModule->findUnderlyingClangModule()) {
ImportedUnderlyingModule = import.module.importedModule;
break;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Rather than looping over the imports this way, you could modify ImportResolver::getModule() to save off the underlying clang module in a new member variable of ImportResolver (there's a separate code path to look it up—look for the use of getClangModuleLoader()) and then have performImportResolution() set that on the SourceFile immediately before/after calling setImports(). Would that be a cleaner solution?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That does seem like a cleaner solution because it keeps the logic for identifying the underlying clang module more centralized. In the interest of time I think I'm going to land this solution first, but I will follow it up with what you've suggested - thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Implemented here: #71330

@tshortli
Copy link
Contributor Author

tshortli commented Feb 1, 2024

To be clear, this is making it so that overlays (where you use an explicit @_exported import MyModule) are treated the same as mixed-language targets (where you use -import-underlying-module or -import-objc-header), right?

It is intended to work for both of them, yes. But in case this was not clear, the diagnostics were not behaving correctly in either case.

@tshortli
Copy link
Contributor Author

tshortli commented Feb 1, 2024

@swift-ci please smoke test Linux

@tshortli tshortli enabled auto-merge February 1, 2024 23:18
@tshortli tshortli merged commit 8fd77e3 into swiftlang:main Feb 1, 2024
@tshortli tshortli deleted the suppress-retroactive-warning-for-types-from-underlying-module branch February 1, 2024 23:56
tshortli added a commit to tshortli/swift that referenced this pull request Feb 2, 2024
As recommended in feedback on swiftlang#71302, cache
the underlying clang module after loading it in `ImportResolver`, rather than
filtering it out of the overall set of resolved imports. This is more efficient
and results in less duplicated code that must identify the underlying clang
module.
tshortli added a commit to tshortli/swift that referenced this pull request Feb 9, 2024
tshortli added a commit to tshortli/swift that referenced this pull request Feb 9, 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