Implement SE-0195, which introduces "Dynamic Member Lookup" Types (#14546)

* Implement the recently accepted SE-0195 proposal, which introduces "Dynamic
Member Lookup" Types.  This is a dusted off and updated version of PR13361,
which switches from DynamicMemberLookupProtocol to @dynamicMemberLookup as
was requested by the final review decision.  This also rebases it,
updates it for other changes in the compiler, fixes a bunch of bugs, and adds support for keypaths.  

Thank you to @rudx and @DougGregor in particular for the helpful review comments and test cases!
This commit is contained in:
Chris Lattner
2018-02-16 16:19:50 -08:00
committed by GitHub
parent 3184dd8ad4
commit a0fa5d11b4
17 changed files with 845 additions and 71 deletions

View File

@@ -2863,6 +2863,64 @@ getArgumentLabels(ConstraintSystem &cs, ConstraintLocatorBuilder locator) {
return known->second;
}
/// Return true if the specified type or a super-class/super-protocol has the
/// @dynamicMemberLookup attribute on it. This implementation is not
/// particularly fast in the face of deep class hierarchies or lots of protocol
/// conformances, but this is fine because it doesn't get invoked in the normal
/// name lookup path (only when lookup is about to fail).
static bool hasDynamicMemberLookupAttribute(Type ty,
llvm::DenseMap<Type, bool> &IsDynamicMemberLookupCache) {
auto it = IsDynamicMemberLookupCache.find(ty);
if (it != IsDynamicMemberLookupCache.end()) return it->second;
auto calculate = [&]()-> bool {
// If this is a protocol composition, check to see if any of the protocols
// have the attribute on them.
if (auto protocolComp = ty->getAs<ProtocolCompositionType>()) {
for (auto p : protocolComp->getMembers())
if (hasDynamicMemberLookupAttribute(p, IsDynamicMemberLookupCache))
return true;
return false;
}
// Otherwise this has to be a nominal type.
auto nominal = ty->getAnyNominal();
if (!nominal) return false; // Dynamic lookups don't exist on tuples, etc.
// If any of the protocols this type conforms to has the attribute, then
// yes.
for (auto p : nominal->getAllProtocols())
if (p->getAttrs().hasAttribute<DynamicMemberLookupAttr>())
return true;
// Walk superclasses, if present.
llvm::SmallPtrSet<const NominalTypeDecl*, 8> visitedDecls;
while (1) {
// If we found a circular parent class chain, reject this.
if (!visitedDecls.insert(nominal).second)
return false;
// If this type has the attribute on it, then yes!
if (nominal->getAttrs().hasAttribute<DynamicMemberLookupAttr>())
return true;
// If this is a class with a super class, check super classes as well.
if (auto *cd = dyn_cast<ClassDecl>(nominal)) {
if (auto superClass = cd->getSuperclassDecl()) {
nominal = superClass;
continue;
}
}
return false;
}
};
return IsDynamicMemberLookupCache[ty] = calculate();
}
/// Given a ValueMember, UnresolvedValueMember, or TypeMember constraint,
/// perform a lookup into the specified base type to find a candidate list.
/// The list returned includes the viable candidates as well as the unviable
@@ -3240,6 +3298,42 @@ retry_after_fail:
}
}
}
// If we're about to fail lookup, but we are looking for members in a type
// that has the @dynamicMemberLookup attribute, then we resolve the reference
// to the subscript(dynamicMember:) member, and pass the member name as a
// string.
if (result.ViableCandidates.empty() &&
constraintKind == ConstraintKind::ValueMember &&
memberName.isSimpleName() && !memberName.isSpecial()) {
auto name = memberName.getBaseIdentifier();
if (hasDynamicMemberLookupAttribute(instanceTy,
IsDynamicMemberLookupCache)) {
auto &ctx = getASTContext();
// Recursively look up the subscript(dynamicMember:)'s in this type.
auto subscriptName =
DeclName(ctx, DeclBaseName::createSubscript(), ctx.Id_dynamicMember);
auto subscripts = performMemberLookup(constraintKind,
subscriptName,
baseTy, functionRefKind,
memberLocator,
includeInaccessibleMembers);
// Reflect the candidates found as DynamicMemberLookup results.
for (auto candidate : subscripts.ViableCandidates) {
auto decl = cast<SubscriptDecl>(candidate.getDecl());
if (isAcceptableDynamicMemberLookupSubscript(decl, DC, TC))
result.addViable(OverloadChoice::getDynamicMemberLookup(baseTy,
decl, name));
}
for (auto candidate : subscripts.UnviableCandidates) {
auto decl = candidate.first.getDecl();
auto choice = OverloadChoice::getDynamicMemberLookup(baseTy, decl,name);
result.addUnviable(choice, candidate.second);
}
}
}
// If we rejected some possibilities due to an argument-label
// mismatch and ended up with nothing, try again ignoring the