Skip to content

[5.9][Sema/SIL] InitAccessors: Support default initialization of init accessor properties #67437

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 47 additions & 3 deletions lib/SILGen/SILGenConstructor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1459,6 +1459,49 @@ emitAndStoreInitialValueInto(SILGenFunction &SGF,
std::move(result).forwardInto(SGF, loc, init);
}

void SILGenFunction::emitMemberInitializationViaInitAccessor(
DeclContext *dc, VarDecl *selfDecl, PatternBindingDecl *member,
SubstitutionMap subs) {
auto *var = member->getSingleVar();
assert(var->hasInitAccessor());

auto init = member->getExecutableInit(0);
if (!init)
return;

auto *varPattern = member->getPattern(0);

// Cleanup after this initialization.
FullExpr scope(Cleanups, varPattern);

auto resultType =
getInitializationTypeInContext(member->getDeclContext(), dc, varPattern);

RValue initResult = emitApplyOfStoredPropertyInitializer(
init, var, subs, resultType.second, resultType.first, SGFContext());

SILLocation loc(init);
loc.markAutoGenerated();

auto selfValue = emitSelfForMemberInit(*this, varPattern, selfDecl);

ManagedValue selfRef = selfValue;
if (selfValue.isLValue()) {
auto accessToSelf =
B.createBeginAccess(loc, selfValue.getValue(), SILAccessKind::Modify,
SILAccessEnforcement::Unknown,
/*noNestedConflict=*/false,
/*fromBuiltin=*/false);
selfRef = ManagedValue::forUnmanaged(accessToSelf);
}

emitAssignOrInit(loc, selfRef, var,
std::move(initResult).getAsSingleValue(*this, loc), subs);

if (selfValue.isLValue())
B.createEndAccess(loc, selfRef.getValue(), /*aborted=*/false);
}

