Skip to content

Commit 62e93a6

Browse files
authored
Merge pull request #6175 from jrose-apple/bad-property-redeclaration
[ClangImporter] Ignore redeclared properties with different types rdar://problem/29422993
2 parents 706099b + 21dfa78 commit 62e93a6

File tree

12 files changed

+148
-1
lines changed

12 files changed

+148
-1
lines changed

lib/AST/ASTVerifier.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1735,6 +1735,58 @@ struct ASTNodeBase {};
17351735
var->getType().print(Out);
17361736
abort();
17371737
}
1738+
1739+
Type typeForAccessors =
1740+
var->getInterfaceType()->getReferenceStorageReferent();
1741+
typeForAccessors =
1742+
ArchetypeBuilder::mapTypeIntoContext(var->getDeclContext(),
1743+
typeForAccessors);
1744+
if (const FuncDecl *getter = var->getGetter()) {
1745+
if (getter->getParameterLists().back()->size() != 0) {
1746+
Out << "property getter has parameters\n";
1747+
abort();
1748+
}
1749+
Type getterResultType = getter->getResultInterfaceType();
1750+
getterResultType =
1751+
ArchetypeBuilder::mapTypeIntoContext(var->getDeclContext(),
1752+
getterResultType);
1753+
if (!getterResultType->isEqual(typeForAccessors)) {
1754+
Out << "property and getter have mismatched types: '";
1755+
typeForAccessors.print(Out);
1756+
Out << "' vs. '";
1757+
getterResultType.print(Out);
1758+
Out << "'\n";
1759+
abort();
1760+
}
1761+
}
1762+
1763+
if (const FuncDecl *setter = var->getSetter()) {
1764+
if (!setter->getResultInterfaceType()->isVoid()) {
1765+
Out << "property setter has non-Void result type\n";
1766+
abort();
1767+
}
1768+
if (setter->getParameterLists().back()->size() == 0) {
1769+
Out << "property setter has no parameters\n";
1770+
abort();
1771+
}
1772+
if (setter->getParameterLists().back()->size() != 1) {
1773+
Out << "property setter has 2+ parameters\n";
1774+
abort();
1775+
}
1776+
const ParamDecl *param = setter->getParameterLists().back()->get(0);
1777+
Type paramType = param->getInterfaceType();
1778+
paramType = ArchetypeBuilder::mapTypeIntoContext(var->getDeclContext(),
1779+
paramType);
1780+
if (!paramType->isEqual(typeForAccessors)) {
1781+
Out << "property and setter param have mismatched types: '";
1782+
typeForAccessors.print(Out);
1783+
Out << "' vs. '";
1784+
paramType.print(Out);
1785+
Out << "'\n";
1786+
abort();
1787+
}
1788+
}
1789+
17381790
verifyCheckedBase(var);
17391791
}
17401792

lib/ClangImporter/ImportDecl.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4023,6 +4023,16 @@ namespace {
40234023
if (!setter)
40244024
return;
40254025

4026+
// Check that the redeclared property's setter uses the same type as the
4027+
// original property. Objective-C can get away with the types being
4028+
// different (usually in something like nullability), but for Swift it's
4029+
// an AST invariant that's assumed and asserted elsewhere. If the type is
4030+
// different, just drop the setter, and leave the property as get-only.
4031+
assert(setter->getParameterLists().back()->size() == 1);
4032+
const ParamDecl *param = setter->getParameterLists().back()->get(0);
4033+
if (!param->getInterfaceType()->isEqual(original->getInterfaceType()))
4034+
return;
4035+
40264036
original->setComputedSetter(setter);
40274037
}
40284038

