Skip to content

Commit 31ad5b6

Browse files
authored
Merge pull request #11479 from DougGregor/gsb-self-derived-cleanup
2 parents c1b4bcb + c1cf112 commit 31ad5b6

File tree

2 files changed

+86
-145
lines changed

2 files changed

+86
-145
lines changed

include/swift/AST/GenericSignatureBuilder.h

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,19 +1142,21 @@ class GenericSignatureBuilder::RequirementSource final
11421142
/// if they are equivalent in length.
11431143
int compare(const RequirementSource *other) const;
11441144

1145+
/// Retrieve the written requirement location, if there is one.
1146+
WrittenRequirementLoc getWrittenRequirementLoc() const {
1147+
if (!hasTrailingWrittenRequirementLoc) return WrittenRequirementLoc();
1148+
return getTrailingObjects<WrittenRequirementLoc>()[0];
1149+
}
1150+
11451151
/// Retrieve the type representation for this requirement, if there is one.
11461152
const TypeRepr *getTypeRepr() const {
1147-
if (!hasTrailingWrittenRequirementLoc) return nullptr;
1148-
return getTrailingObjects<WrittenRequirementLoc>()[0]
1149-
.dyn_cast<const TypeRepr *>();
1153+
return getWrittenRequirementLoc().dyn_cast<const TypeRepr *>();
11501154
}
11511155

11521156
/// Retrieve the requirement representation for this requirement, if there is
11531157
/// one.
11541158
const RequirementRepr *getRequirementRepr() const {
1155-
if (!hasTrailingWrittenRequirementLoc) return nullptr;
1156-
return getTrailingObjects<WrittenRequirementLoc>()[0]
1157-
.dyn_cast<const RequirementRepr *>();
1159+
return getWrittenRequirementLoc().dyn_cast<const RequirementRepr *>();
11581160
}
11591161

