Skip to content

Commit eb03f37

Browse files
authored
Make sure we infer selectors for accessors when checking a conformance (swiftlang#14794)
Usually this happens directly, through some use of the class and its conformance. However, if a conformance is /only/ used to satisfy an associated type, we seem to bypass the step that actually infers selector names for accessors, even though we do it successfully for methods. Fix this by making sure the accessor decls are validated when a property is, something that normal uses of a property probably have to do anyway. Also, simplify inferObjCName by assuming/asserting that it is only used on things that are already marked @objc. https://bugs.swift.org/browse/SR-6944
1 parent 38e37b2 commit eb03f37

File tree

4 files changed

+43
-35
lines changed

4 files changed

+43
-35
lines changed

lib/Sema/TypeCheckDecl.cpp

Lines changed: 18 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2659,6 +2659,9 @@ static void inferObjCName(TypeChecker &tc, ValueDecl *decl) {
26592659
if (isa<DestructorDecl>(decl))
26602660
return;
26612661

2662+
auto attr = decl->getAttrs().getAttribute<ObjCAttr>();
2663+
assert(attr && "should only be called on decls already marked @objc");
2664+
26622665
// If this declaration overrides an @objc declaration, use its name.
26632666
if (auto overridden = decl->getOverriddenDecl()) {
26642667
if (overridden->isObjC()) {
@@ -2667,17 +2670,6 @@ static void inferObjCName(TypeChecker &tc, ValueDecl *decl) {
26672670
// Determine the selector of the overridden method.
26682671
ObjCSelector overriddenSelector = overriddenFunc->getObjCSelector();
26692672

2670-
// Dig out the @objc attribute on the method, if it exists.
2671-
auto attr = decl->getAttrs().getAttribute<ObjCAttr>();
2672-
if (!attr) {
2673-
// There was no @objc attribute; add one with the
2674-
// appropriate name.
2675-
decl->getAttrs().add(ObjCAttr::create(tc.Context,
2676-
overriddenSelector,
2677-
true));
2678-
return;
2679-
}
2680-
26812673
// Determine whether there is a name conflict.
26822674
bool shouldFixName = !attr->hasName();
26832675
if (attr->hasName() && *attr->getName() != overriddenSelector) {
@@ -2711,18 +2703,6 @@ static void inferObjCName(TypeChecker &tc, ValueDecl *decl) {
27112703
Identifier overriddenName = overriddenProp->getObjCPropertyName();
27122704
ObjCSelector overriddenNameAsSel(tc.Context, 0, overriddenName);
27132705

2714-
// Dig out the @objc attribute, if specified.
2715-
auto attr = decl->getAttrs().getAttribute<ObjCAttr>();
2716-
if (!attr) {
2717-
// There was no @objc attribute; add one with the
2718-
// appropriate name.
2719-
decl->getAttrs().add(
2720-
ObjCAttr::createNullary(tc.Context,
2721-
overriddenName,
2722-
/*isNameImplicit=*/true));
2723-
return;
2724-
}
2725-
27262706
// Determine whether there is a name conflict.
27272707
bool shouldFixName = !attr->hasName();
27282708
if (attr->hasName() && *attr->getName() != overriddenNameAsSel) {
@@ -2751,11 +2731,9 @@ static void inferObjCName(TypeChecker &tc, ValueDecl *decl) {
27512731
}
27522732
}
27532733

2754-
// Dig out the @objc attribute. If it already has a name, do
2755-
// nothing; the protocol conformance checker will handle any
2756-
// mismatches.
2757-
auto attr = decl->getAttrs().getAttribute<ObjCAttr>();
2758-
if (attr && attr->hasName()) return;
2734+
// If the decl already has a name, do nothing; the protocol conformance
2735+
// checker will handle any mismatches.
2736+
if (attr->hasName()) return;
27592737

27602738
// When no override determined the Objective-C name, look for
27612739
// requirements for which this declaration is a witness.
@@ -2799,13 +2777,8 @@ static void inferObjCName(TypeChecker &tc, ValueDecl *decl) {
27992777

28002778
// If we have a name, install it via an @objc attribute.
28012779
if (requirementObjCName) {
2802-
if (attr)
2803-
const_cast<ObjCAttr *>(attr)->setName(*requirementObjCName,
2804-
/*implicit=*/true);
2805-
else
2806-
decl->getAttrs().add(
2807-
ObjCAttr::create(tc.Context, *requirementObjCName,
2808-
/*implicitName=*/true));
2780+
const_cast<ObjCAttr *>(attr)->setName(*requirementObjCName,
2781+
/*implicit=*/true);
28092782
}
28102783
}
28112784

@@ -3943,6 +3916,16 @@ static void validateAbstractStorageDecl(TypeChecker &TC,
39433916
storage->setIsGetterMutating(computeIsGetterMutating(TC, storage));
39443917
storage->setIsSetterMutating(computeIsSetterMutating(TC, storage));
39453918

3919+
// We can't delay validation of getters and setters on @objc properties,
3920+
// because if they never get validated at all then conformance checkers
3921+
// will complain about selector mismatches.
3922+
if (storage->isObjC()) {
3923+
if (auto *getter = storage->getGetter())
3924+
TC.validateDecl(getter);
3925+
if (auto *setter = storage->getSetter())
3926+
TC.validateDecl(setter);
3927+
}
3928+
39463929
// Create a materializeForSet function if necessary. This needs to
39473930
// happen immediately so that subclass materializeForSet functions
39483931
// will be properly marked as overriding it.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
class Test: TestProto {
2+
var swiftFoo: Int32 { return 0 }
3+
var swiftBar: Int32 {
4+
get { return 0 }
5+
set {}
6+
}
7+
}

validation-test/Sema/Inputs/sr9644.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@protocol TestProto
2+
@property (nonatomic, readonly, getter=getFoo) int foo __attribute__((swift_name("swiftFoo")));
3+
@property (nonatomic, getter=getTheBar, setter=setTheBar:) int bar __attribute__((swift_name("swiftBar")));
4+
@end

validation-test/Sema/sr9644.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: %target-swift-frontend -typecheck -primary-file %s %S/Inputs/sr9644-helper.swift -import-objc-header %S/Inputs/sr9644.h -verify
2+
3+
// REQUIRES: objc_interop
4+
// expected-no-warning
5+
6+
protocol HasAssoc {
7+
associatedtype Assoc: TestProto
8+
}
9+
10+
struct Impl: HasAssoc {
11+
// This used to trigger validation of the Test: TestProto conformance, but
12+
// /without/ propagating the correct selector onto Test.swiftFoo's getter.
13+
typealias Assoc = Test
14+
}

0 commit comments

Comments
 (0)