Skip to content

[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

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/swift/AST/ASTPrinter.h
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,10 @@ void getInheritedForPrinting(const Decl *decl, const PrintOptions &options,
llvm::SmallVectorImpl<TypeLoc> &Results);

StringRef getAccessorKindString(AccessorKind value);

bool printCompatibilityFeatureChecksPre(ASTPrinter &printer, Decl *decl);
void printCompatibilityFeatureChecksPost(ASTPrinter &printer);

} // namespace swift

#endif // LLVM_SWIFT_AST_ASTPRINTER_H
4 changes: 4 additions & 0 deletions include/swift/AST/PrintOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,10 @@ struct PrintOptions {
/// Whether to print inheritance lists for types.
bool PrintInherited = true;

/// Whether to print feature checks for compatibility with older Swift
/// compilers that might parse the result.
bool PrintCompatibilityFeatureChecks = false;

/// \see ShouldQualifyNestedDeclarations
enum class QualifyNestedDeclarations {
Never,
Expand Down
34 changes: 34 additions & 0 deletions include/swift/Basic/Feature.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//===--- Feature.h - Helpers related to Swift features ----------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_BASIC_FEATURES_H
#define SWIFT_BASIC_FEATURES_H

#include "llvm/ADT/StringRef.h"

namespace swift {

class LangOptions;

/// Enumeration describing all of the named features.
enum class Feature {
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description, Option) \
FeatureName,
#include "swift/Basic/Features.def"
};

/// Determine the in-source name of the given feature.
llvm::StringRef getFeatureName(Feature feature);

}

#endif // SWIFT_BASIC_FEATURES_H
9 changes: 5 additions & 4 deletions include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
// features.
//
//
// FEATURE(FeatureName, SENumber, Description, Option)
// LANGUAGE_FEATURE(FeatureName, SENumber, Description, Option)
//
// The FEATURE macro describes each named feature that is introduced in
// Swift. It allows Swift code to check for a particular feature with
// "#if $FeatureName" in source code.
// The LANGUAGE_FEATURE macro describes each named feature that is
// introduced in Swift. It allows Swift code to check for a particular
// feature with "#if $FeatureName" in source code.
//
// FeatureName: The name given to this feature to be used in source code,
// e.g., AsyncAwait.
Expand All @@ -36,6 +36,7 @@

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

#undef LANGUAGE_FEATURE
225 changes: 221 additions & 4 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "swift/AST/ClangModuleLoader.h"
#include "swift/AST/Comment.h"
#include "swift/AST/Decl.h"
#include "swift/AST/ExistentialLayout.h"
#include "swift/AST/Expr.h"
#include "swift/AST/FileUnit.h"
#include "swift/AST/GenericSignature.h"
Expand All @@ -36,6 +37,7 @@
#include "swift/AST/TypeWalker.h"
#include "swift/AST/Types.h"
#include "swift/Basic/Defer.h"
#include "swift/Basic/Feature.h"
#include "swift/Basic/PrimitiveParsing.h"
#include "swift/Basic/QuotedString.h"
#include "swift/Basic/STLExtras.h"
Expand Down Expand Up @@ -137,6 +139,9 @@ PrintOptions PrintOptions::printSwiftInterfaceFile(ModuleDecl *ModuleToPrint,
// We should print __consuming, __owned, etc for the module interface file.
result.SkipUnderscoredKeywords = false;

// We should provide backward-compatible Swift interfaces when we can.
result.PrintCompatibilityFeatureChecks = true;

result.FunctionBody = [](const ValueDecl *decl, ASTPrinter &printer) {
auto AFD = dyn_cast<AbstractFunctionDecl>(decl);
if (!AFD)
Expand Down Expand Up @@ -955,10 +960,17 @@ class PrintAST : public ASTVisitor<PrintAST> {
}
}


Printer.callPrintDeclPre(D, Options.BracketOptions);

bool haveFeatureChecks = Options.PrintCompatibilityFeatureChecks &&
printCompatibilityFeatureChecksPre(Printer, D);

ASTVisitor::visit(D);

if (haveFeatureChecks)
printCompatibilityFeatureChecksPost(Printer);

if (Synthesize) {
Printer.setSynthesizedTarget({});
Printer.printSynthesizedExtensionPost(cast<ExtensionDecl>(D),
Expand Down Expand Up @@ -2255,6 +2267,11 @@ static void printExtendedTypeName(Type ExtendedType, ASTPrinter &Printer,

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

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


if (Options.BracketOptions.shouldOpenExtension(ExtDecl)) {
printDocumentationComment(ExtDecl);
printAttributes(ExtDecl);
Expand Down Expand Up @@ -2330,6 +2346,9 @@ void PrintAST::printSynthesizedExtension(Type ExtendedType,
Options.BracketOptions.shouldOpenExtension(ExtDecl),
Options.BracketOptions.shouldCloseExtension(ExtDecl));
}

if (haveFeatureChecks)
printCompatibilityFeatureChecksPost(Printer);
}

void PrintAST::printExtension(ExtensionDecl *decl) {
Expand Down Expand Up @@ -2373,6 +2392,194 @@ void PrintAST::printExtension(ExtensionDecl *decl) {
}
}

/// Functions to determine which features a particular declaration uses. The
/// usesFeatureNNN functions correspond to the features in Features.def.

static bool usesFeatureStaticAssert(Decl *decl) {
return false;
}

static bool usesFeatureAsyncAwait(Decl *decl) {
if (auto func = dyn_cast<AbstractFunctionDecl>(decl)) {
if (func->hasAsync())
return true;
}

// Check for async functions in the types of declarations.
if (auto value = dyn_cast<ValueDecl>(decl)) {
if (Type type = value->getInterfaceType()) {
bool hasAsync = type.findIf([](Type type) {
if (auto fnType = type->getAs<AnyFunctionType>()) {
if (fnType->isAsync())
return true;
}

return false;
});

if (hasAsync)
return true;
}
}

return false;
}

static bool usesFeatureMarkerProtocol(Decl *decl) {
// Check an inheritance clause for a marker protocol.
auto checkInherited = [&](ArrayRef<TypeLoc> inherited) -> bool {
for (const auto &inheritedEntry : inherited) {
if (auto inheritedType = inheritedEntry.getType()) {
if (inheritedType->isExistentialType()) {
auto layout = inheritedType->getExistentialLayout();
for (ProtocolType *protoTy : layout.getProtocols()) {
if (protoTy->getDecl()->isMarkerProtocol())
return true;
}
}
}
}

return false;
};

// Check generic requirements for a marker protocol.
auto checkRequirements = [&](ArrayRef<Requirement> requirements) -> bool {
for (const auto &req: requirements) {
if (req.getKind() == RequirementKind::Conformance &&
req.getSecondType()->castTo<ProtocolType>()->getDecl()
->isMarkerProtocol())
return true;
}

return false;
};

if (auto proto = dyn_cast<ProtocolDecl>(decl)) {
if (proto->isMarkerProtocol())
return true;

if (checkInherited(proto->getInherited()))
return true;

if (checkRequirements(proto->getRequirementSignature()))
return true;
}

if (auto ext = dyn_cast<ExtensionDecl>(decl)) {
if (checkRequirements(ext->getGenericRequirements()))
return true;

if (checkInherited(ext->getInherited()))
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;
}

// Check for actors in the types of declarations.
if (auto value = dyn_cast<ValueDecl>(decl)) {
if (Type type = value->getInterfaceType()) {
bool hasActor = type.findIf([](Type type) {
if (auto classDecl = type->getClassOrBoundGenericClass()) {
if (classDecl->isActor())
return true;
}

return false;
});

if (hasActor)
return true;
}
}

return false;
}

/// Determine the set of "new" features used on a given declaration.
///
/// Note: right now, all features we check for are "new". At some point, we'll
/// want a baseline version.
static std::vector<Feature> getFeaturesUsed(Decl *decl) {
std::vector<Feature> features;

// Go through each of the features, checking whether the declaration uses that
// feature. This also ensures that the resulting set is in sorted order.
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description, Option) \
if (usesFeature##FeatureName(decl)) \
features.push_back(Feature::FeatureName);
#include "swift/Basic/Features.def"

return features;
}

/// Get the set of features that are uniquely used by this declaration, and are
/// not part of the enclosing context.
static std::vector<Feature> getUniqueFeaturesUsed(Decl *decl) {
auto features = getFeaturesUsed(decl);
if (features.empty())
return features;

auto enclosingDecl = decl->getDeclContext()->getAsDecl();
if (!enclosingDecl)
return features;

if (!isa<NominalTypeDecl>(enclosingDecl) &&
!isa<ExtensionDecl>(enclosingDecl))
return features;

auto enclosingFeatures = getFeaturesUsed(enclosingDecl);
if (enclosingFeatures.empty())
return features;

// Remove any features from the enclosing context from this set of features so
// that we are left with a unique set.
std::vector<Feature> uniqueFeatures;
std::set_difference(
features.begin(), features.end(),
enclosingFeatures.begin(), enclosingFeatures.end(),
std::back_inserter(uniqueFeatures));
return uniqueFeatures;
}


bool swift::printCompatibilityFeatureChecksPre(
ASTPrinter &printer, Decl *decl) {
auto features = getUniqueFeaturesUsed(decl);
if (features.empty())
return false;

printer.printNewline();
printer << "#if compiler(>=5.3) && ";
llvm::interleave(features.begin(), features.end(),
[&](Feature feature) {
printer << "$" << getFeatureName(feature);
},
[&] { printer << " && "; });
printer.printNewline();

return true;
}

void swift::printCompatibilityFeatureChecksPost(ASTPrinter &printer) {
printer.printNewline();
printer << "#endif\n";
}


void PrintAST::visitExtensionDecl(ExtensionDecl *decl) {
if (Options.TransformContext &&
Options.TransformContext->isPrintingSynthesizedExtension()) {
Expand Down Expand Up @@ -5213,11 +5420,21 @@ swift::getInheritedForPrinting(const Decl *decl, const PrintOptions &options,
// Collect explicit inherited types.
for (auto TL: inherited) {
if (auto ty = TL.getType()) {
bool foundUnprintable = ty.findIf([&options](Type subTy) {
bool foundUnprintable = ty.findIf([&](Type subTy) {
if (auto aliasTy = dyn_cast<TypeAliasType>(subTy.getPointer()))
return !options.shouldPrint(aliasTy->getDecl());
if (auto NTD = subTy->getAnyNominal())
return !options.shouldPrint(NTD);
if (auto NTD = subTy->getAnyNominal()) {
if (!options.shouldPrint(NTD))
return true;

if (auto PD = dyn_cast<ProtocolDecl>(NTD)) {
// Marker protocols are unprintable on concrete types, but they're
// okay on extension declarations and protocols.
if (PD->isMarkerProtocol() && !isa<ExtensionDecl>(decl) &&
!isa<ProtocolDecl>(decl))
return true;
}
}
return false;
});
if (foundUnprintable)
Expand Down
9 changes: 9 additions & 0 deletions lib/Basic/LangOptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
//===----------------------------------------------------------------------===//

#include "swift/Basic/LangOptions.h"
#include "swift/Basic/Feature.h"
#include "swift/Basic/Platform.h"
#include "swift/Basic/Range.h"
#include "swift/Config.h"
Expand Down Expand Up @@ -375,3 +376,11 @@ std::pair<bool, bool> LangOptions::setTarget(llvm::Triple triple) {

return { false, false };
}

llvm::StringRef swift::getFeatureName(Feature feature) {
switch (feature) {
#define LANGUAGE_FEATURE(FeatureName, SENumber, Description, Option) \
case Feature::FeatureName: return #FeatureName;
#include "swift/Basic/Features.def"
}
}
Loading