Specializing friend template functions?

Hi.

I sort of have a suspicion that this might be a known issue, but I
haven't found anything so far. The code below compiles just fine with
GCC, but Clang complains:

---<snip>---
class c { template<class> friend bool f(c) { return false; } };
template<> bool f<int>(c) { return true; }

int main(){}
---<snip>---

cl.cc:2:1: error: extraneous 'template<>' in declaration of variable 'f'
template<> bool f<int>(c) { return true; }
^~~~~~~~~~
cl.cc:2:17: error: redefinition of 'f' as different kind of symbol
template<> bool f<int>(c) { return true; }
                ^
cl.cc:1:39: note: previous definition is here
class c { template<class> friend bool f(c) { return false; } };
                                      ^

As I am not an expert on the standard at all, the first question that
comes to mind is: is this actually legal? Given how inline friend
functions usually work, I have a feeling it should be legal.
Clang at least compiles it if the friend template function is not
defined inline, like this:

class c { template<class> friend bool f(c); };
template<class> bool f(c) { return false; }
template<> bool f<int>(c) { return true; }

In any case, while the error message hints at what is going on
internally, I think it could be improved. If this is legal, there
shouldn't be an error, of course. And if it is not, we could actually
let the user know that what they are trying isn't about to work, instead
of claiming that we are looking at a variable definition, which is quite
misleading methinks.

Secondly, looking at the AST for the code with the offending
specialisation commented out, I get:

-CXXRecordDecl 0x3123330 <cl.cc:1:1, col:62> col:7 class c definition
>-CXXRecordDecl 0x3123440 <col:1, col:7> col:7 implicit referenced class c
`-FriendDecl 0x3167120 <col:11, col:60> col:39
  `-FunctionTemplateDecl 0x31670b0 parent 0x3122980 <col:11, col:60> col:39 f
    >-TemplateTypeParmDecl 0x31234d0 <col:20> col:20 class
    `-FunctionDecl 0x3167000 parent 0x3122980 <col:27, col:60> col:39 f '_Bool (class c)'
      >-ParmVarDecl 0x3123590 <col:41> col:42 'class c'
      `-CompoundStmt 0x31671c0 <col:44, col:60>
        `-ReturnStmt 0x31671a0 <col:46, col:53>
          `-CXXBoolLiteralExpr 0x3167188 <col:53> '_Bool' false

So the FunctionTemplateDecl looks fine, but AST print is a bit messy:

class c {
    friend template <class > bool f(c) {
        return false;
    }

;
};
int main() {
}

Note, the extra semicolon (likely harmless) and the swapped 'friend'
keyword position. I don't know if 'friend' can be switched around like
this, but GCC *and* clang actually fail to compile that.

I guess the AST printer doesn't really require to always produce valid
source code (or does it), but this AST print seems to hint at some gaps
in AST handling around FriendDecl and nested TemplateFunctionDecl.

Or am I just babbling along, and everyone knows about this one already?

Hi.

I sort of have a suspicion that this might be a known issue, but I
haven't found anything so far. The code below compiles just fine with
GCC, but Clang complains:

---<snip>---
class c { template<class> friend bool f(c) { return false; } };
template<> bool f<int>(c) { return true; }

int main(){}
---<snip>---

cl.cc:2:1: error: extraneous 'template<>' in declaration of variable 'f'
template<> bool f<int>(c) { return true; }
^~~~~~~~~~
cl.cc:2:17: error: redefinition of 'f' as different kind of symbol
template<> bool f<int>(c) { return true; }
                ^
cl.cc:1:39: note: previous definition is here
class c { template<class> friend bool f(c) { return false; } };
                                      ^

As I am not an expert on the standard at all, the first question that
comes to mind is: is this actually legal? Given how inline friend
functions usually work, I have a feeling it should be legal.

I don't think this is legal. For an explicit specialization to be valid,
name lookup must find the template being explicitly specialized. Name
lookup for 'f' after the definition of 'c' does not find anything, so the
declaration appears to be ill-formed. (See [temp.names]p2 and p3.)

Clang at least compiles it if the friend template function is not

defined inline, like this:

class c { template<class> friend bool f(c); };
template<class> bool f(c) { return false; }
template<> bool f<int>(c) { return true; }

In any case, while the error message hints at what is going on
internally, I think it could be improved. If this is legal, there
shouldn't be an error, of course. And if it is not, we could actually
let the user know that what they are trying isn't about to work, instead
of claiming that we are looking at a variable definition, which is quite
misleading methinks.

I completely agree that we should diagnose this better.

Secondly, looking at the AST for the code with the offending

specialisation commented out, I get:

>-CXXRecordDecl 0x3123330 <cl.cc:1:1, col:62> col:7 class c definition
> >-CXXRecordDecl 0x3123440 <col:1, col:7> col:7 implicit referenced class
c
> `-FriendDecl 0x3167120 <col:11, col:60> col:39
> `-FunctionTemplateDecl 0x31670b0 parent 0x3122980 <col:11, col:60>
col:39 f
> >-TemplateTypeParmDecl 0x31234d0 <col:20> col:20 class
> `-FunctionDecl 0x3167000 parent 0x3122980 <col:27, col:60> col:39 f
'_Bool (class c)'
> >-ParmVarDecl 0x3123590 <col:41> col:42 'class c'
> `-CompoundStmt 0x31671c0 <col:44, col:60>
> `-ReturnStmt 0x31671a0 <col:46, col:53>
> `-CXXBoolLiteralExpr 0x3167188 <col:53> '_Bool' false

So the FunctionTemplateDecl looks fine, but AST print is a bit messy:

