Changing overload rules to match C++ behavior: Emit a warning when the best overload match has some number of no-best matching parameters.

This commit is contained in:
Ilia Filippov
2014-03-24 16:20:23 +04:00
parent 6f44d5b55f
commit ecdc695b22
14 changed files with 59 additions and 27 deletions

View File

@@ -2935,7 +2935,9 @@ of a function, ``ispc`` uses the following model to choose the best function:
each conversion of two types has its cost. ``ispc`` tries to find conversion each conversion of two types has its cost. ``ispc`` tries to find conversion
with the smallest cost. When ``ispc`` can't find any conversion it means that with the smallest cost. When ``ispc`` can't find any conversion it means that
this function is not suitable. Then ``ispc`` sums costs for all arguments and this function is not suitable. Then ``ispc`` sums costs for all arguments and
chooses the function with the smallest final cost. chooses the function with the smallest final cost. If the chosen function
has some arguments which costs are bigger than their costs in other function
this treats as ambiguous.
Costs of type conversions placed from small to big: Costs of type conversions placed from small to big:
1. Parameter types match exactly. 1. Parameter types match exactly.

View File

@@ -8188,13 +8188,15 @@ int
FunctionSymbolExpr::computeOverloadCost(const FunctionType *ftype, FunctionSymbolExpr::computeOverloadCost(const FunctionType *ftype,
const std::vector<const Type *> &argTypes, const std::vector<const Type *> &argTypes,
const std::vector<bool> *argCouldBeNULL, const std::vector<bool> *argCouldBeNULL,
const std::vector<bool> *argIsConstant) { const std::vector<bool> *argIsConstant,
int * cost) {
int costSum = 0; int costSum = 0;
// In computing the cost function, we only worry about the actual // In computing the cost function, we only worry about the actual
// argument types--using function default parameter values is free for // argument types--using function default parameter values is free for
// the purposes here... // the purposes here...
for (int i = 0; i < (int)argTypes.size(); ++i) { for (int i = 0; i < (int)argTypes.size(); ++i) {
cost[i] = 0;
// The cost imposed by this argument will be a multiple of // The cost imposed by this argument will be a multiple of
// costScale, which has a value set so that for each of the cost // costScale, which has a value set so that for each of the cost
// buckets, even if all of the function arguments undergo the next // buckets, even if all of the function arguments undergo the next
@@ -8208,12 +8210,12 @@ FunctionSymbolExpr::computeOverloadCost(const FunctionType *ftype,
if (Type::Equal(callType, fargType)) if (Type::Equal(callType, fargType))
// Perfect match: no cost // Perfect match: no cost
// Step "1" from documentation // Step "1" from documentation
costSum += 0; cost[i] += 0;
else if (argCouldBeNULL && (*argCouldBeNULL)[i] && else if (argCouldBeNULL && (*argCouldBeNULL)[i] &&
lArgIsPointerType(fargType)) lArgIsPointerType(fargType))
// Passing NULL to a pointer-typed parameter is also a no-cost operation // Passing NULL to a pointer-typed parameter is also a no-cost operation
// Step "1" from documentation // Step "1" from documentation
costSum += 0; cost[i] += 0;
else { else {
// If the argument is a compile-time constant, we'd like to // If the argument is a compile-time constant, we'd like to
// count the cost of various conversions as much lower than the // count the cost of various conversions as much lower than the
@@ -8232,7 +8234,7 @@ FunctionSymbolExpr::computeOverloadCost(const FunctionType *ftype,
// It is possible to pass (vf -> cvfr) // It is possible to pass (vf -> cvfr)
// but it is worse than (vf -> vfr) or (cvf -> cvfr) // but it is worse than (vf -> vfr) or (cvf -> cvfr)
// Step "3" from documentation // Step "3" from documentation
costSum += 2 * costScale; cost[i] += 2 * costScale;
} }
if (!Type::Equal(callType->GetReferenceTarget()->GetAsNonConstType(), if (!Type::Equal(callType->GetReferenceTarget()->GetAsNonConstType(),
fargType->GetReferenceTarget()->GetAsNonConstType())) { fargType->GetReferenceTarget()->GetAsNonConstType())) {
@@ -8242,7 +8244,7 @@ FunctionSymbolExpr::computeOverloadCost(const FunctionType *ftype,
} }
// penalty for equal types under reference (vf -> vfr is worse than vf -> vf) // penalty for equal types under reference (vf -> vfr is worse than vf -> vf)
// Step "2" from documentation // Step "2" from documentation
costSum += 2 * costScale; cost[i] += 2 * costScale;
continue; continue;
} }
const Type *callTypeNP = callType; const Type *callTypeNP = callType;
@@ -8250,7 +8252,7 @@ FunctionSymbolExpr::computeOverloadCost(const FunctionType *ftype,
callTypeNP = callType->GetReferenceTarget(); callTypeNP = callType->GetReferenceTarget();
// we can treat vfr as vf for callType with some penalty // we can treat vfr as vf for callType with some penalty
// Step "5" from documentation // Step "5" from documentation
costSum += 2 * costScale; cost[i] += 2 * costScale;
} }
// Now we deal with references, so we can normalize to non-const types // Now we deal with references, so we can normalize to non-const types
@@ -8263,13 +8265,13 @@ FunctionSymbolExpr::computeOverloadCost(const FunctionType *ftype,
if (Type::Equal(callTypeNC, fargTypeNC)) { if (Type::Equal(callTypeNC, fargTypeNC)) {
// The best case: vf -> vf. // The best case: vf -> vf.
// Step "4" from documentation // Step "4" from documentation
costSum += 1 * costScale; cost[i] += 1 * costScale;
continue; continue;
} }
if (lIsMatchWithTypeWidening(callTypeNC, fargTypeNC)) { if (lIsMatchWithTypeWidening(callTypeNC, fargTypeNC)) {
// A little bit worse case: vf -> vd. // A little bit worse case: vf -> vd.
// Step "6" from documentation // Step "6" from documentation
costSum += 8 * costScale; cost[i] += 8 * costScale;
continue; continue;
} }
if (fargType->IsVaryingType() && callType->IsUniformType()) { if (fargType->IsVaryingType() && callType->IsUniformType()) {
@@ -8278,31 +8280,34 @@ FunctionSymbolExpr::computeOverloadCost(const FunctionType *ftype,
if (Type::Equal(callTypeNC->GetAsVaryingType(), fargTypeNC)) { if (Type::Equal(callTypeNC->GetAsVaryingType(), fargTypeNC)) {
// uf -> vf is better than uf -> ui or uf -> ud // uf -> vf is better than uf -> ui or uf -> ud
// Step "7" from documentation // Step "7" from documentation
costSum += 16 * costScale; cost[i] += 16 * costScale;
continue; continue;
} }
if (lIsMatchWithTypeWidening(callTypeNC->GetAsVaryingType(), fargTypeNC)) { if (lIsMatchWithTypeWidening(callTypeNC->GetAsVaryingType(), fargTypeNC)) {
// uf -> vd is better than uf -> vi (128 < 128 + 64) // uf -> vd is better than uf -> vi (128 < 128 + 64)
// but worse than uf -> ui (128 > 64) // but worse than uf -> ui (128 > 64)
// Step "9" from documentation // Step "9" from documentation
costSum += 128 * costScale; cost[i] += 128 * costScale;
continue; continue;
} }
// 128 + 64 is the max. uf -> vi is the worst case. // 128 + 64 is the max. uf -> vi is the worst case.
// Step "10" from documentation // Step "10" from documentation
costSum += 128 * costScale; cost[i] += 128 * costScale;
} }
if (CanConvertTypes(callTypeNC, fargTypeNC)) if (CanConvertTypes(callTypeNC, fargTypeNC))
// two cases: the worst is 128 + 64: uf -> vi and // two cases: the worst is 128 + 64: uf -> vi and
// the only 64: (64 < 128) uf -> ui worse than uf -> vd // the only 64: (64 < 128) uf -> ui worse than uf -> vd
// Step "8" from documentation // Step "8" from documentation
costSum += 64 * costScale; cost[i] += 64 * costScale;
else else
// Failure--no type conversion possible... // Failure--no type conversion possible...
return -1; return -1;
} }
} }
for (int i = 0; i < (int)argTypes.size(); ++i) {
costSum = costSum + cost[i];
}
return costSum; return costSum;
} }
@@ -8337,6 +8342,7 @@ FunctionSymbolExpr::ResolveOverloads(SourcePos argPos,
int bestMatchCost = 1<<30; int bestMatchCost = 1<<30;
std::vector<Symbol *> matches; std::vector<Symbol *> matches;
std::vector<int> candidateCosts; std::vector<int> candidateCosts;
std::vector<int*> candidateExpandCosts;
if (actualCandidates.size() == 0) if (actualCandidates.size() == 0)
goto failure; goto failure;
@@ -8346,9 +8352,12 @@ FunctionSymbolExpr::ResolveOverloads(SourcePos argPos,
const FunctionType *ft = const FunctionType *ft =
CastType<FunctionType>(actualCandidates[i]->type); CastType<FunctionType>(actualCandidates[i]->type);
AssertPos(pos, ft != NULL); AssertPos(pos, ft != NULL);
int * cost = new int[argTypes.size()];
candidateCosts.push_back(computeOverloadCost(ft, argTypes, candidateCosts.push_back(computeOverloadCost(ft, argTypes,
argCouldBeNULL, argCouldBeNULL,
argIsConstant)); argIsConstant,
cost));
candidateExpandCosts.push_back(cost);
} }
// Find the best cost, and then the candidate or candidates that have // Find the best cost, and then the candidate or candidates that have
@@ -8361,8 +8370,28 @@ FunctionSymbolExpr::ResolveOverloads(SourcePos argPos,
if (bestMatchCost == (1<<30)) if (bestMatchCost == (1<<30))
goto failure; goto failure;
for (int i = 0; i < (int)candidateCosts.size(); ++i) { for (int i = 0; i < (int)candidateCosts.size(); ++i) {
if (candidateCosts[i] == bestMatchCost) if (candidateCosts[i] == bestMatchCost) {
for (int j = 0; j < (int)candidateCosts.size(); ++j) {
for (int k = 0; k < argTypes.size(); k++) {
if (candidateCosts[j] != -1 &&
candidateExpandCosts[j][k] < candidateExpandCosts[i][k]) {
std::vector<Symbol *> temp;
temp.push_back(actualCandidates[i]);
temp.push_back(actualCandidates[j]);
std::string candidateMessage =
lGetOverloadCandidateMessage(temp, argTypes, argCouldBeNULL);
Warning(pos, "call to \"%s\" is ambiguous. "
"This warning will be turned into error in the next ispc release.\n"
"Please add explicit cast to arguments to have unambiguous match."
"\n%s", funName, candidateMessage.c_str());
}
}
}
matches.push_back(actualCandidates[i]); matches.push_back(actualCandidates[i]);
}
}
for (int i = 0; i < (int)candidateExpandCosts.size(); ++i) {
delete [] candidateExpandCosts[i];
} }
if (matches.size() == 1) { if (matches.size() == 1) {

3
expr.h
View File

@@ -635,7 +635,8 @@ private:
static int computeOverloadCost(const FunctionType *ftype, static int computeOverloadCost(const FunctionType *ftype,
const std::vector<const Type *> &argTypes, const std::vector<const Type *> &argTypes,
const std::vector<bool> *argCouldBeNULL, const std::vector<bool> *argCouldBeNULL,
const std::vector<bool> *argIsConstant); const std::vector<bool> *argIsConstant,
int * cost);
/** Name of the function that is being called. */ /** Name of the function that is being called. */
std::string name; std::string name;

View File

@@ -16,7 +16,7 @@ export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
else else
x[a][b-1] = 1; x[a][b-1] = 1;
a = min(a, 46); a = min(a, 46.f);
RET[programIndex] = x[3][a]; RET[programIndex] = x[3][a];
} }

View File

@@ -10,7 +10,7 @@ export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
for (uniform int j = 0; j < 47; ++j) for (uniform int j = 0; j < 47; ++j)
x[i][j] = 2+b-5; x[i][j] = 2+b-5;
a = min(a,46); a = min(a,46.f);
x[a][b-1] = 0; x[a][b-1] = 0;
RET[programIndex] = x[2][a]; RET[programIndex] = x[2][a];
} }

