Skip to content

Commit 29256e5

Browse files
committed
[GenericSigBuilder] Track all same-type-to-concrete constraints.
When we see a second same-type-to-concrete constraint on a particular potential archetype, record it. Previously, we were checking it and then updating the requirement source eagerly. That won't work with proper recursion detection, and meant that we missed out on some obvious redundant-same-type-constraint diagnostics. The scheme here is to build up the equivalence classes without losing any information, and then determine which information is redundant at the end.
1 parent 6492cc3 commit 29256e5

File tree

3 files changed

+113
-83
lines changed

3 files changed

+113
-83
lines changed

include/swift/AST/GenericSignatureBuilder.h

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -736,13 +736,15 @@ class GenericSignatureBuilder::PotentialArchetype {
736736
llvm::MapVector<Identifier, llvm::TinyPtrVector<PotentialArchetype *>>
737737
NestedTypes;
738738

739-
/// The concrete type to which a this potential archetype has been
739+
/// The concrete types to which this potential archetype has been
740740
/// constrained.
741-
Type ConcreteType;
741+
///
742+
/// This vector runs parallel to ConcreteTypeSources.
743+
llvm::TinyPtrVector<Type> concreteTypes;
742744

743-
/// The source of the concrete type requirement, if one was written
744-
/// on this potential archetype.
745-
const RequirementSource *ConcreteTypeSource = nullptr;
745+
/// The source of the concrete type requirements that were written on
746+
/// this potential archetype.
747+
llvm::TinyPtrVector<const RequirementSource *> concreteTypeSources;
746748

747749
/// Whether this is an unresolved nested type.
748750
unsigned isUnresolvedNestedType : 1;
@@ -971,15 +973,21 @@ class GenericSignatureBuilder::PotentialArchetype {
971973
SameTypeConstraints.end());
972974
}
973975

