Skip to content

Commit c9037a7

Browse files
authored
Merge pull request swiftlang#36483 from slavapestov/use-redundant-requirement-graph-for-diagnostics-part-1
GSB: Use redundant requirements graph to diagnose redundant conformance requirements
2 parents f99b477 + bb5df9d commit c9037a7

File tree

3 files changed

+114
-53
lines changed

3 files changed

+114
-53
lines changed

include/swift/AST/GenericSignatureBuilder.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,8 @@ class GenericSignatureBuilder {
646646
private:
647647
void computeRedundantRequirements();
648648

649+
void diagnoseRedundantRequirements() const;
650+
649651
bool hasExplicitConformancesImpliedByConcrete() const;
650652

651653
/// Describes the relationship between a given constraint and

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 108 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -5598,6 +5598,29 @@ static void expandSameTypeConstraints(GenericSignatureBuilder &builder,
55985598
}
55995599
}
56005600

5601+
static bool compareSourceLocs(SourceManager &SM, SourceLoc lhsLoc, SourceLoc rhsLoc) {
5602+
if (lhsLoc.isValid() != rhsLoc.isValid())
5603+
return lhsLoc.isValid();
5604+
5605+
if (lhsLoc.isValid() && rhsLoc.isValid()) {
5606+
unsigned lhsBuffer = SM.findBufferContainingLoc(lhsLoc);
5607+
unsigned rhsBuffer = SM.findBufferContainingLoc(rhsLoc);
5608+
5609+
// If the buffers are the same, use source location ordering.
5610+
if (lhsBuffer == rhsBuffer) {
5611+
if (SM.isBeforeInBuffer(lhsLoc, rhsLoc))
5612+
return true;
5613+
} else {
5614+
// Otherwise, order by buffer identifier.
5615+
if (SM.getIdentifierForBuffer(lhsBuffer)
5616+
.compare(SM.getIdentifierForBuffer(lhsBuffer)))
5617+
return true;
5618+
}
5619+
}
5620+
5621+
return false;
5622+
}
5623+
56015624
/// A directed graph where the vertices are explicit requirement sources and
56025625
/// an edge (v, w) records that the explicit source v implies the explicit
56035626
/// source w.
@@ -5996,29 +6019,10 @@ class RedundantRequirementGraph {
59966019
if (cmpTypes != 0)
59976020
return cmpTypes < 0;
59986021

5999-
// Prefer source-location-based over abstract.
60006022
auto existingLoc = existingSource->getLoc();
60016023
auto currentLoc = currentSource->getLoc();
6002-
if (existingLoc.isValid() != currentLoc.isValid())
6003-
return currentLoc.isValid();
6004-
6005-
if (existingLoc.isValid() && currentLoc.isValid()) {
6006-
unsigned existingBuffer = SM.findBufferContainingLoc(existingLoc);
6007-
unsigned currentBuffer = SM.findBufferContainingLoc(currentLoc);
60086024

6009-
// If the buffers are the same, use source location ordering.
6010-
if (existingBuffer == currentBuffer) {
6011-
if (SM.isBeforeInBuffer(currentLoc, existingLoc))
6012-
return true;
6013-
} else {
6014-
// Otherwise, order by buffer identifier.
6015-
if (SM.getIdentifierForBuffer(currentBuffer)
6016-
.compare(SM.getIdentifierForBuffer(existingBuffer)))
6017-
return true;
6018-
}
6019-
}
6020-
6021-
return true;
6025+
return compareSourceLocs(SM, currentLoc, existingLoc);
60226026
}
60236027

60246028
public:
@@ -6230,6 +6234,7 @@ GenericSignatureBuilder::finalize(TypeArrayView<GenericTypeParamType> genericPar
62306234
processDelayedRequirements();
62316235

62326236
computeRedundantRequirements();
6237+
diagnoseRedundantRequirements();
62336238

62346239
assert(!Impl->finalized && "Already finalized builder");
62356240
#ifndef NDEBUG
@@ -6868,41 +6873,95 @@ void GenericSignatureBuilder::checkConformanceConstraints(
68686873

68696874
// Remove any self-derived constraints.
68706875
removeSelfDerived(*this, entry.second, entry.first);
6876+
}
6877+
}
68716878

