Skip to content

Commit 12630f4

Browse files
authored
Merge pull request #69031 from xedin/rdar-116376651
[ConstraintSystem] A few improvements to key path handling and diagnostics
2 parents 03d8a62 + b17ec0e commit 12630f4

File tree

8 files changed

+147
-89
lines changed

8 files changed

+147
-89
lines changed

include/swift/Sema/CSFix.h

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -396,10 +396,6 @@ enum class FixKind : uint8_t {
396396
/// is marked as `inout`.
397397
AllowConversionThroughInOut,
398398

399-
/// Ignore either capability (read/write) or type mismatch in conversion
400-
/// between two key path types.
401-
IgnoreKeyPathContextualMismatch,
402-
403399
/// Ignore a type mismatch between deduced element type and externally
404400
/// imposed one.
405401
IgnoreCollectionElementContextualMismatch,
@@ -1193,36 +1189,6 @@ class GenericArgumentsMismatch final
11931189
}
11941190
};
11951191

1196-
/// Detect situations where key path doesn't have capability required
1197-
/// by the context e.g. read-only vs. writable, or either root or value
1198-
/// types are incorrect e.g.
1199-
///
1200-
/// ```swift
1201-
/// struct S { let foo: Int }
1202-
/// let _: WritableKeyPath<S, Int> = \.foo
1203-
/// ```
1204-
///
1205-
/// Here context requires a writable key path but `foo` property is
1206-
/// read-only.
1207-
class KeyPathContextualMismatch final : public ContextualMismatch {
1208-
KeyPathContextualMismatch(ConstraintSystem &cs, Type lhs, Type rhs,
1209-
ConstraintLocator *locator)
1210-
: ContextualMismatch(cs, FixKind::IgnoreKeyPathContextualMismatch, lhs,
1211-
rhs, locator) {}
1212-
1213-
public:
1214-
std::string getName() const override {
1215-
return "fix key path contextual mismatch";
1216-
}
1217-
1218-
static KeyPathContextualMismatch *
1219-
create(ConstraintSystem &cs, Type lhs, Type rhs, ConstraintLocator *locator);
1220-
1221-
static bool classof(const ConstraintFix *fix) {
1222-
return fix->getKind() == FixKind::IgnoreKeyPathContextualMismatch;
1223-
}
1224-
};
1225-
12261192
/// Detect situations when argument of the @autoclosure parameter is itself
12271193
/// marked as @autoclosure and is not applied. Form a fix which suggests a
12281194
/// proper way to forward such arguments, e.g.:

lib/Sema/CSBindings.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2060,8 +2060,15 @@ bool TypeVarBindingProducer::computeNext() {
20602060

20612061
for (auto supertype : enumerateDirectSupertypes(type)) {
20622062
// If we're not allowed to try this binding, skip it.
2063-
if (auto simplifiedSuper = checkTypeOfBinding(TypeVar, supertype))
2064-
addNewBinding(binding.withType(*simplifiedSuper));
2063+
if (auto simplifiedSuper = checkTypeOfBinding(TypeVar, supertype)) {
2064+
auto supertype = *simplifiedSuper;
2065+
// A key path type cannot be bound to type-erased key path variants.
2066+
if (TypeVar->getImpl().isKeyPathType() &&
2067+
(supertype->isPartialKeyPath() || supertype->isAnyKeyPath()))
2068+
continue;
2069+
2070+
addNewBinding(binding.withType(supertype));
2071+
}
20652072
}
20662073
}
20672074
}

