Skip to content

[sending] Look through the sending type repr when printing the type of a function result using the type repr fallback path. #76491

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 1 commit into from
Sep 18, 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
31 changes: 29 additions & 2 deletions lib/AST/ASTPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4127,8 +4127,35 @@ void PrintAST::visitFuncDecl(FuncDecl *decl) {
}

PrintWithOpaqueResultTypeKeywordRAII x(Options);
printTypeLocForImplicitlyUnwrappedOptional(
ResultTyLoc, decl->isImplicitlyUnwrappedOptional());

// Check if we would go down the type repr path... in such a case, see if
// we can find a type repr and if that type has a sending type repr. In
// such a case, look through the sending type repr since we handle it here
// ourselves.
bool usedTypeReprPrinting = false;
{
llvm::SaveAndRestore<PrintOptions> printOptions(Options);
Options.PrintOptionalAsImplicitlyUnwrapped =
decl->isImplicitlyUnwrappedOptional();
if (willUseTypeReprPrinting(ResultTyLoc, CurrentType, Options)) {
if (auto repr = ResultTyLoc.getTypeRepr()) {
// If we are printing a sending result... and we found that we have
// to use type repr printing, look through sending type repr.
// Sending was already applied in our caller.
if (auto *sendingRepr = dyn_cast<SendingTypeRepr>(repr)) {
repr = sendingRepr->getBase();
}
repr->print(Printer, Options);
usedTypeReprPrinting = true;
}
}
}

// If we printed using type repr printing, do not print again.
if (!usedTypeReprPrinting) {
printTypeLocForImplicitlyUnwrappedOptional(
ResultTyLoc, decl->isImplicitlyUnwrappedOptional());
}
Printer.printStructurePost(PrintStructureKind::FunctionReturnType);
}
printDeclGenericRequirements(decl);
Expand Down
24 changes: 22 additions & 2 deletions lib/AST/TypeRepr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,21 @@ void FunctionTypeRepr::printImpl(ASTPrinter &Printer,
}
Printer << " -> ";
Printer.callPrintStructurePre(PrintStructureKind::FunctionReturnType);
printTypeRepr(RetTy, Printer, Opts);

// Check if we are supposed to suppress sending results. If so, look through
// the ret ty if it is a Sending TypeRepr.
//
// DISCUSSION: The reason why we do this is that Sending TypeRepr is used for
// arguments and results... and we need the arguments case when we suppress to
// print __owned. So this lets us handle both cases.
auto ActualRetTy = RetTy;
if (Opts.SuppressSendingArgsAndResults) {
if (auto *x = dyn_cast<SendingTypeRepr>(RetTy)) {
ActualRetTy = x->getBase();
}
}
printTypeRepr(ActualRetTy, Printer, Opts);

Printer.printStructurePost(PrintStructureKind::FunctionReturnType);
Printer.printStructurePost(PrintStructureKind::FunctionType);
}
Expand Down Expand Up @@ -853,7 +867,13 @@ void SpecifierTypeRepr::printImpl(ASTPrinter &Printer,
Printer.printKeyword("isolated", Opts, " ");
break;
case TypeReprKind::Sending:
Printer.printKeyword("sending", Opts, " ");
// This handles the argument case. The result case is handled in
// FunctionTypeRepr.
if (!Opts.SuppressSendingArgsAndResults) {
Printer.printKeyword("sending", Opts, " ");
} else {
Printer.printKeyword("__owned", Opts, " ");
}
break;
case TypeReprKind::CompileTimeConst:
Printer.printKeyword("_const", Opts, " ");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@

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

// CHECK: #if compiler(>=5.3) && $SendingArgsAndResults
// CHECK-NEXT: public func test2(_ x: sending NonSendableKlass)
// CHECK-NEXT: #else
// CHECK-NEXT: public func test2(_ x: __owned NonSendableKlass)
// CHECK-NEXT: #endif

// CHECK: #if compiler(>=5.3) && $SendingArgsAndResults
// CHECK-NEXT: @_Concurrency.MainActor public var closure: () -> sending NonSendableKlass
// CHECK-NEXT: #else
// CHECK-NEXT: @_Concurrency.MainActor public var closure: () -> NonSendableKlass
// CHECK-NEXT: #endif
36 changes: 36 additions & 0 deletions test/Concurrency/sending_interfacefile_printing.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// RUN: %empty-directory(%t)
// RUN: %target-swift-frontend %s -typecheck -enable-library-evolution -parse-as-library -emit-module-interface-path - -module-name MyFile -swift-version 6 | %FileCheck %s
// RUN: %target-swift-frontend %s -typecheck -enable-library-evolution -parse-as-library -emit-module-interface-path - -module-name MyFile -swift-version 6 -module-interface-preserve-types-as-written | %FileCheck %S/Inputs/sending_interfacefile_printing_repr_filecheck

// The force printing type reprs option is only available in asserts builds.
// REQUIRES: asserts

// This test validates that when we produce interface files we produce the
// correct interface file for sending when printing normally or with type reprs
// enabled.

public class NonSendableKlass {}

// The two possible outputs are MyFile.NonSendableKlass and NonSendableKlass.
//
// So we just check for an optional M
// CHECK: #if compiler(>=5.3) && $SendingArgsAndResults
// CHECK-NEXT: public func test() -> sending MyFile.NonSendableKlass
// CHECK-NEXT: #else
// CHECK-NEXT: public func test() -> MyFile.NonSendableKlass
// CHECK-NEXT: #endif
public func test() -> sending NonSendableKlass { NonSendableKlass() }

// CHECK: #if compiler(>=5.3) && $SendingArgsAndResults
// CHECK-NEXT: public func test2(_ x: sending MyFile.NonSendableKlass)
// CHECK-NEXT: #else
// CHECK-NEXT: public func test2(_ x: __owned MyFile.NonSendableKlass)
// CHECK-NEXT: #endif
public func test2(_ x: sending NonSendableKlass) {}

// CHECK: #if compiler(>=5.3) && $SendingArgsAndResults
// CHECK-NEXT: @_Concurrency.MainActor public var closure: () -> sending MyFile.NonSendableKlass
// CHECK-NEXT: #else
// CHECK-NEXT: @_Concurrency.MainActor public var closure: () -> MyFile.NonSendableKlass
// CHECK-NEXT: #endif
@MainActor public var closure: () -> sending NonSendableKlass = { NonSendableKlass() }