Skip to content

Commit 1ff3fda

Browse files
authored
Merge pull request #41757 from slavapestov/protocol-self-reference-fixes
Sema: Fix a couple of issues related to variance of protocol 'Self'
2 parents 4343d94 + 599bb79 commit 1ff3fda

File tree

7 files changed

+151
-19
lines changed

7 files changed

+151
-19
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1935,8 +1935,8 @@ WARNING(type_does_not_conform_swiftui_warning,none,
19351935

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

19411941
ERROR(cannot_use_nil_with_this_type,none,
19421942
"'nil' cannot be used in context expecting type %0", (Type))

lib/Sema/ConstraintSystem.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6023,7 +6023,7 @@ void ConstraintSystem::maybeProduceFallbackDiagnostic(
60236023
/// Because opened archetypes are not part of the surface language, these
60246024
/// constraints render the member inaccessible.
60256025
static bool doesMemberHaveUnfulfillableConstraintsWithExistentialBase(
6026-
Type baseTy, const ValueDecl *member, const DeclContext *useDC) {
6026+
Type baseTy, const ValueDecl *member) {
60276027
const auto sig =
60286028
member->getInnermostDeclContext()->getGenericSignatureOfContext();
60296029

@@ -6060,7 +6060,7 @@ static bool doesMemberHaveUnfulfillableConstraintsWithExistentialBase(
60606060
return Action::Stop;
60616061
}
60626062
} isDependentOnSelfWalker(member->getASTContext().getOpenedArchetypeSignature(
6063-
baseTy, useDC->getGenericSignatureOfContext()));
6063+
baseTy, GenericSignature()));
60646064

