Skip to content

Commit 2efc0f6

Browse files
Merge pull request #60940 from nate-chandler/eager_move_self
Self parameters can be @_noImplicitCopy or @_eagerMove.
2 parents 0d1c336 + cacac0b commit 2efc0f6

File tree

11 files changed

+118
-26
lines changed

11 files changed

+118
-26
lines changed

include/swift/AST/Attr.def

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,7 @@ SIMPLE_DECL_ATTR(_eagerMove, EagerMove,
667667
UserInaccessible |
668668
ABIStableToAdd | ABIStableToRemove |
669669
APIStableToAdd | APIStableToRemove |
670-
OnParam | OnVar | OnNominalType,
670+
OnFunc | OnParam | OnVar | OnNominalType,
671671
117)
672672

673673
CONTEXTUAL_SIMPLE_DECL_ATTR(distributed, DistributedActor,
@@ -680,7 +680,7 @@ SIMPLE_DECL_ATTR(_lexical, Lexical,
680680
UserInaccessible |
681681
ABIStableToAdd | ABIStableToRemove |
682682
APIStableToAdd | APIStableToRemove |
683-
OnParam | OnVar | OnNominalType,
683+
OnFunc | OnParam | OnVar | OnNominalType,
684684
119)
685685

686686
SIMPLE_DECL_ATTR(_assemblyVision, EmitAssemblyVisionRemarks,
@@ -699,7 +699,7 @@ SIMPLE_DECL_ATTR(_noImplicitCopy, NoImplicitCopy,
699699
UserInaccessible |
700700
ABIStableToAdd | ABIBreakingToRemove |
701701
APIStableToAdd | APIBreakingToRemove |
702-
OnParam | OnVar,
702+
OnFunc | OnParam | OnVar,
703703
122)
704704

705705
SIMPLE_DECL_ATTR(_noLocks, NoLocks,

include/swift/AST/Decl.h

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,19 @@ class alignas(1 << DeclAlignInBits) Decl : public ASTAllocated<Decl> {
986986
/// Check if this is a declaration defined at the top level of the Swift module
987987
bool isStdlibDecl() const;
988988

989+
LifetimeAnnotation getLifetimeAnnotation() const {
990+
auto &attrs = getAttrs();
991+
if (attrs.hasAttribute<EagerMoveAttr>())
992+
return LifetimeAnnotation::EagerMove;
993+
if (attrs.hasAttribute<LexicalAttr>())
994+
return LifetimeAnnotation::Lexical;
995+
return LifetimeAnnotation::None;
996+
}
997+
998+
bool isNoImplicitCopy() const {
999+
return getAttrs().hasAttribute<NoImplicitCopyAttr>();
1000+
}
1001+
9891002
AvailabilityContext getAvailabilityForLinkage() const;
9901003

9911004
/// Whether this declaration or one of its outer contexts has the
@@ -2714,15 +2727,6 @@ class ValueDecl : public Decl {
27142727
/// 'func foo(Int) -> () -> Self?'.
27152728
GenericParameterReferenceInfo findExistentialSelfReferences(
27162729
Type baseTy, bool treatNonResultCovariantSelfAsInvariant) const;
2717-
2718-
LifetimeAnnotation getLifetimeAnnotation() const {
2719-
auto &attrs = getAttrs();
2720-
if (attrs.hasAttribute<EagerMoveAttr>())
2721-
return LifetimeAnnotation::EagerMove;
2722-
if (attrs.hasAttribute<LexicalAttr>())
2723-
return LifetimeAnnotation::Lexical;
2724-
return LifetimeAnnotation::None;
2725-
}
27262730
};
27272731

27282732
/// This is a common base class for declarations which declare a type.
@@ -5809,10 +5813,6 @@ class ParamDecl : public VarDecl {
58095813
setDefaultArgumentKind(K.argumentKind);
58105814
}
58115815

5812-
bool isNoImplicitCopy() const {
5813-
return getAttrs().hasAttribute<NoImplicitCopyAttr>();
5814-
}
5815-
58165816
/// Whether this parameter has a default argument expression available.
58175817
///
58185818
/// Note that this will return false for deserialized declarations, which only

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3224,6 +3224,8 @@ ERROR(reasync_without_async_parameter,none,
32243224
ERROR(inherits_executor_without_async,none,
32253225
"non-async functions cannot inherit an executor", ())
32263226

3227+
ERROR(lifetime_invalid_global_scope,none, "%0 is only valid on methods",
3228+
(DeclAttribute))
32273229
ERROR(eagermove_and_lexical_combined,none,
32283230
"@_eagerMove and @_lexical attributes are alternate styles of lifetimes "
32293231
"and can't be combined", ())

lib/AST/ASTDumper.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -980,7 +980,7 @@ namespace {
980980
break;
981981
}
982982

983-
if (P->getAttrs().hasAttribute<NoImplicitCopyAttr>())
983+
if (P->isNoImplicitCopy())
984984
OS << " noImplicitCopy";
985985

986986
if (P->getDefaultArgumentKind() != DefaultArgumentKind::None) {

lib/SILGen/SILGenDecl.cpp

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ class LetValueInitialization : public Initialization {
450450
LetValueInitialization(VarDecl *vd, SILGenFunction &SGF) : vd(vd) {
451451
const TypeLowering *lowering = nullptr;
452452
if (SGF.getASTContext().LangOpts.Features.count(Feature::MoveOnly) &&
453-
vd->getAttrs().hasAttribute<NoImplicitCopyAttr>()) {
453+
vd->isNoImplicitCopy()) {
454454
lowering = &SGF.getTypeLowering(
455455
SILMoveOnlyWrappedType::get(vd->getType()->getCanonicalType()));
456456
} else {
@@ -487,8 +487,7 @@ class LetValueInitialization : public Initialization {
487487
// Make sure that we have a non-address only type when binding a
488488
// @_noImplicitCopy let.
489489
if (SGF.getASTContext().LangOpts.Features.count(Feature::MoveOnly) &&
490-
lowering->isAddressOnly() &&
491-
vd->getAttrs().hasAttribute<NoImplicitCopyAttr>()) {
490+
lowering->isAddressOnly() && vd->isNoImplicitCopy()) {
492491
auto d = diag::noimplicitcopy_used_on_generic_or_existential;
493492
diagnose(SGF.getASTContext(), vd->getLoc(), d);
494493
}
@@ -569,8 +568,7 @@ class LetValueInitialization : public Initialization {
569568
// ... and we don't have a no implicit copy trivial type, just return
570569
// value.
571570
if (!SGF.getASTContext().LangOpts.Features.count(Feature::MoveOnly) ||
572-
!vd->getAttrs().hasAttribute<NoImplicitCopyAttr>() ||
573-
!value->getType().isTrivial(SGF.F))
571+
!vd->isNoImplicitCopy() || !value->getType().isTrivial(SGF.F))
574572
return value;
575573

576574
// Otherwise, we have a no implicit copy trivial type, so wrap it in the
@@ -626,7 +624,7 @@ class LetValueInitialization : public Initialization {
626624

627625
// Otherwise, if we do not have a no implicit copy variable, just follow
628626
// the "normal path": perform a lexical borrow if the lifetime is lexical.
629-
if (!vd->getAttrs().hasAttribute<NoImplicitCopyAttr>()) {
627+
if (!vd->isNoImplicitCopy()) {
630628
if (SGF.F.getLifetime(vd, value->getType()).isLexical())
631629
return SGF.B.createBeginBorrow(PrologueLoc, value, /*isLexical*/ true);
632630
else

lib/SILGen/SILGenProlog.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,8 +355,20 @@ struct ArgumentInitHelper {
355355
SILLocation loc(pd);
356356
loc.markAsPrologue();
357357

358-
ManagedValue argrv = makeArgument(ty, pd->isInOut(), pd->isNoImplicitCopy(),
359-
pd->getLifetimeAnnotation(), parent, loc);
358+
LifetimeAnnotation lifetimeAnnotation = LifetimeAnnotation::None;
359+
bool isNoImplicitCopy = false;
360+
if (pd->isSelfParameter()) {
361+
if (auto *afd = dyn_cast<AbstractFunctionDecl>(pd->getDeclContext())) {
362+
lifetimeAnnotation = afd->getLifetimeAnnotation();
363+
isNoImplicitCopy = afd->isNoImplicitCopy();
364+
}
365+
} else {
366+
lifetimeAnnotation = pd->getLifetimeAnnotation();
367+
isNoImplicitCopy = pd->isNoImplicitCopy();
368+
}
369+
370+
ManagedValue argrv = makeArgument(ty, pd->isInOut(), isNoImplicitCopy,
371+
lifetimeAnnotation, parent, loc);
360372

361373
if (pd->isInOut()) {
362374
assert(argrv.getType().isAddress() && "expected inout to be address");

lib/Sema/TypeCheckAttr.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
322322

323323
void visitUnsafeInheritExecutorAttr(UnsafeInheritExecutorAttr *attr);
324324

325+
bool visitLifetimeAttr(DeclAttribute *attr);
325326
void visitEagerMoveAttr(EagerMoveAttr *attr);
326327
void visitLexicalAttr(LexicalAttr *attr);
327328

@@ -346,6 +347,19 @@ void AttributeChecker::visitNoImplicitCopyAttr(NoImplicitCopyAttr *attr) {
346347
return;
347348
}
348349

350+
if (auto *funcDecl = dyn_cast<FuncDecl>(D)) {
351+
if (visitLifetimeAttr(attr))
352+
return;
353+
354+
// We only handle non-lvalue arguments today.
355+
if (funcDecl->isMutating()) {
356+
auto error = diag::noimplicitcopy_attr_valid_only_on_local_let_params;
357+
diagnoseAndRemoveAttr(attr, error);
358+
return;
359+
}
360+
return;
361+
}
362+
349363
auto *dc = D->getDeclContext();
350364

351365
// If we have a param decl that is marked as no implicit copy, change our
@@ -6469,9 +6483,26 @@ void AttributeChecker::visitUnsafeInheritExecutorAttr(
64696483
}
64706484
}
64716485

6472-
void AttributeChecker::visitEagerMoveAttr(EagerMoveAttr *attr) {}
6486+
bool AttributeChecker::visitLifetimeAttr(DeclAttribute *attr) {
6487+
if (auto *funcDecl = dyn_cast<FuncDecl>(D)) {
6488+
auto declContext = funcDecl->getDeclContext();
6489+
// eagerMove attribute may only appear in type context
6490+
if (!declContext->getDeclaredInterfaceType()) {
6491+
diagnoseAndRemoveAttr(attr, diag::lifetime_invalid_global_scope, attr);
6492+
return true;
6493+
}
6494+
}
6495+
return false;
6496+
}
6497+
6498+
void AttributeChecker::visitEagerMoveAttr(EagerMoveAttr *attr) {
6499+
if (visitLifetimeAttr(attr))
6500+
return;
6501+
}
64736502

64746503
void AttributeChecker::visitLexicalAttr(LexicalAttr *attr) {
6504+
if (visitLifetimeAttr(attr))
6505+
return;
64756506
// @_lexical and @_eagerMove are opposites and can't be combined.
64766507
if (D->getAttrs().hasAttribute<EagerMoveAttr>()) {
64776508
diagnoseAndRemoveAttr(attr, diag::eagermove_and_lexical_combined);

test/SILGen/lexical_lifetime.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,24 @@ func lexical_borrow_arg_trivial(_ trivial: Trivial) {
129129
use_generic(trivial)
130130
}
131131

132+
extension C {
133+
// CHECK-LABEL: sil hidden [ossa] @lexical_method_attr : $@convention(method) (@owned C) -> () {
134+
// CHECK: {{bb[0-9]+}}({{%[^,]+}} : @_lexical @owned $C):
135+
// CHECK-LABEL: } // end sil function 'lexical_method_attr'
136+
@_silgen_name("lexical_method_attr")
137+
@_lexical
138+
__consuming
139+
func lexical_method_attr() {}
140+
141+
// CHECK-LABEL: sil hidden [ossa] @eagermove_method_attr : $@convention(method) (@owned C) -> () {
142+
// CHECK: {{bb[0-9]+}}({{%[^,]+}} : @_eagerMove @owned $C):
143+
// CHECK-LABEL: } // end sil function 'eagermove_method_attr'
144+
@_silgen_name("eagermove_method_attr")
145+
@_eagerMove
146+
__consuming
147+
func eagermove_method_attr() {}
148+
}
149+
132150
////////////////////////////////////////////////////////////////////////////////
133151
// Test }}
134152
////////////////////////////////////////////////////////////////////////////////

test/SILGen/noimplicitcopy_attr.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,13 @@ public func arguments(@_noImplicitCopy _ x: Klass) {
1818
public func argumentsOwned(@_noImplicitCopy _ x: __owned Klass) {
1919
x.doSomething()
2020
}
21+
22+
extension Klass {
23+
// CHECK-LABEL: sil hidden [ossa] @noimplicitcopy_method_attr : $@convention(method) (@owned Klass) -> () {
24+
// CHECK: {{bb[0-9]+}}({{%[^,]+}} : @noImplicitCopy @owned $Klass):
25+
// CHECK-LABEL: } // end sil function 'noimplicitcopy_method_attr'
26+
@_silgen_name("noimplicitcopy_method_attr")
27+
@_noImplicitCopy
28+
__consuming
29+
func noimplicitcopy_method_attr() {}
30+
}

test/attr/lexical.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,20 @@ func bar(@_lexical _ s: S) {} // okay
1515

1616
func baz(@_eagerMove _ c: C) {} // okay
1717

18+
@_eagerMove // expected-error {{@_eagerMove is only valid on methods}}
19+
func bazzoo(_ c: C) {}
20+
21+
@_lexical // expected-error {{@_lexical is only valid on methods}}
22+
func bazzoozoo(_ c: C) {}
23+
24+
extension C {
25+
@_eagerMove
26+
func pazzoo() {}
27+
28+
@_lexical
29+
func pazzoozoo() {}
30+
}
31+
1832
struct S2 {
1933
@_eagerMove let c: C // okay
2034
@_lexical let e: E // okay

0 commit comments

Comments
 (0)