Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why clang does not find a function declared prior to the call site?

I want to provide some sqrt wrapper functions for simd types, such that they can be used from templates together with std::sqrt.

Now I have the problem, that they somehow are not visible. Only the ones defined in std can be used.

This a highly reduced part of the code:

#include <cmath>
#include <pmmintrin.h>

using float_simd = __m128;
using double_simd = __m128d;

float_simd sqrt(float_simd a) { return _mm_sqrt_ps(a); }
double_simd sqrt(double_simd a) { return _mm_sqrt_pd(a); }

template <typename T>
void do_fancy(T val) 
{
    using std::sqrt;
    auto ret = sqrt(val);
    (void)ret;
}

int main() {
    double testDouble = 1.0; 
    float testFloat = 1.0f;
    double_simd testSimdDouble;
    float_simd testSimdFloat;
    do_fancy(testDouble);
    do_fancy(testFloat);
    do_fancy(testSimdDouble);
    do_fancy(testSimdFloat);
    return 0;
}

Now clang gives this:

main.cpp:14:16: error: call to function 'sqrt' that is neither visible in the template definition nor found by argument-dependent lookup
    auto ret = sqrt(val);
               ^
main.cpp:25:5: note: in instantiation of function template specialization 'do_fancy<__attribute__((__vector_size__(2 * sizeof(double)))) double>' requested here
    do_fancy(testSimdDouble);
    ^
main.cpp:8:13: note: 'sqrt' should be declared prior to the call site
double_simd sqrt(double_simd a) { return _mm_sqrt_pd(a); }
            ^
main.cpp:14:16: error: call to function 'sqrt' that is neither visible in the template definition nor found by argument-dependent lookup
    auto ret = sqrt(val);
               ^
main.cpp:26:5: note: in instantiation of function template specialization 'do_fancy<__attribute__((__vector_size__(4 * sizeof(float)))) float>' requested here
    do_fancy(testSimdFloat);
    ^
main.cpp:7:12: note: 'sqrt' should be declared prior to the call site
float_simd sqrt(float_simd a) { return _mm_sqrt_ps(a); }
           ^

It says, that the simd sqrt functions "should be declared prior to the call site", but I think they are.

I did a web search, but unfortunately I only found cases, where the order of call and declaration actually was wrong.

I just commented out the using std::sqrt and everything works fine. I don't get it... How it is able to find the std::sqrt ones now?

I use clang 3.9.1 from homebrew on macOS.

like image 889
thorink Avatar asked Jan 04 '23 18:01

thorink


1 Answers

The using std::sqrt; adds a declaration of sqrt in the function body, so name lookup for the function finds that declaration and doesn't consider the ones outside the function body in the enclosing scope.

This is a form of "name hiding", which is a property of C++ where names in one scope "hide" entities with the same name in outer scopes. This happens because the compiler starts at the innermost scope and looks for a name, then only if there are no matches tries the enclosing scope, and so one until it reaches the outermost (i.e. global) scope. So it stops searching for the name once it finds one or matches in a given scope, and matching names in outer scopes don't get seen.

In your code the name sqrt is declared in the function body by the using declaration that refers to the std::sqrt function. Name lookup starts in the scope of that function body, finds a match, and doesn't look in the surrounding namespace.

You need to do:

using std::sqrt;
using ::sqrt;

That means that both sets of overloads (the ones you defined in the global namespace, and the ones the <cmath> header defined in namespace std) are declared in the function scope, and both can be found by name lookup. Now the compiler will find all those overloads in the function scope, and overload resolution will choose the best one based on the argument type.

Another option is to move the using std::sqrt using-declaration to the global namespace, instead of in the function body. That will add std::sqrt to the global namespace, so it doesn't hide your own sqrt overloads. Without the using-declaration in the function body there will be no matches in the innermost scope, so the compiler will look in the enclosing scope, which is the global namespace. In that scope it finds all the overloads and and overload resolution will choose the best one.

Another option is to replace <cmath> with <math.h> and remove the using std::sqrt;. That will ensure that the Standard Library version of sqrt is declared in the global namespace instead of namespace std, and therefore you don't need the using std::sqrt; to be able to call it unqualified.

As you noted in the comment below, it might also work if you just comment out the using-declaration, however that is not portable and is not guaranteed to work. It happens to work with the compiler you used, because <cmath> declares both std::sqrt and ::sqrt (so it's equivalent to having the using std::sqrt; in the global namespace), but not all compilers do that. To guarantee that you get ::sqrt in the global namespace you should put using std::sqrt; in the global namespace or include <math.h>.

like image 154
Jonathan Wakely Avatar answered Jan 30 '23 15:01

Jonathan Wakely