60656065
for (const auto &req : sig.getRequirements()) {
60666066
switch (req.getKind()) {
@@ -6121,8 +6121,7 @@ bool ConstraintSystem::isMemberAvailableOnExistential(
61216121
}
61226122

61236123
if (doesMemberHaveUnfulfillableConstraintsWithExistentialBase(baseTy,
6124-
member,
6125-
DC)) {
6124+
member)) {
61266125
return false;
61276126
}
61286127

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 98 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4840,6 +4840,102 @@ ResolveWitnessResult ConformanceChecker::resolveTypeWitnessViaLookup(
48404840
return ResolveWitnessResult::ExplicitFailed;
48414841
}
48424842

4843+
/// FIXME: It feels like this could be part of findExistentialSelfReferences().
4844+
static Optional<Requirement>
4845+
hasInvariantSelfRequirement(const ProtocolDecl *proto,
4846+
ArrayRef<Requirement> reqSig) {
4847+
auto selfTy = proto->getSelfInterfaceType();
4848+
4849+
auto containsInvariantSelf = [&](Type t) -> bool {
4850+
struct Walker : public TypeWalker {
4851+
Type SelfTy;
4852+
bool Found = false;
4853+
4854+
Walker(Type selfTy) : SelfTy(selfTy) {}
4855+
4856+
Action walkToTypePre(Type ty) override {
4857+
// Check for 'Self'.
4858+
if (ty->isEqual(SelfTy)) {
4859+
Found = true;
4860+
return Action::Stop;
4861+
}
4862+
4863+
// 'Self.A' is OK.
4864+
if (ty->is<DependentMemberType>())
4865+
return Action::SkipChildren;
4866+
4867+
return Action::Continue;
4868+
}
4869+
};
4870+
4871+
Walker walker(selfTy);
4872+
t.walk(walker);
4873+
4874+
return walker.Found;
4875+
};
4876+
4877+
for (auto req : reqSig) {
4878+
switch (req.getKind()) {
4879+
case RequirementKind::SameType:
4880+
if (req.getSecondType()->isTypeParameter()) {
4881+
if (req.getFirstType()->isEqual(selfTy))
4882+
return req;
4883+
} else {
4884+
if (containsInvariantSelf(req.getSecondType()))
4885+
return req;
4886+
}
4887+
continue;
4888+
case RequirementKind::Superclass:
4889+
if (containsInvariantSelf(req.getSecondType()))
4890+
return req;
4891+
continue;
4892+
case RequirementKind::Conformance:
4893+
case RequirementKind::Layout:
4894+
continue;
4895+
}
4896+
4897+
llvm_unreachable("Bad requirement kind");
4898+
}
4899+
4900+
return None;
4901+
}
4902+
4903+
static void diagnoseInvariantSelfRequirement(
4904+
SourceLoc loc, Type adoptee, const ProtocolDecl *proto,
4905+
Requirement req, DiagnosticEngine &diags) {
4906+
Type firstType, secondType;
4907+
unsigned kind = 0;
4908+
4909+
switch (req.getKind()) {
4910+
case RequirementKind::SameType:
4911+
if (req.getSecondType()->isTypeParameter()) {
4912+
// eg, 'Self == Self.A.B'
4913+
firstType = req.getSecondType();
4914+
secondType = req.getFirstType();
4915+
} else {
4916+
// eg, 'Self.A.B == G<Self>'
4917+
firstType = req.getFirstType();
4918+
secondType = req.getSecondType();
4919+
}
4920+
kind = 0;
4921+
break;
4922+
case RequirementKind::Superclass:
4923+
// eg, 'Self.A.B : G<Self>'
4924+
firstType = req.getFirstType();
4925+
secondType = req.getSecondType();
4926+
kind = 1;
4927+
break;
4928+
case RequirementKind::Conformance:
4929+
case RequirementKind::Layout:
4930+
llvm_unreachable("Invalid requirement kind");
4931+
}
4932+
4933+
diags.diagnose(loc, diag::non_final_class_cannot_conform_to_self_same_type,
4934+
adoptee, proto->getDeclaredInterfaceType(),
4935+
firstType, kind, secondType)
4936+
.warnUntilSwiftVersion(6);
4937+
}
4938+
48434939
void ConformanceChecker::ensureRequirementsAreSatisfied() {
48444940
Conformance->finishSignatureConformances();
48454941
auto proto = Conformance->getProtocol();
@@ -4867,15 +4963,8 @@ void ConformanceChecker::ensureRequirementsAreSatisfied() {
48674963
// instead of the concrete class type itself.
48684964
if (auto *classDecl = Adoptee->getClassOrBoundGenericClass()) {
48694965
if (!classDecl->isSemanticallyFinal()) {
4870-
for (auto req : reqSig) {
4871-
if (req.getKind() == RequirementKind::SameType &&
4872-
req.getFirstType()->isEqual(proto->getSelfInterfaceType())) {
4873-
diags.diagnose(Loc, diag::non_final_class_cannot_conform_to_self_same_type,
4874-
Adoptee, proto->getDeclaredInterfaceType(),
4875-
req.getSecondType())
4876-
.warnUntilSwiftVersion(6);
4877-
break;
4878-
}
4966+
if (auto req = hasInvariantSelfRequirement(proto, reqSig)) {
4967+
diagnoseInvariantSelfRequirement(Loc, Adoptee, proto, *req, diags);
48794968
}
48804969
}
48814970
}

test/Generics/non_final_class_conforms_same_type_requirement_on_self.swift

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public protocol Q {
1414
// exactly equal C; since C is not final, this means the conformance
1515
// is not covariant.
1616
public class C : P {
17-
// 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}}
17+
// 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}}
1818
public typealias A = D
1919
}
2020

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

4646
// CHECK-LABEL: Generic signature: <U where U : C>
4747
public func takesBoth2<U>(_: U) where U : C, U : P {}
48+
49+
// 'Self' can also occur inside of a concrete type or superclass requirement.
50+
public class G<T> {}
51+
52+
public protocol ConcreteExampleP {
53+
associatedtype A : Q where A.B == G<Self>
54+
}
55+
56+
public class ConcreteExampleC : ConcreteExampleP {
57+
// 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}}
58+
public typealias A = ConcreteExampleD
59+
}
60+
61+
public class ConcreteExampleD : Q {
62+
public typealias B = G<ConcreteExampleC>
63+
}
64+
65+
public protocol SuperclassExampleP {
66+
associatedtype A : Q where A.B : G<Self>
67+
}
68+
69+
public class SuperclassExampleC : SuperclassExampleP {
70+
// 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}}
71+
public typealias A = SuperclassExampleD
72+
}
73+
74+
public class SuperclassExampleD : Q {
75+
public typealias B = G<SuperclassExampleC>
76+
}

test/Generics/rdar80503090.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ extension P where T : Q {
2020
}
2121

2222
class C : P {}
23-
// 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}}
23+
// 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}}
2424

2525
extension P where T : C {
2626
// CHECK-LABEL: Generic signature: <Self where Self : C>

test/decl/protocol/existential_member_accesses_self_assoctype.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,8 @@ extension UnfulfillableGenericRequirements {
560560
A: Sequence, A.Element: Sequence,
561561
U.A == A.Element.Element {}
562562
func method7<U>(_: U) where U: UnfulfillableGenericRequirements & Class<Self> {}
563+
564+
func method8<U>(_: U) where U == Self.A {}
563565
}
564566
do {
565567
let exist: any UnfulfillableGenericRequirements
@@ -579,6 +581,19 @@ do {
579581
exist.method7(false)
580582
// expected-error@-1 {{instance method 'method7' requires that 'U' conform to 'UnfulfillableGenericRequirements'}}
581583
// expected-error@-2 {{member 'method7' cannot be used on value of type 'any UnfulfillableGenericRequirements'; consider using a generic constraint instead}}
584+
585+
exist.method8(false)
586+
// expected-error@-1 {{member 'method8' cannot be used on value of type 'any UnfulfillableGenericRequirements'; consider using a generic constraint instead}}
587+
}
588+
589+
// Make sure this also works in a generic context!
590+
struct G<X, Y, Z> {
591+
func doIt() {
592+
let exist: any UnfulfillableGenericRequirements
593+
594+
exist.method8(false)
595+
// expected-error@-1 {{member 'method8' cannot be used on value of type 'any UnfulfillableGenericRequirements'; consider using a generic constraint instead}}
596+
}
582597
}
583598
protocol UnfulfillableGenericRequirementsDerived1: UnfulfillableGenericRequirements where A == Bool {}
584599
protocol UnfulfillableGenericRequirementsDerived2: UnfulfillableGenericRequirements where A == Class<Self> {}

validation-test/compiler_crashers_2_fixed/0189-sr10033.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ extension P2 {
1515
}
1616

1717
class C1 : P1 {
18-
// 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}}
18+
// 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}}
1919
class A2 : P2 {
20-
// 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}}
20+
// 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}}
2121
typealias A1 = C1
2222
}
2323
}

0 commit comments

Comments
 (0)