Skip to content

Commit ad95e91

Browse files
committed
[Sema/SIL] InitAccessors: Incorporate init accessors into memberwise initializers
Properties with init accessors are used in place of properties they are supposed to initialize in order of stored properties.
1 parent 22061e6 commit ad95e91

File tree

5 files changed

+199
-3
lines changed

5 files changed

+199
-3
lines changed

include/swift/AST/Decl.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include "swift/Basic/Range.h"
4848
#include "llvm/ADT/DenseSet.h"
4949
#include "llvm/Support/TrailingObjects.h"
50+
#include <map>
5051
#include <type_traits>
5152

5253
namespace swift {
@@ -3985,6 +3986,12 @@ class NominalTypeDecl : public GenericTypeDecl, public IterableDeclContext {
39853986
/// this type.
39863987
ArrayRef<VarDecl *> getInitAccessorProperties() const;
39873988

3989+
/// Establish a mapping between properties that could be iniitalized
3990+
/// via other properties by means of init accessors. This mapping is
3991+
/// one-to-many because we allow intersecting `initializes(...)`.
3992+
void collectPropertiesInitializableByInitAccessors(
3993+
std::multimap<VarDecl *, VarDecl *> &result) const;
3994+
39883995
/// Return a collection of the stored member variables of this type, along
39893996
/// with placeholders for unimportable stored properties.
39903997
ArrayRef<Decl *> getStoredPropertiesAndMissingMemberPlaceholders() const;

lib/AST/Decl.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4894,6 +4894,18 @@ NominalTypeDecl::getInitAccessorProperties() const {
48944894
{});
48954895
}
48964896

4897+
void NominalTypeDecl::collectPropertiesInitializableByInitAccessors(
4898+
std::multimap<VarDecl *, VarDecl *> &result) const {
4899+
for (auto *property : getInitAccessorProperties()) {
4900+
auto *initAccessor = property->getAccessor(AccessorKind::Init);
4901+
if (auto *initAttr =
4902+
initAccessor->getAttrs().getAttribute<InitializesAttr>()) {
4903+
for (auto *subsumed : initAttr->getPropertyDecls(initAccessor))
4904+
result.insert({subsumed, property});
4905+
}
4906+
}
4907+
}
4908+
48974909
ArrayRef<Decl *>
48984910
NominalTypeDecl::getStoredPropertiesAndMissingMemberPlaceholders() const {
48994911
auto &ctx = getASTContext();

lib/SILGen/SILGenConstructor.cpp

Lines changed: 84 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "swift/SIL/SILInstruction.h"
3131
#include "swift/SIL/SILUndef.h"
3232
#include "swift/SIL/TypeLowering.h"
33+
#include <map>
3334

3435
using namespace swift;
3536
using namespace Lowering;
@@ -271,6 +272,47 @@ static RValue maybeEmitPropertyWrapperInitFromValue(
271272
subs, std::move(arg));
272273
}
273274

275+
static void emitApplyOfInitAccessor(SILGenFunction &SGF, SILLocation loc,
276+
AccessorDecl *accessor, SILValue selfValue,
277+
SILType selfTy, RValue &&initialValue) {
278+
SmallVector<SILValue> arguments;
279+
280+
auto emitFieldReference = [&](VarDecl *field) {
281+
auto fieldTy =
282+
selfTy.getFieldType(field, SGF.SGM.M, SGF.getTypeExpansionContext());
283+
return SGF.B.createStructElementAddr(loc, selfValue, field,
284+
fieldTy.getAddressType());
285+
};
286+
287+
// First, let's emit all of the indirect results.
288+
if (auto *initAttr = accessor->getAttrs().getAttribute<InitializesAttr>()) {
289+
for (auto *property : initAttr->getPropertyDecls(accessor)) {
290+
arguments.push_back(emitFieldReference(property));
291+
}
292+
}
293+
294+
// `initialValue`
295+
std::move(initialValue).forwardAll(SGF, arguments);
296+
297+
// And finally, all of the properties in `accesses(...)` list which are
298+
// `inout` arguments.
299+
if (auto *accessAttr = accessor->getAttrs().getAttribute<AccessesAttr>()) {
300+
for (auto *property : accessAttr->getPropertyDecls(accessor)) {
301+
arguments.push_back(emitFieldReference(property));
302+
}
303+
}
304+
305+
SubstitutionMap subs;
306+
if (auto *env =
307+
accessor->getDeclContext()->getGenericEnvironmentOfContext()) {
308+
subs = env->getForwardingSubstitutionMap();
309+
}
310+
311+
SILValue accessorRef =
312+
SGF.emitGlobalFunctionRef(loc, SGF.getAccessorDeclRef(accessor));
313+
(void)SGF.B.createApply(loc, accessorRef, subs, arguments, ApplyOptions());
314+
}
315+
274316
static SubstitutionMap getSubstitutionsForPropertyInitializer(
275317
DeclContext *dc,
276318
NominalTypeDecl *nominal) {
@@ -312,6 +354,12 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF,
312354
auto selfIfaceTy = selfDecl->getInterfaceType();
313355
SILType selfTy = SGF.getSILTypeInContext(selfResultInfo, loweredFunctionTy);
314356

357+
auto *decl = selfTy.getStructOrBoundGenericStruct();
358+
assert(decl && "not a struct?!");
359+
360+
std::multimap<VarDecl *, VarDecl *> initializedViaAccessor;
361+
decl->collectPropertiesInitializableByInitAccessors(initializedViaAccessor);
362+
315363
// Emit the indirect return argument, if any.
316364
SILValue resultSlot;
317365
if (selfTy.isAddress()) {
@@ -324,6 +372,10 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF,
324372
VD->setSpecifier(ParamSpecifier::InOut);
325373
VD->setInterfaceType(selfIfaceTy);
326374
resultSlot = SGF.F.begin()->createFunctionArgument(selfTy, VD);
375+
} else if (!initializedViaAccessor.empty()) {
376+
// Allocate "self" on stack which we are going to use to
377+
// reference/init fields and then load to return.
378+
resultSlot = SGF.emitTemporaryAllocation(Loc, selfTy);
327379
}
328380

329381
LoweredParamsInContextGenerator loweredParams(SGF);
@@ -343,15 +395,34 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF,
343395
(void) loweredParams.claimNext();
344396
loweredParams.finish();
345397

346-
auto *decl = selfTy.getStructOrBoundGenericStruct();
347-
assert(decl && "not a struct?!");
348-
349398
auto subs = getSubstitutionsForPropertyInitializer(decl, decl);
350399

351400
// If we have an indirect return slot, initialize it in-place.
352401
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+
353406
auto elti = elements.begin(), eltEnd = elements.end();
354407
for (VarDecl *field : decl->getStoredProperties()) {
408+
409+
// Handle situations where this stored propery is initialized
410+
// 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))
416+
continue;
417+
418+
emitApplyOfInitAccessor(SGF, Loc, initAccessor, resultSlot, selfTy,
419+
std::move(*elti));
420+
421+
emittedInitAccessors.insert(initAccessor);
422+
++elti;
423+
continue;
424+
}
425+
355426
auto fieldTy =
356427
selfTy.getFieldType(field, SGF.SGM.M, SGF.getTypeExpansionContext());
357428
SILValue slot =
@@ -423,6 +494,16 @@ static void emitImplicitValueConstructor(SILGenFunction &SGF,
423494
}
424495
}
425496

497+
// Load as "take" from our stack allocation and return.
498+
if (!selfTy.isAddress() && !initializedViaAccessor.empty()) {
499+
auto resultValue = SGF.B.emitLoadValueOperation(
500+
Loc, resultSlot, LoadOwnershipQualifier::Take);
501+
502+
SGF.B.createReturn(ImplicitReturnLocation(Loc), resultValue,
503+
std::move(functionLevelScope));
504+
return;
505+
}
506+
426507
SGF.B.createReturn(ImplicitReturnLocation(Loc),
427508
SGF.emitEmptyTuple(Loc), std::move(functionLevelScope));
428509
return;

lib/Sema/CodeSynthesis.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,12 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl,
294294
if (ICK == ImplicitConstructorKind::Memberwise) {
295295
assert(isa<StructDecl>(decl) && "Only struct have memberwise constructor");
296296

297+
std::multimap<VarDecl *, VarDecl *> initializedViaAccessor;
298+
decl->collectPropertiesInitializableByInitAccessors(initializedViaAccessor);
299+
300+
// A single property could be used to initialize N other stored
301+
// properties via a call to its init accessor.
302+
llvm::SmallPtrSet<VarDecl *, 4> usedInitProperties;
297303
for (auto member : decl->getMembers()) {
298304
auto var = dyn_cast<VarDecl>(member);
299305
if (!var)
@@ -302,6 +308,20 @@ static ConstructorDecl *createImplicitConstructor(NominalTypeDecl *decl,
302308
if (!var->isMemberwiseInitialized(/*preferDeclaredProperties=*/true))
303309
continue;
304310

311+
// Check whether this property could be initialized via init accessor.
312+
//
313+
// Note that we check for a single match here because intersecting
314+
// properties are going to be diagnosed.
315+
if (initializedViaAccessor.count(var) == 1) {
316+
auto *initializerProperty = initializedViaAccessor.find(var)->second;
317+
// Parameter for this property is already emitted.
318+
if (usedInitProperties.count(initializerProperty))
319+
continue;
320+
321+
var = initializerProperty;
322+
usedInitProperties.insert(initializerProperty);
323+
}
324+
305325
accessLevel = std::min(accessLevel, var->getFormalAccess());
306326

307327
params.push_back(createMemberwiseInitParameter(decl, Loc, var));

test/SIL/init_accessors.swift

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,3 +303,79 @@ struct TestGeneric<T, U> {
303303
self.data = (b, a)
304304
}
305305
}
306+
307+
func test_local_with_memberwise() {
308+
class MyValue {}
309+
310+
struct TestMemberwiseConcrete {
311+
var a: Int
312+
var b: String
313+
314+
var pair: (Int, String) {
315+
init(initialValue) initializes(a b) {
316+
a = initialValue.0
317+
b = initialValue.1
318+
}
319+
320+
get { (a, b) }
321+
set { }
322+
}
323+
324+
var c: [MyValue]
325+
326+
// CHECK-LABEL: sil private @$s14init_accessors26test_local_with_memberwiseyyF22TestMemberwiseConcreteL_V4pair1cADSi_SSt_SayAaByyF7MyValueL_CGtcfC : $@convention(method) (Int, @owned String, @owned Array<MyValue>, @thin TestMemberwiseConcrete.Type) -> @owned TestMemberwiseConcrete
327+
// CHECK: [[SELF_VALUE:%.*]] = alloc_stack $TestMemberwiseConcrete
328+
// CHECK-NEXT: [[A_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*TestMemberwiseConcrete, #<abstract function>TestMemberwiseConcrete.a
329+
// CHECK-NEXT: [[B_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*TestMemberwiseConcrete, #<abstract function>TestMemberwiseConcrete.b
330+
// CHECK: [[INIT_ACCESSOR_REF:%.*]] = function_ref @$s14init_accessors26test_local_with_memberwiseyyF22TestMemberwiseConcreteL_V4pairSi_SStvi : $@convention(thin) (Int, @owned String) -> (@out Int, @out String)
331+
// CHECK-NEXT: {{.*}} = apply [[INIT_ACCESSOR_REF]]([[A_REF]], [[B_REF]], %0, %1) : $@convention(thin) (Int, @owned String) -> (@out Int, @out String)
332+
// CHECK-NEXT: [[C_REF:%.*]] = struct_element_addr %4 : $*TestMemberwiseConcrete, #<abstract function>TestMemberwiseConcrete.c
333+
// CHECK-NEXT: store %2 to [[C_REF]] : $*Array<MyValue>
334+
// CHECK-NEXT: [[RESULT:%.*]] = load [[SELF_VALUE]] : $*TestMemberwiseConcrete
335+
// CHECK-NEXT: dealloc_stack [[SELF_VALUE]] : $*TestMemberwiseConcrete
336+
// CHECK-NEXT: return [[RESULT]] : $TestMemberwiseConcrete
337+
}
338+
339+
_ = TestMemberwiseConcrete(pair: (0, "a"), c: [])
340+
341+
struct TestMemberwiseGeneric<T, C> where C: RangeReplaceableCollection, C.Element == T {
342+
var _a: T
343+
var _b: String
344+
var _c: C
345+
346+
var a: T {
347+
init(initialValue) initializes(_a) {
348+
_a = initialValue
349+
}
350+
351+
get { _a }
352+
set { }
353+
}
354+
355+
var pair: (String, C) {
356+
init(initialValue) initializes(_b _c) accesses(_a) {
357+
_b = initialValue.0
358+
_c = initialValue.1
359+
_c.append(_a)
360+
}
361+
362+
get { (_b, _c) }
363+
set { }
364+
}
365+
366+
// CHECK-LABEL: sil private @$s14init_accessors26test_local_with_memberwiseyyF21TestMemberwiseGenericL_V1a4pairADyxq_Gx_SS_q_ttcfC : $@convention(method) <T, C where T == C.Element, C : RangeReplaceableCollection> (@in T, @owned String, @in C, @thin TestMemberwiseGeneric<T, C>.Type) -> @out TestMemberwiseGeneric<T, C>
367+
// CHECK: bb0([[SELF_VALUE:%.*]] : $*TestMemberwiseGeneric<T, C>, [[A_VALUE:%*.]] : $*T, [[B_VALUE:%.*]] : $String, [[C_VALUE:%.*]] : $*C, [[METATYPE:%.*]] : $@thin TestMemberwiseGeneric<T, C>.Type):
368+
// CHECK-NEXT: [[A_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*TestMemberwiseGeneric<T, C>, #<abstract function>TestMemberwiseGeneric._a
369+
// CHECK: [[INIT_ACCESSOR_REF:%.*]] = function_ref @$s14init_accessors26test_local_with_memberwiseyyF21TestMemberwiseGenericL_V1axvi : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 == τ_0_1.Element, τ_0_1 : RangeReplaceableCollection> (@in τ_0_0) -> @out τ_0_0 // user: %7
370+
// CHECK-NEXT: {{.*}} = apply [[INIT_ACCESSOR_REF]]<T, C>([[A_REF]], [[A_VALUE]]) : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 == τ_0_1.Element, τ_0_1 : RangeReplaceableCollection> (@in τ_0_0) -> @out τ_0_0
371+
// CHECK-NEXT: [[B_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*TestMemberwiseGeneric<T, C>, #<abstract function>TestMemberwiseGeneric._b
372+
// CHECK-NEXT: [[C_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*TestMemberwiseGeneric<T, C>, #<abstract function>TestMemberwiseGeneric._c
373+
// CHECK-NEXT: [[A_REF:%.*]] = struct_element_addr [[SELF_VALUE]] : $*TestMemberwiseGeneric<T, C>, #<abstract function>TestMemberwiseGeneric._a
374+
// CHECK: [[INIT_ACCESSOR_REF:%.*]] = function_ref @$s14init_accessors26test_local_with_memberwiseyyF21TestMemberwiseGenericL_V4pairSS_q_tvi : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 == τ_0_1.Element, τ_0_1 : RangeReplaceableCollection> (@owned String, @in τ_0_1, @inout τ_0_0) -> (@out String, @out τ_0_1)
375+
// CHECK-NEXT: {{.*}} = apply [[INIT_ACCESSOR_REF]]<T, C>([[B_REF]], [[C_REF]], [[B_VALUE]], [[C_VALUE]], [[A_REF]]) : $@convention(thin) <τ_0_0, τ_0_1 where τ_0_0 == τ_0_1.Element, τ_0_1 : RangeReplaceableCollection> (@owned String, @in τ_0_1, @inout τ_0_0) -> (@out String, @out τ_0_1)
376+
// CHECK-NEXT: [[VOID:%.*]] = tuple ()
377+
// CHECK-NEXT: return [[VOID]] : $()
378+
}
379+
380+
_ = TestMemberwiseGeneric(a: 1, pair: ("a", [0]))
381+
}

0 commit comments

Comments
 (0)