View File

@@ -11,7 +11,7 @@ export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
uniform int index[4] = { 0, 1, 2, 4 }; uniform int index[4] = { 0, 1, 2, 4 };
float v = index[programIndex & 0x3]; float v = index[programIndex & 0x3];
x[min(a,39)][v] = 0; x[min(a,39.f)][v] = 0;
RET[programIndex] = x[v+1][v]; RET[programIndex] = x[v+1][v];
} }

View File

@@ -4,7 +4,7 @@ export uniform int width() { return programCount; }
export void f_f(uniform float RET[], uniform float aFOO[]) { export void f_f(uniform float RET[], uniform float aFOO[]) {
float a = 1. / aFOO[programIndex]; float a = 1. / aFOO[programIndex];
RET[programIndex] = max(0, a); RET[programIndex] = max(0.f, a);
} }
export void result(uniform float RET[]) { export void result(uniform float RET[]) {

View File

@@ -16,7 +16,7 @@ export void f_f(uniform float RET[], uniform float aFOO[]) {
uniform FuncType func[] = { bar, foo }; uniform FuncType func[] = { bar, foo };
float a = aFOO[programIndex]; float a = aFOO[programIndex];
float b = aFOO[0]-1; // == 0 float b = aFOO[0]-1; // == 0
func[min(a, 0)] = foo; func[min(a, 0.f)] = foo;
RET[programIndex] = func[0](a, b); RET[programIndex] = func[0](a, b);
} }

View File

@@ -9,7 +9,7 @@ export void f_f(uniform float RET[], uniform float aFOO[]) {
encore: encore:
++RET[programIndex]; ++RET[programIndex];
if (any(a != 0)) { if (any(a != 0)) {
a = max(a-1, 0); a = max(a-1, 0.f);
goto encore; goto encore;
} }
} }

