I think Clang may actually be correct.
According to [lambda.capture]/11, an id-expression used in the lambda refers to the lambda's by-copy-captured member only if it constitutes an odr-use. If it doesn't, then it refers to the original entity. This applies to all C++ versions since C++11.
According to C++17's [basic.dev.odr]/3 a reference variable is not odr-used if applying lvalue-to-rvalue conversion to it yields a constant expression.
In the C++20 draft however the requirement for the lvalue-to-rvalue conversion is dropped and the relevant passage changed multiple times to include or not include the conversion. See CWG issue 1472 and CWG issue 1741, as well as open CWG issue 2083.
Since m is initialized with a constant expression (referring to a static storage duration object), using it yields a constant expression per exception in [expr.const]/2.11.1.
This is not the case however if lvalue-to-rvalue conversions are applied, because the value of n is not usable in a constant expression.
Therefore, depending on whether or not lvalue-to-rvalue conversions are supposed to be applied in determining odr-use, when you use m in the lambda, it may or may not refer to the member of the lambda.
If the conversion should be applied, GCC and MSVC are correct, otherwise Clang is.
You can see that Clang changes it behavior if you change the initialization of m to not be a constant expression anymore:
#include <stdio.h>
#include <functional>
int n = 100;
void g() {}
std::function<int()> f()
{
int &m = (g(), n);
return [m] () mutable -> int {
m += 123;
return m;
};
}
int main()
{
int x = n;
int y = f()();
int z = n;
printf("%d %d %d\n", x, y, z);
return 0;
}
In this case all compilers agree that the output is
100 223 100
because m in the lambda will refer to the closure's member which is of type int copy-initialized from the reference variable m in f.