Skip to content

Commit 9d342f1

Browse files
authored
Merge pull request #66681 from xedin/enable-inits-on-props-with-init-accessors
[Sema/SILGen] InitAccessors: Synthesize default arguments for init accessor properties
2 parents e1d2198 + f58d407 commit 9d342f1

12 files changed

+150
-18
lines changed

include/swift/AST/Decl.h

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

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

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>),

lib/AST/Decl.cpp

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

6723+
bool AbstractStorageDecl::hasInitAccessor() const {
6724+
return evaluateOrDefault(
6725+
getASTContext().evaluator,
6726+
HasInitAccessorRequest{const_cast<AbstractStorageDecl *>(this)}, false);
6727+
}
6728+
67236729
VarDecl::VarDecl(DeclKind kind, bool isStatic, VarDecl::Introducer introducer,
67246730
SourceLoc nameLoc, Identifier name,
67256731
DeclContext *dc, StorageIsMutable_t supportsMutation)

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

lib/SILGen/SILGenConstructor.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1474,6 +1474,13 @@ void SILGenFunction::emitMemberInitializers(DeclContext *dc,
14741474
if (auto pbd = dyn_cast<PatternBindingDecl>(member)) {
14751475
if (pbd->isStatic()) continue;
14761476

1477+
// Skip properties with init accessors, they could only be used
1478+
// explicitly and in memberwise initializers.
1479+
if (auto *var = pbd->getSingleVar()) {
1480+
if (var->hasInitAccessor())
1481+
continue;
1482+
}
1483+
14771484
for (auto i : range(pbd->getNumPatternEntries())) {
14781485
auto init = pbd->getExecutableInit(i);
14791486
if (!init) continue;

lib/Sema/CodeSynthesis.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,6 @@ static void maybeAddMemberwiseDefaultArg(ParamDecl *arg, VarDecl *var,
144144
if (!var->getParentPattern()->getSingleVar())
145145
return;
146146

147-
// FIXME: Don't attempt to synthesize default arguments for init
148-
// accessor properties because there could be multiple properties
149-
// with default values they are going to initialize.
150-
if (var->getAccessor(AccessorKind::Init))
151-
return;
152-
153147
// Whether we have explicit initialization.
154148
bool isExplicitlyInitialized = false;
155149
if (auto pbd = var->getParentPatternBinding()) {

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2289,7 +2289,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
22892289
if (PBD->isInitialized(i)) {
22902290
// Add the attribute that preserves the "has an initializer" value
22912291
// across module generation, as required for TBDGen.
2292-
if (var->hasStorage() &&
2292+
if (var->supportsInitialization() &&
22932293
!var->getAttrs().hasAttribute<HasInitialValueAttr>()) {
22942294
var->getAttrs().add(new (Ctx)
22952295
HasInitialValueAttr(/*IsImplicit=*/true));

lib/Sema/TypeCheckMacros.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1302,9 +1302,11 @@ Optional<unsigned> swift::expandAccessors(
13021302
!accessorMacroOnlyIntroducesObservers(macro, roleAttr);
13031303
if (foundNonObservingAccessor) {
13041304
// If any non-observing accessor was added, mark the initializer as
1305-
// subsumed.
1305+
// subsumed unless it has init accessor, because the initializer in
1306+
// such cases could be used for memberwise initialization.
13061307
if (auto var = dyn_cast<VarDecl>(storage)) {
1307-
if (auto binding = var->getParentPatternBinding()) {
1308+
if (auto binding = var->getParentPatternBinding();
1309+
!var->getAccessor(AccessorKind::Init)) {
13081310
unsigned index = binding->getPatternEntryIndexForVarDecl(var);
13091311
binding->setInitializerSubsumed(index);
13101312
}

lib/Sema/TypeCheckStorage.cpp

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -301,8 +301,12 @@ StoredPropertiesAndMissingMembersRequest::evaluate(Evaluator &evaluator,
301301
return decl->getASTContext().AllocateCopy(results);
302302
}
303303

304-
/// Determine whether the given variable has an init accessor.
305-
static bool hasInitAccessor(VarDecl *var) {
304+
bool HasInitAccessorRequest::evaluate(Evaluator &evaluator,
305+
AbstractStorageDecl *decl) const {
306+
auto *var = dyn_cast<VarDecl>(decl);
307+
if (!var)
308+
return false;
309+
306310
if (var->getAccessor(AccessorKind::Init))
307311
return true;
308312

@@ -340,7 +344,7 @@ InitAccessorPropertiesRequest::evaluate(Evaluator &evaluator,
340344
SmallVector<VarDecl *, 4> results;
341345
for (auto *member : decl->getMembers()) {
342346
auto *var = dyn_cast<VarDecl>(member);
343-
if (!var || var->isStatic() || !hasInitAccessor(var)) {
347+
if (!var || var->isStatic() || !var->hasInitAccessor()) {
344348
continue;
345349
}
346350

@@ -3362,7 +3366,7 @@ static void finishStorageImplInfo(AbstractStorageDecl *storage,
33623366
auto dc = storage->getDeclContext();
33633367

33643368
if (auto var = dyn_cast<VarDecl>(storage)) {
3365-
if (!info.hasStorage()) {
3369+
if (!info.hasStorage() && !var->hasInitAccessor()) {
33663370
if (auto *init = var->getParentExecutableInitializer()) {
33673371
auto &Diags = var->getASTContext().Diags;
33683372
Diags.diagnose(init->getLoc(), diag::getset_init)

test/Interpreter/init_accessors.swift

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,3 +393,72 @@ test_memberwise_ordering()
393393
// CHECK: test-memberwise-ordering-1: Test1(_a: 0, _b: 42)
394394
// CHECK-NEXT: test-memberwise-ordering-2: Test2(_a: -1, _b: -2)
395395
// CHECK-NEXT: test-memberwise-ordering-3: Test3(_a: 1, _b: 2, _c: 3)
396+
397+
func test_memberwise_with_default_args() {
398+
struct TestWithoutDefault {
399+
var _a: Int
400+
var _b: Int
401+
402+
var pair: (Int, Int) = (-1, 42) {
403+
init(initialValue) initializes(_a, _b) {
404+
_a = initialValue.0
405+
_b = initialValue.1
406+
}
407+
408+
get { (0, 42) }
409+
set { }
410+
}
411+
}
412+
413+
let test1 = TestWithoutDefault()
414+
print("test-memberwise_with_default-1: \(test1)")
415+
416+
let test2 = TestWithoutDefault(pair: (42, -1))
417+
print("test-memberwise_with_default-2: \(test2)")
418+
419+
struct TestDefaulted {
420+
var _a: Int = 0
421+
var _b: Int = 0
422+
423+
var pair: (Int, Int) = (1, 2) {
424+
init(initialValue) initializes(_a, _b) {
425+
_a = initialValue.0
426+
_b = initialValue.1
427+
}
428+
429+
get { (_a, _b) }
430+
set { }
431+
}
432+
}
433+
434+
let test3 = TestDefaulted()
435+
print("test-defaulted-1: \(test3)")
436+
437+
let test4 = TestDefaulted(pair: (3, 4))
438+
print("test-defaulted-2: \(test4)")
439+
440+
class TestClass {
441+
var _q: String = "<<default>>"
442+
var _a: Int = 1
443+
444+
var pair: (String, Int) = ("", 42) {
445+
init(initialValue) initializes(_q, _a) {
446+
_q = initialValue.0
447+
_a = initialValue.1
448+
}
449+
450+
get { (_q, _a) }
451+
set { }
452+
}
453+
}
454+
455+
let test5 = TestClass()
456+
print("test-defaulted-class: \(test5.pair)")
457+
}
458+
459+
test_memberwise_with_default_args()
460+
// CHECK: test-memberwise_with_default-1: TestWithoutDefault(_a: -1, _b: 42)
461+
// CHECK-NEXT: test-memberwise_with_default-2: TestWithoutDefault(_a: 42, _b: -1)
462+
// CHECK-NEXT: test-defaulted-1: TestDefaulted(_a: 0, _b: 0)
463+
// CHECK-NEXT: test-defaulted-2: TestDefaulted(_a: 3, _b: 4)
464+
// CHECK-NEXT: test-defaulted-class: ("<<default>>", 1)

test/decl/var/init_accessors.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,3 +444,24 @@ func test_memberwise_ordering() {
444444

445445
_ = Test5(_a: 0, _b: 1, c: 2) // Ok
446446
}
447+
448+
func test_default_arguments_are_analyzed() {
449+
struct Test {
450+
var pair: (Int, Int) = (0, 1) { // Ok
451+
init {}
452+
}
453+
454+
var other: (Int, String) = ("", 42) {
455+
// expected-error@-1 {{cannot convert value of type '(String, Int)' to specified type '(Int, String)'}}
456+
init(initialValue) {}
457+
}
458+
459+
var otherPair = (0, 1) {
460+
// expected-error@-1 {{computed property must have an explicit type}}
461+
init(initalValue) {}
462+
463+
get { 42 }
464+
// expected-error@-1 {{cannot convert return expression of type 'Int' to return type '(Int, Int)'}}
465+
}
466+
}
467+
}

0 commit comments

Comments
 (0)