lib/Sema/CSDiagnostics.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1493,10 +1493,10 @@ bool MemberAccessOnOptionalBaseFailure::diagnoseAsError() {
14931493
} else {
14941494
emitDiagnostic(diag::invalid_optional_inferred_keypath_root, baseType,
14951495
Member, unwrappedBaseType);
1496-
emitDiagnostic(diag::optional_key_path_root_base_chain, Member)
1497-
.fixItInsert(sourceRange.End, "?.");
1498-
emitDiagnostic(diag::optional_key_path_root_base_unwrap, Member)
1499-
.fixItInsert(sourceRange.End, "!.");
1496+
// Note that unwrapping fix-its cannot be suggested in this case
1497+
// because neither `.?` nor `.!` can be used to start a key path
1498+
// literal. Suggesting an explicit type here won't work either
1499+
// because contextual root is going to be optional still.
15001500
}
15011501
} else {
15021502
// Check whether or not the base of this optional unwrap is implicit self

lib/Sema/CSFix.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1296,13 +1296,6 @@ AllowInvalidRefInKeyPath::create(ConstraintSystem &cs, RefKind kind,
12961296
AllowInvalidRefInKeyPath(cs, kind, member, locator);
12971297
}
12981298

1299-
KeyPathContextualMismatch *
1300-
KeyPathContextualMismatch::create(ConstraintSystem &cs, Type lhs, Type rhs,
1301-
ConstraintLocator *locator) {
1302-
return new (cs.getAllocator())
1303-
KeyPathContextualMismatch(cs, lhs, rhs, locator);
1304-
}
1305-
13061299
bool RemoveAddressOf::diagnose(const Solution &solution, bool asNote) const {
13071300
InvalidUseOfAddressOf failure(solution, getFromType(), getToType(),
13081301
getLocator());

lib/Sema/CSSimplify.cpp

Lines changed: 95 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6081,14 +6081,6 @@ bool ConstraintSystem::repairFailures(
60816081
if (repairViaBridgingCast(*this, lhs, rhs, conversionsOrFixes, locator))
60826082
break;
60836083

6084-
// If both types are key path, the only differences
6085-
// between them are mutability and/or root, value type mismatch.
6086-
if (isKnownKeyPathType(lhs) && isKnownKeyPathType(rhs)) {
6087-
auto *fix = KeyPathContextualMismatch::create(
6088-
*this, lhs, rhs, getConstraintLocator(locator));
6089-
conversionsOrFixes.push_back(fix);
6090-
}
6091-
60926084
if (lhs->is<FunctionType>() && !rhs->is<AnyFunctionType>() &&
60936085
isExpr<ClosureExpr>(anchor)) {
60946086
auto *fix = ContextualMismatch::create(*this, lhs, rhs,
@@ -6731,8 +6723,20 @@ bool ConstraintSystem::repairFailures(
67316723
if (hasFixFor(lastCompLoc, FixKind::AllowTypeOrInstanceMember))
67326724
return true;
67336725

6726+
auto *keyPathLoc = getConstraintLocator(anchor);
6727+
6728+
if (hasFixFor(keyPathLoc))
6729+
return true;
6730+
6731+
if (auto contextualInfo = getContextualTypeInfo(anchor)) {
6732+
if (hasFixFor(getConstraintLocator(
6733+
keyPathLoc,
6734+
LocatorPathElt::ContextualType(contextualInfo->purpose))))
6735+
return true;
6736+
}
6737+
67346738
conversionsOrFixes.push_back(IgnoreContextualType::create(
6735-
*this, lhs, rhs, getConstraintLocator(anchor)));
6739+
*this, lhs, rhs, keyPathLoc));
67366740
break;
67376741
}
67386742
default:
@@ -10964,6 +10968,12 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
1096410968
if (result.ViableCandidates.empty() && result.UnviableCandidates.empty())
1096510969
return fixMissingMember(origBaseTy, memberTy, locator);
1096610970

10971+
bool baseIsKeyPathRootType = [&]() {
10972+
auto keyPathComponent =
10973+
locator->getLastElementAs<LocatorPathElt::KeyPathComponent>();
10974+
return keyPathComponent && keyPathComponent->getIndex() == 0;
10975+
}();
10976+
1096710977
// The result of the member access can either be the expected member type
1096810978
// (for '!' or optional members with '?'), or the original member type
1096910979
// with one extra level of optionality ('?' with non-optional members).
@@ -10977,13 +10987,17 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
1097710987
*this, ConstraintKind::Bind,
1097810988
UnwrapOptionalBase::create(*this, member, baseObjTy, locator),
1097910989
memberTy, innerTV, locator);
10980-
auto optionalResult = Constraint::createFixed(
10981-
*this, ConstraintKind::Bind,
10982-
UnwrapOptionalBase::createWithOptionalResult(*this, member,
10983-
baseObjTy, locator),
10984-
optTy, memberTy, locator);
1098510990
optionalities.push_back(nonoptionalResult);
10986-
optionalities.push_back(optionalResult);
10991+
10992+
if (!baseIsKeyPathRootType) {
10993+
auto optionalResult = Constraint::createFixed(
10994+
*this, ConstraintKind::Bind,
10995+
UnwrapOptionalBase::createWithOptionalResult(*this, member,
10996+
baseObjTy, locator),
10997+
optTy, memberTy, locator);
10998+
optionalities.push_back(optionalResult);
10999+
}
11000+
1098711001
addDisjunctionConstraint(optionalities, locator);
1098811002

1098911003
// Look through one level of optional.
@@ -11759,6 +11773,13 @@ bool ConstraintSystem::resolveKeyPath(TypeVariableType *typeVar,
1175911773
contextualType = BoundGenericType::get(
1176011774
keyPathTy->getDecl(), keyPathTy->getParent(), {root, value});
1176111775
}
11776+
} else if (contextualType->isPlaceholder()) {
11777+
auto root = simplifyType(getKeyPathRootType(keyPath));
11778+
if (!(root->isTypeVariableOrMember() || root->isPlaceholder())) {
11779+
auto value = getKeyPathValueType(keyPath);
11780+
contextualType =
11781+
BoundGenericType::get(ctx.getKeyPathDecl(), Type(), {root, value});
11782+
}
1176211783
}
1176311784
}
1176411785

