Skip to content

[AST] Protocol conformance cleanups #8852

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Apr 19, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion include/swift/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -662,7 +662,6 @@ class ASTContext {
/// Produce a new normal conformance for a property behavior.
NormalProtocolConformance *
getBehaviorConformance(Type conformingType,
Type conformingInterfaceType,
ProtocolDecl *protocol,
SourceLoc loc,
AbstractStorageDecl *storage,
Expand Down
111 changes: 41 additions & 70 deletions include/swift/AST/ProtocolConformance.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,9 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance {
/// conformance definition.
Type ConformingType;

/// \brief The interface type that conforms to the protocol.
Type ConformingInterfaceType;

protected:
ProtocolConformance(ProtocolConformanceKind kind, Type conformingType,
Type conformingInterfaceType)
: Kind(kind), ConformingType(conformingType),
ConformingInterfaceType(conformingInterfaceType) { }
ProtocolConformance(ProtocolConformanceKind kind, Type conformingType)
: Kind(kind), ConformingType(conformingType) { }

public:
/// Determine the kind of protocol conformance.
Expand All @@ -110,9 +105,6 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance {
/// Get the conforming type.
Type getType() const { return ConformingType; }

/// Get the conforming interface type.
Type getInterfaceType() const { return ConformingInterfaceType; }

/// Get the protocol being conformed to.
ProtocolDecl *getProtocol() const;

Expand Down Expand Up @@ -190,41 +182,16 @@ class alignas(1 << DeclAlignInBits) ProtocolConformance {
return false;
}

/// Retrieve the non-type witness for the given requirement.
Witness getWitness(ValueDecl *requirement, LazyResolver *resolver) const;
/// Retrieve the value witness declaration corresponding to the given
/// requirement.
ValueDecl *getWitnessDecl(ValueDecl *requirement,
LazyResolver *resolver) const;

private:
/// Determine whether we have a witness for the given requirement.
bool hasWitness(ValueDecl *requirement) const;

public:
/// Apply the given function object to each value witness within this
/// protocol conformance.
///
/// The function object should accept a \c ValueDecl* for the requirement
/// followed by the \c Witness for the witness. Note that a generic
/// witness will only be specialized if the conformance came from the current
/// file.
template<typename F>
void forEachValueWitness(LazyResolver *resolver, F f) const {
const ProtocolDecl *protocol = getProtocol();
for (auto req : protocol->getMembers()) {
auto valueReq = dyn_cast<ValueDecl>(req);
if (!valueReq || isa<AssociatedTypeDecl>(valueReq) ||
valueReq->isInvalid())
continue;

if (!valueReq->isProtocolRequirement())
continue;

// If we don't have and cannot resolve witnesses, skip it.
if (!resolver && !hasWitness(valueReq))
continue;

f(valueReq, getWitness(valueReq, resolver));
}
}

/// Retrieve the protocol conformance for the inherited protocol.
ProtocolConformance *getInheritedConformance(ProtocolDecl *protocol) const;

Expand Down Expand Up @@ -357,21 +324,16 @@ class NormalProtocolConformance : public ProtocolConformance,
NormalProtocolConformance(Type conformingType, ProtocolDecl *protocol,
SourceLoc loc, DeclContext *dc,
ProtocolConformanceState state)
: ProtocolConformance(ProtocolConformanceKind::Normal, conformingType,
// FIXME: interface type should be passed in
dc->getDeclaredInterfaceType()),
: ProtocolConformance(ProtocolConformanceKind::Normal, conformingType),
ProtocolAndState(protocol, state), Loc(loc), ContextAndInvalid(dc, false)
{
}

NormalProtocolConformance(Type conformingType,
Type conformingInterfaceType,
ProtocolDecl *protocol,
SourceLoc loc, AbstractStorageDecl *behaviorStorage,
ProtocolConformanceState state)
: ProtocolConformance(ProtocolConformanceKind::Normal, conformingType,
// FIXME: interface type should be passed in
conformingInterfaceType),
: ProtocolConformance(ProtocolConformanceKind::Normal, conformingType),
ProtocolAndState(protocol, state), Loc(loc),
ContextAndInvalid(behaviorStorage, false)
{
Expand Down Expand Up @@ -457,11 +419,6 @@ class NormalProtocolConformance : public ProtocolConformance,
LazyResolver *resolver = nullptr) const;

/// Retrieve the value witness corresponding to the given requirement.
///
/// Note that a generic witness will only be specialized if the conformance
/// came from the current file.
///
/// FIXME: The 'only specialized if from the same file' bit is awful.
Witness getWitness(ValueDecl *requirement, LazyResolver *resolver) const;

/// Determine whether the protocol conformance has a witness for the given
Expand All @@ -475,6 +432,33 @@ class NormalProtocolConformance : public ProtocolConformance,
/// Set the witness for the given requirement.
void setWitness(ValueDecl *requirement, Witness witness) const;

/// Apply the given function object to each value witness within this
/// protocol conformance.
///
/// The function object should accept a \c ValueDecl* for the requirement
/// followed by the \c Witness for the witness. Note that a generic
/// witness will only be specialized if the conformance came from the current
/// file.
template<typename F>
void forEachValueWitness(LazyResolver *resolver, F f) const {
const ProtocolDecl *protocol = getProtocol();
for (auto req : protocol->getMembers()) {
auto valueReq = dyn_cast<ValueDecl>(req);
if (!valueReq || isa<AssociatedTypeDecl>(valueReq) ||
valueReq->isInvalid())
continue;

if (!valueReq->isProtocolRequirement())
continue;

// If we don't have and cannot resolve witnesses, skip it.
if (!resolver && !hasWitness(valueReq))
continue;

f(valueReq, getWitness(valueReq, resolver));
}
}

/// Retrieve the protocol conformances that satisfy the requirements of the
/// protocol, which line up with the conformance constraints in the
/// protocol's requirement signature.
Expand Down Expand Up @@ -583,10 +567,6 @@ class SpecializedProtocolConformance : public ProtocolConformance,
getTypeWitnessAndDecl(AssociatedTypeDecl *assocType,
LazyResolver *resolver) const;

/// Retrieve the value witness corresponding to the given requirement.
Witness getWitness(ValueDecl *requirement, LazyResolver *resolver) const;


/// Given that the requirement signature of the protocol directly states
/// that the given dependent type must conform to the given protocol,
/// return its associated conformance.
Expand All @@ -601,17 +581,16 @@ class SpecializedProtocolConformance : public ProtocolConformance,
}

void Profile(llvm::FoldingSetNodeID &ID) {
Profile(ID, getType(), getGenericConformance());
Profile(ID, getType(), getGenericConformance(),
getGenericSubstitutions());
}

static void Profile(llvm::FoldingSetNodeID &ID, Type type,
ProtocolConformance *genericConformance) {
// FIXME: Consider profiling substitutions here. They could differ in
// some crazy cases that also require major diagnostic work, where the
// substitutions involve conformances of the same type to the same
// protocol drawn from different imported modules.
ProtocolConformance *genericConformance,
SubstitutionList subs) {
ID.AddPointer(type.getPointer());
ID.AddPointer(genericConformance);
profileSubstitutionList(ID, subs);
}

static bool classof(const ProtocolConformance *conformance) {
Expand Down Expand Up @@ -640,9 +619,7 @@ class InheritedProtocolConformance : public ProtocolConformance,

InheritedProtocolConformance(Type conformingType,
ProtocolConformance *inheritedConformance)
: ProtocolConformance(ProtocolConformanceKind::Inherited, conformingType,
// FIXME: interface type should be passed in
inheritedConformance->getDeclContext()->getDeclaredInterfaceType()),
: ProtocolConformance(ProtocolConformanceKind::Inherited, conformingType),
InheritedConformance(inheritedConformance)
{
}
Expand Down Expand Up @@ -686,12 +663,6 @@ class InheritedProtocolConformance : public ProtocolConformance,
return InheritedConformance->getTypeWitnessAndDecl(assocType, resolver);
}

/// Retrieve the value witness corresponding to the given requirement.
Witness getWitness(ValueDecl *requirement, LazyResolver *resolver) const {
// FIXME: Substitute!
return InheritedConformance->getWitness(requirement, resolver);
}

/// Given that the requirement signature of the protocol directly states
/// that the given dependent type must conform to the given protocol,
/// return its associated conformance.
Expand Down
9 changes: 9 additions & 0 deletions include/swift/AST/SubstitutionList.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@

#include "swift/AST/Substitution.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/FoldingSet.h"

namespace llvm {
class FoldingSetNodeID;
} // end namespace llvm

namespace swift {

Expand All @@ -41,6 +46,10 @@ void dump(SubstitutionList subs);
SubstitutionList
getCanonicalSubstitutionList(SubstitutionList subs,
SmallVectorImpl<Substitution> &canSubs);

/// Profile the substitution list for use in a folding set.
void profileSubstitutionList(llvm::FoldingSetNodeID &id, SubstitutionList subs);

} // end namespace swift

#endif
8 changes: 3 additions & 5 deletions lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1476,16 +1476,14 @@ void ValueDecl::setLocalDiscriminator(unsigned index) {

NormalProtocolConformance *
ASTContext::getBehaviorConformance(Type conformingType,
Type conformingInterfaceType,
ProtocolDecl *protocol,
SourceLoc loc,
AbstractStorageDecl *storage,
ProtocolConformanceState state) {
auto conformance = new (*this, AllocationArena::Permanent)
NormalProtocolConformance(conformingType, conformingInterfaceType,
protocol, loc, storage, state);
NormalProtocolConformance(conformingType, protocol, loc, storage, state);

if (auto nominal = conformingInterfaceType->getAnyNominal()) {
if (auto nominal = conformingType->getRValueInstanceType()->getAnyNominal()) {
// Note: this is an egregious hack. The conformances need to be associated
// with the actual storage declarations.
SmallVector<ProtocolConformance *, 2> conformances;
Expand Down Expand Up @@ -1528,7 +1526,7 @@ ASTContext::getSpecializedConformance(Type type,
ProtocolConformance *generic,
SubstitutionList substitutions) {
llvm::FoldingSetNodeID id;
SpecializedProtocolConformance::Profile(id, type, generic);
SpecializedProtocolConformance::Profile(id, type, generic, substitutions);

// Figure out which arena this conformance should go into.
AllocationArena arena = getArena(type->getRecursiveProperties());
Expand Down
5 changes: 4 additions & 1 deletion lib/AST/ASTMangler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1839,7 +1839,10 @@ void ASTMangler::appendProtocolConformance(const ProtocolConformance *conformanc
appendProtocolName(conformance->getProtocol());
appendIdentifier(behaviorStorage->getName().str());
} else {
appendType(conformance->getInterfaceType()->getCanonicalType());
auto conformanceDC = conformance->getDeclContext();
auto conformingType =
conformanceDC->mapTypeOutOfContext(conformance->getType());
appendType(conformingType->getCanonicalType());
appendProtocolName(conformance->getProtocol());
appendModule(conformance->getDeclContext()->getParentModule());
}
Expand Down
3 changes: 2 additions & 1 deletion lib/AST/ConformanceLookupTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1103,7 +1103,8 @@ ConformanceLookupTable::getSatisfiedProtocolRequirementsForMember(
if (conf->isInvalid())
continue;

conf->forEachValueWitness(resolver, [&](ValueDecl *req,
auto normal = conf->getRootNormalConformance();
normal->forEachValueWitness(resolver, [&](ValueDecl *req,
ConcreteDeclRef witness) {
if (witness.getDecl() == member)
reqs.push_back(req);
Expand Down
36 changes: 18 additions & 18 deletions lib/AST/ProtocolConformance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,22 @@ Type ProtocolConformance::getTypeWitness(AssociatedTypeDecl *assocType,
return getTypeWitnessAndDecl(assocType, resolver).first;
}

Witness ProtocolConformance::getWitness(ValueDecl *requirement,
LazyResolver *resolver) const {
CONFORMANCE_SUBCLASS_DISPATCH(getWitness, (requirement, resolver))
ValueDecl *ProtocolConformance::getWitnessDecl(ValueDecl *requirement,
LazyResolver *resolver) const {
switch (getKind()) {
case ProtocolConformanceKind::Normal:
return cast<NormalProtocolConformance>(this)->getWitness(requirement,
resolver)
.getDecl();

case ProtocolConformanceKind::Inherited:
return cast<InheritedProtocolConformance>(this)
->getInheritedConformance()->getWitnessDecl(requirement, resolver);

case ProtocolConformanceKind::Specialized:
return cast<SpecializedProtocolConformance>(this)
->getGenericConformance()->getWitnessDecl(requirement, resolver);
}
}

/// Determine whether the witness for the given requirement
Expand All @@ -266,7 +279,7 @@ usesDefaultDefinition(AssociatedTypeDecl *requirement) const {
bool ProtocolConformance::hasFixedLayout() const {
// A conformance/witness table has fixed layout if type has a fixed layout in
// all resilience domains, and the conformance is externally visible.
if (auto nominal = getInterfaceType()->getAnyNominal())
if (auto nominal = getType()->getAnyNominal())
if (nominal->hasFixedLayout() &&
getProtocol()->getEffectiveAccess() >= Accessibility::Public &&
nominal->getEffectiveAccess() >= Accessibility::Public)
Expand Down Expand Up @@ -652,11 +665,7 @@ SpecializedProtocolConformance::SpecializedProtocolConformance(
Type conformingType,
ProtocolConformance *genericConformance,
SubstitutionList substitutions)
: ProtocolConformance(ProtocolConformanceKind::Specialized, conformingType,
// FIXME: interface type should be passed in.
// assumes specialized conformance is always fully
// specialized
conformingType),
: ProtocolConformance(ProtocolConformanceKind::Specialized, conformingType),
GenericConformance(genericConformance),
GenericSubstitutions(substitutions)
{
Expand Down Expand Up @@ -708,13 +717,6 @@ SpecializedProtocolConformance::getTypeWitnessAndDecl(
return TypeWitnesses[assocType];
}

Witness
SpecializedProtocolConformance::getWitness(ValueDecl *requirement,
LazyResolver *resolver) const {
// FIXME: Apply substitutions here!
return GenericConformance->getWitness(requirement, resolver);
}

ProtocolConformanceRef
SpecializedProtocolConformance::getAssociatedConformance(Type assocType,
ProtocolDecl *protocol,
Expand Down Expand Up @@ -1068,8 +1070,6 @@ bool ProtocolConformance::isCanonical() const {

if (!getType()->isCanonical())
return false;
if (!getInterfaceType()->isCanonical())
return false;

switch (getKind()) {
case ProtocolConformanceKind::Normal: {
Expand Down
12 changes: 12 additions & 0 deletions lib/AST/SubstitutionList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,17 @@
//===----------------------------------------------------------------------===//

#include "swift/AST/SubstitutionList.h"
#include "swift/AST/ProtocolConformanceRef.h"
#include "llvm/ADT/FoldingSet.h"

using namespace swift;

void swift::profileSubstitutionList(llvm::FoldingSetNodeID &id,
SubstitutionList subs) {
id.AddInteger(subs.size());
for (auto &sub : subs) {
id.AddPointer(sub.getReplacement().getPointer());
for (auto conformance : sub.getConformances())
id.AddPointer(conformance.getOpaqueValue());
}
}
6 changes: 1 addition & 5 deletions lib/AST/Type.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4060,11 +4060,7 @@ bool TypeBase::usesNativeReferenceCounting(ResilienceExpansion resilience) {
void SILBoxType::Profile(llvm::FoldingSetNodeID &id, SILLayout *Layout,
SubstitutionList Args) {
id.AddPointer(Layout);
for (auto &arg : Args) {
id.AddPointer(arg.getReplacement().getPointer());
for (auto conformance : arg.getConformances())
id.AddPointer(conformance.getOpaqueValue());
}
profileSubstitutionList(id, Args);
}

SILBoxType::SILBoxType(ASTContext &C,
Expand Down
2 changes: 1 addition & 1 deletion lib/IRGen/GenReflection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ class AssociatedTypeMetadataBuilder : public ReflectionMetadataBuilder {
void layout() override {
// If the conforming type is generic, we just want to emit the
// unbound generic type here.
auto *Nominal = Conformance->getInterfaceType()->getAnyNominal();
auto *Nominal = Conformance->getType()->getAnyNominal();
assert(Nominal && "Structural conformance?");

PrettyStackTraceDecl DebugStack("emitting associated type metadata",
Expand Down
Loading