Skip to content

Commit 71eb2a7

Browse files
committed
[SILGen] Start emitting assign_by_wrapper for type wrappers
Properties that are managed by a type wrapper have to be initialized through a temporary `_storage` variable and only DI can tell us when `_storage` is fully initialized, so we need to emit a `assign_by_wrapper` instruction (with originator - type wrapper) for every assignment to keep that and inject `$_storage.init` as soon as all the managed properties are initialized.
1 parent df87a49 commit 71eb2a7

File tree

1 file changed

+173
-87
lines changed

1 file changed

+173
-87
lines changed

lib/SILGen/SILGenLValue.cpp

Lines changed: 173 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1407,6 +1407,21 @@ namespace {
14071407
return false;
14081408
}
14091409

1410+
bool canRewriteSetAsTypeWrapperInit(SILGenFunction &SGF) const {
1411+
auto *VD = dyn_cast<VarDecl>(Storage);
1412+
if (!(VD && VD->isAccessedViaTypeWrapper()))
1413+
return false;
1414+
1415+
auto *fnDecl = SGF.FunctionDC->getAsDecl();
1416+
// Type wrapper transform applies only to user-defined
1417+
// designated initializers.
1418+
if (auto *ctor = dyn_cast_or_null<ConstructorDecl>(fnDecl)) {
1419+
return !ctor->isImplicit() && ctor->isDesignatedInit();
1420+
}
1421+
1422+
return false;
1423+
}
1424+
14101425
void emitAssignWithSetter(SILGenFunction &SGF, SILLocation loc,
14111426
LValue &&dest, ArgumentSource &&value) {
14121427
assert(getAccessorDecl()->isSetter());
@@ -1470,6 +1485,154 @@ namespace {
14701485
assert(!ActorIso && "no support for cross-actor set operations");
14711486
SILDeclRef setter = Accessor;
14721487

1488+
auto getSetterFRef = [&]() -> SILValue {
1489+
auto setterInfo =
1490+
SGF.getConstantInfo(SGF.getTypeExpansionContext(), setter);
1491+
if (setter.hasDecl() && setter.getDecl()->shouldUseObjCDispatch()) {
1492+
// Emit a thunk we might have to bridge arguments.
1493+
auto foreignSetterThunk = setter.asForeign(false);
1494+
return SGF
1495+
.emitDynamicMethodRef(
1496+
loc, foreignSetterThunk,
1497+
SGF.SGM.Types
1498+
.getConstantInfo(SGF.getTypeExpansionContext(),
1499+
foreignSetterThunk)
1500+
.SILFnType)
1501+
.getValue();
1502+
}
1503+
1504+
return SGF.emitGlobalFunctionRef(loc, setter, setterInfo);
1505+
};
1506+
1507+
auto getSetterType = [&](SILValue setterFRef) {
1508+
CanSILFunctionType setterTy =
1509+
setterFRef->getType().castTo<SILFunctionType>();
1510+
return setterTy->substGenericArgs(SGF.SGM.M, Substitutions,
1511+
SGF.getTypeExpansionContext());
1512+
};
1513+
1514+
auto emitPartialSetterApply =
1515+
[&](SILValue setterFRef,
1516+
const SILFunctionConventions &setterConv) -> ManagedValue {
1517+
// Emit captures for thI e setter
1518+
SmallVector<SILValue, 4> capturedArgs;
1519+
auto captureInfo = SGF.SGM.Types.getLoweredLocalCaptures(setter);
1520+
if (!captureInfo.getCaptures().empty()) {
1521+
SmallVector<ManagedValue, 4> captures;
1522+
SGF.emitCaptures(loc, setter, CaptureEmission::AssignByWrapper,
1523+
captures);
1524+
1525+
for (auto capture : captures)
1526+
capturedArgs.push_back(capture.forward(SGF));
1527+
} else {
1528+
assert(base);
1529+
1530+
SILValue capturedBase;
1531+
unsigned argIdx = setterConv.getNumSILArguments() - 1;
1532+
1533+
if (setterConv.getSILArgumentConvention(argIdx).isInoutConvention()) {
1534+
capturedBase = base.getValue();
1535+
} else if (base.getType().isAddress() &&
1536+
base.getType().getObjectType() ==
1537+
setterConv.getSILArgumentType(
1538+
argIdx, SGF.getTypeExpansionContext())) {
1539+
// If the base is a reference and the setter expects a value, emit a
1540+
// load. This pattern is emitted for property wrappers with a
1541+
// nonmutating setter, for example.
1542+
capturedBase = SGF.B.createTrivialLoadOr(
1543+
loc, base.getValue(), LoadOwnershipQualifier::Copy);
1544+
} else {
1545+
capturedBase = base.copy(SGF, loc).forward(SGF);
1546+
}
1547+
1548+
capturedArgs.push_back(capturedBase);
1549+
}
1550+
1551+
PartialApplyInst *setterPAI = SGF.B.createPartialApply(
1552+
loc, setterFRef, Substitutions, capturedArgs,
1553+
ParameterConvention::Direct_Guaranteed);
1554+
return SGF.emitManagedRValueWithCleanup(setterPAI);
1555+
};
1556+
1557+
auto emitValue =
1558+
[&](VarDecl *field, Type fieldType, CanSILFunctionType setterTy,
1559+
const SILFunctionConventions &setterConv) -> ManagedValue {
1560+
// FIXME: This should use CallEmission instead of doing everything
1561+
// manually.
1562+
assert(value.isRValue());
1563+
ManagedValue Mval =
1564+
std::move(value).asKnownRValue(SGF).getAsSingleValue(SGF, loc);
1565+
auto param = setterTy->getParameters()[0];
1566+
SILType loweredSubstArgType = Mval.getType();
1567+
if (param.isIndirectInOut()) {
1568+
loweredSubstArgType = SILType::getPrimitiveAddressType(
1569+
loweredSubstArgType.getASTType());
1570+
}
1571+
auto loweredSubstParamTy = SILType::getPrimitiveType(
1572+
param.getArgumentType(SGF.SGM.M, setterTy,
1573+
SGF.getTypeExpansionContext()),
1574+
loweredSubstArgType.getCategory());
1575+
// Handle reabstraction differences.
1576+
if (Mval.getType() != loweredSubstParamTy) {
1577+
Mval = SGF.emitSubstToOrigValue(
1578+
loc, Mval, SGF.SGM.Types.getAbstractionPattern(field),
1579+
fieldType->getCanonicalType());
1580+
}
1581+
1582+
// If we need the argument in memory, materialize an address.
1583+
if (setterConv.getSILArgumentConvention(0).isIndirectConvention() &&
1584+
!Mval.getType().isAddress()) {
1585+
Mval = Mval.materialize(SGF, loc);
1586+
}
1587+
1588+
return Mval;
1589+
};
1590+
1591+
if (canRewriteSetAsTypeWrapperInit(SGF)) {
1592+
auto *ctor = cast<ConstructorDecl>(SGF.FunctionDC->getAsDecl());
1593+
auto *field = cast<VarDecl>(Storage);
1594+
auto FieldType = field->getValueInterfaceType();
1595+
if (!Substitutions.empty()) {
1596+
FieldType = FieldType.subst(Substitutions);
1597+
}
1598+
1599+
auto *localVar = ctor->getLocalTypeWrapperStorageVar();
1600+
1601+
// First, we need to find index of the current field in `_storage.
1602+
auto *tempType = localVar->getInterfaceType()->castTo<TupleType>();
1603+
int fieldIdx = tempType->getNamedElementId(field->getName());
1604+
assert(fieldIdx >= 0);
1605+
1606+
// Load `_storage.<name>`
1607+
auto storageBase =
1608+
SGF.maybeEmitValueOfLocalVarDecl(localVar, AccessKind::Write);
1609+
1610+
auto typeData = getLogicalStorageTypeData(
1611+
SGF.getTypeExpansionContext(), SGF.SGM, getTypeData().AccessKind,
1612+
FieldType->getCanonicalType());
1613+
1614+
TupleElementComponent TEC(fieldIdx, typeData);
1615+
auto storage = std::move(TEC).project(SGF, loc, storageBase);
1616+
1617+
// Partially apply the setter so it could be used by assign_by_wrapper
1618+
1619+
auto setterFRef = getSetterFRef();
1620+
auto setterTy = getSetterType(setterFRef);
1621+
SILFunctionConventions setterConv(setterTy, SGF.SGM.M);
1622+
auto setterFn = emitPartialSetterApply(setterFRef, setterConv);
1623+
1624+
// Create the assign_by_wrapper with the initializer and setter.
1625+
1626+
auto Mval = emitValue(field, FieldType, setterTy, setterConv);
1627+
1628+
// Inject assign_by_wrapper instruction
1629+
1630+
SGF.B.createAssignByTypeWrapper(
1631+
loc, Mval.forward(SGF), storage.forward(SGF), setterFn.getValue(),
1632+
AssignByWrapperInst::Unknown);
1633+
return;
1634+
}
1635+
14731636
if (canRewriteSetAsPropertyWrapperInit(SGF) &&
14741637
!Storage->isStatic() &&
14751638
isBackingVarVisible(cast<VarDecl>(Storage),
@@ -1528,95 +1691,14 @@ namespace {
15281691
ManagedValue initFn = SGF.emitManagedRValueWithCleanup(initPAI);
15291692

15301693
// Create the allocating setter function. It captures the base address.
1531-
auto setterInfo =
1532-
SGF.getConstantInfo(SGF.getTypeExpansionContext(), setter);
1533-
SILValue setterFRef;
1534-
if (setter.hasDecl() && setter.getDecl()->shouldUseObjCDispatch()) {
1535-
// Emit a thunk we might have to bridge arguments.
1536-
auto foreignSetterThunk = setter.asForeign(false);
1537-
setterFRef =
1538-
SGF.emitDynamicMethodRef(
1539-
loc, foreignSetterThunk,
1540-
SGF.SGM.Types
1541-
.getConstantInfo(SGF.getTypeExpansionContext(), foreignSetterThunk)
1542-
.SILFnType)
1543-
.getValue();
1544-
1545-
} else {
1546-
setterFRef = SGF.emitGlobalFunctionRef(loc, setter, setterInfo);
1547-
}
1548-
1549-
CanSILFunctionType setterTy = setterFRef->getType().castTo<SILFunctionType>();
1550-
auto substSetterTy = setterTy->substGenericArgs(SGF.SGM.M, Substitutions,
1551-
SGF.getTypeExpansionContext());
1552-
SILFunctionConventions setterConv(substSetterTy, SGF.SGM.M);
1553-
1554-
// Emit captures for the setter
1555-
SmallVector<SILValue, 4> capturedArgs;
1556-
auto captureInfo = SGF.SGM.Types.getLoweredLocalCaptures(setter);
1557-
if (!captureInfo.getCaptures().empty()) {
1558-
SmallVector<ManagedValue, 4> captures;
1559-
SGF.emitCaptures(loc, setter, CaptureEmission::AssignByWrapper, captures);
1560-
1561-
for (auto capture : captures)
1562-
capturedArgs.push_back(capture.forward(SGF));
1563-
} else {
1564-
assert(base);
1565-
1566-
SILValue capturedBase;
1567-
unsigned argIdx = setterConv.getNumSILArguments() - 1;
1568-
1569-
if (setterConv.getSILArgumentConvention(argIdx).isInoutConvention()) {
1570-
capturedBase = base.getValue();
1571-
} else if (base.getType().isAddress() &&
1572-
base.getType().getObjectType() ==
1573-
setterConv.getSILArgumentType(argIdx,
1574-
SGF.getTypeExpansionContext())) {
1575-
// If the base is a reference and the setter expects a value, emit a
1576-
// load. This pattern is emitted for property wrappers with a
1577-
// nonmutating setter, for example.
1578-
capturedBase = SGF.B.createTrivialLoadOr(
1579-
loc, base.getValue(), LoadOwnershipQualifier::Copy);
1580-
} else {
1581-
capturedBase = base.copy(SGF, loc).forward(SGF);
1582-
}
1694+
auto setterFRef = getSetterFRef();
1695+
auto setterTy = getSetterType(setterFRef);
1696+
SILFunctionConventions setterConv(setterTy, SGF.SGM.M);
1697+
auto setterFn = emitPartialSetterApply(setterFRef, setterConv);
15831698

1584-
capturedArgs.push_back(capturedBase);
1585-
}
1699+
// Create the assign_by_wrapper with the initializer and setter.
15861700

1587-
PartialApplyInst *setterPAI =
1588-
SGF.B.createPartialApply(loc, setterFRef,
1589-
Substitutions, capturedArgs,
1590-
ParameterConvention::Direct_Guaranteed);
1591-
ManagedValue setterFn = SGF.emitManagedRValueWithCleanup(setterPAI);
1592-
1593-
// Create the assign_by_wrapper with the allocator and setter.
1594-
// FIXME: This should use CallEmission instead of doing everything manually.
1595-
assert(value.isRValue());
1596-
ManagedValue Mval = std::move(value).asKnownRValue(SGF).
1597-
getAsSingleValue(SGF, loc);
1598-
auto param = substSetterTy->getParameters()[0];
1599-
SILType loweredSubstArgType = Mval.getType();
1600-
if (param.isIndirectInOut()) {
1601-
loweredSubstArgType =
1602-
SILType::getPrimitiveAddressType(loweredSubstArgType.getASTType());
1603-
}
1604-
auto loweredSubstParamTy = SILType::getPrimitiveType(
1605-
param.getArgumentType(SGF.SGM.M, substSetterTy,
1606-
SGF.getTypeExpansionContext()),
1607-
loweredSubstArgType.getCategory());
1608-
// Handle reabstraction differences.
1609-
if (Mval.getType() != loweredSubstParamTy) {
1610-
Mval = SGF.emitSubstToOrigValue(loc, Mval,
1611-
SGF.SGM.Types.getAbstractionPattern(field),
1612-
FieldType->getCanonicalType());
1613-
}
1614-
1615-
// If we need the argument in memory, materialize an address.
1616-
if (setterConv.getSILArgumentConvention(0).isIndirectConvention() &&
1617-
!Mval.getType().isAddress()) {
1618-
Mval = Mval.materialize(SGF, loc);
1619-
}
1701+
auto Mval = emitValue(field, FieldType, setterTy, setterConv);
16201702

16211703
SGF.B.createAssignByPropertyWrapper(
16221704
loc, Mval.forward(SGF), proj.forward(SGF), initFn.getValue(),
@@ -4717,6 +4799,10 @@ static bool trySetterPeephole(SILGenFunction &SGF, SILLocation loc,
47174799
auto &setterComponent = static_cast<GetterSetterComponent&>(component);
47184800
if (setterComponent.canRewriteSetAsPropertyWrapperInit(SGF))
47194801
return false;
4802+
4803+
if (setterComponent.canRewriteSetAsTypeWrapperInit(SGF))
4804+
return false;
4805+
47204806
setterComponent.emitAssignWithSetter(SGF, loc, std::move(dest),
47214807
std::move(src));
47224808
return true;;

0 commit comments

Comments
 (0)