Skip to content

Commit 8ab64ea

Browse files
committed
Set a minimum deployment target for objcImpl
The runtime layout tricks we’re playing only work on ABI-stable OSes, so require that as a minimum deployment target if you’re implementing whole ObjC classes with @implementation. This allows us to simplify the runtime function by eliminating a pre-stable code path, which is nice because we’ll probably need to either port it to Swift or open-code it in IRGen.
1 parent 14f0ce6 commit 8ab64ea

File tree

4 files changed

+46
-30
lines changed

4 files changed

+46
-30
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1796,6 +1796,10 @@ ERROR(attr_objc_implementation_no_conformance,none,
17961796
"add this conformance %select{with an ordinary extension|"
17971797
"in the Objective-C header}1",
17981798
(Type, bool))
1799+
ERROR(attr_objc_implementation_raise_minimum_deployment_target,none,
1800+
"'@implementation' of an Objective-C class requires a minimum deployment "
1801+
"target of at least %0 %1",
1802+
(StringRef, llvm::VersionTuple))
17991803

18001804
ERROR(member_of_objc_implementation_not_objc_or_final,none,
18011805
"%kind0 does not match any %kindonly0 declared in the headers for %1; "

lib/Sema/TypeCheckAttr.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1607,6 +1607,27 @@ visitObjCImplementationAttr(ObjCImplementationAttr *attr) {
16071607

16081608
return;
16091609
}
1610+
1611+
// Stored property support may require ivar layouts to be updated at runtime
1612+
// using features only available on ABI-stable runtimes. Make sure the
1613+
// minimum deployment target is high enough if we're implementing a main
1614+
// class body. (Categories ought to back-deploy arbitrarily far.)
1615+
//
1616+
// This check could perhaps be refined further; for instance, we might be
1617+
// able to allow extensions with appropriate availability even if they're in
1618+
// a module with a lower minimum deployment target. But in the year 2024,
1619+
// it's probably reasonable to limit an advanced feature to 2019 runtimes.
1620+
auto deploymentAvailability = AvailabilityContext::forDeploymentTarget(Ctx);
1621+
auto metadataUpdateAvailability =
1622+
Ctx.getObjCMetadataUpdateCallbackAvailability();
1623+
if (attr->CategoryName.empty() &&
1624+
!deploymentAvailability.isContainedIn(metadataUpdateAvailability)) {
1625+
auto currentPlatform = targetPlatform(Ctx.LangOpts);
1626+
diagnose(attr->getLocation(),
1627+
diag::attr_objc_implementation_raise_minimum_deployment_target,
1628+
prettyPlatformString(currentPlatform),
1629+
metadataUpdateAvailability.getOSVersion().getLowerEndpoint());
1630+
}
16101631
}
16111632
else if (auto AFD = dyn_cast<AbstractFunctionDecl>(D)) {
16121633
if (!hasObjCImplementationFeature(D, attr, Feature::CImplementation))

stdlib/public/runtime/Metadata.cpp

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3980,36 +3980,10 @@ swift::swift_updatePureObjCClassMetadata(Class cls,
39803980
ClassLayoutFlags flags,
39813981
size_t numFields,
39823982
const TypeLayout * const *fieldTypes) {
3983-
auto self = (ObjCClass *)cls;
3984-
bool requiresUpdate = SWIFT_RUNTIME_WEAK_CHECK(_objc_realizeClassFromSwift);
3985-
3986-
// Realize the superclass first.
3987-
(void)swift_getInitializedObjCClass((Class)self->Isa);
3988-
3989-
auto rodata = getROData(self);
3990-
3991-
// If we're running on a older Objective-C runtime, just realize
3992-
// the class.
3993-
if (!requiresUpdate) {
3994-
// If we don't have a backward deployment layout, we cannot proceed here.
3995-
if (rodata->InstanceSize == 0) {
3996-
fatalError(0, "class %s does not have a fragile layout; "
3997-
"the deployment target was newer than this OS\n",
3998-
rodata->Name);
3999-
}
4000-
4001-
// Realize the class. This causes the runtime to slide the field offsets
4002-
// stored in the field offset globals.
4003-
//
4004-
// Note that the field offset vector is *not* updated; however in
4005-
// Objective-C interop mode, we don't actually use the field offset vector
4006-
// of non-generic classes.
4007-
//
4008-
// In particular, class mirrors always use the Objective-C ivar descriptors,
4009-
// which point at field offset globals and not the field offset vector.
4010-
swift_getInitializedObjCClass((Class)self);
4011-
return cls;
4012-
}
3983+
bool hasRealizeClassFromSwift =
3984+
SWIFT_RUNTIME_WEAK_CHECK(_objc_realizeClassFromSwift);
3985+
assert(hasRealizeClassFromSwift);
3986+
(void)hasRealizeClassFromSwift;
40133987

40143988
SWIFT_DEFER {
40153989
// Realize the class. This causes the runtime to slide the field offsets
@@ -4019,7 +3993,10 @@ swift::swift_updatePureObjCClassMetadata(Class cls,
40193993

40203994
// Update the field offset globals using runtime type information; the layout
40213995
// of resilient types might be different than the statically-emitted layout.
3996+
ObjCClass *self = (ObjCClass *)cls;
3997+
ClassROData *rodata = getROData(self);
40223998
ClassIvarList *ivars = rodata->IvarList;
3999+
40234000
if (!ivars) {
40244001
assert(numFields == 0);
40254002
return cls;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Hardcode x86_64 macOS because Apple Silicon was born ABI-stable
2+
// RUN: %target-typecheck-verify-swift -import-objc-header %S/Inputs/objc_implementation.h -target x86_64-apple-macosx10.14.3
3+
// REQUIRES: objc_interop
4+
// REQUIRES: OS=macosx
5+
6+
@_objcImplementation extension ObjCImplSubclass {
7+
// expected-error@-1 {{'@implementation' of an Objective-C class requires a minimum deployment target of at least macOS 10.14.4}}
8+
}
9+
10+
@_objcImplementation(Conformance) extension ObjCClass {
11+
// no-error@-1
12+
func requiredMethod1() {}
13+
func requiredMethod2() {}
14+
}

0 commit comments

Comments
 (0)