Skip to content

Sema: Allow non-final classes to satisfy properties and subscripts with covariant Self #34005

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions include/swift/AST/Decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -4123,12 +4123,14 @@ struct SelfReferenceKind {
return SelfReferenceKind(false, false, false, false);
}

/// The type refers to 'Self', but only as the result type of a method.
/// The type refers to 'Self', but only as the type of a property or
/// the result type of a method/subscript.
static SelfReferenceKind Result() {
return SelfReferenceKind(true, false, false, false);
}

/// The type refers to 'Self', but only as the parameter type of a method.
/// The type refers to 'Self', but only as the parameter type
/// of a method/subscript.
static SelfReferenceKind Parameter() {
return SelfReferenceKind(false, true, false, false);
}
Expand Down
15 changes: 8 additions & 7 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1992,14 +1992,15 @@ NOTE(witness_self_weaken_same_type,none,
"consider weakening the same-type requirement %0 == %1 to a superclass "
"requirement", (Type, Type))
ERROR(witness_requires_dynamic_self,none,
"method %0 in non-final class %1 must return 'Self' to conform to "
"protocol %2",
(DeclName, Type, Type))
"%select{%error|method|property|subscript}0 %1 in non-final class %2 "
"must %select{%error|return|specify type|return}0 'Self' "
"to conform to protocol %3",
(RequirementKind, DeclName, Type, Type))
ERROR(witness_requires_class_implementation,none,
"method %0 in non-final class %1 cannot be implemented in a "
"protocol extension because it returns 'Self' and has associated type "
"requirements",
(DeclName, Type))
"%select{%error|method|%error|subscript}0 %1 in non-final class %2 "
"cannot be implemented in a protocol extension because it returns 'Self' "
"and has associated type requirements",
(RequirementKind, DeclName, Type))
ERROR(witness_not_accessible_proto,none,
"%select{initializer %1|method %1|%select{|setter for }2property %1"
"|subscript%select{| setter}2}0 must be declared "
Expand Down
11 changes: 6 additions & 5 deletions lib/AST/Decl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4965,12 +4965,13 @@ ProtocolDecl::findProtocolSelfReferences(const ValueDecl *value,
return ::findProtocolSelfReferences(this, type,
skipAssocTypes);
} else {
if (::findProtocolSelfReferences(this, type,
skipAssocTypes)) {
return SelfReferenceKind::Other();
}
return SelfReferenceKind::None();
assert(isa<VarDecl>(value));

return ::findProtocolSelfReferences(this, type,
skipAssocTypes);
}

return SelfReferenceKind::None();
}