@@ -12273,12 +12294,14 @@ ConstraintSystem::simplifyKeyPathConstraint(
1227312294
}
1227412295

1227512296
if (boundRoot &&
12276-
matchTypes(boundRoot, rootTy, ConstraintKind::Bind, subflags, locator)
12297+
matchTypes(rootTy, boundRoot, ConstraintKind::Bind, subflags,
12298+
locator.withPathElement(ConstraintLocator::KeyPathRoot))
1227712299
.isFailure())
1227812300
return false;
1227912301

1228012302
if (boundValue &&
12281-
matchTypes(boundValue, valueTy, ConstraintKind::Bind, subflags, locator)
12303+
matchTypes(valueTy, boundValue, ConstraintKind::Bind, subflags,
12304+
locator.withPathElement(ConstraintLocator::KeyPathValue))
1228212305
.isFailure())
1228312306
return false;
1228412307

@@ -12329,6 +12352,16 @@ ConstraintSystem::simplifyKeyPathConstraint(
1232912352
tv->getImpl().canBindToHole();
1233012353
})) {
1233112354
(void)tryMatchRootAndValueFromType(keyPathTy);
12355+
12356+
// If the type of the key path is not yet resolved simplifying this
12357+
// constraint would disconnect it from root and value, let's bind it
12358+
// to a placeholder type to make sure this doesn't happen.
12359+
if (auto *typeVar = keyPathTy->getAs<TypeVariableType>()) {
12360+
return matchTypes(keyPathTy, PlaceholderType::get(Context, typeVar),
12361+
ConstraintKind::Bind, subflags,
12362+
locator.getBaseLocator());
12363+
}
12364+
1233212365
return SolutionKind::Solved;
1233312366
}
1233412367
}
@@ -12487,21 +12520,58 @@ ConstraintSystem::simplifyKeyPathConstraint(
1248712520
kpDecl = getASTContext().getWritableKeyPathDecl();
1248812521
}
1248912522

12523+
auto formUnsolved = [&]() {
12524+
addUnsolvedConstraint(Constraint::create(
12525+
*this, ConstraintKind::KeyPath, keyPathTy, rootTy, valueTy,
12526+
locator.getBaseLocator(), componentTypeVars));
12527+
};
12528+
1249012529
auto loc = locator.getBaseLocator();
1249112530
if (definitelyFunctionType) {
1249212531
increaseScore(SK_FunctionConversion, locator);
1249312532
return SolutionKind::Solved;
1249412533
} else if (!anyComponentsUnresolved ||
1249512534
(definitelyKeyPathType && capability == ReadOnly)) {
12496-
auto resolvedKPTy =
12497-
BoundGenericType::get(kpDecl, nullptr, {rootTy, valueTy});
12498-
return matchTypes(resolvedKPTy, keyPathTy, ConstraintKind::Bind, subflags,
12499-
loc);
12535+
// If key path is connected to a disjunction (i.e. through coercion
12536+
// or used as an argument to a function/subscipt call) it cannot be
12537+
// bound until the choice is selected because it's undetermined
12538+
// until then whether key path is implicitly converted to a function
12539+
// type or not.
12540+
//
12541+
// \code
12542+
// struct Data {
12543+
// var value: Int = 42
12544+
// }
12545+
//
12546+
// func test<S: Sequence>(_: S, _: (S.Element) -> Int) {}
12547+
// func test<C: Collection>(_: C, _: (C.Element) -> Int) {}
12548+
//
12549+
// func test(arr: [Data]) {
12550+
// test(arr, \Data.value)
12551+
// }
12552+
// \endcode
12553+
//
12554+
// In this example if we did allow to bind the key path before
12555+
// an overload of `test` is selected, we'd end up with no solutions
12556+
// because the type of the key path expression is actually: '(Data) -> Int'
12557+
// and not 'WritableKeyPath<Data, Int>`.
12558+
auto *typeVar = keyPathTy->getAs<TypeVariableType>();
12559+
if (typeVar && findConstraintThroughOptionals(
12560+
typeVar, OptionalWrappingDirection::Unwrap,
12561+
[&](Constraint *match, TypeVariableType *) {
12562+
return match->getKind() ==
12563+
ConstraintKind::ApplicableFunction ||
12564+
match->getKind() == ConstraintKind::Disjunction;
12565+
})) {
12566+
formUnsolved();
12567+
} else {
12568+
auto resolvedKPTy =
12569+
BoundGenericType::get(kpDecl, nullptr, {rootTy, valueTy});
12570+
return matchTypes(resolvedKPTy, keyPathTy, ConstraintKind::Bind,
12571+
subflags, loc);
12572+
}
1250012573
} else {
12501-
addUnsolvedConstraint(Constraint::create(*this, ConstraintKind::KeyPath,
12502-
keyPathTy, rootTy, valueTy,
12503-
locator.getBaseLocator(),
12504-
componentTypeVars));
12574+
formUnsolved();
1250512575
}
1250612576
return SolutionKind::Solved;
1250712577
}
@@ -15110,7 +15180,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1511015180
case FixKind::RemoveExtraneousArguments:
1511115181
case FixKind::SpecifyTypeForPlaceholder:
1511215182
case FixKind::AllowAutoClosurePointerConversion:
15113-
case FixKind::IgnoreKeyPathContextualMismatch:
1511415183
case FixKind::NotCompileTimeConst:
1511515184
case FixKind::RenameConflictingPatternVariables:
1511615185
case FixKind::MustBeCopyable:

