Cannot declare an operator within a function. Clang bug or spec?
Asked Answered
A

2

19

One of the weirder corner cases of C is that functions can be declared within other functions, e.g.

void foo(void)
{
  void bar(void); // Behaves as if this was written above void foo(void)
  bar();
}

This has carried through into C++, at least for most functions. Clang doesn't appear to recognise the pattern if the function in question happens to be called operator==.

struct foo
{
  int value;
};

struct bar
{
  foo value;
};

bool wot(const bar &x, const bar &y)
{
  bool eq(const foo &, const foo &);         // Declare function eq
  bool operator==(const foo &, const foo &); // Declare function operator==
  bool func = eq(x.value, y.value);          // This line compiles fine
  bool call = operator==(x.value, y.value);  // Also OK - thanks user657267!
  bool op = x.value == y.value;              // This one doesn't
  return func && call && op;
}

bool test()
{
  bar a;
  bar b;
  return wot(a,b);
}

GCC and ICC compile this fine. Checking name mangling in the object suggests the operator== has been declared with the right types. Clang (I tried up to 3.8) errors:

error: invalid operands to binary expression
      ('const foo' and 'const foo')
      bool op = x.value == y.value;
                ~~~~~~~ ^  ~~~~~~~

Unless the declaration is moved to directly above the function, in which case Clang is happy too:

bool operator==(const foo &, const foo &);
bool wot(const bar &x, const bar &y)
{
  return x.value == y.value; // fine
}

I can't use this workaround as the "real world" case that provoked this question involves layers of templates, meaning I only know the type name "foo" within the function declaration.

I believe this is a bug in Clang - is there special handling of operatorX free functions which prohibits declaring them within a function?

Atilt answered 22/3, 2016 at 9:20 Comment(3)
Seems to work fine using the explicit call bool op = operator==(x.value, y.value);, for whatever reason clang's operator lookup is failing to find the block scope declaration.Novel
Very good spot! Thank youAtilt
[over.match.oper]/2 says that operator invocation a@b causes a non-member candidate operator@(a,b) before anything else happens , so the fact that the second form works but the first doesn't strongly suggests a bugGardiner
S
7

For overloaded operators, see [over.match.oper]/(3.2):

[…] for a binary operator @ with a left operand of a type whose cv-unqualified version is T1 and a right operand of a type whose cv-unqualified version is T2, […] non-member candidates […] are constructed as follows:

The set of non-member candidates is the result of the unqualified lookup of operator@ in the context of the expression according to the usual rules for name lookup in unqualified function calls (3.4.2) except that all member functions are ignored. However, if no operand has a class type, […]

That is, we have the exact same name lookup rules as in ordinary calls, because x.value is of class type (foo). It's a Clang bug, and it occurs with all binary operators. Filed as #27027.

Subsequent answered 22/3, 2016 at 10:46 Comment(0)
S
2

It is true that C allows functions to be declared inside functions: 6.7.5.3 Function declarators §17 (draft n1256 for C99) says (emphasize mine)

If the declaration occurs outside of any function, the identifiers have file scope and external linkage. If the declaration occurs inside a function, the identifiers of the functions f and fip have block scope and either internal or external linkage (depending on what file scope declarations for these identifiers are visible), and the identifier of the pointer pfi has block scope and no linkage.

C++ also allows them. Draft n4296 for C++ 14 says:

13.2 Declaration matching [over.dcl]
...
2 A locally declared function is not in the same scope as a function in a containing scope. [ Example:
void f(const char*);
void g() {
extern void f(int);
...

(the above quote is only here to have an explicit evidence that C++ allows function declarations inside function)

I could even test that with your example this line:

bool op2 = operator == (x.value, y.value);

compiles fine without a single warning and gives expected results.

So I would say that it is a bug in Clang

Skittle answered 22/3, 2016 at 10:30 Comment(3)
What is the relevance of the 13.2 quote? In OP code there is no function of the same name in a containing scope anyway.Gardiner
@Gardiner it was only to give an explicit evidence that C++ allows functions declaration inside functions.Skittle
OK. [basic.link]/6 and /7 actually describe the matching rules for block scope function declarationsGardiner

© 2022 - 2024 — McMap. All rights reserved.