@@ -958,67 +958,94 @@ SubclassScope SILDeclRef::getSubclassScope() const {
958
958
if (!isa<AbstractFunctionDecl>(decl))
959
959
return SubclassScope::NotApplicable;
960
960
961
- // If this declaration is a function which goes into a vtable, then it's
962
- // symbol must be as visible as its class, because derived classes have to put
963
- // all less visible methods of the base class into their vtables.
961
+ DeclContext *context = decl->getDeclContext ();
962
+
963
+ // Only methods in non-final classes go in the vtable.
964
+ auto *classType = dyn_cast<ClassDecl>(context);
965
+ if (!classType || classType->isFinal ())
966
+ return SubclassScope::NotApplicable;
967
+
968
+ // If a method appears in the vtable of a class, we must give it's symbol
969
+ // special consideration when computing visibility because the SIL-level
970
+ // linkage does not map to the symbol's visibility in a straightforward
971
+ // way.
972
+ //
973
+ // In particular, the rules are:
974
+ // - If the class metadata is not resilient, then all method symbols must
975
+ // be visible from any translation unit where a subclass might be defined,
976
+ // because the subclass metadata will re-emit all vtable entries.
977
+ //
978
+ // - For resilient classes, we do the opposite: generally, a method's symbol
979
+ // can be hidden from other translation units, because we want to enforce
980
+ // that resilient access patterns are used for method calls and overrides.
981
+ //
982
+ // Constructors and final methods are the exception here, because they can
983
+ // be called directly.
984
+
985
+ // FIXME: This is too narrow. Any class with resilient metadata should
986
+ // probably have this, at least for method overrides that don't add new
987
+ // vtable entries.
988
+ bool isResilientClass = classType->isResilient ();
964
989
965
990
if (auto *CD = dyn_cast<ConstructorDecl>(decl)) {
991
+ if (isResilientClass)
992
+ return SubclassScope::NotApplicable;
966
993
// Initializing entry points do not appear in the vtable.
967
994
if (kind == SILDeclRef::Kind::Initializer)
968
995
return SubclassScope::NotApplicable;
969
- // Non-required convenience inits do not apper in the vtable.
996
+ // Non-required convenience inits do not appear in the vtable.
970
997
if (!CD->isRequired () && !CD->isDesignatedInit ())
971
998
return SubclassScope::NotApplicable;
972
999
} else if (isa<DestructorDecl>(decl)) {
973
- // Detructors do not appear in the vtable.
1000
+ // Destructors do not appear in the vtable.
974
1001
return SubclassScope::NotApplicable;
975
1002
} else {
976
1003
assert (isa<FuncDecl>(decl));
977
1004
}
978
1005
979
- DeclContext *context = decl->getDeclContext ();
980
-
981
- // Methods from extensions don't go in the vtable.
982
- if (isa<ExtensionDecl>(context))
983
- return SubclassScope::NotApplicable;
984
-
985
- // Various forms of thunks don't either.
1006
+ // Various forms of thunks don't go in the vtable.
986
1007
if (isThunk () || isForeign)
987
1008
return SubclassScope::NotApplicable;
988
1009
989
1010
// Default arg generators don't go in the vtable.
990
1011
if (isDefaultArgGenerator ())
991
1012
return SubclassScope::NotApplicable;
992
1013
993
- // Only methods in non-final classes go in the vtable.
994
- auto *classType = context-> getSelfClassDecl ();
995
- if (!classType || classType-> isFinal ())
996
- return SubclassScope::NotApplicable;
1014
+ if (decl-> isFinal ()) {
1015
+ // Final methods only go in the vtable if they override something.
1016
+ if (!decl-> getOverriddenDecl ())
1017
+ return SubclassScope::NotApplicable;
997
1018
998
- // Final methods only go in the vtable if they override something.
999
- if (decl->isFinal () && !decl->getOverriddenDecl ())
1000
- return SubclassScope::NotApplicable;
1019
+ // In the resilient case, we're going to be making symbols _less_
1020
+ // visible, so make sure we stop now; final methods can always be
1021
+ // called directly.
1022
+ if (isResilientClass)
1023
+ return SubclassScope::Internal;
1024
+ }
1001
1025
1002
1026
assert (decl->getEffectiveAccess () <= classType->getEffectiveAccess () &&
1003
1027
" class must be as visible as its members" );
1004
1028
1005
- // FIXME: This is too narrow. Any class with resilient metadata should
1006
- // probably have this, at least for method overrides that don't add new
1007
- // vtable entries.
1008
- if (classType->isResilient ()) {
1009
- if (isa<ConstructorDecl>(decl))
1010
- return SubclassScope::NotApplicable;
1029
+ if (isResilientClass) {
1030
+ // The symbol should _only_ be reached via the vtable, so we're
1031
+ // going to make it hidden.
1011
1032
return SubclassScope::Resilient;
1012
1033
}
1013
1034
1014
1035
switch (classType->getEffectiveAccess ()) {
1015
1036
case AccessLevel::Private:
1016
1037
case AccessLevel::FilePrivate:
1038
+ // If the class is private, it can only be subclassed from the same
1039
+ // SILModule, so we don't need to do anything.
1017
1040
return SubclassScope::NotApplicable;
1018
1041
case AccessLevel::Internal:
1019
1042
case AccessLevel::Public:
1043
+ // If the class is internal or public, it can only be subclassed from
1044
+ // the same AST Module, but possibly a different SILModule.
1020
1045
return SubclassScope::Internal;
1021
1046
case AccessLevel::Open:
1047
+ // If the class is open, it can be subclassed from a different
1048
+ // AST Module. All method symbols are public.
1022
1049
return SubclassScope::External;
1023
1050
}
1024
1051
0 commit comments