Skip to content

Commit 70e2aea

Browse files
authored
Merge pull request #18156 from rjmccall/generalized-accessors
Implement generalized accessors using yield-once coroutines
2 parents 457fab4 + 7a4aeed commit 70e2aea

File tree

91 files changed

+1935
-136
lines changed

Some content is hidden

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

91 files changed

+1935
-136
lines changed

docs/ABI/Mangling.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,10 +223,12 @@ Entities
223223
ACCESSOR ::= 'G' // global getter
224224
ACCESSOR ::= 'w' // willSet
225225
ACCESSOR ::= 'W' // didSet
226+
ACCESSOR ::= 'r' // read
227+
ACCESSOR ::= 'M' // modify (temporary)
226228
ACCESSOR ::= 'a' ADDRESSOR-KIND // mutable addressor
227229
ACCESSOR ::= 'l' ADDRESSOR-KIND // non-mutable addressor
228230
ACCESSOR ::= 'p' // pseudo accessor referring to the storage itself
229-
231+
230232
ADDRESSOR-KIND ::= 'u' // unsafe addressor (no owner)
231233
ADDRESSOR-KIND ::= 'O' // owning addressor (non-native owner)
232234
ADDRESSOR-KIND ::= 'o' // owning addressor (native owner)

include/swift/AST/AccessorKinds.def

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,14 @@
6565
#define OBJC_ACCESSOR(ID, KEYWORD) OPAQUE_ACCESSOR(ID, KEYWORD)
6666
#endif
6767

68+
/// COROUTINE_ACCESSOR(ID, KEYWORD)
69+
/// The given accessor is a coroutine accessor, i.e. a reader or modifier.
70+
///
71+
/// Defaults to SINGLETON_ACCESSOR(ID, KEYWORD).
72+
#ifndef COROUTINE_ACCESSOR
73+
#define COROUTINE_ACCESSOR(ID, KEYWORD) SINGLETON_ACCESSOR(ID, KEYWORD)
74+
#endif
75+
6876
/// ANY_ADDRESSOR(ACCESSOR_ID, ADDRESSOR_ID, KEYWORD)
6977
/// The given keyword corresponds to an addressor of the given kind.
7078
///
@@ -148,6 +156,24 @@ OBSERVING_ACCESSOR(WillSet, willSet)
148156
/// setter idiom.
149157
OBSERVING_ACCESSOR(DidSet, didSet)
150158

159+
/// This is a read accessor: a coroutine which is called when a
160+
/// value is loaded from the storage, like a getter, but which works
161+
/// by yielding a borrowed value of the storage type.
162+
///
163+
/// If the storage is not implemented with a read accessor then
164+
/// one can always be synthesized (even if the storage type is move-only).
165+
COROUTINE_ACCESSOR(Read, _read)
166+
167+
/// This is a modify accessor: a coroutine which is called when a
168+
/// the storage is assigned to (like a setter) or read-modified
169+
/// (like materializeForSet), but which works by yielding an inout
170+
/// value of the storage type.
171+
///
172+
/// If the storage is not implemented with a modify accessor then
173+
/// one can be synthesized if the storage is mutable at all.
174+
/// Modify accessors are intended to eventually replace materializeForSet.
175+
COROUTINE_ACCESSOR(Modify, _modify)
176+
151177
/// This is an address-family accessor: a function that is called when
152178
/// a value is loaded from the storage, like a getter, but which works
153179
/// by returning a pointer to an immutable value of the storage type.
@@ -189,6 +215,7 @@ LAST_ACCESSOR(MutableAddress)
189215
#undef ANY_ADDRESSOR
190216
#undef OBJC_ACCESSOR
191217
#undef OPAQUE_ACCESSOR
218+
#undef COROUTINE_ACCESSOR
192219
#undef OBSERVING_ACCESSOR
193220
#undef SINGLETON_ACCESSOR
194221
#undef ACCESSOR

include/swift/AST/AnyFunctionRef.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,31 @@ class AnyFunctionRef {
8383
return TheFunction.get<AbstractClosureExpr *>()->getResultType();
8484
}
8585

