mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user