Skip to content

Commit d7d3608

Browse files
committed
Break cyclic dependency due to implicit initialization of optional values
The fundamental problem here is that we don't know a priori whether an accessor macro will convert a stored property into a computed one. That can only be determined after macro expansion, which depends on having a determined type for the property. Implicit initialization of optional-typed values (e.g., "var birthDate: Date?") adds the initializer when there is storage, triggering the cycle. Introduce a very narrow fix that assumes that properties that have an accessor macro on them do not have storage. We probably want to enforce this, so that the "does this variable have storage?" query can be made cheaper.
1 parent 5427191 commit d7d3608

File tree

2 files changed

+23
-2
lines changed

2 files changed

+23
-2
lines changed

lib/Sema/TypeCheckStorage.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,27 @@ StoredPropertiesAndMissingMembersRequest::evaluate(Evaluator &evaluator,
280280
return decl->getASTContext().AllocateCopy(results);
281281
}
282282

283+
/// Check whether the pattern may have storage.
284+
///
285+
/// This query is careful not to trigger accessor macro expansion, which
286+
/// creates a cycle. It conservatively assumes that all accessor macros
287+
/// produce computed properties, which is... incorrect.
288+
static bool mayHaveStorage(Pattern *pattern) {
289+
// Check whether there are any accessor macros.
290+
bool hasAccessorMacros = false;
291+
pattern->forEachVariable([&](VarDecl *VD) {
292+
VD->forEachAttachedMacro(MacroRole::Accessor,
293+
[&](CustomAttr *customAttr, MacroDecl *macro) {
294+
hasAccessorMacros = true;
295+
});
296+
});
297+
298+
if (hasAccessorMacros)
299+
return false;
300+
301+
return pattern->hasStorage();
302+
}
303+
283304
/// Validate the \c entryNumber'th entry in \c binding.
284305
const PatternBindingEntry *PatternBindingEntryRequest::evaluate(
285306
Evaluator &eval, PatternBindingDecl *binding, unsigned entryNumber,
@@ -380,7 +401,7 @@ const PatternBindingEntry *PatternBindingEntryRequest::evaluate(
380401
// default-initializable. If so, do it.
381402
if (!pbe.isInitialized() &&
382403
binding->isDefaultInitializable(entryNumber) &&
383-
pattern->hasStorage()) {
404+
mayHaveStorage(pattern)) {
384405
if (auto defaultInit = TypeChecker::buildDefaultInitializer(patternType)) {
385406
// If we got a default initializer, install it and re-type-check it
386407
// to make sure it is properly coerced to the pattern type.

test/Macros/accessor_macros.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ struct MyStruct {
5353
// CHECK-DUMP: }
5454

5555
@myPropertyWrapper
56-
var birthDate: Date? = nil
56+
var birthDate: Date?
5757
// CHECK-DUMP: @__swiftmacro_15accessor_macros8MyStructV9birthDateAA0F0VSgvp17myPropertyWrapperfMa_.swift
5858
// CHECK-DUMP: get {
5959
// CHECK-DUMP: _birthDate.wrappedValue

0 commit comments

Comments
 (0)