SILGen: Decouple "should use materializeForSet" from "materializeForSet was synthesized"

We will sometimes emit materializeForSet for resilience, even if
it is not the preferred method for inout accesses of the property
or subscript.

To avoid having SILGen behavior depend on whether Sema synthesized
a materializeForSet or not, add a more precise predicate. The
general idea as I understand it, is that we want to use
materializeForSet if we do not have perfect information about the
implementation of the property or subscript, due to polymorphism
or resilience.
This commit is contained in:
Slava Pestov
2016-03-13 23:11:10 -07:00
parent 5271817bca
commit 2ee4fd6b2a
2 changed files with 66 additions and 10 deletions

View File

@@ -799,24 +799,63 @@ namespace {
std::move(value));
}
bool shouldUseMaterializeForSet(SILGenFunction &gen,
AccessKind accessKind) {
// If this access is for a read, we can just call the getter.
if (accessKind == AccessKind::Read)
return false;
// If the declaration is dynamic, there's no materializeForSet.
if (decl->getAttrs().hasAttribute<DynamicAttr>())
return false;
// If the declaration was imported from C, we won't gain anything
// from using materializeForSet, and furthermore, it might not
// exist.
if (decl->hasClangNode())
return false;
// If the declaration is not in type context, there's no
// materializeForSet.
if (!decl->getDeclContext()->isTypeContext())
return false;
// If the declaration is in a different resilience domain, we have
// to use materializeForSet.
//
// FIXME: Use correct ResilienceExpansion if gen is @transparent
if (!decl->hasFixedLayout(gen.SGM.M.getSwiftModule(),
ResilienceExpansion::Maximal))
return true;
// If the declaration is dynamically dispatched through a class,
// we have to use materializeForSet.
if (isa<ClassDecl>(decl->getDeclContext())) {
if (decl->isFinal())
return false;
return true;
}
// If the declaration is dynamically dispatched through a protocol,
// we have to use materializeForSet.
if (isa<ProtocolDecl>(decl->getDeclContext()))
return true;
return false;
}
ManagedValue getMaterialized(SILGenFunction &gen,
SILLocation loc,
ManagedValue base,
AccessKind accessKind) && override {
// If this is just for a read, or the property is dynamic, or if
// it doesn't have a materializeForSet, or if this is a direct
// use of something defined in a protocol extension (see
// maybeEmitMaterializeForSetThunk), just materialize to a
// temporary directly.
if (accessKind == AccessKind::Read ||
decl->getAttrs().hasAttribute<DynamicAttr>() ||
!decl->getMaterializeForSetFunc() ||
isa<StructDecl>(decl->getDeclContext()) ||
decl->getDeclContext()->getAsProtocolExtensionContext()) {
if (!shouldUseMaterializeForSet(gen, accessKind)) {
return std::move(*this).LogicalPathComponent::getMaterialized(gen,
loc, base, accessKind);
}
assert(decl->getMaterializeForSetFunc() &&
"polymorphic storage without materializeForSet");
assert(gen.InWritebackScope &&
"materializing l-value for modification without writeback scope");