Skip to content

Commit b74e531

Browse files
authored
Merge pull request #66715 from xedin/init-accessor-improvements-5.9
[5.9][Sema/SILGen] Omnibus of `init accessor` feature improvements
2 parents f4b2843 + 161240b commit b74e531

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
@@ -450,6 +450,10 @@ enum class FixKind : uint8_t {
450450

451451
/// Ignore missing 'each' keyword before value pack reference.
452452
IgnoreMissingEachKeyword,
453+
454+
/// Ignore the fact that member couldn't be referenced within init accessor
455+
/// because its name doesn't appear in 'initializes' or 'accesses' attributes.
456+
AllowInvalidMemberReferenceInInitAccessor,
453457
};
454458

455459
class ConstraintFix {
@@ -3566,6 +3570,39 @@ class IgnoreMissingEachKeyword final : public ConstraintFix {
35663570
}
35673571
};
35683572

3573+
class AllowInvalidMemberReferenceInInitAccessor final : public ConstraintFix {
3574+
DeclNameRef MemberName;
3575+
3576+
AllowInvalidMemberReferenceInInitAccessor(ConstraintSystem &cs,
3577+
DeclNameRef memberName,
3578+
ConstraintLocator *locator)
3579+
: ConstraintFix(cs, FixKind::AllowInvalidMemberReferenceInInitAccessor,
3580+
locator),
3581+
MemberName(memberName) {}
3582+
3583+
public:
3584+
std::string getName() const override {
3585+
llvm::SmallVector<char, 16> scratch;
3586+
auto memberName = MemberName.getString(scratch);
3587+
return "allow reference to member '" + memberName.str() +
3588+
"' in init accessor";
3589+
}
3590+
3591+
bool diagnose(const Solution &solution, bool asNote = false) const override;
3592+
3593+
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
3594+
return diagnose(*commonFixes.front().first);
3595+
}
3596+
3597+
static AllowInvalidMemberReferenceInInitAccessor *
3598+
create(ConstraintSystem &cs, DeclNameRef memberName,
3599+
ConstraintLocator *locator);
3600+
3601+
static bool classof(const ConstraintFix *fix) {
3602+
return fix->getKind() == FixKind::AllowInvalidMemberReferenceInInitAccessor;
3603+
}
3604+
};
3605+
35693606
} // end namespace constraints
35703607
} // end namespace swift
35713608

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)