Skip to content

Sema: Fix a couple of issues related to variance of protocol 'Self' #41757

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 2 commits into from
Mar 10, 2022
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
4 changes: 2 additions & 2 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -1935,8 +1935,8 @@ WARNING(type_does_not_conform_swiftui_warning,none,

ERROR(non_final_class_cannot_conform_to_self_same_type,none,
"non-final class %0 cannot safely conform to protocol %1, "
"which requires that 'Self' is exactly equal to %2",
(Type, Type, Type))
"which requires that %2 %select{is exactly equal to|inherit from}3 %4",
(Type, Type, Type, unsigned, Type))

ERROR(cannot_use_nil_with_this_type,none,
"'nil' cannot be used in context expecting type %0", (Type))
Expand Down
7 changes: 3 additions & 4 deletions lib/Sema/ConstraintSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6023,7 +6023,7 @@ void ConstraintSystem::maybeProduceFallbackDiagnostic(
/// Because opened archetypes are not part of the surface language, these
/// constraints render the member inaccessible.
static bool doesMemberHaveUnfulfillableConstraintsWithExistentialBase(
Type baseTy, const ValueDecl *member, const DeclContext *useDC) {
Type baseTy, const ValueDecl *member) {
const auto sig =
member->getInnermostDeclContext()->getGenericSignatureOfContext();

Expand Down Expand Up @@ -6060,7 +6060,7 @@ static bool doesMemberHaveUnfulfillableConstraintsWithExistentialBase(
return Action::Stop;
}
} isDependentOnSelfWalker(member->getASTContext().getOpenedArchetypeSignature(
baseTy, useDC->getGenericSignatureOfContext()));
baseTy, GenericSignature()));

