Skip to content

Commit b2bac74

Browse files
Merge pull request #65379 from nate-chandler/cherrypick/release/5.9/rdar108385761
5.9: [SILGen] Consuming a param implies it's eager-move.
2 parents afa80d9 + 9f358c4 commit b2bac74

File tree

8 files changed

+205
-17
lines changed

8 files changed

+205
-17
lines changed

include/swift/AST/Decl.h

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

1101-
LifetimeAnnotation getLifetimeAnnotation() const {
1102-
auto &attrs = getAttrs();
1103-
if (attrs.hasAttribute<EagerMoveAttr>())
1104-
return LifetimeAnnotation::EagerMove;
1105-
if (attrs.hasAttribute<NoEagerMoveAttr>())
1106-
return LifetimeAnnotation::Lexical;
1107-
return LifetimeAnnotation::None;
1108-
}
1101+
/// The effective lifetime resulting from the decorations on the declaration.
1102+
///
1103+
/// Usually, this, not getLifetimeAnnotationFromAttributes should be used.
1104+
LifetimeAnnotation getLifetimeAnnotation() const;
1105+
1106+
/// The source-level lifetime attribute, either @_eagerMove or @_noEagerMove
1107+
/// that the declaration bears.
1108+
///
1109+
/// Usually getLifetimeAnnotation should be used.
1110+
///
1111+
/// Needed to access the attributes before the AST has been fully formed, such
1112+
/// as when printing.
1113+
LifetimeAnnotation getLifetimeAnnotationFromAttributes() const;
11091114