lib/Sema/TypeCheckDecl.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4524,7 +4524,12 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
45244524
auto typeRepr = func->getBodyResultTypeLoc().getTypeRepr();
45254525
if (!typeRepr)
45264526
return false;
4527-
4527+
4528+
// 'Self' on a property accessor is not dynamic 'Self'...even on a read-only
4529+
// property. We could implement it as such in the future.
4530+
if (func->isAccessor())
4531+
return false;
4532+
45284533
return checkDynamicSelfReturn(func, typeRepr, 0);
45294534
}
45304535

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
@interface RPFoo
2+
@property (readonly, nonnull) int *nonnullToNullable;
3+
@property (readonly, nullable) int *nullableToNonnull;
4+
@property (readonly, nonnull) id typeChangeMoreSpecific;
5+
@property (readonly, nonnull) RPFoo *typeChangeMoreGeneral;
6+
7+
@property (readonly, nonnull) id accessorRedeclaredAsNullable;
8+
- (nullable id)accessorRedeclaredAsNullable;
9+
10+
- (nullable id)accessorDeclaredFirstAsNullable;
11+
@property (readonly, nonnull) id accessorDeclaredFirstAsNullable;
12+
@end
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
@interface RPFoo ()
2+
@property (readwrite, nullable) int *nonnullToNullable;
3+
@property (readwrite, nonnull) int *nullableToNonnull;
4+
@property (readwrite, nonnull) RPFoo *typeChangeMoreSpecific;
5+
@property (readwrite, nonnull) id typeChangeMoreGeneral;
6+
@end
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#import "RPFirst.h"
2+
#import "RPSecond.h"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#import "RPFirst.h"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#import "RedeclaredPropertiesSplit.h"
2+
#import "RPSecond.h"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#import "RPFirst.h"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#import "RedeclaredPropertiesSub.h"
2+
#import "RPSecond.h"
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
module RedeclaredPropertiesTextual {
2+
textual header "RPFirst.h"
3+
textual header "RPSecond.h"
4+
}
5+
6+
module RedeclaredProperties {
7+
header "RedeclaredProperties.h"
8+
}
9+
10+
module RedeclaredPropertiesSub {
11+
header "RedeclaredPropertiesSub.h"
12+
explicit module Private {
13+
header "RedeclaredPropertiesSubPrivate.h"
14+
}
15+
}
16+
17+
module RedeclaredPropertiesSplit {
18+
header "RedeclaredPropertiesSplit.h"
19+
}
20+
module RedeclaredPropertiesSplit2 {
21+
header "RedeclaredPropertiesSplit2.h"
22+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules -D ONE_MODULE %s -verify
2+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules -D SUB_MODULE %s -verify
3+
// RUN: %target-swift-frontend(mock-sdk: %clang-importer-sdk) -typecheck -I %S/Inputs/custom-modules -D TWO_MODULES %s -verify
4+
5+
// REQUIRES: objc_interop
6+
7+
#if ONE_MODULE
8+
import RedeclaredProperties
9+
#elseif SUB_MODULE
10+
import RedeclaredPropertiesSub
11+
import RedeclaredPropertiesSub.Private
12+
#elseif TWO_MODULES
13+
import RedeclaredPropertiesSplit
14+
import RedeclaredPropertiesSplit2
15+
#endif
16+
17+
func test(obj: RPFoo) {
18+
if let _ = obj.nonnullToNullable {} // expected-error {{initializer for conditional binding must have Optional type}}
19+
obj.nonnullToNullable = obj // expected-error {{cannot assign to property: 'nonnullToNullable' is a get-only property}}
20+
21+
if let _ = obj.nullableToNonnull {} // okay
22+
obj.nullableToNonnull = obj // expected-error {{cannot assign to property: 'nullableToNonnull' is a get-only property}}
23+
24+
let _: RPFoo = obj.typeChangeMoreSpecific // expected-error {{cannot convert value of type 'Any' to specified type 'RPFoo'}}
25+
obj.typeChangeMoreSpecific = obj // expected-error {{cannot assign to property: 'typeChangeMoreSpecific' is a get-only property}}
26+
27+
let _: RPFoo = obj.typeChangeMoreGeneral
28+
obj.typeChangeMoreGeneral = obj // expected-error {{cannot assign to property: 'typeChangeMoreGeneral' is a get-only property}}
29+
30+
if let _ = obj.accessorRedeclaredAsNullable {} // expected-error {{initializer for conditional binding must have Optional type}}
31+
if let _ = obj.accessorDeclaredFirstAsNullable {} // expected-error {{initializer for conditional binding must have Optional type}}
32+
}

0 commit comments

Comments
 (0)