Skip to content

Commit 0c0ec72

Browse files
Merge pull request #65377 from nate-chandler/rdar108385761
[SILGen] Consuming a param implies it's eager-move.
2 parents 6e63015 + 525541f commit 0c0ec72

File tree

8 files changed

+201
-11
lines changed

8 files changed

+201
-11
lines changed

include/swift/AST/Decl.h

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

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

11111116
bool isNoImplicitCopy() const {
11121117
return getAttrs().hasAttribute<NoImplicitCopyAttr>();
@@ -6327,6 +6332,8 @@ class ParamDecl : public VarDecl {
63276332
Specifier getSpecifier() const;
63286333
void setSpecifier(Specifier Spec);
63296334

6335+
LifetimeAnnotation getLifetimeAnnotation() const;
6336+
63306337
/// Is the type of this parameter 'inout'?
63316338
bool isInOut() const { return getSpecifier() == Specifier::InOut; }
63326339

@@ -7338,6 +7345,8 @@ class FuncDecl : public AbstractFunctionDecl {
73387345

73397346
SelfAccessKind getSelfAccessKind() const;
73407347

7348+
LifetimeAnnotation getLifetimeAnnotation() const;
7349+
73417350
void setSelfAccessKind(SelfAccessKind mod) {
73427351
Bits.FuncDecl.SelfAccess = static_cast<unsigned>(mod);
73437352
Bits.FuncDecl.SelfAccessComputed = true;

include/swift/AST/DiagnosticsSema.def

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

35333535
ERROR(autoclosure_function_type,none,
35343536
"@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

@@ -7040,6 +7059,18 @@ ParamDecl::Specifier ParamDecl::getSpecifier() const {
70407059
ParamDecl::Specifier::Default);
70417060
}
70427061

7062+
LifetimeAnnotation ParamDecl::getLifetimeAnnotation() const {
7063+
auto specifier = getSpecifier();
7064+
// Copyable parameters which are consumed have eager-move semantics.
7065+
if (specifier == ParamDecl::Specifier::Consuming &&
7066+
!getType()->isPureMoveOnly()) {
7067+
if (getAttrs().hasAttribute<NoEagerMoveAttr>())
7068+
return LifetimeAnnotation::Lexical;
7069+
return LifetimeAnnotation::EagerMove;
7070+
}
7071+
return getLifetimeAnnotationFromAttributes();
7072+
}
7073+
70437074
StringRef ParamDecl::getSpecifierSpelling(ParamSpecifier specifier) {
70447075
switch (specifier) {
70457076
case ParamSpecifier::Default:
@@ -9225,6 +9256,19 @@ SelfAccessKind FuncDecl::getSelfAccessKind() const {
92259256
SelfAccessKind::NonMutating);
92269257
}
92279258

9259+
LifetimeAnnotation FuncDecl::getLifetimeAnnotation() const {
9260+
// Copyable parameters which are consumed have eager-move semantics.
9261+
if (getSelfAccessKind() == SelfAccessKind::Consuming) {
9262+
auto *selfDecl = getImplicitSelfDecl();
9263+
if (selfDecl && !selfDecl->getType()->isPureMoveOnly()) {
9264+
if (getAttrs().hasAttribute<NoEagerMoveAttr>())
9265+
return LifetimeAnnotation::Lexical;
9266+
return LifetimeAnnotation::EagerMove;
9267+
}
9268+
}
9269+
return getLifetimeAnnotationFromAttributes();
9270+
}
9271+
92289272
bool FuncDecl::isCallAsFunctionMethod() const {
92299273
return getBaseIdentifier() == getASTContext().Id_callAsFunction &&
92309274
isInstanceMember();

lib/Sema/TypeCheckAttr.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6897,6 +6897,25 @@ bool AttributeChecker::visitLifetimeAttr(DeclAttribute *attr) {
68976897
void AttributeChecker::visitEagerMoveAttr(EagerMoveAttr *attr) {
68986898
if (visitLifetimeAttr(attr))
68996899
return;
6900+
if (auto *nominal = dyn_cast<NominalTypeDecl>(D)) {
6901+
if (nominal->getDeclaredInterfaceType()->isPureMoveOnly()) {
6902+
diagnoseAndRemoveAttr(attr, diag::eagermove_and_noncopyable_combined);
6903+
return;
6904+
}
6905+
}
6906+
if (auto *func = dyn_cast<FuncDecl>(D)) {
6907+
auto *self = func->getImplicitSelfDecl();
6908+
if (self && self->getType()->isPureMoveOnly()) {
6909+
diagnoseAndRemoveAttr(attr, diag::eagermove_and_noncopyable_combined);
6910+
return;
6911+
}
6912+
}
6913+
if (auto *pd = dyn_cast<ParamDecl>(D)) {
6914+
if (pd->getType()->isPureMoveOnly()) {
6915+
diagnoseAndRemoveAttr(attr, diag::eagermove_and_noncopyable_combined);
6916+
return;
6917+
}
6918+
}
69006919
}
69016920

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

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: 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)