class c {
    friend template <class > bool f(c) {
        return false;
    }

;
};
int main() {
}

Note, the extra semicolon (likely harmless) and the swapped 'friend'
keyword position. I don't know if 'friend' can be switched around like
this, but GCC *and* clang actually fail to compile that.

I guess the AST printer doesn't really require to always produce valid
source code (or does it), but this AST print seems to hint at some gaps
in AST handling around FriendDecl and nested TemplateFunctionDecl.

We don't guarantee that the AST printer always produces valid code. The AST
dump looks fine to me for this case, though.

Richard Smith <richard@metafoo.co.uk> writes:

Hi.

I sort of have a suspicion that this might be a known issue, but I
haven't found anything so far. The code below compiles just fine with
GCC, but Clang complains:

---<snip>---
class c { template<class> friend bool f(c) { return false; } };
template<> bool f<int>(c) { return true; }

int main(){}
---<snip>---

cl.cc:2:1: error: extraneous 'template<>' in declaration of variable 'f'
template<> bool f<int>(c) { return true; }
^~~~~~~~~~
cl.cc:2:17: error: redefinition of 'f' as different kind of symbol
template<> bool f<int>(c) { return true; }
                ^
cl.cc:1:39: note: previous definition is here
class c { template<class> friend bool f(c) { return false; } };
                                      ^

As I am not an expert on the standard at all, the first question that
comes to mind is: is this actually legal? Given how inline friend
functions usually work, I have a feeling it should be legal.

I don't think this is legal. For an explicit specialization to be valid,
name lookup must find the template being explicitly specialized. Name
lookup for 'f' after the definition of 'c' does not find anything, so the
declaration appears to be ill-formed. (See [temp.names]p2 and p3.)

I wonder why name-lookup doesn't find anything. At least non-templated
free friend functions have always worked that way. Or is this a GCC
extension I am not aware of?

class d {
  ...
  friend bool operator==(d, d) { ... }
};
// here, d{} == d{} is found

[...]

So the FunctionTemplateDecl looks fine, but AST print is a bit messy:

class c {
    friend template <class > bool f(c) {
        return false;
    }

;
};
int main() {
}

Note, the extra semicolon (likely harmless) and the swapped 'friend'
keyword position. I don't know if 'friend' can be switched around like
this, but GCC *and* clang actually fail to compile that.

I guess the AST printer doesn't really require to always produce valid
source code (or does it), but this AST print seems to hint at some gaps
in AST handling around FriendDecl and nested TemplateFunctionDecl.

We don't guarantee that the AST printer always produces valid code.

Guessed that.

The AST dump looks fine to me for this case, though.

Note friend template<class> vs template<class> friend.
The original code was:

class c { template<class> friend bool f(c) { return false; } };

and ast-print switched that around to:

    friend template <class > bool f(c) {

which fails to compile on gcc and clang.

Richard Smith <richard@metafoo.co.uk> writes:

>
>> Hi.
>>
>> I sort of have a suspicion that this might be a known issue, but I
>> haven't found anything so far. The code below compiles just fine with
>> GCC, but Clang complains:
>>
>> ---<snip>---
>> class c { template<class> friend bool f(c) { return false; } };
>> template<> bool f<int>(c) { return true; }
>>
>> int main(){}
>> ---<snip>---
>>
>> cl.cc:2:1: error: extraneous 'template<>' in declaration of variable 'f'
>> template<> bool f<int>(c) { return true; }
>> ^~~~~~~~~~
>> cl.cc:2:17: error: redefinition of 'f' as different kind of symbol
>> template<> bool f<int>(c) { return true; }
>> ^
>> cl.cc:1:39: note: previous definition is here
>> class c { template<class> friend bool f(c) { return false; } };
>> ^
>>
>> As I am not an expert on the standard at all, the first question that
>> comes to mind is: is this actually legal? Given how inline friend
>> functions usually work, I have a feeling it should be legal.
>>
>
> I don't think this is legal. For an explicit specialization to be valid,
> name lookup must find the template being explicitly specialized. Name
> lookup for 'f' after the definition of 'c' does not find anything, so the
> declaration appears to be ill-formed. (See [temp.names]p2 and p3.)

I wonder why name-lookup doesn't find anything. At least non-templated
free friend functions have always worked that way.

That's not the case; try this:

struct S {
  friend void f(int);
};
void g() { f(0); } // error, undeclared identifier 'f'

Or is this a GCC
extension I am not aware of?

class d {
  ...
  friend bool operator==(d, d) { ... }
};
// here, d{} == d{} is found

There's a special lookup rule used for function calls that allows it to
find declarations that are not in scope; see
Argument-dependent name lookup - Wikipedia. This only
applies to function calls, and not, for instance, to explicit
specializations.

[...]

>> So the FunctionTemplateDecl looks fine, but AST print is a bit messy:
>>
>> class c {
>> friend template <class > bool f(c) {
>> return false;
>> }
>>
>> ;
>> };
>> int main() {
>> }
>>
>> Note, the extra semicolon (likely harmless) and the swapped 'friend'
>> keyword position. I don't know if 'friend' can be switched around like
>> this, but GCC *and* clang actually fail to compile that.
>>
>> I guess the AST printer doesn't really require to always produce valid
>> source code (or does it), but this AST print seems to hint at some gaps
>> in AST handling around FriendDecl and nested TemplateFunctionDecl.
>
> We don't guarantee that the AST printer always produces valid code.

Guessed that.

> The AST dump looks fine to me for this case, though.

Note friend template<class> vs template<class> friend.

Right, the AST pretty-printing is obviously producing ill-formed code. But
the AST dump looks fine.