11101115
bool isNoImplicitCopy() const {
11111116
return getAttrs().hasAttribute<NoImplicitCopyAttr>();
@@ -6317,6 +6322,8 @@ class ParamDecl : public VarDecl {
63176322
Specifier getSpecifier() const;
63186323
void setSpecifier(Specifier Spec);
63196324

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

@@ -7328,6 +7335,8 @@ class FuncDecl : public AbstractFunctionDecl {
73287335

73297336
SelfAccessKind getSelfAccessKind() const;
73307337

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

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3526,6 +3526,8 @@ ERROR(lifetime_invalid_global_scope,none, "%0 is only valid on methods",
35263526
ERROR(eagermove_and_lexical_combined,none,
35273527
"@_eagerMove and @_noEagerMove attributes are alternate styles of lifetimes "
35283528
"and can't be combined", ())
3529+
ERROR(eagermove_and_noncopyable_combined,none,
3530+
"@_eagerMove cannot be applied to NonCopyable types", ())
35293531

35303532
ERROR(autoclosure_function_type,none,
35313533
"@autoclosure attribute only applies to function types",

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: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,6 +1094,25 @@ bool Decl::isStdlibDecl() const {
10941094
DC->getParentModule()->isStdlibModule();
10951095
}
10961096

1097+
LifetimeAnnotation Decl::getLifetimeAnnotationFromAttributes() 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+
}
1105+
1106+
LifetimeAnnotation Decl::getLifetimeAnnotation() const {
1107+
if (auto *pd = dyn_cast<ParamDecl>(this)) {
1108+
return pd->getLifetimeAnnotation();
1109+
}
1110+
if (auto *fd = dyn_cast<FuncDecl>(this)) {
1111+
return fd->getLifetimeAnnotation();
1112+
}
1113+
return getLifetimeAnnotationFromAttributes();
1114+
}
1115+
10971116
AvailabilityContext Decl::getAvailabilityForLinkage() const {
10981117
ASTContext &ctx = getASTContext();
10991118

@@ -7032,6 +7051,18 @@ ParamDecl::Specifier ParamDecl::getSpecifier() const {
70327051
ParamDecl::Specifier::Default);
70337052
}
70347053

7054+
LifetimeAnnotation ParamDecl::getLifetimeAnnotation() const {
7055+
auto specifier = getSpecifier();
7056+
// Copyable parameters which are consumed have eager-move semantics.
7057+
if (specifier == ParamDecl::Specifier::Consuming &&
7058+
!getType()->isPureMoveOnly()) {
7059+
if (getAttrs().hasAttribute<NoEagerMoveAttr>())
7060+
return LifetimeAnnotation::Lexical;
7061+
return LifetimeAnnotation::EagerMove;
7062+
}
7063+
return getLifetimeAnnotationFromAttributes();
7064+
}
7065+
70357066
StringRef ParamDecl::getSpecifierSpelling(ParamSpecifier specifier) {
70367067
switch (specifier) {
70377068
case ParamSpecifier::Default:
@@ -9217,6 +9248,19 @@ SelfAccessKind FuncDecl::getSelfAccessKind() const {
92179248
SelfAccessKind::NonMutating);
92189249
}
92199250

9251+
LifetimeAnnotation FuncDecl::getLifetimeAnnotation() const {
9252+
// Copyable parameters which are consumed have eager-move semantics.
9253+
if (getSelfAccessKind() == SelfAccessKind::Consuming) {
9254+
auto *selfDecl = getImplicitSelfDecl();
9255+
if (selfDecl && !selfDecl->getType()->isPureMoveOnly()) {
9256+
if (getAttrs().hasAttribute<NoEagerMoveAttr>())
9257+
return LifetimeAnnotation::Lexical;
9258+
return LifetimeAnnotation::EagerMove;
9259+
}
9260+
}
9261+
return getLifetimeAnnotationFromAttributes();
9262+
}
9263+
92209264
bool FuncDecl::isCallAsFunctionMethod() const {
92219265
return getBaseIdentifier() == getASTContext().Id_callAsFunction &&
92229266
isInstanceMember();

lib/Sema/TypeCheckAttr.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6887,6 +6887,25 @@ bool AttributeChecker::visitLifetimeAttr(DeclAttribute *attr) {
68876887
void AttributeChecker::visitEagerMoveAttr(EagerMoveAttr *attr) {
68886888
if (visitLifetimeAttr(attr))
68896889
return;
6890+
if (auto *nominal = dyn_cast<NominalTypeDecl>(D)) {
6891+
if (nominal->getDeclaredInterfaceType()->isPureMoveOnly()) {
6892+
diagnoseAndRemoveAttr(attr, diag::eagermove_and_noncopyable_combined);
6893+
return;
6894+
}
6895+
}
6896+
if (auto *func = dyn_cast<FuncDecl>(D)) {
6897+
auto *self = func->getImplicitSelfDecl();
6898+
if (self && self->getType()->isPureMoveOnly()) {
6899+
diagnoseAndRemoveAttr(attr, diag::eagermove_and_noncopyable_combined);
6900+
return;
6901+
}
6902+
}
6903+
if (auto *pd = dyn_cast<ParamDecl>(D)) {
6904+
if (pd->getType()->isPureMoveOnly()) {
6905+
diagnoseAndRemoveAttr(attr, diag::eagermove_and_noncopyable_combined);
6906+
return;
6907+
}
6908+
}
68906909
}
68916910

68926911
void AttributeChecker::visitNoEagerMoveAttr(NoEagerMoveAttr *attr) {

test/SILGen/consuming_parameter.swift

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@ 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 }
9-
// CHECK: [[BOX1:%.*]] = begin_borrow [lexical] [[BOX]]
10-
// CHECK: [[Y:%.*]] = project_box [[BOX1]]
9+
// CHECK: [[Y:%.*]] = project_box [[BOX]]
1110
// CHECK: store %0 to [init] [[Y]]
1211

13-
// CHECK: [[YCAPTURE:%.*]] = copy_value [[BOX1]]
12+
// CHECK: [[YCAPTURE:%.*]] = copy_value [[BOX]]
1413
// CHECK: partial_apply {{.*}} {{%.*}}([[YCAPTURE]])
1514
let r = { y }
1615

@@ -34,13 +33,12 @@ struct Butt {
3433

3534
// CHECK-LABEL: sil {{.*}} @${{.*}}4Butt{{.*}}6merged
3635
consuming func merged(with other: Butt) -> () -> Butt {
37-
// CHECK: bb0(%0 : @guaranteed $Butt, %1 : @owned $Butt):
36+
// CHECK: bb0(%0 : @guaranteed $Butt, %1 : @_eagerMove @owned $Butt):
3837
// CHECK: [[BOX:%.*]] = alloc_box ${ var Butt }
39-
// CHECK: [[BOX1:%.*]] = begin_borrow [lexical] [[BOX]]
40-
// CHECK: [[SELF:%.*]] = project_box [[BOX1]]
38+
// CHECK: [[SELF:%.*]] = project_box [[BOX]]
4139
// CHECK: store %1 to [init] [[SELF]]
4240

43-
// CHECK: [[SELFCAPTURE:%.*]] = copy_value [[BOX1]]
41+
// CHECK: [[SELFCAPTURE:%.*]] = copy_value [[BOX]]
4442
// CHECK: partial_apply {{.*}} {{%.*}}([[SELFCAPTURE]])
4543
let r = { self }
4644

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
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+
// CHECK-LABEL: sil [ossa] @async_dead_arg_call_lexical : {{.*}} {
20+
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @_lexical @owned
21+
// CHECK: [[MOVE:%[^,]+]] = move_value [lexical] [[INSTANCE]]
22+
// CHECK: [[EXECUTOR:%[^,]+]] = enum $Optional<Builtin.Executor>, #Optional.none!enumelt
23+
// CHECK: [[CALLEE:%[^,]+]] = function_ref @async_callee
24+
// CHECK: apply [[CALLEE]]()
25+
// CHECK: hop_to_executor [[EXECUTOR]]
26+
// CHECK: destroy_value [[MOVE]]
27+
// CHECK-LABEL: } // end sil function 'async_dead_arg_call_lexical'
28+
@_silgen_name("async_dead_arg_call_lexical")
29+
public func async_dead_arg_call_lexical(@_noEagerMove o: consuming AnyObject) async {
30+
await bar()
31+
// o should be destroyed here
32+
}
33+
34+
extension C {
35+
// CHECK-LABEL: sil [ossa] @async_dead_arg_call_lexical_method : {{.*}} {
36+
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @_lexical @owned
37+
// CHECK-LABEL: } // end sil function 'async_dead_arg_call_lexical_method'
38+
@_silgen_name("async_dead_arg_call_lexical_method")
39+
@_noEagerMove
40+
consuming
41+
public func async_dead_arg_call_lexical_method() async {
42+
await bar()
43+
// self should be destroyed here
44+
}
45+
}
46+
47+
public class C {
48+
// CHECK-LABEL: sil [ossa] @async_dead_arg_call_method : {{.*}} {
49+
// CHECK: {{bb[0-9]+}}([[INSTANCE:%[^,]+]] : @_eagerMove @owned
50+
// CHECK: destroy_value [[INSTANCE]]
51+
// CHECK: [[EXECUTOR:%[^,]+]] = enum $Optional<Builtin.Executor>, #Optional.none!enumelt
52+
// CHECK: [[CALLEE:%[^,]+]] = function_ref @async_callee : $@convention(thin) @async () -> ()
53+
// CHECK: apply [[CALLEE]]() : $@convention(thin) @async () -> ()
54+
// CHECK: hop_to_executor [[EXECUTOR]]
55+
// CHECK-LABEL: } // end sil function 'async_dead_arg_call_method'
56+
@_silgen_name("async_dead_arg_call_method")
57+
consuming
58+
public func async_dead_arg_call() async {
59+
// self should be destroyed here
60+
await bar()
61+
}
62+
}
63+
64+
@inline(never)
65+
@_silgen_name("async_callee")
66+
func bar() async {}
67+
68+
// CHECK-LABEL: sil [ossa] @write_to_pointer : {{.*}} {
69+
// CHECK: {{bb[0-9]+}}([[CONSUMED_INSTANCE:%[^,]+]] : @_eagerMove @owned $AnyObject, [[UMP:%[^,]+]] :
70+
// CHECK: [[PTR:%[^,]+]] = struct_extract [[UMP]]
71+
// CHECK: [[ADDR:%[^,]+]] = pointer_to_address [[PTR]]
72+
// CHECK: store [[CONSUMED_INSTANCE]] to [assign] [[ADDR]]
73+
// CHECK: [[PTR2:%[^,]+]] = struct_extract [[UMP]]
74+
// CHECK: [[ADDR2:%[^,]+]] = pointer_to_address [[PTR2]]
75+
// CHECK: [[OUT:%[^,]+]] = load [copy] [[ADDR2]]
76+
// CHECK: return [[OUT]]
77+
// CHECK-LABEL: } // end sil function 'write_to_pointer'
78+
@_silgen_name("write_to_pointer")
79+
public func write_to_pointer(o: consuming AnyObject, p: UnsafeMutablePointer<AnyObject>) -> AnyObject {
80+
// o should be destroyed here
81+
p.pointee = o
82+
return p.pointee
83+
}
84+
85+
extension C {
86+
// CHECK-LABEL: sil [ossa] @write_to_pointer_method : {{.*}} {
87+
// CHECK: {{bb[0-9]+}}([[UMP:%[^,]+]] : $UnsafeMutablePointer<C>, [[INSTANCE:%[^,]+]] : @_eagerMove @owned
88+
// CHECK: [[PTR:%[^,]+]] = struct_extract [[UMP]]
89+
// CHECK: [[ADDR:%[^,]+]] = pointer_to_address [[PTR]]
90+
// CHECK: store [[INSTANCE]] to [assign] [[ADDR]]
91+
// CHECK: [[ADDR2:%[^,]+]] = struct_extract [[UMP]]
92+
// CHECK: [[PTR2:%[^,]+]] = pointer_to_address [[ADDR2]]
93+
// CHECK: [[OUT:%[^,]+]] = load [copy] [[PTR2]]
94+
// CHECK: return [[OUT]]
95+
// CHECK-LABEL: } // end sil function 'write_to_pointer_method'
96+
@_silgen_name("write_to_pointer_method")
97+
consuming
98+
public func write_to_pointer(p: UnsafeMutablePointer<C>) -> C {
99+
// o should be destroyed here
100+
p.pointee = self
101+
return p.pointee
102+
}
103+
}

test/attr/lexical.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,16 @@ func foo() {
4343
_ = s2
4444
_ = s3
4545
}
46+
47+
@_moveOnly struct MoveOnly {}
48+
49+
@_eagerMove @_moveOnly struct MoveOnlyEagerly {} // expected-error {{@_eagerMove cannot be applied to NonCopyable types}}
50+
51+
func zoo(@_eagerMove _ : consuming MoveOnly) {} // expected-error {{@_eagerMove cannot be applied to NonCopyable types}}
52+
53+
func zooo(@_noEagerMove _ : consuming C) {} // ok, only way to spell this behavior
54+
55+
extension MoveOnly {
56+
@_eagerMove // expected-error {{@_eagerMove cannot be applied to NonCopyable types}}
57+
func zoo() {}
58+
}

0 commit comments

Comments
 (0)