View File

@@ -6,7 +6,7 @@ export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) { export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex]; float a = aFOO[programIndex];
RET[programIndex] = max(3 * a, 10.f); RET[programIndex] = max(3 * a, 10.f);
RET[width()-1] = max(b, 100); RET[width()-1] = max(b, 100.f);
} }

View File

@@ -6,7 +6,7 @@ export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) { export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex]; float a = aFOO[programIndex];
RET[programIndex] = max(-10 * (a-3), .1f); RET[programIndex] = max(-10 * (a-3), .1f);
RET[width() - 1] = max(-10 * b, 2); RET[width() - 1] = max(-10 * b, 2.f);
} }
export void result(uniform float RET[]) { export void result(uniform float RET[]) {

View File

@@ -6,7 +6,7 @@ export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) { export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex]; float a = aFOO[programIndex];
RET[programIndex] = min(3 * a, 10.f); RET[programIndex] = min(3 * a, 10.f);
RET[width()-1] = min(b, 100); RET[width()-1] = min(b, 100.f);
} }

View File

@@ -6,7 +6,7 @@ export uniform int width() { return programCount; }
export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) { export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
float a = aFOO[programIndex]; float a = aFOO[programIndex];
RET[programIndex] = min(-10 * (a-3), .1f); RET[programIndex] = min(-10 * (a-3), .1f);
RET[width() - 1] = min(-10 * b, 2); RET[width() - 1] = min(-10 * b, 2.f);
} }
export void result(uniform float RET[]) { export void result(uniform float RET[]) {

View File

@@ -19,7 +19,7 @@ export void f_fu(uniform float RET[], uniform float aFOO[], uniform float b) {
for (uniform int i = 0; i < 16; ++i) for (uniform int i = 0; i < 16; ++i)
for (uniform int j = 0; j < 16; ++j) for (uniform int j = 0; j < 16; ++j)
bar.foo[i].x[j] = b; bar.foo[i].x[j] = b;
RET[programIndex] = bar.foo[min(15, a-1)].x[min(15, a-1)]; RET[programIndex] = bar.foo[min(15.f, a-1)].x[min(15.f, a-1)];
} }
export void result(uniform float RET[]) { RET[programIndex] = 5; } export void result(uniform float RET[]) { RET[programIndex] = 5; }