mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[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:
@@ -23,6 +23,7 @@
|
||||
#include "swift/AST/Decl.h"
|
||||
#include "swift/AST/DiagnosticEngine.h"
|
||||
#include "swift/AST/Identifier.h"
|
||||
#include "swift/AST/ProtocolConformanceRef.h"
|
||||
#include "swift/AST/Types.h"
|
||||
#include "swift/AST/TypeLoc.h"
|
||||
#include "swift/AST/TypeRepr.h"
|
||||
@@ -286,6 +287,15 @@ private:
|
||||
FloatingRequirementSource source,
|
||||
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
|
||||
/// of the given potential archetype (if present) to the given protocol.
|
||||
///
|
||||
@@ -293,9 +303,8 @@ private:
|
||||
/// queried.
|
||||
///
|
||||
/// \param proto The protocol to which we are establishing conformance.
|
||||
const RequirementSource *resolveSuperConformance(
|
||||
GenericSignatureBuilder::PotentialArchetype *pa,
|
||||
ProtocolDecl *proto);
|
||||
const RequirementSource *resolveSuperConformance(PotentialArchetype *pa,
|
||||
ProtocolDecl *proto);
|
||||
|
||||
/// \brief Add a new conformance requirement specifying that the given
|
||||
/// potential archetype conforms to the given protocol.
|
||||
@@ -775,7 +784,7 @@ public:
|
||||
|
||||
/// 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.
|
||||
Superclass,
|
||||
|
||||
@@ -826,7 +835,7 @@ private:
|
||||
TypeBase *type;
|
||||
|
||||
/// A protocol conformance used to satisfy the requirement.
|
||||
ProtocolConformance *conformance;
|
||||
void *conformance;
|
||||
|
||||
/// An associated type to which a requirement is being applied.
|
||||
AssociatedTypeDecl *assocType;
|
||||
@@ -943,7 +952,7 @@ public:
|
||||
}
|
||||
|
||||
RequirementSource(Kind kind, const RequirementSource *parent,
|
||||
ProtocolConformance *conformance)
|
||||
ProtocolConformanceRef conformance)
|
||||
: kind(kind), storageKind(StorageKind::ProtocolConformance),
|
||||
hasTrailingWrittenRequirementLoc(false),
|
||||
usesRequirementSignature(false), parent(parent) {
|
||||
@@ -952,7 +961,7 @@ public:
|
||||
assert(isAcceptableStorageKind(kind, storageKind) &&
|
||||
"RequirementSource kind/storageKind mismatch");
|
||||
|
||||
storage.conformance = conformance;
|
||||
storage.conformance = conformance.getOpaqueValue();
|
||||
}
|
||||
|
||||
RequirementSource(Kind kind, const RequirementSource *parent,
|
||||
@@ -1019,13 +1028,14 @@ public:
|
||||
/// A requirement source that describes that a requirement that is resolved
|
||||
/// via a superclass requirement.
|
||||
const RequirementSource *viaSuperclass(
|
||||
GenericSignatureBuilder &builder,
|
||||
ProtocolConformance *conformance) const;
|
||||
GenericSignatureBuilder &builder,
|
||||
ProtocolConformanceRef conformance) const;
|
||||
|
||||
/// A requirement source that describes that a requirement that is resolved
|
||||
/// via a same-type-to-concrete requirement.
|
||||
const RequirementSource *viaConcrete(GenericSignatureBuilder &builder,
|
||||
ProtocolConformance *conformance) const;
|
||||
const RequirementSource *viaConcrete(
|
||||
GenericSignatureBuilder &builder,
|
||||
ProtocolConformanceRef conformance) const;
|
||||
|
||||
/// A constraint source that describes that a constraint that is resolved
|
||||
/// for a nested type via a constraint on its parent.
|
||||
@@ -1126,9 +1136,9 @@ public:
|
||||
ProtocolDecl *getProtocolDecl() const;
|
||||
|
||||
/// Retrieve the protocol conformance for this requirement, if there is one.
|
||||
ProtocolConformance *getProtocolConformance() const {
|
||||
if (storageKind != StorageKind::ProtocolConformance) return nullptr;
|
||||
return storage.conformance;
|
||||
ProtocolConformanceRef getProtocolConformance() const {
|
||||
assert(storageKind == StorageKind::ProtocolConformance);
|
||||
return ProtocolConformanceRef::getFromOpaqueValue(storage.conformance);
|
||||
}
|
||||
|
||||
/// Retrieve the associated type declaration for this requirement, if there
|
||||
|
||||
@@ -544,20 +544,21 @@ const RequirementSource *RequirementSource::viaProtocolRequirement(
|
||||
}
|
||||
|
||||
const RequirementSource *RequirementSource::viaSuperclass(
|
||||
GenericSignatureBuilder &builder,
|
||||
ProtocolConformance *conformance) const {
|
||||
GenericSignatureBuilder &builder,
|
||||
ProtocolConformanceRef conformance) const {
|
||||
REQUIREMENT_SOURCE_FACTORY_BODY(
|
||||
(nodeID, Superclass, this, conformance,
|
||||
(nodeID, Superclass, this, conformance.getOpaqueValue(),
|
||||
nullptr, nullptr),
|
||||
(Superclass, this, conformance),
|
||||
0, WrittenRequirementLoc());
|
||||
}
|
||||
|
||||
const RequirementSource *RequirementSource::viaConcrete(
|
||||
GenericSignatureBuilder &builder,
|
||||
ProtocolConformance *conformance) const {
|
||||
GenericSignatureBuilder &builder,
|
||||
ProtocolConformanceRef conformance) const {
|
||||
REQUIREMENT_SOURCE_FACTORY_BODY(
|
||||
(nodeID, Concrete, this, conformance, nullptr, nullptr),
|
||||
(nodeID, Concrete, this, conformance.getOpaqueValue(),
|
||||
nullptr, nullptr),
|
||||
(Concrete, this, conformance),
|
||||
0, WrittenRequirementLoc());
|
||||
}
|
||||
@@ -679,10 +680,7 @@ ProtocolDecl *RequirementSource::getProtocolDecl() const {
|
||||
return nullptr;
|
||||
|
||||
case StorageKind::ProtocolConformance:
|
||||
if (storage.conformance)
|
||||
return storage.conformance->getProtocol();
|
||||
|
||||
return nullptr;
|
||||
return getProtocolConformance().getRequirement();
|
||||
|
||||
case StorageKind::AssociatedTypeDecl:
|
||||
return storage.assocType->getProtocol();
|
||||
@@ -873,12 +871,16 @@ void RequirementSource::print(llvm::raw_ostream &out,
|
||||
}
|
||||
break;
|
||||
|
||||
case StorageKind::ProtocolConformance:
|
||||
if (storage.conformance) {
|
||||
out << " (" << storage.conformance->getType()->getString() << ": "
|
||||
<< storage.conformance->getProtocol()->getName() << ")";
|
||||
case StorageKind::ProtocolConformance: {
|
||||
auto conformance = getProtocolConformance();
|
||||
if (conformance.isConcrete()) {
|
||||
out << " (" << conformance.getConcrete()->getType()->getString() << ": "
|
||||
<< conformance.getConcrete()->getProtocol()->getName() << ")";
|
||||
} else {
|
||||
out << " (abstract " << conformance.getRequirement()->getName() << ")";
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case StorageKind::AssociatedTypeDecl:
|
||||
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(
|
||||
GenericSignatureBuilder::PotentialArchetype *pa,
|
||||
ProtocolDecl *proto) {
|
||||
PotentialArchetype *pa,
|
||||
ProtocolDecl *proto) {
|
||||
// Get the superclass constraint.
|
||||
Type superclass = pa->getSuperclass();
|
||||
if (!superclass) return nullptr;
|
||||
@@ -1332,7 +1365,7 @@ const RequirementSource *GenericSignatureBuilder::resolveSuperConformance(
|
||||
superclassSource = paEquivClass->superclassConstraints.front().source;
|
||||
|
||||
superclassSource =
|
||||
superclassSource->viaSuperclass(*this, conformance->getConcrete());
|
||||
superclassSource->viaSuperclass(*this, *conformance);
|
||||
paEquivClass->conformsTo[proto].push_back({pa, proto, superclassSource});
|
||||
++NumConformanceConstraints;
|
||||
return superclassSource;
|
||||
@@ -1376,7 +1409,7 @@ static void maybeAddSameTypeRequirementForNestedType(
|
||||
if (!assocType) return;
|
||||
|
||||
// Dig out the type witness.
|
||||
auto superConformance = superSource->getProtocolConformance();
|
||||
auto superConformance = superSource->getProtocolConformance().getConcrete();
|
||||
auto concreteType =
|
||||
superConformance->getTypeWitness(assocType, builder.getLazyResolver());
|
||||
if (!concreteType) return;
|
||||
@@ -1421,9 +1454,13 @@ bool PotentialArchetype::addConformance(ProtocolDecl *proto,
|
||||
++NumConformanceConstraints;
|
||||
++NumConformances;
|
||||
|
||||
// Determine whether there is a superclass constraint where the
|
||||
// superclass conforms to this protocol.
|
||||
(void)getBuilder()->resolveSuperConformance(this, proto);
|
||||
// If there is a concrete type that resolves this conformance requirement,
|
||||
// record the conformance.
|
||||
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.
|
||||
for (auto &nested : NestedTypes) {
|
||||
@@ -1664,12 +1701,11 @@ namespace {
|
||||
// parent PA that has a concrete type.
|
||||
static void concretizeNestedTypeFromConcreteParent(
|
||||
GenericSignatureBuilder::PotentialArchetype *parent,
|
||||
const RequirementSource *parentConcreteSource,
|
||||
GenericSignatureBuilder::PotentialArchetype *nestedPA,
|
||||
GenericSignatureBuilder &builder,
|
||||
llvm::function_ref<ProtocolConformanceRef(ProtocolDecl *)>
|
||||
lookupConformance) {
|
||||
auto concreteParent = parent->getConcreteType();
|
||||
GenericSignatureBuilder &builder) {
|
||||
auto parentEquiv = parent->getEquivalenceClassIfPresent();
|
||||
assert(parentEquiv && "can't have a concrete type without an equiv class");
|
||||
auto concreteParent = parentEquiv->concreteType;
|
||||
assert(concreteParent &&
|
||||
"attempting to resolve concrete nested type of non-concrete PA");
|
||||
|
||||
@@ -1678,11 +1714,21 @@ static void concretizeNestedTypeFromConcreteParent(
|
||||
auto assocType = nestedPA->getResolvedAssociatedType();
|
||||
if (!assocType) return;
|
||||
|
||||
auto source = parentConcreteSource->viaConcrete(builder, /*FIXME: */nullptr)
|
||||
->viaParent(builder, assocType);
|
||||
auto proto = assocType->getProtocol();
|
||||
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.
|
||||
auto conformance = lookupConformance(assocType->getProtocol());
|
||||
// Error condition: parent did not conform to this protocol, so they
|
||||
if (!parentConcreteSource) return;
|
||||
|
||||
auto source = parentConcreteSource->viaParent(builder, assocType);
|
||||
auto conformance = parentConcreteSource->getProtocolConformance();
|
||||
|
||||
Type witnessType;
|
||||
if (conformance.isConcrete()) {
|
||||
@@ -2059,21 +2105,7 @@ PotentialArchetype *PotentialArchetype::updateNestedTypeForConformance(
|
||||
// FIXME: This feels like massive overkill. Why do we have to loop?
|
||||
if (isConcreteType()) {
|
||||
for (auto equivT : getRepresentative()->getEquivalenceClassMembers()) {
|
||||
concretizeNestedTypeFromConcreteParent(
|
||||
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;
|
||||
});
|
||||
concretizeNestedTypeFromConcreteParent(equivT, resultPA, builder);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3379,49 +3411,26 @@ ConstraintResult GenericSignatureBuilder::addSameTypeRequirementToConcrete(
|
||||
// Record the requirement.
|
||||
equivClass->concreteType = Concrete;
|
||||
|
||||
// Make sure the concrete type fulfills the requirements on the archetype.
|
||||
// FIXME: Move later...
|
||||
DenseMap<ProtocolDecl *, ProtocolConformanceRef> conformances;
|
||||
CanType depTy = rep->getDependentType({ }, /*allowUnresolved=*/true)
|
||||
->getCanonicalType();
|
||||
// Make sure the concrete type fulfills the conformance requirements of
|
||||
// this equivalence class.
|
||||
for (auto protocol : rep->getConformsTo()) {
|
||||
auto conformance =
|
||||
getLookupConformanceFn()(depTy, Concrete,
|
||||
protocol->getDeclaredInterfaceType()
|
||||
->castTo<ProtocolType>());
|
||||
if (!conformance) {
|
||||
if (!Concrete->hasError()) {
|
||||
if (!resolveConcreteConformance(rep, protocol)) {
|
||||
if (!Concrete->hasError() && Source->getLoc().isValid()) {
|
||||
Diags.diagnose(Source->getLoc(),
|
||||
diag::requires_generic_param_same_type_does_not_conform,
|
||||
Concrete, protocol->getName());
|
||||
}
|
||||
|
||||
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
|
||||
// will be "concretized" as they are constructed, in getNestedType).
|
||||
for (auto equivT : rep->getEquivalenceClassMembers()) {
|
||||
for (auto nested : equivT->getNestedTypes()) {
|
||||
concretizeNestedTypeFromConcreteParent(
|
||||
equivT, Source, nested.second.front(), *this,
|
||||
[&](ProtocolDecl *proto) -> ProtocolConformanceRef {
|
||||
return conformances.find(proto)->second;
|
||||
});
|
||||
concretizeNestedTypeFromConcreteParent(equivT, nested.second.front(),
|
||||
*this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -6,5 +6,5 @@
|
||||
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
||||
|
||||
// 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
|
||||
Reference in New Issue
Block a user