How can I inspect the overload resolution set?
I have 4 competing functions used in multiple call sites. In one call site, I'm expecting one function to be called, but another one is picked up by the compiler. I don't know why/it's not trivial. To learn what is going on, I'm using enable_if/disable_if
to turn functions on/off but this is really slow/tedious/annoying.
So I would like the compiler to tell me "Why?". That is, for this single call site:
Information about access control is not required.
Basically I'm hoping for marking the call site with a #pragma
or similar (__builtin
...). But libclang would also be an option.
I have access to tip-of-trunk clang and gcc but can install other compiler/tools if necessary.
To correct this error In your calling statement, make the data types of the arguments match the data types of the parameters defined for the desired overload. You might have to use the CType Function to convert one or more data types to the defined types.
The process of selecting the most appropriate overloaded function or operator is called overload resolution. Suppose that f is an overloaded function name. When you call the overloaded function f() , the compiler creates a set of candidate functions.
There are two ways to resolve this ambiguity: Typecast char to float. Remove either one of the ambiguity generating functions float or double and add overloaded function with an int type parameter.
At compile time, the compiler chooses which overload to use based on the types and number of arguments passed in by the caller. If you call print(42.0) , then the void print(double d) function is invoked. If you call print("hello world") , then the void print(std::string) overload is invoked.
I can imagine writing a clang plug-in inspecting which function is being called and what others are in the overload set. I'd think tracing the look-up rules and finding out why the candidates from the overload set are discarded and why the chosen function is the best candidate in the overload set is something quite different, though.
I haven't played with determining overload sets, etc. However, below is a simple starting point: a clang plug-in which prints the function called if a function with a specific name (currently hard-coded to be "foo"
) is found. It also print the found overloads.
I'm compiling the code and running it with the commands (obviously, these are stored in a make
file):
/opt/llvm-debug/bin/clang -I/usr/include/c++/4.2.1 -I/opt/llvm-debug/include -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -g -fno-exceptions -fno-rtti -c -o MacOS/overloads.o overloads.cpp
/opt/llvm-debug/bin/clang -L/opt/llvm-debug/lib -Wl,-undefined,dynamic_lookup -dynamiclib -o MacOS/overloads.dylib MacOS/overloads.o
/opt/llvm-debug/bin/clang -cc1 -load MacOS/overloads.dylib -plugin overloads -plugin-arg-overloads argument -fexceptions tst.cpp
The version of clang used is built with debug information: otherwise it doesn't seem to find a debug symbol. I should probably find out how to build a tool directly and not run from within clang.
#include <clang/Frontend/FrontendPluginRegistry.h>
#include <clang/Frontend/CompilerInstance.h>
#include <clang/Lex/Preprocessor.h>
#include <clang/Lex/PPCallbacks.h>
#include <clang/AST/ASTConsumer.h>
#include <clang/AST/AST.h>
#include <clang/AST/RecursiveASTVisitor.h>
#include <clang/Sema/Sema.h>
#include <clang/Sema/Lookup.h>
#include <llvm/Support/raw_ostream.h>
#include <string>
using namespace clang;
using namespace llvm;
typedef clang::CompilerInstance CI;
typedef clang::DeclGroupRef DGR;
typedef clang::DiagnosticsEngine DE;
// ----------------------------------------------------------------------------
namespace
{
struct Consumer: clang::ASTConsumer
{
Consumer(CI& c, std::string const& name): c_(&c), name_(name) {}
bool HandleTopLevelDecl(clang::DeclGroupRef DG);
CI* c_;
std::string name_;
};
}
// ----------------------------------------------------------------------------
struct Visitor: RecursiveASTVisitor<Visitor>
{
CI* c_;
std::string name_;
Visitor(CI* c, std::string const& name): c_(c), name_(name) {}
bool VisitCallExpr(CallExpr* d);
};
bool Visitor::VisitCallExpr(CallExpr* c) {
FunctionDecl* fun = c->getDirectCallee();
if (fun && fun->getNameAsString() == this->name_) {
SourceLocation w(c->getExprLoc());
DE &de(this->c_->getDiagnostics());
int id = de.getCustomDiagID(DE::Warning, "function call: %0");
int info = de.getCustomDiagID(DE::Note, "function called");
DiagnosticBuilder(de.Report(w, id))
<< fun->getNameAsString()
;
DiagnosticBuilder(de.Report(fun->getLocStart(), info))
<< fun->getNameAsString()
;
Sema& sema = this->c_->getSema();
LookupResult result(sema, fun->getDeclName(), w, Sema::LookupOrdinaryName);
DeclContext* context = fun->getDeclContext();
if (sema.LookupName(result, sema.getScopeForContext(context))) {
int over = de.getCustomDiagID(DE::Note, "function overload");
LookupResult::Filter filter = result.makeFilter();
while (filter.hasNext()) {
DiagnosticBuilder(de.Report(filter.next()->getLocStart(), over))
;
}
filter.done();
}
}
//else {
// // I think the callee was a function object or a function pointer
//}
return true;
}
void doDecl(Consumer* c, Decl* d) {
Visitor(c->c_, c->name_).TraverseDecl(d);
}
// ----------------------------------------------------------------------------
bool Consumer::HandleTopLevelDecl(DeclGroupRef DG) {
std::for_each(DG.begin(), DG.end(),
std::bind1st(std::ptr_fun(&doDecl), this));
return true;
}
// ----------------------------------------------------------------------------
namespace
{
class Plug
: public clang::PluginASTAction
{
protected:
ASTConsumer*
CreateASTConsumer(CompilerInstance& c, llvm::StringRef);
bool ParseArgs(clang::CompilerInstance const&,
std::vector<std::string> const&) {
return true;
}
};
}
ASTConsumer*
Plug::CreateASTConsumer(CompilerInstance& c, llvm::StringRef) {
return new Consumer(c, "foo");
}
static clang::FrontendPluginRegistry::Add<Plug>
registerPlugin("overloads", "report overloads of a function at a call");
The code isn't pretty and isn't really doing what you are looking for. However, formatting the function declarations a bit nicer, possibly investigating a bit with the Sema
object why it isn't a match, etc. could get the code reasonably close to the tool you are looking for, I think.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With