Skip to content

Commit adf1492

Browse files
authored
Merge pull request #86337 from kavon/noncopyable-opaque-read-ownership
introduce UnderscoreOwned feature
2 parents dc4b144 + 1642675 commit adf1492

File tree

15 files changed

+214
-2
lines changed

15 files changed

+214
-2
lines changed

docs/ReferenceGuides/UnderscoredAttributes.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -946,6 +946,44 @@ More generally, multiple availabilities can be specified, like so:
946946
enum Toast { ... }
947947
```
948948

949+
## `@_owned`
950+
951+
Indicates that the [conservative access pattern](/docs/Lexicon.md#access-pattern)
952+
for some storage (a subscript or a property) should use the `get` accessor instead of `_read`.
953+
954+
This attribute is particularly useful for accessors returning noncopyable values.
955+
By default, all explicitly-declared `get` accessors that return a noncopyable value and are declared in
956+
resilient libraries, or accessed opaquely via a protocol, are treated as if they return a borrowed value,
957+
rather than one that is valid to consume:
958+
959+
```swift
960+
// MutableSpan is noncopyable
961+
962+
public protocol Giver {
963+
var mutableSpan: MutableSpan { get } // has 'yielding borrow' semantics
964+
}
965+
966+
func example(_ s: some Giver) {
967+
let x = s.mutableSpan // error
968+
}
969+
```
970+
971+
This `@_owned` attribute allows you to override that behavior, so that the `get` requirement (or accessor) is truly
972+
exposed in a resilient interface, yielding an owned value of noncopyable type:
973+
974+
```swift
975+
public protocol Giver {
976+
@_owned
977+
var mutableSpan: MutableSpan { get } // has 'get' semantics
978+
}
979+
980+
func example(_ s: some Giver) {
981+
let x = s.mutableSpan // ok
982+
}
983+
```
984+
985+
Adding or removing this attribute is potentially an ABI and Source breaking change.
986+
949987
## `@_preInverseGenerics`
950988

951989
By default when mangling a generic signature, the presence of a conformance

include/swift/AST/DeclAttr.def

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,10 @@ SIMPLE_DECL_ATTR(reasync, AtReasync,
608608
ConcurrencyOnly | ABIBreakingToAdd | ABIBreakingToRemove | APIBreakingToAdd | APIBreakingToRemove | UnreachableInABIAttr,
609609
110)
610610

611-
// Unused '111'
611+
SIMPLE_DECL_ATTR(_owned, Owned,
612+
OnVar | OnSubscript,
613+
NotSerialized | ABIBreakingToAdd | ABIBreakingToRemove | APIStableToAdd | APIBreakingToRemove | ForbiddenInABIAttr,
614+
111)
612615

613616
CONTEXTUAL_DECL_ATTR(nonisolated, Nonisolated,
614617
OnFunc | OnConstructor | OnDestructor | OnVar | OnSubscript | OnProtocol | OnExtension | OnClass | OnStruct | OnEnum,

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6929,7 +6929,7 @@ ERROR(exclusivity_on_computed_property,none,
69296929
())
69306930

69316931
//------------------------------------------------------------------------------
6932-
// MARK: @_borrowed
6932+
// MARK: @_borrowed and @_owned
69336933
//------------------------------------------------------------------------------
69346934
ERROR(borrowed_with_objc_dynamic,none,
69356935
"%kindonly0 cannot be '@_borrowed' if it is '@objc dynamic'",
@@ -6941,6 +6941,12 @@ ERROR(borrowed_on_objc_protocol_requirement,none,
69416941
ERROR(borrowed_with_effect,none,
69426942
"%kindonly0 cannot be '@_borrowed' if it is 'async' or 'throws'",
69436943
(const AccessorDecl *))
6944+
ERROR(borrowed_and_owned,none,
6945+
"%kindonly0 cannot be '@_borrowed' and '@_owned' at the same time",
6946+
(const AbstractStorageDecl *))
6947+
ERROR(owned_non_getter,none,
6948+
"%kindonly0 must define a 'get' to support '@_owned'",
6949+
(const AbstractStorageDecl *))
69446950

69456951
//------------------------------------------------------------------------------
69466952
// MARK: dynamic

include/swift/Basic/Features.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,9 @@ EXPERIMENTAL_FEATURE(EnforceSPIOperatorGroup, true)
578578
/// Enable source-level warning control with `@warn`
579579
EXPERIMENTAL_FEATURE(SourceWarningControl, true)
580580

581+
/// Allows a `get` returning a noncopyable type have owned semantics in opaque interfaces.
582+
EXPERIMENTAL_FEATURE(UnderscoreOwned, true)
583+
581584
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
582585
#undef EXPERIMENTAL_FEATURE
583586
#undef UPCOMING_FEATURE

lib/AST/ASTDumper.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5106,6 +5106,7 @@ class PrintAttribute : public AttributeVisitor<PrintAttribute, void, Label>,
51065106
TRIVIAL_ATTR_PRINTER(ObjCNonLazyRealization, objc_non_lazy_realization)
51075107
TRIVIAL_ATTR_PRINTER(Optional, optional)
51085108
TRIVIAL_ATTR_PRINTER(Override, override)
5109+
TRIVIAL_ATTR_PRINTER(Owned, owned)
51095110
TRIVIAL_ATTR_PRINTER(Postfix, postfix)
51105111
TRIVIAL_ATTR_PRINTER(PreInverseGenerics, pre_inverse_generics)
51115112
TRIVIAL_ATTR_PRINTER(Preconcurrency, preconcurrency)

lib/AST/FeatureSet.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ UNINTERESTING_FEATURE(ImportMacroAliases)
131131
UNINTERESTING_FEATURE(NoExplicitNonIsolated)
132132
UNINTERESTING_FEATURE(EmbeddedExistentials)
133133

134+
static bool usesFeatureUnderscoreOwned(Decl *D) {
135+
return D->getAttrs().hasAttribute<OwnedAttr>();
136+
}
137+
134138
// TODO: Return true for inlinable function bodies with module selectors in them
135139
UNINTERESTING_FEATURE(ModuleSelector)
136140

lib/ASTGen/Sources/ASTGen/DeclAttrs.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ extension ASTGenVisitor {
291291
.NSManaged,
292292
.ObjCMembers,
293293
.ObjCNonLazyRealization,
294+
.Owned,
294295
.Preconcurrency,
295296
.PreInverseGenerics,
296297
.PropertyWrapper,

lib/Sema/TypeCheckAttr.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,32 @@ class AttributeChecker : public AttributeVisitor<AttributeChecker> {
304304
D->getAttrs().removeAttribute(attr);
305305
return;
306306
}
307+
308+
// Can't have both @_owned and @_borrwed on the same decl.
309+
if (D->getAttrs().hasAttribute<OwnedAttr>()) {
310+
diagnose(attr->getLocation(), diag::borrowed_and_owned,
311+
ASD)
312+
.fixItRemove(attr->getRange());
313+
D->getAttrs().removeAttribute(attr);
314+
}
315+
}
316+
317+
void visitOwnedAttr(OwnedAttr *attr) {
318+
assert(!D->hasClangNode() && "@_owned on imported declaration?");
319+
320+
auto *ASD = cast<AbstractStorageDecl>(D);
321+
322+
// Ensure it defines a 'get' for read accesses.
323+
//
324+
// The only known use-case for this attribute is for getters returning
325+
// noncopyable types that want to override the resilient '_read' that is
326+
// always generated, exposing 'get' that returns an owned value.
327+
if (!ASD->getAccessor(AccessorKind::Get)) {
328+
diagnose(attr->getLocation(), diag::owned_non_getter,
329+
ASD)
330+
.fixItRemove(attr->getRange());
331+
D->getAttrs().removeAttribute(attr);
332+
}
307333
}
308334

309335
void visitTransparentAttr(TransparentAttr *attr);

lib/Sema/TypeCheckDeclOverride.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1615,6 +1615,7 @@ namespace {
16151615
UNINTERESTING_ATTR(Inline)
16161616
UNINTERESTING_ATTR(Isolated)
16171617
UNINTERESTING_ATTR(Optimize)
1618+
UNINTERESTING_ATTR(Owned)
16181619
UNINTERESTING_ATTR(Exclusivity)
16191620
UNINTERESTING_ATTR(Nonexhaustive)
16201621
UNINTERESTING_ATTR(NoLocks)

lib/Sema/TypeCheckStorage.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,6 +1074,9 @@ OpaqueReadOwnershipRequest::evaluate(Evaluator &evaluator,
10741074
if (storage->getAttrs().hasAttribute<BorrowedAttr>())
10751075
return usesBorrowed(DiagKind::BorrowedAttr);
10761076

1077+
if (storage->getAttrs().hasAttribute<OwnedAttr>())
1078+
return OpaqueReadOwnership::Owned;
1079+
10771080
if (storage->getInnermostDeclContext()->mapTypeIntoEnvironment(
10781081
storage->getValueInterfaceType())->isNoncopyable())
10791082
return usesBorrowed(DiagKind::NoncopyableType);

0 commit comments

Comments
 (0)