Skip to content

Commit b3ae13d

Browse files
committed
[Sema] Added _eagerMove and _lexical attributes.
The new attributes affect how the lifetimes of values may be shortened.
1 parent 0383415 commit b3ae13d

File tree

8 files changed

+128
-3
lines changed

8 files changed

+128
-3
lines changed

docs/ReferenceGuides/UnderscoredAttributes.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,23 @@ library), instead of at an arbitrary point in time.
131131
For more details, see the forum post on
132132
[dynamic method replacement](https://forums.swift.org/t/dynamic-method-replacement/16619).
133133

134+
## `@_eagerMove`
135+
136+
When applied to a value, indicates that the value's lifetime is _not_ lexical,
137+
that releases of the value may be hoisted without respect to deinit barriers.
138+
139+
When applied to a type, indicates that all values which are _statically_
140+
instances of that type are themselves `@_eagerMove` as above, unless overridden
141+
with `@_lexical`.
142+
143+
Aggregates all of whose fields are `@_eagerMove` or trivial are inferred to be
144+
`@_eagerMove`.
145+
146+
Note that a value of an `@_eagerMove` type that is passed to a generic API (to a
147+
parameter not annotated `@_eagerMove` will, in that generic function's context,
148+
not be statically an instance of the `@_eagerMove` type. As a result it will
149+
have a lexical lifetime in that function.
150+
134151
## `@_effects(effectname)`
135152

136153
Tells the compiler that the implementation of the defined function is limited
@@ -473,12 +490,28 @@ initializers from its superclass. This implies that all designated initializers
473490
overridden. This attribute is often printed alongside
474491
`@_hasMissingDesignatedInitializers` in this case.
475492

493+
## `@_lexical`
494+
495+
When applied to a value, indicates that the value's lifetime is lexical, that
496+
releases of the value may not be hoisted over deinit barriers.
497+
498+
This is the default behavior, unless the value's type is annotated
499+
`@_eagerMove`, in which case this attribute overrides that type-level
500+
annotation.
501+
502+
When applied to a type, indicates that all values which are instances of that
503+
type are themselves `@_lexical` as above.
504+
505+
This is the default behavior, unless the type annotated is an aggregate that
506+
consists entirely of `@_eagerMove` or trivial values, in which case the
507+
attribute overrides the inferred type-level annotation.
508+
476509
## `@_marker`
477510

478511
Indicates that a protocol is a marker protocol. Marker protocols represent some
479512
meaningful property at compile-time but have no runtime representation.
480513

481-
For more details, see [SE-0302](https://github.com/apple/swift-evolution/blob/main/proposals/0302-concurrent-value-and-concurrent-closures.md#marker-protocols), which introduces marker protocols.
514+
For more details, see [](https://github.com/apple/swift-evolution/blob/main/proposals/0302-concurrent-value-and-concurrent-closures.md#marker-protocols), which introduces marker protocols.
482515
At the moment, the language only has one marker protocol: `Sendable`.
483516

484517
Fun fact: Rust has a very similar concept called

include/swift/AST/Attr.def

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -662,15 +662,25 @@ SIMPLE_DECL_ATTR(_inheritActorContext, InheritActorContext,
662662
ABIStableToAdd | ABIStableToRemove | APIBreakingToAdd | APIBreakingToRemove,
663663
116)
664664

665-
// 117 was 'spawn' and is now unused
665+
SIMPLE_DECL_ATTR(_eagerMove, EagerMove,
666+
UserInaccessible |
667+
ABIStableToAdd | ABIStableToRemove |
668+
APIStableToAdd | APIStableToRemove |
669+
OnParam | OnVar | OnNominalType,
670+
117)
666671

667672
CONTEXTUAL_SIMPLE_DECL_ATTR(distributed, DistributedActor,
668673
DeclModifier | OnClass | OnFunc | OnAccessor | OnVar |
669674
ABIBreakingToAdd | ABIBreakingToRemove |
670675
APIBreakingToAdd | APIBreakingToRemove,
671676
118)
672677

673-
// 119 is unused
678+
SIMPLE_DECL_ATTR(_lexical, Lexical,
679+
UserInaccessible |
680+
ABIStableToAdd | ABIStableToRemove |
681+
APIStableToAdd | APIStableToRemove |
682+
OnParam | OnVar | OnNominalType,
683+
119)
674684

675685
SIMPLE_DECL_ATTR(_assemblyVision, EmitAssemblyVisionRemarks,
676686
OnFunc | UserInaccessible | NotSerialized | OnNominalType |

include/swift/AST/Decl.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "swift/AST/GenericParamKey.h"
3030
#include "swift/AST/IfConfigClause.h"
3131
#include "swift/AST/LayoutConstraint.h"
32+
#include "swift/AST/LifetimeAnnotation.h"
3233
#include "swift/AST/ReferenceCounting.h"
3334
#include "swift/AST/RequirementSignature.h"
3435
#include "swift/AST/StorageImpl.h"
@@ -2713,6 +2714,15 @@ class ValueDecl : public Decl {
27132714
/// 'func foo(Int) -> () -> Self?'.
27142715
GenericParameterReferenceInfo findExistentialSelfReferences(
27152716
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+
}
27162726
};
27172727

27182728
/// This is a common base class for declarations which declare a type.

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3207,6 +3207,10 @@ ERROR(reasync_without_async_parameter,none,
32073207
ERROR(inherits_executor_without_async,none,
32083208
"non-async functions cannot inherit an executor", ())
32093209

3210+
ERROR(eagermove_and_lexical_combined,none,
3211+
"@_eagerMove and @_lexical attributes are alternate styles of lifetimes "
3212+
"and can't be combined", ())
3213+
32103214
ERROR(autoclosure_function_type,none,
32113215
"@autoclosure attribute only applies to function types",
32123216
())

lib/AST/ASTDumper.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,16 @@ namespace {
690690
OS << attr->getReplacedFunctionName();
691691
OS << "\")";
692692
}
693+
switch (VD->getLifetimeAnnotation()) {
694+
case LifetimeAnnotation::EagerMove:
695+
OS << " _eagerMove";
696+
break;
697+
case LifetimeAnnotation::Lexical:
698+
OS << " _lexical";
699+
break;
700+
case LifetimeAnnotation::None:
701+
break;
702+
}
693703
}
694704

695705
void printCommon(NominalTypeDecl *NTD, const char *Name,
@@ -958,6 +968,17 @@ namespace {
958968
if (P->getAttrs().hasAttribute<NonEphemeralAttr>())
959969
OS << " nonEphemeral";
960970

971+
switch (P->getLifetimeAnnotation()) {
972+
case LifetimeAnnotation::EagerMove:
973+
OS << " _eagerMove";
974+
break;
975+
case LifetimeAnnotation::Lexical:
976+
OS << " _lexical";
977+
break;
978+
case LifetimeAnnotation::None:
979+
break;
980+
}
981+
961982
if (P->getAttrs().hasAttribute<NoImplicitCopyAttr>())
962983
OS << " noImplicitCopy";
963984

lib/Sema/TypeCheckAttr.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -318,6 +318,9 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
318318

319319
void visitUnsafeInheritExecutorAttr(UnsafeInheritExecutorAttr *attr);
320320

321+
void visitEagerMoveAttr(EagerMoveAttr *attr);
322+
void visitLexicalAttr(LexicalAttr *attr);
323+
321324
void visitCompilerInitializedAttr(CompilerInitializedAttr *attr);
322325

323326
void checkBackDeployAttrs(ArrayRef<BackDeployAttr *> Attrs);
@@ -6217,6 +6220,16 @@ void AttributeChecker::visitUnsafeInheritExecutorAttr(
62176220
}
62186221
}
62196222

6223+
void AttributeChecker::visitEagerMoveAttr(EagerMoveAttr *attr) {}
6224+
6225+
void AttributeChecker::visitLexicalAttr(LexicalAttr *attr) {
6226+
// @_lexical and @_eagerMove are opposites and can't be combined.
6227+
if (D->getAttrs().hasAttribute<EagerMoveAttr>()) {
6228+
diagnoseAndRemoveAttr(attr, diag::eagermove_and_lexical_combined);
6229+
return;
6230+
}
6231+
}
6232+
62206233
void AttributeChecker::visitCompilerInitializedAttr(
62216234
CompilerInitializedAttr *attr) {
62226235
auto var = cast<VarDecl>(D);

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1614,6 +1614,9 @@ namespace {
16141614
UNINTERESTING_ATTR(UnsafeInheritExecutor)
16151615
UNINTERESTING_ATTR(CompilerInitialized)
16161616
UNINTERESTING_ATTR(AlwaysEmitConformanceMetadata)
1617+
1618+
UNINTERESTING_ATTR(EagerMove)
1619+
UNINTERESTING_ATTR(Lexical)
16171620
#undef UNINTERESTING_ATTR
16181621

16191622
void visitAvailableAttr(AvailableAttr *attr) {

test/attr/lexical.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
struct S {}
4+
5+
class C {}
6+
7+
enum E {}
8+
9+
@_eagerMove // expected-error {{'@_eagerMove' attribute cannot be applied to this declaration}}
10+
typealias EagerTuple = (C, C)
11+
12+
func foo(@_lexical @_eagerMove _ e: E) {} // expected-error {{@_eagerMove and @_lexical attributes are alternate styles of lifetimes and can't be combined}}
13+
14+
func bar(@_lexical _ s: S) {} // okay
15+
16+
func baz(@_eagerMove _ c: C) {} // okay
17+
18+
struct S2 {
19+
@_eagerMove let c: C // okay
20+
@_lexical let e: E // okay
21+
}
22+
23+
func foo() {
24+
@_lexical let s1 = S()
25+
@_eagerMove var s2 = S()
26+
@_lexical @_eagerMove let s3 = S() // expected-error {{@_eagerMove and @_lexical attributes are alternate styles of lifetimes and can't be combined}}
27+
_ = s1
28+
s2 = S()
29+
_ = s2
30+
_ = s3
31+
}

0 commit comments

Comments
 (0)