[Serialization] Store whether an override depends on its base for ABI (#27784)

In some circumstances, a Swift declaration in module A will depend on
another declaration (usually from Objective-C) that can't be loaded,
for whatever reason. If the Swift declaration is *overriding* the
missing declaration, this can present a problem, because the way
methods are dispatched in Swift can depend on knowing the original
class that introduced the method. However, if the compiler can prove
that the override can still be safely invoked/used in all cases, it
doesn't need to worry about the overridden declaration being missing.

This is especially relevant for property accessors, because there's
currently no logic to recover from a property being successfully
deserialized and then finding out that an accessor couldn't be.

The decision of whether or not an override can be safely invoked
without knowledge of the base method is something to be cautious
about---a mistaken analysis would effectively be a miscompile. So up
until now, this was limited to one case: when a method is known to be
`@objc dynamic`, i.e. always dispatched through objc_msgSend. (Even
this may become questionable if we have first-class method references,
like we do for key paths.) This worked particularly well because the
compiler infers 'dynamic' for any overload of an imported Objective-C
method or accessor, in case it imports differently in a different
-swift-version and a client ends up subclassing it.

However...that inference does not apply if the class is final, because
then there are no subclasses to worry about.

This commit changes the test to be more careful: if the /missing/
declaration was `@objc dynamic`, we know that it can't affect ABI,
because either the override is properly `@objc dynamic` as well, or
the override has introduced its own calling ABI (in practice, a direct
call for final methods) that doesn't depend on the superclass. Again,
this isn't 100% correct in the presence of first-class methods, but it
does fix the issue in practice where a property accessor in a parent
class goes missing. And since Objective-C allows adding property
setters separately from the original property declaration, that's
something that can happen even under normal circumstances. Sadly.

This approach could probably be extended to constructors as well. I'm
a little more cautious about throwing vars and subscripts into the mix
because of the presence of key paths, which do allow identity-based
comparison of overrides and bases.

rdar://problem/56388950
This commit is contained in:
Jordan Rose
2019-10-21 15:53:25 -07:00
committed by GitHub
parent 6904dba822
commit a52fac4470
5 changed files with 72 additions and 14 deletions

View File

@@ -2593,6 +2593,24 @@ class Serializer::DeclSerializer : public DeclVisitor<DeclSerializer> {
return count;
}
/// Returns true if a client can still use decls that override \p overridden
/// even if \p overridden itself isn't available (isn't found, can't be
/// imported, can't be deserialized, whatever).
///
/// This should be kept conservative. Compiler crashes are still better than
/// miscompiles.
static bool overriddenDeclAffectsABI(const ValueDecl *overridden) {
if (!overridden)
return false;
// There's one case where we know a declaration doesn't affect the ABI of
// its overrides after they've been compiled: if the declaration is '@objc'
// and 'dynamic'. In that case, all accesses to the method or property will
// go through the Objective-C method tables anyway.
if (overridden->hasClangNode() || overridden->isObjCDynamic())
return false;
return true;
}
public:
DeclSerializer(Serializer &S, DeclID id) : S(S), id(id) {}
~DeclSerializer() {
@@ -3212,6 +3230,7 @@ public:
fn->isImplicitlyUnwrappedOptional(),
S.addDeclRef(fn->getOperatorDecl()),
S.addDeclRef(fn->getOverriddenDecl()),
overriddenDeclAffectsABI(fn->getOverriddenDecl()),
fn->getFullName().getArgumentNames().size() +
fn->getFullName().isCompoundName(),
rawAccessLevel,
@@ -3267,6 +3286,9 @@ public:
uint8_t rawAccessorKind =
uint8_t(getStableAccessorKind(fn->getAccessorKind()));
bool overriddenAffectsABI =
overriddenDeclAffectsABI(fn->getOverriddenDecl());
Type ty = fn->getInterfaceType();
SmallVector<IdentifierID, 4> dependencies;
for (auto dependency : collectDependenciesFromType(ty->getCanonicalType()))
@@ -3288,6 +3310,7 @@ public:
S.addTypeRef(fn->getResultInterfaceType()),
fn->isImplicitlyUnwrappedOptional(),
S.addDeclRef(fn->getOverriddenDecl()),
overriddenAffectsABI,
S.addDeclRef(fn->getStorage()),
rawAccessorKind,
rawAccessLevel,