Skip to content

Commit 0537131

Browse files
committed
[ConstraintSystem] Re-activate constraints if, due to incorrect reference, member type has been bound before base type
If member has been bound before the base and the base was incorrect at that (e.g. fallback to default `Any` type), then we need to re-activate all of the constraints associated with this member reference otherwise some of the constraints could be left unchecked in inactive state. This is especially important for key path expressions because `key path` constraint can't be retired until all components are simplified. Resolves: rdar://78035829
1 parent bfb5ccd commit 0537131

File tree

7 files changed

+99
-16
lines changed

7 files changed

+99
-16
lines changed

include/swift/Sema/ConstraintLocator.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,13 @@ class ConstraintLocator : public llvm::FoldingSetNode {
199199
/// via key path dynamic member lookup.
200200
bool isForKeyPathDynamicMemberLookup() const;
201201

202-
/// Determine whether this locator points to one of the key path
203-
/// components.
204-
bool isForKeyPathComponent() const;
202+
/// Determine whether this locator points to element inside
203+
/// of a key path component.
204+
bool isInKeyPathComponent() const;
205+
206+
/// Determine whether this locator points to a result type of
207+
/// a key path component.
208+
bool isForKeyPathComponentResult() const;
205209

206210
/// Determine whether this locator points to the generic parameter.
207211
bool isForGenericParameter() const;

lib/Sema/CSBindings.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1304,6 +1304,13 @@ bool TypeVarBindingProducer::computeNext() {
13041304
}
13051305
}
13061306

1307+
// There is a tailored fix for optional key path root references,
1308+
// let's not create ambiguity by attempting unwrap when it's
1309+
// not allowed.
1310+
if (binding.Kind != BindingKind::Subtypes &&
1311+
getLocator()->isKeyPathRoot() && type->getOptionalObjectType())
1312+
continue;
1313+
13071314
// Allow solving for T even for a binding kind where that's invalid
13081315
// if fixes are allowed, because that gives us the opportunity to
13091316
// match T? values to the T binding by adding an unwrap fix.

