SILGen: Work around for stored property keypath components not supporting generic resilient classes

A keypath component for a stored property can take one of several forms:

- The property offset is known to be constant at compile-time.

  This is used in the simplest cases for classes and structs.

- The property offset is not constant, but can be loaded from a global.

  This is used for classes that require runtime resilient layout, but where
  the offsets do not depend on the generic context.

- The property offset is not constant, and must be loaded from metadata.

  This is the case where the offset depends on the generic context. Here,
  we were only set up to load it from a fixed offset in the metadata.
  This works for generic structs, or generic classes where the superclass
  chain does not cross a resilience boundary.

  However, if a resilience boundary is crossed, the offset of the field
  offset in the metadata must itself be obtained at runtime by adding a
  constant to a value loaded from a global. This case is not supported by
  the current keypath ABI due to an oversight.

I filed <rdar://problem/59777983> to track extending the ABI to handle
this more elegantly in the future.

Fixes <rdar://problem/59617119>.
This commit is contained in:
Slava Pestov
2020-02-25 16:27:46 -05:00
parent 49017c8ee8
commit 10c37c6565
3 changed files with 58 additions and 2 deletions

View File

@@ -1353,6 +1353,24 @@ SILGenModule::canStorageUseStoredKeyPathComponent(AbstractStorageDecl *decl,
// unowned properties.
if (decl->getInterfaceType()->is<ReferenceStorageType>())
return false;
// If the field offset depends on the generic instantiation, we have to
// load it from metadata when instantiating the keypath component.
//
// However the metadata offset itself will not be fixed if the superclass
// is resilient. Fall back to treating the property as computed in this
// case.
//
// See the call to getClassFieldOffsetOffset() inside
// emitKeyPathComponent().
if (auto *parentClass = dyn_cast<ClassDecl>(decl->getDeclContext())) {
if (parentClass->isGeneric()) {
auto ancestry = parentClass->checkAncestry();
if (ancestry.contains(AncestryFlags::ResilientOther))
return false;
}
}
// If the stored value would need to be reabstracted in fully opaque
// context, then we have to treat the component as computed.
auto componentObjTy = decl->getValueInterfaceType();