Skip to content

Commit abba5ef

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 abba5ef

File tree

5 files changed

+133
-11
lines changed

5 files changed

+133
-11
lines changed

include/swift/AST/Decl.h

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,14 +1094,19 @@ 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+
/// The effective lifetime resulting from the decorations on the declaration.
1098+
///
1099+
/// Usually, this, not getLifetimeAnnotationFromAttributes should be used.
1100+
LifetimeAnnotation getLifetimeAnnotation() const;
1101+
1102+
/// The source-level lifetime attribute, either @_eagerMove or @_noEagerMove
1103+
/// that the declaration bears.
1104+
///
1105+
/// Usually getLifetimeAnnotation should be used.
1106+
///
1107+
/// Needed to access the attributes before the AST has been fully formed, such
1108+
/// as when printing.
1109+
LifetimeAnnotation getLifetimeAnnotationFromAttributes() const;
11051110

11061111
bool isNoImplicitCopy() const {
11071112
return getAttrs().hasAttribute<NoImplicitCopyAttr>();
@@ -6318,6 +6323,8 @@ class ParamDecl : public VarDecl {
63186323
Specifier getSpecifier() const;
63196324
void setSpecifier(Specifier Spec);
63206325

6326+
LifetimeAnnotation getLifetimeAnnotation() const;
6327+
63216328
/// Is the type of this parameter 'inout'?
63226329
bool isInOut() const { return getSpecifier() == Specifier::InOut; }
63236330

@@ -7329,6 +7336,8 @@ class FuncDecl : public AbstractFunctionDecl {
73297336

73307337
SelfAccessKind getSelfAccessKind() const;
73317338

7339+
LifetimeAnnotation getLifetimeAnnotation() const;
7340+
73327341
void setSelfAccessKind(SelfAccessKind mod) {
73337342
Bits.FuncDecl.SelfAccess = static_cast<unsigned>(mod);
73347343
Bits.FuncDecl.SelfAccessComputed = true;

lib/AST/ASTDumper.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1030,7 +1030,7 @@ namespace {
10301030
if (P->getAttrs().hasAttribute<NonEphemeralAttr>())
10311031
OS << " nonEphemeral";
10321032

1033-
switch (P->getLifetimeAnnotation()) {
1033+
switch (P->getLifetimeAnnotationFromAttributes()) {
10341034
case LifetimeAnnotation::EagerMove:
10351035
OS << " _eagerMove";
10361036
break;

lib/AST/Decl.cpp

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

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

@@ -7026,6 +7045,15 @@ ParamDecl::Specifier ParamDecl::getSpecifier() const {
70267045
ParamDecl::Specifier::Default);
70277046
}
70287047

7048+
LifetimeAnnotation ParamDecl::getLifetimeAnnotation() const {
7049+
auto specifier = getSpecifier();
7050+
// Copyable parameters which are consumed have eager-move semantics.
7051+
if (specifier == ParamDecl::Specifier::Consuming &&
7052+
!getType()->isPureMoveOnly())
7053+
return LifetimeAnnotation::EagerMove;
7054+
return getLifetimeAnnotationFromAttributes();
7055+
}
7056+
70297057
StringRef ParamDecl::getSpecifierSpelling(ParamSpecifier specifier) {
70307058
switch (specifier) {
70317059
case ParamSpecifier::Default:
@@ -9211,6 +9239,16 @@ SelfAccessKind FuncDecl::getSelfAccessKind() const {
92119239
SelfAccessKind::NonMutating);
92129240
}
92139241

9242+
LifetimeAnnotation FuncDecl::getLifetimeAnnotation() const {
9243+
// Copyable parameters which are consumed have eager-move semantics.
9244+
if (getSelfAccessKind() == SelfAccessKind::Consuming) {
9245+
auto *selfDecl = getImplicitSelfDecl();
9246+
if (selfDecl && !selfDecl->getType()->isPureMoveOnly())
9247+
return LifetimeAnnotation::EagerMove;
9248+
}
9249+
return getLifetimeAnnotationFromAttributes();
9250+
}
9251+
92149252
bool FuncDecl::isCallAsFunctionMethod() const {
92159253
return getBaseIdentifier() == getASTContext().Id_callAsFunction &&
92169254
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: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// RUN: %target-swift-frontend -c -disable-availability-checking -Xllvm --sil-print-final-ossa-module -O -module-name=main -o /dev/null %s 2>&1 | %FileCheck %s
2+
3+
// REQUIRES: concurrency
4+
5+
// CHECK-LABEL: sil [ossa] @async_dead_arg_call : {{.*}} {
6+
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @_eagerMove @owned
7+
// CHECK: destroy_value [[INSTANCE]]
8+
// CHECK: [[EXECUTOR:%[^,]+]] = enum $Optional<Builtin.Executor>, #Optional.none!enumelt
9+
// CHECK: [[CALLEE:%[^,]+]] = function_ref @async_callee
10+
// CHECK: apply [[CALLEE]]()
11+
// CHECK: hop_to_executor [[EXECUTOR]]
12+
// CHECK-LABEL: } // end sil function 'async_dead_arg_call'
13+
@_silgen_name("async_dead_arg_call")
14+
public func async_dead_arg_call(o: consuming AnyObject) async {
15+
// o should be destroyed here
16+
await bar()
17+
}
18+
19+
public class C {
20+
// CHECK-LABEL: sil [ossa] @async_dead_arg_call_method : {{.*}} {
21+
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @_eagerMove @owned
22+
// CHECK: destroy_value [[INSTANCE]]
23+
// CHECK: [[EXECUTOR:%[^,]+]] = enum $Optional<Builtin.Executor>, #Optional.none!enumelt
24+
// CHECK: [[CALLEE:%[^,]+]] = function_ref @async_callee : $@convention(thin) @async () -> ()
25+
// CHECK: apply [[CALLEE]]() : $@convention(thin) @async () -> ()
26+
// CHECK: hop_to_executor [[EXECUTOR]]
27+
// CHECK-LABEL: } // end sil function 'async_dead_arg_call_method'
28+
@_silgen_name("async_dead_arg_call_method")
29+
consuming
30+
public func async_dead_arg_call() async {
31+
// self should be destroyed here
32+
await bar()
33+
}
34+
}
35+
36+
@inline(never)
37+
@_silgen_name("async_callee")
38+
func bar() async {}
39+
40+
// CHECK-LABEL: sil [ossa] @write_to_pointer : {{.*}} {
41+
// CHECK: {{bb[0-9]+}}([[CONSUMED_INSTANCE:%[^,]+]] : @_eagerMove @owned $AnyObject, [[UMP:%[^,]+]] :
42+
// CHECK: [[PTR:%[^,]+]] = struct_extract [[UMP]]
43+
// CHECK: [[ADDR:%[^,]+]] = pointer_to_address [[PTR]]
44+
// CHECK: store [[CONSUMED_INSTANCE]] to [assign] [[ADDR]]
45+
// CHECK: [[PTR2:%[^,]+]] = struct_extract [[UMP]]
46+
// CHECK: [[ADDR2:%[^,]+]] = pointer_to_address [[PTR2]]
47+
// CHECK: [[OUT:%[^,]+]] = load [copy] [[ADDR2]]
48+
// CHECK: return [[OUT]]
49+
// CHECK-LABEL: } // end sil function 'write_to_pointer'
50+
@_silgen_name("write_to_pointer")
51+
public func write_to_pointer(o: consuming AnyObject, p: UnsafeMutablePointer<AnyObject>) -> AnyObject {
52+
// o should be destroyed here
53+
p.pointee = o
54+
return p.pointee
55+
}
56+
57+
extension C {
58+
// CHECK-LABEL: sil [ossa] @write_to_pointer_method : {{.*}} {
59+
// CHECK: {{bb[0-9]+}}([[UMP:%[^,]+]] : $UnsafeMutablePointer<C>, [[INSTANCE:%[^,]+]] : @_eagerMove @owned
60+
// CHECK: [[PTR:%[^,]+]] = struct_extract [[UMP]]
61+
// CHECK: [[ADDR:%[^,]+]] = pointer_to_address [[PTR]]
62+
// CHECK: store [[INSTANCE]] to [assign] [[ADDR]]
63+
// CHECK: [[ADDR2:%[^,]+]] = struct_extract [[UMP]]
64+
// CHECK: [[PTR2:%[^,]+]] = pointer_to_address [[ADDR2]]
65+
// CHECK: [[OUT:%[^,]+]] = load [copy] [[PTR2]]
66+
// CHECK: return [[OUT]]
67+
// CHECK-LABEL: } // end sil function 'write_to_pointer_method'
68+
@_silgen_name("write_to_pointer_method")
69+
consuming
70+
public func write_to_pointer(p: UnsafeMutablePointer<C>) -> C {
71+
// o should be destroyed here
72+
p.pointee = self
73+
return p.pointee
74+
}
75+
}

0 commit comments

Comments
 (0)