Skip to content

Commit ca53be7

Browse files
authored
Merge pull request #69405 from xedin/rework-keypath-type-inference
[ConstraintSystem] Initial rework of key path inference
2 parents 9d846a0 + 7363812 commit ca53be7

File tree

14 files changed

+343
-284
lines changed

14 files changed

+343
-284
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -678,9 +678,12 @@ ERROR(expr_swift_keypath_not_starting_with_type,none,
678678
ERROR(expr_swift_keypath_not_starting_with_dot,none,
679679
"a Swift key path with contextual root must begin with a leading dot",
680680
())
681-
ERROR(expr_smart_keypath_value_covert_to_contextual_type,none,
681+
ERROR(expr_keypath_value_covert_to_contextual_type,none,
682682
"key path value type %0 cannot be converted to contextual type %1",
683683
(Type, Type))
684+
ERROR(expr_keypath_type_covert_to_contextual_type,none,
685+
"cannot convert key path type %0 to contextual type %1",
686+
(Type, Type))
684687
ERROR(expr_swift_keypath_empty, none,
685688
"key path must have at least one component", ())
686689
ERROR(expr_string_interpolation_outside_string,none,

include/swift/Sema/CSBindings.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -620,10 +620,7 @@ class BindingSet {
620620

621621
void addLiteralRequirement(Constraint *literal);
622622

623-
void addDefault(Constraint *constraint) {
624-
auto defaultTy = constraint->getSecondType();
625-
Defaults.insert({defaultTy->getCanonicalType(), constraint});
626-
}
623+
void addDefault(Constraint *constraint);
627624

628625
/// Check whether the given binding set covers any of the
629626
/// literal protocols associated with this type variable.

include/swift/Sema/ConstraintLocatorPathElts.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,9 @@ SIMPLE_LOCATOR_PATH_ELT(ThrownErrorType)
281281
/// A type coercion operand.
282282
SIMPLE_LOCATOR_PATH_ELT(CoercionOperand)
283283

284+
/// A fallback type for some AST location (i.e. key path literal).
285+
SIMPLE_LOCATOR_PATH_ELT(FallbackType)
286+
284287
#undef LOCATOR_PATH_ELT
285288
#undef CUSTOM_LOCATOR_PATH_ELT
286289
#undef SIMPLE_LOCATOR_PATH_ELT

include/swift/Sema/ConstraintSystem.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,12 @@ enum TypeVariableOptions {
339339
TVO_PackExpansion = 0x40,
340340
};
341341

342+
enum class KeyPathCapability : uint8_t {
343+
ReadOnly,
344+
Writable,
345+
ReferenceWritable
346+
};
347+
342348
/// The implementation object for a type variable used within the
343349
/// constraint-solving type checker.
344350
///
@@ -5513,6 +5519,25 @@ class ConstraintSystem {
55135519
bool isMemberAvailableOnExistential(Type baseTy,
55145520
const ValueDecl *member) const;
55155521

5522+
/// Attempts to infer a capability of a key path (i.e. whether it
5523+
/// is read-only, writable, etc.) based on the referenced members.
5524+
///
5525+
/// \param keyPath The key path literal expression.
5526+
///
5527+
/// \returns `bool` to indicate whether key path is valid or not,
5528+
/// and capability if it could be determined.
5529+
std::pair</*isValid=*/bool, llvm::Optional<KeyPathCapability>>
5530+
inferKeyPathLiteralCapability(KeyPathExpr *keyPath);
5531+
5532+
/// A convenience overload of \c inferKeyPathLiteralCapability.
5533+
///
5534+
/// \param keyPathType The type variable that represents the key path literal.
5535+
///
5536+
/// \returns `bool` to indicate whether key path is valid or not,
5537+
/// and capability if it could be determined.
5538+
std::pair</*isValid=*/bool, llvm::Optional<KeyPathCapability>>
5539+
inferKeyPathLiteralCapability(TypeVariableType *keyPathType);
5540+
55165541
SWIFT_DEBUG_DUMP;
55175542
SWIFT_DEBUG_DUMPER(dump(Expr *));
55185543

lib/Sema/CSBindings.cpp

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ bool BindingSet::isDelayed() const {
9494
}
9595
}
9696

97+
// Delay key path literal type binding until there is at least
98+
// one contextual binding (or default is promoted into a binding).
99+
if (TypeVar->getImpl().isKeyPathType() && Bindings.empty())
100+
return true;
101+
97102
if (isHole()) {
98103
auto *locator = TypeVar->getImpl().getLocator();
99104
assert(locator && "a hole without locator?");
@@ -169,6 +174,12 @@ bool BindingSet::isPotentiallyIncomplete() const {
169174
if (Info.isGenericParameter())
170175
return true;
171176

177+
// Key path literal type is incomplete until there is a
178+
// contextual type or key path is resolved enough to infer
179+
// capability and promote default into a binding.
180+
if (TypeVar->getImpl().isKeyPathType())
181+
return Bindings.empty();
182+
172183
// If current type variable is associated with a code completion token
173184
// it's possible that it doesn't have enough contextual information
174185
// to be resolved to anything so let's delay considering it until everything
@@ -873,6 +884,65 @@ void PotentialBindings::addDefault(Constraint *constraint) {
873884
Defaults.insert(constraint);
874885
}
875886

887+
void BindingSet::addDefault(Constraint *constraint) {
888+
auto defaultTy = constraint->getSecondType();
889+
890+
if (TypeVar->getImpl().isKeyPathType() && Bindings.empty()) {
891+
if (constraint->getKind() == ConstraintKind::FallbackType) {
892+
auto &ctx = CS.getASTContext();
893+
894+
bool isValid;
895+
llvm::Optional<KeyPathCapability> capability;
896+
897+
std::tie(isValid, capability) = CS.inferKeyPathLiteralCapability(TypeVar);
898+
899+
if (!isValid) {
900+
// If one of the references in a key path is invalid let's add
901+
// a placeholder binding in diagnostic mode to indicate that
902+
// the key path cannot be properly resolved.
903+
if (CS.shouldAttemptFixes()) {
904+
addBinding({PlaceholderType::get(ctx, TypeVar),
905+
AllowedBindingKind::Exact, constraint});
906+
}
907+
908+
// During normal solving the set has to stay empty.
909+
return;
910+
}
911+
912+
if (capability) {
913+
auto *keyPathType = defaultTy->castTo<BoundGenericType>();
914+
915+
auto root = keyPathType->getGenericArgs()[0];
916+
auto value = keyPathType->getGenericArgs()[1];
917+
918+
switch (*capability) {
919+
case KeyPathCapability::ReadOnly:
920+
break;
921+
922+
case KeyPathCapability::Writable:
923+
keyPathType = BoundGenericType::get(ctx.getWritableKeyPathDecl(),
924+
/*parent=*/Type(), {root, value});
925+
break;
926+
927+
case KeyPathCapability::ReferenceWritable:
928+
keyPathType =
929+
BoundGenericType::get(ctx.getReferenceWritableKeyPathDecl(),
930+
/*parent=*/Type(), {root, value});
931+
break;
932+
}
933+
934+
addBinding({keyPathType, AllowedBindingKind::Exact, constraint});
935+
}
936+
937+
// If key path is not yet sufficiently resolved, don't add any
938+
// bindings.
939+
return;
940+
}
941+
}
942+
943+
Defaults.insert({defaultTy->getCanonicalType(), constraint});
944+
}
945+
876946
bool LiteralRequirement::isCoveredBy(Type type, ConstraintSystem &CS) const {
877947
auto coversDefaultType = [](Type type, Type defaultType) -> bool {
878948
if (!defaultType->hasUnboundGenericType())
@@ -2097,6 +2167,10 @@ bool TypeVarBindingProducer::computeNext() {
20972167
}
20982168

20992169
if (newBindings.empty()) {
2170+
// If key path type had contextual types, let's not attempt fallback.
2171+
if (TypeVar->getImpl().isKeyPathType() && !ExploredTypes.empty())
2172+
return false;
2173+
21002174
// Add defaultable constraints (if any).
21012175
for (auto *constraint : DelayedDefaults) {
21022176
if (constraint->getKind() == ConstraintKind::FallbackType) {

lib/Sema/CSDiagnostics.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2497,7 +2497,7 @@ bool ContextualFailure::diagnoseAsError() {
24972497
if (path.empty()) {
24982498
if (auto *KPE = getAsExpr<KeyPathExpr>(anchor)) {
24992499
emitDiagnosticAt(KPE->getLoc(),
2500-
diag::expr_smart_keypath_value_covert_to_contextual_type,
2500+
diag::expr_keypath_type_covert_to_contextual_type,
25012501
getFromType(), getToType());
25022502
return true;
25032503
}
@@ -2744,6 +2744,11 @@ bool ContextualFailure::diagnoseAsError() {
27442744
break;
27452745
}
27462746

2747+
case ConstraintLocator::KeyPathValue: {
2748+
diagnostic = diag::expr_keypath_value_covert_to_contextual_type;
2749+
break;
2750+
}
2751+
27472752
default:
27482753
return false;
27492754
}
@@ -7646,7 +7651,7 @@ bool ArgumentMismatchFailure::diagnoseKeyPathAsFunctionResultMismatch() const {
76467651
paramFnType->getParams().front().getPlainType()->isEqual(kpRootType)))
76477652
return false;
76487653

7649-
emitDiagnostic(diag::expr_smart_keypath_value_covert_to_contextual_type,
7654+
emitDiagnostic(diag::expr_keypath_value_covert_to_contextual_type,
76507655
kpValueType, paramFnType->getResult());
76517656
return true;
76527657
}

lib/Sema/CSGen.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3742,9 +3742,10 @@ namespace {
37423742
// Allow \Derived.property to be inferred as \Base.property to
37433743
// simulate a sort of covariant conversion from
37443744
// KeyPath<Derived, T> to KeyPath<Base, T>.
3745-
CS.addConstraint(ConstraintKind::Subtype, rootObjectTy, root, locator);
3745+
CS.addConstraint(ConstraintKind::Subtype, rootObjectTy, root,
3746+
rootLocator);
37463747
}
3747-
3748+
37483749
bool didOptionalChain = false;
37493750
// We start optimistically from an lvalue base.
37503751
Type base = LValueType::get(root);
@@ -3866,6 +3867,12 @@ namespace {
38663867
base = optTy;
38673868
}
38683869

3870+
// If we have a malformed KeyPathExpr e.g. let _: KeyPath<A, C> = \A
3871+
// let's record a AllowKeyPathMissingComponent fix.
3872+
if (E->hasSingleInvalidComponent()) {
3873+
(void)CS.recordFix(AllowKeyPathWithoutComponents::create(CS, locator));
3874+
}
3875+
38693876
auto valueLocator =
38703877
CS.getConstraintLocator(E, ConstraintLocator::KeyPathValue);
38713878
auto *value = CS.createTypeVariable(valueLocator, TVO_CanBindToNoEscape |
@@ -3884,6 +3891,13 @@ namespace {
38843891

38853892
CS.addKeyPathConstraint(kpTy, root, value, componentTypeVars, locator);
38863893

3894+
// Add a fallback constraint so we have an anchor to use for
3895+
// capability inference when there is no context.
3896+
CS.addUnsolvedConstraint(Constraint::create(
3897+
CS, ConstraintKind::FallbackType, kpTy,
3898+
BoundGenericType::get(kpDecl, /*parent=*/Type(), {root, value}),
3899+
CS.getConstraintLocator(E, ConstraintLocator::FallbackType)));
3900+
38873901
return kpTy;
38883902
}
38893903

0 commit comments

Comments
 (0)