Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to inspect the overload resolution set for a given call site

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:

  • all functions found by ADL,
  • all functions in the overload resolution set,
  • all functions rejected from the overload resolution set and why they were rejected, and
  • the ranks of the functions in the overload resolution set as well as the reason for their ranks.

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.

like image 927
gnzlbg Avatar asked Nov 20 '13 09:11

gnzlbg


People also ask

How do I fix overload resolution failed?

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.

What is overload resolution?

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.

How do you fix an ambiguous call to overloaded function?

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.

When you call an overloaded function How does compiler identify the right one?

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.


1 Answers

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.

like image 172
Dietmar Kühl Avatar answered Sep 17 '22 12:09

Dietmar Kühl