Skip to content

Commit f16d1e4

Browse files
committed
introduce @_manualOwnership performance attribute
This attribute forces programmers to acknowledge every copy that is required to happen in the body of the function. only those copies that make sense according to Swift's ownership rules are "required". The way this is implemented is to flag every non-explicit copy in a function as an error. This keeps SILGen and the Swift language in sync about what copies are actually happening, which motivates fixes for cases where SILGen can be more clever. This also means that there is _no_ hand-holding to avoid exclusivity violations. Static checking will continue to catch some cases, but for other cases, like class objects, it is up to the programmer to understand where they may need to rewrite code with mutable references. This is where the "manual" comes from. For example, ```swift // unsafe if x === y swap(&x.a, &y.a) // a safe rewrite let t = copy x.a x.a = copy y.a y.a = consume t ```
1 parent b444a0b commit f16d1e4

29 files changed

+341
-5
lines changed

SwiftCompilerSources/Sources/SIL/Function.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,7 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash
290290
case noRuntime
291291
case noExistentials
292292
case noObjCRuntime
293+
case manualOwnership
293294
}
294295

295296
public var performanceConstraints: PerformanceConstraints {
@@ -300,6 +301,7 @@ final public class Function : CustomStringConvertible, HasShortDescription, Hash
300301
case .NoRuntime: return .noRuntime
301302
case .NoExistentials: return .noExistentials
302303
case .NoObjCBridging: return .noObjCRuntime
304+
case .ManualOwnership: return .manualOwnership
303305
default: fatalError("unknown performance constraint")
304306
}
305307
}

include/swift/AST/DeclAttr.def

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -815,7 +815,10 @@ SIMPLE_DECL_ATTR(_noObjCBridging, NoObjCBridging,
815815
UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr,
816816
155)
817817

818-
// Unused '156': Used to be `_distributedThunkTarget` but completed implementation in Swift 6.0 does not need it after all
818+
SIMPLE_DECL_ATTR(_manualOwnership, ManualOwnership,
819+
OnAbstractFunction | OnSubscript,
820+
UserInaccessible | ABIStableToAdd | ABIStableToRemove | APIStableToAdd | APIStableToRemove | ForbiddenInABIAttr,
821+
156)
819822

