Skip to content

Commit 2a3c533

Browse files
committed
[SILGen] Consuming a param implies it's eager-move
If a parameter is marked consuming and its type is Copyable, that parameter has eager-move semantics. Apply that attribute to the SILFunctionArgument corresponding to the parameter. rdar://108385761
1 parent 7a1829f commit 2a3c533

File tree

4 files changed

+119
-10
lines changed

4 files changed

+119
-10
lines changed

include/swift/AST/Decl.h

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,14 +1094,7 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
10941094
/// Check if this is a declaration defined at the top level of the Swift module
10951095
bool isStdlibDecl() const;
10961096

1097-
LifetimeAnnotation getLifetimeAnnotation() const {
1098-
auto &attrs = getAttrs();
1099-
if (attrs.hasAttribute<EagerMoveAttr>())
1100-
return LifetimeAnnotation::EagerMove;
1101-
if (attrs.hasAttribute<NoEagerMoveAttr>())
1102-
return LifetimeAnnotation::Lexical;
1103-
return LifetimeAnnotation::None;
1104-
}
1097+
LifetimeAnnotation getLifetimeAnnotation() const;
11051098

11061099
bool isNoImplicitCopy() const {
11071100
return getAttrs().hasAttribute<NoImplicitCopyAttr>();
@@ -6318,6 +6311,8 @@ class ParamDecl : public VarDecl {
63186311
Specifier getSpecifier() const;
63196312
void setSpecifier(Specifier Spec);
63206313

6314+
LifetimeAnnotation getLifetimeAnnotation() const;
6315+
63216316
/// Is the type of this parameter 'inout'?
63226317
bool isInOut() const { return getSpecifier() == Specifier::InOut; }
63236318

@@ -7329,6 +7324,8 @@ class FuncDecl : public AbstractFunctionDecl {
73297324

73307325
SelfAccessKind getSelfAccessKind() const;
73317326

7327+
LifetimeAnnotation getLifetimeAnnotation() const;
7328+
73327329
void setSelfAccessKind(SelfAccessKind mod) {
73337330
Bits.FuncDecl.SelfAccess = static_cast<unsigned>(mod);
73347331
Bits.FuncDecl.SelfAccessComputed = true;

lib/AST/Decl.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,6 +1080,26 @@ bool Decl::isStdlibDecl() const {
10801080
DC->getParentModule()->isStdlibModule();
10811081
}
10821082

1083+
static LifetimeAnnotation
1084+
getLifetimeAnnotationFromAttributes(const Decl *decl) {
1085+
auto &attrs = decl->getAttrs();
1086+
if (attrs.hasAttribute<EagerMoveAttr>())
1087+
return LifetimeAnnotation::EagerMove;
1088+
if (attrs.hasAttribute<NoEagerMoveAttr>())
1089+
return LifetimeAnnotation::Lexical;
1090+
return LifetimeAnnotation::None;
1091+
}
1092+
1093+
LifetimeAnnotation Decl::getLifetimeAnnotation() const {
1094+
if (auto *pd = dyn_cast<ParamDecl>(this)) {
1095+
return pd->getLifetimeAnnotation();
1096+
}
1097+
if (auto *fd = dyn_cast<FuncDecl>(this)) {
1098+
return fd->getLifetimeAnnotation();
1099+
}
1100+
return getLifetimeAnnotationFromAttributes(this);
1101+
}
1102+
10831103
AvailabilityContext Decl::getAvailabilityForLinkage() const {
10841104
ASTContext &ctx = getASTContext();
10851105

@@ -7026,6 +7046,15 @@ ParamDecl::Specifier ParamDecl::getSpecifier() const {
70267046
ParamDecl::Specifier::Default);
70277047
}
70287048

7049+
LifetimeAnnotation ParamDecl::getLifetimeAnnotation() const {
7050+
auto specifier = getSpecifier();
7051+
// Copyable parameters which are consumed have eager-move semantics.
7052+
if (specifier == ParamDecl::Specifier::Consuming &&
7053+
!getType()->isPureMoveOnly())
7054+
return LifetimeAnnotation::EagerMove;
7055+
return getLifetimeAnnotationFromAttributes(this);
7056+
}
7057+
70297058
StringRef ParamDecl::getSpecifierSpelling(ParamSpecifier specifier) {
70307059
switch (specifier) {
70317060
case ParamSpecifier::Default:
@@ -9211,6 +9240,16 @@ SelfAccessKind FuncDecl::getSelfAccessKind() const {
92119240
SelfAccessKind::NonMutating);
92129241
}
92139242

9243+
LifetimeAnnotation FuncDecl::getLifetimeAnnotation() const {
9244+
// Copyable parameters which are consumed have eager-move semantics.
9245+
if (getSelfAccessKind() == SelfAccessKind::Consuming) {
9246+
auto *selfDecl = getImplicitSelfDecl();
9247+
if (selfDecl && !selfDecl->getType()->isPureMoveOnly())
9248+
return LifetimeAnnotation::EagerMove;
9249+
}
9250+
return getLifetimeAnnotationFromAttributes(this);
9251+
}
9252+
92149253
bool FuncDecl::isCallAsFunctionMethod() const {
92159254
return getBaseIdentifier() == getASTContext().Id_callAsFunction &&
92169255
isInstanceMember();

test/SILGen/consuming_parameter.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ func bar(_: String) {}
44

55
// CHECK-LABEL: sil {{.*}} @${{.*}}3foo
66
func foo(y: consuming String, z: String) -> () -> String {
7-
// CHECK: bb0(%0 : @owned $String, %1 : @guaranteed $String):
7+
// CHECK: bb0(%0 : @_eagerMove @owned $String, %1 : @guaranteed $String):
88
// CHECK: [[BOX:%.*]] = alloc_box ${ var String }
99
// CHECK: [[Y:%.*]] = project_box [[BOX]]
1010
// CHECK: store %0 to [init] [[Y]]
@@ -33,7 +33,7 @@ struct Butt {
3333

3434
// CHECK-LABEL: sil {{.*}} @${{.*}}4Butt{{.*}}6merged
3535
consuming func merged(with other: Butt) -> () -> Butt {
36-
// CHECK: bb0(%0 : @guaranteed $Butt, %1 : @owned $Butt):
36+
// CHECK: bb0(%0 : @guaranteed $Butt, %1 : @_eagerMove @owned $Butt):
3737
// CHECK: [[BOX:%.*]] = alloc_box ${ var Butt }
3838
// CHECK: [[SELF:%.*]] = project_box [[BOX]]
3939
// CHECK: store %1 to [init] [[SELF]]
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// RUN: %target-swift-frontend -c -Xllvm --sil-print-final-ossa-module -O -module-name=main -o /dev/null %s 2>&1 | %FileCheck %s
2+
3+
// CHECK-LABEL: sil [ossa] @async_dead_arg_call : {{.*}} {
4+
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @_eagerMove @owned
5+
// CHECK: destroy_value [[INSTANCE]]
6+
// CHECK: [[EXECUTOR:%[^,]+]] = enum $Optional<Builtin.Executor>, #Optional.none!enumelt
7+
// CHECK: [[CALLEE:%[^,]+]] = function_ref @async_callee
8+
// CHECK: apply [[CALLEE]]()
9+
// CHECK: hop_to_executor [[EXECUTOR]]
10+
// CHECK-LABEL: } // end sil function 'async_dead_arg_call'
11+
@_silgen_name("async_dead_arg_call")
12+
public func async_dead_arg_call(o: consuming AnyObject) async {
13+
// o should be destroyed here
14+
await bar()
15+
}
16+
17+
public class C {
18+
// CHECK-LABEL: sil [ossa] @async_dead_arg_call_method : {{.*}} {
19+
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @_eagerMove @owned
20+
// CHECK: destroy_value [[INSTANCE]]
21+
// CHECK: [[EXECUTOR:%[^,]+]] = enum $Optional<Builtin.Executor>, #Optional.none!enumelt
22+
// CHECK: [[CALLEE:%[^,]+]] = function_ref @async_callee : $@convention(thin) @async () -> ()
23+
// CHECK: apply [[CALLEE]]() : $@convention(thin) @async () -> ()
24+
// CHECK: hop_to_executor [[EXECUTOR]]
25+
// CHECK-LABEL: } // end sil function 'async_dead_arg_call_method'
26+
@_silgen_name("async_dead_arg_call_method")
27+
consuming
28+
public func async_dead_arg_call() async {
29+
// self should be destroyed here
30+
await bar()
31+
}
32+
}
33+
34+
@inline(never)
35+
@_silgen_name("async_callee")
36+
func bar() async {}
37+
38+
// CHECK-LABEL: sil [ossa] @write_to_pointer : {{.*}} {
39+
// CHECK: {{bb[0-9]+}}([[CONSUMED_INSTANCE:%[^,]+]] : @_eagerMove @owned $AnyObject, [[UMP:%[^,]+]] :
40+
// CHECK: [[PTR:%[^,]+]] = struct_extract [[UMP]]
41+
// CHECK: [[ADDR:%[^,]+]] = pointer_to_address [[PTR]]
42+
// CHECK: store [[CONSUMED_INSTANCE]] to [assign] [[ADDR]]
43+
// CHECK: [[PTR2:%[^,]+]] = struct_extract [[UMP]]
44+
// CHECK: [[ADDR2:%[^,]+]] = pointer_to_address [[PTR2]]
45+
// CHECK: [[OUT:%[^,]+]] = load [copy] [[ADDR2]]
46+
// CHECK: return [[OUT]]
47+
// CHECK-LABEL: } // end sil function 'write_to_pointer'
48+
@_silgen_name("write_to_pointer")
49+
public func write_to_pointer(o: consuming AnyObject, p: UnsafeMutablePointer<AnyObject>) -> AnyObject {
50+
// o should be destroyed here
51+
p.pointee = o
52+
return p.pointee
53+
}
54+
55+
extension C {
56+
// CHECK-LABEL: sil [ossa] @write_to_pointer_method : {{.*}} {
57+
// CHECK: {{bb[0-9]+}}([[UMP:%[^,]+]] : $UnsafeMutablePointer<C>, [[INSTANCE:%[^,]+]] : @_eagerMove @owned
58+
// CHECK: [[PTR:%[^,]+]] = struct_extract [[UMP]]
59+
// CHECK: [[ADDR:%[^,]+]] = pointer_to_address [[PTR]]
60+
// CHECK: store [[INSTANCE]] to [assign] [[ADDR]]
61+
// CHECK: [[ADDR2:%[^,]+]] = struct_extract [[UMP]]
62+
// CHECK: [[PTR2:%[^,]+]] = pointer_to_address [[ADDR2]]
63+
// CHECK: [[OUT:%[^,]+]] = load [copy] [[PTR2]]
64+
// CHECK: return [[OUT]]
65+
// CHECK-LABEL: } // end sil function 'write_to_pointer_method'
66+
@_silgen_name("write_to_pointer_method")
67+
consuming
68+
public func write_to_pointer(p: UnsafeMutablePointer<C>) -> C {
69+
// o should be destroyed here
70+
p.pointee = self
71+
return p.pointee
72+
}
73+
}

0 commit comments

Comments
 (0)