Skip to content

Commit d953fa6

Browse files
Merge pull request #34005 from AnthonyLatsis/coself
Sema: Allow non-final classes to satisfy properties and subscripts with covariant Self
2 parents 7566e9f + 9f58968 commit d953fa6

File tree

10 files changed

+295
-105
lines changed

10 files changed

+295
-105
lines changed

include/swift/AST/Decl.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4121,12 +4121,14 @@ struct SelfReferenceKind {
41214121
return SelfReferenceKind(false, false, false, false);
41224122
}
41234123

4124-
/// The type refers to 'Self', but only as the result type of a method.
4124+
/// The type refers to 'Self', but only as the type of a property or
4125+
/// the result type of a method/subscript.
41254126
static SelfReferenceKind Result() {
41264127
return SelfReferenceKind(true, false, false, false);
41274128
}
41284129

4129-
/// The type refers to 'Self', but only as the parameter type of a method.
4130+
/// The type refers to 'Self', but only as the parameter type
4131+
/// of a method/subscript.
41304132
static SelfReferenceKind Parameter() {
41314133
return SelfReferenceKind(false, true, false, false);
41324134
}

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1992,14 +1992,15 @@ NOTE(witness_self_weaken_same_type,none,
19921992
"consider weakening the same-type requirement %0 == %1 to a superclass "
19931993
"requirement", (Type, Type))
19941994
ERROR(witness_requires_dynamic_self,none,
1995-
"method %0 in non-final class %1 must return 'Self' to conform to "
1996-
"protocol %2",
1997-
(DeclName, Type, Type))
1995+
"%select{%error|method|property|subscript}0 %1 in non-final class %2 "
1996+
"must %select{%error|return|specify type|return}0 'Self' "
1997+
"to conform to protocol %3",
1998+
(RequirementKind, DeclName, Type, Type))
19981999
ERROR(witness_requires_class_implementation,none,
1999-
"method %0 in non-final class %1 cannot be implemented in a "
2000-
"protocol extension because it returns 'Self' and has associated type "
2001-
"requirements",
2002-
(DeclName, Type))
2000+
"%select{%error|method|%error|subscript}0 %1 in non-final class %2 "
2001+
"cannot be implemented in a protocol extension because it returns 'Self' "
2002+
"and has associated type requirements",
2003+
(RequirementKind, DeclName, Type))
20032004
ERROR(witness_not_accessible_proto,none,
20042005
"%select{initializer %1|method %1|%select{|setter for }2property %1"
20052006
"|subscript%select{| setter}2}0 must be declared "

lib/AST/Decl.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4960,12 +4960,13 @@ ProtocolDecl::findProtocolSelfReferences(const ValueDecl *value,
49604960
return ::findProtocolSelfReferences(this, type,
49614961
skipAssocTypes);
49624962
} else {
4963-
if (::findProtocolSelfReferences(this, type,
4964-
skipAssocTypes)) {
4965-
return SelfReferenceKind::Other();
4966-
}
4967-
return SelfReferenceKind::None();
4963+
assert(isa<VarDecl>(value));
4964+
4965+
return ::findProtocolSelfReferences(this, type,
4966+
skipAssocTypes);
49684967
}
4968+
4969+
return SelfReferenceKind::None();
49694970
}
49704971

