[ConstraintSystem] Fix bug of argument reordering in matchCallArguments

This commit is contained in:
omochimetaru
2020-03-11 04:02:23 +09:00
parent ee36c1cc3e
commit 484606dc3a
2 changed files with 247 additions and 43 deletions

View File

@@ -651,58 +651,67 @@ matchCallArguments(SmallVectorImpl<AnyFunctionType::Param> &args,
// parameter (or even worse - OoO parameter with label re-naming),
// we most likely have no idea what would be the best
// diagnostic for this situation, so let's just try to re-label.
auto isOutOfOrderArgument = [&](bool hadLabelMismatch, unsigned argIdx,
unsigned prevArgIdx) {
if (hadLabelMismatch)
auto isOutOfOrderArgument = [&](unsigned toParamIdx, unsigned fromArgIdx,
unsigned toArgIdx) {
if (fromArgIdx <= toArgIdx) {
return false;
}
auto newLabel = args[argIdx].getLabel();
auto oldLabel = args[prevArgIdx].getLabel();
auto newLabel = args[fromArgIdx].getLabel();
auto oldLabel = args[toArgIdx].getLabel();
unsigned actualIndex = prevArgIdx;
for (; actualIndex != argIdx; ++actualIndex) {
if (newLabel != params[toParamIdx].getLabel()) {
return false;
}
auto paramIdx = toParamIdx + 1;
for (; paramIdx < params.size(); ++paramIdx) {
// Looks like new position (excluding defaulted parameters),
// has a valid label.
if (newLabel == params[actualIndex].getLabel())
if (oldLabel == params[paramIdx].getLabel())
break;
// If we are moving the the position with a different label
// and there is no default value for it, can't diagnose the
// problem as a simple re-ordering.
if (!paramInfo.hasDefaultArgument(actualIndex))
if (!paramInfo.hasDefaultArgument(paramIdx))
return false;
}
for (unsigned i = actualIndex + 1, n = params.size(); i != n; ++i) {
if (oldLabel == params[i].getLabel())
break;
if (!paramInfo.hasDefaultArgument(i))
return false;
// label was not found
if (paramIdx == params.size()) {
return false;
}
return true;
};
unsigned argIdx = 0;
SmallVector<unsigned, 4> paramToArgMap;
paramToArgMap.reserve(params.size());
{
unsigned argIdx = 0;
for (const auto &binding : parameterBindings) {
paramToArgMap.push_back(argIdx);
argIdx += binding.size();
}
}
// Enumerate the parameters and their bindings to see if any arguments are
// our of order
bool hadLabelMismatch = false;
for (auto binding : parameterBindings) {
for (auto boundArgIdx : binding) {
for (const auto paramIdx : indices(params)) {
const auto toArgIdx = paramToArgMap[paramIdx];
const auto &binding = parameterBindings[paramIdx];
for (const auto paramBindIdx : indices(binding)) {
// We've found the parameter that has an out of order
// argument, and know the indices of the argument that
// needs to move (fromArgIdx) and the argument location
// it should move to (toArgIdx).
auto fromArgIdx = boundArgIdx;
auto toArgIdx = argIdx;
const auto fromArgIdx = binding[paramBindIdx];
// If there is no re-ordering going on, and index is past
// the number of parameters, it could only mean that this
// is variadic parameter, so let's just move on.
if (fromArgIdx == toArgIdx && toArgIdx >= params.size()) {
// Does nothing for variadic tail.
if (params[paramIdx].isVariadic() && paramBindIdx > 0) {
assert(args[fromArgIdx].getLabel().empty());
argIdx++;
continue;
}
@@ -711,40 +720,40 @@ matchCallArguments(SmallVectorImpl<AnyFunctionType::Param> &args,
// one argument requires label and another one doesn't, but caller
// doesn't provide either, problem is going to be identified as
// out-of-order argument instead of label mismatch.
auto expectedLabel = params[toArgIdx].getLabel();
auto argumentLabel = args[fromArgIdx].getLabel();
const auto expectedLabel = params[paramIdx].getLabel();
const auto argumentLabel = args[fromArgIdx].getLabel();
if (argumentLabel != expectedLabel) {
// - The parameter is unnamed, in which case we try to fix the
// problem by removing the name.
if (expectedLabel.empty()) {
hadLabelMismatch = true;
if (listener.extraneousLabel(toArgIdx))
if (listener.extraneousLabel(paramIdx))
return true;
// - The argument is unnamed, in which case we try to fix the
// problem by adding the name.
} else if (argumentLabel.empty()) {
hadLabelMismatch = true;
if (listener.missingLabel(toArgIdx))
if (listener.missingLabel(paramIdx))
return true;
// - The argument label has a typo at the same position.
} else if (fromArgIdx == toArgIdx) {
hadLabelMismatch = true;
if (listener.incorrectLabel(toArgIdx))
return true;
if (listener.incorrectLabel(paramIdx))
return true;
}
}
if (boundArgIdx == argIdx) {
if (fromArgIdx == toArgIdx) {
// If the argument is in the right location, just continue
argIdx++;
continue;
}
// This situation looks like out-of-order argument but it's hard
// to say exactly without considering other factors, because it
// could be invalid labeling too.
if (isOutOfOrderArgument(hadLabelMismatch, fromArgIdx, toArgIdx))
if (!hadLabelMismatch &&
isOutOfOrderArgument(paramIdx, fromArgIdx, toArgIdx))
return listener.outOfOrderArgument(fromArgIdx, toArgIdx);
SmallVector<Identifier, 8> expectedLabels;