for (const auto &req : sig.getRequirements()) {
switch (req.getKind()) {
Expand Down Expand Up @@ -6121,8 +6121,7 @@ bool ConstraintSystem::isMemberAvailableOnExistential(
}

if (doesMemberHaveUnfulfillableConstraintsWithExistentialBase(baseTy,
member,
DC)) {
member)) {
return false;
}

Expand Down
107 changes: 98 additions & 9 deletions lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4840,6 +4840,102 @@ ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup(
return ResolveWitnessResult::ExplicitFailed;
}

/// FIXME: It feels like this could be part of findExistentialSelfReferences().
static Optional<Requirement>
hasInvariantSelfRequirement(const ProtocolDecl *proto,
ArrayRef<Requirement> reqSig) {
auto selfTy = proto->getSelfInterfaceType();

auto containsInvariantSelf = [&](Type t) -> bool {
struct Walker : public TypeWalker {
Type SelfTy;
bool Found = false;

Walker(Type selfTy) : SelfTy(selfTy) {}

Action walkToTypePre(Type ty) override {
// Check for 'Self'.
if (ty->isEqual(SelfTy)) {
Found = true;
return Action::Stop;
}

// 'Self.A' is OK.
if (ty->is<DependentMemberType>())
return Action::SkipChildren;

return Action::Continue;
}
};

Walker walker(selfTy);
t.walk(walker);

return walker.Found;
};

for (auto req : reqSig) {
switch (req.getKind()) {
case RequirementKind::SameType:
if (req.getSecondType()->isTypeParameter()) {
if (req.getFirstType()->isEqual(selfTy))
return req;
} else {
if (containsInvariantSelf(req.getSecondType()))
return req;
}
continue;
case RequirementKind::Superclass:
if (containsInvariantSelf(req.getSecondType()))
return req;
continue;
case RequirementKind::Conformance:
case RequirementKind::Layout:
continue;
}

llvm_unreachable("Bad requirement kind");
}

return None;
}

static void diagnoseInvariantSelfRequirement(
SourceLoc loc, Type adoptee, const ProtocolDecl *proto,
Requirement req, DiagnosticEngine &diags) {
Type firstType, secondType;
unsigned kind = 0;

switch (req.getKind()) {
case RequirementKind::SameType:
if (req.getSecondType()->isTypeParameter()) {
// eg, 'Self == Self.A.B'
firstType = req.getSecondType();
secondType = req.getFirstType();
} else {
// eg, 'Self.A.B == G<Self>'
firstType = req.getFirstType();
secondType = req.getSecondType();
}
kind = 0;
break;
case RequirementKind::Superclass:
// eg, 'Self.A.B : G<Self>'
firstType = req.getFirstType();
secondType = req.getSecondType();
kind = 1;
break;
case RequirementKind::Conformance:
case RequirementKind::Layout:
llvm_unreachable("Invalid requirement kind");
}

diags.diagnose(loc, diag::non_final_class_cannot_conform_to_self_same_type,
adoptee, proto->getDeclaredInterfaceType(),
firstType, kind, secondType)
.warnUntilSwiftVersion(6);
}

void ConformanceChecker::ensureRequirementsAreSatisfied() {
Conformance->finishSignatureConformances();
auto proto = Conformance->getProtocol();
Expand Down Expand Up @@ -4867,15 +4963,8 @@ void ConformanceChecker::ensureRequirementsAreSatisfied() {
// instead of the concrete class type itself.
if (auto *classDecl = Adoptee->getClassOrBoundGenericClass()) {
if (!classDecl->isSemanticallyFinal()) {
for (auto req : reqSig) {
if (req.getKind() == RequirementKind::SameType &&
req.getFirstType()->isEqual(proto->getSelfInterfaceType())) {
diags.diagnose(Loc, diag::non_final_class_cannot_conform_to_self_same_type,
Adoptee, proto->getDeclaredInterfaceType(),
req.getSecondType())
.warnUntilSwiftVersion(6);
break;
}
if (auto req = hasInvariantSelfRequirement(proto, reqSig)) {
diagnoseInvariantSelfRequirement(Loc, Adoptee, proto, *req, diags);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public protocol Q {
// exactly equal C; since C is not final, this means the conformance
// is not covariant.
public class C : P {
// expected-warning@-1 {{non-final class 'C' cannot safely conform to protocol 'P', which requires that 'Self' is exactly equal to 'Self.A.B'; this is an error in Swift 6}}
// expected-warning@-1 {{non-final class 'C' cannot safely conform to protocol 'P', which requires that 'Self.A.B' is exactly equal to 'Self'; this is an error in Swift 6}}
public typealias A = D
}

Expand Down Expand Up @@ -45,3 +45,32 @@ public func takesBoth1<T>(_: T) where T : P, T : C {}

// CHECK-LABEL: Generic signature: <U where U : C>
public func takesBoth2<U>(_: U) where U : C, U : P {}

// 'Self' can also occur inside of a concrete type or superclass requirement.
public class G<T> {}

public protocol ConcreteExampleP {
associatedtype A : Q where A.B == G<Self>
}

public class ConcreteExampleC : ConcreteExampleP {
// expected-warning@-1 {{non-final class 'ConcreteExampleC' cannot safely conform to protocol 'ConcreteExampleP', which requires that 'Self.A.B' is exactly equal to 'G<Self>'; this is an error in Swift 6}}
public typealias A = ConcreteExampleD
}

public class ConcreteExampleD : Q {
public typealias B = G<ConcreteExampleC>
}

public protocol SuperclassExampleP {
associatedtype A : Q where A.B : G<Self>
}

public class SuperclassExampleC : SuperclassExampleP {
// expected-warning@-1 {{non-final class 'SuperclassExampleC' cannot safely conform to protocol 'SuperclassExampleP', which requires that 'Self.A.B' inherit from 'G<Self>'; this is an error in Swift 6}}
public typealias A = SuperclassExampleD
}

public class SuperclassExampleD : Q {
public typealias B = G<SuperclassExampleC>
}
2 changes: 1 addition & 1 deletion test/Generics/rdar80503090.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ extension P where T : Q {
}

class C : P {}
// expected-warning@-1 {{non-final class 'C' cannot safely conform to protocol 'P', which requires that 'Self' is exactly equal to 'Self.T'; this is an error in Swift 6}}
// expected-warning@-1 {{non-final class 'C' cannot safely conform to protocol 'P', which requires that 'Self.T' is exactly equal to 'Self'; this is an error in Swift 6}}

extension P where T : C {
// CHECK-LABEL: Generic signature: <Self where Self : C>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,8 @@ extension UnfulfillableGenericRequirements {
A: Sequence, A.Element: Sequence,
U.A == A.Element.Element {}
func method7<U>(_: U) where U: UnfulfillableGenericRequirements & Class<Self> {}

func method8<U>(_: U) where U == Self.A {}
}
do {
let exist: any UnfulfillableGenericRequirements
Expand All @@ -579,6 +581,19 @@ do {
exist.method7(false)
// expected-error@-1 {{instance method 'method7' requires that 'U' conform to 'UnfulfillableGenericRequirements'}}
// expected-error@-2 {{member 'method7' cannot be used on value of type 'any UnfulfillableGenericRequirements'; consider using a generic constraint instead}}

exist.method8(false)
// expected-error@-1 {{member 'method8' cannot be used on value of type 'any UnfulfillableGenericRequirements'; consider using a generic constraint instead}}
}

// Make sure this also works in a generic context!
struct G<X, Y, Z> {
func doIt() {
let exist: any UnfulfillableGenericRequirements

exist.method8(false)
// expected-error@-1 {{member 'method8' cannot be used on value of type 'any UnfulfillableGenericRequirements'; consider using a generic constraint instead}}
}
Comment on lines +589 to +596
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, if only these declarations could just be anonymous.

}
protocol UnfulfillableGenericRequirementsDerived1: UnfulfillableGenericRequirements where A == Bool {}
protocol UnfulfillableGenericRequirementsDerived2: UnfulfillableGenericRequirements where A == Class<Self> {}
Expand Down
4 changes: 2 additions & 2 deletions validation-test/compiler_crashers_2_fixed/0189-sr10033.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ extension P2 {
}

class C1 : P1 {
// expected-warning@-1 {{non-final class 'C1' cannot safely conform to protocol 'P1', which requires that 'Self' is exactly equal to 'Self.A2.A1'; this is an error in Swift 6}}
// expected-warning@-1 {{non-final class 'C1' cannot safely conform to protocol 'P1', which requires that 'Self.A2.A1' is exactly equal to 'Self'; this is an error in Swift 6}}
class A2 : P2 {
// expected-warning@-1 {{non-final class 'C1.A2' cannot safely conform to protocol 'P2', which requires that 'Self' is exactly equal to 'Self.A1.A2'; this is an error in Swift 6}}
// expected-warning@-1 {{non-final class 'C1.A2' cannot safely conform to protocol 'P2', which requires that 'Self.A1.A2' is exactly equal to 'Self'; this is an error in Swift 6}}
typealias A1 = C1
}
}