[GSB] Improve handling of conformances resolved by concrete types.

Centralize and simplify the handling of conformance requirements
resolved by same-type-to-concrete requirements in a few ways:

* Always store a ProtocolConformanceRef in via-superclass and
  via-concrete requirement sources, so we never lose this information.

* When concretizing a nested type based on its parent, use the
  via-concrete conformance information rather than performing lookup
  again, simplifying this operation considerably and avoiding
  redundant lookups.

* When adding a conformance requirement to a potential archetype that
  is equivalent to a concrete type, attempt to find and record the
  conformance.

Fixes SR-4295 / rdar://problem/31372308.
This commit is contained in:
Doug Gregor
2017-06-06 11:13:14 -07:00
parent ffea1b35ca
commit 52e52b564b
4 changed files with 110 additions and 91 deletions

View File

@@ -23,6 +23,7 @@
#include "swift/AST/Decl.h" #include "swift/AST/Decl.h"
#include "swift/AST/DiagnosticEngine.h" #include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/Identifier.h" #include "swift/AST/Identifier.h"
#include "swift/AST/ProtocolConformanceRef.h"
#include "swift/AST/Types.h" #include "swift/AST/Types.h"
#include "swift/AST/TypeLoc.h" #include "swift/AST/TypeLoc.h"
#include "swift/AST/TypeRepr.h" #include "swift/AST/TypeRepr.h"
@@ -286,6 +287,15 @@ private:
FloatingRequirementSource source, FloatingRequirementSource source,
UnresolvedHandlingKind unresolvedHandling); UnresolvedHandlingKind unresolvedHandling);
/// Resolve the conformance of the given potential archetype to
/// the given protocol when the potential archetype is known to be equivalent
/// to a concrete type.
///
/// \returns the requirement source for the resolved conformance, or nullptr
/// if the conformance could not be resolved.
const RequirementSource *resolveConcreteConformance(PotentialArchetype *pa,
ProtocolDecl *proto);
/// Retrieve the constraint source conformance for the superclass constraint /// Retrieve the constraint source conformance for the superclass constraint
/// of the given potential archetype (if present) to the given protocol. /// of the given potential archetype (if present) to the given protocol.
/// ///
@@ -293,9 +303,8 @@ private:
/// queried. /// queried.
/// ///
/// \param proto The protocol to which we are establishing conformance. /// \param proto The protocol to which we are establishing conformance.
const RequirementSource *resolveSuperConformance( const RequirementSource *resolveSuperConformance(PotentialArchetype *pa,
GenericSignatureBuilder::PotentialArchetype *pa, ProtocolDecl *proto);
ProtocolDecl *proto);
/// \brief Add a new conformance requirement specifying that the given /// \brief Add a new conformance requirement specifying that the given
/// potential archetype conforms to the given protocol. /// potential archetype conforms to the given protocol.
@@ -775,7 +784,7 @@ public:
/// A requirement that was resolved via a superclass requirement. /// A requirement that was resolved via a superclass requirement.
/// ///
/// This stores the \c ProtocolConformance* used to resolve the /// This stores the \c ProtocolConformanceRef used to resolve the
/// requirement. /// requirement.
Superclass, Superclass,
@@ -826,7 +835,7 @@ private:
TypeBase *type; TypeBase *type;
/// A protocol conformance used to satisfy the requirement. /// A protocol conformance used to satisfy the requirement.
ProtocolConformance *conformance; void *conformance;
/// An associated type to which a requirement is being applied. /// An associated type to which a requirement is being applied.
AssociatedTypeDecl *assocType; AssociatedTypeDecl *assocType;
@@ -943,7 +952,7 @@ public:
} }
RequirementSource(Kind kind, const RequirementSource *parent, RequirementSource(Kind kind, const RequirementSource *parent,
ProtocolConformance *conformance) ProtocolConformanceRef conformance)
: kind(kind), storageKind(StorageKind::ProtocolConformance), : kind(kind), storageKind(StorageKind::ProtocolConformance),
hasTrailingWrittenRequirementLoc(false), hasTrailingWrittenRequirementLoc(false),
usesRequirementSignature(false), parent(parent) { usesRequirementSignature(false), parent(parent) {
@@ -952,7 +961,7 @@ public:
assert(isAcceptableStorageKind(kind, storageKind) && assert(isAcceptableStorageKind(kind, storageKind) &&
"RequirementSource kind/storageKind mismatch"); "RequirementSource kind/storageKind mismatch");
storage.conformance = conformance; storage.conformance = conformance.getOpaqueValue();
} }
RequirementSource(Kind kind, const RequirementSource *parent, RequirementSource(Kind kind, const RequirementSource *parent,
@@ -1019,13 +1028,14 @@ public:
/// A requirement source that describes that a requirement that is resolved /// A requirement source that describes that a requirement that is resolved
/// via a superclass requirement. /// via a superclass requirement.
const RequirementSource *viaSuperclass( const RequirementSource *viaSuperclass(
GenericSignatureBuilder &builder, GenericSignatureBuilder &builder,
ProtocolConformance *conformance) const; ProtocolConformanceRef conformance) const;
/// A requirement source that describes that a requirement that is resolved /// A requirement source that describes that a requirement that is resolved
/// via a same-type-to-concrete requirement. /// via a same-type-to-concrete requirement.
const RequirementSource *viaConcrete(GenericSignatureBuilder &builder, const RequirementSource *viaConcrete(
ProtocolConformance *conformance) const; GenericSignatureBuilder &builder,
ProtocolConformanceRef conformance) const;
/// A constraint source that describes that a constraint that is resolved /// A constraint source that describes that a constraint that is resolved
/// for a nested type via a constraint on its parent. /// for a nested type via a constraint on its parent.
@@ -1126,9 +1136,9 @@ public:
ProtocolDecl *getProtocolDecl() const; ProtocolDecl *getProtocolDecl() const;
/// Retrieve the protocol conformance for this requirement, if there is one. /// Retrieve the protocol conformance for this requirement, if there is one.
ProtocolConformance *getProtocolConformance() const { ProtocolConformanceRef getProtocolConformance() const {
if (storageKind != StorageKind::ProtocolConformance) return nullptr; assert(storageKind == StorageKind::ProtocolConformance);
return storage.conformance; return ProtocolConformanceRef::getFromOpaqueValue(storage.conformance);
} }
/// Retrieve the associated type declaration for this requirement, if there /// Retrieve the associated type declaration for this requirement, if there

View File

@@ -544,20 +544,21 @@ const RequirementSource *RequirementSource::viaProtocolRequirement(
} }
const RequirementSource *RequirementSource::viaSuperclass( const RequirementSource *RequirementSource::viaSuperclass(
GenericSignatureBuilder &builder, GenericSignatureBuilder &builder,
ProtocolConformance *conformance) const { ProtocolConformanceRef conformance) const {
REQUIREMENT_SOURCE_FACTORY_BODY( REQUIREMENT_SOURCE_FACTORY_BODY(
(nodeID, Superclass, this, conformance, (nodeID, Superclass, this, conformance.getOpaqueValue(),
nullptr, nullptr), nullptr, nullptr),
(Superclass, this, conformance), (Superclass, this, conformance),
0, WrittenRequirementLoc()); 0, WrittenRequirementLoc());
} }
const RequirementSource *RequirementSource::viaConcrete( const RequirementSource *RequirementSource::viaConcrete(
GenericSignatureBuilder &builder, GenericSignatureBuilder &builder,
ProtocolConformance *conformance) const { ProtocolConformanceRef conformance) const {
REQUIREMENT_SOURCE_FACTORY_BODY( REQUIREMENT_SOURCE_FACTORY_BODY(
(nodeID, Concrete, this, conformance, nullptr, nullptr), (nodeID, Concrete, this, conformance.getOpaqueValue(),
nullptr, nullptr),
(Concrete, this, conformance), (Concrete, this, conformance),
0, WrittenRequirementLoc()); 0, WrittenRequirementLoc());
} }
@@ -679,10 +680,7 @@ ProtocolDecl *RequirementSource::getProtocolDecl() const {
return nullptr; return nullptr;
case StorageKind::ProtocolConformance: case StorageKind::ProtocolConformance:
if (storage.conformance) return getProtocolConformance().getRequirement();
return storage.conformance->getProtocol();
return nullptr;
case StorageKind::AssociatedTypeDecl: case StorageKind::AssociatedTypeDecl:
return storage.assocType->getProtocol(); return storage.assocType->getProtocol();
@@ -873,12 +871,16 @@ void RequirementSource::print(llvm::raw_ostream &out,
} }
break; break;
case StorageKind::ProtocolConformance: case StorageKind::ProtocolConformance: {
if (storage.conformance) { auto conformance = getProtocolConformance();
out << " (" << storage.conformance->getType()->getString() << ": " if (conformance.isConcrete()) {
<< storage.conformance->getProtocol()->getName() << ")"; out << " (" << conformance.getConcrete()->getType()->getString() << ": "
<< conformance.getConcrete()->getProtocol()->getName() << ")";
} else {
out << " (abstract " << conformance.getRequirement()->getName() << ")";
} }
break; break;
}
case StorageKind::AssociatedTypeDecl: case StorageKind::AssociatedTypeDecl:
out << " (" << storage.assocType->getProtocol()->getName() out << " (" << storage.assocType->getProtocol()->getName()
@@ -1305,9 +1307,40 @@ ConstraintResult GenericSignatureBuilder::handleUnresolvedRequirement(
} }
} }
const RequirementSource *
GenericSignatureBuilder::resolveConcreteConformance(PotentialArchetype *pa,
ProtocolDecl *proto) {
auto concrete = pa->getConcreteType();
if (!concrete) return nullptr;
// Lookup the conformance of the concrete type to this protocol.
auto conformance =
getLookupConformanceFn()(pa->getDependentType({ }, /*allowUnresolved=*/true)
->getCanonicalType(),
concrete,
proto->getDeclaredInterfaceType()
->castTo<ProtocolType>());
if (!conformance) return nullptr;
// Conformance to this protocol is redundant; update the requirement source
// appropriately.
auto paEquivClass = pa->getOrCreateEquivalenceClass();
const RequirementSource *concreteSource;
if (auto writtenSource =
paEquivClass->findAnyConcreteConstraintAsWritten(pa))
concreteSource = writtenSource->source;
else
concreteSource = paEquivClass->concreteTypeConstraints.front().source;
concreteSource = concreteSource->viaConcrete(*this, *conformance);
paEquivClass->conformsTo[proto].push_back({pa, proto, concreteSource});
++NumConformanceConstraints;
return concreteSource;
}
const RequirementSource *GenericSignatureBuilder::resolveSuperConformance( const RequirementSource *GenericSignatureBuilder::resolveSuperConformance(
GenericSignatureBuilder::PotentialArchetype *pa, PotentialArchetype *pa,
ProtocolDecl *proto) { ProtocolDecl *proto) {
// Get the superclass constraint. // Get the superclass constraint.
Type superclass = pa->getSuperclass(); Type superclass = pa->getSuperclass();
if (!superclass) return nullptr; if (!superclass) return nullptr;
@@ -1332,7 +1365,7 @@ const RequirementSource *GenericSignatureBuilder::resolveSuperConformance(
superclassSource = paEquivClass->superclassConstraints.front().source; superclassSource = paEquivClass->superclassConstraints.front().source;
superclassSource = superclassSource =
superclassSource->viaSuperclass(*this, conformance->getConcrete()); superclassSource->viaSuperclass(*this, *conformance);
paEquivClass->conformsTo[proto].push_back({pa, proto, superclassSource}); paEquivClass->conformsTo[proto].push_back({pa, proto, superclassSource});
++NumConformanceConstraints; ++NumConformanceConstraints;
return superclassSource; return superclassSource;
@@ -1376,7 +1409,7 @@ static void maybeAddSameTypeRequirementForNestedType(
if (!assocType) return; if (!assocType) return;
// Dig out the type witness. // Dig out the type witness.
auto superConformance = superSource->getProtocolConformance(); auto superConformance = superSource->getProtocolConformance().getConcrete();
auto concreteType = auto concreteType =
superConformance->getTypeWitness(assocType, builder.getLazyResolver()); superConformance->getTypeWitness(assocType, builder.getLazyResolver());
if (!concreteType) return; if (!concreteType) return;
@@ -1421,9 +1454,13 @@ bool PotentialArchetype::addConformance(ProtocolDecl *proto,
++NumConformanceConstraints; ++NumConformanceConstraints;
++NumConformances; ++NumConformances;
// Determine whether there is a superclass constraint where the // If there is a concrete type that resolves this conformance requirement,
// superclass conforms to this protocol. // record the conformance.
(void)getBuilder()->resolveSuperConformance(this, proto); if (!builder.resolveConcreteConformance(this, proto)) {
// Otherwise, determine whether there is a superclass constraint where the
// superclass conforms to this protocol.
(void)builder.resolveSuperConformance(this, proto);
}
// Resolve any existing nested types that need it. // Resolve any existing nested types that need it.
for (auto &nested : NestedTypes) { for (auto &nested : NestedTypes) {
@@ -1664,12 +1701,11 @@ namespace {
// parent PA that has a concrete type. // parent PA that has a concrete type.
static void concretizeNestedTypeFromConcreteParent( static void concretizeNestedTypeFromConcreteParent(
GenericSignatureBuilder::PotentialArchetype *parent, GenericSignatureBuilder::PotentialArchetype *parent,
const RequirementSource *parentConcreteSource,
GenericSignatureBuilder::PotentialArchetype *nestedPA, GenericSignatureBuilder::PotentialArchetype *nestedPA,
GenericSignatureBuilder &builder, GenericSignatureBuilder &builder) {
llvm::function_ref<ProtocolConformanceRef(ProtocolDecl *)> auto parentEquiv = parent->getEquivalenceClassIfPresent();
lookupConformance) { assert(parentEquiv && "can't have a concrete type without an equiv class");
auto concreteParent = parent->getConcreteType(); auto concreteParent = parentEquiv->concreteType;
assert(concreteParent && assert(concreteParent &&
"attempting to resolve concrete nested type of non-concrete PA"); "attempting to resolve concrete nested type of non-concrete PA");
@@ -1678,11 +1714,21 @@ static void concretizeNestedTypeFromConcreteParent(
auto assocType = nestedPA->getResolvedAssociatedType(); auto assocType = nestedPA->getResolvedAssociatedType();
if (!assocType) return; if (!assocType) return;
auto source = parentConcreteSource->viaConcrete(builder, /*FIXME: */nullptr) auto proto = assocType->getProtocol();
->viaParent(builder, assocType); assert(parentEquiv->conformsTo.count(proto) > 0 &&
"No conformance requirement");
const RequirementSource *parentConcreteSource = nullptr;
for (const auto &constraint : parentEquiv->conformsTo.find(proto)->second) {
if (constraint.source->kind == RequirementSource::Concrete) {
parentConcreteSource = constraint.source;
}
}
// FIXME: Get the conformance from the parent. // Error condition: parent did not conform to this protocol, so they
auto conformance = lookupConformance(assocType->getProtocol()); if (!parentConcreteSource) return;
auto source = parentConcreteSource->viaParent(builder, assocType);
auto conformance = parentConcreteSource->getProtocolConformance();
Type witnessType; Type witnessType;
if (conformance.isConcrete()) { if (conformance.isConcrete()) {
@@ -2059,21 +2105,7 @@ PotentialArchetype *PotentialArchetype::updateNestedTypeForConformance(
// FIXME: This feels like massive overkill. Why do we have to loop? // FIXME: This feels like massive overkill. Why do we have to loop?
if (isConcreteType()) { if (isConcreteType()) {
for (auto equivT : getRepresentative()->getEquivalenceClassMembers()) { for (auto equivT : getRepresentative()->getEquivalenceClassMembers()) {
concretizeNestedTypeFromConcreteParent( concretizeNestedTypeFromConcreteParent(equivT, resultPA, builder);
equivT, RequirementSource::forNestedTypeNameMatch(this),
resultPA, builder,
[&](ProtocolDecl *proto) -> ProtocolConformanceRef {
auto depTy = resultPA->getDependentType({},
/*allowUnresolved=*/true)
->getCanonicalType();
auto protocolTy =
proto->getDeclaredInterfaceType()->castTo<ProtocolType>();
auto conformance = builder.getLookupConformanceFn()(
depTy, getConcreteType(), protocolTy);
assert(conformance &&
"failed to find PA's conformance to known protocol");
return *conformance;
});
} }
} }
} }
@@ -3379,49 +3411,26 @@ ConstraintResult GenericSignatureBuilder::addSameTypeRequirementToConcrete(
// Record the requirement. // Record the requirement.
equivClass->concreteType = Concrete; equivClass->concreteType = Concrete;
// Make sure the concrete type fulfills the requirements on the archetype. // Make sure the concrete type fulfills the conformance requirements of
// FIXME: Move later... // this equivalence class.
DenseMap<ProtocolDecl *, ProtocolConformanceRef> conformances;
CanType depTy = rep->getDependentType({ }, /*allowUnresolved=*/true)
->getCanonicalType();
for (auto protocol : rep->getConformsTo()) { for (auto protocol : rep->getConformsTo()) {
auto conformance = if (!resolveConcreteConformance(rep, protocol)) {
getLookupConformanceFn()(depTy, Concrete, if (!Concrete->hasError() && Source->getLoc().isValid()) {
protocol->getDeclaredInterfaceType()
->castTo<ProtocolType>());
if (!conformance) {
if (!Concrete->hasError()) {
Diags.diagnose(Source->getLoc(), Diags.diagnose(Source->getLoc(),
diag::requires_generic_param_same_type_does_not_conform, diag::requires_generic_param_same_type_does_not_conform,
Concrete, protocol->getName()); Concrete, protocol->getName());
} }
return ConstraintResult::Conflicting; return ConstraintResult::Conflicting;
} }
conformances.insert({protocol, *conformance});
// Abstract conformances are acceptable for existential types.
assert(conformance->isConcrete() || Concrete->isExistentialType());
// Update the requirement source now that we know it's concrete.
// FIXME: Bad concrete source info.
auto concreteSource = Source->viaConcrete(*this,
conformance->isConcrete()
? conformance->getConcrete()
: nullptr);
equivClass->conformsTo[protocol].push_back({T, protocol, concreteSource});
++NumConformanceConstraints;
} }
// Eagerly resolve any existing nested types to their concrete forms (others // Eagerly resolve any existing nested types to their concrete forms (others
// will be "concretized" as they are constructed, in getNestedType). // will be "concretized" as they are constructed, in getNestedType).
for (auto equivT : rep->getEquivalenceClassMembers()) { for (auto equivT : rep->getEquivalenceClassMembers()) {
for (auto nested : equivT->getNestedTypes()) { for (auto nested : equivT->getNestedTypes()) {
concretizeNestedTypeFromConcreteParent( concretizeNestedTypeFromConcreteParent(equivT, nested.second.front(),
equivT, Source, nested.second.front(), *this, *this);
[&](ProtocolDecl *proto) -> ProtocolConformanceRef {
return conformances.find(proto)->second;
});
} }
} }

View File

@@ -1,4 +1,4 @@
// RUN: not --crash %target-swift-frontend -emit-ir -primary-file %s // RUN: not %target-swift-frontend -emit-ir -primary-file %s
// REQUIRES: asserts // REQUIRES: asserts

View File

@@ -6,5 +6,5 @@
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
// REQUIRES: asserts // REQUIRES: asserts
// RUN: not --crash %target-swift-frontend %s -emit-ir // RUN: not %target-swift-frontend %s -emit-ir
protocol P{let c{}typealias e:RangeReplaceableCollection}extension P{typealias e:a protocol P{let c{}typealias e:RangeReplaceableCollection}extension P{typealias e:a