Skip to content

Allow overrides in @_objcImplementation extensions #61782

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -2379,6 +2379,10 @@ class ValueDecl : public Decl {
/// Asserts if this is not a member of a protocol.
bool isProtocolRequirement() const;

/// Return true if this is a member implementation for an \c @_objcImplementation
/// extension.
bool isObjCMemberImplementation() const;

void setUserAccessible(bool Accessible) {
Bits.ValueDecl.IsUserAccessible = Accessible;
}
Expand Down
4 changes: 0 additions & 4 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1511,10 +1511,6 @@ ERROR(attr_objc_implementation_category_not_found,none,
NOTE(attr_objc_implementation_fixit_remove_category_name,none,
"remove arguments to implement the main '@interface' for this class",
())
ERROR(attr_objc_implementation_no_objc_final,none,
"%0 %1 cannot be 'final' because Objective-C subclasses of %2 can "
"override it",
(DescriptiveDeclKind, ValueDecl *, ValueDecl *))

ERROR(member_of_objc_implementation_not_objc_or_final,none,
"%0 %1 does not match any %0 declared in the headers for %2; did you use "
Expand Down
1 change: 1 addition & 0 deletions lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1458,6 +1458,7 @@ void ValueDecl::dumpRef(raw_ostream &os) const {

void LLVM_ATTRIBUTE_USED ValueDecl::dumpRef() const {
dumpRef(llvm::errs());
llvm::errs() << "\n";
}

void SourceFile::dump() const {
Expand Down
17 changes: 14 additions & 3 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3739,18 +3739,29 @@ static bool checkAccessUsingAccessScopes(const DeclContext *useDC,
/// Checks if \p VD is an ObjC member implementation:
///
/// \li It's in an \c \@_objcImplementation extension
/// \li It's \c \@objc
/// \li It's not explicitly \c final
/// \li Its access level is not \c private or \c fileprivate
static bool
isObjCMemberImplementation(const ValueDecl *VD,
llvm::function_ref<AccessLevel()> getAccessLevel) {
if (auto ED = dyn_cast<ExtensionDecl>(VD->getDeclContext()))
if (ED->isObjCImplementation())
return VD->isObjC() && getAccessLevel() >= AccessLevel::Internal;
if (ED->isObjCImplementation() && !isa<TypeDecl>(VD)) {
auto attrDecl = isa<AccessorDecl>(VD)
? cast<AccessorDecl>(VD)->getStorage()
: VD;
return !attrDecl->isFinal()
&& !attrDecl->getAttrs().hasAttribute<OverrideAttr>()
&& getAccessLevel() >= AccessLevel::Internal;
}

return false;
}

bool ValueDecl::isObjCMemberImplementation() const {
return ::isObjCMemberImplementation(
this, [&]() { return this->getFormalAccess(); });
}

/// Checks if \p VD may be used from \p useDC, taking \@testable and \@_spi
/// imports into account.
///
Expand Down
13 changes: 13 additions & 0 deletions lib/AST/NameLookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1701,11 +1701,24 @@ NominalTypeDecl::lookupDirect(ObjCSelector selector, bool isInstance) {
return stored.Methods;
}

static bool inObjCImplExtension(AbstractFunctionDecl *newDecl) {
if (auto ext = dyn_cast<ExtensionDecl>(newDecl->getDeclContext()))
return ext->isObjCImplementation();
return false;
}

/// If there is an apparent conflict between \p newDecl and one of the methods
/// in \p vec, should we diagnose it?
static bool
shouldDiagnoseConflict(NominalTypeDecl *ty, AbstractFunctionDecl *newDecl,
llvm::TinyPtrVector<AbstractFunctionDecl *> &vec) {
// Conflicts between member implementations and their interfaces, or
// inherited inits and their overrides in @_objcImpl extensions, are spurious.
if (newDecl->isObjCMemberImplementation()
|| (isa<ConstructorDecl>(newDecl) && inObjCImplExtension(newDecl)
&& newDecl->getAttrs().hasAttribute<OverrideAttr>()))
return false;

// Are all conflicting methods imported from ObjC and in our ObjC half or a
// bridging header? Some code bases implement ObjC methods in Swift even
// though it's not exactly supported.
Expand Down
33 changes: 11 additions & 22 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -811,22 +811,6 @@ IsFinalRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
if (!cls)
return false;

// Objective-C doesn't have a way to prevent overriding, so if this member
// is accessible from ObjC and it's possible to subclass its parent from ObjC,
// `final` doesn't make sense even when inferred.
//
// FIXME: This is technically true iff `!cls->hasKnownSwiftImplementation()`,
// but modeling that would be source-breaking, so instead we only
// enforce it in `@_objcImplementation extension`s.
if (auto ext = dyn_cast<ExtensionDecl>(decl->getDeclContext()))
if (ext->isObjCImplementation() && decl->isObjC()) {
if (explicitFinalAttr)
diagnoseAndRemoveAttr(decl, explicitFinalAttr,
diag::attr_objc_implementation_no_objc_final,
decl->getDescriptiveKind(), decl, cls);
return false;
}

switch (decl->getKind()) {
case DeclKind::Var: {
// Properties are final if they are declared 'static' or a 'let'
Expand Down Expand Up @@ -854,6 +838,12 @@ IsFinalRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
return true;

if (VD->isLet()) {
// If this `let` is in an `@_objcImplementation extension`, don't
// infer `final` unless it is written explicitly.
auto ed = dyn_cast<ExtensionDecl>(VD->getDeclContext());
if (!explicitFinalAttr && ed && ed->isObjCImplementation())
return false;

if (VD->getFormalAccess() == AccessLevel::Open) {
auto &context = decl->getASTContext();
auto diagID = diag::implicitly_final_cannot_be_open;
Expand Down Expand Up @@ -974,6 +964,11 @@ IsDynamicRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
return true;
}

// @_objcImplementation extension member implementations are implicitly
// dynamic.
if (decl->isObjCMemberImplementation())
return true;

if (auto accessor = dyn_cast<AccessorDecl>(decl)) {
// Runtime-replaceable accessors are dynamic when their storage declaration
// is dynamic and they were explicitly defined or they are implicitly defined
Expand Down Expand Up @@ -1005,12 +1000,6 @@ IsDynamicRequest::evaluate(Evaluator &evaluator, ValueDecl *decl) const {
if (isa<ExtensionDecl>(dc) && dc->getSelfClassDecl())
return true;

// @objc declarations in @_objcImplementation extensions are implicitly
// dynamic.
if (auto ED = dyn_cast_or_null<ExtensionDecl>(dc->getAsDecl()))
if (ED->isObjCImplementation())
return true;

// If any of the declarations overridden by this declaration are dynamic
// or were imported from Objective-C, this declaration is dynamic.
// Don't do this if the declaration is not exposed to Objective-C; that's
Expand Down
23 changes: 1 addition & 22 deletions lib/Sema/TypeCheckDeclObjC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1881,26 +1881,6 @@ static ObjCSelector inferObjCName(ValueDecl *decl) {
return *decl->getObjCRuntimeName(true);
}

static bool isObjCImplementation(AbstractFunctionDecl *method,
ObjCSelector methodName) {
auto ext = dyn_cast<ExtensionDecl>(method->getDeclContext());
if (!ext || !ext->isObjCImplementation())
return false;

auto contextInterface =
dyn_cast<IterableDeclContext>(ext->getImplementedObjCDecl());

for (auto otherMember : contextInterface->getMembers()) {
auto otherMethod = dyn_cast<AbstractFunctionDecl>(otherMember);
if (!otherMethod) continue;

if (otherMethod->getObjCSelector() == methodName)
return true;
}

return false;
}

/// Mark the given declaration as being Objective-C compatible (or
/// not) as appropriate.
///
Expand Down Expand Up @@ -2062,8 +2042,7 @@ void markAsObjC(ValueDecl *D, ObjCReason reason,

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

// Record the method in the source file.
Expand Down
7 changes: 5 additions & 2 deletions lib/Sema/TypeCheckDeclPrimary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1230,8 +1230,11 @@ static void checkObjCImplementationMemberAvoidsVTable(ValueDecl *VD) {
!ED->getSelfClassDecl()->hasKnownSwiftImplementation() &&
"@_objcImplementation on non-class or Swift class?");

if (VD->isSemanticallyFinal() || VD->isObjC()) {
assert(isa<DestructorDecl>(VD) || !VD->isObjC() || VD->isDynamic() &&
if (!VD->isObjCMemberImplementation())
return;

if (VD->isObjC()) {
assert(isa<DestructorDecl>(VD) || VD->isDynamic() &&
"@objc decls in @_objcImplementations should be dynamic!");
return;
}
Expand Down
6 changes: 3 additions & 3 deletions test/IRGen/objc_implementation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,14 @@
// 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

// Class
// 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
// 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
// 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
// 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

// Swift metadata
// 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
// 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
// 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
// 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
// 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
// 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
// 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
open class SwiftSubclass: ImplClass {
Expand Down
8 changes: 7 additions & 1 deletion test/Interpreter/objc_implementation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class LastWords {
print("implProperty =", impl.implProperty)
impl.implProperty = 42
print("implProperty =", impl.implProperty)
print("description =", impl.description)
}

do {
Expand All @@ -43,7 +44,7 @@ class LastWords {
print("implProperty =", swiftSub.implProperty)
swiftSub.implProperty = 42
print("implProperty =", swiftSub.implProperty)

print("otherProperty =", swiftSub.otherProperty)
swiftSub.otherProperty = 13
print("otherProperty =", swiftSub.otherProperty)
Expand All @@ -52,6 +53,10 @@ class LastWords {
}

@objc func someMethod() -> String { "ImplClass.someMethod()" }

open override var description: String {
"ImplClass(implProperty: \(implProperty), object: \(object))"
}
}

class SwiftSubclass: ImplClass {
Expand All @@ -70,6 +75,7 @@ ImplClass.runTests()
// CHECK: someMethod = ImplClass.someMethod()
// CHECK: implProperty = 0
// CHECK: implProperty = 42
// CHECK: description = ImplClass(implProperty: 42, object: main.LastWords)
// CHECK: ImplClass It's better to burn out than to fade away.
// CHECK: someMethod = SwiftSubclass.someMethod()
// CHECK: implProperty = 0
Expand Down
20 changes: 19 additions & 1 deletion test/decl/ext/Inputs/objc_implementation.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
@interface ObjCClass
@interface ObjCBaseClass


// Need two initializers to reproduce certain conflict bugs.
- (instancetype)initFromSuperclass:(int)param __attribute__((objc_designated_initializer));
- (instancetype)initFromSuperclass2:(int)param __attribute__((objc_designated_initializer));

- (void)superclassMethod:(int)param;
@property (assign) int superclassProperty;

@end

@interface ObjCClass : ObjCBaseClass
- (void)methodFromHeader1:(int)param;
- (void)methodFromHeader2:(int)param;
- (void)methodFromHeader3:(int)param;
Expand Down Expand Up @@ -40,6 +52,12 @@

@end

@interface ObjCSubclass : ObjCClass

- (void)subclassMethodFromHeader1:(int)param;

@end

struct ObjCStruct {
int foo;
};
4 changes: 4 additions & 0 deletions test/decl/ext/Inputs/objc_implementation_private.modulemap
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module objc_implementation_private {
header "objc_implementation.h"
export *
}
Loading