Skip to content

Commit 161240b

Browse files
committed
[Sema/SILGen] Omnibus of init accessor feature improvements
- Diagnose standalone `self` within init accessors - Diagnose references to members not listed in `initializes` or `accesses` attributes within init accessors - Enforce that `@out` parameters are fully initialized before every terminator - Detect and don't synthesize memberwise initializer with intersecting init accessor calls - Diagnose situations when memberwise init cannot be synthesized due to use of not-yet-initialized properties - Allow initialization expressions when computed property has init accessor; - Synthesize default argument for init accessor property included in memberwise initializer. (cherry picked from commit 5613006) (cherry picked from commit 935142f) (cherry picked from commit 3c924be) (cherry picked from commit db024d9) (cherry picked from commit f6fd0bc) (cherry picked from commit ddcfe01) (cherry picked from commit 4f59538) (cherry picked from commit cffc3fd) (cherry picked from commit 6ca9728) (cherry picked from commit 34c8cf6) (cherry picked from commit 8b1c9c9) (cherry picked from commit d1554f2) (cherry picked from commit fc895b4) (cherry picked from commit f58d407)
1 parent 76ce58e commit 161240b

24 files changed

+889
-68
lines changed

include/swift/AST/Decl.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5319,6 +5319,16 @@ class AbstractStorageDecl : public ValueDecl {
53195319
/// it.
53205320
bool hasStorage() const;
53215321

5322+
/// Return true if this is a VarDecl that has init accessor associated
5323+
/// with it.
5324+
bool hasInitAccessor() const;
5325+
5326+
/// Return true if this is a property that either has storage
5327+
/// or init accessor associated with it.
5328+
bool supportsInitialization() const {
5329+
return hasStorage() || hasInitAccessor();
5330+
}
5331+
53225332
/// Return true if this storage has the basic accessors/capability
53235333
/// to be mutated. This is generally constant after the accessors are
53245334
/// installed by the parser/importer/whatever.

include/swift/AST/DeclContext.h

Lines changed: 13 additions & 0 deletions
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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7296,7 +7296,22 @@ ERROR(init_accessor_accesses_attribute_on_other_declaration,none,
72967296
ERROR(init_accessor_property_both_init_and_accessed,none,
72977297
"property %0 cannot be both initialized and accessed",
72987298
(DeclName))
7299-
7299+
ERROR(invalid_use_of_self_in_init_accessor,none,
7300+
"'self' within init accessors can only be used to reference "
7301+
"properties listed in 'initializes' and 'accesses'; "
7302+
"init accessors are run before 'self' is fully available", ())
7303+
ERROR(init_accessor_invalid_member_ref,none,
7304+
"cannot reference instance member %0; init accessors can only "
7305+
"refer to instance properties listed in 'initializes' and "
7306+
"'accesses' attributes",
7307+
(DeclNameRef))
7308+
ERROR(cannot_synthesize_memberwise_due_to_property_init_order,none,
7309+
"cannot synthesize memberwise initializer",
7310+
())
7311+
NOTE(out_of_order_access_in_init_accessor,none,
7312+
"init accessor for %0 cannot access stored property %1 because it "
7313+
"is called before %1 is initialized",
7314+
(Identifier, Identifier))
73007315

73017316
#define UNDEFINE_DIAGNOSTIC_MACROS
73027317
#include "DefineDiagnosticMacros.h"

include/swift/AST/TypeCheckRequests.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4312,6 +4312,22 @@ class IsNonUserModuleRequest
43124312
bool isCached() const { return true; }
43134313
};
43144314

4315+
class HasInitAccessorRequest
4316+
: public SimpleRequest<HasInitAccessorRequest, bool(AbstractStorageDecl *),
4317+
RequestFlags::Cached> {
4318+
public:
4319+
using SimpleRequest::SimpleRequest;
4320+
4321+
private:
4322+
friend SimpleRequest;
4323+
4324+
// Evaluation.
4325+
bool evaluate(Evaluator &evaluator, AbstractStorageDecl *decl) const;
4326+
4327+
public:
4328+
bool isCached() const { return true; }
4329+
};
4330+
43154331
class InitAccessorReferencedVariablesRequest
43164332
: public SimpleRequest<InitAccessorReferencedVariablesRequest,
43174333
ArrayRef<VarDecl *>(DeclAttribute *, AccessorDecl *,

include/swift/AST/TypeCheckerTypeIDZone.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,9 @@ SWIFT_REQUEST(TypeChecker, IsNonUserModuleRequest,
487487
SWIFT_REQUEST(TypeChecker, TypeCheckObjCImplementationRequest,
488488
unsigned(ExtensionDecl *),
489489
Cached, NoLocationInfo)
490+
SWIFT_REQUEST(TypeChecker, HasInitAccessorRequest,
491+
bool(AbstractStorageDecl *), Cached,
492+
NoLocationInfo)
490493
SWIFT_REQUEST(TypeChecker, InitAccessorReferencedVariablesRequest,
491494
ArrayRef<VarDecl *>(DeclAttribute *, AccessorDecl *,
492495
ArrayRef<Identifier>),

include/swift/Sema/CSFix.h

Lines changed: 37 additions & 0 deletions
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

Lines changed: 5 additions & 0 deletions
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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6711,6 +6711,12 @@ Type AbstractStorageDecl::getValueInterfaceType() const {
67116711
return cast<SubscriptDecl>(this)->getElementInterfaceType();
67126712
}
67136713

6714+
bool AbstractStorageDecl::hasInitAccessor() const {
6715+
return evaluateOrDefault(
6716+
getASTContext().evaluator,
6717+
HasInitAccessorRequest{const_cast<AbstractStorageDecl *>(this)}, false);
6718+
}
6719+
67146720
VarDecl::VarDecl(DeclKind kind, bool isStatic, VarDecl::Introducer introducer,
67156721
SourceLoc nameLoc, Identifier name,
67166722
DeclContext *dc, StorageIsMutable_t supportsMutation)
@@ -7159,6 +7165,12 @@ bool VarDecl::isMemberwiseInitialized(bool preferDeclaredProperties) const {
71597165
isBackingStorageForDeclaredProperty(this))
71607166
return false;
71617167

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

lib/AST/DeclContext.cpp

Lines changed: 18 additions & 0 deletions
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/Parse/ParseExpr.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,14 +1059,14 @@ bool Parser::isStartOfGetSetAccessor() {
10591059
// The only case this can happen is if the accessor label is immediately after
10601060
// a brace (possibly preceded by attributes). "get" is implicit, so it can't
10611061
// be checked for. Conveniently however, get/set properties are not allowed
1062-
// to have initializers, so we don't have an ambiguity, we just have to check
1063-
// for observing accessors.
1062+
// to have initializers unless they have `init` accessor, so we don't have an
1063+
// ambiguity, we just have to check for observing accessors and init accessor.
10641064
//
10651065
// If we have a 'didSet' or a 'willSet' label, disambiguate immediately as
10661066
// an accessor block.
10671067
Token NextToken = peekToken();
10681068
if (NextToken.isContextualKeyword("didSet") ||
1069-
NextToken.isContextualKeyword("willSet"))
1069+
NextToken.isContextualKeyword("willSet") || NextToken.is(tok::kw_init))
10701070
return true;
10711071

10721072
// If we don't have attributes, then it cannot be an accessor block.
@@ -1087,9 +1087,9 @@ bool Parser::isStartOfGetSetAccessor() {
10871087
skipSingle();
10881088
}
10891089

1090-
// Check if we have 'didSet'/'willSet' after attributes.
1090+
// Check if we have 'didSet'/'willSet' or 'init' after attributes.
10911091
return Tok.isContextualKeyword("didSet") ||
1092-
Tok.isContextualKeyword("willSet");
1092+
Tok.isContextualKeyword("willSet") || Tok.is(tok::kw_init);
10931093
}
10941094

10951095
/// Recover invalid uses of trailing closures in a situation

0 commit comments

Comments
 (0)