test/Constraints/keypath.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,9 +255,11 @@ func rdar32101765() {
255255
}
256256

257257
let _: KeyPath<R32101765, Float> = \.prop32101765
258-
// expected-error@-1 {{key path value type 'Int' cannot be converted to contextual type 'Float'}}
258+
// expected-error@-1 {{cannot assign value of type 'KeyPath<R32101765, Int>' to type 'KeyPath<R32101765, Float>'}}
259+
// expected-note@-2 {{arguments to generic parameter 'Value' ('Int' and 'Float') are expected to be equal}}
259260
let _: KeyPath<R32101765, Float> = \R32101765.prop32101765
260-
// expected-error@-1 {{key path value type 'Int' cannot be converted to contextual type 'Float'}}
261+
// expected-error@-1 {{cannot assign value of type 'KeyPath<R32101765, Int>' to type 'KeyPath<R32101765, Float>'}}
262+
// expected-note@-2 {{arguments to generic parameter 'Value' ('Int' and 'Float') are expected to be equal}}
261263
let _: KeyPath<R32101765, Float> = \.prop32101765.unknown
262264
// expected-error@-1 {{type 'Int' has no member 'unknown'}}
263265
let _: KeyPath<R32101765, Float> = \R32101765.prop32101765.unknown

test/expr/unary/keypath/keypath.swift

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -794,9 +794,11 @@ func test_keypath_with_method_refs() {
794794
}
795795

796796
let _: KeyPath<S, Int> = \.foo // expected-error {{key path cannot refer to instance method 'foo()'}}
797-
// expected-error@-1 {{key path value type '() -> Int' cannot be converted to contextual type 'Int'}}
797+
// expected-error@-1 {{cannot assign value of type 'KeyPath<S, () -> Int>' to type 'KeyPath<S, Int>'}}
798+
// expected-note@-2 {{arguments to generic parameter 'Value' ('() -> Int' and 'Int') are expected to be equal}}
798799
let _: KeyPath<S, Int> = \.bar // expected-error {{key path cannot refer to static method 'bar()'}}
799-
// expected-error@-1 {{key path value type '() -> Int' cannot be converted to contextual type 'Int'}}
800+
// expected-error@-1 {{cannot assign value of type 'KeyPath<S, () -> Int>' to type 'KeyPath<S, Int>'}}
801+
// expected-note@-2 {{arguments to generic parameter 'Value' ('() -> Int' and 'Int') are expected to be equal}}
800802
let _ = \S.Type.bar // expected-error {{key path cannot refer to static method 'bar()'}}
801803

