From 6d5ff209d0ac310b62f873a704a25e41030b8836 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 2 Dec 2019 17:56:19 -0500 Subject: [PATCH 1/2] IRGen: Implement support for __attribute__((ns_consumed)) block parameters Fixes . --- lib/IRGen/GenClangType.cpp | 15 ++++++++++--- test/IRGen/Inputs/usr/include/Gizmo.h | 1 + test/IRGen/objc_block_consumed.swift | 19 +++++++++++++++++ .../SDK/Inputs/objc_block_consumed.h | 5 +++++ .../Interpreter/SDK/objc_block_consumed.swift | 21 +++++++++++++++++++ 5 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 test/IRGen/objc_block_consumed.swift create mode 100644 test/Interpreter/SDK/Inputs/objc_block_consumed.h create mode 100644 test/Interpreter/SDK/objc_block_consumed.swift diff --git a/lib/IRGen/GenClangType.cpp b/lib/IRGen/GenClangType.cpp index 14beaa4bce220..43bd92c18afbf 100644 --- a/lib/IRGen/GenClangType.cpp +++ b/lib/IRGen/GenClangType.cpp @@ -585,7 +585,10 @@ clang::CanQualType GenClangType::visitSILFunctionType(CanSILFunctionType type) { } SmallVector paramTypes; + SmallVector extParamInfos; for (auto paramTy : type->getParameters()) { + clang::FunctionProtoType::ExtParameterInfo extParamInfo; + // Blocks should only take direct +0 parameters. switch (paramTy.getConvention()) { case ParameterConvention::Direct_Guaranteed: @@ -594,7 +597,9 @@ clang::CanQualType GenClangType::visitSILFunctionType(CanSILFunctionType type) { break; case ParameterConvention::Direct_Owned: - llvm_unreachable("block takes owned parameter"); + extParamInfo = extParamInfo.withIsConsumed(true); + break; + case ParameterConvention::Indirect_In: case ParameterConvention::Indirect_In_Constant: case ParameterConvention::Indirect_Inout: @@ -606,12 +611,16 @@ clang::CanQualType GenClangType::visitSILFunctionType(CanSILFunctionType type) { paramTy.getArgumentType(IGM.getSILModule(), type)); if (param.isNull()) return clang::CanQualType(); + paramTypes.push_back(param); + extParamInfos.push_back(extParamInfo); } // Build the Clang function type. - clang::FunctionProtoType::ExtProtoInfo defaultEPI; - auto fnTy = clangCtx.getFunctionType(resultType, paramTypes, defaultEPI); + clang::FunctionProtoType::ExtProtoInfo extProtoInfo; + extProtoInfo.ExtParameterInfos = extParamInfos.begin(); + + auto fnTy = clangCtx.getFunctionType(resultType, paramTypes, extProtoInfo); clang::QualType ptrTy; switch (kind) { diff --git a/test/IRGen/Inputs/usr/include/Gizmo.h b/test/IRGen/Inputs/usr/include/Gizmo.h index d741d042ca29a..474f15047b0ce 100644 --- a/test/IRGen/Inputs/usr/include/Gizmo.h +++ b/test/IRGen/Inputs/usr/include/Gizmo.h @@ -45,6 +45,7 @@ typedef long NSInteger; - (void) setFrame: (struct NSRect) rect; - (void) frob; - (void) test: (struct Fob) fob; +- (void) perform: (void (^)(NS_CONSUMED Gizmo*)) block; + (void) runce; @end diff --git a/test/IRGen/objc_block_consumed.swift b/test/IRGen/objc_block_consumed.swift new file mode 100644 index 0000000000000..8fc68c2a31346 --- /dev/null +++ b/test/IRGen/objc_block_consumed.swift @@ -0,0 +1,19 @@ + +// RUN: %empty-directory(%t) +// RUN: %build-irgen-test-overlays +// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -primary-file %s -emit-ir -disable-objc-attr-requires-foundation-module +// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -primary-file %s -emit-silgen -disable-objc-attr-requires-foundation-module | %FileCheck %s + +// We want to test that IRGen doesn't assert on this code. SIL is the best place +// to file check that the block parameter is actually +1. + +// REQUIRES: CPU=x86_64 +// REQUIRES: objc_interop + +import gizmo + +// CHECK-LABEL: sil hidden [ossa] @$s19objc_block_consumed24passBlockWithConsumedArgyySo5GizmoC_ADtF : $@convention(thin) (@guaranteed Gizmo, @guaranteed Gizmo) -> () { +func passBlockWithConsumedArg(_ g: Gizmo, _ other: Gizmo) { + // CHECK: objc_method %0 : $Gizmo, #Gizmo.perform!1.foreign : (Gizmo) -> (((Gizmo?) -> ())?) -> (), $@convention(objc_method) (Optional<@convention(block) (@owned Optional) -> ()>, Gizmo) -> () + g.perform { other in } +} diff --git a/test/Interpreter/SDK/Inputs/objc_block_consumed.h b/test/Interpreter/SDK/Inputs/objc_block_consumed.h new file mode 100644 index 0000000000000..b6a907dcfc324 --- /dev/null +++ b/test/Interpreter/SDK/Inputs/objc_block_consumed.h @@ -0,0 +1,5 @@ +#import + +static inline void takesBlockWithConsumedArg(void (^ block)(NS_RELEASES_ARGUMENT NSObject *x), NSObject *x) { + block(x); +} diff --git a/test/Interpreter/SDK/objc_block_consumed.swift b/test/Interpreter/SDK/objc_block_consumed.swift new file mode 100644 index 0000000000000..bee0890a639d6 --- /dev/null +++ b/test/Interpreter/SDK/objc_block_consumed.swift @@ -0,0 +1,21 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -import-objc-header %S/Inputs/objc_block_consumed.h -o %t/main +// RUN: %target-run %t/main + +// REQUIRES: executable_test +// REQUIRES: objc_interop + +import Foundation +import StdlibUnittest + +class C : NSObject { + var tracked = LifetimeTracked(0) +} + +var ObjCBlockConsumedTestSuite = TestSuite("ObjCBlockConsumed") + +ObjCBlockConsumedTestSuite.test("Test") { + takesBlockWithConsumedArg({ arg in }, C()) +} + +runAllTests() From eef1428a2b0cdff7ff862f2ef7860c5a8044c8f7 Mon Sep 17 00:00:00 2001 From: Slava Pestov Date: Mon, 2 Dec 2019 18:41:10 -0500 Subject: [PATCH 2/2] PrintAsObjC: Support for blocks with __owned parameters --- lib/PrintAsObjC/DeclAndTypePrinter.cpp | 13 ++++++++++++- lib/PrintAsObjC/PrintAsObjC.cpp | 5 +++++ test/PrintAsObjC/blocks.swift | 3 +++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lib/PrintAsObjC/DeclAndTypePrinter.cpp b/lib/PrintAsObjC/DeclAndTypePrinter.cpp index 240e83d8a2ff8..66541f1df637d 100644 --- a/lib/PrintAsObjC/DeclAndTypePrinter.cpp +++ b/lib/PrintAsObjC/DeclAndTypePrinter.cpp @@ -1934,7 +1934,18 @@ class DeclAndTypePrinter::Implementation if (!FT->getParams().empty()) { interleave(FT->getParams(), [this](const AnyFunctionType::Param ¶m) { - print(param.getOldType(), OTK_None, param.getLabel(), + switch (param.getValueOwnership()) { + case ValueOwnership::Default: + case ValueOwnership::Shared: + break; + case ValueOwnership::Owned: + os << "SWIFT_RELEASES_ARGUMENT "; + break; + case ValueOwnership::InOut: + llvm_unreachable("bad specifier"); + } + + print(param.getParameterType(), OTK_None, param.getLabel(), IsFunctionParam); }, [this] { os << ", "; }); diff --git a/lib/PrintAsObjC/PrintAsObjC.cpp b/lib/PrintAsObjC/PrintAsObjC.cpp index b9e441ee16375..23187675fa1a6 100644 --- a/lib/PrintAsObjC/PrintAsObjC.cpp +++ b/lib/PrintAsObjC/PrintAsObjC.cpp @@ -112,6 +112,11 @@ static void writePrologue(raw_ostream &out, ASTContext &ctx) { "#else\n" "# define SWIFT_NOESCAPE\n" "#endif\n" + "#if __has_attribute(ns_consumed)\n" + "# define SWIFT_RELEASES_ARGUMENT __attribute__((ns_consumed))\n" + "#else\n" + "# define SWIFT_RELEASES_ARGUMENT\n" + "#endif\n" "#if __has_attribute(warn_unused_result)\n" "# define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result))\n" "#else\n" diff --git a/test/PrintAsObjC/blocks.swift b/test/PrintAsObjC/blocks.swift index 0a8a0c80b09a4..f7d355493d713 100644 --- a/test/PrintAsObjC/blocks.swift +++ b/test/PrintAsObjC/blocks.swift @@ -113,6 +113,9 @@ typealias MyBlockWithNoescapeParam = (() -> ()) -> Int return input } + // CHECK-NEXT: - (void)blockWithConsumingArgument:(void (^ _Nonnull)(SWIFT_RELEASES_ARGUMENT NSObject * _Nonnull))block; + @objc func blockWithConsumingArgument(_ block: @escaping (__owned NSObject) -> ()) {} + // CHECK-NEXT: @property (nonatomic, copy) NSInteger (^ _Nullable savedBlock)(NSInteger); @objc var savedBlock: ((Int) -> Int)?