Skip to content

Commit

Permalink
Map from an arbitrary requirement to a related derivable requirement.
Browse files Browse the repository at this point in the history
Use this to determine whether a requirement is derivable within the
current conformance checking. Effectively NFC

Swift SVN r32190
  • Loading branch information
DougGregor committed Sep 23, 2015
1 parent dd9f28e commit 373a73e
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 4 deletions.
72 changes: 72 additions & 0 deletions lib/Sema/DerivedConformances.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,78 @@
using namespace swift;
using namespace DerivedConformance;

ValueDecl *DerivedConformance::getDerivableRequirement(NominalTypeDecl *nominal,
ValueDecl *requirement) {
ASTContext &ctx = nominal->getASTContext();
auto name = requirement->getFullName();

// Local function that retrieves the requirement with the same name as
// the provided requirement, but within the given known protocol.
auto getRequirement = [&](KnownProtocolKind kind) -> ValueDecl * {
// Dig out the protocol.
auto proto = ctx.getProtocol(kind);
if (!proto) return nullptr;

// Check whether this nominal type derives conformances to the
if (!nominal->derivesProtocolConformance(proto)) return nullptr;

// Retrieve the requirement.
for (auto result : proto->lookupDirect(name))
return result;

return nullptr;
};

// Properties.
if (isa<VarDecl>(requirement)) {
// RawRepresentable.rawValue
if (name.isSimpleName(ctx.Id_rawValue))
return getRequirement(KnownProtocolKind::RawRepresentable);

// Hashable.hashValue
if (name.isSimpleName(ctx.Id_hashValue))
return getRequirement(KnownProtocolKind::Hashable);

// ErrorType._code
if (name.isSimpleName(ctx.Id_code_))
return getRequirement(KnownProtocolKind::ErrorType);

// _BridgedNSError._NSErrorDomain
if (name.isSimpleName(ctx.Id_NSErrorDomain))
return getRequirement(KnownProtocolKind::_BridgedNSError);

return nullptr;
}

// Functions.
if (auto func = dyn_cast<FuncDecl>(requirement)) {
if (func->isOperator() && name.getBaseName().str() == "==")
return getRequirement(KnownProtocolKind::Equatable);

return nullptr;
}

// Initializers.
if (isa<ConstructorDecl>(requirement)) {
auto argumentNames = name.getArgumentNames();
if (argumentNames.size() == 1 && argumentNames[0] == ctx.Id_rawValue)
return getRequirement(KnownProtocolKind::RawRepresentable);

return nullptr;
}

// Associated types.
if (isa<AssociatedTypeDecl>(requirement)) {
// RawRepresentable.RawValue
if (name.isSimpleName(ctx.Id_RawValue))
return getRequirement(KnownProtocolKind::RawRepresentable);

return nullptr;
}

return nullptr;
}

void DerivedConformance::_insertOperatorDecl(ASTContext &C,
IterableDeclContext *scope,
Decl *member) {
Expand Down
16 changes: 16 additions & 0 deletions lib/Sema/DerivedConformances.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,22 @@ namespace swift {

namespace DerivedConformance {

/// Determine the derivable requirement that would satisfy the given
/// requirement, if there is one.
///
/// \param nominal The nominal type for which we are determining whether to
/// derive a witness.
///
/// \param requirement The requirement for which we are checking for a
/// derivation. This requirement need not be within a derivable protocol,
/// because derivable requirements can get restated in inherited unrelated or
/// unrelated protocols.
///
/// \returns The requirement whose witness could be derived to potentially
/// satisfy this given requirement, or NULL if there is no such requirement.
ValueDecl *getDerivableRequirement(NominalTypeDecl *nominal,
ValueDecl *requirement);

/// Derive a RawRepresentable requirement for an enum, if it has a valid
/// raw type and raw values for all of its cases.
///
Expand Down
12 changes: 8 additions & 4 deletions lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2052,12 +2052,16 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
}
}

// Determine whether we can derive this conformance.
// FIXME: Hoist this computation out of here.
// Determine whether we can derive a witness for this requirement.
bool canDerive = false;
if (auto *nominal = Adoptee->getAnyNominal()) {
if (nominal->derivesProtocolConformance(Proto))
canDerive = true;
// Can a witness for this requirement be derived for this nominal type?
if (auto derivable = DerivedConformance::getDerivableRequirement(
nominal,
requirement)) {
if (derivable == requirement)
canDerive = true;
}
}

// Gather the witnesses.
Expand Down

0 comments on commit 373a73e

Please sign in to comment.