Skip to content

Enable NonEscapableTypes in standard library and make the feature suppressible #76455

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 2 commits into from
Sep 16, 2024
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
3 changes: 3 additions & 0 deletions include/swift/AST/PrintOptions.h
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,9 @@ struct PrintOptions {
/// Replace BitwiseCopyable with _BitwiseCopyable.
bool SuppressBitwiseCopyable = false;

/// Suppress ~Escapable types and lifetime dependence annotations
bool SuppressNonEscapableTypes = false;

/// List of attribute kinds that should not be printed.
std::vector<AnyAttrKind> ExcludeAttrList = {
DeclAttrKind::Transparent, DeclAttrKind::Effects,
Expand Down
2 changes: 1 addition & 1 deletion include/swift/Basic/Features.def
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ EXPERIMENTAL_FEATURE(StructLetDestructuring, true)

/// Enable non-escapable type attributes and function attributes that support
/// lifetime-dependent results.
EXPERIMENTAL_FEATURE(NonescapableTypes, true)
SUPPRESSIBLE_EXPERIMENTAL_FEATURE(NonescapableTypes, true)

/// Enable the `@_staticExclusiveOnly` attribute.
EXPERIMENTAL_FEATURE(StaticExclusiveOnly, true)
Expand Down
64 changes: 52 additions & 12 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1978,6 +1978,11 @@ void PrintAST::printSingleDepthOfGenericSignature(
if (dependsOnOpaque(inverse.subject))
continue;

if (inverse.getKind() == InvertibleProtocolKind::Escapable &&
Options.SuppressNonEscapableTypes) {
continue;
}

if (isFirstReq) {
if (printRequirements)
Printer << " " << tok::kw_where << " ";
Expand Down Expand Up @@ -3099,6 +3104,16 @@ suppressingFeatureAllowUnsafeAttribute(PrintOptions &options,
options.ExcludeAttrList.resize(originalExcludeAttrCount);
}

static void
suppressingFeatureNonescapableTypes(PrintOptions &options,
llvm::function_ref<void()> action) {
unsigned originalExcludeAttrCount = options.ExcludeAttrList.size();
options.ExcludeAttrList.push_back(DeclAttrKind::Lifetime);
llvm::SaveAndRestore<bool> scope(options.SuppressNonEscapableTypes, true);
action();
options.ExcludeAttrList.resize(originalExcludeAttrCount);
}

/// Suppress the printing of a particular feature.
static void suppressingFeature(PrintOptions &options, Feature feature,
llvm::function_ref<void()> action) {
Expand Down Expand Up @@ -4107,10 +4122,12 @@ void PrintAST::visitFuncDecl(FuncDecl *decl) {
Printer.printDeclResultTypePre(decl, ResultTyLoc);
Printer.callPrintStructurePre(PrintStructureKind::FunctionReturnType);
{
if (auto *typeRepr = dyn_cast_or_null<LifetimeDependentTypeRepr>(
decl->getResultTypeRepr())) {
for (auto &dep : typeRepr->getLifetimeDependencies()) {
Printer << " " << dep.getDependsOnString() << " ";
if (!Options.SuppressNonEscapableTypes) {
if (auto *typeRepr = dyn_cast_or_null<LifetimeDependentTypeRepr>(
decl->getResultTypeRepr())) {
for (auto &dep : typeRepr->getLifetimeDependencies()) {
Printer << " " << dep.getDependsOnString() << " ";
}
}
}
}
Expand Down Expand Up @@ -4330,15 +4347,17 @@ void PrintAST::visitConstructorDecl(ConstructorDecl *decl) {

printGenericDeclGenericParams(decl);
printFunctionParameters(decl);
if (decl->hasLifetimeDependentReturn()) {
Printer << " -> ";
auto *typeRepr =
cast<LifetimeDependentTypeRepr>(decl->getResultTypeRepr());
for (auto &dep : typeRepr->getLifetimeDependencies()) {
Printer << dep.getDependsOnString() << " ";
if (!Options.SuppressNonEscapableTypes) {
if (decl->hasLifetimeDependentReturn()) {
Printer << " -> ";
auto *typeRepr =
cast<LifetimeDependentTypeRepr>(decl->getResultTypeRepr());
for (auto &dep : typeRepr->getLifetimeDependencies()) {
Printer << dep.getDependsOnString() << " ";
}
// TODO: Handle failable initializers with lifetime dependent returns
Printer << "Self";
}
// TODO: Handle failable initializers with lifetime dependent returns
Printer << "Self";
}
});

Expand Down Expand Up @@ -7796,7 +7815,28 @@ swift::getInheritedForPrinting(
}
continue;
}

// Suppress Escapable and ~Escapable.
if (options.SuppressNonEscapableTypes) {
if (auto pct = ty->getAs<ProtocolCompositionType>()) {
auto inverses = pct->getInverses();
if (inverses.contains(InvertibleProtocolKind::Escapable)) {
inverses.remove(InvertibleProtocolKind::Escapable);
ty = ProtocolCompositionType::get(decl->getASTContext(),
pct->getMembers(), inverses,
pct->hasExplicitAnyObject());
if (ty->isAny())
continue;
}
}

if (auto protoTy = ty->getAs<ProtocolType>())
if (protoTy->getDecl()->isSpecificProtocol(
KnownProtocolKind::Escapable))
continue;
}
}

if (options.SuppressConformanceSuppression &&
inherited.getEntry(i).isSuppressed()) {
continue;
Expand Down
70 changes: 69 additions & 1 deletion lib/AST/FeatureSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,75 @@ UNINTERESTING_FEATURE(Embedded)
UNINTERESTING_FEATURE(Volatile)
UNINTERESTING_FEATURE(SuppressedAssociatedTypes)
UNINTERESTING_FEATURE(StructLetDestructuring)
UNINTERESTING_FEATURE(NonescapableTypes)

static bool usesFeatureNonescapableTypes(Decl *decl) {
auto containsNonEscapable =
[](SmallVectorImpl<InverseRequirement> &inverseReqs) {
auto foundIt =
llvm::find_if(inverseReqs, [](InverseRequirement inverseReq) {
if (inverseReq.getKind() == InvertibleProtocolKind::Escapable) {
return true;
}
return false;
});
return foundIt != inverseReqs.end();
};

if (auto *valueDecl = dyn_cast<ValueDecl>(decl)) {
if (isa<StructDecl, EnumDecl, ClassDecl>(decl)) {
auto *nominalDecl = cast<NominalTypeDecl>(valueDecl);
InvertibleProtocolSet inverses;
bool anyObject = false;
getDirectlyInheritedNominalTypeDecls(nominalDecl, inverses, anyObject);
if (inverses.containsEscapable()) {
return true;
}
}

if (auto proto = dyn_cast<ProtocolDecl>(decl)) {
auto reqSig = proto->getRequirementSignature();

SmallVector<Requirement, 2> reqs;
SmallVector<InverseRequirement, 2> inverses;
reqSig.getRequirementsWithInverses(proto, reqs, inverses);
if (containsNonEscapable(inverses))
return true;
}

if (isa<AbstractFunctionDecl>(valueDecl) ||
isa<AbstractStorageDecl>(valueDecl)) {
if (valueDecl->getInterfaceType().findIf([&](Type type) -> bool {
if (auto *nominalDecl = type->getAnyNominal()) {
if (isa<StructDecl, EnumDecl, ClassDecl>(nominalDecl))
return usesFeatureNonescapableTypes(nominalDecl);
}
return false;
})) {
return true;
}
}
}

if (auto *ext = dyn_cast<ExtensionDecl>(decl)) {
if (auto *nominal = ext->getExtendedNominal())
if (usesFeatureNonescapableTypes(nominal))
return true;
}

if (auto *genCtx = decl->getAsGenericContext()) {
if (auto genericSig = genCtx->getGenericSignature()) {
SmallVector<Requirement, 2> reqs;
SmallVector<InverseRequirement, 2> inverseReqs;
genericSig->getRequirementsWithInverses(reqs, inverseReqs);
if (containsNonEscapable(inverseReqs)) {
return true;
}
}
}

return false;
}

UNINTERESTING_FEATURE(StaticExclusiveOnly)
UNINTERESTING_FEATURE(ExtractConstantsFromMembers)
UNINTERESTING_FEATURE(FixedArrays)
Expand Down
4 changes: 1 addition & 3 deletions stdlib/cmake/modules/SwiftSource.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -630,9 +630,7 @@ function(_compile_swift_files
list(APPEND swift_flags "-enable-experimental-feature" "SE427NoInferenceOnExtension")
list(APPEND swift_flags "-enable-experimental-feature" "AllowUnsafeAttribute")

if(SWIFT_ENABLE_EXPERIMENTAL_NONESCAPABLE_TYPES)
list(APPEND swift_flags "-enable-experimental-feature" "NonescapableTypes")
endif()
list(APPEND swift_flags "-enable-experimental-feature" "NonescapableTypes")

list(APPEND swift_flags "-enable-experimental-feature" "ExtensionImportVisiblity")

Expand Down
4 changes: 4 additions & 0 deletions test/ModuleInterface/noncopyable_generics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,11 @@ import NoncopyableGenerics_Misc
// CHECK-MISC: extension {{.*}}.Outer.InnerVariation2 : Swift.Escapable where D : Swift.Escapable {

// CHECK-MISC: extension {{.*}}.Outer.InnerStruct {
// CHECK-MISC-NEXT: #if compiler(>=5.3) && $NonescapableTypes
// CHECK-MISC-NEXT: public func hello<T>(_ t: T) where T : ~Escapable
// CHECK-MISC-NEXT: #else
// CHECK-MISC-NEXT: public func hello<T>(_ t: T)
// CHECK-MISC-NEXT: #endif

// CHECK-MISC: @_preInverseGenerics public func old_swap<T>(_ a: inout T, _ b: inout T) where T : ~Copyable

Expand Down
88 changes: 88 additions & 0 deletions test/ModuleInterface/nonescapable_types.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-emit-module-interface(%t.swiftinterface) %s -module-name Test -enable-experimental-feature NonescapableTypes
Comment on lines +1 to +2
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like this file may be over-indented by one space on most of the lines

// RUN: %target-swift-typecheck-module-from-interface(%t.swiftinterface) -module-name Test
// RUN: %FileCheck %s < %t.swiftinterface

// CHECK: #if compiler(>=5.3) && $NonescapableTypes
// CHECK: public protocol P : ~Escapable {
// CHECK: associatedtype A
// CHECK: }
// CHECK: #else
// CHECK: public protocol P {
// CHECK: associatedtype A
// CHECK: }
// CHECK: #endif
public protocol P: ~Escapable {
associatedtype A
}

// CHECK: #if compiler(>=5.3) && $NonescapableTypes
// CHECK: public struct X<T> : ~Swift.Escapable where T : ~Escapable {
// CHECK: }
// CHECK: #else
// CHECK: public struct X<T> {
// CHECK: }
// CHECK: #endif
public struct X<T: ~Escapable>: ~Escapable { }

// CHECK: #if compiler(>=5.3) && $NonescapableTypes
// CHECK: extension Test.X {
// CHECK-NEXT: func f()
// CHECK: }
// CHECK: #else
// CHECK: extension Test.X {
// CHECK-NEXT: func f()
// CHECK: }
extension X where T: Escapable {
public func f() { }
}

// CHECK: #if compiler(>=5.3) && $NonescapableTypes
// CHECK: extension Test.X where T : ~Escapable {
// CHECK: public func g(other: borrowing T)
// CHECK: }
// CHECK: #else
// CHECK: extension Test.X {
// CHECK: public func g(other: borrowing T)
// CHECK: }
// CHECK: #endif
extension X where T: ~Escapable {
public func g(other: borrowing T) { }
}

// CHECK: #if compiler(>=5.3) && $NonescapableTypes
// CHECK: public enum Y<T> : ~Swift.Escapable where T : ~Escapable {
// CHECK: case none
// CHECK: case some(T)
// CHECK: }
// CHECK: #else
// CHECK: public enum Y<T> {
// CHECK: case none
// CHECK: case some(T)
// CHECK: }
public enum Y<T: ~Escapable>: ~Escapable {
case none
case some(T)
}

extension Y: Escapable where T: Escapable { }

// CHECK: #if compiler(>=5.3) && $NonescapableTypes
// CHECK: @lifetime(y)
// CHECK: public func derive<T>(_ y: Test.Y<T>) -> Test.Y<T> where T : ~Escapable
// CHECK: #else
// CHECK: public func derive<T>(_ y: Test.Y<T>) -> Test.Y<T>
// CHECK: #endif
@lifetime(y)
public func derive<T : ~Escapable>(_ y: Y<T>) -> Y<T> {
y
}

// CHECK: #if compiler(>=5.3) && $NonescapableTypes
// CHECK: public func derive<T>(_ x: Test.X<T>) -> dependsOn(x) Test.X<T> where T : ~Escapable
// CHECK: #else
// CHECK: public func derive<T>(_ x: Test.X<T>) -> Test.X<T>
// CHECK: #endif
public func derive<T : ~Escapable>(_ x: X<T>) -> dependsOn(x) X<T> {
x
}