bool ProtocolDecl::isAvailableInExistential(const ValueDecl *decl) const {
Expand Down
9 changes: 4 additions & 5 deletions lib/Sema/CSApply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1061,7 +1061,6 @@ namespace {
AccessSemantics semantics) {
auto choice = overload.choice;
auto openedType = overload.openedType;
auto openedFullType = overload.openedFullType;

ValueDecl *member = choice.getDecl();

Expand Down Expand Up @@ -1097,13 +1096,15 @@ namespace {
return result;
}

auto refTy = simplifyType(overload.openedFullType);

// If we're referring to the member of a module, it's just a simple
// reference.
if (baseTy->is<ModuleType>()) {
assert(semantics == AccessSemantics::Ordinary &&
"Direct property access doesn't make sense for this");
auto ref = new (context) DeclRefExpr(memberRef, memberLoc, Implicit);
cs.setType(ref, simplifyType(openedFullType));
cs.setType(ref, refTy);
ref->setFunctionRefKind(choice.getFunctionRefKind());
auto *DSBI = cs.cacheType(new (context) DotSyntaxBaseIgnoredExpr(
base, dotLoc, ref, cs.getType(ref)));
Expand All @@ -1114,8 +1115,6 @@ namespace {
(!baseIsInstance && member->isInstanceMember());
bool isPartialApplication = shouldBuildCurryThunk(choice, baseIsInstance);

auto refTy = simplifyType(openedFullType);

// The formal type of the 'self' value for the member's declaration.
Type containerTy = getBaseType(refTy->castTo<FunctionType>());

Expand Down Expand Up @@ -1278,8 +1277,8 @@ namespace {
= new (context) MemberRefExpr(base, dotLoc, memberRef,
memberLoc, Implicit, semantics);
memberRefExpr->setIsSuper(isSuper);
cs.setType(memberRefExpr, refTy->castTo<FunctionType>()->getResult());

cs.setType(memberRefExpr, simplifyType(openedType));
Expr *result = memberRefExpr;
closeExistential(result, locator);

Expand Down
48 changes: 22 additions & 26 deletions lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2078,7 +2078,7 @@ static Type getRequirementTypeForDisplay(ModuleDecl *module,
return FunctionType::get(params, result, fnTy->getExtInfo());
}

return substType(type, /*result*/false);
return substType(type, /*result*/ true);
}

diag::RequirementKind
Expand Down Expand Up @@ -3343,41 +3343,36 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement,
emitDeclaredHereIfNeeded(diags, diagLoc, witness);
});
} else if (selfKind.result) {
// The reference to Self occurs in the result type. A non-final class
// can satisfy this requirement with a method that returns Self.
// The reference to Self occurs in the result type of a method/subscript
// or the type of a property. A non-final class can satisfy this requirement
// by holding onto Self accordingly.
if (witness->getDeclContext()->getSelfClassDecl()) {
const bool hasDynamicSelfResult = [&] {
if (auto func = dyn_cast<AbstractFunctionDecl>(witness)) {
return func->hasDynamicSelfResult();
} else if (auto var = dyn_cast<VarDecl>(witness)) {
return var->getInterfaceType()->hasDynamicSelfType();
}

// If the function has a dynamic Self, it's okay.
if (auto func = dyn_cast<FuncDecl>(witness)) {
if (func->getDeclContext()->getSelfClassDecl() &&
!func->hasDynamicSelfResult()) {
return cast<SubscriptDecl>(witness)
->getElementInterfaceType()
->hasDynamicSelfType();
}();

if (!hasDynamicSelfResult) {
diagnoseOrDefer(requirement, false,
[witness, requirement](NormalProtocolConformance *conformance) {
auto proto = conformance->getProtocol();
auto &diags = proto->getASTContext().Diags;
SourceLoc diagLoc = getLocForDiagnosingWitness(conformance,witness);
diags.diagnose(diagLoc,
diag::witness_requires_dynamic_self,
diags.diagnose(diagLoc, diag::witness_requires_dynamic_self,
getProtocolRequirementKind(requirement),
requirement->getName(),
conformance->getType(),
proto->getDeclaredInterfaceType());
emitDeclaredHereIfNeeded(diags, diagLoc, witness);
});
}

// Constructors conceptually also have a dynamic Self
// return type, so they're okay.
} else if (!isa<ConstructorDecl>(witness)) {
diagnoseOrDefer(requirement, false,
[witness, requirement](NormalProtocolConformance *conformance) {
auto proto = conformance->getProtocol();
auto &diags = proto->getASTContext().Diags;
SourceLoc diagLoc = getLocForDiagnosingWitness(conformance, witness);
diags.diagnose(diagLoc, diag::witness_self_non_subtype,
proto->getDeclaredInterfaceType(),
requirement->getName(),
conformance->getType());
emitDeclaredHereIfNeeded(diags, diagLoc, witness);
});
}
} else if (selfKind.requirement) {
if (auto targetPair = getAdopteeSelfSameTypeConstraint(classDecl,
Expand Down Expand Up @@ -3413,8 +3408,8 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement,
// constraint that either the requirement not produce 'Self' in a
// covariant position, or the type of the requirement does not involve
// associated types.
if (auto func = dyn_cast<FuncDecl>(witness)) {
if (func->getDeclContext()->getExtendedProtocolDecl()) {
if (isa<FuncDecl>(witness) || isa<SubscriptDecl>(witness)) {
if (witness->getDeclContext()->getExtendedProtocolDecl()) {
auto selfKindWithAssocTypes = Proto->findProtocolSelfReferences(
requirement,
/*allowCovariantParameters=*/false,
Expand All @@ -3427,6 +3422,7 @@ void ConformanceChecker::checkNonFinalClassWitness(ValueDecl *requirement,
auto &diags = proto->getASTContext().Diags;
diags.diagnose(conformance->getLoc(),
diag::witness_requires_class_implementation,
getProtocolRequirementKind(requirement),
requirement->getName(),
conformance->getType());
diags.diagnose(witness, diag::decl_declared_here,
Expand Down
Loading