Skip to content

Commit 2b8a397

Browse files
authored
Merge pull request #66513 from xedin/init-accessor-diagnostics
[Sema/SIL] Improve diagnostics related to init accessors
2 parents 183902d + 34c8cf6 commit 2b8a397

17 files changed

+739
-50
lines changed

include/swift/AST/DeclContext.h

+13
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ namespace swift {
8282
class SerializedDefaultArgumentInitializer;
8383
class SerializedTopLevelCodeDecl;
8484
class StructDecl;
85+
class AccessorDecl;
8586

8687
namespace serialization {
8788
using DeclID = llvm::PointerEmbeddedInt<unsigned, 31>;
@@ -457,6 +458,18 @@ class alignas(1 << DeclContextAlignInBits) DeclContext
457458
return const_cast<DeclContext*>(this)->getInnermostMethodContext();
458459
}
459460

461+
/// Returns the innermost accessor context that belongs to a property.
462+
///
463+
/// This routine looks through closure, initializer, and local function
464+
/// contexts to find the innermost accessor declaration.
465+
///
466+
/// \returns the innermost accessor, or null if there is no such context.
467+
LLVM_READONLY
468+
AccessorDecl *getInnermostPropertyAccessorContext();
469+
const AccessorDecl *getInnermostPropertyAccessorContext() const {
470+
return const_cast<DeclContext*>(this)->getInnermostPropertyAccessorContext();
471+
}
472+
460473
/// Returns the innermost type context.
461474
///
462475
/// This routine looks through closure, initializer, and local function

include/swift/AST/DiagnosticsSema.def

+16-1
Original file line numberDiff line numberDiff line change
@@ -7364,7 +7364,22 @@ ERROR(init_accessor_accesses_attribute_on_other_declaration,none,
73647364
ERROR(init_accessor_property_both_init_and_accessed,none,
73657365
"property %0 cannot be both initialized and accessed",
73667366
(DeclName))
7367-
7367+
ERROR(invalid_use_of_self_in_init_accessor,none,
7368+
"'self' within init accessors can only be used to reference "
7369+
"properties listed in 'initializes' and 'accesses'; "
7370+
"init accessors are run before 'self' is fully available", ())
7371+
ERROR(init_accessor_invalid_member_ref,none,
7372+
"cannot reference instance member %0; init accessors can only "
7373+
"refer to instance properties listed in 'initializes' and "
7374+
"'accesses' attributes",
7375+
(DeclNameRef))
7376+
ERROR(cannot_synthesize_memberwise_due_to_property_init_order,none,
7377+
"cannot synthesize memberwise initializer",
7378+
())
7379+
NOTE(out_of_order_access_in_init_accessor,none,
7380+
"init accessor for %0 cannot access stored property %1 because it "
7381+
"is called before %1 is initialized",
7382+
(Identifier, Identifier))
73687383

73697384
#define UNDEFINE_DIAGNOSTIC_MACROS
73707385
#include "DefineDiagnosticMacros.h"

include/swift/Sema/CSFix.h

+37
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,10 @@ enum class FixKind : uint8_t {
447447

448448
/// Ignore missing 'each' keyword before value pack reference.
449449
IgnoreMissingEachKeyword,
450+
451+
/// Ignore the fact that member couldn't be referenced within init accessor
452+
/// because its name doesn't appear in 'initializes' or 'accesses' attributes.
453+
AllowInvalidMemberReferenceInInitAccessor,
450454
};
451455

452456
class ConstraintFix {
@@ -3536,6 +3540,39 @@ class IgnoreMissingEachKeyword final : public ConstraintFix {
35363540
}
35373541
};
35383542

3543+
class AllowInvalidMemberReferenceInInitAccessor final : public ConstraintFix {
3544+
DeclNameRef MemberName;
3545+
3546+
AllowInvalidMemberReferenceInInitAccessor(ConstraintSystem &cs,
3547+
DeclNameRef memberName,
3548+
ConstraintLocator *locator)
3549+
: ConstraintFix(cs, FixKind::AllowInvalidMemberReferenceInInitAccessor,
3550+
locator),
3551+
MemberName(memberName) {}
3552+
3553+
public:
3554+
std::string getName() const override {
3555+
llvm::SmallVector<char, 16> scratch;
3556+
auto memberName = MemberName.getString(scratch);
3557+
return "allow reference to member '" + memberName.str() +
3558+
"' in init accessor";
3559+
}
3560+
3561+
bool diagnose(const Solution &solution, bool asNote = false) const override;
3562+
3563+
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
3564+
return diagnose(*commonFixes.front().first);
3565+
}
3566+
3567+
static AllowInvalidMemberReferenceInInitAccessor *
3568+
create(ConstraintSystem &cs, DeclNameRef memberName,
3569+
ConstraintLocator *locator);
3570+
3571+
static bool classof(const ConstraintFix *fix) {
3572+
return fix->getKind() == FixKind::AllowInvalidMemberReferenceInInitAccessor;
3573+
}
3574+
};
3575+
35393576
} // end namespace constraints
35403577
} // end namespace swift
35413578