49714972
bool ProtocolDecl::isAvailableInExistential(const ValueDecl *decl) const {

lib/Sema/CSApply.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,7 +1057,6 @@ namespace {
10571057
AccessSemantics semantics) {
10581058
auto choice = overload.choice;
10591059
auto openedType = overload.openedType;
1060-
auto openedFullType = overload.openedFullType;
10611060

10621061
ValueDecl *member = choice.getDecl();
10631062

@@ -1093,13 +1092,15 @@ namespace {
10931092
return result;
10941093
}
10951094

1095+
auto refTy = simplifyType(overload.openedFullType);
1096+
10961097
// If we're referring to the member of a module, it's just a simple
10971098
// reference.
10981099
if (baseTy->is<ModuleType>()) {
10991100
assert(semantics == AccessSemantics::Ordinary &&
11001101
"Direct property access doesn't make sense for this");
11011102
auto ref = new (context) DeclRefExpr(memberRef, memberLoc, Implicit);
1102-
cs.setType(ref, simplifyType(openedFullType));
1103+
cs.setType(ref, refTy);
11031104
ref->setFunctionRefKind(choice.getFunctionRefKind());
11041105
auto *DSBI = cs.cacheType(new (context) DotSyntaxBaseIgnoredExpr(
11051106
base, dotLoc, ref, cs.getType(ref)));
@@ -1110,8 +1111,6 @@ namespace {
11101111
(!baseIsInstance && member->isInstanceMember());
11111112
bool isPartialApplication = shouldBuildCurryThunk(choice, baseIsInstance);
11121113

1113-
auto refTy = simplifyType(openedFullType);
1114-
11151114
// The formal type of the 'self' value for the member's declaration.
11161115
Type containerTy = getBaseType(refTy->castTo<FunctionType>());
11171116

@@ -1274,8 +1273,8 @@ namespace {
12741273
= new (context) MemberRefExpr(base, dotLoc, memberRef,
12751274
memberLoc, Implicit, semantics);
12761275
memberRefExpr->setIsSuper(isSuper);
1276+
cs.setType(memberRefExpr, refTy->castTo<FunctionType>()->getResult());
12771277

1278-
cs.setType(memberRefExpr, simplifyType(openedType));
12791278
Expr *result = memberRefExpr;
12801279
closeExistential(result, locator);
12811280

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2092,7 +2092,7 @@ static Type getRequirementTypeForDisplay(ModuleDecl *module,
20922092
return FunctionType::get(params, result, fnTy->getExtInfo());
20932093
}
20942094

2095-
return substType(type, /*result*/false);
2095+
return substType(type, /*result*/ true);
20962096
}
20972097

20982098
diag::RequirementKind
@@ -3361,41 +3361,36 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement,
33613361
emitDeclaredHereIfNeeded(diags, diagLoc, witness);
33623362
});
33633363
} else if (selfKind.result) {
3364-
// The reference to Self occurs in the result type. A non-final class
3365-
// can satisfy this requirement with a method that returns Self.
3364+
// The reference to Self occurs in the result type of a method/subscript
3365+
// or the type of a property. A non-final class can satisfy this requirement
3366+
// by holding onto Self accordingly.
3367+
if (witness->getDeclContext()->getSelfClassDecl()) {
3368+
const bool hasDynamicSelfResult = [&] {
3369+
if (auto func = dyn_cast<AbstractFunctionDecl>(witness)) {
3370+
return func->hasDynamicSelfResult();
3371+
} else if (auto var = dyn_cast<VarDecl>(witness)) {
3372+
return var->getInterfaceType()->hasDynamicSelfType();
3373+
}
33663374

3367-
// If the function has a dynamic Self, it's okay.
3368-
if (auto func = dyn_cast<FuncDecl>(witness)) {
3369-
if (func->getDeclContext()->getSelfClassDecl() &&
3370-
!func->hasDynamicSelfResult()) {
3375+
return cast<SubscriptDecl>(witness)
3376+
->getElementInterfaceType()
3377+
->hasDynamicSelfType();
3378+
}();
3379+
3380+
if (!hasDynamicSelfResult) {
33713381
diagnoseOrDefer(requirement, false,
33723382
[witness, requirement](NormalProtocolConformance *conformance) {
33733383
auto proto = conformance->getProtocol();
33743384
auto &diags = proto->getASTContext().Diags;
33753385
SourceLoc diagLoc = getLocForDiagnosingWitness(conformance,witness);
3376-
diags.diagnose(diagLoc,
3377-
diag::witness_requires_dynamic_self,
3386+
diags.diagnose(diagLoc, diag::witness_requires_dynamic_self,
3387+
getProtocolRequirementKind(requirement),
33783388
requirement->getName(),
33793389
conformance->getType(),
33803390
proto->getDeclaredInterfaceType());
33813391
emitDeclaredHereIfNeeded(diags, diagLoc, witness);
33823392
});
33833393
}
3384-
3385-
// Constructors conceptually also have a dynamic Self
3386-
// return type, so they're okay.
3387-
} else if (!isa<ConstructorDecl>(witness)) {
3388-
diagnoseOrDefer(requirement, false,
3389-
[witness, requirement](NormalProtocolConformance *conformance) {
3390-
auto proto = conformance->getProtocol();
3391-
auto &diags = proto->getASTContext().Diags;
3392-
SourceLoc diagLoc = getLocForDiagnosingWitness(conformance, witness);
3393-
diags.diagnose(diagLoc, diag::witness_self_non_subtype,
3394-
proto->getDeclaredInterfaceType(),
3395-
requirement->getName(),
3396-
conformance->getType());
3397-
emitDeclaredHereIfNeeded(diags, diagLoc, witness);
3398-
});
33993394
}
34003395
} else if (selfKind.requirement) {
34013396
if (auto targetPair = getAdopteeSelfSameTypeConstraint(classDecl,
@@ -3431,8 +3426,8 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement,
34313426
// constraint that either the requirement not produce 'Self' in a
34323427
// covariant position, or the type of the requirement does not involve
34333428
// associated types.
3434-
if (auto func = dyn_cast<FuncDecl>(witness)) {
3435-
if (func->getDeclContext()->getExtendedProtocolDecl()) {
3429+
if (isa<FuncDecl>(witness) || isa<SubscriptDecl>(witness)) {
3430+
if (witness->getDeclContext()->getExtendedProtocolDecl()) {
34363431
auto selfKindWithAssocTypes = Proto->findProtocolSelfReferences(
34373432
requirement,
34383433
/*allowCovariantParameters=*/false,
@@ -3445,6 +3440,7 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement,
34453440
auto &diags = proto->getASTContext().Diags;
34463441
diags.diagnose(conformance->getLoc(),
34473442
diag::witness_requires_class_implementation,
3443+
getProtocolRequirementKind(requirement),
34483444
requirement->getName(),
34493445
conformance->getType());
34503446
diags.diagnose(witness, diag::decl_declared_here,

0 commit comments

Comments
 (0)