Skip to content

Commit f190e51

Browse files
committed
GSB: Diagnose redundant superclass requirements using the redundant requirement graph
1 parent 45ab4f4 commit f190e51

11 files changed

+191
-115
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2414,16 +2414,17 @@ NOTE(same_type_redundancy_here,none,
24142414
"same-type constraint %1 == %2 %select{written here|implied here|"
24152415
"inferred from type here}0",
24162416
(unsigned, Type, Type))
2417-
ERROR(requires_superclass_conflict,none,
2418-
"%select{generic parameter %1 cannot|protocol %1 cannot require 'Self' to|"
2419-
"%1 cannot}0 be a subclass of both %2 and %3",
2420-
(unsigned, Type, Type, Type))
2417+
ERROR(conflicting_superclass_constraints,none,
2418+
"type %0 cannot be a subclass of both %1 and %2",
2419+
(Type, Type, Type))
2420+
NOTE(conflicting_superclass_constraint,none,
2421+
"constraint conflicts with %0 : %1",
2422+
(Type, Type))
24212423
WARNING(redundant_superclass_constraint,none,
24222424
"redundant superclass constraint %0 : %1", (Type, Type))
24232425
NOTE(superclass_redundancy_here,none,
2424-
"superclass constraint %1 : %2 %select{written here|implied here|"
2425-
"inferred from type here}0",
2426-
(unsigned, Type, Type))
2426+
"superclass constraint %0 : %1 implied here",
2427+
(Type, Type))
24272428