include/swift/Sema/ConstraintSystem.h

+5
Original file line numberDiff line numberDiff line change
@@ -1879,6 +1879,11 @@ struct MemberLookupResult {
18791879
/// This is a static member being access through a protocol metatype
18801880
/// but its result type doesn't conform to this protocol.
18811881
UR_InvalidStaticMemberOnProtocolMetatype,
1882+
1883+
/// This is a member that doesn't appear in 'initializes' and/or
1884+
/// 'accesses' attributes of the init accessor and therefore canno
1885+
/// t be referenced in its body.
1886+
UR_UnavailableWithinInitAccessor,
18821887
};
18831888

18841889
/// This is a list of considered (but rejected) candidates, along with a

lib/AST/Decl.cpp

+6
Original file line numberDiff line numberDiff line change
@@ -7168,6 +7168,12 @@ bool VarDecl::isMemberwiseInitialized(bool preferDeclaredProperties) const {
71687168
isBackingStorageForDeclaredProperty(this))
71697169
return false;
71707170

7171+
// If this is a computed property with `init` accessor, it's
7172+
// memberwise initializable when it could be used to initialize
7173+
// other stored properties.
7174+
if (auto *init = getAccessor(AccessorKind::Init))
7175+
return init->getAttrs().hasAttribute<InitializesAttr>();
7176+
71717177
// If this is a computed property, it's not memberwise initialized unless
71727178
// the caller has asked for the declared properties and it is either a
71737179
// `lazy` property or a property with an attached wrapper.

lib/AST/DeclContext.cpp

+18
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,24 @@ AbstractFunctionDecl *DeclContext::getInnermostMethodContext() {
211211
return nullptr;
212212
}
213213

214+
AccessorDecl *DeclContext::getInnermostPropertyAccessorContext() {
215+
auto dc = this;
216+
do {
217+
if (auto decl = dc->getAsDecl()) {
218+
auto accessor = dyn_cast<AccessorDecl>(decl);
219+
// If we found a non-accessor decl, we're done.
220+
if (accessor == nullptr)
221+
return nullptr;
222+
223+
auto *storage = accessor->getStorage();
224+
if (isa<VarDecl>(storage) && storage->getDeclContext()->isTypeContext())
225+
return accessor;
226+
}
227+
} while ((dc = dc->getParent()));
228+
229+
return nullptr;
230+
}
231+
214232
bool DeclContext::isTypeContext() const {
215233
if (auto decl = getAsDecl())
216234
return isa<NominalTypeDecl>(decl) || isa<ExtensionDecl>(decl);

lib/SILGen/SILGenConstructor.cpp

+32-21
Original file line numberDiff line numberDiff line change
@@ -272,12 +272,13 @@ static RValue maybeEmitPropertyWrapperInitFromValue(
272272
subs, std::move(arg));
273273
}
274274

