Skip to content

Commit

Permalink
[GSB] Improve handling of conformances resolved by concrete types.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
DougGregor committed Jun 23, 2017
1 parent ffea1b3 commit 52e52b5
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 91 deletions.
38 changes: 24 additions & 14 deletions include/swift/AST/GenericSignatureBuilder.h
Expand Up @@ -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"
Expand Down Expand Up @@ -286,16 +287,24 @@ class GenericSignatureBuilder {
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.
///
/// \param pa The potential archetype whose superclass constraint is being
/// 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.
Expand Down Expand Up @@ -775,7 +784,7 @@ class GenericSignatureBuilder::RequirementSource final

/// 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,

Expand Down Expand Up @@ -826,7 +835,7 @@ class GenericSignatureBuilder::RequirementSource final
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;
Expand Down Expand Up @@ -943,7 +952,7 @@ class GenericSignatureBuilder::RequirementSource final
}

RequirementSource(Kind kind, const RequirementSource *parent,
ProtocolConformance *conformance)
ProtocolConformanceRef conformance)
: kind(kind), storageKind(StorageKind::ProtocolConformance),
hasTrailingWrittenRequirementLoc(false),
usesRequirementSignature(false), parent(parent) {
Expand All @@ -952,7 +961,7 @@ class GenericSignatureBuilder::RequirementSource final
assert(isAcceptableStorageKind(kind, storageKind) &&
"RequirementSource kind/storageKind mismatch");

storage.conformance = conformance;
storage.conformance = conformance.getOpaqueValue();
}

RequirementSource(Kind kind, const RequirementSource *parent,
Expand Down Expand Up @@ -1019,13 +1028,14 @@ class GenericSignatureBuilder::RequirementSource final
/// 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.
Expand Down Expand Up @@ -1126,9 +1136,9 @@ class GenericSignatureBuilder::RequirementSource final
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
Expand Down
159 changes: 84 additions & 75 deletions lib/AST/GenericSignatureBuilder.cpp
Expand Up @@ -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());
}
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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");

Expand All @@ -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()) {
Expand Down Expand Up @@ -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);
}
}
}
Expand Down Expand Up @@ -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);
}
}

Expand Down
@@ -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

Expand Down
Expand Up @@ -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

0 comments on commit 52e52b5

Please sign in to comment.