<div dir="ltr"><div dir="ltr">On Tue, 27 Oct 2020 at 12:11, Arthur O'Dwyer <<a href="mailto:arthur.j.odwyer@gmail.com">arthur.j.odwyer@gmail.com</a>> wrote:<br></div><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr">On Mon, Oct 26, 2020 at 8:14 PM Richard Smith via cfe-dev <<a href="mailto:cfe-dev@lists.llvm.org" target="_blank">cfe-dev@lists.llvm.org</a>> wrote:<br>> On Mon, 26 Oct 2020 at 16:57, Lewis, Cannada via cfe-dev <<a href="mailto:cfe-dev@lists.llvm.org" target="_blank">cfe-dev@lists.llvm.org</a>> wrote:<br>>><br>>> I’m not a standards reading expert but does<br>>><br>>> Note 7:<br>>> An id-expression that is not an odr-use refers to the original entity, never to a member of the closure type.<br>>> However, such an id-expression can still cause the implicit capture of the entity.<br>>> — end note<br>>><br>>> From the text imply that the program <a href="https://godbolt.org/z/feKxdK" target="_blank">https://godbolt.org/z/feKxdK</a> is actually implementation defined? Or does gcc have a bug here?<br>><br>> GCC has a bug, according to the standard wording. The mention of myfoo does not constitute an odr-use, so is not rewritten to use the capture. Clang's behavior is correct per the standard wording.<br>><br>> The standard rule is certainly surprising in this particular case. I think the rule in question is driven by the desire for adding a capture-default to a valid lambda to not change its meaning. For example: <a href="https://godbolt.org/z/nrWsvj" target="_blank">https://godbolt.org/z/nrWsvj</a><br><br>Hmm. It's insane that you can use local variable `x` inside a lambda that doesn't capture anything; I wasn't aware of that.</div></blockquote><div><br></div><div>Aside: "insane" (here) and "crazy" (below) are somewhat more charged language than we'd prefer here.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr">If the formal wording really says you can do that, then that seems like a formal-wording bug to me.</div></blockquote><div><br></div><div>It's entirely intentional, though the case that was being thought about at the time was primarily local constant non-references, not local references (though the same rules apply to both).</div><div><br></div><div>void f() {</div><div>  constexpr int k = 5;</div><div>  [] { int arr[k]; };</div><div>}</div><div><br></div><div>... should obviously be valid. And more broadly, the rule is that anything that you can name without an odr-use is valid. And this isn't at all special to lambdas; the same thing happens with all nested constructs:</div><div><br></div><div>int n;</div><div>void g() {</div><div>  const int q = 6;</div><div>  int &r = n;</div><div>  constexpr int *p = &n;</div><div>  struct A {</div><div>    void f() {</div><div>      int arr[q]; // ok</div><div>      r = *p + 1; // ok</div><div>    }</div><div>  };</div><div>}</div><div><br></div><div><a href="https://godbolt.org/z/avG7qx">https://godbolt.org/z/avG7qx</a><br></div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div>GCC and Clang disagree in a different way about whether it's okay to dereference a <i>pointer</i> inside a lambda without capturing it; here it's GCC that is doing the crazy thing, and Clang that is reasonably refusing to compile a reference to `x` when `x` hasn't been captured.</div><div><a href="https://godbolt.org/z/bjbr3f" target="_blank">https://godbolt.org/z/bjbr3f</a></div></div></blockquote><div><br></div><div>Yeah, GCC's behavior is not in line with the language rules in the pointer case. I'd guess they're using a more-liberal evaluation mode when evaluating the initializer of the reference, rather than using the strict constant initializer rules, and that more-liberal mode permits them to read the initializer values of all const-qualified variables, not only the ones that are usable in constant expressions. (Clang has a more-liberal mode too, and I'd expect we also have bugs where a conforming program can tell the difference.)</div><div><br></div><div>The rules in question are approximately equivalent to: if a variable of const integral or enumeration type, or of reference type, can be constexpr, then it is implicitly constexpr. And constexpr variables can be used from anywhere, without triggering an odr-use, if only their value, not their address, is used. (We don't actually say such things are implicitly constexpr, and there are corner cases where the outcome is different -- in particular, for static data members, where constexpr implies inline but const does not. But "implicitly constexpr" is how they behave.)</div><div><br></div><div>If you change your example to declare the pointer to be constexpr then the two compilers agree again.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr">The rules about when compilers are able to "constant-fold" away variables that otherwise would need to be captured, strikes me as similar in spirit to the rules about when compilers are able to "constant-fold" away complicated expressions in constant expressions. Some compilers seem to be trying to be "helpful" by letting the optimizer's knowledge leak into the front-end, and some are following the same rules as one's "head compiler" would.<br></div></blockquote><div><br></div><div>I think this is at least not the proximal cause in the standard-conforming case -- correctly implementing the odr-use rules in the Clang frontend was a non-trivial effort and wasn't related to constant folding / optimizer knowledge leaking through. But I do think the standard rules here probably originally came from looking at what implementations at the time happened to do and writing that down (eg, use of a const static data member with no definition is OK, because implementations happen to not emit a reference to the symbol), and complexity has built up from that point. I'm not sure where the references-are-implicitly-constexpr part came from, but the const-ints-are-implicitly-constexpr part is a backwards-compatibility concern inherited from C++98.</div><div><br></div><div>From a language design perspective, I think it would have made a lot more sense if we'd said that constexpr variables are implicitly static (so the question of capture or of use from other scopes can be answered trivially), and deprecated the "implicit constexpr" behavior for const integral/enumeration variables and references. Where we've ended up isn't a great compromise. But this is really the wrong place to have such discussions :)</div></div></div>