Skip to content

[6.0][sending] Fix a bunch of issues around suppressing sending #74068

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 7 commits into from
Jun 3, 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
28 changes: 25 additions & 3 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
#include "clang/Lex/MacroInfo.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/ConvertUTF.h"
#include "llvm/Support/SaveAndRestore.h"
Expand All @@ -71,6 +72,15 @@

using namespace swift;

#ifndef NDEBUG
static llvm::cl::opt<bool> NumberSuppressionChecks(
"swift-ast-printer-number-suppression-checks",
llvm::cl::desc("Used to number suppression checks in swift interface files "
"to make it easier to FileCheck them. Only available with "
"asserts enabled and intended for compiler tests."),
llvm::cl::init(false), llvm::cl::Hidden);
#endif

// Defined here to avoid repeatedly paying the price of template instantiation.
const std::function<bool(const ExtensionDecl *)>
PrintOptions::defaultPrintExtensionContentAsMembers
Expand Down Expand Up @@ -3205,6 +3215,15 @@ static void printCompatibilityCheckIf(ASTPrinter &printer, bool isElseIf,
}
printer << "$" << getFeatureName(feature);
}

#ifndef NDEBUG
if (NumberSuppressionChecks) {
static unsigned totalSuppressionChecks = 0;
printer << " // Suppression Count: " << totalSuppressionChecks;
++totalSuppressionChecks;
}
#endif

printer.printNewline();
}

