Skip to content

Commit a6df5ac

Browse files
authored
Merge pull request #37466 from slavapestov/ban-implicit-protocol-refinement
GSB: When looking at protocol refinements, only consider other sources on 'Self'
2 parents 4d9971e + 3e1e9fa commit a6df5ac

File tree

7 files changed

+164
-0
lines changed

7 files changed

+164
-0
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2424,6 +2424,10 @@ NOTE(redundant_conformance_here,none,
24242424
"conformance constraint %0 : %1 implied here",
24252425
(Type, ProtocolDecl *))
24262426

2427+
WARNING(missing_protocol_refinement, none,
2428+
"protocol %0 should be declared to refine %1 due to a same-type constraint on 'Self'",
2429+
(ProtocolDecl *, ProtocolDecl *))
2430+
24272431
ERROR(unsupported_recursive_requirements, none,
24282432
"requirement involves recursion that is not currently supported", ())
24292433

include/swift/AST/GenericSignatureBuilder.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,9 @@ class GenericSignatureBuilder {
689689
void computeRedundantRequirements(
690690
const ProtocolDecl *requirementSignatureSelfProto);
691691

692+
void diagnoseProtocolRefinement(
693+
const ProtocolDecl *requirementSignatureSelfProto);
694+
692695
void diagnoseRedundantRequirements() const;
693696

694697
void diagnoseConflictingConcreteTypeRequirements(

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6067,6 +6067,18 @@ void GenericSignatureBuilder::checkIfRequirementCanBeDerived(
60676067
}
60686068
}
60696069

6070+
static bool involvesNonSelfSubjectTypes(const RequirementSource *source) {
6071+
while (source->kind != RequirementSource::RequirementSignatureSelf) {
6072+
if (source->isProtocolRequirement() &&
6073+
!source->getStoredType()->is<GenericTypeParamType>())
6074+
return true;
6075+
6076+
source = source->parent;
6077+
}
6078+
6079+
return false;
6080+
}
6081+
60706082
void GenericSignatureBuilder::computeRedundantRequirements(
60716083
const ProtocolDecl *requirementSignatureSelfProto) {
60726084
assert(!Impl->computedRedundantRequirements &&
@@ -6126,10 +6138,27 @@ void GenericSignatureBuilder::computeRedundantRequirements(
61266138
auto found = equivClass->conformsTo.find(proto);
61276139
assert(found != equivClass->conformsTo.end());
61286140

6141+
// If this is a conformance requirement on 'Self', only consider it
6142+
// redundant if it can be derived from other sources involving 'Self'.
6143+
//
6144+
// What this means in practice is that we will never try to build
6145+
// a witness table for an inherited conformance from an associated
6146+
// conformance.
6147+
//
6148+
// This is important since witness tables for inherited conformances
6149+
// are realized eagerly before the witness table for the original
6150+
// conformance, and allowing more complex requirement sources for
6151+
// inherited conformances could introduce cycles at runtime.
6152+
bool isProtocolRefinement =
6153+
(requirementSignatureSelfProto &&
6154+
equivClass->getAnchor(*this, { })->is<GenericTypeParamType>());
6155+
61296156
checkIfRequirementCanBeDerived(
61306157
req, found->second,
61316158
requirementSignatureSelfProto,
61326159
[&](const Constraint<ProtocolDecl *> &constraint) {
6160+
if (isProtocolRefinement)
6161+
return involvesNonSelfSubjectTypes(constraint.source);
61336162
return false;
61346163
});
61356164

@@ -6351,6 +6380,7 @@ GenericSignatureBuilder::finalize(TypeArrayView<GenericTypeParamType> genericPar
63516380
processDelayedRequirements();
63526381

63536382
computeRedundantRequirements(requirementSignatureSelfProto);
6383+
diagnoseProtocolRefinement(requirementSignatureSelfProto);
63546384
diagnoseRedundantRequirements();
63556385
diagnoseConflictingConcreteTypeRequirements(requirementSignatureSelfProto);
63566386

@@ -6928,6 +6958,50 @@ static bool isRedundantlyInheritableObjCProtocol(
69286958
return true;
69296959
}
69306960

6961+
/// Diagnose any conformance requirements on 'Self' that are not derived from
6962+
/// the protocol's inheritance clause.
6963+
void GenericSignatureBuilder::diagnoseProtocolRefinement(
6964+
const ProtocolDecl *requirementSignatureSelfProto) {
6965+
if (requirementSignatureSelfProto == nullptr)
6966+
return;
6967+
6968+
auto selfType = requirementSignatureSelfProto->getSelfInterfaceType();
6969+
auto *equivClass = resolveEquivalenceClass(
6970+
selfType, ArchetypeResolutionKind::AlreadyKnown);
6971+
assert(equivClass != nullptr);
6972+
6973+
for (auto pair : equivClass->conformsTo) {
6974+
auto *proto = pair.first;
6975+
bool found = false;
6976+
SourceLoc loc;
6977+
for (const auto &constraint : pair.second) {
6978+
if (!involvesNonSelfSubjectTypes(constraint.source)) {
6979+
found = true;
6980+
break;
6981+
}
6982+
6983+
auto *parent = constraint.source;
6984+
while (parent->kind != RequirementSource::RequirementSignatureSelf) {
6985+
loc = parent->getLoc();
6986+
if (loc)
6987+
break;
6988+
}
6989+
}
6990+
6991+
if (!found) {
6992+
requirementSignatureSelfProto->diagnose(
6993+
diag::missing_protocol_refinement,
6994+
const_cast<ProtocolDecl *>(requirementSignatureSelfProto),
6995+
proto);
6996+
6997+
if (loc.isValid()) {
6998+
Context.Diags.diagnose(loc, diag::redundant_conformance_here,
6999+
selfType, proto);
7000+
}
7001+
}
7002+
}
7003+
}
7004+
69317005
void GenericSignatureBuilder::diagnoseRedundantRequirements() const {
69327006
for (const auto &req : Impl->ExplicitRequirements) {
69337007
auto *source = req.getSource();
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: %target-typecheck-verify-swift
2+
// RUN: %target-swift-frontend -typecheck -debug-generic-signatures %s 2>&1 | %FileCheck %s
3+
4+
protocol Base {}
5+
protocol Middle : Base {}
6+
protocol Derived : Middle, Base {}
7+
// expected-note@-1 {{conformance constraint 'Self' : 'Base' implied here}}
8+
// expected-warning@-2 {{redundant conformance constraint 'Self' : 'Base'}}
9+
10+
protocol P1 {}
11+
12+
protocol P2 {
13+
associatedtype Assoc: P1
14+
}
15+
16+
// no warning here
17+
protocol Good: P2, P1 where Assoc == Self {}
18+
// CHECK-LABEL: Requirement signature: <Self where Self : P1, Self : P2, Self == Self.Assoc>
19+
20+
// missing refinement of 'P1'
21+
protocol Bad: P2 where Assoc == Self {}
22+
// expected-warning@-1 {{protocol 'Bad' should be declared to refine 'P1' due to a same-type constraint on 'Self'}}
23+
// expected-note@-2 {{conformance constraint 'Self' : 'P1' implied here}}
24+
// CHECK-LABEL: Requirement signature: <Self where Self : P2, Self == Self.Assoc>

test/Interpreter/sr10941.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-build-swift %s -o %t/a.out
3+
// RUN: %target-codesign %t/a.out
4+
// RUN: %target-run %t/a.out
5+
// REQUIRES: executable_test
6+
7+
// https://bugs.swift.org/browse/SR-10941
8+
9+
public protocol SupportedPropertyType { }
10+
11+
public protocol TypedSupportedType: SupportedPropertyType where FactoryType.BuildType == Self {
12+
associatedtype FactoryType: TypedSupportedTypeFactory
13+
}
14+
15+
public protocol TypedSupportedTypeFactory {
16+
associatedtype BuildType: SupportedPropertyType
17+
}
18+
19+
public class Factory<BuildType: TypedSupportedType>: TypedSupportedTypeFactory {
20+
}
21+
22+
public struct ContentMode : TypedSupportedType {
23+
public typealias FactoryType = Factory<ContentMode>
24+
}
25+
26+
func bar<T : SupportedPropertyType>(_: T.Type) {}
27+
28+
func baz<T : TypedSupportedType>(_ t: T.Type) {
29+
bar(t)
30+
}
31+
32+
// This used to recurse infinitely because the base witness table
33+
// for 'ContentMode : SupportedPropertyType' was incorrectly
34+
// minimized away.
35+
baz(ContentMode.self)
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// RUN: not %target-swift-frontend -typecheck %s
2+
3+
protocol Item {
4+
associatedtype Rule
5+
func isValide(valid: Rule) -> Bool
6+
}
7+
8+
protocol PairableItem: Item {
9+
associatedtype AssociatedItem: PairableItem where AssociatedItem.Rule: Rule
10+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// RUN: not %target-swift-frontend -typecheck %s
2+
3+
struct S<N> {}
4+
5+
protocol P {
6+
associatedtype A: P = Self
7+
static func f(_ x: A) -> A
8+
}
9+
10+
extension S: P where N: P {
11+
static func f<X: P>(_ x: X) -> S<X.A> where A == X, X.A == N {
12+
return S<X.A>()
13+
}
14+
}

0 commit comments

Comments
 (0)