Skip to content

Commit 54c59b3

Browse files
authored
Merge pull request swiftlang#26073 from xedin/rdar-52779809
[ConstraintSystem] Don't let conditional conformances shadow members …
2 parents e250a02 + 22fb079 commit 54c59b3

File tree

2 files changed

+107
-20
lines changed

2 files changed

+107
-20
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 64 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4019,6 +4019,47 @@ static bool isForKeyPathSubscript(ConstraintSystem &cs,
40194019
return false;
40204020
}
40214021

4022+
/// Determine whether all of the given candidate overloads
4023+
/// found through conditional conformances of a given base type.
4024+
/// This is useful to figure out whether it makes sense to
4025+
/// perform dynamic member lookup or not.
4026+
static bool
4027+
allFromConditionalConformances(DeclContext *DC, Type baseTy,
4028+
ArrayRef<OverloadChoice> candidates) {
4029+
auto *NTD = baseTy->getAnyNominal();
4030+
if (!NTD)
4031+
return false;
4032+
4033+
return llvm::all_of(candidates, [&](const OverloadChoice &choice) {
4034+
auto *decl = choice.getDeclOrNull();
4035+
if (!decl)
4036+
return false;
4037+
4038+
auto *candidateDC = decl->getDeclContext();
4039+
4040+
if (auto *extension = dyn_cast<ExtensionDecl>(candidateDC)) {
4041+
if (extension->isConstrainedExtension())
4042+
return true;
4043+
}
4044+
4045+
if (auto *protocol = candidateDC->getSelfProtocolDecl()) {
4046+
SmallVector<ProtocolConformance *, 4> conformances;
4047+
if (!NTD->lookupConformance(DC->getParentModule(), protocol,
4048+
conformances))
4049+
return false;
4050+
4051+
// This is opportunistic, there should be a way to narrow the
4052+
// list down to a particular declaration member comes from.
4053+
return llvm::any_of(
4054+
conformances, [](const ProtocolConformance *conformance) {
4055+
return !conformance->getConditionalRequirements().empty();
4056+
});
4057+
}
4058+
4059+
return false;
4060+
});
4061+
}
4062+
40224063
/// Given a ValueMember, UnresolvedValueMember, or TypeMember constraint,
40234064
/// perform a lookup into the specified base type to find a candidate list.
40244065
/// The list returned includes the viable candidates as well as the unviable
@@ -4479,30 +4520,32 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
44794520
}
44804521
}
44814522
}
4482-
4483-
// If we're about to fail lookup, but we are looking for members in a type
4484-
// with the @dynamicMemberLookup attribute, then we resolve a reference
4485-
// to a `subscript(dynamicMember:)` method and pass the member name as a
4486-
// string parameter.
4487-
if (result.ViableCandidates.empty() &&
4488-
constraintKind == ConstraintKind::ValueMember &&
4489-
memberName.isSimpleName() && !memberName.isSpecial()) {
4490-
auto name = memberName.getBaseIdentifier();
4491-
if (::hasDynamicMemberLookupAttribute(instanceTy,
4492-
DynamicMemberLookupCache)) {
4523+
4524+
// If we're about to fail lookup because there are no viable candidates
4525+
// or if all of the candidates come from conditional conformances (which
4526+
// might not be applicable), and we are looking for members in a type with
4527+
// the @dynamicMemberLookup attribute, then we resolve a reference to a
4528+
// `subscript(dynamicMember:)` method and pass the member name as a string
4529+
// parameter.
4530+
if (constraintKind == ConstraintKind::ValueMember &&
4531+
memberName.isSimpleName() && !memberName.isSpecial() &&
4532+
::hasDynamicMemberLookupAttribute(instanceTy, DynamicMemberLookupCache)) {
4533+
const auto &candidates = result.ViableCandidates;
4534+
4535+
if (candidates.empty() ||
4536+
allFromConditionalConformances(DC, instanceTy, candidates)) {
44934537
auto &ctx = getASTContext();
44944538

44954539
// Recursively look up `subscript(dynamicMember:)` methods in this type.
44964540
auto subscriptName =
4497-
DeclName(ctx, DeclBaseName::createSubscript(), ctx.Id_dynamicMember);
4498-
auto subscripts = performMemberLookup(constraintKind,
4499-
subscriptName,
4500-
baseTy, functionRefKind,
4501-
memberLocator,
4502-
includeInaccessibleMembers);
4541+
DeclName(ctx, DeclBaseName::createSubscript(), ctx.Id_dynamicMember);
4542+
auto subscripts = performMemberLookup(
4543+
constraintKind, subscriptName, baseTy, functionRefKind, memberLocator,
4544+
includeInaccessibleMembers);
45034545

45044546
// Reflect the candidates found as `DynamicMemberLookup` results.
4505-
for (auto candidate : subscripts.ViableCandidates) {
4547+
auto name = memberName.getBaseIdentifier();
4548+
for (const auto &candidate : subscripts.ViableCandidates) {
45064549
auto *SD = cast<SubscriptDecl>(candidate.getDecl());
45074550
bool isKeyPathBased = isValidKeyPathDynamicMemberLookup(SD, TC);
45084551

@@ -4512,9 +4555,10 @@ performMemberLookup(ConstraintKind constraintKind, DeclName memberName,
45124555
}
45134556

45144557
for (auto index : indices(subscripts.UnviableCandidates)) {
4515-
auto *SD = cast<SubscriptDecl>(subscripts.UnviableCandidates[index].getDecl());
4558+
auto *SD =
4559+
cast<SubscriptDecl>(subscripts.UnviableCandidates[index].getDecl());
45164560
auto choice = OverloadChoice::getDynamicMemberLookup(
4517-
baseTy, SD, name, isValidKeyPathDynamicMemberLookup(SD, TC));
4561+
baseTy, SD, name, isValidKeyPathDynamicMemberLookup(SD, TC));
45184562
result.addUnviable(choice, subscripts.UnviableReasons[index]);
45194563
}
45204564
}

test/Constraints/keypath_dynamic_member_lookup.swift

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,3 +285,46 @@ func prefer_readonly_keypath_over_reference_writable() {
285285
// CHECK-NEXT: function_ref @$s29keypath_dynamic_member_lookup14RefWritableBoxV0B6Memberqd__s7KeyPathCyxqd__G_tcluig
286286
_ = box.foo
287287
}
288+
289+
290+
// rdar://problem/52779809 - condiitional conformance shadows names of members reachable through dynamic lookup
291+
292+
protocol P {
293+
var foo: Int { get }
294+
}
295+
296+
@dynamicMemberLookup struct Ref<T> {
297+
var value: T
298+
299+
subscript<U>(dynamicMember member: KeyPath<T, U>) -> U {
300+
get { return value[keyPath: member] }
301+
}
302+
}
303+
304+
extension P {
305+
var foo: Int { return 42 }
306+
}
307+
308+
struct S {
309+
var foo: Int { return 0 }
310+
var baz: Int { return 1 }
311+
}
312+
313+
struct Q {
314+
var bar: Int { return 1 }
315+
}
316+
317+
extension Ref : P where T == Q {
318+
var baz: String { return "hello" }
319+
}
320+
321+
func rdar52779809(_ ref1: Ref<S>, _ ref2: Ref<Q>) {
322+
// CHECK: function_ref @$s29keypath_dynamic_member_lookup3RefV0B6Memberqd__s7KeyPathCyxqd__G_tcluig
323+
_ = ref1.foo // Ok
324+
// CHECK: function_ref @$s29keypath_dynamic_member_lookup3RefV0B6Memberqd__s7KeyPathCyxqd__G_tcluig
325+
_ = ref1.baz // Ok
326+
// CHECK: function_ref @$s29keypath_dynamic_member_lookup1PPAAE3fooSivg
327+
_ = ref2.foo // Ok
328+
// CHECK: function_ref @$s29keypath_dynamic_member_lookup3RefV0B6Memberqd__s7KeyPathCyxqd__G_tcluig
329+
_ = ref2.bar // Ok
330+
}

0 commit comments

Comments
 (0)