[CSSimplify] Detect and record optional base infered base for unresolved member that could be an unwraped base member

This commit is contained in:
Luciano Almeida
2020-11-21 01:51:41 -03:00
parent c8ea08df78
commit 1c9ba53038

View File

@@ -7109,6 +7109,95 @@ fixMemberRef(ConstraintSystem &cs, Type baseTy,
return nullptr;
}
/// Diagnose if the base type is optional, we're referring to a nominal
/// type member via the dot syntax and the member name matches
/// Optional<T>.{member} or a .none member inferred as non-optional static
/// member e.g. let _ : Foo? = .none where Foo has a static member none.
static bool attemptUnresolvedMemberFix(ConstraintSystem &cs,
ConstraintKind kind, Type baseTy,
DeclNameRef member,
FunctionRefKind functionRefKind,
ConstraintLocator *locator,
MemberLookupResult result) {
if (kind != ConstraintKind::UnresolvedValueMember)
return false;
// None or only one viable candidate, there is no ambiguity.
if (result.ViableCandidates.size() <= 1)
return false;
// Only diagnose those situations for static members.
if (!baseTy->is<MetatypeType>())
return false;
// Don't diagnose for function members e.g. Foo? = .none(0).
if (functionRefKind != FunctionRefKind::Unapplied)
return false;
Type underlyingBaseType = baseTy->getMetatypeInstanceType();
if (!underlyingBaseType->getNominalOrBoundGenericNominal())
return false;
if (!underlyingBaseType->getOptionalObjectType())
return false;
auto unwrappedType = underlyingBaseType->lookThroughAllOptionalTypes();
bool allOptionalBaseCandidates = true;
auto filterViableCandidates =
[&](SmallVector<OverloadChoice, 4> &candidates,
SmallVector<OverloadChoice, 4> &viableCandidates,
bool &allOptionalBase) {
for (OverloadChoice choice : candidates) {
if (!choice.isDecl())
continue;
auto memberDecl = choice.getDecl();
if (isa<FuncDecl>(memberDecl))
continue;
if (memberDecl->isInstanceMember())
continue;
allOptionalBase &= bool(choice.getBaseType()
->getMetatypeInstanceType()
->getOptionalObjectType());
if (auto EED = dyn_cast<EnumElementDecl>(memberDecl)) {
if (!EED->hasAssociatedValues())
viableCandidates.push_back(choice);
} else if (auto VD = dyn_cast<VarDecl>(memberDecl)) {
if (unwrappedType->hasTypeVariable() ||
VD->getInterfaceType()->isEqual(unwrappedType))
viableCandidates.push_back(choice);
}
}
};
SmallVector<OverloadChoice, 4> viableCandidates;
filterViableCandidates(result.ViableCandidates, viableCandidates,
allOptionalBaseCandidates);
// Also none or only one viable candidate after filtering candidates, there is
// no ambiguity.
if (viableCandidates.size() <= 1)
return false;
// Right now, name lookup only unwraps a single layer of optionality, which
// for cases where base type is a multi-optional type e.g. Foo?? so, it only
// finds optional base candidates. To produce the correct warning perform an
// extra lookup on unwrapped type is required.
if (!allOptionalBaseCandidates)
return true;
MemberLookupResult unwrappedResult = cs.performMemberLookup(
kind, member, MetatypeType::get(unwrappedType), functionRefKind, locator,
/*includeInaccessibleMembers*/ false);
SmallVector<OverloadChoice, 4> unwrappedViableCandidates;
filterViableCandidates(unwrappedResult.ViableCandidates,
unwrappedViableCandidates, allOptionalBaseCandidates);
return !unwrappedViableCandidates.empty();
}
ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
ConstraintKind kind, Type baseTy, DeclNameRef member, Type memberTy,
DeclContext *useDC, FunctionRefKind functionRefKind,
@@ -7290,7 +7379,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
}
if (!result.UnviableCandidates.empty()) {
// Generate constraints for unvailable choices if they have a fix,
// Generate constraints for unavailable choices if they have a fix,
// and disable them by default, they'd get picked up in the "salvage" mode.
generateConstraints(
candidates, memberTy, result.UnviableCandidates, useDC, locator,
@@ -7301,6 +7390,21 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
});
}
// Attempt to record a warning where the unresolved member could be
// ambiguous with optional member. e.g.
// enum Foo {
// case none
// }
//
// let _: Foo? = .none // Although base is inferred as Optional.none
// it could be also.
if (attemptUnresolvedMemberFix(*this, kind, baseObjTy, member,
functionRefKind, locator, result)) {
auto *fix = SpecifyBaseTypeForOptionalUnresolvedMember::create(
*this, member, locator);
(void)recordFix(fix);
}
if (!candidates.empty()) {
addOverloadSet(candidates, locator);
return SolutionKind::Solved;
@@ -10234,7 +10338,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
case FixKind::AllowKeyPathWithoutComponents:
case FixKind::IgnoreInvalidResultBuilderBody:
case FixKind::SpecifyContextualTypeForNil:
case FixKind::AllowRefToInvalidDecl: {
case FixKind::AllowRefToInvalidDecl:
case FixKind::SpecifyBaseTypeForOptionalUnresolvedMember: {
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
}
@@ -10341,7 +10446,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
if (auto *fnType1 = type1->getAs<FunctionType>()) {
// If this is a contextual mismatch between two
// function types which we couldn't find a more
// speficit fix for. Let's assume that such types
// specific fix for. Let's assume that such types
// are competely disjoint and adjust impact of
// the fix accordingly.
if (auto *fnType2 = type2->getAs<FunctionType>()) {