-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[Module interface] Use features in module interface generation. #35839
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
[Module interface] Use features in module interface generation. #35839
Conversation
When generating a module interface, emit `#if` around any declarations that are tied to specific, named language features. This allows module interfaces to be processed by older Swift compilers that do not support these newer features, such as async/await or actors. The amount of effort required to correctly handle a new kind of feature varies somewhat drastically based on the feature itself. The "simple" case is where a particular declaration can only exist if a feature is available. For example, and `async` declaration is fairly easy to handle; a `@_marker` protocol's conformances are not. Fixes rdar://73326633.
@swift-ci please smoke test |
lib/AST/ASTPrinter.cpp
Outdated
static bool usesFeatureAsyncAwait(Decl *decl) { | ||
if (auto func = dyn_cast<AbstractFunctionDecl>(decl)) { | ||
if (func->hasAsync()) | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
static bool usesFeatureMarkerProtocol(Decl *decl) { | ||
if (auto proto = dyn_cast<ProtocolDecl>(decl)) { | ||
if (proto->isMarkerProtocol()) | ||
return true; | ||
} | ||
|
||
if (auto ext = dyn_cast<ExtensionDecl>(decl)) { | ||
for (const auto &req: ext->getGenericRequirements()) { | ||
if (req.getKind() == RequirementKind::Conformance && | ||
req.getSecondType()->castTo<ProtocolType>()->getDecl() | ||
->isMarkerProtocol()) | ||
return true; | ||
} | ||
|
||
for (const auto &inherited : ext->getInherited()) { | ||
if (auto inheritedType = inherited.getType()) { | ||
if (inheritedType->isExistentialType()) { | ||
auto layout = inheritedType->getExistentialLayout(); | ||
for (ProtocolType *protoTy : layout.getProtocols()) { | ||
if (protoTy->getDecl()->isMarkerProtocol()) | ||
return true; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
static bool usesFeatureActors(Decl *decl) { | ||
if (auto classDecl = dyn_cast<ClassDecl>(decl)) { | ||
if (classDecl->isActor()) | ||
return true; | ||
} | ||
|
||
if (auto ext = dyn_cast<ExtensionDecl>(decl)) { | ||
if (auto classDecl = ext->getSelfClassDecl()) | ||
if (classDecl->isActor()) | ||
return true; | ||
} | ||
|
||
return false; | ||
} |
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.
Unless I'm mistaken, all three of these probably ought to walk into any parameter or result types and look for the feature being used there, too. For instance, if you write:
public func runAsync(_ fn: () async -> Void) { ... }
That probably ought to be guarded by #if $AsyncAwait
, even though runAsync(_:)
itself isn't an async function. Similarly:
public func transact(with account: BankAccount)
Probably ought to be guarded by #if $Actors
if BankAccount
is an actor class, even though transact(with:)
isn't itself an actor class.
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.
Good point!
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 went ahead and did a bunch of this checking. It is not comprehensive, I'm sure, although we could probably do some additional meta programming to make it less boilerplate-y and more comprehensive.
lib/AST/ASTPrinter.cpp
Outdated
static std::vector<Feature> getFeaturesUsed(Decl *decl) { | ||
std::vector<Feature> features; | ||
|
||
// Only type- and module-scope declarations have any features to speak of. |
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.
Note that, by my expanded definition of "having a feature", this comment is no longer true.
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.
Right, I've zapped this. Thanks!
@swift-ci please smoke test |
1 similar comment
@swift-ci please smoke test |
@swift-ci please smoke test Linux |
Hi @DougGregor – The test is broken and confusing. Superficially, it needs Also, FYI: |
When generating a module interface, emit
#if
around any declarationsthat are tied to specific, named language features. This allows module
interfaces to be processed by older Swift compilers that do not
support these newer features, such as async/await or actors.
The amount of effort required to correctly handle a new kind of
feature varies somewhat drastically based on the feature itself. The
"simple" case is where a particular declaration can only exist if a
feature is available. For example, and
async
declaration is fairlyeasy to handle; a
@_marker
protocol's conformances are not.Fixes rdar://73326633.
Design credit to @beccadax, bugs are my own.