Skip to content

Commit aa139a1

Browse files
committed
[Module interface] Use features in module interface generation.
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.
1 parent 09f0838 commit aa139a1

File tree

9 files changed

+323
-11
lines changed

9 files changed

+323
-11
lines changed

include/swift/AST/ASTPrinter.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,10 @@ void getInheritedForPrinting(const Decl *decl, const PrintOptions &options,
355355
llvm::SmallVectorImpl<TypeLoc> &Results);
356356

357357
StringRef getAccessorKindString(AccessorKind value);
358+
359+
bool printCompatibilityFeatureChecksPre(ASTPrinter &printer, Decl *decl);
360+
void printCompatibilityFeatureChecksPost(ASTPrinter &printer);
361+
358362
} // namespace swift
359363

360364
#endif // LLVM_SWIFT_AST_ASTPRINTER_H

include/swift/AST/PrintOptions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,10 @@ struct PrintOptions {
460460
/// Whether to print inheritance lists for types.
461461
bool PrintInherited = true;
462462

463+
/// Whether to print feature checks for compatibility with older Swift
464+
/// compilers that might parse the result.
465+
bool PrintCompatibilityFeatureChecks = false;
466+
463467
/// \see ShouldQualifyNestedDeclarations
464468
enum class QualifyNestedDeclarations {
465469
Never,

include/swift/Basic/Feature.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===--- Feature.h - Helpers related to Swift features ----------*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef SWIFT_BASIC_FEATURES_H
14+
#define SWIFT_BASIC_FEATURES_H
15+
16+
#include "llvm/ADT/StringRef.h"
17+
18+
namespace swift {
19+
20+
class LangOptions;
21+
22+
/// Enumeration describing all of the named features.
23+
enum class Feature {
24+
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description, Option) \
25+
FeatureName,
26+
#include "swift/Basic/Features.def"
27+
};
28+
29+
/// Determine the in-source name of the given feature.
30+
llvm::StringRef getFeatureName(Feature feature);
31+
32+
}
33+
34+
#endif // SWIFT_BASIC_FEATURES_H

include/swift/Basic/Features.def

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
1414
// features.
1515
//
1616
//
17-
// FEATURE(FeatureName, SENumber, Description, Option)
17+
// LANGUAGE_FEATURE(FeatureName, SENumber, Description, Option)
1818
//
19-
// The FEATURE macro describes each named feature that is introduced in
20-
// Swift. It allows Swift code to check for a particular feature with
21-
// "#if $FeatureName" in source code.
19+
// The LANGUAGE_FEATURE macro describes each named feature that is
20+
// introduced in Swift. It allows Swift code to check for a particular
21+
// feature with "#if $FeatureName" in source code.
2222
//
2323
// FeatureName: The name given to this feature to be used in source code,
2424
// e.g., AsyncAwait.
@@ -36,6 +36,7 @@
3636

3737
LANGUAGE_FEATURE(StaticAssert, 0, "#assert", langOpts.EnableExperimentalStaticAssert)
3838
LANGUAGE_FEATURE(AsyncAwait, 296, "async/await", true)
39+
LANGUAGE_FEATURE(MarkerProtocol, 0, "@_marker protocol", true)
3940
LANGUAGE_FEATURE(Actors, 0, "actors", langOpts.EnableExperimentalConcurrency)
4041

4142
#undef LANGUAGE_FEATURE

lib/AST/ASTPrinter.cpp

Lines changed: 169 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "swift/AST/ClangModuleLoader.h"
2323
#include "swift/AST/Comment.h"
2424
#include "swift/AST/Decl.h"
25+
#include "swift/AST/ExistentialLayout.h"
2526
#include "swift/AST/Expr.h"
2627
#include "swift/AST/FileUnit.h"
2728
#include "swift/AST/GenericSignature.h"
@@ -36,6 +37,7 @@
3637
#include "swift/AST/TypeWalker.h"
3738
#include "swift/AST/Types.h"
3839
#include "swift/Basic/Defer.h"
40+
#include "swift/Basic/Feature.h"
3941
#include "swift/Basic/PrimitiveParsing.h"
4042
#include "swift/Basic/QuotedString.h"
4143
#include "swift/Basic/STLExtras.h"
@@ -137,6 +139,9 @@ PrintOptions PrintOptions::printSwiftInterfaceFile(ModuleDecl *ModuleToPrint,
137139
// We should print __consuming, __owned, etc for the module interface file.
138140
result.SkipUnderscoredKeywords = false;
139141

142+
// We should provide backward-compatible Swift interfaces when we can.
143+
result.PrintCompatibilityFeatureChecks = true;
144+
140145
result.FunctionBody = [](const ValueDecl *decl, ASTPrinter &printer) {
141146
auto AFD = dyn_cast<AbstractFunctionDecl>(decl);
142147
if (!AFD)
@@ -955,10 +960,17 @@ class PrintAST : public ASTVisitor<PrintAST> {
955960
}
956961
}
957962

963+
958964
Printer.callPrintDeclPre(D, Options.BracketOptions);
959965

966+
bool haveFeatureChecks = Options.PrintCompatibilityFeatureChecks &&
967+
printCompatibilityFeatureChecksPre(Printer, D);
968+
960969
ASTVisitor::visit(D);
961970

971+
if (haveFeatureChecks)
972+
printCompatibilityFeatureChecksPost(Printer);
973+
962974
if (Synthesize) {
963975
Printer.setSynthesizedTarget({});
964976
Printer.printSynthesizedExtensionPost(cast<ExtensionDecl>(D),
@@ -2255,6 +2267,11 @@ static void printExtendedTypeName(Type ExtendedType, ASTPrinter &Printer,
22552267

22562268
void PrintAST::printSynthesizedExtension(Type ExtendedType,
22572269
ExtensionDecl *ExtDecl) {
2270+
// Print compatibility features checks first, if we need them.
2271+
bool haveFeatureChecks = Options.PrintCompatibilityFeatureChecks &&
2272+
Options.BracketOptions.shouldOpenExtension(ExtDecl) &&
2273+
Options.BracketOptions.shouldCloseExtension(ExtDecl) &&
2274+
printCompatibilityFeatureChecksPre(Printer, ExtDecl);
22582275

22592276
auto printRequirementsFrom = [&](ExtensionDecl *ED, bool &IsFirst) {
22602277
auto Sig = ED->getGenericSignature();
@@ -2297,7 +2314,6 @@ void PrintAST::printSynthesizedExtension(Type ExtendedType,
22972314
return true;
22982315
};
22992316

2300-
23012317
if (Options.BracketOptions.shouldOpenExtension(ExtDecl)) {
23022318
printDocumentationComment(ExtDecl);
23032319
printAttributes(ExtDecl);
@@ -2330,6 +2346,9 @@ void PrintAST::printSynthesizedExtension(Type ExtendedType,
23302346
Options.BracketOptions.shouldOpenExtension(ExtDecl),
23312347
Options.BracketOptions.shouldCloseExtension(ExtDecl));
23322348
}
2349+
2350+
if (haveFeatureChecks)
2351+
printCompatibilityFeatureChecksPost(Printer);
23332352
}
23342353

23352354
void PrintAST::printExtension(ExtensionDecl *decl) {
@@ -2373,6 +2392,143 @@ void PrintAST::printExtension(ExtensionDecl *decl) {
23732392
}
23742393
}
23752394

2395+
/// Functions to determine which features a particular declaration uses. The
2396+
/// usesFeatureNNN functions correspond to the features in Features.def.
2397+
2398+
static bool usesFeatureStaticAssert(Decl *decl) {
2399+
return false;
2400+
}
2401+
2402+
static bool usesFeatureAsyncAwait(Decl *decl) {
2403+
if (auto func = dyn_cast<AbstractFunctionDecl>(decl)) {
2404+
if (func->hasAsync())
2405+
return true;
2406+
}
2407+
2408+
return false;
2409+
}
2410+
2411+
static bool usesFeatureMarkerProtocol(Decl *decl) {
2412+
if (auto proto = dyn_cast<ProtocolDecl>(decl)) {
2413+
if (proto->isMarkerProtocol())
2414+
return true;
2415+
}
2416+
2417+
if (auto ext = dyn_cast<ExtensionDecl>(decl)) {
2418+
for (const auto &req: ext->getGenericRequirements()) {
2419+
if (req.getKind() == RequirementKind::Conformance &&
2420+
req.getSecondType()->castTo<ProtocolType>()->getDecl()
2421+
->isMarkerProtocol())
2422+
return true;
2423+
}
2424+
2425+
for (const auto &inherited : ext->getInherited()) {
2426+
if (auto inheritedType = inherited.getType()) {
2427+
if (inheritedType->isExistentialType()) {
2428+
auto layout = inheritedType->getExistentialLayout();
2429+
for (ProtocolType *protoTy : layout.getProtocols()) {
2430+
if (protoTy->getDecl()->isMarkerProtocol())
2431+
return true;
2432+
}
2433+
}
2434+
}
2435+
}
2436+
}
2437+
2438+
return false;
2439+
}
2440+
2441+
static bool usesFeatureActors(Decl *decl) {
2442+
if (auto classDecl = dyn_cast<ClassDecl>(decl)) {
2443+
if (classDecl->isActor())
2444+
return true;
2445+
}
2446+
2447+
if (auto ext = dyn_cast<ExtensionDecl>(decl)) {
2448+
if (auto classDecl = ext->getSelfClassDecl())
2449+
if (classDecl->isActor())
2450+
return true;
2451+
}
2452+
2453+
return false;
2454+
}
2455+
2456+
/// Determine the set of "new" features used on a given declaration.
2457+
///
2458+
/// Note: right now, all features we check for are "new". At some point, we'll
2459+
/// want a baseline version.
2460+
static std::vector<Feature> getFeaturesUsed(Decl *decl) {
2461+
std::vector<Feature> features;
2462+
2463+
// Only type- and module-scope declarations have any features to speak of.
2464+
auto dc = decl->getDeclContext();
2465+
if (!dc->isTypeContext() && !dc->isModuleScopeContext())
2466+
return features;
2467+
2468+
// Go through each of the features, checking whether the declaration uses that
2469+
// feature. This also ensures that the resulting set is in sorted order.
2470+
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description, Option) \
2471+
if (usesFeature##FeatureName(decl)) \
2472+
features.push_back(Feature::FeatureName);
2473+
#include "swift/Basic/Features.def"
2474+
2475+
return features;
2476+
}
2477+
2478+
/// Get the set of features that are uniquely used by this declaration, and are
2479+
/// not part of the enclosing context.
2480+
static std::vector<Feature> getUniqueFeaturesUsed(Decl *decl) {
2481+
auto features = getFeaturesUsed(decl);
2482+
if (features.empty())
2483+
return features;
2484+
2485+
auto enclosingDecl = decl->getDeclContext()->getAsDecl();
2486+
if (!enclosingDecl)
2487+
return features;
2488+
2489+
if (!isa<NominalTypeDecl>(enclosingDecl) &&
2490+
!isa<ExtensionDecl>(enclosingDecl))
2491+
return features;
2492+
2493+
auto enclosingFeatures = getFeaturesUsed(enclosingDecl);
2494+
if (enclosingFeatures.empty())
2495+
return features;
2496+
2497+
// Remove any features from the enclosing context from this set of features so
2498+
// that we are left with a unique set.
2499+
std::vector<Feature> uniqueFeatures;
2500+
std::set_difference(
2501+
features.begin(), features.end(),
2502+
enclosingFeatures.begin(), enclosingFeatures.end(),
2503+
std::back_inserter(uniqueFeatures));
2504+
return uniqueFeatures;
2505+
}
2506+
2507+
2508+
bool swift::printCompatibilityFeatureChecksPre(
2509+
ASTPrinter &printer, Decl *decl) {
2510+
auto features = getUniqueFeaturesUsed(decl);
2511+
if (features.empty())
2512+
return false;
2513+
2514+
printer.printNewline();
2515+
printer << "#if compiler(>=5.3) && ";
2516+
llvm::interleave(features.begin(), features.end(),
2517+
[&](Feature feature) {
2518+
printer << "$" << getFeatureName(feature);
2519+
},
2520+
[&] { printer << " && "; });
2521+
printer.printNewline();
2522+
2523+
return true;
2524+
}
2525+
2526+
void swift::printCompatibilityFeatureChecksPost(ASTPrinter &printer) {
2527+
printer.printNewline();
2528+
printer << "#endif\n";
2529+
}
2530+
2531+
23762532
void PrintAST::visitExtensionDecl(ExtensionDecl *decl) {
23772533
if (Options.TransformContext &&
23782534
Options.TransformContext->isPrintingSynthesizedExtension()) {
@@ -5213,11 +5369,20 @@ swift::getInheritedForPrinting(const Decl *decl, const PrintOptions &options,
52135369
// Collect explicit inherited types.
52145370
for (auto TL: inherited) {
52155371
if (auto ty = TL.getType()) {
5216-
bool foundUnprintable = ty.findIf([&options](Type subTy) {
5372+
bool foundUnprintable = ty.findIf([&](Type subTy) {
52175373
if (auto aliasTy = dyn_cast<TypeAliasType>(subTy.getPointer()))
52185374
return !options.shouldPrint(aliasTy->getDecl());
5219-
if (auto NTD = subTy->getAnyNominal())
5220-
return !options.shouldPrint(NTD);
5375+
if (auto NTD = subTy->getAnyNominal()) {
5376+
if (!options.shouldPrint(NTD))
5377+
return true;
5378+
5379+
if (auto PD = dyn_cast<ProtocolDecl>(NTD)) {
5380+
// Marker protocols are unprintable on nominal types, but they're
5381+
// okay on extension declarations.
5382+
if (PD->isMarkerProtocol() && !isa<ExtensionDecl>(decl))
5383+
return true;
5384+
}
5385+
}
52215386
return false;
52225387
});
52235388
if (foundUnprintable)

lib/Basic/LangOptions.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
//===----------------------------------------------------------------------===//
1717

1818
#include "swift/Basic/LangOptions.h"
19+
#include "swift/Basic/Feature.h"
1920
#include "swift/Basic/Platform.h"
2021
#include "swift/Basic/Range.h"
2122
#include "swift/Config.h"
@@ -375,3 +376,11 @@ std::pair<bool, bool> LangOptions::setTarget(llvm::Triple triple) {
375376

376377
return { false, false };
377378
}
379+
380+
llvm::StringRef swift::getFeatureName(Feature feature) {
381+
switch (feature) {
382+
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description, Option) \
383+
case Feature::FeatureName: return #FeatureName;
384+
#include "swift/Basic/Features.def"
385+
}
386+
}

0 commit comments

Comments
 (0)