Print better error messages when function overload resolution fails.

This commit is contained in:
Matt Pharr
2011-12-14 11:41:34 -08:00
parent 891919074e
commit 89a5248f4f
3 changed files with 41 additions and 114 deletions

148
expr.cpp
View File

@@ -2206,7 +2206,7 @@ AssignExpr::TypeCheck() {
for (int i = 0; i < ftype->GetNumParameters(); ++i) for (int i = 0; i < ftype->GetNumParameters(); ++i)
paramTypes.push_back(ftype->GetParameterType(i)); paramTypes.push_back(ftype->GetParameterType(i));
if (!fse->ResolveOverloads(paramTypes)) { if (!fse->ResolveOverloads(rvalue->pos, paramTypes)) {
Error(pos, "Unable to find overloaded function for function " Error(pos, "Unable to find overloaded function for function "
"pointer assignment."); "pointer assignment.");
return NULL; return NULL;
@@ -2719,7 +2719,7 @@ FunctionCallExpr::TypeCheck() {
argCouldBeNULL.push_back(lIsAllIntZeros(args->exprs[i])); argCouldBeNULL.push_back(lIsAllIntZeros(args->exprs[i]));
} }
if (fse->ResolveOverloads(argTypes, &argCouldBeNULL) == false) if (fse->ResolveOverloads(args->pos, argTypes, &argCouldBeNULL) == false)
return NULL; return NULL;
func = fse->TypeCheck(); func = fse->TypeCheck();
@@ -6189,110 +6189,22 @@ FunctionSymbolExpr::GetConstant(const Type *type) const {
} }
static std::string
lGetFunctionDeclaration(const std::string &name, const FunctionType *type) {
std::string ret;
ret += type->GetReturnType()->GetString();
ret += " ";
ret += name;
ret += "(";
for (int i = 0; i < type->GetNumParameters(); ++i) {
const Type *paramType = type->GetParameterType(i);
ConstExpr *paramDefault = type->GetParameterDefault(i);
ret += paramType->GetString();
ret += " ";
ret += type->GetParameterName(i);
// Print the default value if present
if (paramDefault != NULL) {
char buf[32];
if (paramType->IsFloatType()) {
double val;
int count = paramDefault->AsDouble(&val);
assert(count == 1);
sprintf(buf, " = %g", val);
}
else if (paramType->IsBoolType()) {
bool val;
int count = paramDefault->AsBool(&val);
assert(count == 1);
sprintf(buf, " = %s", val ? "true" : "false");
}
else if (paramType->IsUnsignedType()) {
uint64_t val;
int count = paramDefault->AsUInt64(&val);
assert(count == 1);
#ifdef ISPC_IS_LINUX
sprintf(buf, " = %lu", val);
#else
sprintf(buf, " = %llu", val);
#endif
}
else {
int64_t val;
int count = paramDefault->AsInt64(&val);
assert(count == 1);
#ifdef ISPC_IS_LINUX
sprintf(buf, " = %ld", val);
#else
sprintf(buf, " = %lld", val);
#endif
}
ret += buf;
}
if (i != type->GetNumParameters() - 1)
ret += ", ";
}
ret += ")";
return ret;
}
static void static void
lPrintFunctionOverloads(const std::string &name, lPrintOverloadCandidates(SourcePos pos, const std::vector<Symbol *> &funcs,
const std::vector<std::pair<int, Symbol *> > &matches) { const std::vector<const Type *> &argTypes,
fprintf(stderr, "Matching functions:\n"); const std::vector<bool> *argCouldBeNULL) {
int minCost = matches[0].first; for (unsigned int i = 0; i < funcs.size(); ++i)
for (unsigned int i = 1; i < matches.size(); ++i) Error(funcs[i]->pos, "Candidate function:");
minCost = std::min(minCost, matches[i].first);
for (unsigned int i = 0; i < matches.size(); ++i) { std::string passedTypes = "Passed types: (";
const FunctionType *t =
dynamic_cast<const FunctionType *>(matches[i].second->type);
assert(t != NULL);
if (matches[i].first == minCost)
fprintf(stderr, "\t%s\n", lGetFunctionDeclaration(name, t).c_str());
}
}
static void
lPrintFunctionOverloads(const std::string &name,
const std::vector<Symbol *> &funcs) {
fprintf(stderr, "Candidate functions:\n");
for (unsigned int i = 0; i < funcs.size(); ++i) {
const FunctionType *t =
dynamic_cast<const FunctionType *>(funcs[i]->type);
assert(t != NULL);
fprintf(stderr, "\t%s\n", lGetFunctionDeclaration(name, t).c_str());
}
}
static void
lPrintPassedTypes(const char *funName,
const std::vector<const Type *> &argTypes) {
fprintf(stderr, "Passed types: %*c(", (int)strlen(funName), ' ');
for (unsigned int i = 0; i < argTypes.size(); ++i) { for (unsigned int i = 0; i < argTypes.size(); ++i) {
if (argTypes[i] != NULL) if (argTypes[i] != NULL)
fprintf(stderr, "%s%s", argTypes[i]->GetString().c_str(), passedTypes += argTypes[i]->GetString();
(i < argTypes.size()-1) ? ", " : ")\n\n");
else else
fprintf(stderr, "(unknown type)%s", passedTypes += "(unknown type)";
(i < argTypes.size()-1) ? ", " : ")\n\n"); passedTypes += (i < argTypes.size()-1) ? ", " : ")\n\n";
} }
Error(pos, "%s", passedTypes.c_str());
} }
@@ -6468,6 +6380,7 @@ lGetBestMatch(std::vector<std::pair<int, Symbol *> > &matches) {
*/ */
bool bool
FunctionSymbolExpr::tryResolve(int (*matchFunc)(const Type *, const Type *), FunctionSymbolExpr::tryResolve(int (*matchFunc)(const Type *, const Type *),
SourcePos argPos,
const std::vector<const Type *> &callTypes, const std::vector<const Type *> &callTypes,
const std::vector<bool> *argCouldBeNULL) { const std::vector<bool> *argCouldBeNULL) {
const char *funName = candidateFunctions.front()->name.c_str(); const char *funName = candidateFunctions.front()->name.c_str();
@@ -6547,8 +6460,19 @@ FunctionSymbolExpr::tryResolve(int (*matchFunc)(const Type *, const Type *),
else { else {
Error(pos, "Multiple overloaded instances of function \"%s\" matched.", Error(pos, "Multiple overloaded instances of function \"%s\" matched.",
funName); funName);
lPrintFunctionOverloads(funName, matches);
lPrintPassedTypes(funName, callTypes); // select the matches that have the lowest cost
std::vector<Symbol *> bestMatches;
int minCost = matches[0].first;
for (unsigned int i = 1; i < matches.size(); ++i)
minCost = std::min(minCost, matches[i].first);
for (unsigned int i = 0; i < matches.size(); ++i)
if (matches[i].first == minCost)
bestMatches.push_back(matches[i].second);
// And print a useful error message
lPrintOverloadCandidates(argPos, bestMatches, callTypes, argCouldBeNULL);
// Stop trying to find more matches after an ambigious set of // Stop trying to find more matches after an ambigious set of
// matches. // matches.
return true; return true;
@@ -6557,7 +6481,8 @@ FunctionSymbolExpr::tryResolve(int (*matchFunc)(const Type *, const Type *),
bool bool
FunctionSymbolExpr::ResolveOverloads(const std::vector<const Type *> &argTypes, FunctionSymbolExpr::ResolveOverloads(SourcePos argPos,
const std::vector<const Type *> &argTypes,
const std::vector<bool> *argCouldBeNULL) { const std::vector<bool> *argCouldBeNULL) {
triedToResolve = true; triedToResolve = true;
@@ -6571,32 +6496,33 @@ FunctionSymbolExpr::ResolveOverloads(const std::vector<const Type *> &argTypes,
// Is there an exact match that doesn't require any argument type // Is there an exact match that doesn't require any argument type
// conversion (other than converting type -> reference type)? // conversion (other than converting type -> reference type)?
if (tryResolve(lExactMatch, argTypes, argCouldBeNULL)) if (tryResolve(lExactMatch, argPos, argTypes, argCouldBeNULL))
return true; return true;
if (exactMatchOnly == false) { if (exactMatchOnly == false) {
// Try to find a single match ignoring references // Try to find a single match ignoring references
if (tryResolve(lMatchIgnoringReferences, argTypes, argCouldBeNULL)) if (tryResolve(lMatchIgnoringReferences, argPos, argTypes,
argCouldBeNULL))
return true; return true;
// Try to find an exact match via type widening--i.e. int8 -> // Try to find an exact match via type widening--i.e. int8 ->
// int16, etc.--things that don't lose data. // int16, etc.--things that don't lose data.
if (tryResolve(lMatchWithTypeWidening, argTypes, argCouldBeNULL)) if (tryResolve(lMatchWithTypeWidening, argPos, argTypes, argCouldBeNULL))
return true; return true;
// Next try to see if there's a match via just uniform -> varying // Next try to see if there's a match via just uniform -> varying
// promotions. // promotions.
if (tryResolve(lMatchIgnoringUniform, argTypes, argCouldBeNULL)) if (tryResolve(lMatchIgnoringUniform, argPos, argTypes, argCouldBeNULL))
return true; return true;
// Try to find a match via type conversion, but don't change // Try to find a match via type conversion, but don't change
// unif->varying // unif->varying
if (tryResolve(lMatchWithTypeConvSameVariability, argTypes, if (tryResolve(lMatchWithTypeConvSameVariability, argPos, argTypes,
argCouldBeNULL)) argCouldBeNULL))
return true; return true;
// Last chance: try to find a match via arbitrary type conversion. // Last chance: try to find a match via arbitrary type conversion.
if (tryResolve(lMatchWithTypeConv, argTypes, argCouldBeNULL)) if (tryResolve(lMatchWithTypeConv, argPos, argTypes, argCouldBeNULL))
return true; return true;
} }
@@ -6604,8 +6530,8 @@ FunctionSymbolExpr::ResolveOverloads(const std::vector<const Type *> &argTypes,
const char *funName = candidateFunctions.front()->name.c_str(); const char *funName = candidateFunctions.front()->name.c_str();
Error(pos, "Unable to find matching overload for call to function \"%s\"%s.", Error(pos, "Unable to find matching overload for call to function \"%s\"%s.",
funName, exactMatchOnly ? " only considering exact matches" : ""); funName, exactMatchOnly ? " only considering exact matches" : "");
lPrintFunctionOverloads(funName, candidateFunctions); lPrintOverloadCandidates(argPos, candidateFunctions, argTypes,
lPrintPassedTypes(funName, argTypes); argCouldBeNULL);
return false; return false;
} }

5
expr.h
View File

@@ -634,13 +634,14 @@ public:
being done just given type information without the parameter being done just given type information without the parameter
argument expressions being available. It returns true on success. argument expressions being available. It returns true on success.
*/ */
bool ResolveOverloads(const std::vector<const Type *> &argTypes, bool ResolveOverloads(SourcePos argPos,
const std::vector<const Type *> &argTypes,
const std::vector<bool> *argCouldBeNULL = NULL); const std::vector<bool> *argCouldBeNULL = NULL);
Symbol *GetMatchingFunction(); Symbol *GetMatchingFunction();
private: private:
bool tryResolve(int (*matchFunc)(const Type *, const Type *), bool tryResolve(int (*matchFunc)(const Type *, const Type *),
const std::vector<const Type *> &argTypes, SourcePos argPos, const std::vector<const Type *> &argTypes,
const std::vector<bool> *argCouldBeNULL); const std::vector<bool> *argCouldBeNULL);
/** Name of the function that is being called. */ /** Name of the function that is being called. */

View File

@@ -135,7 +135,7 @@ lPossiblyResolveFunctionOverloads(Expr *expr, const Type *type) {
for (int i = 0; i < funcType->GetNumParameters(); ++i) for (int i = 0; i < funcType->GetNumParameters(); ++i)
paramTypes.push_back(funcType->GetParameterType(i)); paramTypes.push_back(funcType->GetParameterType(i));
if (fse->ResolveOverloads(paramTypes) == false) if (fse->ResolveOverloads(expr->pos, paramTypes) == false)
return false; return false;
} }
return true; return true;