Skip to content

Commit 16af02d

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 6b1580f commit 16af02d

File tree

4 files changed

+48
-33
lines changed

4 files changed

+48
-33
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1778,6 +1778,10 @@ ERROR(attr_objc_implementation_no_conformance,none,
17781778
"add this conformance %select{with an ordinary extension|"
17791779
"in the Objective-C header}1",
17801780
(Type, bool))
1781+
ERROR(attr_objc_implementation_raise_minimum_deployment_target,none,
1782+
"'@implementation' of an Objective-C class requires a minimum deployment "
1783+
"target of at least %0 %1",
1784+
(StringRef, llvm::VersionTuple))
17811785

17821786
ERROR(member_of_objc_implementation_not_objc_or_final,none,
17831787
"%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
@@ -1573,6 +1573,27 @@ visitObjCImplementationAttr(ObjCImplementationAttr *attr) {
15731573

15741574
return;
15751575
}
1576+
1577+
// Stored property support may require ivar layouts to be updated at runtime
1578+
// using features only available on ABI-stable runtimes. Make sure the
1579+
// minimum deployment target is high enough if we're implementing a main
1580+
// class body. (Categories ought to back-deploy arbitrarily far.)
1581+
//
1582+
// This check could perhaps be refined further; for instance, we might be
1583+
// able to allow extensions with appropriate availability even if they're in
1584+
// a module with a lower minimum deployment target. But in the year 2024,
1585+
// it's probably reasonable to limit an advanced feature to 2019 runtimes.
1586+
auto deploymentAvailability = AvailabilityContext::forDeploymentTarget(Ctx);
1587+
auto metadataUpdateAvailability =
1588+
Ctx.getObjCMetadataUpdateCallbackAvailability();
1589+
if (attr->CategoryName.empty() &&
1590+
!deploymentAvailability.isContainedIn(metadataUpdateAvailability)) {
1591+
auto currentPlatform = targetPlatform(Ctx.LangOpts);
1592+
diagnose(attr->getLocation(),
1593+
diag::attr_objc_implementation_raise_minimum_deployment_target,
1594+
prettyPlatformString(currentPlatform),
1595+
metadataUpdateAvailability.getOSVersion().getLowerEndpoint());
1596+
}
15761597
}
15771598
else if (auto AFD = dyn_cast<AbstractFunctionDecl>(D)) {
15781599
if (!attr->CategoryName.empty()) {

stdlib/public/runtime/Metadata.cpp

Lines changed: 9 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "MetadataCache.h"
2626
#include "BytecodeLayouts.h"
2727
#include "swift/ABI/TypeIdentity.h"
28+
#include "swift/Basic/Defer.h"
2829
#include "swift/Basic/MathUtils.h"
2930
#include "swift/Basic/Lazy.h"
3031
#include "swift/Basic/Range.h"
@@ -3980,7 +3981,6 @@ swift::swift_updatePureObjCClassMetadata(Class cls,
39803981
size_t numFields,
39813982
const TypeLayout * const *fieldTypes) {
39823983
auto self = (ObjCClass *)cls;
3983-
bool requiresUpdate = SWIFT_RUNTIME_WEAK_CHECK(_objc_realizeClassFromSwift);
39843984

39853985
// Realize the superclass first.
39863986
(void)swift_getInitializedObjCClass((Class)self->Isa);
@@ -3990,38 +3990,21 @@ swift::swift_updatePureObjCClassMetadata(Class cls,
39903990
auto rodata = getROData(self);
39913991
self->RODataAndFlags = (uintptr_t)rodata;
39923992

3993-
// If we're running on a older Objective-C runtime, just realize
3994-
// the class.
3995-
if (!requiresUpdate) {
3996-
// If we don't have a backward deployment layout, we cannot proceed here.
3997-
if (rodata->InstanceSize == 0) {
3998-
fatalError(0, "class %s does not have a fragile layout; "
3999-
"the deployment target was newer than this OS\n",
4000-
rodata->Name);
4001-
}
3993+
bool requiresRealizeClassFromSwift =
3994+
SWIFT_RUNTIME_WEAK_CHECK(_objc_realizeClassFromSwift);
3995+
assert(requiresRealizeClassFromSwift);
40023996

3997+
SWIFT_DEFER {
40033998
// Realize the class. This causes the runtime to slide the field offsets
40043999
// stored in the field offset globals.
4005-
//
4006-
// Note that the field offset vector is *not* updated; however in
4007-
// Objective-C interop mode, we don't actually use the field offset vector
4008-
// of non-generic classes.
4009-
//
4010-
// In particular, class mirrors always use the Objective-C ivar descriptors,
4011-
// which point at field offset globals and not the field offset vector.
4012-
swift_getInitializedObjCClass((Class)self);
4013-
return cls;
4014-
}
4000+
SWIFT_RUNTIME_WEAK_USE(_objc_realizeClassFromSwift(cls, cls));
4001+
};
40154002

40164003
// Update the field offset globals using runtime type information; the layout
40174004
// of resilient types might be different than the statically-emitted layout.
40184005
ClassIvarList *ivars = rodata->IvarList;
40194006
if (!ivars) {
40204007
assert(numFields == 0);
4021-
4022-
// See remark above about how this slides field offset globals.
4023-
SWIFT_RUNTIME_WEAK_USE(_objc_realizeClassFromSwift(cls, cls));
4024-
40254008
return cls;
40264009
}
40274010

@@ -4030,16 +4013,12 @@ swift::swift_updatePureObjCClassMetadata(Class cls,
40304013

40314014
bool copiedIvarList = false;
40324015

4033-
// Update the field offset globals using runtime type information; the layout
4034-
// of resilient types might be different than the statically-emitted layout.
4035-
size_t size, alignMask;
4036-
40374016
// Start layout from our static notion of where the superclass starts.
40384017
// Objective-C expects us to have generated a correct ivar layout, which it
40394018
// will simply slide if it needs to.
40404019
assert(self->Superclass && "Swift cannot implement a root class");
4041-
size = rodata->InstanceStart;
4042-
alignMask = 0xF; // malloc alignment guarantee
4020+
size_t size = rodata->InstanceStart;
4021+
size_t alignMask = 0xF; // malloc alignment guarantee
40434022

40444023
// Okay, now do layout.
40454024
for (unsigned i = 0; i != numFields; ++i) {
@@ -4092,9 +4071,6 @@ swift::swift_updatePureObjCClassMetadata(Class cls,
40924071
if (rodata->InstanceSize != size)
40934072
rodata->InstanceSize = size;
40944073

4095-
// See remark above about how this slides field offset globals.
4096-
SWIFT_RUNTIME_WEAK_USE(_objc_realizeClassFromSwift(cls, cls));
4097-
40984074
return cls;
40994075
}
41004076
#endif
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)