24282429
ERROR(conflicting_layout_constraints,none,
24292430
"type %0 has conflicting constraints %1 and %2",

include/swift/AST/GenericSignatureBuilder.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,8 @@ class GenericSignatureBuilder {
653653

654654
void diagnoseRedundantRequirements() const;
655655

656+
void diagnoseConflictingConcreteTypeRequirements() const;
657+
656658
bool hasExplicitConformancesImpliedByConcrete() const;
657659

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

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 158 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,15 @@ template<> struct DenseMapInfo<swift::GenericSignatureBuilder::ExplicitRequireme
647647

648648
}
649649

650+
namespace {
651+
struct ConflictingConcreteTypeRequirement {
652+
ExplicitRequirement concreteTypeRequirement;
653+
ExplicitRequirement otherRequirement;
654+
Type resolvedConcreteType;
655+
RequirementRHS otherRHS;
656+
};
657+
}
658+
650659
struct GenericSignatureBuilder::Implementation {
651660
/// Allocator.
652661
llvm::BumpPtrAllocator Allocator;
@@ -716,6 +725,11 @@ struct GenericSignatureBuilder::Implementation {
716725
/// the second one is recorded here. Built by computeRedundantRequirements().
717726
llvm::DenseMap<ExplicitRequirement, RequirementRHS> ConflictingRequirements;
718727

728+
/// Pairs of conflicting concrete type vs. superclass/layout/conformance
729+
/// requirements.
730+
std::vector<ConflictingConcreteTypeRequirement>
731+
ConflictingConcreteTypeRequirements;
732+
719733
#ifndef NDEBUG
720734
/// Whether we've already computed redundant requiremnts.
721735
bool computedRedundantRequirements = false;
@@ -5775,10 +5789,25 @@ class RedundantRequirementGraph {
57755789

57765790
public:
57775791
template<typename T>
5778-
void addConstraintsFromEquivClass(
5792+
void handleConstraintsImpliedByConcrete(
5793+
RequirementKind kind,
5794+
const SmallVectorImpl<Constraint<T>> &impliedByConcrete,
5795+
const Optional<ExplicitRequirement> &concreteTypeRequirement) {
5796+
for (auto constraint : impliedByConcrete) {
5797+
if (constraint.source->isDerivedNonRootRequirement())
5798+
continue;
5799+
5800+
auto req = ExplicitRequirement::fromExplicitConstraint(kind, constraint);
5801+
addEdge(*concreteTypeRequirement, req);
5802+
}
5803+
}
5804+
5805+
template<typename T>
5806+
Optional<ExplicitRequirement>
5807+
addConstraintsFromEquivClass(
57795808
RequirementKind kind,
5780-
SmallVectorImpl<Constraint<T>> &exact,
5781-
SmallVectorImpl<Constraint<T>> &lessSpecific) {
5809+
const SmallVectorImpl<Constraint<T>> &exact,
5810+
const SmallVectorImpl<Constraint<T>> &lessSpecific) {
57825811
// The set of 'redundant explicit requirements', which are known to be less
57835812
// specific than some other explicit requirements. An example is a type
57845813
// parameter with multiple superclass constraints:
@@ -5843,6 +5872,13 @@ class RedundantRequirementGraph {
58435872
redundantExplicitReqs.push_back(req);
58445873
}
58455874

5875+
// Get the "best" explicit requirement that implies the rest.
5876+
Optional<ExplicitRequirement> result;
5877+
if (!rootReqs.empty())
5878+
result = rootReqs[0];
5879+
else if (!explicitReqs.empty())
5880+
result = explicitReqs[0];
5881+
58465882
// If all requirements are derived, there is nothing to do.
58475883
if (explicitReqs.empty()) {
58485884
if (!redundantExplicitReqs.empty()) {
@@ -5853,7 +5889,7 @@ class RedundantRequirementGraph {
58535889
}
58545890
}
58555891
}
5856-
return;
5892+
return result;
58575893
}
58585894

58595895
// If there are multiple most specific explicit requirements, they will form a cycle.
@@ -5876,6 +5912,8 @@ class RedundantRequirementGraph {
58765912
for (auto rootReq : rootReqs) {
58775913
addEdge(rootReq, explicitReqs[0]);
58785914
}
5915+
5916+
return result;
58795917
}
58805918

58815919
private:
@@ -6241,8 +6279,11 @@ void GenericSignatureBuilder::computeRedundantRequirements() {
62416279
exact, lessSpecific);
62426280
}
62436281

6282+
Type resolvedConcreteType;
6283+
Optional<ExplicitRequirement> concreteTypeRequirement;
6284+
62446285
if (equivClass.concreteType) {
6245-
Type resolvedConcreteType =
6286+
resolvedConcreteType =
62466287
getCanonicalTypeInContext(equivClass.concreteType, { });
62476288

62486289
SmallVector<Constraint<Type>, 2> exact;
@@ -6285,17 +6326,22 @@ void GenericSignatureBuilder::computeRedundantRequirements() {
62856326
lessSpecific.push_back(constraint);
62866327
}
62876328

6288-
graph.addConstraintsFromEquivClass(RequirementKind::SameType,
6289-
exact, lessSpecific);
6329+
concreteTypeRequirement =
6330+
graph.addConstraintsFromEquivClass(RequirementKind::SameType,
6331+
exact, lessSpecific);
62906332
}
62916333

6334+
Type resolvedSuperclass;
6335+
Optional<ExplicitRequirement> superclassRequirement;
6336+
62926337
if (equivClass.superclass) {
62936338
// Resolve any thus-far-unresolved dependent types.
6294-
Type resolvedSuperclass =
6295-
getCanonicalTypeInContext(equivClass.superclass, { });
6339+
resolvedSuperclass =
6340+
getCanonicalTypeInContext(equivClass.superclass, getGenericParams());
62966341

62976342
SmallVector<Constraint<Type>, 2> exact;
62986343
SmallVector<Constraint<Type>, 2> lessSpecific;
6344+
SmallVector<Constraint<Type>, 2> impliedByConcrete;
62996345

63006346
for (const auto &constraint : equivClass.superclassConstraints) {
63016347
auto *source = constraint.source;
@@ -6312,6 +6358,12 @@ void GenericSignatureBuilder::computeRedundantRequirements() {
63126358
if (derivedViaConcrete)
63136359
continue;
63146360

6361+
if (resolvedConcreteType) {
6362+
Type resolvedType = getCanonicalTypeInContext(t, { });
6363+
if (resolvedType->isExactSuperclassOf(resolvedConcreteType))
6364+
impliedByConcrete.push_back(constraint);
6365+
}
6366+
63156367
if (t->isEqual(resolvedSuperclass)) {
63166368
exact.push_back(constraint);
63176369
continue;
@@ -6333,12 +6385,16 @@ void GenericSignatureBuilder::computeRedundantRequirements() {
63336385
std::make_pair(req, resolvedSuperclass));
63346386
}
63356387

6336-
// FIXME: Check for a conflict via the concrete type.
63376388
lessSpecific.push_back(constraint);
63386389
}
63396390

6340-
graph.addConstraintsFromEquivClass(RequirementKind::Superclass,
6341-
exact, lessSpecific);
6391+
superclassRequirement =
6392+
graph.addConstraintsFromEquivClass(RequirementKind::Superclass,
6393+
exact, lessSpecific);
6394+
6395+
graph.handleConstraintsImpliedByConcrete(RequirementKind::Superclass,
6396+
impliedByConcrete,
6397+
concreteTypeRequirement);
63426398
}
63436399

63446400
if (equivClass.layout) {
@@ -6382,6 +6438,14 @@ void GenericSignatureBuilder::computeRedundantRequirements() {
63826438
graph.addConstraintsFromEquivClass(RequirementKind::Layout,
63836439
exact, lessSpecific);
63846440
}
6441+
6442+
if (resolvedConcreteType && resolvedSuperclass) {
6443+
if (!resolvedSuperclass->isExactSuperclassOf(resolvedConcreteType)) {
6444+
Impl->ConflictingConcreteTypeRequirements.push_back(
6445+
{*concreteTypeRequirement, *superclassRequirement,
6446+
resolvedConcreteType, resolvedSuperclass});
6447+
}
6448+
}
63856449
}
63866450

63876451
auto &SM = getASTContext().SourceMgr;
@@ -6403,6 +6467,7 @@ GenericSignatureBuilder::finalize(TypeArrayView<GenericTypeParamType> genericPar
64036467

64046468
computeRedundantRequirements();
64056469
diagnoseRedundantRequirements();
6470+
diagnoseConflictingConcreteTypeRequirements();
64066471

64076472
assert(!Impl->finalized && "Already finalized builder");
64086473
#ifndef NDEBUG
@@ -7151,14 +7216,93 @@ void GenericSignatureBuilder::diagnoseRedundantRequirements() const {
71517216
break;
71527217
}
71537218

7154-
case RequirementKind::Superclass:
7219+
case RequirementKind::Superclass: {
7220+
auto superclass = req.getRHS().get<Type>();
7221+
7222+
auto conflict = Impl->ConflictingRequirements.find(req);
7223+
if (conflict != Impl->ConflictingRequirements.end()) {
7224+
Impl->HadAnyError = true;
7225+
7226+
auto otherSuperclass = conflict->second.get<Type>();
7227+
7228+
Context.Diags.diagnose(loc, diag::conflicting_superclass_constraints,
7229+
subjectType, superclass, otherSuperclass);
7230+
7231+
for (auto otherReq : found->second) {
7232+
auto *otherSource = otherReq.getSource();
7233+
auto otherLoc = otherSource->getLoc();
7234+
if (otherLoc.isInvalid())
7235+
continue;
7236+
7237+
Context.Diags.diagnose(otherLoc, diag::conflicting_superclass_constraint,
7238+
subjectType, otherSuperclass);
7239+
}
7240+
} else {
7241+
Context.Diags.diagnose(loc, diag::redundant_superclass_constraint,
7242+
subjectType, superclass);
7243+
7244+
for (auto otherReq : found->second) {
7245+
auto *otherSource = otherReq.getSource();
7246+
auto otherLoc = otherSource->getLoc();
7247+
if (otherLoc.isInvalid())
7248+
continue;
7249+
7250+
Context.Diags.diagnose(otherLoc, diag::superclass_redundancy_here,
7251+
subjectType, superclass);
7252+
}
7253+
}
7254+
7255+
break;
7256+
}
7257+
71557258
case RequirementKind::SameType:
71567259
// TODO
71577260
break;
71587261
}
71597262
}
71607263
}
71617264

7265+
void GenericSignatureBuilder::diagnoseConflictingConcreteTypeRequirements() const {
7266+
for (auto pair : Impl->ConflictingConcreteTypeRequirements) {
7267+
SourceLoc loc = pair.concreteTypeRequirement.getSource()->getLoc();
7268+
SourceLoc otherLoc = pair.otherRequirement.getSource()->getLoc();
7269+
7270+
Type otherRHS = pair.otherRHS.get<Type>();
7271+
7272+
if (loc.isInvalid() && otherLoc.isInvalid())
7273+
continue;
7274+
7275+
auto subjectType = pair.concreteTypeRequirement.getSource()->getStoredType();
7276+
SourceLoc subjectLoc = (loc.isInvalid() ? otherLoc : loc);
7277+
7278+
Impl->HadAnyError = true;
7279+
7280+
switch (pair.otherRequirement.getKind()) {
7281+
case RequirementKind::Superclass: {
7282+
Context.Diags.diagnose(subjectLoc, diag::type_does_not_inherit,
7283+
subjectType, pair.resolvedConcreteType, otherRHS);
7284+
7285+
if (otherLoc.isValid()) {
7286+
Context.Diags.diagnose(otherLoc, diag::superclass_redundancy_here,
7287+
subjectType, otherRHS);
7288+
}
7289+
7290+
break;
7291+
}
7292+
7293+
case RequirementKind::Conformance:
7294+
case RequirementKind::Layout:
7295+
case RequirementKind::SameType:
7296+
llvm_unreachable("TODO");
7297+
}
7298+
7299+
if (loc.isValid()) {
7300+
Context.Diags.diagnose(loc, diag::same_type_redundancy_here,
7301+
1, subjectType, pair.resolvedConcreteType);
7302+
}
7303+
}
7304+
}
7305+
71627306
namespace swift {
71637307
bool operator<(const DerivedSameTypeComponent &lhs,
71647308
const DerivedSameTypeComponent &rhs) {
@@ -7978,81 +8122,7 @@ void GenericSignatureBuilder::checkSuperclassConstraints(
79788122
EquivalenceClass *equivClass) {
79798123
assert(equivClass->superclass && "No superclass constraint?");
79808124

7981-
// Resolve any thus-far-unresolved dependent types.
7982-
Type resolvedSuperclass =
7983-
getCanonicalTypeInContext(equivClass->superclass, genericParams);
7984-
7985-
auto representativeConstraint =
7986-
checkConstraintList<Type>(
7987-
genericParams, equivClass->superclassConstraints, RequirementKind::Superclass,
7988-
[&](const ConcreteConstraint &constraint) {
7989-
if (constraint.value->isEqual(resolvedSuperclass))
7990-
return true;
7991-
7992-
Type resolvedType =
7993-
getCanonicalTypeInContext(constraint.value, { });
7994-
return resolvedType->isEqual(resolvedSuperclass);
7995-
},
7996-
[&](const Constraint<Type> &constraint) {
7997-
Type superclass = constraint.value;
7998-
7999-
// If this class is a superclass of the "best"
8000-
if (superclass->isExactSuperclassOf(resolvedSuperclass))
8001-
return ConstraintRelation::Redundant;
8002-
8003-
// Otherwise, it conflicts.
8004-
return ConstraintRelation::Conflicting;
8005-
},
8006-
diag::requires_superclass_conflict,
8007-
diag::redundant_superclass_constraint,
8008-
diag::superclass_redundancy_here);
8009-
8010-
// If we have a concrete type, check it.
8011-
// FIXME: Substitute into the concrete type.
8012-
if (equivClass->concreteType) {
8013-
Type resolvedConcreteType =
8014-
getCanonicalTypeInContext(equivClass->concreteType, genericParams);
8015-
auto existing = equivClass->findAnyConcreteConstraintAsWritten();
8016-
// Make sure the concrete type fulfills the superclass requirement.
8017-
if (!equivClass->superclass->isExactSuperclassOf(resolvedConcreteType)){
8018-
Impl->HadAnyError = true;
8019-
if (existing) {
8020-
Diags.diagnose(existing->source->getLoc(), diag::type_does_not_inherit,
8021-
existing->getSubjectDependentType(getGenericParams()),
8022-
existing->value, equivClass->superclass);
8023-
8024-
if (representativeConstraint.source->getLoc().isValid()) {
8025-
Diags.diagnose(representativeConstraint.source->getLoc(),
8026-
diag::superclass_redundancy_here,
8027-
representativeConstraint.source->classifyDiagKind(),
8028-
representativeConstraint.getSubjectDependentType(
8029-
genericParams),
8030-
equivClass->superclass);
8031-
}
8032-
} else if (representativeConstraint.source->getLoc().isValid()) {
8033-
Diags.diagnose(representativeConstraint.source->getLoc(),
8034-
diag::type_does_not_inherit,
8035-
representativeConstraint.getSubjectDependentType(
8036-
genericParams),
8037-
resolvedConcreteType, equivClass->superclass);
8038-
}
8039-
} else if (representativeConstraint.source->shouldDiagnoseRedundancy(true)
8040-
&& existing &&
8041-
existing->source->shouldDiagnoseRedundancy(false)) {
8042-
// It does fulfill the requirement; diagnose the redundancy.
8043-
Diags.diagnose(representativeConstraint.source->getLoc(),
8044-
diag::redundant_superclass_constraint,
8045-
representativeConstraint.getSubjectDependentType(
8046-
genericParams),
8047-
representativeConstraint.value);
8048-
8049-
Diags.diagnose(existing->source->getLoc(),
8050-
diag::same_type_redundancy_here,
8051-
existing->source->classifyDiagKind(),
8052-
existing->getSubjectDependentType(genericParams),
8053-
existing->value);
8054-
}
8055-
}
8125+
removeSelfDerived(*this, equivClass->superclassConstraints, /*proto=*/nullptr);
80568126
}
80578127

80588128
void GenericSignatureBuilder::checkLayoutConstraints(

0 commit comments

Comments
 (0)