802804
struct A {
@@ -1008,16 +1010,10 @@ func testMemberAccessOnOptionalKeyPathComponent() {
10081010
func kp(_: KeyPath<String?, Int>) {}
10091011

10101012
kp(\.count) // expected-error {{key path root inferred as optional type 'String?' must be unwrapped to refer to member 'count' of unwrapped type 'String'}}
1011-
// expected-note@-1 {{chain the optional using '?.' to access unwrapped type member 'count'}} {{8-8=?.}}
1012-
// expected-note@-2 {{unwrap the optional using '!.' to access unwrapped type member 'count'}} {{8-8=!.}}
10131013
let _ : KeyPath<String?, Int> = \.count // expected-error {{key path root inferred as optional type 'String?' must be unwrapped to refer to member 'count' of unwrapped type 'String'}}
1014-
// expected-note@-1 {{chain the optional using '?.' to access unwrapped type member 'count'}} {{37-37=?.}}
1015-
// expected-note@-2 {{unwrap the optional using '!.' to access unwrapped type member 'count'}} {{37-37=!.}}
10161014

10171015
let _ : KeyPath<String?, Int> = \.utf8.count
10181016
// expected-error@-1 {{key path root inferred as optional type 'String?' must be unwrapped to refer to member 'utf8' of unwrapped type 'String'}}
1019-
// expected-note@-2 {{chain the optional using '?.' to access unwrapped type member 'utf8'}} {{37-37=?.}}
1020-
// expected-note@-3 {{unwrap the optional using '!.' to access unwrapped type member 'utf8'}} {{37-37=!.}}
10211017
}
10221018

10231019
func testSyntaxErrors() {
@@ -1067,8 +1063,9 @@ func f_56996() {
10671063
// https://github.com/apple/swift/issues/55805
10681064
// Key-path missing optional crashes compiler: Inactive constraints left over?
10691065
func f_55805() {
1070-
let _: KeyPath<String?, Int?> = \.utf8.count // expected-error {{value of optional type 'String.UTF8View?' must be unwrapped to refer to member 'count' of wrapped base type 'String.UTF8View'}}
1071-
// expected-note@-1 {{chain the optional using '?' to access member 'count' only for non-'nil' base values}}
1066+
let _: KeyPath<String?, Int?> = \.utf8.count
1067+
// expected-error@-1 {{key path root inferred as optional type 'String?' must be unwrapped to refer to member 'utf8' of unwrapped type 'String'}}
1068+
// expected-error@-2 {{key path value type 'Int' cannot be converted to contextual type 'Int?'}}
10721069
}
10731070

10741071
// rdar://74711236 - crash due to incorrect member access in key path
@@ -1202,3 +1199,28 @@ func test_keypath_inference_from_existentials() {
12021199
test(\.other, 42) // Ok
12031200
test(\.member, "") // Ok
12041201
}
1202+
1203+
// rdar://116376651 - key path type is bound before context is fully resolved.
1204+
func keypath_to_func_conversion_as_arg_to_overloaded_func() {
1205+
struct Data {
1206+
var value: Int = 42
1207+
}
1208+
1209+
func test<S: Sequence>(_: S, _: (S.Element) -> Int) {}
1210+
func test<C: Collection>(_: C, _: (C.Element) -> Int) {}
1211+
1212+
func test(arr: [Data]) {
1213+
test(arr, \Data.value) // Ok
1214+
}
1215+
}
1216+
1217+
// https://github.com/apple/swift/issues/55436
1218+
func test_keypath_coercion_to_function() {
1219+
struct User {
1220+
let email: String
1221+
}
1222+
1223+
let users = [User]()
1224+
let fn = \User.email as (User) -> String // Ok
1225+
_ = users.map(fn) // Ok
1226+
}

test/expr/unary/keypath/salvage-with-other-type-errors.swift

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
struct P<T: K> { }
77

88
struct S {
9-
init<B>(_ a: P<B>) { // expected-note {{in call to initializer}}
9+
init<B>(_ a: P<B>) { // expected-note {{where 'B' = 'String'}}
1010
fatalError()
1111
}
1212
}
@@ -27,9 +27,8 @@ struct A {
2727
}
2828

2929
extension A: K {
30-
static let j = S(\A.id + "id") // expected-error {{generic parameter 'B' could not be inferred}}
31-
// expected-error@-1 {{binary operator '+' cannot be applied to operands of type 'KeyPath<A, String>' and 'String'}}
32-
// expected-note@-2 {{overloads for '+' exist with these partially matching parameter lists: (String, String)}}
30+
static let j = S(\A.id + "id")
31+
// expected-error@-1 {{initializer 'init(_:)' requires that 'String' conform to 'K'}}
3332
}
3433

3534
// https://github.com/apple/swift/issues/47610

0 commit comments

Comments
 (0)