Skip to content

Commit e79aefa

Browse files
authored
Merge pull request #61782 from beccadax/at-implementation-2
Allow overrides in @_objcImplementation extensions
2 parents 9eefc2e + 3e4ea43 commit e79aefa

14 files changed

+296
-61
lines changed

include/swift/AST/Decl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2379,6 +2379,10 @@ class ValueDecl : public Decl {
23792379
/// Asserts if this is not a member of a protocol.
23802380
bool isProtocolRequirement() const;
23812381

2382+
/// Return true if this is a member implementation for an \c @_objcImplementation
2383+
/// extension.
2384+
bool isObjCMemberImplementation() const;
2385+
23822386
void setUserAccessible(bool Accessible) {
23832387
Bits.ValueDecl.IsUserAccessible = Accessible;
23842388
}

include/swift/AST/DiagnosticsSema.def

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1511,10 +1511,6 @@ ERROR(attr_objc_implementation_category_not_found,none,
15111511
NOTE(attr_objc_implementation_fixit_remove_category_name,none,
15121512
"remove arguments to implement the main '@interface' for this class",
15131513
())
1514-
ERROR(attr_objc_implementation_no_objc_final,none,
1515-
"%0 %1 cannot be 'final' because Objective-C subclasses of %2 can "
1516-
"override it",
1517-
(DescriptiveDeclKind, ValueDecl *, ValueDecl *))
15181514

15191515
ERROR(member_of_objc_implementation_not_objc_or_final,none,
15201516
"%0 %1 does not match any %0 declared in the headers for %2; did you use "

lib/AST/ASTDumper.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1458,6 +1458,7 @@ void ValueDecl::dumpRef(raw_ostream &os) const {
14581458

14591459
void LLVM_ATTRIBUTE_USED ValueDecl::dumpRef() const {
14601460
dumpRef(llvm::errs());
1461+
llvm::errs() << "\n";
14611462
}
14621463

14631464
void SourceFile::dump() const {

lib/AST/Decl.cpp

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3739,18 +3739,29 @@ static bool checkAccessUsingAccessScopes(const DeclContext *useDC,
37393739
/// Checks if \p VD is an ObjC member implementation:
37403740
///
37413741
/// \li It's in an \c \@_objcImplementation extension
3742-
/// \li It's \c \@objc
3742+
/// \li It's not explicitly \c final
37433743
/// \li Its access level is not \c private or \c fileprivate
37443744
static bool
37453745
isObjCMemberImplementation(const ValueDecl *VD,
37463746
llvm::function_ref<AccessLevel()> getAccessLevel) {
37473747
if (auto ED = dyn_cast<ExtensionDecl>(VD->getDeclContext()))
3748-
if (ED->isObjCImplementation())
3749-
return VD->isObjC() && getAccessLevel() >= AccessLevel::Internal;
3748+
if (ED->isObjCImplementation() && !isa<TypeDecl>(VD)) {
3749+
auto attrDecl = isa<AccessorDecl>(VD)
3750+
? cast<AccessorDecl>(VD)->getStorage()
3751+
: VD;
3752+
return !attrDecl->isFinal()
3753+
&& !attrDecl->getAttrs().hasAttribute<OverrideAttr>()
3754+
&& getAccessLevel() >= AccessLevel::Internal;
3755+
}
37503756

37513757
return false;
37523758
}
37533759

3760+
bool ValueDecl::isObjCMemberImplementation() const {
3761+
return ::isObjCMemberImplementation(
3762+
this, [&]() { return this->getFormalAccess(); });
3763+
}
3764+
37543765
/// Checks if \p VD may be used from \p useDC, taking \@testable and \@_spi
37553766
/// imports into account.
37563767
///

lib/AST/NameLookup.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1701,11 +1701,24 @@ NominalTypeDecl::lookupDirect(ObjCSelector selector, bool isInstance) {
17011701
return stored.Methods;
17021702
}
17031703

1704+
static bool inObjCImplExtension(AbstractFunctionDecl *newDecl) {
1705+
if (auto ext = dyn_cast<ExtensionDecl>(newDecl->getDeclContext()))
1706+
return ext->isObjCImplementation();
1707+
return false;
1708+
}
1709+
17041710
/// If there is an apparent conflict between \p newDecl and one of the methods
17051711
/// in \p vec, should we diagnose it?
17061712
static bool
17071713
shouldDiagnoseConflict(NominalTypeDecl *ty, AbstractFunctionDecl *newDecl,
17081714
llvm::TinyPtrVector<AbstractFunctionDecl *> &vec) {
1715+
// Conflicts between member implementations and their interfaces, or
1716+
// inherited inits and their overrides in @_objcImpl extensions, are spurious.
1717+
if (newDecl->isObjCMemberImplementation()
1718+
|| (isa<ConstructorDecl>(newDecl) && inObjCImplExtension(newDecl)
1719+
&& newDecl->getAttrs().hasAttribute<OverrideAttr>()))
1720+
return false;
1721+
17091722
// Are all conflicting methods imported from ObjC and in our ObjC half or a
17101723
// bridging header? Some code bases implement ObjC methods in Swift even
17111724
// though it's not exactly supported.

lib/Sema/TypeCheckDecl.cpp

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -811,22 +811,6 @@ IsFinalRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
811811
if (!cls)
812812
return false;
813813

814-
// Objective-C doesn't have a way to prevent overriding, so if this member
815-
// is accessible from ObjC and it's possible to subclass its parent from ObjC,
816-
// `final` doesn't make sense even when inferred.
817-
//
818-
// FIXME: This is technically true iff `!cls->hasKnownSwiftImplementation()`,
819-
// but modeling that would be source-breaking, so instead we only
820-
// enforce it in `@_objcImplementation extension`s.
821-
if (auto ext = dyn_cast<ExtensionDecl>(decl->getDeclContext()))
822-
if (ext->isObjCImplementation() && decl->isObjC()) {
823-
if (explicitFinalAttr)
824-
diagnoseAndRemoveAttr(decl, explicitFinalAttr,
825-
diag::attr_objc_implementation_no_objc_final,
826-
decl->getDescriptiveKind(), decl, cls);
827-
return false;
828-
}
829-
830814
switch (decl->getKind()) {
831815
case DeclKind::Var: {
832816
// Properties are final if they are declared 'static' or a 'let'
@@ -854,6 +838,12 @@ IsFinalRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
854838
return true;
855839

856840
if (VD->isLet()) {
841+
// If this `let` is in an `@_objcImplementation extension`, don't
842+
// infer `final` unless it is written explicitly.
843+
auto ed = dyn_cast<ExtensionDecl>(VD->getDeclContext());
844+
if (!explicitFinalAttr && ed && ed->isObjCImplementation())
845+
return false;
846+
857847
if (VD->getFormalAccess() == AccessLevel::Open) {
858848
auto &context = decl->getASTContext();
859849
auto diagID = diag::implicitly_final_cannot_be_open;
@@ -974,6 +964,11 @@ IsDynamicRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
974964
return true;
975965
}
976966

967+
// @_objcImplementation extension member implementations are implicitly
968+
// dynamic.
969+
if (decl->isObjCMemberImplementation())
970+
return true;
971+
977972
if (auto accessor = dyn_cast<AccessorDecl>(decl)) {
978973
// Runtime-replaceable accessors are dynamic when their storage declaration
979974
// is dynamic and they were explicitly defined or they are implicitly defined
@@ -1005,12 +1000,6 @@ IsDynamicRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
10051000
if (isa<ExtensionDecl>(dc) && dc->getSelfClassDecl())
10061001
return true;
10071002

1008-
// @objc declarations in @_objcImplementation extensions are implicitly
1009-
// dynamic.
1010-
if (auto ED = dyn_cast_or_null<ExtensionDecl>(dc->getAsDecl()))
1011-
if (ED->isObjCImplementation())
1012-
return true;
1013-
10141003
// If any of the declarations overridden by this declaration are dynamic
10151004
// or were imported from Objective-C, this declaration is dynamic.
10161005
// Don't do this if the declaration is not exposed to Objective-C; that's

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1881,26 +1881,6 @@ static ObjCSelector inferObjCName(ValueDecl *decl) {
18811881
return *decl->getObjCRuntimeName(true);
18821882
}
18831883

1884-
static bool isObjCImplementation(AbstractFunctionDecl *method,
1885-
ObjCSelector methodName) {
1886-
auto ext = dyn_cast<ExtensionDecl>(method->getDeclContext());
1887-
if (!ext || !ext->isObjCImplementation())
1888-
return false;
1889-
1890-
auto contextInterface =
1891-
dyn_cast<IterableDeclContext>(ext->getImplementedObjCDecl());
1892-
1893-
for (auto otherMember : contextInterface->getMembers()) {
1894-
auto otherMethod = dyn_cast<AbstractFunctionDecl>(otherMember);
1895-
if (!otherMethod) continue;
1896-
1897-
if (otherMethod->getObjCSelector() == methodName)
1898-
return true;
1899-
}
1900-
1901-
return false;
1902-
}
1903-
19041884
/// Mark the given declaration as being Objective-C compatible (or
19051885
/// not) as appropriate.
19061886
///
@@ -2062,8 +2042,7 @@ void markAsObjC(ValueDecl *D, ObjCReason reason,
20622042

20632043
// Record the method in the type, if it's a member of one.
20642044
if (auto tyDecl = D->getDeclContext()->getSelfNominalTypeDecl()) {
2065-
if (!isObjCImplementation(method, selector))
2066-
tyDecl->recordObjCMethod(method, selector);
2045+
tyDecl->recordObjCMethod(method, selector);
20672046
}
20682047

20692048
// Record the method in the source file.

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,8 +1230,11 @@ static void checkObjCImplementationMemberAvoidsVTable(ValueDecl *VD) {
12301230
!ED->getSelfClassDecl()->hasKnownSwiftImplementation() &&
12311231
"@_objcImplementation on non-class or Swift class?");
12321232

1233-
if (VD->isSemanticallyFinal() || VD->isObjC()) {
1234-
assert(isa<DestructorDecl>(VD) || !VD->isObjC() || VD->isDynamic() &&
1233+
if (!VD->isObjCMemberImplementation())
1234+
return;
1235+
1236+
if (VD->isObjC()) {
1237+
assert(isa<DestructorDecl>(VD) || VD->isDynamic() &&
12351238
"@objc decls in @_objcImplementations should be dynamic!");
12361239
return;
12371240
}

test/IRGen/objc_implementation.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,14 @@
7575
// CHECK: [[_METACLASS_DATA_SwiftSubclass]] = internal constant { i32, i32, i32, i32, i8*, i8*, i8*, i8*, i8*, i8*, i8* } { i32 129, i32 40, i32 40, i32 0, i8* null, i8* getelementptr inbounds ([41 x i8], [41 x i8]* @.str.40._TtC19objc_implementation13SwiftSubclass, i64 0, i64 0), i8* null, i8* null, i8* null, i8* null, i8* null }, section "__DATA, __objc_const", align 8
7676

7777
// Class
78-
// CHECK: [[_INSTANCE_METHODS_SwiftSubclass:@[^, ]+]] = internal constant { i32, i32, [1 x { i8*, i8*, i8* }] } { i32 24, i32 1, [1 x { i8*, i8*, i8* }] [{ i8*, i8*, i8* } { i8* getelementptr inbounds ([12 x i8], [12 x i8]* [[selector_data_mainMethod_]], i64 0, i64 0), i8* getelementptr inbounds ([11 x i8], [11 x i8]* @".str.10.v20@0:8i16", i64 0, i64 0), i8* bitcast ({{.*}}* @"$s19objc_implementation13SwiftSubclassC10mainMethodyys5Int32VFTo{{(\.ptrauth)?}}" to i8*) }] }, section "__DATA, __objc_data", align 8
79-
// CHECK: [[_DATA_SwiftSubclass:@[^, ]+]] = internal constant { i32, i32, i32, i32, i8*, i8*, { i32, i32, [1 x { i8*, i8*, i8* }] }*, i8*, i8*, i8*, i8* } { i32 128, i32 24, i32 24, i32 0, i8* null, i8* getelementptr inbounds ([41 x i8], [41 x i8]* @.str.40._TtC19objc_implementation13SwiftSubclass, i64 0, i64 0), { i32, i32, [1 x { i8*, i8*, i8* }] }* [[_INSTANCE_METHODS_SwiftSubclass]], i8* null, i8* null, i8* null, i8* null }, section "__DATA, __objc_data", align 8
78+
// CHECK: [[_INSTANCE_METHODS_SwiftSubclass:@[^, ]+]] = internal constant { i32, i32, [2 x { i8*, i8*, i8* }] } { i32 24, i32 2, [2 x { i8*, i8*, i8* }] [{ i8*, i8*, i8* } { i8* getelementptr inbounds ([12 x i8], [12 x i8]* [[selector_data_mainMethod_]], i64 0, i64 0), i8* getelementptr inbounds ([11 x i8], [11 x i8]* @".str.10.v20@0:8i16", i64 0, i64 0), i8* bitcast ({{.*}}* @"$s19objc_implementation13SwiftSubclassC10mainMethodyys5Int32VFTo{{(\.ptrauth)?}}" to i8*) }, { i8*, i8*, i8* } { i8* getelementptr inbounds ([5 x i8], [5 x i8]* [[selector_data_init]], i64 0, i64 0), i8* getelementptr inbounds ([8 x i8], [8 x i8]* @".str.7.@16@0:8", i64 0, i64 0), i8* bitcast ({{.*}}* @"$s19objc_implementation13SwiftSubclassCACycfcTo{{(\.ptrauth)?}}" to i8*) }] }, section "__DATA, __objc_data", align 8
79+
// CHECK: [[_DATA_SwiftSubclass:@[^, ]+]] = internal constant { i32, i32, i32, i32, i8*, i8*, { i32, i32, [2 x { i8*, i8*, i8* }] }*, i8*, i8*, i8*, i8* } { i32 128, i32 24, i32 24, i32 0, i8* null, i8* getelementptr inbounds ([41 x i8], [41 x i8]* @.str.40._TtC19objc_implementation13SwiftSubclass, i64 0, i64 0), { i32, i32, [2 x { i8*, i8*, i8* }] }* [[_INSTANCE_METHODS_SwiftSubclass]], i8* null, i8* null, i8* null, i8* null }, section "__DATA, __objc_data", align 8
8080

8181
// Swift metadata
8282
// CHECK: @"$s19objc_implementationMXM" = linkonce_odr hidden constant <{ i32, i32, i32 }> <{ i32 0, i32 0, i32 trunc (i64 sub (i64 ptrtoint ([20 x i8]* @.str.19.objc_implementation to i64), i64 ptrtoint (i32* getelementptr inbounds (<{ i32, i32, i32 }>, <{ i32, i32, i32 }>* @"$s19objc_implementationMXM", i32 0, i32 2) to i64)) to i32) }>, section "__TEXT,__const", align 4
8383
// CHECK: @"symbolic So9ImplClassC" = linkonce_odr hidden constant <{ [13 x i8], i8 }> <{ [13 x i8] c"So9ImplClassC", i8 0 }>, section "__TEXT,__swift5_typeref, regular", align 2
8484
// CHECK: @"$s19objc_implementation13SwiftSubclassCMn" = constant <{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }> <{ i32 80, i32 trunc (i64 sub (i64 ptrtoint (<{ i32, i32, i32 }>* @"$s19objc_implementationMXM" to i64), i64 ptrtoint (i32* getelementptr inbounds (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>, <{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s19objc_implementation13SwiftSubclassCMn", i32 0, i32 1) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ([14 x i8]* @.str.13.SwiftSubclass to i64), i64 ptrtoint (i32* getelementptr inbounds (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>, <{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s19objc_implementation13SwiftSubclassCMn", i32 0, i32 2) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (%swift.metadata_response (i64)* @"$s19objc_implementation13SwiftSubclassCMa" to i64), i64 ptrtoint (i32* getelementptr inbounds (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>, <{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s19objc_implementation13SwiftSubclassCMn", i32 0, i32 3) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint ({ i32, i32, i16, i16, i32 }* @"$s19objc_implementation13SwiftSubclassCMF" to i64), i64 ptrtoint (i32* getelementptr inbounds (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>, <{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s19objc_implementation13SwiftSubclassCMn", i32 0, i32 4) to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (<{ [13 x i8], i8 }>* @"symbolic So9ImplClassC" to i64), i64 ptrtoint (i32* getelementptr inbounds (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>, <{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s19objc_implementation13SwiftSubclassCMn", i32 0, i32 5) to i64)) to i32), i32 2, i32 10, i32 0, i32 0, i32 10 }>, section "__TEXT,__const", align 4
85-
// CHECK: @"$s19objc_implementation13SwiftSubclassCMf" = internal global <{ void (%T19objc_implementation13SwiftSubclassC*)*, i8**, i64, %objc_class*, %swift.opaque*, %swift.opaque*, {{i64|%swift.opaque\*}}, i32, i32, i32, i16, i16, i32, i32, <{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>*, i8* }> <{ {{.*}}* @"$s19objc_implementation13SwiftSubclassCfD{{(\.ptrauth)?}}"{{( to void \(.*\)\*\))?}}, i8** @"$sBOWV", i64 ptrtoint (%objc_class* @"OBJC_METACLASS_$__TtC19objc_implementation13SwiftSubclass" to i64), %objc_class* bitcast (<{ i64, %objc_class*, %swift.opaque*, %swift.opaque*, { i32, i32, i32, i32, i8*, i8*, { i32, i32, [5 x { i8*, i8*, i8* }] }*, i8*, { i32, i32, [2 x { i64*, i8*, i8*, i32, i32 }] }*, i8*, { i32, i32, [1 x { i8*, i8* }] }* }* }>* @"OBJC_CLASS_$_ImplClass" to %objc_class*), %swift.opaque* @_objc_empty_cache, %swift.opaque* null, {{i64 add \(i64 ptrtoint|%swift.opaque\* bitcast \(i8\* getelementptr \(i8, i8\* bitcast}} ({ i32, i32, i32, i32, i8*, i8*, { i32, i32, [1 x { i8*, i8*, i8* }] }*, i8*, i8*, i8*, i8* }* @_DATA__TtC19objc_implementation13SwiftSubclass to {{i64\), i64 1|i8\*\), i64 1\) to %swift.opaque\*}}), i32 0, i32 0, i32 24, i16 7, i16 0, i32 96, i32 16, <{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* {{(bitcast \(\{ i8\*, i32, i64, i64 \}\* )?}}@"$s19objc_implementation13SwiftSubclassCMn{{(\.ptrauth)?}}"{{( to <\{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 \}>\*\))?}}, i8* null }>, section "__DATA,__objc_data, regular", align 8
85+
// CHECK: @"$s19objc_implementation13SwiftSubclassCMf" = internal global <{ void (%T19objc_implementation13SwiftSubclassC*)*, i8**, i64, %objc_class*, %swift.opaque*, %swift.opaque*, {{i64|%swift.opaque\*}}, i32, i32, i32, i16, i16, i32, i32, <{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>*, i8* }> <{ {{.*}}* @"$s19objc_implementation13SwiftSubclassCfD{{(\.ptrauth)?}}"{{( to void \(.*\)\*\))?}}, i8** @"$sBOWV", i64 ptrtoint (%objc_class* @"OBJC_METACLASS_$__TtC19objc_implementation13SwiftSubclass" to i64), %objc_class* bitcast (<{ i64, %objc_class*, %swift.opaque*, %swift.opaque*, { i32, i32, i32, i32, i8*, i8*, { i32, i32, [5 x { i8*, i8*, i8* }] }*, i8*, { i32, i32, [2 x { i64*, i8*, i8*, i32, i32 }] }*, i8*, { i32, i32, [1 x { i8*, i8* }] }* }* }>* @"OBJC_CLASS_$_ImplClass" to %objc_class*), %swift.opaque* @_objc_empty_cache, %swift.opaque* null, {{i64 add \(i64 ptrtoint|%swift.opaque\* bitcast \(i8\* getelementptr \(i8, i8\* bitcast}} ({ i32, i32, i32, i32, i8*, i8*, { i32, i32, [2 x { i8*, i8*, i8* }] }*, i8*, i8*, i8*, i8* }* @_DATA__TtC19objc_implementation13SwiftSubclass to {{i64\), i64 1|i8\*\), i64 1\) to %swift.opaque\*}}), i32 0, i32 0, i32 24, i16 7, i16 0, i32 96, i32 16, <{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* {{(bitcast \(\{ i8\*, i32, i64, i64 \}\* )?}}@"$s19objc_implementation13SwiftSubclassCMn{{(\.ptrauth)?}}"{{( to <\{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 \}>\*\))?}}, i8* null }>, section "__DATA,__objc_data, regular", align 8
8686
// CHECK: @"symbolic _____ 19objc_implementation13SwiftSubclassC" = linkonce_odr hidden constant <{ i8, i32, i8 }> <{ i8 1, i32 trunc (i64 sub (i64 ptrtoint (<{ i32, i32, i32, i32, i32, i32, i32, i32, i32, i32, i32 }>* @"$s19objc_implementation13SwiftSubclassCMn" to i64), i64 ptrtoint (i32* getelementptr inbounds (<{ i8, i32, i8 }>, <{ i8, i32, i8 }>* @"symbolic _____ 19objc_implementation13SwiftSubclassC", i32 0, i32 1) to i64)) to i32), i8 0 }>, section "__TEXT,__swift5_typeref, regular", align 2
8787
// CHECK: @"$s19objc_implementation13SwiftSubclassCMF" = internal constant { i32, i32, i16, i16, i32 } { i32 trunc (i64 sub (i64 ptrtoint (<{ i8, i32, i8 }>* @"symbolic _____ 19objc_implementation13SwiftSubclassC" to i64), i64 ptrtoint ({ i32, i32, i16, i16, i32 }* @"$s19objc_implementation13SwiftSubclassCMF" to i64)) to i32), i32 trunc (i64 sub (i64 ptrtoint (<{ [13 x i8], i8 }>* @"symbolic So9ImplClassC" to i64), i64 ptrtoint (i32* getelementptr inbounds ({ i32, i32, i16, i16, i32 }, { i32, i32, i16, i16, i32 }* @"$s19objc_implementation13SwiftSubclassCMF", i32 0, i32 1) to i64)) to i32), i16 7, i16 12, i32 0 }, section "__TEXT,__swift5_fieldmd, regular", align 4
8888
open class SwiftSubclass: ImplClass {

test/Interpreter/objc_implementation.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class LastWords {
3535
print("implProperty =", impl.implProperty)
3636
impl.implProperty = 42
3737
print("implProperty =", impl.implProperty)
38+
print("description =", impl.description)
3839
}
3940

4041
do {
@@ -43,7 +44,7 @@ class LastWords {
4344
print("implProperty =", swiftSub.implProperty)
4445
swiftSub.implProperty = 42
4546
print("implProperty =", swiftSub.implProperty)
46-
47+
4748
print("otherProperty =", swiftSub.otherProperty)
4849
swiftSub.otherProperty = 13
4950
print("otherProperty =", swiftSub.otherProperty)
@@ -52,6 +53,10 @@ class LastWords {
5253
}
5354

5455
@objc func someMethod() -> String { "ImplClass.someMethod()" }
56+
57+
open override var description: String {
58+
"ImplClass(implProperty: \(implProperty), object: \(object))"
59+
}
5560
}
5661

5762
class SwiftSubclass: ImplClass {
@@ -70,6 +75,7 @@ ImplClass.runTests()
7075
// CHECK: someMethod = ImplClass.someMethod()
7176
// CHECK: implProperty = 0
7277
// CHECK: implProperty = 42
78+
// CHECK: description = ImplClass(implProperty: 42, object: main.LastWords)
7379
// CHECK: ImplClass It's better to burn out than to fade away.
7480
// CHECK: someMethod = SwiftSubclass.someMethod()
7581
// CHECK: implProperty = 0

test/decl/ext/Inputs/objc_implementation.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
1-
@interface ObjCClass
1+
@interface ObjCBaseClass
2+
3+
4+
// Need two initializers to reproduce certain conflict bugs.
5+
- (instancetype)initFromSuperclass:(int)param __attribute__((objc_designated_initializer));
6+
- (instancetype)initFromSuperclass2:(int)param __attribute__((objc_designated_initializer));
7+
8+
- (void)superclassMethod:(int)param;
9+
@property (assign) int superclassProperty;
10+
11+
@end
12+
13+
@interface ObjCClass : ObjCBaseClass
214
- (void)methodFromHeader1:(int)param;
315
- (void)methodFromHeader2:(int)param;
416
- (void)methodFromHeader3:(int)param;
@@ -40,6 +52,12 @@
4052

4153
@end
4254

55+
@interface ObjCSubclass : ObjCClass
56+
57+
- (void)subclassMethodFromHeader1:(int)param;
58+
59+
@end
60+
4361
struct ObjCStruct {
4462
int foo;
4563
};
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module objc_implementation_private {
2+
header "objc_implementation.h"
3+
export *
4+
}

0 commit comments

Comments
 (0)