Skip to content

Commit 1c9ba53

Browse files
[CSSimplify] Detect and record optional base infered base for unresolved member that could be an unwraped base member
1 parent c8ea08d commit 1c9ba53

File tree

1 file changed

+108
-3
lines changed

1 file changed

+108
-3
lines changed

lib/Sema/CSSimplify.cpp

Lines changed: 108 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7109,6 +7109,95 @@ fixMemberRef(ConstraintSystem &cs, Type baseTy,
71097109
return nullptr;
71107110
}
71117111

7112+
/// Diagnose if the base type is optional, we're referring to a nominal
7113+
/// type member via the dot syntax and the member name matches
7114+
/// Optional<T>.{member} or a .none member inferred as non-optional static
7115+
/// member e.g. let _ : Foo? = .none where Foo has a static member none.
7116+
static bool attemptUnresolvedMemberFix(ConstraintSystem &cs,
7117+
ConstraintKind kind, Type baseTy,
7118+
DeclNameRef member,
7119+
FunctionRefKind functionRefKind,
7120+
ConstraintLocator *locator,
7121+
MemberLookupResult result) {
7122+
7123+
if (kind != ConstraintKind::UnresolvedValueMember)
7124+
return false;
7125+
7126+
// None or only one viable candidate, there is no ambiguity.
7127+
if (result.ViableCandidates.size() <= 1)
7128+
return false;
7129+
7130+
// Only diagnose those situations for static members.
7131+
if (!baseTy->is<MetatypeType>())
7132+
return false;
7133+
7134+
// Don't diagnose for function members e.g. Foo? = .none(0).
7135+
if (functionRefKind != FunctionRefKind::Unapplied)
7136+
return false;
7137+
7138+
Type underlyingBaseType = baseTy->getMetatypeInstanceType();
7139+
if (!underlyingBaseType->getNominalOrBoundGenericNominal())
7140+
return false;
7141+
7142+
if (!underlyingBaseType->getOptionalObjectType())
7143+
return false;
7144+
7145+
auto unwrappedType = underlyingBaseType->lookThroughAllOptionalTypes();
7146+
bool allOptionalBaseCandidates = true;
7147+
auto filterViableCandidates =
7148+
[&](SmallVector<OverloadChoice, 4> &candidates,
7149+
SmallVector<OverloadChoice, 4> &viableCandidates,
7150+
bool &allOptionalBase) {
7151+
for (OverloadChoice choice : candidates) {
7152+
if (!choice.isDecl())
7153+
continue;
7154+
7155+
auto memberDecl = choice.getDecl();
7156+
if (isa<FuncDecl>(memberDecl))
7157+
continue;
7158+
if (memberDecl->isInstanceMember())
7159+
continue;
7160+
7161+
allOptionalBase &= bool(choice.getBaseType()
7162+
->getMetatypeInstanceType()
7163+
->getOptionalObjectType());
7164+
7165+
if (auto EED = dyn_cast<EnumElementDecl>(memberDecl)) {
7166+
if (!EED->hasAssociatedValues())
7167+
viableCandidates.push_back(choice);
7168+
} else if (auto VD = dyn_cast<VarDecl>(memberDecl)) {
7169+
if (unwrappedType->hasTypeVariable() ||
7170+
VD->getInterfaceType()->isEqual(unwrappedType))
7171+
viableCandidates.push_back(choice);
7172+
}
7173+
}
7174+
};
7175+
7176+
SmallVector<OverloadChoice, 4> viableCandidates;
7177+
filterViableCandidates(result.ViableCandidates, viableCandidates,
7178+
allOptionalBaseCandidates);
7179+
7180+
// Also none or only one viable candidate after filtering candidates, there is
7181+
// no ambiguity.
7182+
if (viableCandidates.size() <= 1)
7183+
return false;
7184+
7185+
// Right now, name lookup only unwraps a single layer of optionality, which
7186+
// for cases where base type is a multi-optional type e.g. Foo?? so, it only
7187+
// finds optional base candidates. To produce the correct warning perform an
7188+
// extra lookup on unwrapped type is required.
7189+
if (!allOptionalBaseCandidates)
7190+
return true;
7191+
7192+
MemberLookupResult unwrappedResult = cs.performMemberLookup(
7193+
kind, member, MetatypeType::get(unwrappedType), functionRefKind, locator,
7194+
/*includeInaccessibleMembers*/ false);
7195+
SmallVector<OverloadChoice, 4> unwrappedViableCandidates;
7196+
filterViableCandidates(unwrappedResult.ViableCandidates,
7197+
unwrappedViableCandidates, allOptionalBaseCandidates);
7198+
return !unwrappedViableCandidates.empty();
7199+
}
7200+
71127201
ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
71137202
ConstraintKind kind, Type baseTy, DeclNameRef member, Type memberTy,
71147203
DeclContext *useDC, FunctionRefKind functionRefKind,
@@ -7290,7 +7379,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
72907379
}
72917380

72927381
if (!result.UnviableCandidates.empty()) {
7293-
// Generate constraints for unvailable choices if they have a fix,
7382+
// Generate constraints for unavailable choices if they have a fix,
72947383
// and disable them by default, they'd get picked up in the "salvage" mode.
72957384
generateConstraints(
72967385
candidates, memberTy, result.UnviableCandidates, useDC, locator,
@@ -7301,6 +7390,21 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
73017390
});
73027391
}
73037392

7393+
// Attempt to record a warning where the unresolved member could be
7394+
// ambiguous with optional member. e.g.
7395+
// enum Foo {
7396+
// case none
7397+
// }
7398+
//
7399+
// let _: Foo? = .none // Although base is inferred as Optional.none
7400+
// it could be also.
7401+
if (attemptUnresolvedMemberFix(*this, kind, baseObjTy, member,
7402+
functionRefKind, locator, result)) {
7403+
auto *fix = SpecifyBaseTypeForOptionalUnresolvedMember::create(
7404+
*this, member, locator);
7405+
(void)recordFix(fix);
7406+
}
7407+
73047408
if (!candidates.empty()) {
73057409
addOverloadSet(candidates, locator);
73067410
return SolutionKind::Solved;
@@ -10234,7 +10338,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1023410338
case FixKind::AllowKeyPathWithoutComponents:
1023510339
case FixKind::IgnoreInvalidResultBuilderBody:
1023610340
case FixKind::SpecifyContextualTypeForNil:
10237-
case FixKind::AllowRefToInvalidDecl: {
10341+
case FixKind::AllowRefToInvalidDecl:
10342+
case FixKind::SpecifyBaseTypeForOptionalUnresolvedMember: {
1023810343
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
1023910344
}
1024010345

@@ -10341,7 +10446,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1034110446
if (auto *fnType1 = type1->getAs<FunctionType>()) {
1034210447
// If this is a contextual mismatch between two
1034310448
// function types which we couldn't find a more
10344-
// speficit fix for. Let's assume that such types
10449+
// specific fix for. Let's assume that such types
1034510450
// are competely disjoint and adjust impact of
1034610451
// the fix accordingly.
1034710452
if (auto *fnType2 = type2->getAs<FunctionType>()) {

0 commit comments

Comments
 (0)