86+
struct YieldResult {
87+
Type Ty;
88+
VarDecl::Specifier Specifier;
89+
};
90+
91+
ArrayRef<YieldResult>
92+
getBodyYieldResults(SmallVectorImpl<YieldResult> &buffer) const {
93+
assert(buffer.empty());
94+
if (auto *AFD = TheFunction.dyn_cast<AbstractFunctionDecl *>()) {
95+
if (auto *AD = dyn_cast<AccessorDecl>(AFD)) {
96+
if (AD->isCoroutine()) {
97+
auto valueTy = AD->getStorage()->getValueInterfaceType();
98+
valueTy = AD->mapTypeIntoContext(valueTy);
99+
auto specifier =
100+
AD->getAccessorKind() == AccessorKind::Modify
101+
? VarDecl::Specifier::InOut
102+
: VarDecl::Specifier::Shared;
103+
buffer.push_back({valueTy, specifier});
104+
return buffer;
105+
}
106+
}
107+
}
108+
return {};
109+
}
110+
86111
BraceStmt *getBody() const {
87112
if (auto *AFD = TheFunction.dyn_cast<AbstractFunctionDecl *>())
88113
return AFD->getBody();

include/swift/AST/Decl.h

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ enum class DescriptiveDeclKind : uint8_t {
151151
MaterializeForSet,
152152
Addressor,
153153
MutableAddressor,
154+
ReadAccessor,
155+
ModifyAccessor,
154156
WillSet,
155157
DidSet,
156158
EnumElement,
@@ -428,9 +430,9 @@ class alignas(1 << DeclAlignInBits) Decl {
428430
SelfAccess : 2
429431
);
430432

431-
SWIFT_INLINE_BITFIELD(AccessorDecl, FuncDecl, 3+3,
433+
SWIFT_INLINE_BITFIELD(AccessorDecl, FuncDecl, 4+3,
432434
/// The kind of accessor this is.
433-
AccessorKind : 3,
435+
AccessorKind : 4,
434436

435437
/// The kind of addressor this is.
436438
AddressorKind : 3
@@ -4334,8 +4336,7 @@ class AbstractStorageDecl : public ValueDecl {
43344336
return getAccessor(AccessorKind::Address);
43354337
}
43364338

4337-
/// \brief Return the decl for the 'mutableAddress' accessors if
4338-
/// it exists; this is only valid on a declaration with addressors.
4339+
/// \brief Return the decl for the mutable accessor if it exists.
43394340
AccessorDecl *getMutableAddressor() const {
43404341
return getAccessor(AccessorKind::MutableAddress);
43414342
}
@@ -4346,7 +4347,17 @@ class AbstractStorageDecl : public ValueDecl {
43464347
return getAddressor();
43474348
return getMutableAddressor();
43484349
}
4349-
4350+
4351+
/// \brief Return the decl for the 'read' coroutine accessor if it exists.
4352+
AccessorDecl *getReadCoroutine() const {
4353+
return getAccessor(AccessorKind::Read);
4354+
}
4355+
4356+
/// \brief Return the decl for the 'modify' coroutine accessor if it exists.
4357+
AccessorDecl *getModifyCoroutine() const {
4358+
return getAccessor(AccessorKind::Modify);
4359+
}
4360+
43504361
/// \brief Return the decl for the willSet specifier if it exists, this is
43514362
/// only valid on a declaration with Observing storage.
43524363
AccessorDecl *getWillSetFunc() const {
@@ -5626,8 +5637,26 @@ class AccessorDecl final : public FuncDecl {
56265637
bool isGetterOrSetter() const { return isGetter() || isSetter(); }
56275638

56285639
bool isObservingAccessor() const {
5629-
return getAccessorKind() == AccessorKind::DidSet ||
5630-
getAccessorKind() == AccessorKind::WillSet;
5640+
switch (getAccessorKind()) {
5641+
#define OBSERVING_ACCESSOR(ID, KEYWORD) \
5642+
case AccessorKind::ID: return true;
5643+
#define ACCESSOR(ID) \
5644+
case AccessorKind::ID: return false;
5645+
#include "swift/AST/AccessorKinds.def"
5646+
}
5647+
llvm_unreachable("bad accessor kind");
5648+
}
5649+
5650+
/// Is this accessor one of the kinds that's implicitly a coroutine?
5651+
bool isCoroutine() const {
5652+
switch (getAccessorKind()) {
5653+
#define COROUTINE_ACCESSOR(ID, KEYWORD) \
5654+
case AccessorKind::ID: return true;
5655+
#define ACCESSOR(ID) \
5656+
case AccessorKind::ID: return false;
5657+
#include "swift/AST/AccessorKinds.def"
5658+
}
5659+
llvm_unreachable("bad accessor kind");
56315660
}
56325661

56335662
static bool classof(const Decl *D) {

include/swift/AST/DiagnosticsParse.def

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -262,8 +262,6 @@ ERROR(duplicate_accessor,none,
262262
ERROR(conflicting_accessor,none,
263263
"%select{variable|subscript}0 cannot provide both %1 and %2",
264264
(unsigned, StringRef, StringRef))
265-
ERROR(modify_mutatingness_different_from_setter,none,
266-
"mutating-ness of 'modify' accessor cannot differ from setter", ())
267265
NOTE(previous_accessor,none,
268266
"%select{|previous definition of }1%0 %select{defined |}1here", (StringRef, bool))
269267
ERROR(expected_accessor_name,none,
@@ -285,7 +283,7 @@ ERROR(missing_getter,none,
285283
(unsigned, StringRef))
286284
ERROR(missing_reading_accessor,none,
287285
"%select{variable|subscript}0 with %1 must also have "
288-
"a getter or an addressor",
286+
"a getter, addressor, or 'read' accessor",
289287
(unsigned, StringRef))
290288
ERROR(observing_accessor_conflicts_with_accessor,none,
291289
"%select{'willSet'|'didSet'}0 cannot be provided together with %1",
@@ -938,6 +936,10 @@ NOTE(indent_expression_to_silence,none,
938936
ERROR(expected_expr_throw,PointsToFirstBadToken,
939937
"expected expression in 'throw' statement", ())
940938

939+
// Yield Statment
940+
ERROR(expected_expr_yield,PointsToFirstBadToken,
941+
"expected expression in 'yield' statement", ())
942+
941943
// Defer Statement
942944
ERROR(expected_lbrace_after_defer,PointsToFirstBadToken,
943945
"expected '{' after 'defer'", ())
@@ -1061,9 +1063,9 @@ ERROR(case_stmt_without_body,none,
10611063
// 'try' on statements
10621064
ERROR(try_on_stmt,none,
10631065
"'try' cannot be used with '%0'", (StringRef))
1064-
ERROR(try_on_return_throw,none,
1065-
"'try' must be placed on the %select{returned|thrown}0 expression",
1066-
(bool))
1066+
ERROR(try_on_return_throw_yield,none,
1067+
"'try' must be placed on the %select{returned|thrown|yielded}0 expression",
1068+
(unsigned))
10671069
ERROR(try_on_var_let,none,
10681070
"'try' must be placed on the initial value expression", ())
10691071

include/swift/AST/DiagnosticsSema.def

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,9 @@ ERROR(cannot_throw_error_code,none,
336336
"thrown error code type %0 does not conform to 'Error'; construct an %1 "
337337
"instance", (Type, Type))
338338

339+
ERROR(bad_yield_count,none,
340+
"expected %0 yield value(s)", (unsigned))
341+
339342
ERROR(cannot_throw_nil,none,
340343
"cannot infer concrete Error for thrown 'nil' value", ())
341344

@@ -368,6 +371,23 @@ ERROR(cannot_convert_partial_argument_value_protocol,none,
368371
ERROR(cannot_convert_argument_value_nil,none,
369372
"'nil' is not compatible with expected argument type %0", (Type))
370373

374+
ERROR(cannot_yield_rvalue_by_reference_same_type,none,
375+
"cannot yield immutable value of type %0 as an inout yield", (Type))
376+
ERROR(cannot_yield_rvalue_by_reference,none,
377+
"cannot yield immutable value of type %0 as an inout yield of type %1",
378+
(Type,Type))
379+
ERROR(cannot_yield_wrong_type_by_reference,none,
380+
"cannot yield reference to storage of type %0 as an inout yield of type %1",
381+
(Type,Type))
382+
ERROR(cannot_convert_yield_value,none,
383+
"cannot convert value of type %0 to expected yield type %1",
384+
(Type,Type))
385+
ERROR(cannot_convert_yield_value_protocol,none,
386+
"yielded type %0 does not conform to expected type %1",
387+
(Type,Type))
388+
ERROR(cannot_convert_yield_value_nil,none,
389+
"nil is not compatible with expected yield type %0", (Type))
390+
371391
ERROR(cannot_convert_closure_result,none,
372392
"cannot convert value of type %0 to closure result type %1",
373393
(Type,Type))
@@ -924,6 +944,9 @@ ERROR(missing_explicit_conversion,none,
924944
ERROR(missing_address_of,none,
925945
"passing value of type %0 to an inout parameter requires explicit '&'",
926946
(Type))
947+
ERROR(missing_address_of_yield,none,
948+
"yielding mutable value of type %0 requires explicit '&'",
949+
(Type))
927950
ERROR(extraneous_address_of,none,
928951
"use of extraneous '&'",
929952
())
@@ -1079,6 +1102,10 @@ ERROR(functions_mutating_and_not,none,
10791102
ERROR(static_functions_not_mutating,none,
10801103
"static functions must not be declared mutating", ())
10811104

1105+
ERROR(modify_mutatingness_differs_from_setter,none,
1106+
"'modify' accessor cannot be %select{nonmutating|mutating}0 "
1107+
"when the setter is %select{mutating|nonmutating}0", (bool))
1108+
10821109
ERROR(transparent_in_protocols_not_supported,none,
10831110
"'@_transparent' attribute is not supported on declarations within protocols", ())
10841111
ERROR(transparent_in_classes_not_supported,none,
@@ -3632,6 +3659,8 @@ ERROR(objc_observing_accessor, none,
36323659
"observing accessors are not allowed to be marked @objc", ())
36333660
ERROR(objc_addressor, none,
36343661
"addressors are not allowed to be marked @objc", ())
3662+
ERROR(objc_coroutine_accessor, none,
3663+
"'read' and 'modify' accessors are not allowed to be marked @objc", ())
36353664
ERROR(objc_invalid_on_func_variadic,none,
36363665
"method cannot be %" OBJC_ATTR_SELECT "0 because it has a variadic "
36373666
"parameter", (unsigned))

include/swift/AST/Stmt.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,11 @@ class alignas(8) Stmt {
8484
CaseCount : 32
8585
);
8686

87+
SWIFT_INLINE_BITFIELD_FULL(YieldStmt, Stmt, 32,
88+
: NumPadBits,
89+
NumYields : 32
90+
);
91+
8792
} Bits;
8893

8994
/// Return the given value for the 'implicit' flag if present, or if None,
@@ -209,7 +214,48 @@ class ReturnStmt : public Stmt {
209214

210215
static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Return;}
211216
};
217+
218+
/// YieldStmt - A yield statement. The yield-values sequence is not optional,
219+
/// but the parentheses are.
220+
/// yield 42
221+
class YieldStmt final
222+
: public Stmt, private llvm::TrailingObjects<YieldStmt, Expr*> {
223+
friend TrailingObjects;
224+
225+
SourceLoc YieldLoc;
226+
SourceLoc LPLoc;
227+
SourceLoc RPLoc;
228+
229+
YieldStmt(SourceLoc yieldLoc, SourceLoc lpLoc, ArrayRef<Expr *> yields,
230+
SourceLoc rpLoc, Optional<bool> implicit = None)
231+
: Stmt(StmtKind::Yield, getDefaultImplicitFlag(implicit, yieldLoc)),
232+
YieldLoc(yieldLoc), LPLoc(lpLoc), RPLoc(rpLoc) {
233+
Bits.YieldStmt.NumYields = yields.size();
234+
memcpy(getMutableYields().data(), yields.data(),
235+
yields.size() * sizeof(Expr*));
236+
}
237+
238+
public:
239+
static YieldStmt *create(const ASTContext &ctx, SourceLoc yieldLoc,
240+
SourceLoc lp, ArrayRef<Expr*> yields, SourceLoc rp);
241+
242+
SourceLoc getYieldLoc() const { return YieldLoc; }
243+
SourceLoc getLParenLoc() const { return LPLoc; }
244+
SourceLoc getRParenLoc() const { return RPLoc; }
245+
246+
SourceLoc getStartLoc() const { return YieldLoc; }
247+
SourceLoc getEndLoc() const;
248+
249+
ArrayRef<Expr*> getYields() const {
250+
return {getTrailingObjects<Expr*>(), Bits.YieldStmt.NumYields};
251+
}
252+
MutableArrayRef<Expr*> getMutableYields() {
253+
return {getTrailingObjects<Expr*>(), Bits.YieldStmt.NumYields};
254+
}
212255

256+
static bool classof(const Stmt *S) { return S->getKind() == StmtKind::Yield; }
257+
};
258+
213259
/// DeferStmt - A 'defer' statement. This runs the substatement it contains
214260
/// when the enclosing scope is exited.
215261
///

include/swift/AST/StmtNodes.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646

4747
STMT(Brace, Stmt)
4848
STMT(Return, Stmt)
49+
STMT(Yield, Stmt)
4950
STMT(Defer, Stmt)
5051
ABSTRACT_STMT(Labeled, Stmt)
5152
ABSTRACT_STMT(LabeledConditional, LabeledStmt)

0 commit comments

Comments
 (0)