@@ -57,6 +57,33 @@ bool PotentialBinding::isViableForJoin() const {
57
57
}
58
58
59
59
bool PotentialBindings::isDelayed () const {
60
+ if (auto *locator = TypeVar->getImpl ().getLocator ()) {
61
+ if (locator->isLastElement <LocatorPathElt::MemberRefBase>()) {
62
+ // If first binding is a "fallback" to a protocol type,
63
+ // it means that this type variable should be delayed
64
+ // until it either gains more contextual information, or
65
+ // there are no other type variables to attempt to make
66
+ // forward progress.
67
+ if (Bindings.empty ())
68
+ return true ;
69
+
70
+
71
+ if (Bindings[0 ].BindingType ->is <ProtocolType>())
72
+ return true ;
73
+ }
74
+
75
+ // Since force unwrap preserves l-valueness, resulting
76
+ // type variable has to be delayed until either l-value
77
+ // binding becomes available or there are no other
78
+ // variables to attempt.
79
+ if (locator->directlyAt <ForceValueExpr>() &&
80
+ TypeVar->getImpl ().canBindToLValue ()) {
81
+ return llvm::none_of (Bindings, [](const PotentialBinding &binding) {
82
+ return binding.BindingType ->is <LValueType>();
83
+ });
84
+ }
85
+ }
86
+
60
87
if (isHole ()) {
61
88
auto *locator = TypeVar->getImpl ().getLocator ();
62
89
assert (locator && " a hole without locator?" );
@@ -98,19 +125,6 @@ bool PotentialBindings::isDelayed() const {
98
125
});
99
126
}
100
127
101
- if (auto *locator = TypeVar->getImpl ().getLocator ()) {
102
- // Since force unwrap preserves l-valueness, resulting
103
- // type variable has to be delayed until either l-value
104
- // binding becomes available or there are no other
105
- // variables to attempt.
106
- if (locator->directlyAt <ForceValueExpr>() &&
107
- TypeVar->getImpl ().canBindToLValue ()) {
108
- return llvm::none_of (Bindings, [](const PotentialBinding &binding) {
109
- return binding.BindingType ->is <LValueType>();
110
- });
111
- }
112
- }
113
-
114
128
return !DelayedBy.empty ();
115
129
}
116
130
@@ -140,6 +154,25 @@ bool PotentialBindings::isPotentiallyIncomplete() const {
140
154
if (!locator)
141
155
return false ;
142
156
157
+ if (locator->isLastElement <LocatorPathElt::MemberRefBase>() &&
158
+ !Bindings.empty ()) {
159
+ // If the base of the unresolved member reference like `.foo`
160
+ // couldn't be resolved we'd want to bind it to a hole at the
161
+ // very last moment possible, just like generic parameters.
162
+ if (isHole ())
163
+ return true ;
164
+
165
+ auto &binding = Bindings.front ();
166
+ // If base type of a member chain is inferred to be a protocol type,
167
+ // let's consider this binding set to be potentially incomplete since
168
+ // that's done as a last resort effort at resolving first member.
169
+ if (auto *constraint = binding.getSource ()) {
170
+ if (binding.BindingType ->is <ProtocolType>() &&
171
+ constraint->getKind () == ConstraintKind::ConformsTo)
172
+ return true ;
173
+ }
174
+ }
175
+
143
176
if (locator->isLastElement <LocatorPathElt::UnresolvedMemberChainResult>()) {
144
177
// If subtyping is allowed and this is a result of an implicit member chain,
145
178
// let's delay binding it to an optional until its object type resolved too or
@@ -159,12 +192,6 @@ bool PotentialBindings::isPotentiallyIncomplete() const {
159
192
}
160
193
161
194
if (isHole ()) {
162
- // If the base of the unresolved member reference like `.foo`
163
- // couldn't be resolved we'd want to bind it to a hole at the
164
- // very last moment possible, just like generic parameters.
165
- if (locator->isLastElement <LocatorPathElt::MemberRefBase>())
166
- return true ;
167
-
168
195
// Delay resolution of the code completion expression until
169
196
// the very end to give it a chance to be bound to some
170
197
// contextual type even if it's a hole.
@@ -408,6 +435,50 @@ void PotentialBindings::finalize(
408
435
&inferredBindings) {
409
436
inferTransitiveProtocolRequirements (inferredBindings);
410
437
inferTransitiveBindings (inferredBindings);
438
+
439
+ if (auto *locator = TypeVar->getImpl ().getLocator ()) {
440
+ if (locator->isLastElement <LocatorPathElt::MemberRefBase>()) {
441
+ // If this is a base of an unresolved member chain, as a last
442
+ // resort effort let's infer base to be a protocol type based
443
+ // on contextual conformance requirements.
444
+ //
445
+ // This allows us to find solutions in cases like this:
446
+ //
447
+ // \code
448
+ // func foo<T: P>(_: T) {}
449
+ // foo(.bar) <- `.bar` should be a static member of `P`.
450
+ // \endcode
451
+ if (!hasViableBindings () && TransitiveProtocols.hasValue ()) {
452
+ for (auto *constraint : *TransitiveProtocols) {
453
+ auto protocolTy = constraint->getSecondType ();
454
+ addPotentialBinding (
455
+ {protocolTy, AllowedBindingKind::Exact, constraint});
456
+ }
457
+ }
458
+ }
459
+
460
+ if (CS.shouldAttemptFixes () &&
461
+ locator->isLastElement <LocatorPathElt::UnresolvedMemberChainResult>()) {
462
+ // Let's see whether this chain is valid, if it isn't then to avoid
463
+ // diagnosing the same issue multiple different ways, let's infer
464
+ // result of the chain to be a hole.
465
+ auto *resultExpr =
466
+ castToExpr<UnresolvedMemberChainResultExpr>(locator->getAnchor ());
467
+ auto *baseLocator = CS.getConstraintLocator (
468
+ resultExpr->getChainBase (), ConstraintLocator::UnresolvedMember);
469
+
470
+ if (CS.hasFixFor (
471
+ baseLocator,
472
+ FixKind::AllowInvalidStaticMemberRefOnProtocolMetatype)) {
473
+ CS.recordPotentialHole (TypeVar);
474
+ // Clear all of the previously collected bindings which are inferred
475
+ // from inside of a member chain.
476
+ Bindings.remove_if ([](const PotentialBinding &binding) {
477
+ return binding.Kind == AllowedBindingKind::Supertypes;
478
+ });
479
+ }
480
+ }
481
+ }
411
482
}
412
483
413
484
PotentialBindings::BindingScore
@@ -451,6 +522,14 @@ Optional<PotentialBindings> ConstraintSystem::determineBestBindings() {
451
522
auto isViableForRanking = [this ](const PotentialBindings &bindings) -> bool {
452
523
auto *typeVar = bindings.TypeVar ;
453
524
525
+ // Type variable representing a base of unresolved member chain should
526
+ // always be considered viable for ranking since it's allow to infer
527
+ // types from transitive protocol requirements.
528
+ if (auto *locator = typeVar->getImpl ().getLocator ()) {
529
+ if (locator->isLastElement <LocatorPathElt::MemberRefBase>())
530
+ return true ;
531
+ }
532
+
454
533
// If type variable is marked as a potential hole there is always going
455
534
// to be at least one binding available for it.
456
535
if (shouldAttemptFixes () && typeVar->getImpl ().canBindToHole ())
@@ -967,6 +1046,13 @@ PotentialBindings::inferFromRelational(Constraint *constraint) {
967
1046
if (!BGT || !isKnownKeyPathDecl (CS.getASTContext (), BGT->getDecl ()))
968
1047
return None;
969
1048
}
1049
+
1050
+ // Don't allow a protocol type to get propagated from the base to the result
1051
+ // type of a chain, Result should always be a concrete type which conforms
1052
+ // to the protocol inferred for the base.
1053
+ if (constraint->getKind () == ConstraintKind::UnresolvedMemberChainBase &&
1054
+ kind == AllowedBindingKind::Subtypes && type->is <ProtocolType>())
1055
+ return None;
970
1056
}
971
1057
972
1058
// If the source of the binding is 'OptionalObject' constraint
@@ -1063,7 +1149,8 @@ PotentialBindings::inferFromRelational(Constraint *constraint) {
1063
1149
1064
1150
case ConstraintKind::Bind:
1065
1151
case ConstraintKind::BindParam:
1066
- case ConstraintKind::Equal: {
1152
+ case ConstraintKind::Equal:
1153
+ case ConstraintKind::UnresolvedMemberChainBase: {
1067
1154
EquivalentTo.insert ({bindingTypeVar, constraint});
1068
1155
break ;
1069
1156
}
@@ -1118,7 +1205,8 @@ void PotentialBindings::infer(Constraint *constraint) {
1118
1205
case ConstraintKind::Conversion:
1119
1206
case ConstraintKind::ArgumentConversion:
1120
1207
case ConstraintKind::OperatorArgumentConversion:
1121
- case ConstraintKind::OptionalObject: {
1208
+ case ConstraintKind::OptionalObject:
1209
+ case ConstraintKind::UnresolvedMemberChainBase: {
1122
1210
auto binding = inferFromRelational (constraint);
1123
1211
if (!binding)
1124
1212
break ;
0 commit comments