void SILGenFunction::emitMemberInitializers(DeclContext *dc,
VarDecl *selfDecl,
NominalTypeDecl *nominal) {
Expand All @@ -1469,11 +1512,12 @@ void SILGenFunction::emitMemberInitializers(DeclContext *dc,
if (auto pbd = dyn_cast<PatternBindingDecl>(member)) {
if (pbd->isStatic()) continue;

// Skip properties with init accessors, they could only be used
// explicitly and in memberwise initializers.
// Emit default initialization for an init accessor property.
if (auto *var = pbd->getSingleVar()) {
if (var->hasInitAccessor())
if (var->hasInitAccessor()) {
emitMemberInitializationViaInitAccessor(dc, selfDecl, pbd, subs);
continue;
}
}

for (auto i : range(pbd->getNumPatternEntries())) {
Expand Down
108 changes: 108 additions & 0 deletions lib/SILGen/SILGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1754,3 +1754,111 @@ ParamDecl *SILGenFunction::isMappedToInitAccessorArgument(VarDecl *property) {

return arg->second;
}

SILValue
SILGenFunction::emitApplyOfSetterToBase(SILLocation loc, SILDeclRef setter,
ManagedValue base,
SubstitutionMap substitutions) {
auto setterFRef = [&]() -> SILValue {
auto setterInfo = getConstantInfo(getTypeExpansionContext(), setter);
if (setter.hasDecl() && setter.getDecl()->shouldUseObjCDispatch()) {
// Emit a thunk we might have to bridge arguments.
auto foreignSetterThunk = setter.asForeign(false);
return emitDynamicMethodRef(
loc, foreignSetterThunk,
SGM.Types
.getConstantInfo(getTypeExpansionContext(),
foreignSetterThunk)
.SILFnType)
.getValue();
}

return emitGlobalFunctionRef(loc, setter, setterInfo);
}();

auto getSetterType = [&](SILValue setterFRef) {
CanSILFunctionType setterTy =
setterFRef->getType().castTo<SILFunctionType>();
return setterTy->substGenericArgs(SGM.M, substitutions,
getTypeExpansionContext());
};

SILFunctionConventions setterConv(getSetterType(setterFRef), SGM.M);

// Emit captures for the setter
SmallVector<SILValue, 4> capturedArgs;
auto captureInfo = SGM.Types.getLoweredLocalCaptures(setter);
if (!captureInfo.getCaptures().empty()) {
SmallVector<ManagedValue, 4> captures;
emitCaptures(loc, setter, CaptureEmission::AssignByWrapper, captures);

for (auto capture : captures)
capturedArgs.push_back(capture.forward(*this));
} else {
assert(base);

SILValue capturedBase;
unsigned argIdx = setterConv.getNumSILArguments() - 1;

if (setterConv.getSILArgumentConvention(argIdx).isInoutConvention()) {
capturedBase = base.getValue();
} else if (base.getType().isAddress() &&
base.getType().getObjectType() ==
setterConv.getSILArgumentType(argIdx,
getTypeExpansionContext())) {
// If the base is a reference and the setter expects a value, emit a
// load. This pattern is emitted for property wrappers with a
// nonmutating setter, for example.
capturedBase = B.createTrivialLoadOr(loc, base.getValue(),
LoadOwnershipQualifier::Copy);
} else {
capturedBase = base.copy(*this, loc).forward(*this);
}

capturedArgs.push_back(capturedBase);
}

PartialApplyInst *setterPAI =
B.createPartialApply(loc, setterFRef, substitutions, capturedArgs,
ParameterConvention::Direct_Guaranteed);
return emitManagedRValueWithCleanup(setterPAI).getValue();
}

void SILGenFunction::emitAssignOrInit(SILLocation loc, ManagedValue selfValue,
VarDecl *field, ManagedValue newValue,
SubstitutionMap substitutions) {
auto fieldTy = field->getValueInterfaceType();
if (!substitutions.empty())
fieldTy = fieldTy.subst(substitutions);

// Emit the init accessor function partially applied to the base.
SILValue initFRef = emitGlobalFunctionRef(
loc, getAccessorDeclRef(field->getOpaqueAccessor(AccessorKind::Init)));
if (!substitutions.empty()) {
// If there are substitutions we need to emit partial apply to
// apply substitutions to the init accessor reference type.
auto initTy =
initFRef->getType().castTo<SILFunctionType>()->substGenericArgs(
SGM.M, substitutions, getTypeExpansionContext());

SILFunctionConventions setterConv(initTy, SGM.M);

// Emit partial apply without argument to produce a substituted
// init accessor reference.
PartialApplyInst *initPAI =
B.createPartialApply(loc, initFRef, substitutions, ArrayRef<SILValue>(),
ParameterConvention::Direct_Guaranteed);
initFRef = emitManagedRValueWithCleanup(initPAI).getValue();
}

SILValue setterFRef;
if (auto *setter = field->getOpaqueAccessor(AccessorKind::Set)) {
setterFRef = emitApplyOfSetterToBase(loc, SILDeclRef(setter), selfValue,
substitutions);
} else {
setterFRef = SILUndef::get(initFRef->getType(), F);
}

B.createAssignOrInit(loc, selfValue.getValue(), newValue.forward(*this),
initFRef, setterFRef, AssignOrInitInst::Unknown);
}
21 changes: 21 additions & 0 deletions lib/SILGen/SILGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,11 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
void emitMemberInitializers(DeclContext *dc, VarDecl *selfDecl,
NominalTypeDecl *nominal);

void emitMemberInitializationViaInitAccessor(DeclContext *dc,
VarDecl *selfDecl,
PatternBindingDecl *member,
SubstitutionMap subs);

/// Emit a method that initializes the ivars of a class.
void emitIVarInitializer(SILDeclRef ivarInitializer);

Expand All @@ -814,6 +819,22 @@ class LLVM_LIBRARY_VISIBILITY SILGenFunction
/// value assignment.
void emitInitAccessor(AccessorDecl *accessor);

/// Generates code to emit the given setter reference to the given base value.
SILValue emitApplyOfSetterToBase(SILLocation loc, SILDeclRef setter,
ManagedValue base,
SubstitutionMap substitutions);

/// Emit `assign_or_init` instruction that is going to either initialize
/// or assign the given value to the given field.
///
/// \param loc The location to use for the instruction.
/// \param selfValue The 'self' value.
/// \param field The field to assign or initialize.
/// \param newValue the value to assign/initialize the field with.
/// \param substitutions The substitutions to apply to initializer and setter.
void emitAssignOrInit(SILLocation loc, ManagedValue selfValue, VarDecl *field,
ManagedValue newValue, SubstitutionMap substitutions);

/// Generates code to destroy the instance variables of a class.
///
/// \param selfValue The 'self' value.
Expand Down
Loading