820823
DECL_ATTR(_allowFeatureSuppression, AllowFeatureSuppression,
821824
OnAnyDecl,

include/swift/AST/DiagnosticsSIL.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,8 @@ ERROR(wrong_linkage_for_serialized_function,none,
428428
"function has wrong linkage to be called from %0", (StringRef))
429429
NOTE(performance_called_from,none,
430430
"called from here", ())
431+
ERROR(manualownership_copy,none,
432+
"explicit 'copy' required here", ())
431433

432434
// 'transparent' diagnostics
433435
ERROR(circular_transparent,none,

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2139,6 +2139,10 @@ ERROR(attr_static_exclusive_only_mutating,none,
21392139
ERROR(attr_static_exclusive_no_setters,none,
21402140
"variable of type %0 must not have a setter", (Type))
21412141

2142+
// @_manualOwnership
2143+
ERROR(attr_manual_ownership_experimental,none,
2144+
"'@_manualOwnership' requires '-enable-experimental-feature ManualOwnership'", ())
2145+
21422146
// @extractConstantsFromMembers
21432147
ERROR(attr_extractConstantsFromMembers_experimental,none,
21442148
"'@extractConstantsFromMembers' requires "

include/swift/AST/Expr.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2290,7 +2290,7 @@ class CopyExpr final : public Expr {
22902290
/// getSemanticsProvidingExpr() looks through this because it doesn't
22912291
/// provide the value and only very specific clients care where the
22922292
/// 'borrow' was written.
2293-
class BorrowExpr final : public IdentityExpr {
2293+
class BorrowExpr final : public IdentityExpr { // FIXME: this should be a ImplicitConversionExpr like LoadExpr
22942294
SourceLoc BorrowLoc;
22952295

22962296
public:

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,9 @@ EXPERIMENTAL_FEATURE(LifetimeDependence, true)
441441
/// Enable the `@_staticExclusiveOnly` attribute.
442442
EXPERIMENTAL_FEATURE(StaticExclusiveOnly, true)
443443

444+
/// Enable the `@_manualOwnership` attribute.
445+
EXPERIMENTAL_FEATURE(ManualOwnership, false)
446+
444447
/// Enable the @extractConstantsFromMembers attribute.
445448
EXPERIMENTAL_FEATURE(ExtractConstantsFromMembers, false)
446449

include/swift/SIL/SILBridging.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,8 @@ struct BridgedFunction {
443443
NoLocks = 2,
444444
NoRuntime = 3,
445445
NoExistentials = 4,
446-
NoObjCBridging = 5
446+
NoObjCBridging = 5,
447+
ManualOwnership = 6,
447448
};
448449

449450
enum class InlineStrategy {

include/swift/SIL/SILBuilder.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,14 @@ class SILBuilder {
280280
return false;
281281
}
282282

283+
/// If we have a SILFunction, return true if it has a ManualOwnership
284+
/// PerformanceConstraint, which corresponds to an attribute on the FuncDecl.
285+
bool hasManualOwnershipAttr() const {
286+
if (F)
287+
return F->getPerfConstraints() == PerformanceConstraints::ManualOwnership;
288+
return false;
289+
}
290+
283291
//===--------------------------------------------------------------------===//
284292
// Insertion Point Management
285293
//===--------------------------------------------------------------------===//

include/swift/SIL/SILCloner.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1706,6 +1706,16 @@ template<typename ImplClass>
17061706
void
17071707
SILCloner<ImplClass>::visitCopyAddrInst(CopyAddrInst *Inst) {
17081708
getBuilder().setCurrentDebugScope(getOpScope(Inst->getDebugScope()));
1709+
// When cloning into a function using manual ownership, convert to explicit
1710+
// copies, in order to preserve the local nature of the perf constraint.
1711+
if (getBuilder().hasManualOwnershipAttr() && getBuilder().hasOwnership()) {
1712+
recordClonedInstruction(
1713+
Inst, getBuilder().createExplicitCopyAddr(
1714+
getOpLocation(Inst->getLoc()), getOpValue(Inst->getSrc()),
1715+
getOpValue(Inst->getDest()), Inst->isTakeOfSrc(),
1716+
Inst->isInitializationOfDest()));
1717+
return;
1718+
}
17091719
recordClonedInstruction(
17101720
Inst, getBuilder().createCopyAddr(
17111721
getOpLocation(Inst->getLoc()), getOpValue(Inst->getSrc()),
@@ -2100,6 +2110,15 @@ void SILCloner<ImplClass>::visitCopyValueInst(CopyValueInst *Inst) {
21002110
return recordFoldedValue(Inst, newValue);
21012111
}
21022112

2113+
// When cloning into a function using manual ownership, convert to explicit
2114+
// copies, in order to preserve the local nature of the perf constraint.
2115+
if (getBuilder().hasManualOwnershipAttr() && getBuilder().hasOwnership()) {
2116+
recordClonedInstruction(
2117+
Inst, getBuilder().createExplicitCopyValue(getOpLocation(Inst->getLoc()),
2118+
getOpValue(Inst->getOperand())));
2119+
return;
2120+
}
2121+
21032122
recordClonedInstruction(
21042123
Inst, getBuilder().createCopyValue(getOpLocation(Inst->getLoc()),
21052124
getOpValue(Inst->getOperand())));

include/swift/SIL/SILFunction.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ enum class PerformanceConstraints : uint8_t {
9393
NoLocks = 2,
9494
NoRuntime = 3,
9595
NoExistentials = 4,
96-
NoObjCBridging = 5
96+
NoObjCBridging = 5,
97+
ManualOwnership = 6,
9798
};
9899

99100
class SILSpecializeAttr final {

0 commit comments

Comments
 (0)