10

I have some very simple (C++11) code which the latest clang (version 3.4 trunk 187493) fails to compile, but GCC compiles fine.

The code (below) instantiates the function-template foo with the function-local type Bar and then tries to use its address as a non-type template parameter for the class-template Func:

template<void(*FUNC_PTR)(void)>
struct Func {};

template<typename T> extern inline
void foo() {
    using Foo = Func<foo<T>>;
}
int main() {
    struct Bar {}; // function-local type
    foo<Bar>();
    return 0;
}

clang emits the following error:

error: non-type template argument refers to function 'foo' that does not have linkage

However, if I move type Bar to global scope (by taking it out of the function), then clang compiles it fine, proving the issue is with the type being function-local.

So is clang correct to emit this error, or does the standard not support this (in which case GCC is being too lenient by allowing it)?


EDIT #1 : To be clear, this is not a duplicate of this question since the 'cannot use local types as template parameters' restriction was removed in C++11. However, it's still unclear if there are linkage implications involved with using a local type, and whether clang is correct or not in emitting this error.


EDIT #2 : It has been determined that clang was correct to emit the error for the above code (see answer from @jxh), but that it incorrectly also emits an error for the following code (with using declaration moved from foo<Bar>() scope to main() scope):
template<void(*FUNC_PTR)(void)>
struct Func {};

template<typename T> extern inline
void foo() {}

int main() {
    struct Bar {};
    using F = Func<foo<Bar>>;
    return 0;
}
Community
  • 1
  • 1
etherice
  • 1,761
  • 15
  • 25
  • 1
    The clang that ships with the latest version of Xcode says "__error__: non-template argument refers to function 'foo' with internal linkage" instead. – zneak Aug 28 '13 at 22:07
  • 1
    @jxh, this happens even when passing `--std=c++11` to clang, though. – zneak Aug 28 '13 at 22:08
  • 1
    @jxh: It's not a duplicate of that question, because C++11 removed that restriction, and clang is claiming this is a linking error of some kind. – etherice Aug 28 '13 at 22:08
  • @zneak: Interesting, since the function is declared as `extern` it makes me think this is a clang bug. – etherice Aug 28 '13 at 22:09
  • Does `foo` have linkage or not? I can't quite figure this out. – Kerrek SB Aug 28 '13 at 22:31
  • @KerrekSB: I guess that's the question, isn't it? It appears that it *should* since it's declared as `extern inline` which the standard specifically allows for template functions per the ODR. Furthermore, gcc has no problem compiling & linking this code. – etherice Aug 28 '13 at 22:35
  • @KerrekSB: To be more specific, ODR (C++11 § 3.2/5) states `There can be more than one definition of a class type, enumeration type, inline function with external linkage, [...]` and since the function template is declared as `extern inline` I would expect an instantiation of it to be an "inline function with external linkage" as acknowledged by the standard. – etherice Aug 28 '13 at 22:53
  • 1
    @etherice: Never mind the `inline` - what I can't figure out is whether it's OK to instantiate the `foo` template with a type that has no linkage and get something out that has linkage... – Kerrek SB Aug 28 '13 at 22:53
  • 1
    It would be nice if 14/4 said something about linkage of template specializations. It seems to be avoiding the issue. – aschepler Aug 28 '13 at 22:59
  • @KerrekSB: Thanks for clarifying- And that does seem to be the question at hand. I would *expect* the template instantiation to simply have extern linkage as something like `foo`, since C++11 specifically allows the use of local types as template parameters. – etherice Aug 28 '13 at 22:59
  • What happens if you use `Func<&foo>` instead, does `clang` still give the error? – jxh Aug 28 '13 at 23:14

2 Answers2

3

By definition of no linkage in C++.11 §3.5 Program and linkage ¶2, I originally believed foo<Bar> has no linkage since it cannot be referred to by name by any other scope except that which defined the type Bar (ie, main()). However, this is not correct. This is because the definition of a name with external linkage is described as:

When a name has external linkage, the entity it denotes can be referred to by names from scopes of other translation units or from other scopes of the same translation unit.

And for a template function, this will always be the case. This is because there is one other scope from which the name can be referred. Namely, the template function can refer to itself. Therefore, foo<Bar> has external linkage. zneak's answer, EDIT 2, has an e-mail thread with the clang developers confirming that foo<Bar> should have external linkage.

Thus, from C++.11 §14.3.2 Template non-type arguments ¶1:

A template-argument for a non-type, non-template template-parameter shall be one of: ...

  • a constant expression (5.19) that designates the address of an object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as &id-expression, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; ...
  • The most relevant bullet is the third bullet. Since foo<bar> has external linkage, passing it as a non-type template-parameter should be fine.

    Community
    • 1
    • 1
    jxh
    • 69,070
    • 8
    • 110
    • 193
    • @zneak: You cannot call `foo()` from `main`, but you **can** instantiate `Func>` from `main`, proving that jxh has provided the correct answer. – etherice Aug 28 '13 at 23:25
    • Then, if you have global `void (*p)();`, does `p = foo;` work from `main`? – jxh Aug 28 '13 at 23:35
    • @jxh: Yes `foo` (by itself) always works from `main`, but try to use it as a non-type template parameter such as `Foo>` (or even `Foo<&foo>`) and it does **not** work. It appears that clang is too strict, and gcc is too lenient... Neither one is implemented correctly in this case. – etherice Aug 28 '13 at 23:38
    • Yes, `foo` can be assigned to a global `p`. – zneak Aug 28 '13 at 23:39
    • @jxh: I see what you're getting at though. It allows a global pointer to point to `foo` but does not allow a template parameter to have the address, which is inconsistent. Again, clang is too strict by not allowing instantiation of `Foo>` inside of `main`, though it is correct not to allow it in `foo` since that is outside of `main` (gcc is incorrect to allow that case). – etherice Aug 28 '13 at 23:45
    • Did you play with alternative declarations of `Func::func_ptr`? I was thinking `clang` is doing something different because of the use of `constexpr`. – jxh Aug 28 '13 at 23:54
    • @jxh: Yes, it makes no difference whether it's `const` or `constexpr` or removed completely and just do `using F = Func>;` from `main` ... either way clang doesn't allow the non-type template argument to `refer to object that does not have linkage`. – etherice Aug 28 '13 at 23:56
    • Thanks, I want to do some more research before I make the edits. – jxh Aug 29 '13 at 00:04
    • 1
      Okay, based on the response @zneak got from the clang developers, I think there is loophole that allows `foo` to be considered to have external linkage. – jxh Aug 29 '13 at 05:18
    3

    I'm late to the party, but standard says that type-local functions don't have linkage (§3.5:8):

    Names not covered by these rules have no linkage. Moreover, except as noted, a name declared at block scope (3.3.3) has no linkage.

    Same section goes on to say:

    A type without linkage shall not be used as the type of a variable or function with external linkage unless

    • the entity has C language linkage (7.5), or
    • the entity is declared within an unnamed namespace (7.3.1), or
    • the entity is not odr-used (3.2) or is defined in the same translation unit.

    And, as a matter of fact, Clang will allow this:

    namespace
    {
        template<void (*FUNC_PTR)(void)>
        struct Func {};
    
        template<typename T>
        void foo() {}
    }
    
    int main() {
        struct Bar {}; // function-local type
        Func<foo<Bar>> x;
    }
    

    And will reject it without the anonymous namespace.


    EDIT 1: As jxh notes, the names are also defined in the same translation unit, so I'm not sure what to think of this one.


    EDIT 2: The guys at clang confirm it's a bug.

    zneak
    • 134,922
    • 42
    • 253
    • 328
    • 1
      Doesn't the third bullet apply, since they are in the same translation unit? – jxh Aug 29 '13 at 00:34
    • I also was initially referring to this section when I first wrote my answer, but I deleted it because I decided I could not really think of a function name as a type. – jxh Aug 29 '13 at 00:42
    • @zneak: `foo` already worked from `main` even when `foo<>` was not declared in an anonymous namespace. The only difference from before is class template `Func` can be instantiated with `foo` from inside `main`, but that does not seem pertinent to the paragraph you stated since the "type of the variable" `FUNC_PTR` is `void(*)(void)` and it is not really using `foo` as "the type of" `FUNC_ADDR` but rather its address/pointer value. But I can't blame clang (or gcc) since there is a lot of room for interpretation and confusion. – etherice Aug 29 '13 at 01:12
    • Okay, I am thoroughly confused. The e-mail exchange says `foo` has external linkage. But, there is no way any code can call it except for code within `main()` itself. Oh wait, there's a loop hole provided by the definition of external linkage... – jxh Aug 29 '13 at 02:29
    • @jxh: The response from the clang dev team makes sense actually, and is what I initially suspected. The function `foo` has external linkage, but the compiler mistakenly downgrades the object's linkage because it was instantiated with a function-local type without linkage, and consequently does not allow it to be used for the non-type parameter which requires "the address of an object with [...] external or internal linkage". So it's not really a loophole in the standard or anything, it's just a "linkage downgrading" bug in clang. – etherice Aug 30 '13 at 00:20
    • @etherice: I call it a loop hole because `foo` can only be addressed by name within two scopes: `main()`'s and `foo()`'s. Anyways, this was a neat question, and I'd upvote it more if I could. – jxh Aug 30 '13 at 00:29
    • @jxh: I see what you meant, but ... The function **object** (which has extern linkage) is not bound to the **name** `foo`. The function object is still valid and callable outside of `main`, e.g., if a global `void(*)void` stores its address. Or, assuming its **address** is `A`, callable from `Func::func_ptr` which is a type with extern linkage (unless it's in an anonymous namespace). – etherice Aug 30 '13 at 00:48
    • @etherice: All valid objects are accessible via their address, I never disputed that. But external linkage only applies to identifiers (of objects or of types). The only identifier that applies as a non-type template argument would be `foo`, I don't see any other way to interpret the standard on this point. – jxh Aug 30 '13 at 01:05
    • @jxh: Good points. Thanks again for your help (and zneak as well). I upvoted both answers, and I would accept both if I could. – etherice Aug 30 '13 at 03:57