Template programming: std::function<> vs lambda not compiling on Clang but GCC.

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

Template programming: std::function<> vs lambda not compiling on Clang but GCC.

Manuel Klimek via cfe-dev
Dear Clang-Mailinglist,

I found some code which does compile on GCC (7.2) but fails to compile on Clang. Could you enlighten me, whether it should compile or not? It is using `if constexpr` to type match a template. 

* Clang complains:  "function: '...' with deduced return type cannot be used before it is defined."
* GCC compiles this fine and main returns "6" as expected.
* I am using Clang from trunk.

Is Clang right in rejecting this code, or is GCC too permissive in accepting this? Could you let me know, why this is the case?

Here is a minimal example, and the complete compile output for Clang. I also tried to reduce the example further with C-Reduce, but don't think the result aids understanding. 

// Compile with -std=c++17
#include <functional>

template <typename F> decltype(auto) call(F &&f) {

  // Match F to either f(), f(int) or f(int, int).
  // Return            int, int(), or int(int, int)
  if constexpr (std::is_invocable<F>::value) {
    return f();
  } else if constexpr (std::is_invocable<F, int>::value) {
    auto b = [f]() { return f(3); };
    return call(b);
  } else {
    // Note: Error in next line.
    // If beginning replaced to read auto b = ..., it compiles on Clang.
    std::function<int(int)> b = [f](int x) { return f(x, 2); };
    return call(b);
  }
}

int main() {
  return call([](int x, int y) { return x * y; });
}

Output: 
bug5_manual.cc:17:12: error: function 'call<std::__1::function<int (int)> &>' with deduced return type cannot be used before it is defined
    return call(b);
           ^
bug5_manual.cc:4:38: note: 'call<std::__1::function<int (int)> &>' declared here
template <typename F> decltype(auto) call(F &&f) {
                                     ^
bug5_manual.cc:22:10: error: cannot initialize return object of type 'int' with an rvalue of type 'void'
  return call([](int x, int y) { return x * y; });
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 errors generated.

Kind regards,
Christoph


-------------------------------------

PS: Here's the reduced code. As I said, I don't think it helps much.
template <typename c, c d> struct e { static constexpr c f = d; };
typedef e<bool, true> g;
struct h : g {};
template <typename> struct i : h {};
template <typename> class function;
template <typename j, typename... k> class function<j(k...)> {};
template <typename a> auto call(a) {
  if constexpr (i<int>::f)
    ;
  else {
    function<int()> b;
    call(b);
  }
}
main() {}

Reduced with the following script. For the reduction, I got the pre-processed code from GCC, the pre-processed code from Clang couldn't be compiled with GCC.
#!/bin/bash
set -e

# Should continue to compile on GCC.
g++ -std=c++17 bug5.cc # NOTE: take care to use GCC 7.2 here.

clang++ -std=c++17 --compile bug5.cc 2>&1 \
| grep -q "with deduced return type cannot be used before it is defined" 


_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: Template programming: std::function<> vs lambda not compiling on Clang but GCC.

Manuel Klimek via cfe-dev
Here is a really reduced example:

template<int I> auto foo() {
  if constexpr (I != 0)
    return 10;
  else
    return foo<1>();
}
int main() { return foo<0>(); }

gcc seems to apply [dcl.spec.auto]p9 wrongly, I think. It says:

If the type of an entity with an undeduced placeholder type is needed to determine the type of an expression, the program is ill-formed. Once a non-discarded return statement has been seen in a function, however, the return type deduced from that statement can be used in the rest of the function, including in other return statements.

The key here is "Once a non-discarded return statement [...]". Here, `return 10;` is a discarded return statement and must be ignored for deduction, and so the first sentence applies to the last return statement. Note that if it weren't for if constexpr, that function would be well-formed.

Cheers,
Nicolas

On Thu, Jan 4, 2018 at 6:18 PM, Christoph Siedentop via cfe-dev <[hidden email]> wrote:
Dear Clang-Mailinglist,

I found some code which does compile on GCC (7.2) but fails to compile on Clang. Could you enlighten me, whether it should compile or not? It is using `if constexpr` to type match a template. 

* Clang complains:  "function: '...' with deduced return type cannot be used before it is defined."
* GCC compiles this fine and main returns "6" as expected.
* I am using Clang from trunk.

Is Clang right in rejecting this code, or is GCC too permissive in accepting this? Could you let me know, why this is the case?

Here is a minimal example, and the complete compile output for Clang. I also tried to reduce the example further with C-Reduce, but don't think the result aids understanding. 

// Compile with -std=c++17
#include <functional>

template <typename F> decltype(auto) call(F &&f) {

  // Match F to either f(), f(int) or f(int, int).
  // Return            int, int(), or int(int, int)
  if constexpr (std::is_invocable<F>::value) {
    return f();
  } else if constexpr (std::is_invocable<F, int>::value) {
    auto b = [f]() { return f(3); };
    return call(b);
  } else {
    // Note: Error in next line.
    // If beginning replaced to read auto b = ..., it compiles on Clang.
    std::function<int(int)> b = [f](int x) { return f(x, 2); };
    return call(b);
  }
}

int main() {
  return call([](int x, int y) { return x * y; });
}

Output: 
bug5_manual.cc:17:12: error: function 'call<std::__1::function<int (int)> &>' with deduced return type cannot be used before it is defined
    return call(b);
           ^
bug5_manual.cc:4:38: note: 'call<std::__1::function<int (int)> &>' declared here
template <typename F> decltype(auto) call(F &&f) {
                                     ^
bug5_manual.cc:22:10: error: cannot initialize return object of type 'int' with an rvalue of type 'void'
  return call([](int x, int y) { return x * y; });
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 errors generated.

Kind regards,
Christoph


-------------------------------------

PS: Here's the reduced code. As I said, I don't think it helps much.
template <typename c, c d> struct e { static constexpr c f = d; };
typedef e<bool, true> g;
struct h : g {};
template <typename> struct i : h {};
template <typename> class function;
template <typename j, typename... k> class function<j(k...)> {};
template <typename a> auto call(a) {
  if constexpr (i<int>::f)
    ;
  else {
    function<int()> b;
    call(b);
  }
}
main() {}

Reduced with the following script. For the reduction, I got the pre-processed code from GCC, the pre-processed code from Clang couldn't be compiled with GCC.
#!/bin/bash
set -e

# Should continue to compile on GCC.
g++ -std=c++17 bug5.cc # NOTE: take care to use GCC 7.2 here.

clang++ -std=c++17 --compile bug5.cc 2>&1 \
| grep -q "with deduced return type cannot be used before it is defined" 


_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev



_______________________________________________
cfe-dev mailing list
[hidden email]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev