From d1ab178471e6c901b6c47c7cbfa4f6e2cf21c2fc Mon Sep 17 00:00:00 2001 From: Pavel Yaskevich Date: Fri, 30 Oct 2020 13:46:30 -0700 Subject: [PATCH] [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. --- lib/Sema/CSSimplify.cpp | 90 +++++++++++++++++++++-------------- lib/Sema/ConstraintSystem.cpp | 24 ++++++---- 2 files changed, 70 insertions(+), 44 deletions(-) diff --git a/lib/Sema/CSSimplify.cpp b/lib/Sema/CSSimplify.cpp index e9156da0b42..78b755ea929 100644 --- a/lib/Sema/CSSimplify.cpp +++ b/lib/Sema/CSSimplify.cpp @@ -5787,20 +5787,27 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint( return SolutionKind::Solved; } - // If we hit a type variable without a fixed type, we can't - // solve this yet. - if (type->isTypeVariableOrMember()) { + auto formUnsolved = [&](bool activate = false) { // If we're supposed to generate constraints, do so. if (flags.contains(TMF_GenerateConstraints)) { - addUnsolvedConstraint( - Constraint::create(*this, kind, type, - protocol->getDeclaredInterfaceType(), - getConstraintLocator(locator))); + auto *conformance = Constraint::create( + *this, kind, type, protocol->getDeclaredInterfaceType(), + getConstraintLocator(locator)); + + addUnsolvedConstraint(conformance); + if (activate) + activateConstraint(conformance); + return SolutionKind::Solved; } 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); @@ -5922,7 +5929,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint( return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved; } - if (path.back().is()) { + if (auto req = path.back().getAs()) { // If this is a requirement associated with `Self` which is bound // to `Any`, let's consider this "too incorrect" to continue. // @@ -5941,6 +5948,39 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint( } } + auto anchor = locator.getAnchor(); + + if ((isExpr(anchor) || + isExpr(anchor)) && + req->is()) { + auto signature = path[path.size() - 2] + .castTo() + .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 = fixRequirementFailure(*this, type, protocolTy, anchor, path)) { auto impact = assessRequirementFailureImpact(*this, rawType, locator); @@ -5953,27 +5993,6 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyConformsToConstraint( } } - if (path.back().is()) { - 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 // index argument of the keypath subscript component, we could just treat // it as though it conforms. @@ -6833,8 +6852,6 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName, // referred from. assert(hasStaticMembers); - Type resultTy; - // Cannot instantiate a protocol or reference a member on // protocol composition type. if (isa(decl) || @@ -6844,6 +6861,8 @@ performMemberLookup(ConstraintKind constraintKind, DeclNameRef memberName, return; } + Type resultTy; + if (isa(decl)) { auto refTy = decl->getInterfaceType()->castTo()->getResult(); @@ -8017,10 +8036,11 @@ ConstraintSystem::simplifyUnresolvedMemberChainBaseConstraint( auto *memberLoc = getConstraintLocator(baseExpr, ConstraintLocator::UnresolvedMember); - auto *memberRef = findResolvedMemberRef(memberLoc); - assert(memberRef); + if (shouldAttemptFixes() && hasFixFor(memberLoc)) + return SolutionKind::Solved; - if (memberRef->isStatic()) { + auto *memberRef = findResolvedMemberRef(memberLoc); + if (memberRef && memberRef->isStatic()) { return simplifyConformsToConstraint( resultTy, baseTy, ConstraintKind::ConformsTo, getConstraintLocator(memberLoc, ConstraintLocator::MemberRefBase), diff --git a/lib/Sema/ConstraintSystem.cpp b/lib/Sema/ConstraintSystem.cpp index e43bd4a39a0..d19e6f1d173 100644 --- a/lib/Sema/ConstraintSystem.cpp +++ b/lib/Sema/ConstraintSystem.cpp @@ -1577,15 +1577,21 @@ ConstraintSystem::getTypeOfMemberReference( Type baseOpenedTy = baseObjTy; if (isStaticMemberRefOnProtocol) { - // Member type with Self applied. - auto refTy = openedType->castTo()->getResult(); - // If member is a function type, let's use its result type - // since it could be either a static method or a property - // which returns a function type. - if (auto *funcTy = refTy->getAs()) - baseOpenedTy = funcTy->getResult(); - else - baseOpenedTy = refTy; + // In diagnostic mode, let's not try to replace base type + // if there is already a known issue associated with this + // reference e.g. it might be incorrect initializer call + // or result type is invalid. + if (!(shouldAttemptFixes() && hasFixFor(getConstraintLocator(locator)))) { + // Member type with Self applied. + auto refTy = openedType->castTo()->getResult(); + // If member is a function type, let's use its result type + // since it could be either a static method or a property + // which returns a function type. + if (auto *funcTy = refTy->getAs()) + baseOpenedTy = funcTy->getResult(); + else + baseOpenedTy = refTy; + } } else if (baseObjTy->isExistentialType()) { auto openedArchetype = OpenedArchetypeType::get(baseObjTy); OpenedExistentialTypes.push_back(