Skip to content

Commit 2ecfee3

Browse files
authored
Merge pull request #9438 from jrose-apple/no-subclass-for-you
Disallow subclassing when a class is missing vtable entries.
2 parents 31f7dfe + a1b9304 commit 2ecfee3

File tree

13 files changed

+143
-7
lines changed

13 files changed

+143
-7
lines changed

include/swift/AST/Decl.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3193,6 +3193,7 @@ class ClassDecl : public NominalTypeDecl {
31933193
unsigned ObjCKind : 3;
31943194

31953195
unsigned HasMissingDesignatedInitializers : 1;
3196+
unsigned HasMissingVTableEntries : 1;
31963197

31973198
friend class IterativeTypeChecker;
31983199

@@ -3289,6 +3290,16 @@ class ClassDecl : public NominalTypeDecl {
32893290
HasMissingDesignatedInitializers = newValue;
32903291
}
32913292

3293+
/// Returns true if the class has missing members that require vtable entries.
3294+
///
3295+
/// In this case, the class cannot be subclassed, because we cannot construct
3296+
/// the vtable for the subclass.
3297+
bool hasMissingVTableEntries() const;
3298+
3299+
void setHasMissingVTableEntries(bool newValue = true) {
3300+
HasMissingVTableEntries = newValue;
3301+
}
3302+
32923303
/// Find a method of a class that overrides a given method.
32933304
/// Return nullptr, if no such method exists.
32943305
AbstractFunctionDecl *findOverridingDecl(

include/swift/AST/DiagnosticsSema.def

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1379,6 +1379,10 @@ ERROR(objc_runtime_visible_cannot_conform_to_objc_protocol,none,
13791379
ERROR(protocol_has_missing_requirements,none,
13801380
"type %0 cannot conform to protocol %1 because it has requirements that "
13811381
"cannot be satisfied", (Type, Type))
1382+
ERROR(protocol_has_missing_requirements_versioned,none,
1383+
"type %0 cannot conform to protocol %1 (compiled with Swift %2) because "
1384+
"it has requirements that could not be loaded in Swift %3",
1385+
(Type, Type, clang::VersionTuple, clang::VersionTuple))
13821386
ERROR(requirement_restricts_self,none,
13831387
"%0 requirement %1 cannot add constraint '%2%select{:|:| ==|:}3 %4' on "
13841388
"'Self'",
@@ -1908,6 +1912,14 @@ ERROR(inheritance_from_final_class,none,
19081912
ERROR(inheritance_from_unspecialized_objc_generic_class,none,
19091913
"inheritance from a generic Objective-C class %0 must bind "
19101914
"type parameters of %0 to specific concrete types", (Identifier))
1915+
ERROR(inheritance_from_class_with_missing_vtable_entries,none,
1916+
"cannot inherit from class %0 because it has overridable members that "
1917+
"could not be loaded",
1918+
(Identifier))
1919+
ERROR(inheritance_from_class_with_missing_vtable_entries_versioned,none,
1920+
"cannot inherit from class %0 (compiled with Swift %1) because it has "
1921+
"overridable members that could not be loaded in Swift %2",
1922+
(Identifier, clang::VersionTuple, clang::VersionTuple))
19111923
ERROR(inheritance_from_cf_class,none,
19121924
"cannot inherit from Core Foundation type %0", (Identifier))
19131925
ERROR(inheritance_from_objc_runtime_visible_class,none,

include/swift/Serialization/SerializedModuleLoader.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ class SerializedASTFile final : public LoadedFile {
120120
public:
121121
bool isSIB() const { return IsSIB; }
122122

123+
/// Returns the language version that was used to compile the contents of this
124+
/// file.
125+
const version::Version &getLanguageVersionBuiltWith() const;
126+
123127
virtual bool isSystemModule() const override;
124128

125129
virtual void lookupValue(ModuleDecl::AccessPathTy accessPath,

lib/AST/Decl.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2493,6 +2493,7 @@ ClassDecl::ClassDecl(SourceLoc ClassLoc, Identifier Name, SourceLoc NameLoc,
24932493
ClassDeclBits.HasDestructorDecl = 0;
24942494
ObjCKind = 0;
24952495
HasMissingDesignatedInitializers = 0;
2496+
HasMissingVTableEntries = 0;
24962497
}
24972498

24982499
DestructorDecl *ClassDecl::getDestructor() {
@@ -2510,6 +2511,11 @@ bool ClassDecl::hasMissingDesignatedInitializers() const {
25102511
return HasMissingDesignatedInitializers;
25112512
}
25122513

2514+
bool ClassDecl::hasMissingVTableEntries() const {
2515+
(void)getMembers();
2516+
return HasMissingVTableEntries;
2517+
}
2518+
25132519
bool ClassDecl::inheritsSuperclassInitializers(LazyResolver *resolver) {
25142520
// Get a resolver from the ASTContext if we don't have one already.
25152521
if (resolver == nullptr)

lib/Sema/TypeCheckDecl.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4587,6 +4587,27 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
45874587
break;
45884588
}
45894589

4590+
if (!isInvalidSuperclass && Super->hasMissingVTableEntries()) {
4591+
auto *superFile = Super->getModuleScopeContext();
4592+
if (auto *serialized = dyn_cast<SerializedASTFile>(superFile)) {
4593+
if (serialized->getLanguageVersionBuiltWith() !=
4594+
TC.getLangOpts().EffectiveLanguageVersion) {
4595+
TC.diagnose(CD,
4596+
diag::inheritance_from_class_with_missing_vtable_entries_versioned,
4597+
Super->getName(),
4598+
serialized->getLanguageVersionBuiltWith(),
4599+
TC.getLangOpts().EffectiveLanguageVersion);
4600+
isInvalidSuperclass = true;
4601+
}
4602+
}
4603+
if (!isInvalidSuperclass) {
4604+
TC.diagnose(
4605+
CD, diag::inheritance_from_class_with_missing_vtable_entries,
4606+
Super->getName());
4607+
isInvalidSuperclass = true;
4608+
}
4609+
}
4610+
45904611
// Require the superclass to be open if this is outside its
45914612
// defining module. But don't emit another diagnostic if we
45924613
// already complained about the class being inherently

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include "swift/Basic/Defer.h"
3939
#include "swift/ClangImporter/ClangModule.h"
4040
#include "swift/Sema/IDETypeChecking.h"
41+
#include "swift/Serialization/SerializedModuleLoader.h"
4142
#include "llvm/ADT/ScopedHashTable.h"
4243
#include "llvm/ADT/SmallString.h"
4344
#include "llvm/Support/Compiler.h"
@@ -2063,8 +2064,23 @@ namespace {
20632064
// If the protocol contains missing requirements, it can't be conformed to
20642065
// at all.
20652066
if (Proto->hasMissingRequirements()) {
2066-
TC.diagnose(ComplainLoc, diag::protocol_has_missing_requirements,
2067-
T, Proto->getDeclaredType());
2067+
bool hasDiagnosed = false;
2068+
auto *protoFile = Proto->getModuleScopeContext();
2069+
if (auto *serialized = dyn_cast<SerializedASTFile>(protoFile)) {
2070+
if (serialized->getLanguageVersionBuiltWith() !=
2071+
TC.getLangOpts().EffectiveLanguageVersion) {
2072+
TC.diagnose(ComplainLoc,
2073+
diag::protocol_has_missing_requirements_versioned,
2074+
T, Proto->getDeclaredType(),
2075+
serialized->getLanguageVersionBuiltWith(),
2076+
TC.getLangOpts().EffectiveLanguageVersion);
2077+
hasDiagnosed = true;
2078+
}
2079+
}
2080+
if (!hasDiagnosed) {
2081+
TC.diagnose(ComplainLoc, diag::protocol_has_missing_requirements,
2082+
T, Proto->getDeclaredType());
2083+
}
20682084
conformance->setInvalid();
20692085
return conformance;
20702086
}

lib/Serialization/Deserialization.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4458,6 +4458,9 @@ void ModuleFile::loadAllMembers(Decl *container, uint64_t contextData) {
44584458
[&](const DeclDeserializationError &error) {
44594459
if (error.isDesignatedInitializer())
44604460
containingClass->setHasMissingDesignatedInitializers();
4461+
if (error.needsVTableEntry() || error.needsAllocatingVTableEntry())
4462+
containingClass->setHasMissingVTableEntries();
4463+
44614464
if (error.getName().getBaseName() == getContext().Id_init) {
44624465
members.push_back(MissingMemberDecl::forInitializer(
44634466
getContext(), containingClass, error.getName(),

lib/Serialization/ModuleFile.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1925,3 +1925,7 @@ ClassDecl *SerializedASTFile::getMainClass() const {
19251925
assert(hasEntryPoint());
19261926
return cast_or_null<ClassDecl>(File.getDecl(File.Bits.EntryPointDeclID));
19271927
}
1928+
1929+
const version::Version &SerializedASTFile::getLanguageVersionBuiltWith() const {
1930+
return File.CompatibilityVersion;
1931+
}

test/Serialization/Recovery/Inputs/custom-modules/Types.apinotes

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ SwiftVersions:
2525
Typedefs:
2626
- Name: RenamedTypedef
2727
SwiftName: Swift3RenamedTypedef
28+
- Name: NewlyWrappedTypedef
29+
SwiftWrapper: none
2830
Tags:
2931
- Name: RenamedStruct
3032
SwiftName: Swift3RenamedStruct

test/Serialization/Recovery/Inputs/custom-modules/Types.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
@end
1010

1111
typedef int RenamedTypedef;
12+
typedef int NewlyWrappedTypedef __attribute__((swift_wrapper(struct)));
1213

1314
struct RenamedStruct {
1415
int value;

test/Serialization/Recovery/typedefs.swift

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// RUN: rm -rf %t && mkdir -p %t
2-
// RUN: %target-swift-frontend -emit-sil -o - -emit-module-path %t/Lib.swiftmodule -module-name Lib -I %S/Inputs/custom-modules %s | %FileCheck -check-prefix CHECK-VTABLE %s
2+
// RUN: %target-swift-frontend -emit-sil -o - -emit-module-path %t/Lib.swiftmodule -module-name Lib -I %S/Inputs/custom-modules -disable-objc-attr-requires-foundation-module %s | %FileCheck -check-prefix CHECK-VTABLE %s
33

44
// RUN: %target-swift-ide-test -source-filename=x -print-module -module-to-print Lib -I %t -I %S/Inputs/custom-modules | %FileCheck %s
55

@@ -52,17 +52,19 @@ let _ = unwrapped // okay
5252
_ = usesWrapped(nil) // expected-error {{use of unresolved identifier 'usesWrapped'}}
5353
_ = usesUnwrapped(nil) // expected-error {{nil is not compatible with expected argument type 'Int32'}}
5454

55-
public class UserSub: User {
55+
public class UserDynamicSub: UserDynamic {
5656
override init() {}
5757
}
5858
// FIXME: Bad error message; really it's that the convenience init hasn't been
5959
// inherited.
60-
_ = UserSub(conveniently: 0) // expected-error {{argument passed to call that takes no arguments}}
60+
_ = UserDynamicSub(conveniently: 0) // expected-error {{argument passed to call that takes no arguments}}
6161

62-
public class UserConvenienceSub: UserConvenience {
62+
public class UserDynamicConvenienceSub: UserDynamicConvenience {
6363
override init() {}
6464
}
65-
_ = UserConvenienceSub(conveniently: 0)
65+
_ = UserDynamicConvenienceSub(conveniently: 0)
66+
67+
public class UserSub : User {} // expected-error {{cannot inherit from class 'User' because it has overridable members that could not be loaded}}
6668

6769
#endif // VERIFY
6870

@@ -154,6 +156,42 @@ open class UserConvenience {
154156
// CHECK: {{^}$}}
155157
// CHECK-RECOVERY: {{^}$}}
156158

159+
// CHECK-LABEL: class UserDynamic
160+
// CHECK-RECOVERY-LABEL: class UserDynamic
161+
open class UserDynamic {
162+
// CHECK: init()
163+
// CHECK-RECOVERY: init()
164+
@objc public dynamic init() {}
165+
166+
// CHECK: init(wrapped: WrappedInt)
167+
// CHECK-RECOVERY: /* placeholder for init(wrapped:) */
168+
@objc public dynamic init(wrapped: WrappedInt) {}
169+
170+
// CHECK: convenience init(conveniently: Int)
171+
// CHECK-RECOVERY: convenience init(conveniently: Int)
172+
@objc public dynamic convenience init(conveniently: Int) { self.init() }
173+
}
174+
// CHECK: {{^}$}}
175+
// CHECK-RECOVERY: {{^}$}}
176+
177+
// CHECK-LABEL: class UserDynamicConvenience
178+
// CHECK-RECOVERY-LABEL: class UserDynamicConvenience
179+
open class UserDynamicConvenience {
180+
// CHECK: init()
181+
// CHECK-RECOVERY: init()
182+
@objc public dynamic init() {}
183+
184+
// CHECK: convenience init(wrapped: WrappedInt)
185+
// CHECK-RECOVERY: /* placeholder for init(wrapped:) */
186+
@objc public dynamic convenience init(wrapped: WrappedInt) { self.init() }
187+
188+
// CHECK: convenience init(conveniently: Int)
189+
// CHECK-RECOVERY: convenience init(conveniently: Int)
190+
@objc public dynamic convenience init(conveniently: Int) { self.init() }
191+
}
192+
// CHECK: {{^}$}}
193+
// CHECK-RECOVERY: {{^}$}}
194+
157195

158196
// CHECK-LABEL: class UserSub
159197
// CHECK-RECOVERY-LABEL: class UserSub

test/Serialization/Recovery/types-3-to-4.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import Lib
1616
func requiresConformance(_: B_RequiresConformance<B_ConformsToProto>) {}
1717
func requiresConformance(_: B_RequiresConformance<C_RelyOnConformanceImpl.Assoc>) {}
1818

19+
class Sub: Base {} // okay
20+
class Impl: Proto {} // expected-error {{type 'Impl' does not conform to protocol 'Proto'}}
1921

2022
#else // TEST
2123

@@ -86,4 +88,11 @@ public class C_RelyOnConformanceImpl: C_RelyOnConformance {
8688
public typealias Assoc = Swift3RenamedClass
8789
}
8890

91+
open class Base {
92+
public init(wrapped: NewlyWrappedTypedef) {}
93+
}
94+
public protocol Proto {
95+
func useWrapped(_ wrapped: NewlyWrappedTypedef)
96+
}
97+
8998
#endif

test/Serialization/Recovery/types-4-to-3.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import Lib
1616
func requiresConformance(_: B_RequiresConformance<B_ConformsToProto>) {}
1717
func requiresConformance(_: B_RequiresConformance<C_RelyOnConformanceImpl.Assoc>) {}
1818

19+
class Sub: Base {} // expected-error {{cannot inherit from class 'Base' (compiled with Swift 4.0) because it has overridable members that could not be loaded in Swift 3.2}}
20+
class Impl: Proto {} // expected-error {{type 'Impl' cannot conform to protocol 'Proto' (compiled with Swift 4.0) because it has requirements that could not be loaded in Swift 3.2}}
1921

2022
#else // TEST
2123

@@ -62,4 +64,11 @@ public class C_RelyOnConformanceImpl: C_RelyOnConformance {
6264
public typealias Assoc = RenamedClass
6365
}
6466

67+
open class Base {
68+
public init(wrapped: NewlyWrappedTypedef) {}
69+
}
70+
public protocol Proto {
71+
func useWrapped(_ wrapped: NewlyWrappedTypedef)
72+
}
73+
6574
#endif

0 commit comments

Comments
 (0)