Skip to content

Commit 8c2a040

Browse files
authored
Merge pull request #36427 from xedin/rdar-74711236
[ConstraintSystem] Re-activate constraints if, due to incorrect reference, member type has been bound before base type
2 parents 2083e1b + dde9fa3 commit 8c2a040

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
@@ -200,9 +200,13 @@ class ConstraintLocator : public llvm::FoldingSetNode {
200200
/// via key path dynamic member lookup.
201201
bool isForKeyPathDynamicMemberLookup() const;
202202

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

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

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/CSDiagnostics.cpp

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

lib/Sema/CSDiagnostics.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1576,7 +1576,7 @@ class InvalidMemberRefInKeyPath : public FailureDiagnostic {
15761576
ConstraintLocator *locator)
15771577
: FailureDiagnostic(solution, locator), Member(member) {
15781578
assert(member->hasName());
1579-
assert(locator->isForKeyPathComponent() ||
1579+
assert(locator->isInKeyPathComponent() ||
15801580
locator->isForKeyPathDynamicMemberLookup());
15811581
}
15821582

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()->isForKeyPathComponent() &&
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;

lib/Sema/ConstraintLocator.cpp

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

174-
bool ConstraintLocator::isForKeyPathComponent() const {
174+
bool ConstraintLocator::isInKeyPathComponent() const {
175175
return llvm::any_of(getPath(), [&](const LocatorPathElt &elt) {
176176
return elt.isKeyPathComponent();
177177
});
178178
}
179179

180+
bool ConstraintLocator::isForKeyPathComponentResult() const {
181+
return isLastElement<LocatorPathElt::KeyPathComponentResult>();
182+
}
183+
180184
bool ConstraintLocator::isForGenericParameter() const {
181185
return isLastElement<LocatorPathElt::GenericParameter>();
182186
}

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)