[ConstraintSystem] Diagnose conformance failure with base of a static member lookup

Produce a tailored diagnostic when it has been established that
base type of a static member reference on protocol metatype doesn't
conform to a required protocol.
This commit is contained in:
Pavel Yaskevich
2020-10-30 13:46:30 -07:00
parent 5758ae2bcc
commit d1ab178471
2 changed files with 70 additions and 44 deletions

View File

@@ -5787,20 +5787,27 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
return SolutionKind::Solved; return SolutionKind::Solved;
} }
// If we hit a type variable without a fixed type, we can't auto formUnsolved = [&](bool activate = false) {
// solve this yet.
if (type->isTypeVariableOrMember()) {
// If we're supposed to generate constraints, do so. // If we're supposed to generate constraints, do so.
if (flags.contains(TMF_GenerateConstraints)) { if (flags.contains(TMF_GenerateConstraints)) {
addUnsolvedConstraint( auto *conformance = Constraint::create(
Constraint::create(*this, kind, type, *this, kind, type, protocol->getDeclaredInterfaceType(),
protocol->getDeclaredInterfaceType(), getConstraintLocator(locator));
getConstraintLocator(locator)));
addUnsolvedConstraint(conformance);
if (activate)
activateConstraint(conformance);
return SolutionKind::Solved; return SolutionKind::Solved;
} }
return SolutionKind::Unsolved; return SolutionKind::Unsolved;
} };
// If we hit a type variable without a fixed type, we can't
// solve this yet.
if (type->isTypeVariableOrMember())
return formUnsolved();
auto *loc = getConstraintLocator(locator); auto *loc = getConstraintLocator(locator);
@@ -5922,7 +5929,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
} }
if (path.back().is<LocatorPathElt::AnyRequirement>()) { if (auto req = path.back().getAs<LocatorPathElt::AnyRequirement>()) {
// If this is a requirement associated with `Self` which is bound // If this is a requirement associated with `Self` which is bound
// to `Any`, let's consider this "too incorrect" to continue. // to `Any`, let's consider this "too incorrect" to continue.
// //
@@ -5941,6 +5948,39 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
} }
} }
auto anchor = locator.getAnchor();
if ((isExpr<UnresolvedDotExpr>(anchor) ||
isExpr<UnresolvedMemberExpr>(anchor)) &&
req->is<LocatorPathElt::TypeParameterRequirement>()) {
auto signature = path[path.size() - 2]
.castTo<LocatorPathElt::OpenedGeneric>()
.getSignature();
auto requirement = signature->getRequirements()[req->getIndex()];
auto *memberLoc = getConstraintLocator(anchor, path.front());
auto *memberRef = findResolvedMemberRef(memberLoc);
// To figure out what is going on here we need to wait until
// member overload is set in the constraint system.
if (!memberRef)
return formUnsolved(/*activate=*/true);
// If this is a `Self` conformance requirement from a static member
// reference on a protocol metatype, let's produce a tailored diagnostic.
if (memberRef->isStatic()) {
if (auto *protocolDecl =
memberRef->getDeclContext()->getSelfProtocolDecl()) {
auto selfTy = protocolDecl->getProtocolSelfType();
if (selfTy->isEqual(requirement.getFirstType())) {
auto *fix = AllowInvalidStaticMemberRefOnProtocolMetatype::create(
*this, memberLoc);
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
}
}
}
}
if (auto *fix = if (auto *fix =
fixRequirementFailure(*this, type, protocolTy, anchor, path)) { fixRequirementFailure(*this, type, protocolTy, anchor, path)) {
auto impact = assessRequirementFailureImpact(*this, rawType, locator); auto impact = assessRequirementFailureImpact(*this, rawType, locator);
@@ -5953,27 +5993,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint(
} }
} }
if (path.back().is<LocatorPathElt::MemberRefBase>()) {
path.pop_back();
auto *memberLoc = getConstraintLocator(anchor, path);
if (auto overload = findSelectedOverloadFor(memberLoc)) {
const auto &choice = overload->choice;
assert(choice.isDecl());
auto *decl = choice.getDecl();
auto nameRef = choice.getFunctionRefKind() == FunctionRefKind::Compound
? decl->createNameRef()
: DeclNameRef(decl->getBaseName());
auto *fix = AllowTypeOrInstanceMember::create(
*this, MetatypeType::get(protocolTy, getASTContext()), decl,
nameRef, memberLoc);
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
}
}
// If this is an implicit Hashable conformance check generated for each // If this is an implicit Hashable conformance check generated for each
// index argument of the keypath subscript component, we could just treat // index argument of the keypath subscript component, we could just treat
// it as though it conforms. // it as though it conforms.
@@ -6833,8 +6852,6 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
// referred from. // referred from.
assert(hasStaticMembers); assert(hasStaticMembers);
Type resultTy;
// Cannot instantiate a protocol or reference a member on // Cannot instantiate a protocol or reference a member on
// protocol composition type. // protocol composition type.
if (isa<ConstructorDecl>(decl) || if (isa<ConstructorDecl>(decl) ||
@@ -6844,6 +6861,8 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName,
return; return;
} }
Type resultTy;
if (isa<AbstractFunctionDecl>(decl)) { if (isa<AbstractFunctionDecl>(decl)) {
auto refTy = auto refTy =
decl->getInterfaceType()->castTo<AnyFunctionType>()->getResult(); decl->getInterfaceType()->castTo<AnyFunctionType>()->getResult();
@@ -8017,10 +8036,11 @@ ConstraintSystem::simplifyUnresolvedMemberChainBaseConstraint(
auto *memberLoc = auto *memberLoc =
getConstraintLocator(baseExpr, ConstraintLocator::UnresolvedMember); getConstraintLocator(baseExpr, ConstraintLocator::UnresolvedMember);
auto *memberRef = findResolvedMemberRef(memberLoc); if (shouldAttemptFixes() && hasFixFor(memberLoc))
assert(memberRef); return SolutionKind::Solved;
if (memberRef->isStatic()) { auto *memberRef = findResolvedMemberRef(memberLoc);
if (memberRef && memberRef->isStatic()) {
return simplifyConformsToConstraint( return simplifyConformsToConstraint(
resultTy, baseTy, ConstraintKind::ConformsTo, resultTy, baseTy, ConstraintKind::ConformsTo,
getConstraintLocator(memberLoc, ConstraintLocator::MemberRefBase), getConstraintLocator(memberLoc, ConstraintLocator::MemberRefBase),

View File

@@ -1577,15 +1577,21 @@ ConstraintSystem::getTypeOfMemberReference(
Type baseOpenedTy = baseObjTy; Type baseOpenedTy = baseObjTy;
if (isStaticMemberRefOnProtocol) { if (isStaticMemberRefOnProtocol) {
// Member type with Self applied. // In diagnostic mode, let's not try to replace base type
auto refTy = openedType->castTo<FunctionType>()->getResult(); // if there is already a known issue associated with this
// If member is a function type, let's use its result type // reference e.g. it might be incorrect initializer call
// since it could be either a static method or a property // or result type is invalid.
// which returns a function type. if (!(shouldAttemptFixes() && hasFixFor(getConstraintLocator(locator)))) {
if (auto *funcTy = refTy->getAs<FunctionType>()) // Member type with Self applied.
baseOpenedTy = funcTy->getResult(); auto refTy = openedType->castTo<FunctionType>()->getResult();
else // If member is a function type, let's use its result type
baseOpenedTy = refTy; // since it could be either a static method or a property
// which returns a function type.
if (auto *funcTy = refTy->getAs<FunctionType>())
baseOpenedTy = funcTy->getResult();
else
baseOpenedTy = refTy;
}
} else if (baseObjTy->isExistentialType()) { } else if (baseObjTy->isExistentialType()) {
auto openedArchetype = OpenedArchetypeType::get(baseObjTy); auto openedArchetype = OpenedArchetypeType::get(baseObjTy);
OpenedExistentialTypes.push_back( OpenedExistentialTypes.push_back(