Skip to content

Commit bb5df9d

Browse files
committed
GSB: Use redundant requirements graph to diagnose redundant conformance requirements
This almost completely guts checkConformanceConstraints(), and removes a usage of checkConstraintList(). Yet another one of those refactorings that leaves the GSB in an intermediate state, with some new logic that's cleaner, while leaving behind old code for other use cases that haven't been refactored yet.
1 parent 891e053 commit bb5df9d

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
@@ -5594,6 +5594,29 @@ static void expandSameTypeConstraints(GenericSignatureBuilder &builder,
55945594
}
55955595
}
55965596

5597+
static bool compareSourceLocs(SourceManager &SM, SourceLoc lhsLoc, SourceLoc rhsLoc) {
5598+
if (lhsLoc.isValid() != rhsLoc.isValid())
5599+
return lhsLoc.isValid();
5600+
5601+
if (lhsLoc.isValid() && rhsLoc.isValid()) {
5602+
unsigned lhsBuffer = SM.findBufferContainingLoc(lhsLoc);
5603+
unsigned rhsBuffer = SM.findBufferContainingLoc(rhsLoc);
5604+
5605+
// If the buffers are the same, use source location ordering.
5606+
if (lhsBuffer == rhsBuffer) {
5607+
if (SM.isBeforeInBuffer(lhsLoc, rhsLoc))
5608+
return true;
5609+
} else {
5610+
// Otherwise, order by buffer identifier.
5611+
if (SM.getIdentifierForBuffer(lhsBuffer)
5612+
.compare(SM.getIdentifierForBuffer(lhsBuffer)))
5613+
return true;
5614+
}
5615+
}
5616+
5617+
return false;
5618+
}
5619+
55975620
/// A directed graph where the vertices are explicit requirement sources and
55985621
/// an edge (v, w) records that the explicit source v implies the explicit
55995622
/// source w.
@@ -5992,29 +6015,10 @@ class RedundantRequirementGraph {
59926015
if (cmpTypes != 0)
59936016
return cmpTypes < 0;
59946017

5995-
// Prefer source-location-based over abstract.
59966018
auto existingLoc = existingSource->getLoc();
59976019
auto currentLoc = currentSource->getLoc();
5998-
if (existingLoc.isValid() != currentLoc.isValid())
5999-
return currentLoc.isValid();
6000-
6001-
if (existingLoc.isValid() && currentLoc.isValid()) {
6002-
unsigned existingBuffer = SM.findBufferContainingLoc(existingLoc);
6003-
unsigned currentBuffer = SM.findBufferContainingLoc(currentLoc);
60046020

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

60206024
public:
@@ -6226,6 +6230,7 @@ GenericSignatureBuilder::finalize(TypeArrayView<GenericTypeParamType> genericPar
62266230
processDelayedRequirements();
62276231

62286232
computeRedundantRequirements();
6233+
diagnoseRedundantRequirements();
62296234

62306235
assert(!Impl->finalized && "Already finalized builder");
62316236
#ifndef NDEBUG
@@ -6864,41 +6869,95 @@ void GenericSignatureBuilder::checkConformanceConstraints(
68646869

68656870
// Remove any self-derived constraints.
68666871
removeSelfDerived(*this, entry.second, entry.first);
6872+
}
6873+
}
68676874

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

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

6889-
// If this is a redundantly inherited Objective-C protocol, treat it
6890-
// as "unrelated" to silence the warning about the redundant
6891-
// conformance.
6892-
if (isRedundantlyInheritableObjCProtocol(proto, constraint.source))
6893-
return ConstraintRelation::Unrelated;
6933+
// If this is a redundantly inherited Objective-C protocol, treat it
6934+
// as "unrelated" to silence the warning about the redundant
6935+
// conformance.
6936+
if (isRedundantlyInheritableObjCProtocol(proto, source))
6937+
break;
68946938

6895-
return ConstraintRelation::Redundant;
6896-
},
6897-
None,
6898-
diag::redundant_conformance_constraint,
6899-
diag::redundant_conformance_here,
6900-
[](ProtocolDecl *proto) { return proto; },
6901-
/*removeSelfDerived=*/false);
6939+
Context.Diags.diagnose(loc, diag::redundant_conformance_constraint,
6940+
subjectType, proto);
6941+
6942+
for (auto otherReq : Impl->RedundantRequirements[req]) {
6943+
auto *otherSource = otherReq.getSource();
6944+
auto otherLoc = otherSource->getLoc();
6945+
if (otherLoc.isInvalid())
6946+
continue;
6947+
6948+
Context.Diags.diagnose(otherLoc, diag::redundant_conformance_here,
6949+
1, subjectType, proto);
6950+
}
6951+
6952+
break;
6953+
}
6954+
6955+
case RequirementKind::Superclass:
6956+
case RequirementKind::Layout:
6957+
case RequirementKind::SameType:
6958+
// TODO
6959+
break;
6960+
}
69026961
}
69036962
}
69046963

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)