6872-
checkConstraintList<ProtocolDecl *, ProtocolDecl *>(
6873-
genericParams, entry.second, RequirementKind::Conformance,
6874-
[](const Constraint<ProtocolDecl *> &constraint) {
6875-
return true;
6876-
},
6877-
[&](const Constraint<ProtocolDecl *> &constraint) {
6878-
auto proto = constraint.value;
6879-
assert(proto == entry.first && "Mixed up protocol constraints");
6879+
void GenericSignatureBuilder::diagnoseRedundantRequirements() const {
6880+
SmallVector<ExplicitRequirement, 2> redundantRequirements;
6881+
6882+
for (auto pair : Impl->RedundantRequirements) {
6883+
auto *source = pair.first.getSource();
6884+
6885+
// Don't diagnose anything without a source location.
6886+
if (source->getLoc().isInvalid())
6887+
continue;
6888+
6889+
// Don't diagnose redundant inferred requirements.
6890+
if (source->isInferredRequirement())
6891+
continue;
6892+
6893+
// Don't diagnose explicit requirements that are implied by
6894+
// inferred requirements.
6895+
if (llvm::all_of(pair.second,
6896+
[&](const ExplicitRequirement &otherReq) {
6897+
return otherReq.getSource()->isInferredRequirement();
6898+
}))
6899+
continue;
68806900

6881-
// If this conformance requirement recursively makes a protocol
6882-
// conform to itself, don't complain here.
6883-
auto source = constraint.source;
6901+
redundantRequirements.push_back(pair.first);
6902+
}
6903+
6904+
auto &SM = Context.SourceMgr;
6905+
6906+
std::sort(redundantRequirements.begin(), redundantRequirements.end(),
6907+
[&](ExplicitRequirement lhs, ExplicitRequirement rhs) {
6908+
return compareSourceLocs(SM,
6909+
lhs.getSource()->getLoc(),
6910+
rhs.getSource()->getLoc());
6911+
});
6912+
6913+
for (const auto &req : redundantRequirements) {
6914+
auto *source = req.getSource();
6915+
auto loc = source->getLoc();
6916+
assert(loc.isValid());
6917+
6918+
auto subjectType = getSugaredDependentType(source->getStoredType(),
6919+
getGenericParams());
6920+
6921+
switch (req.getKind()) {
6922+
case RequirementKind::Conformance: {
6923+
auto *proto = req.getRHS().get<ProtocolDecl *>();
6924+
6925+
// If this conformance requirement recursively makes a protocol
6926+
// conform to itself, don't complain here, because we diagnose
6927+
// the circular inheritance elsewhere.
6928+
{
68846929
auto rootSource = source->getRoot();
6885-
if (rootSource->kind == RequirementSource::RequirementSignatureSelf &&
6886-
source != rootSource &&
6887-
proto == rootSource->getProtocolDecl() &&
6888-
areInSameEquivalenceClass(rootSource->getRootType(),
6889-
source->getAffectedType())) {
6890-
return ConstraintRelation::Unrelated;
6930+
if (proto == rootSource->getProtocolDecl() &&
6931+
rootSource->kind == RequirementSource::RequirementSignatureSelf &&
6932+
rootSource->getRootType()->isEqual(source->getAffectedType())) {
6933+
break;
68916934
}
6935+
}
68926936

6893-
// If this is a redundantly inherited Objective-C protocol, treat it
6894-
// as "unrelated" to silence the warning about the redundant
6895-
// conformance.
6896-
if (isRedundantlyInheritableObjCProtocol(proto, constraint.source))
6897-
return ConstraintRelation::Unrelated;
6937+
// If this is a redundantly inherited Objective-C protocol, treat it
6938+
// as "unrelated" to silence the warning about the redundant
6939+
// conformance.
6940+
if (isRedundantlyInheritableObjCProtocol(proto, source))
6941+
break;
68986942

6899-
return ConstraintRelation::Redundant;
6900-
},
6901-
None,
6902-
diag::redundant_conformance_constraint,
6903-
diag::redundant_conformance_here,
6904-
[](ProtocolDecl *proto) { return proto; },
6905-
/*removeSelfDerived=*/false);
6943+
Context.Diags.diagnose(loc, diag::redundant_conformance_constraint,
6944+
subjectType, proto);
6945+
6946+
for (auto otherReq : Impl->RedundantRequirements[req]) {
6947+
auto *otherSource = otherReq.getSource();
6948+
auto otherLoc = otherSource->getLoc();
6949+
if (otherLoc.isInvalid())
6950+
continue;
6951+
6952+
Context.Diags.diagnose(otherLoc, diag::redundant_conformance_here,
6953+
1, subjectType, proto);
6954+
}
6955+
6956+
break;
6957+
}
6958+
6959+
case RequirementKind::Superclass:
6960+
case RequirementKind::Layout:
6961+
case RequirementKind::SameType:
6962+
// TODO
6963+
break;
6964+
}
69066965
}
69076966
}
69086967

test/Generics/requirement_inference.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,8 @@ func inferSameType1<T, U>(_ x: Model_P3_P4_Eq<T, U>) {
114114
}
115115

116116
func inferSameType2<T : P3, U : P4>(_: T, _: U) where U.P4Assoc : P2, T.P3Assoc == U.P4Assoc {}
117-
// expected-warning@-1{{redundant conformance constraint 'T.P3Assoc': 'P2'}}
118-
// expected-note@-2{{conformance constraint 'T.P3Assoc': 'P2' implied here}}
117+
// expected-warning@-1{{redundant conformance constraint 'U.P4Assoc': 'P2'}}
118+
// expected-note@-2{{conformance constraint 'U.P4Assoc': 'P2' implied here}}
119119

120120
func inferSameType3<T : PCommonAssoc1>(_: T) where T.CommonAssoc : P1, T : PCommonAssoc2 {
121121
}
@@ -134,8 +134,8 @@ protocol P7 : P6 {
134134

135135
// CHECK-LABEL: P7@
136136
// CHECK: Canonical generic signature: <τ_0_0 where τ_0_0 : P7, τ_0_0.AssocP6.Element : P6, τ_0_0.AssocP6.Element == τ_0_0.AssocP7.AssocP6.Element>
137-
extension P7 where AssocP6.Element : P6, // expected-note{{conformance constraint 'Self.AssocP6.Element': 'P6' written here}}
138-
AssocP7.AssocP6.Element : P6, // expected-warning{{redundant conformance constraint 'Self.AssocP6.Element': 'P6'}}
137+
extension P7 where AssocP6.Element : P6, // expected-note{{conformance constraint 'Self.AssocP7.AssocP6.Element': 'P6' implied here}}
138+
AssocP7.AssocP6.Element : P6, // expected-warning{{redundant conformance constraint 'Self.AssocP7.AssocP6.Element': 'P6'}}
139139
AssocP6.Element == AssocP7.AssocP6.Element {
140140
func nestedSameType1() { }
141141
}

0 commit comments

Comments
 (0)