974-
/// Retrieve the concrete type source as written on this potential archetype.
975-
const RequirementSource *getConcreteTypeSourceAsWritten() const {
976-
return ConcreteTypeSource;
976+
/// Retrieve the concrete types as written on this potential archetype.
977+
const llvm::TinyPtrVector<Type>& getConcreteTypesAsWritten() const {
978+
return concreteTypes;
979+
}
980+
981+
/// Retrieve the concrete type sources as written on this potential archetype.
982+
ArrayRef<const RequirementSource *> getConcreteTypeSourcesAsWritten() const {
983+
return concreteTypeSources;
977984
}
978985

979986
/// Find a source of the same-type constraint that maps this potential
980987
/// archetype to a concrete type somewhere in the equivalence class of this
981-
/// type.
982-
const RequirementSource *findAnyConcreteTypeSourceAsWritten() const;
988+
/// type along with the concrete type that was written there.
989+
Optional<std::pair<Type, const RequirementSource *>>
990+
findAnyConcreteTypeSourceAsWritten() const;
983991

984992
/// \brief Retrieve (or create) a nested type with the given name.
985993
PotentialArchetype *getNestedType(Identifier Name,

lib/AST/GenericSignatureBuilder.cpp

Lines changed: 92 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -501,24 +501,38 @@ void GenericSignatureBuilder::PotentialArchetype::resolveAssociatedType(
501501
--builder.Impl->NumUnresolvedNestedTypes;
502502
}
503503

504-
const RequirementSource *
504+
Optional<std::pair<Type, const RequirementSource *>>
505505
PotentialArchetype::findAnyConcreteTypeSourceAsWritten() const {
506-
// If we have a concrete type source, use that.
507-
if (ConcreteTypeSource && ConcreteTypeSource->getLoc().isValid())
508-
return ConcreteTypeSource;
506+
using Result = std::pair<Type, const RequirementSource *>;
507+
508+
// Local function to look for a source in the given potential archetype.
509+
auto lookInPA = [](const PotentialArchetype *pa) -> Optional<Result> {
510+
for (unsigned i : indices(pa->concreteTypeSources)) {
511+
auto source = pa->concreteTypeSources[i];
512+
if (source->getLoc().isValid())
513+
return Result(pa->concreteTypes[i], source);
514+
}
515+
516+
return None;
517+
};
518+
519+
// If we have a concrete type source with location information, use that.
520+
if (auto result = lookInPA(this))
521+
return result;
509522

510523
// If we don't have a concrete type, there's no source.
511524
auto rep = getRepresentative();
512-
if (!rep->isConcreteType()) return nullptr;
525+
if (!rep->isConcreteType()) return None;
513526

514527
// Otherwise, go look for the source.
515528
for (auto pa : rep->getEquivalenceClassMembers()) {
516-
if (pa->ConcreteTypeSource &&
517-
pa->ConcreteTypeSource->getLoc().isValid())
518-
return pa->ConcreteTypeSource;
529+
if (pa != this) {
530+
if (auto result = lookInPA(pa))
531+
return result;
532+
}
519533
}
520534

521-
return nullptr;
535+
return None;
522536
}
523537

524538
bool GenericSignatureBuilder::updateRequirementSource(
@@ -589,7 +603,7 @@ struct GenericSignatureBuilder::ResolvedType {
589603

590604
static ResolvedType forNewTypeAlias(PotentialArchetype *pa) {
591605
assert(pa->getParent() && pa->getTypeAliasDecl() &&
592-
pa->ConcreteType.isNull() &&
606+
pa->concreteTypes.empty() &&
593607
pa->getEquivalenceClassMembers().size() == 1 &&
594608
"not a new typealias");
595609
return ResolvedType(pa);
@@ -1340,17 +1354,18 @@ void GenericSignatureBuilder::PotentialArchetype::dump(llvm::raw_ostream &Out,
13401354
}
13411355

13421356
// Print concrete type.
1343-
if (ConcreteType) {
1357+
for (unsigned i : indices(concreteTypes)) {
1358+
auto concreteType = concreteTypes[i];
13441359
Out << " == ";
1345-
ConcreteType.print(Out);
1346-
if (ConcreteTypeSource) {
1347-
Out << " ";
1348-
if (!ConcreteTypeSource->isDerivedRequirement())
1349-
Out << "*";
1350-
Out << "[";
1351-
ConcreteTypeSource->print(Out, SrcMgr);
1352-
Out << "]";
1353-
}
1360+
concreteType.print(Out);
1361+
1362+
auto concreteTypeSource = concreteTypeSources[i];
1363+
Out << " ";
1364+
if (!concreteTypeSource->isDerivedRequirement())
1365+
Out << "*";
1366+
Out << "[";
1367+
concreteTypeSource->print(Out, SrcMgr);
1368+
Out << "]";
13541369
}
13551370

13561371
// Print requirements.
@@ -1673,10 +1688,10 @@ bool GenericSignatureBuilder::addSuperclassRequirement(PotentialArchetype *T,
16731688
Type concrete = T->getConcreteType();
16741689
if (!Superclass->isExactSuperclassOf(concrete, getLazyResolver())) {
16751690
if (auto source = T->findAnyConcreteTypeSourceAsWritten()) {
1676-
Diags.diagnose(source->getLoc(), diag::type_does_not_inherit,
1691+
Diags.diagnose(source->second->getLoc(), diag::type_does_not_inherit,
16771692
T->getDependentType(/*FIXME:*/{ },
16781693
/*allowUnresolved=*/true),
1679-
concrete, Superclass)
1694+
source->first, Superclass)
16801695
.highlight(Source->getLoc());
16811696
}
16821697
return true;
@@ -1912,11 +1927,8 @@ bool GenericSignatureBuilder::addSameTypeRequirementToConcrete(
19121927
Type Concrete,
19131928
const RequirementSource *Source) {
19141929
// Record the concrete type and its source.
1915-
if (!T->ConcreteType) {
1916-
// FIXME: Always record this.
1917-
T->ConcreteType = Concrete;
1918-
T->ConcreteTypeSource = Source;
1919-
}
1930+
T->concreteTypes.push_back(Concrete);
1931+
T->concreteTypeSources.push_back(Source);
19201932

19211933
// If we've already been bound to a type, match that type.
19221934
if (auto oldConcrete = T->getConcreteType()) {
@@ -2540,11 +2552,11 @@ GenericSignatureBuilder::finalize(SourceLoc loc,
25402552
// Check for recursive same-type bindings.
25412553
if (isRecursiveConcreteType(archetype, /*isSuperclass=*/false)) {
25422554
if (auto source = archetype->findAnyConcreteTypeSourceAsWritten()) {
2543-
Diags.diagnose(source->getLoc(),
2555+
Diags.diagnose(source->second->getLoc(),
25442556
diag::recursive_same_type_constraint,
25452557
archetype->getDependentType(genericParams,
25462558
/*allowUnresolved=*/true),
2547-
archetype->getConcreteType());
2559+
source->first);
25482560
}
25492561

25502562
archetype->RecursiveConcreteType = true;
@@ -2590,7 +2602,7 @@ GenericSignatureBuilder::finalize(SourceLoc loc,
25902602
// because then we don't actually have a parameter.
25912603
if (rep->getConcreteType()) {
25922604
if (auto source = rep->findAnyConcreteTypeSourceAsWritten())
2593-
Diags.diagnose(source->getLoc(),
2605+
Diags.diagnose(source->second->getLoc(),
25942606
diag::requires_generic_param_made_equal_to_concrete,
25952607
rep->getDependentType(genericParams,
25962608
/*allowUnresolved=*/true));
@@ -2718,46 +2730,49 @@ void GenericSignatureBuilder::checkRedundantConcreteTypeConstraints(
27182730
// Gather the concrete constraints within this equivalence class.
27192731
SmallVector<ConcreteConstraint, 4> concreteConstraints;
27202732
for (auto pa : representative->getEquivalenceClassMembers()) {
2721-
auto source = pa->getConcreteTypeSourceAsWritten();
2722-
if (!source) continue;
2723-
2724-
// Save this constraint.
2725-
auto constraint = ConcreteConstraint{pa, pa->ConcreteType, source};
2726-
concreteConstraints.push_back(constraint);
2727-
2728-
// Check whether this constraint is better than the best we've seen so far
2729-
// at being the representative constraint against which others will be
2730-
// compared.
2731-
if (!representativeConstraint) {
2732-
representativeConstraint = constraint;
2733-
continue;
2734-
}
2735-
2736-
// We prefer derived constraints to non-derived constraints.
2737-
bool thisIsDerived = source->isDerivedRequirement();
2738-
bool representativeIsDerived =
2739-
representativeConstraint->source->isDerivedRequirement();
2740-
if (thisIsDerived != representativeIsDerived) {
2741-
if (thisIsDerived)
2733+
auto types = pa->getConcreteTypesAsWritten();
2734+
auto sources = pa->getConcreteTypeSourcesAsWritten();
2735+
for (unsigned i : indices(types)) {
2736+
auto source = sources[i];
2737+
2738+
// Save this constraint.
2739+
auto constraint = ConcreteConstraint{pa, types[i], source};
2740+
concreteConstraints.push_back(constraint);
2741+
2742+
// Check whether this constraint is better than the best we've seen so far
2743+
// at being the representative constraint against which others will be
2744+
// compared.
2745+
if (!representativeConstraint) {
27422746
representativeConstraint = constraint;
2747+
continue;
2748+
}
27432749

2744-
continue;
2745-
}
2750+
// We prefer derived constraints to non-derived constraints.
2751+
bool thisIsDerived = source->isDerivedRequirement();
2752+
bool representativeIsDerived =
2753+
representativeConstraint->source->isDerivedRequirement();
2754+
if (thisIsDerived != representativeIsDerived) {
2755+
if (thisIsDerived)
2756+
representativeConstraint = constraint;
27462757

2747-
// We prefer constraints with locations to constraints without locations.
2748-
bool thisHasValidSourceLoc = source->getLoc().isValid();
2749-
bool representativeHasValidSourceLoc =
2750-
representativeConstraint->source->getLoc().isValid();
2751-
if (thisHasValidSourceLoc != representativeHasValidSourceLoc) {
2752-
if (thisHasValidSourceLoc)
2753-
representativeConstraint = constraint;
2758+
continue;
2759+
}
27542760

2755-
continue;
2756-
}
2761+
// We prefer constraints with locations to constraints without locations.
2762+
bool thisHasValidSourceLoc = source->getLoc().isValid();
2763+
bool representativeHasValidSourceLoc =
2764+
representativeConstraint->source->getLoc().isValid();
2765+
if (thisHasValidSourceLoc != representativeHasValidSourceLoc) {
2766+
if (thisHasValidSourceLoc)
2767+
representativeConstraint = constraint;
27572768

2758-
// Otherwise, order via the constraint itself.
2759-
if (constraint < *representativeConstraint)
2760-
representativeConstraint = constraint;
2769+
continue;
2770+
}
2771+
2772+
// Otherwise, order via the constraint itself.
2773+
if (constraint < *representativeConstraint)
2774+
representativeConstraint = constraint;
2775+
}
27612776
}
27622777

27632778
// Sort the concrete constraints, so we get deterministic ordering of
@@ -2935,20 +2950,25 @@ static SmallVector<SameTypeComponent, 2> getSameTypeComponents(
29352950

29362951
// Find the best anchor and concrete type source for this component.
29372952
PotentialArchetype *anchor = component[0];
2938-
auto bestConcreteTypeSource = anchor->getConcreteTypeSourceAsWritten();
2953+
const RequirementSource *bestConcreteTypeSource = nullptr;
2954+
auto considerNewSource = [&](const RequirementSource *source) {
2955+
if (!bestConcreteTypeSource ||
2956+
source->compare(bestConcreteTypeSource) < 0)
2957+
bestConcreteTypeSource = source;
2958+
};
29392959

2940-
for (auto componentPA : ArrayRef<PotentialArchetype *>(component).slice(1)){
2960+
if (!anchor->getConcreteTypeSourcesAsWritten().empty())
2961+
bestConcreteTypeSource = anchor->getConcreteTypeSourcesAsWritten()[0];
2962+
2963+
for (auto componentPA : ArrayRef<PotentialArchetype *>(component)) {
29412964
// Update the anchor.
29422965
if (compareDependentTypes(&componentPA, &anchor) < 0)
29432966
anchor = componentPA;
29442967

29452968
// If this potential archetype has a better concrete type source than
29462969
// the best we've seen, take it.
2947-
if (auto concreteSource = componentPA->getConcreteTypeSourceAsWritten()) {
2948-
if (!bestConcreteTypeSource ||
2949-
concreteSource->compare(bestConcreteTypeSource) < 0)
2950-
bestConcreteTypeSource = concreteSource;
2951-
}
2970+
for (auto source: componentPA->getConcreteTypeSourcesAsWritten())
2971+
considerNewSource(source);
29522972
}
29532973

29542974
// Record the anchor.

test/Constraints/same_types.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ func test6<T: Barrable>(_ t: T) -> (Y, X) where T.Bar == Y {
8787
}
8888

8989
func test7<T: Barrable>(_ t: T) -> (Y, X) where T.Bar == Y, T.Bar.Foo == X {
90+
// expected-warning@-1{{redundant same-type constraint 'T.Bar.Foo' == 'X'}}
9091
return (t.bar, t.bar.foo)
9192
}
9293

@@ -99,8 +100,9 @@ func fail4<T: Barrable>(_ t: T) -> (Y, Z)
99100

100101
func fail5<T: Barrable>(_ t: T) -> (Y, Z)
101102
where
102-
T.Bar.Foo == Z,
103+
T.Bar.Foo == Z, // expected-warning{{redundant same-type constraint 'T.Bar.Foo' == 'Z'}}
103104
T.Bar == Y { // expected-error{{associated type 'T.Bar.Foo' cannot be equal to both 'Z' and 'X'}}
105+
// expected-note@-1{{same-type constraint 'T.Bar.Foo' == 'Y.Foo' (aka 'X') implied here}}
104106
return (t.bar, t.bar.foo) // expected-error{{cannot convert return expression of type 'X' to return type 'Z'}}
105107
}
106108

0 commit comments

Comments
 (0)