Skip to content

Commit 79ac431

Browse files
authored
Merge pull request #69742 from xedin/transitive-keypath-base-inference
[ConstraintSystem] More changes to key path inference
2 parents 2e14df5 + 9c5d465 commit 79ac431

File tree

13 files changed

+252
-244
lines changed

13 files changed

+252
-244
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,10 @@ ERROR(cannot_convert_argument_value,none,
376376
"cannot convert value of type %0 to expected argument type %1",
377377
(Type,Type))
378378

379+
ERROR(cannot_convert_unresolved_key_path_argument_value,none,
380+
"cannot convert value of key path type to expected argument type %0",
381+
(Type))
382+
379383
ERROR(cannot_convert_argument_value_for_swift_func,none,
380384
"cannot convert value of type %0 to expected argument type %1 "
381385
"because %kind2 was not imported from C header",

include/swift/Sema/CSBindings.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ enum class LiteralBindingKind : uint8_t {
7171
/// along with information that can be used to construct related
7272
/// bindings, e.g., the supertypes of a given type.
7373
struct PotentialBinding {
74+
friend class BindingSet;
75+
7476
/// The type to which the type variable can be bound.
7577
Type BindingType;
7678

include/swift/Sema/ConstraintSystem.h

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,10 @@ class TypeVariableType::Implementation {
491491
/// a type of a key path expression.
492492
bool isKeyPathType() const;
493493

494+
/// Determine whether this type variable represents a root type of a key path
495+
/// expression.
496+
bool isKeyPathRoot() const;
497+
494498
/// Determine whether this type variable represents a value type of a key path
495499
/// expression.
496500
bool isKeyPathValue() const;
@@ -4008,20 +4012,6 @@ class ConstraintSystem {
40084012
bool resolveClosure(TypeVariableType *typeVar, Type contextualType,
40094013
ConstraintLocatorBuilder locator);
40104014

4011-
/// Given the fact a contextual type is now available for the type
4012-
/// variable representing one of the key path expressions, let's set a
4013-
/// pre-determined key path expression type.
4014-
///
4015-
/// \param typeVar The type variable representing a key path expression.
4016-
/// \param contextualType The contextual type this key path would be
4017-
/// converted to.
4018-
/// \param locator The locator associated with contextual type.
4019-
///
4020-
/// \returns `true` if it was possible to generate constraints for
4021-
/// the keyPath expression, `false` otherwise.
4022-
bool resolveKeyPath(TypeVariableType *typeVar, Type contextualType,
4023-
ConstraintLocatorBuilder locator);
4024-
40254015
/// Given the fact that contextual type is now available for the type
40264016
/// variable representing a pack expansion type, let's resolve the expansion.
40274017
///

lib/Sema/CSBindings.cpp

Lines changed: 139 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ bool BindingSet::isDelayed() const {
9696

9797
// Delay key path literal type binding until there is at least
9898
// one contextual binding (or default is promoted into a binding).
99-
if (TypeVar->getImpl().isKeyPathType() && Bindings.empty())
99+
if (TypeVar->getImpl().isKeyPathType() && !Defaults.empty())
100100
return true;
101101

102102
if (isHole()) {
@@ -178,7 +178,7 @@ bool BindingSet::isPotentiallyIncomplete() const {
178178
// contextual type or key path is resolved enough to infer
179179
// capability and promote default into a binding.
180180
if (TypeVar->getImpl().isKeyPathType())
181-
return Bindings.empty();
181+
return !Defaults.empty();
182182

183183
// If current type variable is associated with a code completion token
184184
// it's possible that it doesn't have enough contextual information
@@ -430,6 +430,41 @@ void BindingSet::inferTransitiveBindings(
430430
&inferredBindings) {
431431
using BindingKind = AllowedBindingKind;
432432

433+
// If the current type variable represents a key path root type
434+
// let's try to transitively infer its type through bindings of
435+
// a key path type.
436+
if (TypeVar->getImpl().isKeyPathRoot()) {
437+
auto *locator = TypeVar->getImpl().getLocator();
438+
if (auto *keyPathTy =
439+
CS.getType(locator->getAnchor())->getAs<TypeVariableType>()) {
440+
auto keyPathBindings = inferredBindings.find(keyPathTy);
441+
if (keyPathBindings != inferredBindings.end()) {
442+
auto &bindings = keyPathBindings->getSecond();
443+
444+
for (auto &binding : bindings.Bindings) {
445+
auto bindingTy = binding.BindingType->lookThroughAllOptionalTypes();
446+
447+
Type inferredRootTy;
448+
if (isKnownKeyPathType(bindingTy)) {
449+
// AnyKeyPath doesn't have a root type.
450+
if (bindingTy->isAnyKeyPath())
451+
continue;
452+
453+
auto *BGT = bindingTy->castTo<BoundGenericType>();
454+
inferredRootTy = BGT->getGenericArgs()[0];
455+
} else if (auto *fnType = bindingTy->getAs<FunctionType>()) {
456+
if (fnType->getNumParams() == 1)
457+
inferredRootTy = fnType->getParams()[0].getParameterType();
458+
}
459+
460+
if (inferredRootTy && !inferredRootTy->isTypeVariableOrMember())
461+
addBinding(
462+
binding.withSameSource(inferredRootTy, BindingKind::Exact));
463+
}
464+
}
465+
}
466+
}
467+
433468
for (const auto &entry : Info.SupertypeOf) {
434469
auto relatedBindings = inferredBindings.find(entry.first);
435470
if (relatedBindings == inferredBindings.end())
@@ -496,6 +531,24 @@ void BindingSet::inferTransitiveBindings(
496531
}
497532
}
498533

534+
static BoundGenericType *getKeyPathType(ASTContext &ctx,
535+
KeyPathCapability capability,
536+
Type rootType, Type valueType) {
537+
switch (capability) {
538+
case KeyPathCapability::ReadOnly:
539+
return BoundGenericType::get(ctx.getKeyPathDecl(), /*parent=*/Type(),
540+
{rootType, valueType});
541+
542+
case KeyPathCapability::Writable:
543+
return BoundGenericType::get(ctx.getWritableKeyPathDecl(),
544+
/*parent=*/Type(), {rootType, valueType});
545+
546+
case KeyPathCapability::ReferenceWritable:
547+
return BoundGenericType::get(ctx.getReferenceWritableKeyPathDecl(),
548+
/*parent=*/Type(), {rootType, valueType});
549+
}
550+
}
551+
499552
void BindingSet::finalize(
500553
llvm::SmallDenseMap<TypeVariableType *, BindingSet> &inferredBindings) {
501554
inferTransitiveBindings(inferredBindings);
@@ -538,6 +591,84 @@ void BindingSet::finalize(
538591
}
539592
}
540593

594+
if (TypeVar->getImpl().isKeyPathType()) {
595+
auto &ctx = CS.getASTContext();
596+
597+
auto *keyPathLoc = TypeVar->getImpl().getLocator();
598+
auto *keyPath = castToExpr<KeyPathExpr>(keyPathLoc->getAnchor());
599+
600+
bool isValid;
601+
llvm::Optional<KeyPathCapability> capability;
602+
603+
std::tie(isValid, capability) = CS.inferKeyPathLiteralCapability(TypeVar);
604+
605+
// Key path literal is not yet sufficiently resolved.
606+
if (isValid && !capability)
607+
return;
608+
609+
// If the key path is sufficiently resolved we can add inferred binding
610+
// to the set.
611+
SmallSetVector<PotentialBinding, 4> updatedBindings;
612+
for (const auto &binding : Bindings) {
613+
auto bindingTy = binding.BindingType->lookThroughAllOptionalTypes();
614+
615+
assert(isKnownKeyPathType(bindingTy) || bindingTy->is<FunctionType>());
616+
617+
// Functions don't have capability so we can simply add them.
618+
if (bindingTy->is<FunctionType>())
619+
updatedBindings.insert(binding);
620+
}
621+
622+
// Note that even though key path literal maybe be invalid it's
623+
// still the best course of action to use contextual function type
624+
// bindings because they allow to propagate type information from
625+
// the key path into the context, so key path bindings are addded
626+
// only if there is absolutely no other choice.
627+
if (updatedBindings.empty()) {
628+
auto rootTy = CS.getKeyPathRootType(keyPath);
629+
630+
// A valid key path literal.
631+
if (capability) {
632+
// Note that the binding is formed using root & value
633+
// type variables produced during constraint generation
634+
// because at this point root is already known (otherwise
635+
// inference wouldn't been able to determine key path's
636+
// capability) and we always want to infer value from
637+
// the key path and match it to a contextual type to produce
638+
// better diagnostics.
639+
auto keyPathTy = getKeyPathType(ctx, *capability, rootTy,
640+
CS.getKeyPathValueType(keyPath));
641+
updatedBindings.insert(
642+
{keyPathTy, AllowedBindingKind::Exact, keyPathLoc});
643+
} else if (CS.shouldAttemptFixes()) {
644+
// If key path is structurally correct and has a resolved root
645+
// type, let's promote the fallback type into a binding because
646+
// root would have been inferred from explicit type already and
647+
// it's benefitial for diagnostics to assign a non-placeholder
648+
// type to key path literal to propagate root/value to the context.
649+
if (!keyPath->hasSingleInvalidComponent() &&
650+
(keyPath->getParsedRoot() ||
651+
!CS.getFixedType(rootTy)->isTypeVariableOrMember())) {
652+
auto fallback = llvm::find_if(Defaults, [](const auto &entry) {
653+
return entry.second->getKind() == ConstraintKind::FallbackType;
654+
});
655+
assert(fallback != Defaults.end());
656+
updatedBindings.insert(
657+
{fallback->first, AllowedBindingKind::Exact, fallback->second});
658+
} else {
659+
updatedBindings.insert(PotentialBinding::forHole(
660+
TypeVar, CS.getConstraintLocator(
661+
keyPath, ConstraintLocator::FallbackType)));
662+
}
663+
}
664+
}
665+
666+
Bindings = std::move(updatedBindings);
667+
Defaults.clear();
668+
669+
return;
670+
}
671+
541672
if (CS.shouldAttemptFixes() &&
542673
locator->isLastElement<LocatorPathElt::UnresolvedMemberChainResult>()) {
543674
// Let's see whether this chain is valid, if it isn't then to avoid
@@ -795,6 +926,12 @@ llvm::Optional<BindingSet> ConstraintSystem::determineBestBindings(
795926
auto isViableForRanking = [this](const BindingSet &bindings) -> bool {
796927
auto *typeVar = bindings.getTypeVariable();
797928

929+
// Key path root type variable is always viable because it can be
930+
// transitively inferred from key path type during binding set
931+
// finalization.
932+
if (typeVar->getImpl().isKeyPathRoot())
933+
return true;
934+
798935
// Type variable representing a base of unresolved member chain should
799936
// always be considered viable for ranking since it's allow to infer
800937
// types from transitive protocol requirements.
@@ -886,60 +1023,6 @@ void PotentialBindings::addDefault(Constraint *constraint) {
8861023

8871024
void BindingSet::addDefault(Constraint *constraint) {
8881025
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-
9431026
Defaults.insert({defaultTy->getCanonicalType(), constraint});
9441027
}
9451028

lib/Sema/CSDiagnostics.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2604,6 +2604,11 @@ bool ContextualFailure::diagnoseAsError() {
26042604
if (diagnoseYieldByReferenceMismatch())
26052605
return true;
26062606

2607+
if (isExpr<KeyPathExpr>(anchor)) {
2608+
diagnostic = diag::expr_keypath_type_covert_to_contextual_type;
2609+
break;
2610+
}
2611+
26072612
if (isExpr<OptionalTryExpr>(anchor) ||
26082613
isExpr<OptionalEvaluationExpr>(anchor)) {
26092614
auto objectType = fromType->getOptionalObjectType();
@@ -7290,6 +7295,18 @@ bool ArgumentMismatchFailure::diagnoseAsError() {
72907295

72917296
auto argType = getFromType();
72927297

7298+
// Unresolved key path argument requires a tailored diagnostic
7299+
// that doesn't mention a fallback type - `KeyPath<_, _>`.
7300+
if (argType->isKeyPath() && !isKnownKeyPathType(paramType)) {
7301+
auto keyPathTy = argType->castTo<BoundGenericType>();
7302+
auto rootTy = keyPathTy->getGenericArgs()[0];
7303+
if (rootTy->is<UnresolvedType>()) {
7304+
emitDiagnostic(diag::cannot_convert_unresolved_key_path_argument_value,
7305+
paramType);
7306+
return true;
7307+
}
7308+
}
7309+
72937310
if (paramType->isAnyObject()) {
72947311
emitDiagnostic(diag::cannot_convert_argument_value_anyobject, argType,
72957312
paramType);

lib/Sema/CSGen.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3853,7 +3853,10 @@ namespace {
38533853
// constraint system so we can find it later.
38543854
CS.setType(E, i, base);
38553855
}
3856-
3856+
3857+
auto valueLocator =
3858+
CS.getConstraintLocator(E, ConstraintLocator::KeyPathValue);
3859+
38573860
// If there was an optional chaining component, the end result must be
38583861
// optional.
38593862
if (didOptionalChain) {
@@ -3862,8 +3865,7 @@ namespace {
38623865
componentTypeVars.push_back(objTy);
38633866

38643867
auto optTy = OptionalType::get(objTy);
3865-
CS.addConstraint(ConstraintKind::Conversion, base, optTy,
3866-
locator);
3868+
CS.addConstraint(ConstraintKind::Conversion, base, optTy, valueLocator);
38673869
base = optTy;
38683870
}
38693871

@@ -3873,8 +3875,7 @@ namespace {
38733875
(void)CS.recordFix(AllowKeyPathWithoutComponents::create(CS, locator));
38743876
}
38753877

3876-
auto valueLocator =
3877-
CS.getConstraintLocator(E, ConstraintLocator::KeyPathValue);
3878+
38783879
auto *value = CS.createTypeVariable(valueLocator, TVO_CanBindToNoEscape |
38793880
TVO_CanBindToHole);
38803881
CS.addConstraint(ConstraintKind::Equal, base, value, valueLocator);

0 commit comments

Comments
 (0)