Skip to content

Commit 1383a63

Browse files
authored
Merge pull request #28527 from slavapestov/owned-block-params
Fixes for __attribute__((ns_consumed)) block parameters
2 parents 0dc9a0e + eef1428 commit 1383a63

File tree

8 files changed

+78
-4
lines changed

8 files changed

+78
-4
lines changed

lib/IRGen/GenClangType.cpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,10 @@ clang::CanQualType GenClangType::visitSILFunctionType(CanSILFunctionType type) {
585585
}
586586

587587
SmallVector<clang::QualType, 4> paramTypes;
588+
SmallVector<clang::FunctionProtoType::ExtParameterInfo, 4> extParamInfos;
588589
for (auto paramTy : type->getParameters()) {
590+
clang::FunctionProtoType::ExtParameterInfo extParamInfo;
591+
589592
// Blocks should only take direct +0 parameters.
590593
switch (paramTy.getConvention()) {
591594
case ParameterConvention::Direct_Guaranteed:
@@ -594,7 +597,9 @@ clang::CanQualType GenClangType::visitSILFunctionType(CanSILFunctionType type) {
594597
break;
595598

596599
case ParameterConvention::Direct_Owned:
597-
llvm_unreachable("block takes owned parameter");
600+
extParamInfo = extParamInfo.withIsConsumed(true);
601+
break;
602+
598603
case ParameterConvention::Indirect_In:
599604
case ParameterConvention::Indirect_In_Constant:
600605
case ParameterConvention::Indirect_Inout:
@@ -606,12 +611,16 @@ clang::CanQualType GenClangType::visitSILFunctionType(CanSILFunctionType type) {
606611
paramTy.getArgumentType(IGM.getSILModule(), type));
607612
if (param.isNull())
608613
return clang::CanQualType();
614+
609615
paramTypes.push_back(param);
616+
extParamInfos.push_back(extParamInfo);
610617
}
611618

612619
// Build the Clang function type.
613-
clang::FunctionProtoType::ExtProtoInfo defaultEPI;
614-
auto fnTy = clangCtx.getFunctionType(resultType, paramTypes, defaultEPI);
620+
clang::FunctionProtoType::ExtProtoInfo extProtoInfo;
621+
extProtoInfo.ExtParameterInfos = extParamInfos.begin();
622+
623+
auto fnTy = clangCtx.getFunctionType(resultType, paramTypes, extProtoInfo);
615624
clang::QualType ptrTy;
616625

617626
switch (kind) {

lib/PrintAsObjC/DeclAndTypePrinter.cpp

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1934,7 +1934,18 @@ class DeclAndTypePrinter::Implementation
19341934
if (!FT->getParams().empty()) {
19351935
interleave(FT->getParams(),
19361936
[this](const AnyFunctionType::Param &param) {
1937-
print(param.getOldType(), OTK_None, param.getLabel(),
1937+
switch (param.getValueOwnership()) {
1938+
case ValueOwnership::Default:
1939+
case ValueOwnership::Shared:
1940+
break;
1941+
case ValueOwnership::Owned:
1942+
os << "SWIFT_RELEASES_ARGUMENT ";
1943+
break;
1944+
case ValueOwnership::InOut:
1945+
llvm_unreachable("bad specifier");
1946+
}
1947+
1948+
print(param.getParameterType(), OTK_None, param.getLabel(),
19381949
IsFunctionParam);
19391950
},
19401951
[this] { os << ", "; });

lib/PrintAsObjC/PrintAsObjC.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ static void writePrologue(raw_ostream &out, ASTContext &ctx) {
112112
"#else\n"
113113
"# define SWIFT_NOESCAPE\n"
114114
"#endif\n"
115+
"#if __has_attribute(ns_consumed)\n"
116+
"# define SWIFT_RELEASES_ARGUMENT __attribute__((ns_consumed))\n"
117+
"#else\n"
118+
"# define SWIFT_RELEASES_ARGUMENT\n"
119+
"#endif\n"
115120
"#if __has_attribute(warn_unused_result)\n"
116121
"# define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result))\n"
117122
"#else\n"

test/IRGen/Inputs/usr/include/Gizmo.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ typedef long NSInteger;
4545
- (void) setFrame: (struct NSRect) rect;
4646
- (void) frob;
4747
- (void) test: (struct Fob) fob;
48+
- (void) perform: (void (^)(NS_CONSUMED Gizmo*)) block;
4849
+ (void) runce;
4950
@end
5051

test/IRGen/objc_block_consumed.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
// RUN: %empty-directory(%t)
3+
// RUN: %build-irgen-test-overlays
4+
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -primary-file %s -emit-ir -disable-objc-attr-requires-foundation-module
5+
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -primary-file %s -emit-silgen -disable-objc-attr-requires-foundation-module | %FileCheck %s
6+
7+
// We want to test that IRGen doesn't assert on this code. SIL is the best place
8+
// to file check that the block parameter is actually +1.
9+
10+
// REQUIRES: CPU=x86_64
11+
// REQUIRES: objc_interop
12+
13+
import gizmo
14+
15+
// CHECK-LABEL: sil hidden [ossa] @$s19objc_block_consumed24passBlockWithConsumedArgyySo5GizmoC_ADtF : $@convention(thin) (@guaranteed Gizmo, @guaranteed Gizmo) -> () {
16+
func passBlockWithConsumedArg(_ g: Gizmo, _ other: Gizmo) {
17+
// CHECK: objc_method %0 : $Gizmo, #Gizmo.perform!1.foreign : (Gizmo) -> (((Gizmo?) -> ())?) -> (), $@convention(objc_method) (Optional<@convention(block) (@owned Optional<Gizmo>) -> ()>, Gizmo) -> ()
18+
g.perform { other in }
19+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#import <Foundation/Foundation.h>
2+
3+
static inline void takesBlockWithConsumedArg(void (^ block)(NS_RELEASES_ARGUMENT NSObject *x), NSObject *x) {
4+
block(x);
5+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift %s -import-objc-header %S/Inputs/objc_block_consumed.h -o %t/main
3+
// RUN: %target-run %t/main
4+
5+
// REQUIRES: executable_test
6+
// REQUIRES: objc_interop
7+
8+
import Foundation
9+
import StdlibUnittest
10+
11+
class C : NSObject {
12+
var tracked = LifetimeTracked(0)
13+
}
14+
15+
var ObjCBlockConsumedTestSuite = TestSuite("ObjCBlockConsumed")
16+
17+
ObjCBlockConsumedTestSuite.test("Test") {
18+
takesBlockWithConsumedArg({ arg in }, C())
19+
}
20+
21+
runAllTests()

test/PrintAsObjC/blocks.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ typealias MyBlockWithNoescapeParam = (() -> ()) -> Int
113113
return input
114114
}
115115

116+
// CHECK-NEXT: - (void)blockWithConsumingArgument:(void (^ _Nonnull)(SWIFT_RELEASES_ARGUMENT NSObject * _Nonnull))block;
117+
@objc func blockWithConsumingArgument(_ block: @escaping (__owned NSObject) -> ()) {}
118+
116119
// CHECK-NEXT: @property (nonatomic, copy) NSInteger (^ _Nullable savedBlock)(NSInteger);
117120
@objc var savedBlock: ((Int) -> Int)?
118121

0 commit comments

Comments
 (0)