[ConstraintSystem] Adjust recording of "fixed" requirements to avoid conflicts

Currently it's possible to have a type conflict between different
requirements deduced as the same type which leads to incorrect
diagnostics. To mitigate that let's adjust how "fixed" requirements
are stored - instead of using resolved type for the left-hand side,
let's use originating generic parameter type.
This commit is contained in:
Pavel Yaskevich
2020-06-25 13:40:15 -07:00
parent c4f4ce1e5d
commit 0ea0b8e27b
10 changed files with 146 additions and 34 deletions

View File

@@ -4810,3 +4810,103 @@ SourceLoc constraints::getLoc(ASTNode anchor) {
SourceRange constraints::getSourceRange(ASTNode anchor) {
return anchor.getSourceRange();
}
static Optional<Requirement> getRequirement(ConstraintSystem &cs,
ConstraintLocator *reqLocator) {
auto reqLoc = reqLocator->getLastElementAs<LocatorPathElt::AnyRequirement>();
if (!reqLoc)
return None;
if (reqLoc->isConditionalRequirement()) {
auto path = reqLocator->getPath();
auto *typeReqLoc =
cs.getConstraintLocator(reqLocator->getAnchor(), path.drop_back());
auto conformances = cs.getCheckedConformances();
auto result = llvm::find_if(
conformances,
[&typeReqLoc](
const std::pair<ConstraintLocator *, ProtocolConformanceRef>
&conformance) { return conformance.first == typeReqLoc; });
assert(result != conformances.end());
auto conformance = result->second;
assert(conformance.isConcrete());
return conformance.getConditionalRequirements()[reqLoc->getIndex()];
}
if (auto openedGeneric =
reqLocator->findLast<LocatorPathElt::OpenedGeneric>()) {
auto signature = openedGeneric->getSignature();
return signature->getRequirements()[reqLoc->getIndex()];
}
return None;
}
static Optional<std::pair<GenericTypeParamType *, RequirementKind>>
getRequirementInfo(ConstraintSystem &cs, ConstraintLocator *reqLocator) {
auto requirement = getRequirement(cs, reqLocator);
if (!requirement)
return None;
auto *GP = requirement->getFirstType()->getAs<GenericTypeParamType>();
if (!GP)
return None;
auto path = reqLocator->getPath();
auto iter = path.rbegin();
auto openedGeneric =
reqLocator->findLast<LocatorPathElt::OpenedGeneric>(iter);
assert(openedGeneric);
auto newPath = path.drop_back(iter - path.rbegin() + 1);
auto *baseLoc = cs.getConstraintLocator(reqLocator->getAnchor(), newPath);
auto openedTypes = cs.getOpenedTypes();
auto substitutions = llvm::find_if(
openedTypes,
[&baseLoc](
const std::pair<ConstraintLocator *, ArrayRef<OpenedType>> &entry) {
return entry.first == baseLoc;
});
if (substitutions == openedTypes.end())
return None;
auto replacement =
llvm::find_if(substitutions->second, [&GP](const OpenedType &entry) {
auto *typeVar = entry.second;
return typeVar->getImpl().getGenericParameter() == GP;
});
if (replacement == substitutions->second.end())
return None;
auto *repr = cs.getRepresentative(replacement->second);
return std::make_pair(repr->getImpl().getGenericParameter(),
requirement->getKind());
}
bool ConstraintSystem::isFixedRequirement(ConstraintLocator *reqLocator,
Type requirementTy) {
if (auto reqInfo = getRequirementInfo(*this, reqLocator)) {
auto *GP = reqInfo->first;
auto reqKind = static_cast<unsigned>(reqInfo->second);
return FixedRequirements.count(
std::make_tuple(GP, reqKind, requirementTy.getPointer()));
}
return false;
}
void ConstraintSystem::recordFixedRequirement(ConstraintLocator *reqLocator,
Type requirementTy) {
if (auto reqInfo = getRequirementInfo(*this, reqLocator)) {
auto *GP = reqInfo->first;
auto reqKind = static_cast<unsigned>(reqInfo->second);
FixedRequirements.insert(
std::make_tuple(GP, reqKind, requirementTy.getPointer()));
}
}