Skip to content

Commit af6ec5a

Browse files
committed
GSB: When looking at protocol refinements, only consider other sources on 'Self'
In a protocol requirement signature, a conformance requirement on 'Self' can only be considered redundant if it is derived entirely from other sources only involving 'Self' as a subject type. What this means in practice is that we will never try to build a witness table for an inherited conformance from an associated conformance. This is important since witness tables for inherited conformances are realized eagerly before the witness table for the original conformance, and allowing more complex requirement sources for inherited conformances could introduce cycles at runtime. What used to happen is that we would emit a bogus 'redundant conformance' warning if a refined protocol conformance was also reachable via a same-type requirement involving 'Self'. Fixing this warning by removing the requirement could produce code that type checked, however it could crash at runtime. In addition to the runtime issue, such 'overly minimized' protocols could also break name lookup at compile-time, since name lookup only consults the inheritance clause of a protocol declaration instead of computing its generic signature to determine the set of refined protocols. Now, we will no longer emit the bogus redundant conformance warnings. Instead, we produce a warning if a protocol is 'overly minimized'. Since the 'overly minimized' protocols that were obtained by fixing the bogus warning would in some cases still work, we cannot diagnose the new violation as an error, to preserve source and ABI compatibility. Currently this warning fires for the SIMDScalar protocol in the standard library, and _ErrorCodeProtocol in the Foundation overlay. We should see if we can fix the protocols in an ABI-compatible way, or just add a hack to muffle the warning. Fixes https://bugs.swift.org/browse/SR-8198 / rdar://problem/77358480, https://bugs.swift.org/browse/SR-10941 / rdar://problem/77358477, https://bugs.swift.org/browse/SR-11670 / rdar://problem/77358476.
1 parent 3ce0065 commit af6ec5a

File tree

5 files changed

+140
-0
lines changed

5 files changed

+140
-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)

0 commit comments

Comments
 (0)