Skip to content

Commit 4dae9ee

Browse files
authored
Merge pull request #76270 from beccadax/objcimpl-serialization-2
2 parents 7753ad0 + 94ec99c commit 4dae9ee

File tree

8 files changed

+124
-23
lines changed

8 files changed

+124
-23
lines changed

include/swift/AST/Attr.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,8 +185,9 @@ class DeclAttribute : public AttributeBase {
185185
isUnchecked : 1
186186
);
187187

188-
SWIFT_INLINE_BITFIELD(ObjCImplementationAttr, DeclAttribute, 2,
188+
SWIFT_INLINE_BITFIELD(ObjCImplementationAttr, DeclAttribute, 3,
189189
isCategoryNameInvalid : 1,
190+
hasInvalidImplicitLangAttrs : 1,
190191
isEarlyAdopter : 1
191192
);
192193

@@ -2435,6 +2436,7 @@ class ObjCImplementationAttr final : public DeclAttribute {
24352436
: DeclAttribute(DeclAttrKind::ObjCImplementation, AtLoc, Range, Implicit),
24362437
CategoryName(CategoryName) {
24372438
Bits.ObjCImplementationAttr.isCategoryNameInvalid = isCategoryNameInvalid;
2439+
Bits.ObjCImplementationAttr.hasInvalidImplicitLangAttrs = false;
24382440
Bits.ObjCImplementationAttr.isEarlyAdopter = isEarlyAdopter;
24392441
}
24402442

@@ -2452,6 +2454,16 @@ class ObjCImplementationAttr final : public DeclAttribute {
24522454
Bits.ObjCImplementationAttr.isCategoryNameInvalid = newValue;
24532455
}
24542456

2457+
/// Has at least one implicitly ObjC member failed to validate? If so,
2458+
/// diagnostics that might be duplicative will be suppressed.
2459+
bool hasInvalidImplicitLangAttrs() const {
2460+
return Bits.ObjCImplementationAttr.hasInvalidImplicitLangAttrs;
2461+
}
2462+
2463+
void setHasInvalidImplicitLangAttrs(bool newValue = true) {
2464+
Bits.ObjCImplementationAttr.hasInvalidImplicitLangAttrs = newValue;
2465+
}
2466+
24552467
static bool classof(const DeclAttribute *DA) {
24562468
return DA->getKind() == DeclAttrKind::ObjCImplementation;
24572469
}

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,12 @@ void ObjCReason::describe(const Decl *D) const {
153153
}
154154

155155
void ObjCReason::setAttrInvalid() const {
156-
if (requiresAttr(kind))
156+
if (!requiresAttr(kind))
157+
return;
158+
159+
if (kind == MemberOfObjCImplementationExtension)
160+
cast<ObjCImplementationAttr>(getAttr())->setHasInvalidImplicitLangAttrs();
161+
else
157162
getAttr()->setInvalid();
158163
}
159164

@@ -3074,6 +3079,17 @@ class ObjCImplementationChecker {
30743079

30753080
bool hasDiagnosed = false;
30763081

3082+
bool hasInvalidLangAttr(ValueDecl *cand) {
3083+
// If isObjC() found a problem, it will have set the invalid bit on either
3084+
// the candidate's ObjCAttr, if it has one, or the controlling
3085+
// ObjCImplementationAttr otherwise.
3086+
if (auto objc = cand->getAttrs()
3087+
.getAttribute<ObjCAttr>(/*AllowInvalid=*/true))
3088+
return objc->isInvalid();
3089+
3090+
return getAttr()->hasInvalidImplicitLangAttrs() || getAttr()->isInvalid();
3091+
}
3092+
30773093
public:
30783094
ObjCImplementationChecker(Decl *D)
30793095
: decl(D), hasDiagnosed(getAttr()->isInvalid())
@@ -3158,18 +3174,11 @@ class ObjCImplementationChecker {
31583174
return;
31593175

31603176
// Don't diagnose if we already diagnosed an unrelated ObjC interop issue,
3161-
// like an un-representable type. If there's an `@objc` attribute on the
3162-
// member, this will be indicated by its `isInvalid()` bit; otherwise we'll
3163-
// use the enclosing extension's `@_objcImplementation` attribute.
3164-
DeclAttribute *attr = afd->getAttrs()
3165-
.getAttribute<ObjCAttr>(/*AllowInvalid=*/true);
3166-
if (!attr)
3167-
attr = member->getDeclContext()->getAsDecl()->getAttrs()
3168-
.getAttribute<ObjCImplementationAttr>(/*AllowInvalid=*/true);
3169-
assert(attr && "expected @_objcImplementation on context of member checked "
3170-
"by ObjCImplementationChecker");
3171-
if (attr->isInvalid())
3177+
// like an un-representable type.
3178+
if (hasInvalidLangAttr(member)) {
3179+
hasDiagnosed = true;
31723180
return;
3181+
}
31733182

31743183
if (auto init = dyn_cast<ConstructorDecl>(afd)) {
31753184
if (!init->isObjC() && (init->isRequired() ||
@@ -3651,6 +3660,13 @@ class ObjCImplementationChecker {
36513660

36523661
void diagnoseOutcome(MatchOutcome outcome, ValueDecl *req, ValueDecl *cand,
36533662
ObjCSelector explicitObjCName) {
3663+
// If the candidate was invalid, we've already diagnosed the likely cause of
3664+
// the mismatch. Don't dogpile.
3665+
if (hasInvalidLangAttr(cand)) {
3666+
hasDiagnosed = true;
3667+
return;
3668+
}
3669+
36543670
auto reqObjCName = getObjCName(req);
36553671

36563672
switch (outcome) {

test/Serialization/Inputs/module.modulemap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,7 @@ module CLibrary {
66
module RawLayoutCXX {
77
header "raw_layout_cxx.h"
88
}
9+
10+
module objc_implementation {
11+
header "objc_implementation.h"
12+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#import <Foundation/Foundation.h>
2+
3+
NS_ASSUME_NONNULL_BEGIN
4+
5+
@interface ObjCImpl : NSObject
6+
7+
- (void)goodMethod;
8+
9+
@end
10+
11+
NS_ASSUME_NONNULL_END
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-typecheck-verify-swift -target %target-stable-abi-triple -module-name main -I %S/Inputs %s
3+
// RUN: %target-swift-frontend -emit-module -target %target-stable-abi-triple -module-name main -o %t -I %S/Inputs %s
4+
// RUN: llvm-bcanalyzer %t/main.swiftmodule > %t/main.swiftmodule.txt
5+
// RUN: %target-swift-ide-test -print-module -target %target-stable-abi-triple -module-to-print=main -I %t -source-filename=%s >%t/main.txt
6+
// RUN: %FileCheck %s --input-file=%t/main.txt
7+
8+
// REQUIRES: objc_interop
9+
10+
import Foundation
11+
import objc_implementation
12+
13+
// rdar://134730183 - ensure that errors reduced to warnings by early adopter
14+
// syntax don't invalidate the @implementation attribute (and cause it to not
15+
// be serialized)
16+
17+
// CHECK-LABEL: @_objcImplementation extension ObjCImpl
18+
@_objcImplementation extension ObjCImpl {
19+
// CHECK-DAG: func cannotBeObjCMethod(_ value: Int?)
20+
private func cannotBeObjCMethod(_ value: Int?) {}
21+
// expected-warning@-1 {{method cannot be in an @objc @implementation extension of a class (without final or @nonobjc) because the type of the parameter cannot be represented in Objective-C}}
22+
23+
// CHECK-DAG: @objc func goodMethod()
24+
@objc public func goodMethod() {}
25+
}

test/decl/ext/Inputs/objc_implementation.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,13 @@
127127

128128
@end
129129

130+
@interface ObjCClass (InvalidMembers)
131+
132+
- (void)unimplementedMember;
133+
- (void)nonObjCMethod:(id)value;
134+
135+
@end
136+
130137
@interface ObjCClass (EmptyCategory)
131138
@end
132139

@@ -162,6 +169,10 @@
162169
- (nullable id)nullableResult;
163170
- (void)nullableArgument:(nullable id)arg;
164171

172+
@end
173+
174+
@interface ObjCClass (TypeMatchOptionalityInvalid)
175+
165176
- (int)nonPointerResult;
166177
- (void)nonPointerArgument:(int)arg;
167178

test/decl/ext/objc_implementation.swift

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -229,11 +229,6 @@ protocol EmptySwiftProto {}
229229
// rdar://122280735 - crash when the parameter of a block property needs @escaping
230230
let rdar122280735: (() -> ()) -> Void = { _ in }
231231
// expected-error@-1 {{property 'rdar122280735' of type '(() -> ()) -> Void' does not match type '(@escaping () -> Void) -> Void' declared by the header}}
232-
233-
private func privateNonObjCMethod(_: EmptySwiftProto) {
234-
// expected-error@-1 {{method cannot be in an @objc @implementation extension of a class (without final or @nonobjc) because the type of the parameter cannot be represented in Objective-C}}
235-
// expected-note@-2 {{protocol-constrained type containing protocol 'EmptySwiftProto' cannot be represented in Objective-C}}
236-
}
237232
}
238233

239234
@objc(PresentAdditions) @implementation extension ObjCClass {
@@ -432,11 +427,27 @@ protocol EmptySwiftProto {}
432427

433428
func nullableResult() -> Any { fatalError() } // expected-error {{instance method 'nullableResult()' of type '() -> Any' does not match type '() -> Any?' declared by the header}}
434429
func nullableArgument(_: Any) {} // expected-error {{instance method 'nullableArgument' of type '(Any) -> ()' does not match type '(Any?) -> Void' declared by the header}}
430+
}
435431

432+
@objc(TypeMatchOptionalityInvalid) @implementation extension ObjCClass {
436433
func nonPointerResult() -> CInt! { fatalError() } // expected-error{{method cannot be in an @objc @implementation extension of a class (without final or @nonobjc) because its result type cannot be represented in Objective-C}}
437434
func nonPointerArgument(_: CInt!) {} // expected-error {{method cannot be in an @objc @implementation extension of a class (without final or @nonobjc) because the type of the parameter cannot be represented in Objective-C}}
438435
}
439436

437+
@objc(InvalidMembers) @implementation extension ObjCClass {
438+
// expected-error@-1 {{extension for category 'InvalidMembers' should provide implementation for instance method 'unimplementedMember()'}}
439+
440+
func nonObjCMethod(_: EmptySwiftProto) {
441+
// expected-error@-1 {{method cannot be in an @objc @implementation extension of a class (without final or @nonobjc) because the type of the parameter cannot be represented in Objective-C}}
442+
// expected-note@-2 {{protocol-constrained type containing protocol 'EmptySwiftProto' cannot be represented in Objective-C}}
443+
}
444+
445+
private func privateNonObjCMethod(_: EmptySwiftProto) {
446+
// expected-error@-1 {{method cannot be in an @objc @implementation extension of a class (without final or @nonobjc) because the type of the parameter cannot be represented in Objective-C}}
447+
// expected-note@-2 {{protocol-constrained type containing protocol 'EmptySwiftProto' cannot be represented in Objective-C}}
448+
}
449+
}
450+
440451
// Intentionally using `@_objcImplementation` for this test; do not upgrade!
441452
@_objcImplementation(EmptyCategory) extension ObjCClass {
442453
// expected-warning@-1 {{'@_objcImplementation' is deprecated; use '@implementation' instead}} {{1-36=@implementation}} {{1-1=@objc(EmptyCategory) }}

test/decl/ext/objc_implementation_early_adopter.swift

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -229,11 +229,6 @@ protocol EmptySwiftProto {}
229229
// rdar://122280735 - crash when the parameter of a block property needs @escaping
230230
let rdar122280735: (() -> ()) -> Void = { _ in }
231231
// expected-warning@-1 {{property 'rdar122280735' of type '(() -> ()) -> Void' does not match type '(@escaping () -> Void) -> Void' declared by the header}}
232-
233-
private func privateNonObjCMethod(_: EmptySwiftProto) {
234-
// expected-warning@-1 {{method cannot be in an @objc @implementation extension of a class (without final or @nonobjc) because the type of the parameter cannot be represented in Objective-C}}
235-
// expected-note@-2 {{protocol-constrained type containing protocol 'EmptySwiftProto' cannot be represented in Objective-C}}
236-
}
237232
}
238233

239234
@_objcImplementation(PresentAdditions) extension ObjCClass {
@@ -432,11 +427,27 @@ protocol EmptySwiftProto {}
432427

433428
func nullableResult() -> Any { fatalError() } // expected-warning {{instance method 'nullableResult()' of type '() -> Any' does not match type '() -> Any?' declared by the header}}
434429
func nullableArgument(_: Any) {} // expected-warning {{instance method 'nullableArgument' of type '(Any) -> ()' does not match type '(Any?) -> Void' declared by the header}}
430+
}
435431

432+
@_objcImplementation(TypeMatchOptionalityInvalid) extension ObjCClass {
436433
func nonPointerResult() -> CInt! { fatalError() } // expected-warning{{method cannot be in an @objc @implementation extension of a class (without final or @nonobjc) because its result type cannot be represented in Objective-C}}
437434
func nonPointerArgument(_: CInt!) {} // expected-warning {{method cannot be in an @objc @implementation extension of a class (without final or @nonobjc) because the type of the parameter cannot be represented in Objective-C}}
438435
}
439436

437+
@_objcImplementation(InvalidMembers) extension ObjCClass {
438+
// expected-warning@-1 {{extension for category 'InvalidMembers' should provide implementation for instance method 'unimplementedMember()'}}
439+
440+
func nonObjCMethod(_: EmptySwiftProto) {
441+
// expected-warning@-1 {{method cannot be in an @objc @implementation extension of a class (without final or @nonobjc) because the type of the parameter cannot be represented in Objective-C}}
442+
// expected-note@-2 {{protocol-constrained type containing protocol 'EmptySwiftProto' cannot be represented in Objective-C}}
443+
}
444+
445+
private func privateNonObjCMethod(_: EmptySwiftProto) {
446+
// expected-warning@-1 {{method cannot be in an @objc @implementation extension of a class (without final or @nonobjc) because the type of the parameter cannot be represented in Objective-C}}
447+
// expected-note@-2 {{protocol-constrained type containing protocol 'EmptySwiftProto' cannot be represented in Objective-C}}
448+
}
449+
}
450+
440451
// Intentionally using `@_objcImplementation` for this test; do not upgrade!
441452
@_objcImplementation(EmptyCategory) extension ObjCClass {
442453
// expected-warning@-1 {{'@_objcImplementation' is deprecated; use '@implementation' instead}} {{1-36=@implementation}} {{1-1=@objc(EmptyCategory) }}

0 commit comments

Comments
 (0)