11601162
/// Retrieve the type stored in this requirement.

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 78 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -296,50 +296,8 @@ bool RequirementSource::isDerivedRequirement() const {
296296

297297
bool RequirementSource::isSelfDerivedSource(PotentialArchetype *pa,
298298
bool &derivedViaConcrete) const {
299-
derivedViaConcrete = false;
300-
301-
// If it's not a derived requirement, it's not self-derived.
302-
if (!isDerivedRequirement()) return false;
303-
304-
/// Keep track of all of the protocol requirements we've seen along the way.
305-
/// If we see the same requirement twice, we have a self-derived source.
306-
llvm::DenseSet<std::pair<PotentialArchetype *, ProtocolDecl *>>
307-
constraintsSeen;
308-
309-
// Note that we've now seen a new conformance constraint, returning true if
310-
// we've seen it before.
311-
auto addConstraint = [&](PotentialArchetype *pa, ProtocolDecl *proto) {
312-
return !constraintsSeen.insert({pa->getRepresentative(), proto}).second;
313-
};
314-
315-
return visitPotentialArchetypesAlongPath(
316-
[&](PotentialArchetype *currentPA, const RequirementSource *source) {
317-
switch (source->kind) {
318-
case RequirementSource::Explicit:
319-
case RequirementSource::Inferred:
320-
case RequirementSource::QuietlyInferred:
321-
case RequirementSource::RequirementSignatureSelf:
322-
return false;
323-
324-
case RequirementSource::Parent:
325-
return currentPA->isInSameEquivalenceClassAs(pa);
326-
327-
case RequirementSource::ProtocolRequirement:
328-
case RequirementSource::InferredProtocolRequirement:
329-
// Note whether we saw derivation through a concrete type.
330-
if (currentPA->isConcreteType())
331-
derivedViaConcrete = true;
332-
333-
return addConstraint(currentPA, source->getProtocolDecl());
334-
335-
case RequirementSource::NestedTypeNameMatch:
336-
case RequirementSource::ConcreteTypeBinding:
337-
case RequirementSource::Concrete:
338-
case RequirementSource::Superclass:
339-
case RequirementSource::Derived:
340-
return false;
341-
}
342-
}) == nullptr;
299+
return getMinimalConformanceSource(pa, /*proto=*/nullptr, derivedViaConcrete)
300+
!= this;
343301
}
344302

345303
/// Replace 'Self' in the given dependent type (\c depTy) with the given
@@ -435,7 +393,7 @@ const RequirementSource *RequirementSource::getMinimalConformanceSource(
435393
PotentialArchetype *rootPA = nullptr;
436394
Optional<std::pair<const RequirementSource *, const RequirementSource *>>
437395
redundantSubpath;
438-
(void)visitPotentialArchetypesAlongPath(
396+
bool isSelfDerived = visitPotentialArchetypesAlongPath(
439397
[&](PotentialArchetype *parentPA, const RequirementSource *source) {
440398
switch (source->kind) {
441399
case ProtocolRequirement:
@@ -459,9 +417,12 @@ const RequirementSource *RequirementSource::getMinimalConformanceSource(
459417
return true;
460418
}
461419

420+
case Parent:
421+
// FIXME: Ad hoc detection of recursive same-type constraints.
422+
return !proto && parentPA->isInSameEquivalenceClassAs(currentPA);
423+
462424
case Concrete:
463425
case Superclass:
464-
case Parent:
465426
case Derived:
466427
return false;
467428
case Explicit:
@@ -473,26 +434,31 @@ const RequirementSource *RequirementSource::getMinimalConformanceSource(
473434
rootPA = parentPA;
474435
return false;
475436
}
476-
});
437+
}) == nullptr;
477438

478439
// If we didn't already find a redundancy, check our end state.
479440
if (!redundantSubpath && proto) {
480441
if (auto startOfPath = addConstraint(currentPA, proto, this)) {
481442
redundantSubpath = { startOfPath, this };
482443
assert(startOfPath != this);
444+
isSelfDerived = true;
483445
}
484446
}
485447

486-
487448
// If we saw a constraint twice, it's self-derived.
488449
if (redundantSubpath) {
450+
assert(isSelfDerived && "Not considered self-derived?");
489451
auto shorterSource =
490452
withoutRedundantSubpath(redundantSubpath->first,
491453
redundantSubpath->second);
492454
return shorterSource
493455
->getMinimalConformanceSource(currentPA, proto, derivedViaConcrete);
494456
}
495457

458+
// It's self-derived but we don't have a redundant subpath to eliminate.
459+
if (isSelfDerived)
460+
return nullptr;
461+
496462
// If we haven't seen a protocol requirement, we're done.
497463
if (!sawProtocolRequirement) return this;
498464

@@ -687,12 +653,14 @@ const RequirementSource *RequirementSource::withoutRedundantSubpath(
687653
case ProtocolRequirement:
688654
return parent->withoutRedundantSubpath(start, end)
689655
->viaProtocolRequirement(builder, getStoredType(),
690-
getProtocolDecl(), /*inferred=*/false);
656+
getProtocolDecl(), /*inferred=*/false,
657+
getWrittenRequirementLoc());
691658

692659
case InferredProtocolRequirement:
693660
return parent->withoutRedundantSubpath(start, end)
694661
->viaProtocolRequirement(builder, getStoredType(),
695-
getProtocolDecl(), /*inferred=*/true);
662+
getProtocolDecl(), /*inferred=*/true,
663+
getWrittenRequirementLoc());
696664

697665
case Concrete:
698666
return parent->withoutRedundantSubpath(start, end)
@@ -4273,37 +4241,72 @@ namespace {
42734241
/// \returns true if any derived-via-concrete constraints were found.
42744242
template<typename T>
42754243
bool removeSelfDerived(std::vector<Constraint<T>> &constraints,
4244+
ProtocolDecl *proto,
42764245
bool dropDerivedViaConcrete = true) {
42774246
bool anyDerivedViaConcrete = false;
4278-
// Remove self-derived constraints.
42794247
Optional<Constraint<T>> remainingConcrete;
4248+
SmallVector<Constraint<T>, 4> minimalSources;
42804249
constraints.erase(
42814250
std::remove_if(constraints.begin(), constraints.end(),
4282-
[&](const Constraint<T> &constraint) {
4283-
bool derivedViaConcrete;
4284-
if (constraint.source->isSelfDerivedSource(
4285-
constraint.archetype, derivedViaConcrete)) {
4286-
++NumSelfDerived;
4287-
return true;
4288-
}
4251+
[&](const Constraint<T> &constraint) {
4252+
bool derivedViaConcrete;
4253+
auto minimalSource =
4254+
constraint.source->getMinimalConformanceSource(constraint.archetype,
4255+
proto,
4256+
derivedViaConcrete);
4257+
if (minimalSource != constraint.source) {
4258+
// The minimal source is smaller than the original source, so the
4259+
// original source is self-derived.
4260+
++NumSelfDerived;
4261+
4262+
// FIXME: "proto" check means we don't do this for same-type
4263+
// constraints, where we still seem to get minimization wrong.
4264+
if (minimalSource && proto) {
4265+
// Record a constraint with a minimized source.
4266+
minimalSources.push_back(
4267+
{constraint.archetype,
4268+
constraint.value,
4269+
minimalSource});
4270+
}
42894271

4290-
if (!derivedViaConcrete)
4291-
return false;
4272+
return true;
4273+
}
42924274

4293-
anyDerivedViaConcrete = true;
4275+
if (!derivedViaConcrete)
4276+
return false;
42944277

4295-
if (!dropDerivedViaConcrete)
4296-
return false;
4278+
anyDerivedViaConcrete = true;
42974279

4298-
// Drop derived-via-concrete requirements.
4299-
if (!remainingConcrete)
4300-
remainingConcrete = constraint;
4280+
if (!dropDerivedViaConcrete)
4281+
return false;
43014282

4302-
++NumSelfDerived;
4303-
return true;
4304-
}),
4283+
// Drop derived-via-concrete requirements.
4284+
if (!remainingConcrete)
4285+
remainingConcrete = constraint;
4286+
4287+
++NumSelfDerived;
4288+
return true;
4289+
}),
43054290
constraints.end());
43064291

4292+
// If we found any minimal sources, add them now, avoiding introducing any
4293+
// redundant sources.
4294+
if (!minimalSources.empty()) {
4295+
// Collect the sources we already know about.
4296+
SmallPtrSet<const RequirementSource *, 4> sources;
4297+
for (const auto &constraint : constraints) {
4298+
sources.insert(constraint.source);
4299+
}
4300+
4301+
// Add any minimal sources we didn't know about.
4302+
for (const auto &minimalSource : minimalSources) {
4303+
if (sources.insert(minimalSource.source).second) {
4304+
constraints.push_back(minimalSource);
4305+
}
4306+
}
4307+
}
4308+
4309+
// If we only had concrete conformances, put one back.
43074310
if (constraints.empty() && remainingConcrete)
43084311
constraints.push_back(*remainingConcrete);
43094312

@@ -4329,7 +4332,7 @@ Constraint<T> GenericSignatureBuilder::checkConstraintList(
43294332
bool removeSelfDerived) {
43304333
assert(!constraints.empty() && "No constraints?");
43314334
if (removeSelfDerived) {
4332-
::removeSelfDerived(constraints);
4335+
::removeSelfDerived(constraints, /*proto=*/nullptr);
43334336
}
43344337

43354338
// Sort the constraints, so we get a deterministic ordering of diagnostics.
@@ -4483,73 +4486,8 @@ void GenericSignatureBuilder::checkConformanceConstraints(
44834486
// Remove self-derived constraints.
44844487
assert(!entry.second.empty() && "No constraints to work with?");
44854488

4486-
// Local function to establish whether a conformance constraint is
4487-
// self-derived.
4488-
Optional<Constraint<ProtocolDecl *>> remainingConcrete;
4489-
SmallVector<Constraint<ProtocolDecl *>, 4> minimalSources;
4490-
auto isSelfDerived =
4491-
[&](const Constraint<ProtocolDecl *> &constraint) {
4492-
// Determine whether there is a subpath of this requirement source that
4493-
// derives the same conformance.
4494-
bool derivedViaConcrete;
4495-
auto minimalSource =
4496-
constraint.source->getMinimalConformanceSource(constraint.archetype,
4497-
entry.first,
4498-
derivedViaConcrete);
4499-
if (minimalSource != constraint.source) {
4500-
// The minimal source is smaller than the original source, so the
4501-
// original source is self-derived.
4502-
++NumSelfDerived;
4503-
4504-
if (minimalSource && minimalSource->isDerivedRequirement()) {
4505-
// Record a constraint with a minimized source.
4506-
minimalSources.push_back(
4507-
{minimalSource->getAffectedPotentialArchetype(),
4508-
constraint.value,
4509-
minimalSource});
4510-
}
4511-
4512-
return true;
4513-
}
4514-
4515-
if (!derivedViaConcrete)
4516-
return false;
4517-
4518-
// Drop derived-via-concrete requirements.
4519-
if (!remainingConcrete)
4520-
remainingConcrete = constraint;
4521-
4522-
++NumSelfDerived;
4523-
return true;
4524-
};
4525-
4526-
// Erase all self-derived constraints.
4527-
entry.second.erase(
4528-
std::remove_if(entry.second.begin(), entry.second.end(), isSelfDerived),
4529-
entry.second.end());
4530-
4531-
// If we found any mininal sources, add them now, avoiding introducing any
4532-
// redundant sources.
4533-
if (!minimalSources.empty()) {
4534-
// Collect the sources we already know about.
4535-
SmallPtrSet<const RequirementSource *, 4> sources;
4536-
for (const auto &constraint : entry.second) {
4537-
sources.insert(constraint.source);
4538-
}
4539-
4540-
// Add any minimal sources we didn't know about.
4541-
for (const auto &minimalSource : minimalSources) {
4542-
if (sources.insert(minimalSource.source).second) {
4543-
entry.second.push_back(minimalSource);
4544-
}
4545-
}
4546-
}
4547-
4548-
// If we only had concrete conformances, put one back.
4549-
if (entry.second.empty() && remainingConcrete)
4550-
entry.second.push_back(*remainingConcrete);
4551-
4552-
assert(!entry.second.empty() && "All constraints were self-derived!");
4489+
// Remove any self-derived constraints.
4490+
removeSelfDerived(entry.second, entry.first);
45534491

45544492
checkConstraintList<ProtocolDecl *, ProtocolDecl *>(
45554493
genericParams, entry.second,
@@ -4788,7 +4726,8 @@ void GenericSignatureBuilder::checkSameTypeConstraints(
47884726
auto &constraints = entry.second;
47894727

47904728
// Remove self-derived constraints.
4791-
if (removeSelfDerived(constraints, /*dropDerivedViaConcrete=*/false))
4729+
if (removeSelfDerived(constraints, /*proto=*/nullptr,
4730+
/*dropDerivedViaConcrete=*/false))
47924731
anyDerivedViaConcrete = true;
47934732

47944733
// Sort the constraints, so we get a deterministic ordering of diagnostics.
@@ -4872,7 +4811,7 @@ void GenericSignatureBuilder::checkSameTypeConstraints(
48724811
auto &constraints = entry.second;
48734812

48744813
// Remove derived-via-concrete constraints.
4875-
(void)removeSelfDerived(constraints);
4814+
(void)removeSelfDerived(constraints, /*proto=*/nullptr);
48764815
anyDerivedViaConcrete = true;
48774816
}
48784817
}

0 commit comments

Comments
 (0)