Skip to content

Commit 86d4c45

Browse files
committed
Sema: Assert that we don't try to do direct-to-storage access of properties in resilient types
Right now, the rule is that any access of a stored property from a constructor or destructor through 'self' must go directly to storage, skipping observers or getter/setter overrides. This means that in particular, constructors of resilient types cannot be @_transparent, or defined in an extension in another module. Previously, this was only caught in IRGen when the @_transparent function was inlined into a function from another module, which made debugging difficult. Now, we hit an assert in Sema in this case. Of course it should be a diagnostic; we'll get there eventually. We could partially lift the restriction, allowing convenience initializers to be defined @_transparent or be added in extensions, by having accesses of stored properties from convenience inits go through accessors. This would be safe, because at that point, we must already have performed a self.init() delegation, however it would potentially break existing code in subtle ways. Also, this patch marks the RangeIterator and Range types @_fixed_layout, since they define @_transparent initializers and this tripped the new assert. Furthermore, note that the @_transparent initializer must be versioned because it is inlined into another versioned @_transparent function.
1 parent 40103ce commit 86d4c45

File tree

2 files changed

+52
-16
lines changed

2 files changed

+52
-16
lines changed

lib/Sema/CSApply.cpp

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,44 @@ static VarDecl *findNamedPropertyWitness(TypeChecker &tc, DeclContext *dc,
257257
return findNamedWitnessImpl<VarDecl>(tc, dc, type, proto, name, diag);
258258
}
259259

260+
static bool shouldAccessStorageDirectly(Expr *base, VarDecl *member,
261+
DeclContext *DC) {
262+
// This only matters for stored properties.
263+
if (!member->hasStorage())
264+
return false;
265+
266+
// ... referenced from constructors and destructors.
267+
auto *AFD = dyn_cast<AbstractFunctionDecl>(DC);
268+
if (AFD == nullptr)
269+
return false;
270+
271+
if (!isa<ConstructorDecl>(AFD) && !isa<DestructorDecl>(AFD))
272+
return false;
273+
274+
// ... via a "self.property" reference.
275+
auto *DRE = dyn_cast<DeclRefExpr>(base);
276+
if (DRE == nullptr)
277+
return false;
278+
279+
if (AFD->getImplicitSelfDecl() != cast<DeclRefExpr>(base)->getDecl())
280+
return false;
281+
282+
// Convenience initializers do not require special handling.
283+
// FIXME: This is a language change -- for now, keep the old behavior
284+
#if 0
285+
if (auto *CD = dyn_cast<ConstructorDecl>(AFD))
286+
if (!CD->isDesignatedInit())
287+
return false;
288+
#endif
289+
290+
// Ctor or dtor are for immediate class, not a derived class.
291+
if (AFD->getParent()->getDeclaredTypeOfContext()->getCanonicalType() !=
292+
member->getDeclContext()->getDeclaredTypeOfContext()->getCanonicalType())
293+
return false;
294+
295+
return true;
296+
}
297+
260298
/// Return the implicit access kind for a MemberRefExpr with the
261299
/// specified base and member in the specified DeclContext.
262300
static AccessSemantics
@@ -265,22 +303,17 @@ getImplicitMemberReferenceAccessSemantics(Expr *base, VarDecl *member,
265303
// Properties that have storage and accessors are frequently accessed through
266304
// accessors. However, in the init and destructor methods for the type
267305
// immediately containing the property, accesses are done direct.
268-
if (auto *AFD_DC = dyn_cast<AbstractFunctionDecl>(DC))
269-
if (member->hasStorage() &&
270-
// In a ctor or dtor.
271-
(isa<ConstructorDecl>(AFD_DC) || isa<DestructorDecl>(AFD_DC)) &&
272-
273-
// Ctor or dtor are for immediate class, not a derived class.
274-
AFD_DC->getParent()->getDeclaredTypeOfContext()->getCanonicalType() ==
275-
member->getDeclContext()->getDeclaredTypeOfContext()->getCanonicalType() &&
276-
277-
// Is a "self.property" reference.
278-
isa<DeclRefExpr>(base) &&
279-
AFD_DC->getImplicitSelfDecl() == cast<DeclRefExpr>(base)->getDecl()) {
280-
// Access this directly instead of going through (e.g.) observing or
281-
// trivial accessors.
282-
return AccessSemantics::DirectToStorage;
283-
}
306+
if (shouldAccessStorageDirectly(base, member, DC)) {
307+
// The storage better not be resilient.
308+
assert(member->hasFixedLayout(DC->getParentModule(),
309+
DC->getResilienceExpansion()) &&
310+
"Designated initializers and destructors of resilient types "
311+
"cannot be @_transparent or defined in extensions");
312+
313+
// Access this directly instead of going through (e.g.) observing or
314+
// trivial accessors.
315+
return AccessSemantics::DirectToStorage;
316+
}
284317

285318
// If the value is always directly accessed from this context, do it.
286319
return member->getAccessSemanticsFromContext(DC);

stdlib/public/core/Range.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
/// An iterator over the elements of `Range<Element>`.
14+
@_fixed_layout
1415
public struct RangeIterator<
1516
Element : ForwardIndex
1617
> : IteratorProtocol, Sequence {
@@ -72,6 +73,7 @@ public struct RangeIterator<
7273
/// }
7374
/// print(brackets(Range<Int>(start: -99, end: 100), 0))
7475
/// // Prints "0"
76+
@_fixed_layout
7577
public struct Range<
7678
Element : ForwardIndex
7779
> : Equatable, Collection,
@@ -87,6 +89,7 @@ public struct Range<
8789

8890
/// Construct a range with `startIndex == start` and `endIndex ==
8991
/// end`.
92+
@_versioned
9093
@_transparent
9194
internal init(_start: Element, end: Element) {
9295
self.startIndex = _start

0 commit comments

Comments
 (0)