Expand Down Expand Up @@ -3295,10 +3314,13 @@ void swift::printWithCompatibilityFeatureChecks(ASTPrinter &printer,
// features, or else just print the body.
if (features.hasAnySuppressible()) {
auto generator = features.generateSuppressibleFeatures();

// NOTE: We emit the compiler check here as well since that also implicitly
// ensures that we ignore parsing errors in the if block. It is harmless
// otherwise.
printWithSuppressibleFeatureChecks(printer, options,
/*first*/ true,
/*compiler check*/ !hasRequiredFeatures,
generator,
/*first*/ true,
/*compiler check*/ true, generator,
printBody);
} else {
printBody();
Expand Down
23 changes: 17 additions & 6 deletions lib/AST/FeatureSet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -643,8 +643,7 @@ UNINTERESTING_FEATURE(GroupActorErrors)

UNINTERESTING_FEATURE(TransferringArgsAndResults)
static bool usesFeatureSendingArgsAndResults(Decl *decl) {
auto functionTypeUsesSending = [](Decl *decl) {
return usesTypeMatching(decl, [](Type type) {
auto isFunctionTypeWithSending = [](Type type) {
auto fnType = type->getAs<AnyFunctionType>();
if (!fnType)
return false;
Expand All @@ -656,28 +655,40 @@ static bool usesFeatureSendingArgsAndResults(Decl *decl) {
[](AnyFunctionType::Param param) {
return param.getParameterFlags().isSending();
});
});
};
auto declUsesFunctionTypesThatUseSending = [&](Decl *decl) {
return usesTypeMatching(decl, isFunctionTypeWithSending);
};

if (auto *pd = dyn_cast<ParamDecl>(decl)) {
if (pd->isSending()) {
return true;
}

if (functionTypeUsesSending(pd))
if (declUsesFunctionTypesThatUseSending(pd))
return true;
}

if (auto *fDecl = dyn_cast<FuncDecl>(decl)) {
if (auto *fDecl = dyn_cast<AbstractFunctionDecl>(decl)) {
// First check for param decl results.
if (llvm::any_of(fDecl->getParameters()->getArray(), [](ParamDecl *pd) {
return usesFeatureSendingArgsAndResults(pd);
}))
return true;
if (functionTypeUsesSending(decl))
if (declUsesFunctionTypesThatUseSending(decl))
return true;
}

// Check if we have a pattern binding decl for a function that has sending
// parameters and results.
if (auto *pbd = dyn_cast<PatternBindingDecl>(decl)) {
for (auto index : range(pbd->getNumPatternEntries())) {
auto *pattern = pbd->getPattern(index);
if (pattern->hasType() && isFunctionTypeWithSending(pattern->getType()))
return true;
}
}

return false;
}

Expand Down
150 changes: 149 additions & 1 deletion test/Concurrency/sending_conditional_suppression.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// RUN: %empty-directory(%t)

// RUN: %target-swift-frontend -enable-upcoming-feature SendingArgsAndResults -swift-version 5 -enable-library-evolution -module-name test -emit-module -o %t/test.swiftmodule -emit-module-interface-path - %s | %FileCheck %s
// RUN: %target-swift-frontend -enable-upcoming-feature SendingArgsAndResults -swift-version 5 -enable-library-evolution -module-name test -emit-module -o %t/test.swiftmodule -emit-module-interface-path - -disable-availability-checking -Xllvm -swift-ast-printer-number-suppression-checks %s | %FileCheck %s

// REQUIRES: asserts

public class NonSendableKlass {}

Expand Down Expand Up @@ -90,4 +92,150 @@ public struct TestInStruct {
// CHECK-NEXT: public func testKlassArgAndResult(_ x: test.NonSendableKlass, _ y: test.NonSendableKlass, z: test.NonSendableKlass) -> test.NonSendableKlass
// CHECK-NEXT: #endif
public func testKlassArgAndResult(_ x: NonSendableKlass, _ y: sending NonSendableKlass, z: NonSendableKlass) -> sending NonSendableKlass { fatalError() }

// CHECK-LABEL: #if compiler(>=5.3) && $SendingArgsAndResults
// CHECK-NEXT: public func testFunctionArg(_ x: () -> sending test.NonSendableKlass)
// CHECK-NEXT: #else
// CHECK-NEXT: public func testFunctionArg(_ x: () -> test.NonSendableKlass)
// CHECK-NEXT: #endif
public func testFunctionArg(_ x: () -> sending NonSendableKlass) { fatalError() }

// CHECK-LABEL: #if compiler(>=5.3) && $SendingArgsAndResults
// CHECK-NEXT: public func testFunctionResult() -> (() -> sending test.NonSendableKlass)
// CHECK-NEXT: #else
// CHECK-NEXT: public func testFunctionResult() -> (() -> test.NonSendableKlass)
// CHECK-NEXT: #endif
public func testFunctionResult() -> (() -> sending NonSendableKlass) { fatalError() }

// CHECK-LABEL: #if compiler(>=5.3) && $SendingArgsAndResults
// CHECK-NEXT: @usableFromInline
// CHECK-NEXT: internal func testUsableFromInlineKlassArg(_ x: sending test.NonSendableKlass)
// CHECK-NEXT: #else
// CHECK-NEXT: @usableFromInline
// CHECK-NEXT: internal func testUsableFromInlineKlassArg(_ x: test.NonSendableKlass)
// CHECK-NEXT: #endif
@usableFromInline func testUsableFromInlineKlassArg(_ x: sending NonSendableKlass) { fatalError() }

// CHECK-LABEL: #if compiler(>=5.3) && $SendingArgsAndResults
// CHECK-NEXT: @usableFromInline
// CHECK-NEXT: internal func testUsableFromInlineKlassResult() -> sending test.NonSendableKlass
// CHECK-NEXT: #else
// CHECK-NEXT: @usableFromInline
// CHECK-NEXT: internal func testUsableFromInlineKlassResult() -> test.NonSendableKlass
// CHECK-NEXT: #endif
@usableFromInline
func testUsableFromInlineKlassResult() -> sending NonSendableKlass { fatalError() }

// CHECK-LABEL: #if compiler(>=5.3) && $SendingArgsAndResults
// CHECK-NEXT: @usableFromInline
// CHECK-NEXT: internal func testUsableFromInlineKlassArgAndResult(_ x: test.NonSendableKlass, _ y: sending test.NonSendableKlass, z: test.NonSendableKlass) -> sending test.NonSendableKlass
// CHECK-NEXT: #else
// CHECK-NEXT: @usableFromInline
// CHECK-NEXT: internal func testUsableFromInlineKlassArgAndResult(_ x: test.NonSendableKlass, _ y: test.NonSendableKlass, z: test.NonSendableKlass) -> test.NonSendableKlass
// CHECK-NEXT: #endif
@usableFromInline
func testUsableFromInlineKlassArgAndResult(_ x: NonSendableKlass, _ y: sending NonSendableKlass, z: NonSendableKlass) -> sending NonSendableKlass { fatalError() }

// CHECK-LABEL: #if compiler(>=5.3) && $SendingArgsAndResults
// CHECK-NEXT: @usableFromInline
// CHECK-NEXT: internal func testUsableFromInlineFunctionArg(_ x: () -> sending test.NonSendableKlass)
// CHECK-NEXT: #else
// CHECK-NEXT: @usableFromInline
// CHECK-NEXT: internal func testUsableFromInlineFunctionArg(_ x: () -> test.NonSendableKlass)
// CHECK-NEXT: #endif
@usableFromInline
func testUsableFromInlineFunctionArg(_ x: () -> sending NonSendableKlass) { fatalError() }

// CHECK-LABEL: #if compiler(>=5.3) && $SendingArgsAndResults
// CHECK-NEXT: @usableFromInline
// CHECK-NEXT: internal func testUsableFromInlineFunctionResult() -> (() -> sending test.NonSendableKlass)
// CHECK-NEXT: #else
// CHECK-NEXT: @usableFromInline
// CHECK-NEXT: internal func testUsableFromInlineFunctionResult() -> (() -> test.NonSendableKlass)
// CHECK-NEXT: #endif
@usableFromInline
func testUsableFromInlineFunctionResult() -> (() -> sending NonSendableKlass) { fatalError() }

// CHECK-LABEL: #if compiler(>=5.3) && $SendingArgsAndResults
// CHECK-NEXT: public var publicVarFieldFunctionArg: (sending test.NonSendableKlass) -> ()
// CHECK-NEXT: #else
// CHECK-NEXT: public var publicVarFieldFunctionArg: (test.NonSendableKlass) -> ()
// CHECK-NEXT: #endif
public var publicVarFieldFunctionArg: (sending NonSendableKlass) -> ()

// CHECK-LABEL: #if compiler(>=5.3) && $SendingArgsAndResults
// CHECK-NEXT: @usableFromInline
// CHECK-NEXT: internal var internalVarFieldFunctionArg: (sending test.NonSendableKlass) -> ()
// CHECK-NEXT: #else
// CHECK-NEXT: @usableFromInline
// CHECK-NEXT: internal var internalVarFieldFunctionArg: (test.NonSendableKlass) -> ()
// CHECK-NEXT: #endif
@usableFromInline
var internalVarFieldFunctionArg: (sending NonSendableKlass) -> ()

// CHECK-LABEL: #if compiler(>=5.3) && $SendingArgsAndResults
// CHECK-NEXT: public let publicLetFieldFunctionArg: (sending test.NonSendableKlass) -> ()
// CHECK-NEXT: #else
// CHECK-NEXT: public let publicLetFieldFunctionArg: (test.NonSendableKlass) -> ()
// CHECK-NEXT: #endif
public let publicLetFieldFunctionArg: (sending NonSendableKlass) -> ()

// CHECK-LABEL: #if compiler(>=5.3) && $SendingArgsAndResults
// CHECK-NEXT: @usableFromInline
// CHECK-NEXT: internal let internalLetFieldFunctionArg: (sending test.NonSendableKlass) -> ()
// CHECK-NEXT: #else
// CHECK-NEXT: @usableFromInline
// CHECK-NEXT: internal let internalLetFieldFunctionArg: (test.NonSendableKlass) -> ()
// CHECK-NEXT: #endif
@usableFromInline
let internalLetFieldFunctionArg: (sending NonSendableKlass) -> ()

// CHECK-LABEL: #if compiler(>=5.3) && $SendingArgsAndResults
// CHECK-NEXT: @usableFromInline
// CHECK-NEXT: internal init(_ x: Swift.Int, transformWithResult: @escaping () async throws -> sending test.NonSendableKlass)
// CHECK-NEXT: #else
// CHECK-NEXT: @usableFromInline
// CHECK-NEXT: internal init(_ x: Swift.Int, transformWithResult: @escaping () async throws -> test.NonSendableKlass)
// CHECK-NEXT: #endif
@usableFromInline
internal init(_ x: Int, transformWithResult: @escaping () async throws -> sending NonSendableKlass) { fatalError() }
}

// Make sure that we emit compiler(>= 5.3) when emitting the suppressing check
// to make sure we do not fail if we fail to parse sending in the if block.

// CHECK-LABEL: #if compiler(>=5.3) && $OptionalIsolatedParameters && $ExpressionMacroDefaultArguments // Suppression Count: 24
// CHECK-NEXT: #if compiler(>=5.3) && $SendingArgsAndResults // Suppression Count: 25
// CHECK-NEXT: @inlinable public func withCheckedContinuation<T>(isolation: isolated (any _Concurrency.Actor)? = #isolation, function: Swift.String = #function, _ body: (_Concurrency.CheckedContinuation<T, Swift.Never>) -> Swift.Void) async -> sending T {
// CHECK-NEXT: fatalError()
// CHECK-NEXT: }
// CHECK-NEXT: #else
// CHECK-NEXT: @inlinable public func withCheckedContinuation<T>(isolation: isolated (any _Concurrency.Actor)? = #isolation, function: Swift.String = #function, _ body: (_Concurrency.CheckedContinuation<T, Swift.Never>) -> Swift.Void) async -> T {
// CHECK-NEXT: fatalError()
// CHECK-NEXT: }
// CHECK-NEXT: #endif
// CHECK-NEXT: #endif
@inlinable public func withCheckedContinuation<T>(
isolation: isolated (any _Concurrency.Actor)? = #isolation,
function: String = #function,
_ body: (_Concurrency.CheckedContinuation<T, Swift.Never>) -> Swift.Void
) async -> sending T {
fatalError()
}

// CHECK-LABEL: #if compiler(>=5.3) && $SendingArgsAndResults // Suppression Count: 26
// CHECK-NEXT: public var publicGlobal: (sending test.NonSendableKlass) -> ()
// CHECK-NEXT: #else
// CHECK-NEXT: public var publicGlobal: (test.NonSendableKlass) -> ()
// CHECK-NEXT: #endif
public var publicGlobal: (sending NonSendableKlass) -> () = { x in fatalError() }

// CHECK-LABEL: #if compiler(>=5.3) && $SendingArgsAndResults // Suppression Count: 27
// CHECK-NEXT: @usableFromInline
// CHECK-NEXT: internal var usableFromInlineGlobal: (sending test.NonSendableKlass) -> ()
// CHECK-NEXT: #else
// CHECK-NEXT: @usableFromInline
// CHECK-NEXT: internal var usableFromInlineGlobal: (test.NonSendableKlass) -> ()
// CHECK-NEXT: #endif
@usableFromInline
internal var usableFromInlineGlobal: (sending NonSendableKlass) -> () = { x in fatalError() }