Skip to content

Commit 064b713

Browse files
Merge pull request #60599 from nate-chandler/eager_move
Add hooks for emitting lifetimes.
2 parents 34d9624 + e9595ab commit 064b713

File tree

74 files changed

+1461
-424
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+1461
-424
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/DiagnosticsParse.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,9 @@ ERROR(silfunc_and_silarg_have_incompatible_sil_value_ownership,none,
517517
"SILFunction and SILArgument have mismatching ValueOwnershipKinds. "
518518
"Function type specifies: '@%0'. SIL argument specifies: '@%1'.",
519519
(StringRef, StringRef))
520+
ERROR(sil_arg_both_lexical_and_eagerMove,none,
521+
"Function argument is annotated both @_eagerMove and @_lexical, "
522+
"but these are incompatible alternatives", ())
520523
ERROR(expected_sil_colon,none,
521524
"expected ':' before %0", (StringRef))
522525
ERROR(expected_sil_tuple_index,none,

include/swift/AST/DiagnosticsSema.def

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

3212+
ERROR(eagermove_and_lexical_combined,none,
3213+
"@_eagerMove and @_lexical attributes are alternate styles of lifetimes "
3214+
"and can't be combined", ())
3215+
32123216
ERROR(autoclosure_function_type,none,
32133217
"@autoclosure attribute only applies to function types",
32143218
())
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//===--- LifetimeAnnotation.h - Lifetime-affecting attributes ---*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Defines a simple type-safe wrapper around the annotations that affect value
14+
// lifetimes. Used for both Decls and SILFunctionArguments.
15+
//
16+
//===----------------------------------------------------------------------===//
17+
18+
#ifndef SWIFT_AST_LIFETIMEANNOTATION_H
19+
#define SWIFT_AST_LIFETIMEANNOTATION_H
20+
21+
#include <cstdint>
22+
23+
namespace swift {
24+
25+
/// The annotation on a value (or type) explicitly indicating the lifetime that
26+
/// it (or its instances) should have.
27+
///
28+
/// A LifetimeAnnotation is one of the following three values:
29+
///
30+
/// 1) None: No annotation has been applied.
31+
/// 2) EagerMove: The @_eagerMove attribute has been applied.
32+
/// 3) Lexical: The @_lexical attribute has been applied.
33+
struct LifetimeAnnotation {
34+
enum Case : uint8_t {
35+
None,
36+
EagerMove,
37+
Lexical,
38+
} value;
39+
40+
LifetimeAnnotation(Case newValue) : value(newValue) {}
41+
42+
operator Case() const { return value; }
43+
44+
bool isSome() { return value != None; }
45+
};
46+
47+
} // namespace swift
48+
49+
#endif

include/swift/SIL/Lifetime.h

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
//===---- Lifetime.h - How long a value should be kept alive ----*- C++ -*-===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// Defines a simple type-safe wrapper type to indicate the kind of lifetime that
14+
// a value has--whether it is tied to a lexical scope or not.
15+
//
16+
//===----------------------------------------------------------------------===//
17+
18+
#include "swift/AST/LifetimeAnnotation.h"
19+
20+
#ifndef SWIFT_SIL_LIFETIME_H
21+
#define SWIFT_SIL_LIFETIME_H
22+
23+
namespace swift {
24+
25+
/// How long a value (such as instances of a type) should be kept alive--how
26+
/// aggressively its destroys may be hoisted.
27+
///
28+
/// By default, types have lifetimes inferred from their structure, see
29+
/// TypeLowering::RecursiveProperties::isLexical. It can be overridden both on
30+
/// the type level and the value level via attributes.
31+
struct Lifetime {
32+
enum Storage : uint8_t {
33+
/// No lifetime. Applicable to values which aren't destroyed.
34+
None,
35+
/// A lifetime independent from the lexical scope of the value: its
36+
/// releases are hoisted without respect to deinit barriers.
37+
EagerMove,
38+
/// A lifetime tied to the lexical scope of the value: its releases are
39+
/// not hoisted over deinit barriers.
40+
Lexical,
41+
} value;
42+
43+
Lifetime(decltype(value) newValue) : value(newValue) {}
44+
45+
operator Storage() const { return value; }
46+
47+
bool isLexical() { return value == Lifetime::Lexical; }
48+
49+
bool isEagerMove() { return value == Lifetime::EagerMove; }
50+
51+
/// Given a lifetime for a type and the lifetime annotation on a value of that
52+
/// type, the lifetime appropriate for that value.
53+
///
54+
/// Value annotations override a type's lifetime, so the result is just the
55+
/// lifetime indicated by the annotation, if there is one; otherwise its the
56+
/// lifetime from the type.
57+
Lifetime getLifetimeForAnnotatedValue(LifetimeAnnotation annotation) const {
58+
switch (annotation) {
59+
case LifetimeAnnotation::None:
60+
return *this;
61+
case LifetimeAnnotation::EagerMove:
62+
return Lifetime::EagerMove;
63+
case LifetimeAnnotation::Lexical:
64+
return Lifetime::Lexical;
65+
}
66+
}
67+
};
68+
69+
} // end swift namespace
70+
71+
#endif

include/swift/SIL/SILArgument.h

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313
#ifndef SWIFT_SIL_SILARGUMENT_H
1414
#define SWIFT_SIL_SILARGUMENT_H
1515

16+
#include "swift/AST/LifetimeAnnotation.h"
1617
#include "swift/Basic/Compiler.h"
18+
#include "swift/SIL/Lifetime.h"
1719
#include "swift/SIL/SILArgumentConvention.h"
18-
#include "swift/SIL/SILValue.h"
1920
#include "swift/SIL/SILFunctionConventions.h"
21+
#include "swift/SIL/SILValue.h"
2022

2123
namespace swift {
2224

@@ -113,8 +115,6 @@ class SILArgument : public ValueBase {
113115
node->getKind() <= SILNodeKind::Last_SILArgument;
114116
}
115117

116-
bool isNoImplicitCopy() const;
117-
118118
unsigned getIndex() const;
119119

120120
/// Return non-null if \p value is a phi.
@@ -329,14 +329,17 @@ class SILFunctionArgument : public SILArgument {
329329
friend class SILBasicBlock;
330330

331331
bool noImplicitCopy = false;
332+
LifetimeAnnotation lifetimeAnnotation = LifetimeAnnotation::None;
332333

333-
SILFunctionArgument(SILBasicBlock *parentBlock, SILType type,
334-
ValueOwnershipKind ownershipKind,
335-
const ValueDecl *decl = nullptr,
336-
bool isNoImplicitCopy = false)
334+
SILFunctionArgument(
335+
SILBasicBlock *parentBlock, SILType type,
336+
ValueOwnershipKind ownershipKind, const ValueDecl *decl = nullptr,
337+
bool isNoImplicitCopy = false,
338+
LifetimeAnnotation lifetimeAnnotation = LifetimeAnnotation::None)
337339
: SILArgument(ValueKind::SILFunctionArgument, parentBlock, type,
338340
ownershipKind, decl),
339-
noImplicitCopy(isNoImplicitCopy) {}
341+
noImplicitCopy(isNoImplicitCopy),
342+
lifetimeAnnotation(lifetimeAnnotation) {}
340343
// A special constructor, only intended for use in
341344
// SILBasicBlock::replaceFunctionArg.
342345
explicit SILFunctionArgument(SILType type, ValueOwnershipKind ownershipKind,
@@ -349,6 +352,20 @@ class SILFunctionArgument : public SILArgument {
349352

350353
void setNoImplicitCopy(bool newValue) { noImplicitCopy = newValue; }
351354

355+
LifetimeAnnotation getLifetimeAnnotation() const {
356+
return lifetimeAnnotation;
357+
}
358+
359+
void setLifetimeAnnotation(LifetimeAnnotation newValue) {
360+
lifetimeAnnotation = newValue;
361+
}
362+
363+
Lifetime getLifetime() const {
364+
return getType()
365+
.getLifetime(*getFunction())
366+
.getLifetimeForAnnotatedValue(getLifetimeAnnotation());
367+
}
368+
352369
bool isIndirectResult() const;
353370

354371
SILArgumentConvention getArgumentConvention() const;
@@ -408,12 +425,6 @@ inline SILPhiArgument *SILArgument::isTerminatorResult(SILValue value) {
408425
return nullptr;
409426
}
410427

411-
inline bool SILArgument::isNoImplicitCopy() const {
412-
if (auto *fArg = dyn_cast<SILFunctionArgument>(this))
413-
return fArg->isNoImplicitCopy();
414-
return false;
415-
}
416-
417428
inline bool SILArgument::isTerminatorResult() const {
418429
switch (getKind()) {
419430
case SILArgumentKind::SILPhiArgument:

include/swift/SIL/SILFunction.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,6 +1346,13 @@ class SILFunction
13461346
// Miscellaneous
13471347
//===--------------------------------------------------------------------===//
13481348

1349+
/// A value's lifetime, determined by looking at annotations on its decl and
1350+
/// the default lifetime for the type.
1351+
Lifetime getLifetime(VarDecl *decl, SILType ty) {
1352+
return ty.getLifetime(*this).getLifetimeForAnnotatedValue(
1353+
decl->getLifetimeAnnotation());
1354+
}
1355+
13491356
/// verify - Run the IR verifier to make sure that the SILFunction follows
13501357
/// invariants.
13511358
void verify(bool SingleFunction = true) const;

0 commit comments

Comments
 (0)