Skip to content

Commit dde9fa3

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: SR-13364 Resolves: rdar://66706980 Resolves: rdar://74711236
1 parent fb89d11 commit dde9fa3

File tree

3 files changed

+85
-10
lines changed

3 files changed

+85
-10
lines changed

lib/Sema/CSBindings.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1612,6 +1612,13 @@ bool TypeVarBindingProducer::computeNext() {
16121612
addNewBinding(binding.withType(LValueType::get(binding.BindingType)));
16131613
}
16141614

1615+
// There is a tailored fix for optional key path root references,
1616+
// let's not create ambiguity by attempting unwrap when it's
1617+
// not allowed.
1618+
if (binding.Kind != BindingKind::Subtypes &&
1619+
getLocator()->isKeyPathRoot() && type->getOptionalObjectType())
1620+
continue;
1621+
16151622
// Allow solving for T even for a binding kind where that's invalid
16161623
// if fixes are allowed, because that gives us the opportunity to
16171624
// match T? values to the T binding by adding an unwrap fix.

lib/Sema/CSSimplify.cpp

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

77527752
// Record a hole for memberTy to make it possible to form solutions
77537753
// when contextual result type cannot be deduced e.g. `let _ = x.foo`.
7754-
if (auto *memberTypeVar = memberTy->getAs<TypeVariableType>())
7755-
recordPotentialHole(memberTypeVar);
7754+
if (auto *memberTypeVar = memberTy->getAs<TypeVariableType>()) {
7755+
if (getFixedType(memberTypeVar)) {
7756+
// If member has been bound before the base and the base was
7757+
// incorrect at that (e.g. fallback to default `Any` type),
7758+
// then we need to re-activate all of the constraints
7759+
// associated with this member reference otherwise some of
7760+
// the constraints could be left unchecked in inactive state.
7761+
// This is especially important for key path expressions because
7762+
// `key path` constraint can't be retired until all components
7763+
// are simplified.
7764+
addTypeVariableConstraintsToWorkList(memberTypeVar);
7765+
} else {
7766+
recordPotentialHole(memberTypeVar);
7767+
}
7768+
}
77567769

77577770
return SolutionKind::Solved;
77587771
};
@@ -8871,15 +8884,28 @@ ConstraintSystem::simplifyKeyPathConstraint(
88718884
return SolutionKind::Solved;
88728885

88738886
// If we have e.g a missing member somewhere, a component type variable
8874-
// will have been marked as a potential hole.
8875-
// FIXME: This relies on the fact that we only mark an overload type
8876-
// variable as a potential hole once we've added a corresponding fix. We
8877-
// can't use 'isPlaceholder' instead, as that doesn't handle cases where the
8878-
// overload type variable gets bound to another type from the context rather
8879-
// than a hole. We need to come up with a better way of handling the
8880-
// relationship between key paths and overloads.
8887+
// would either be marked as a potential hole or would have a fix.
88818888
if (llvm::any_of(componentTypeVars, [&](TypeVariableType *tv) {
8882-
return tv->getImpl().getLocator()->isInKeyPathComponent() &&
8889+
auto *locator = tv->getImpl().getLocator();
8890+
8891+
// Result type of a component could be bound to a contextual
8892+
// (concrete) type if it's the last component in the chain,
8893+
// so the only way to detect errors is to check for fixes.
8894+
if (locator->isForKeyPathComponentResult()) {
8895+
auto path = locator->getPath();
8896+
auto *componentLoc =
8897+
getConstraintLocator(locator->getAnchor(), path.drop_back());
8898+
8899+
if (hasFixFor(componentLoc, FixKind::DefineMemberBasedOnUse) ||
8900+
hasFixFor(componentLoc, FixKind::UnwrapOptionalBase) ||
8901+
hasFixFor(componentLoc,
8902+
FixKind::UnwrapOptionalBaseWithOptionalResult))
8903+
return true;
8904+
}
8905+
8906+
// If something inside of a component is marked as a hole,
8907+
// let's consider while component to be invalid.
8908+
return locator->isInKeyPathComponent() &&
88838909
tv->getImpl().canBindToHole();
88848910
})) {
88858911
return SolutionKind::Solved;

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)