Bug 94025 - Expected-to-fail compilation goes through by not detecting mutable-specifier on lambda
Summary: Expected-to-fail compilation goes through by not detecting mutable-specifier ...
Status: NEW
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 10.0
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords: accepts-invalid
Depends on:
Blocks:
 
Reported: 2020-03-04 01:51 UTC by leon
Modified: 2020-04-18 18:25 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2020-03-04 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description leon 2020-03-04 01:51:15 UTC
From a small thread on the gcc-help mailing list 
https://gcc.gnu.org/ml/gcc-help/2020-03/msg00003.html  

( btw thanks to Jonathan for clearing up various considerations 
https://gcc.gnu.org/ml/gcc-help/2020-03/msg00011.html )

there could be a small bug in GCC (albeit not exclusive to GCC :) in that it doesn't consider the non-const (mutable) nature of lambda's function call operator when dealing with const objects which are stateless (e.g. lambda captures nothing). 

In the following example:

template<typename T>
void foo(T const f)
{
    f();
}

int main()
{
    foo([]() mutable {});
}

the compilation would probably be expected to fail if one is to follow the standard definition for lambda expressions, i.e. "... function call operator ... is declared const if and only if the lambda-expression’s parameter-declaration-clause is not followed by mutable."

Even though one could ponder the inconsequential nature of the above when being applied to stateless (captures-nothing) lambdas, for the sake of standard-compliance and even semantic consistency with explicit, yet equivalent(?), stateless types such as:

struct S {
    void operator()()
    {
    }
};

int main()
{
    foo(S());
}

... which dutifully *fails* to compile unless the function call operator is const-qualified, perhaps lambda with mutable-specifier should also be consistent and also fail to compile.

Sorry if I'm splitting hairs here :) (so far from my experiments with various compilers on godbolt.org MSVC was the only one to detect the above and diagnose the issue, with gcc and clang allowing it to go through).
Comment 1 Daniel Krügler 2020-04-14 20:16:56 UTC
In my opinion, this issue does not demonstrate a bug, but is based on an incomplete analysis of what is going on here. 

1) It is correct, that the lambda function call operator is non-const in this case. The result is that the function call operator of the lambda expression will *not* be called in the shown example.

2) We have here a lambda expression without any capture. This means that the standard requires the existence of an *additional* conversion function to a pointer to function ([expr.prim.lambda.closure] p7 quoted from N4849). And [expr.prim.lambda.closure] p11 says:

"The conversion function [..] is public, constexpr, non-virtual, non-explicit, const, and has a non-throwing exception specification (14.5)."

So effectively a second function call resolution is in affect here, selecting the conversion function (which is a const member function as specified above) to function pointer as the only viable candidate (If both were viable, the conversion function would be less preferred) via the surrogate call function ([over.call.object]). That explains IMO why the code is well-formed.

If you would try to mimic that with a user-defined class type, it would look similar to the following one:

struct Lambda
{
  using f_t = void();
  f_t operator(); // "mutable"
  using fptr_t = f_t*;
  operator fptr_t() const;
};

Note that I use here the very rarely used syntax to declare (but not define) a member function using a typedef for a function type to show the involved function types more precisely.

The example would become invalid once you introduce a capture, because in this case there would be no conversion function anymore.

I'm surprised that the Visual Studio compiler (I tested 2019) rejects the original example, this looks like a bug to me, especially since that compiler also handles the call resolution for the above defined Lambda type correctly. I plan to report an issue for that compiler.
Comment 2 Daniel Krügler 2020-04-18 18:25:52 UTC
(In reply to Daniel Krügler from comment #1)
[..]
> I'm surprised that the Visual Studio compiler (I tested 2019) rejects the
> original example, this looks like a bug to me, especially since that
> compiler also handles the call resolution for the above defined Lambda type
> correctly. I plan to report an issue for that compiler.

I have opened a corresponding bug report against the VS 2019 compiler:

https://developercommunity.visualstudio.com/content/problem/990374/conversion-function-to-function-pointer-for-mutabl-1.html