Skip to content

Commit 21dfa78

Browse files
committed
Getters for properties with type 'Self' shouldn't use dynamic-self.
That is, a property in a protocol with type 'Self' doesn't promise that the result type always matches the type of 'self', just the type of the conforming type. (For a method, a result type of 'Self' /does/ mean you get back a type based on the dynamic 'self', like Objective-C's 'instancetype'.) This applies even to get-only properties, and so their accessors should be treated consistently. With this change, we can have the AST verifier check that getter and setter types always match up with their property.
1 parent 3c5c487 commit 21dfa78

File tree

2 files changed

+58
-1
lines changed

2 files changed

+58
-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/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

0 commit comments

Comments
 (0)