lib/Sema/CSDiagnostics.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3930,7 +3930,7 @@ bool AllowTypeOrInstanceMemberFailure::diagnoseAsError() {
39303930
// If this is a reference to a static member by one of the key path
39313931
// components, let's provide a tailored diagnostic and return because
39323932
// that is unsupported so there is no fix-it.
3933-
if (locator->isForKeyPathComponent()) {
3933+
if (locator->isInKeyPathComponent()) {
39343934
InvalidStaticMemberRefInKeyPath failure(getSolution(), Member, locator);
39353935
return failure.diagnoseAsError();
39363936
}

lib/Sema/CSDiagnostics.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1543,7 +1543,7 @@ class InvalidMemberRefInKeyPath : public FailureDiagnostic {
15431543
ConstraintLocator *locator)
15441544
: FailureDiagnostic(solution, locator), Member(member) {
15451545
assert(member->hasName());
1546-
assert(locator->isForKeyPathComponent() ||
1546+
assert(locator->isInKeyPathComponent() ||
15471547
locator->isForKeyPathDynamicMemberLookup());
15481548
}
15491549

lib/Sema/CSSimplify.cpp

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7371,8 +7371,21 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
73717371

73727372
// Record a hole for memberTy to make it possible to form solutions
73737373
// when contextual result type cannot be deduced e.g. `let _ = x.foo`.
7374-
if (auto *memberTypeVar = memberTy->getAs<TypeVariableType>())
7375-
recordPotentialHole(memberTypeVar);
7374+
if (auto *memberTypeVar = memberTy->getAs<TypeVariableType>()) {
7375+
if (getFixedType(memberTypeVar)) {
7376+
// If member has been bound before the base and the base was
7377+
// incorrect at that (e.g. fallback to default `Any` type),
7378+
// then we need to re-activate all of the constraints
7379+
// associated with this member reference otherwise some of
7380+
// the constraints could be left unchecked in inactive state.
7381+
// This is especially important for key path expressions because
7382+
// `key path` constraint can't be retired until all components
7383+
// are simplified.
7384+
addTypeVariableConstraintsToWorkList(memberTypeVar);
7385+
} else {
7386+
recordPotentialHole(memberTypeVar);
7387+
}
7388+
}
73767389

73777390
return SolutionKind::Solved;
73787391
};
@@ -8395,15 +8408,28 @@ ConstraintSystem::simplifyKeyPathConstraint(
83958408
return SolutionKind::Solved;
83968409

83978410
// If we have e.g a missing member somewhere, a component type variable
8398-
// will have been marked as a potential hole.
8399-
// FIXME: This relies on the fact that we only mark an overload type
8400-
// variable as a potential hole once we've added a corresponding fix. We
8401-
// can't use 'isHole' instead, as that doesn't handle cases where the
8402-
// overload type variable gets bound to another type from the context rather
8403-
// than a hole. We need to come up with a better way of handling the
8404-
// relationship between key paths and overloads.
8411+
// would either be marked as a potential hole or would have a fix.
84058412
if (llvm::any_of(componentTypeVars, [&](TypeVariableType *tv) {
8406-
return tv->getImpl().getLocator()->isForKeyPathComponent() &&
8413+
auto *locator = tv->getImpl().getLocator();
8414+
8415+
// Result type of a component could be bound to a contextual
8416+
// (concrete) type if it's the last component in the chain,
8417+
// so the only way to detect errors is to check for fixes.
8418+
if (locator->isForKeyPathComponentResult()) {
8419+
auto path = locator->getPath();
8420+
auto *componentLoc =
8421+
getConstraintLocator(locator->getAnchor(), path.drop_back());
8422+
8423+
if (hasFixFor(componentLoc, FixKind::DefineMemberBasedOnUse) ||
8424+
hasFixFor(componentLoc, FixKind::UnwrapOptionalBase) ||
8425+
hasFixFor(componentLoc,
8426+
FixKind::UnwrapOptionalBaseWithOptionalResult))
8427+
return true;
8428+
}
8429+
8430+
// If something inside of a component is marked as a hole,
8431+
// let's consider while component to be invalid.
8432+
return locator->isInKeyPathComponent() &&
84078433
tv->getImpl().canBindToHole();
84088434
})) {
84098435
return SolutionKind::Solved;

lib/Sema/ConstraintLocator.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,12 +168,16 @@ bool ConstraintLocator::isForKeyPathDynamicMemberLookup() const {
168168
return !path.empty() && path.back().isKeyPathDynamicMember();
169169
}
170170

171-
bool ConstraintLocator::isForKeyPathComponent() const {
171+
bool ConstraintLocator::isInKeyPathComponent() const {
172172
return llvm::any_of(getPath(), [&](const LocatorPathElt &elt) {
173173
return elt.isKeyPathComponent();
174174
});
175175
}
176176

177+
bool ConstraintLocator::isForKeyPathComponentResult() const {
178+
return isLastElement<LocatorPathElt::KeyPathComponentResult>();
179+
}
180+
177181
bool ConstraintLocator::isForGenericParameter() const {
178182
return isLastElement<LocatorPathElt::GenericParameter>();
179183
}

test/expr/unary/keypath/keypath.swift

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,3 +1052,45 @@ func testSyntaxErrors() {
10521052
_ = \A.a?;
10531053
_ = \A.a!;
10541054
}
1055+
1056+
// SR-13364 - keypath missing optional crashes compiler: "Inactive constraints left over?"
1057+
func sr13364() {
1058+
let _: KeyPath<String?, Int?> = \.utf8.count // expected-error {{no exact matches in reference to property 'count'}}
1059+
// expected-note@-1 {{found candidate with type 'Int'}}
1060+
}
1061+
1062+
// rdar://74711236 - crash due to incorrect member access in key path
1063+
func rdar74711236() {
1064+
struct S {
1065+
var arr: [V] = []
1066+
}
1067+
1068+
struct V : Equatable {
1069+
}
1070+
1071+
enum Type {
1072+
case store
1073+
}
1074+
1075+
struct Context {
1076+
func supported() -> [Type] {
1077+
return []
1078+
}
1079+
}
1080+
1081+
func test(context: Context?) {
1082+
var s = S()
1083+
1084+
s.arr = {
1085+
// FIXME: Missing member reference is pattern needs a better diagnostic
1086+
if let type = context?.store { // expected-error {{type of expression is ambiguous without more context}}
1087+
// `isSupported` should be an invalid declaration to trigger a crash in `map(\.option)`
1088+
let isSupported = context!.supported().contains(type) // expected-error {{missing argument label 'where:' in call}} expected-error {{converting non-escaping value to '(Type) throws -> Bool' may allow it to escape}}
1089+
return (isSupported ? [type] : []).map(\.option)
1090+
// expected-error@-1 {{value of type 'Any' has no member 'option'}}
1091+
// expected-note@-2 {{cast 'Any' to 'AnyObject' or use 'as!' to force downcast to a more specific type to access members}}
1092+
}
1093+
return []
1094+
}()
1095+
}
1096+
}

0 commit comments

Comments
 (0)