275-
static void emitApplyOfInitAccessor(SILGenFunction &SGF, SILLocation loc,
276-
AccessorDecl *accessor, SILValue selfValue,
277-
SILType selfTy, RValue &&initialValue) {
275+
static void
276+
emitApplyOfInitAccessor(SILGenFunction &SGF, SILLocation loc,
277+
AccessorDecl *accessor, SILValue selfValue,
278+
SILType selfTy, RValue &&initialValue) {
278279
SmallVector<SILValue> arguments;
279280

280-
auto emitFieldReference = [&](VarDecl *field) {
281+
auto emitFieldReference = [&](VarDecl *field, bool forInit = false) {
281282
auto fieldTy =
282283
selfTy.getFieldType(field, SGF.SGM.M, SGF.getTypeExpansionContext());
283284
return SGF.B.createStructElementAddr(loc, selfValue, field,
@@ -287,7 +288,7 @@ static void emitApplyOfInitAccessor(SILGenFunction &SGF, SILLocation loc,
287288
// First, let's emit all of the indirect results.
288289
if (auto *initAttr = accessor->getAttrs().getAttribute<InitializesAttr>()) {
289290
for (auto *property : initAttr->getPropertyDecls(accessor)) {
290-
arguments.push_back(emitFieldReference(property));
291+
arguments.push_back(emitFieldReference(property, /*forInit=*/true));
291292
}
292293
}
293294

@@ -399,29 +400,39 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF,
399400

400401
// If we have an indirect return slot, initialize it in-place.
401402
if (resultSlot) {
402-
// Tracks all the init accessors we have emitted
403-
// because they can initialize more than one property.
404-
llvm::SmallPtrSet<AccessorDecl *, 2> emittedInitAccessors;
405-
406403
auto elti = elements.begin(), eltEnd = elements.end();
407-
for (VarDecl *field : decl->getStoredProperties()) {
404+
405+
llvm::SmallPtrSet<VarDecl *, 4> storedProperties;
406+
{
407+
auto properties = decl->getStoredProperties();
408+
storedProperties.insert(properties.begin(), properties.end());
409+
}
410+
411+
for (auto *member : decl->getMembers()) {
412+
auto *field = dyn_cast<VarDecl>(member);
413+
if (!field)
414+
continue;
415+
416+
if (initializedViaAccessor.count(field))
417+
continue;
408418

409419
// Handle situations where this stored propery is initialized
410420
// via a call to an init accessor on some other property.
411-
if (initializedViaAccessor.count(field)) {
412-
auto *initProperty = initializedViaAccessor.find(field)->second;
413-
auto *initAccessor = initProperty->getAccessor(AccessorKind::Init);
414-
415-
if (emittedInitAccessors.count(initAccessor))
421+
if (auto *initAccessor = field->getAccessor(AccessorKind::Init)) {
422+
if (field->isMemberwiseInitialized(/*preferDeclaredProperties=*/true)) {
423+
assert(elti != eltEnd &&
424+
"number of args does not match number of fields");
425+
426+
emitApplyOfInitAccessor(SGF, Loc, initAccessor, resultSlot, selfTy,
427+
std::move(*elti));
428+
++elti;
416429
continue;
430+
}
431+
}
417432

418-
emitApplyOfInitAccessor(SGF, Loc, initAccessor, resultSlot, selfTy,
419-
std::move(*elti));
420-
421-
emittedInitAccessors.insert(initAccessor);
422-
++elti;
433+
// If this is not one of the stored properties, let's move on.
434+
if (!storedProperties.count(field))
423435
continue;
424-
}
425436

426437
auto fieldTy =
427438
selfTy.getFieldType(field, SGF.SGM.M, SGF.getTypeExpansionContext());

lib/SILOptimizer/Mandatory/DefiniteInitialization.cpp

+32-8
Original file line numberDiff line numberDiff line change
@@ -1154,14 +1154,38 @@ void LifetimeChecker::doIt() {
11541154

11551155
// All of the indirect results marked as "out" have to be fully initialized
11561156
// before their lifetime ends.
1157-
if (TheMemory.isOut() && Uses.empty()) {
1158-
auto loc = TheMemory.getLoc();
1159-
1160-
std::string propertyName;
1161-
auto *property = TheMemory.getPathStringToElement(0, propertyName);
1162-
diagnose(Module, F.getLocation(),
1163-
diag::ivar_not_initialized_by_init_accessor, property->getName());
1164-
EmittedErrorLocs.push_back(loc);
1157+
if (TheMemory.isOut()) {
1158+
auto diagnoseMissingInit = [&]() {
1159+
std::string propertyName;
1160+
auto *property = TheMemory.getPathStringToElement(0, propertyName);
1161+
diagnose(Module, F.getLocation(),
1162+
diag::ivar_not_initialized_by_init_accessor,
1163+
property->getName());
1164+
EmittedErrorLocs.push_back(TheMemory.getLoc());
1165+
};
1166+
1167+
// No uses means that there was no initialization.
1168+
if (Uses.empty()) {
1169+
diagnoseMissingInit();
1170+
return;
1171+
}
1172+
1173+
// Go over every return block and check whether member is fully initialized
1174+
// because it's possible that there is branch that doesn't have any use of
1175+
// the memory and nothing else is going to diagnose that. This is different
1176+
// from `self`, for example, because it would always have either `copy_addr`
1177+
// or `load` before return.
1178+
1179+
auto returnBB = F.findReturnBB();
1180+
1181+
while (returnBB != F.end()) {
1182+
auto *terminator = returnBB->getTerminator();
1183+
1184+
if (!isInitializedAtUse(DIMemoryUse(terminator, DIUseKind::Load, 0, 1)))
1185+
diagnoseMissingInit();
1186+
1187+
++returnBB;
1188+
}
11651189
}
11661190

11671191
// If the memory object has nontrivial type, then any destroy/release of the

lib/Sema/CSDiagnostics.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -9059,3 +9059,8 @@ bool MissingEachForValuePackReference::diagnoseAsError() {
90599059

90609060
return true;
90619061
}
9062+
9063+
bool InvalidMemberReferenceWithinInitAccessor::diagnoseAsError() {
9064+
emitDiagnostic(diag::init_accessor_invalid_member_ref, MemberName);
9065+
return true;
9066+
}

lib/Sema/CSDiagnostics.h

+13
Original file line numberDiff line numberDiff line change
@@ -3015,6 +3015,19 @@ class MissingEachForValuePackReference final : public FailureDiagnostic {
30153015
bool diagnoseAsError() override;
30163016
};
30173017

3018+
class InvalidMemberReferenceWithinInitAccessor final
3019+
: public FailureDiagnostic {
3020+
DeclNameRef MemberName;
3021+
3022+
public:
3023+
InvalidMemberReferenceWithinInitAccessor(const Solution &solution,
3024+
DeclNameRef memberName,
3025+
ConstraintLocator *locator)
3026+
: FailureDiagnostic(solution, locator), MemberName(memberName) {}
3027+
3028+
bool diagnoseAsError() override;
3029+
};
3030+
30183031
} // end namespace constraints
30193032
} // end namespace swift
30203033

lib/Sema/CSFix.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -2796,3 +2796,18 @@ IgnoreMissingEachKeyword::create(ConstraintSystem &cs, Type valuePackTy,
27962796
return new (cs.getAllocator())
27972797
IgnoreMissingEachKeyword(cs, valuePackTy, locator);
27982798
}
2799+
2800+
bool AllowInvalidMemberReferenceInInitAccessor::diagnose(
2801+
const Solution &solution, bool asNote) const {
2802+
InvalidMemberReferenceWithinInitAccessor failure(solution, MemberName,
2803+
getLocator());
2804+
return failure.diagnose(asNote);
2805+
}
2806+
2807+
AllowInvalidMemberReferenceInInitAccessor *
2808+
AllowInvalidMemberReferenceInInitAccessor::create(ConstraintSystem &cs,
2809+
DeclNameRef memberName,
2810+
ConstraintLocator *locator) {
2811+
return new (cs.getAllocator())
2812+
AllowInvalidMemberReferenceInInitAccessor(cs, memberName, locator);
2813+
}

lib/Sema/CSSimplify.cpp

+46
Original file line numberDiff line numberDiff line change
@@ -9635,6 +9635,44 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
96359635
}
96369636
}
96379637

9638+
if (auto *UDE =
9639+
getAsExpr<UnresolvedDotExpr>(memberLocator->getAnchor())) {
9640+
auto *base = UDE->getBase();
9641+
if (auto *accessor = DC->getInnermostPropertyAccessorContext()) {
9642+
if (accessor->isInitAccessor() && isa<DeclRefExpr>(base) &&
9643+
accessor->getImplicitSelfDecl() ==
9644+
cast<DeclRefExpr>(base)->getDecl()) {
9645+
bool isValidReference = false;
9646+
9647+
// If name doesn't appear in either `initializes` or `accesses`
9648+
// then it's invalid instance member.
9649+
9650+
if (auto *initializesAttr =
9651+
accessor->getAttrs().getAttribute<InitializesAttr>()) {
9652+
isValidReference |= llvm::any_of(
9653+
initializesAttr->getProperties(), [&](Identifier name) {
9654+
return DeclNameRef(name) == memberName;
9655+
});
9656+
}
9657+
9658+
if (auto *accessesAttr =
9659+
accessor->getAttrs().getAttribute<AccessesAttr>()) {
9660+
isValidReference |= llvm::any_of(
9661+
accessesAttr->getProperties(), [&](Identifier name) {
9662+
return DeclNameRef(name) == memberName;
9663+
});
9664+
}
9665+
9666+
if (!isValidReference) {
9667+
result.addUnviable(
9668+
candidate,
9669+
MemberLookupResult::UR_UnavailableWithinInitAccessor);
9670+
return;
9671+
}
9672+
}
9673+
}
9674+
}
9675+
96389676
// If the underlying type of a typealias is fully concrete, it is legal
96399677
// to access the type with a protocol metatype base.
96409678
} else if (instanceTy->isExistentialType() &&
@@ -10215,6 +10253,10 @@ fixMemberRef(ConstraintSystem &cs, Type baseTy,
1021510253

1021610254
case MemberLookupResult::UR_InvalidStaticMemberOnProtocolMetatype:
1021710255
return AllowInvalidStaticMemberRefOnProtocolMetatype::create(cs, locator);
10256+
10257+
case MemberLookupResult::UR_UnavailableWithinInitAccessor:
10258+
return AllowInvalidMemberReferenceInInitAccessor::create(cs, memberName,
10259+
locator);
1021810260
}
1021910261
}
1022010262

@@ -14640,6 +14682,10 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1464014682
return recordFix(fix, 100) ? SolutionKind::Error : SolutionKind::Solved;
1464114683
}
1464214684

14685+
case FixKind::AllowInvalidMemberReferenceInInitAccessor: {
14686+
return recordFix(fix, 5) ? SolutionKind::Error : SolutionKind::Solved;
14687+
}
14688+
1464314689
case FixKind::ExplicitlyConstructRawRepresentable: {
1464414690
// Let's increase impact of this fix for binary operators because
1464514691
// it's possible to get both `.rawValue` and construction fixes for

0 commit comments

Comments
 (0)