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:
@@ -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.
|
||||||
|
|||||||
57
expr.cpp
57
expr.cpp
@@ -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
3
expr.h
@@ -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;
|
||||||
|
|||||||
@@ -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];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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[]) {
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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[]) {
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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[]) {
|
||||||
|
|||||||
@@ -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; }
|
||||||
|
|||||||
Reference in New Issue
Block a user