Skip to content

Commit 70becc3

Browse files
committed
Fix @dynamicMemberLookup for protocol/archetype types.
- Enable `subscript(dynamicMember:)` as a requirement for `@dynamicMemberLookup` protocols. - Add tests. - Minor `@dynamicCallable`-related gardening. Resolves SR-8077.
1 parent 2863b6c commit 70becc3

File tree

4 files changed

+158
-137
lines changed

4 files changed

+158
-137
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3083,26 +3083,45 @@ getArgumentLabels(ConstraintSystem &cs, ConstraintLocatorBuilder locator) {
30833083
/// particularly fast in the face of deep class hierarchies or lots of protocol
30843084
/// conformances, but this is fine because it doesn't get invoked in the normal
30853085
/// name lookup path (only when lookup is about to fail).
3086-
static bool hasDynamicMemberLookupAttribute(CanType ty,
3086+
static bool hasDynamicMemberLookupAttribute(Type type,
30873087
llvm::DenseMap<CanType, bool> &DynamicMemberLookupCache) {
3088-
auto it = DynamicMemberLookupCache.find(ty);
3088+
auto canType = type->getCanonicalType();
3089+
auto it = DynamicMemberLookupCache.find(canType);
30893090
if (it != DynamicMemberLookupCache.end()) return it->second;
3090-
3091-
auto calculate = [&]()-> bool {
3092-
// If this is a protocol composition, check to see if any of the protocols
3093-
// have the attribute on them.
3094-
if (auto protocolComp = ty->getAs<ProtocolCompositionType>()) {
3095-
for (auto p : protocolComp->getMembers())
3096-
if (hasDynamicMemberLookupAttribute(p->getCanonicalType(),
3097-
DynamicMemberLookupCache))
3098-
return true;
3099-
return false;
3091+
3092+
// Calculate @dynamicMemberLookup attribute for composite types with multiple
3093+
// components (protocol composition types and archetypes).
3094+
auto calculateForComponentTypes =
3095+
[&](ArrayRef<Type> componentTypes) -> bool {
3096+
for (auto componentType : componentTypes)
3097+
if (hasDynamicMemberLookupAttribute(componentType,
3098+
DynamicMemberLookupCache))
3099+
return true;
3100+
return false;
3101+
};
3102+
3103+
auto calculate = [&]() -> bool {
3104+
// If this is an archetype type, check if any types it conforms to
3105+
// (superclass or protocols) have the attribute.
3106+
if (auto archetype = dyn_cast<ArchetypeType>(canType)) {
3107+
SmallVector<Type, 2> componentTypes;
3108+
for (auto protocolDecl : archetype->getConformsTo())
3109+
componentTypes.push_back(protocolDecl->getDeclaredType());
3110+
if (auto superclass = archetype->getSuperclass())
3111+
componentTypes.push_back(superclass);
3112+
return calculateForComponentTypes(componentTypes);
31003113
}
3101-
3102-
// Otherwise this has to be a nominal type.
3103-
auto nominal = ty->getAnyNominal();
3104-
if (!nominal) return false; // Dynamic lookups don't exist on tuples, etc.
3105-
3114+
3115+
// If this is a protocol composition, check if any of its members have the
3116+
// attribute.
3117+
if (auto protocolComp = dyn_cast<ProtocolCompositionType>(canType))
3118+
return calculateForComponentTypes(protocolComp->getMembers());
3119+
3120+
// Otherwise, this must be a nominal type.
3121+
// Dynamic member lookup doesn't work for tuples, etc.
3122+
auto nominal = canType->getAnyNominal();
3123+
if (!nominal) return false;
3124+
31063125
// If this type conforms to a protocol with the attribute, then return true.
31073126
for (auto p : nominal->getAllProtocols())
31083127
if (p->getAttrs().hasAttribute<DynamicMemberLookupAttr>())
@@ -3132,11 +3151,9 @@ static bool hasDynamicMemberLookupAttribute(CanType ty,
31323151
};
31333152

31343153
auto result = calculate();
3135-
3136-
// Cache this if we can.
3137-
if (!ty->hasTypeVariable())
3138-
DynamicMemberLookupCache[ty] = result;
3139-
3154+
// Cache the result if the type does not contain type variables.
3155+
if (!type->hasTypeVariable())
3156+
DynamicMemberLookupCache[canType] = result;
31403157
return result;
31413158
}
31423159

@@ -3552,8 +3569,8 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
35523569
constraintKind == ConstraintKind::ValueMember &&
35533570
memberName.isSimpleName() && !memberName.isSpecial()) {
35543571
auto name = memberName.getBaseIdentifier();
3555-
if (hasDynamicMemberLookupAttribute(instanceTy->getCanonicalType(),
3556-
DynamicMemberLookupCache)) {
3572+
// TC.extract
3573+
if (hasDynamicMemberLookupAttribute(instanceTy, DynamicMemberLookupCache)) {
35573574
auto &ctx = getASTContext();
35583575

35593576
// Recursively look up `subscript(dynamicMember:)` methods in this type.
@@ -4680,7 +4697,11 @@ getDynamicCallableMethods(Type type, ConstraintSystem &CS,
46804697
}
46814698
};
46824699

4683-
return CS.DynamicCallableCache[canType] = calculate();
4700+
auto result = calculate();
4701+
// Cache the result if the type does not contain type variables.
4702+
if (!type->hasTypeVariable())
4703+
CS.DynamicCallableCache[canType] = result;
4704+
return result;
46844705
}
46854706

46864707
ConstraintSystem::SolutionKind

lib/Sema/TypeCheckAttr.cpp

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,26 +1074,20 @@ visitDynamicMemberLookupAttr(DynamicMemberLookupAttr *attr) {
10741074
auto decl = cast<NominalTypeDecl>(D);
10751075
auto type = decl->getDeclaredType();
10761076

1077-
// Lookup our subscript.
1078-
auto subscriptName =
1079-
DeclName(TC.Context, DeclBaseName::createSubscript(),
1080-
TC.Context.Id_dynamicMember);
1077+
// Look up `subscript(dynamicMember:)` candidates.
1078+
auto subscriptName = DeclName(TC.Context, DeclBaseName::createSubscript(),
1079+
TC.Context.Id_dynamicMember);
1080+
auto candidates = TC.lookupMember(decl, type, subscriptName);
10811081

1082-
auto lookupOptions = defaultMemberTypeLookupOptions;
1083-
lookupOptions -= NameLookupFlags::PerformConformanceCheck;
1084-
1085-
// Lookup the implementations of our subscript.
1086-
auto candidates = TC.lookupMember(decl, type, subscriptName, lookupOptions);
1087-
1088-
// If we have none, then the attribute is invalid.
1082+
// If there are no candidates, then the attribute is invalid.
10891083
if (candidates.empty()) {
10901084
TC.diagnose(attr->getLocation(), diag::invalid_dynamic_member_lookup_type,
10911085
type);
10921086
attr->setInvalid();
10931087
return;
10941088
}
10951089

1096-
// If none of the ones we find are acceptable, then reject one.
1090+
// If no candidates are valid, then reject one.
10971091
auto oneCandidate = candidates.front();
10981092
candidates.filter([&](LookupResultEntry entry, bool isOuter) -> bool {
10991093
auto cand = cast<SubscriptDecl>(entry.getValueDecl());

test/attr/attr_dynamic_callable.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %target-swift-frontend -typecheck -verify %s
1+
// RUN: %target-typecheck-verify-swift
22

33
@dynamicCallable
44
struct Callable {
@@ -64,8 +64,8 @@ func testIUO(
6464

6565
@dynamicCallable
6666
struct CallableReturningFunction {
67-
func dynamicallyCall(withArguments arguments: [Int]) -> (_ a: Int) -> Void {
68-
return { a in () }
67+
func dynamicallyCall(withArguments arguments: [Int]) -> (Int) -> Void {
68+
return { x in () }
6969
}
7070
}
7171

@@ -138,7 +138,7 @@ class InvalidDerived : InvalidBase {
138138
}
139139

140140
//===----------------------------------------------------------------------===//
141-
// Multiple `dynamicallyCall` method tests
141+
// Multiple `dynamicallyCall` methods
142142
//===----------------------------------------------------------------------===//
143143

144144
@dynamicCallable
@@ -160,7 +160,7 @@ func testOverloaded(x: OverloadedCallable) {
160160
}
161161

162162
//===----------------------------------------------------------------------===//
163-
// Existential tests
163+
// Existentials
164164
//===----------------------------------------------------------------------===//
165165

166166
@dynamicCallable
@@ -318,7 +318,7 @@ func testEnum() {
318318
}
319319

320320
//===----------------------------------------------------------------------===//
321-
// Generics tests
321+
// Generics
322322
//===----------------------------------------------------------------------===//
323323

324324
@dynamicCallable

0 commit comments

Comments
 (0)