<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div><blockquote type="cite" class=""><div class="">On Jan 5, 2015, at 7:05 AM, Richard Smith <<a href="mailto:richard@metafoo.co.uk" class="">richard@metafoo.co.uk</a>> wrote:</div><div class=""><div dir="ltr" class=""><div class="gmail_extra"><div class="gmail_quote">On Sun, Jan 4, 2015 at 7:57 PM, John McCall <span dir="ltr" class=""><<a href="mailto:rjmccall@apple.com" target="_blank" class="">rjmccall@apple.com</a>></span> wrote:<br class=""><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word" class=""><div class=""><span class=""><blockquote type="cite" class=""><div class="">On Jan 4, 2015, at 6:28 PM, Richard Smith <<a href="mailto:richard@metafoo.co.uk" target="_blank" class="">richard@metafoo.co.uk</a>> wrote:</div><div class=""><p dir="ltr" class="">On 5 Jan 2015 02:08, "John McCall" <<a href="mailto:rjmccall@apple.com" target="_blank" class="">rjmccall@apple.com</a>> wrote:<br class="">
>><br class="">
>> On Jan 4, 2015, at 1:11 AM, Richard Smith <<a href="mailto:richard@metafoo.co.uk" target="_blank" class="">richard@metafoo.co.uk</a>> wrote:<br class="">
>> On Sat, Jan 3, 2015 at 1:46 PM, John McCall <<a href="mailto:rjmccall@apple.com" target="_blank" class="">rjmccall@apple.com</a>> wrote:<br class="">
>>>><br class="">
>>>> On Jan 2, 2015, at 7:10 AM, Richard Smith <<a href="mailto:richard@metafoo.co.uk" target="_blank" class="">richard@metafoo.co.uk</a>> wrote:<br class="">
>>>> On Tue, Dec 16, 2014 at 12:01 AM, Alexey Bataev <<a href="mailto:a.bataev@hotmail.com" target="_blank" class="">a.bataev@hotmail.com</a>> wrote:<br class="">
>>>>><br class="">
>>>>> Author: abataev<br class="">
>>>>> Date: Tue Dec 16 02:01:48 2014<br class="">
>>>>> New Revision: 224329<br class="">
>>>>><br class="">
>>>>> URL: <a href="http://llvm.org/viewvc/llvm-project?rev=224329&view=rev" target="_blank" class="">http://llvm.org/viewvc/llvm-project?rev=224329&view=rev</a><br class="">
>>>>> Log:<br class="">
>>>>> Renamed RefersToEnclosingLocal bitfield to RefersToCapturedVariable.<br class="">
>>>>> Bitfield RefersToEnclosingLocal of Stmt::DeclRefExprBitfields renamed to RefersToCapturedVariable to reflect latest changes introduced in commit 224323. Also renamed method Expr::refersToEnclosingLocal() to Expr::refersToCapturedVariable() and comments for constant arguments.<br class="">
>>>>> No functional changes.<br class="">
>>>><br class="">
>>>><br class="">
>>>> This seems like a bad idea for me. It's incorrect: for a lambda, the flag means that the DeclRefExpr refers to an enclosing local, and does *not* imply that the variable is necessarily captured. This confusion has already led to a bug (fixed in r225060).<br class="">
>>><br class="">
>>><br class="">
>>> If that’s actually a useful property to track, I have no complaint about tracking the two things separately.  I don’t think DRE is short of bits.<br class="">
>><br class="">
>><br class="">
>> The problem is that we can't reasonably track whether a DRE refers to a captured variable, because that is not a local property of the DRE; it depends on non-trivial properties of the context in which the DRE is used (and more generally it depends on how else that same variable is used elsewhere in the lambda, but we can largely ignore that).<br class="">
>><br class="">
>> I'd be fine having separate flags for 'refers to enclosing local' and 'refers to captured global' (if the latter is the point of this change), but we should not claim a DRE refers to a capture when it does not.<br class="">
><br class="">
><br class="">
> Richard, I understand that not all DREs are potentially evaluated and thus may not actually induce capturing, and I concede your point that the flag's name shouldn’t use the word “capture” if it’s set on unevaluated DREs.  The question is this: what information do we actually need to track on DREs?<br class="">
><br class="">
> It is very useful for IR-gen to be able to immediately know whether a DRE refers to a captured local or global variable, no matter why that capture was performed.  As far as I’m aware, IR-gen does not care about whether the flag is set on unevaluated references to enclosing variables, because IR-gen should never see an unevaluated use in normal expression emission.</p><p dir="ltr" class="">This is not about unevaluated references, it's about evaluated references that aren't odr uses, which depends on the context in which the DRE appears. IRGen does need to deal with these cases.</p></div></blockquote></span>Ugh, fine, I will go haul out the standard to figure out what you’re talking about, since you are apparently in a vague mood tonight.</div></div></blockquote><div class=""><br class=""></div><div class="">Apologies for the terseness, was replying from a phone.</div><div class=""> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word" class=""><div class="">Okay, I believe what you’re saying is:</div><div class="">1. A reference to an entity (a variable/this) within a lambda only causes a capture if it's an ODR use.</div><div class="">2. All ODR uses are PE references, but a PE reference isn't an ODR use if the variable can be used in a constant expression and it's a potential result of an expression that’s either ignored or loaded.</div><div class="">3. Whether an expression is ignored or loaded cannot be decided immediately from context; you have to semantically process the entire expression, and it may be different in different template instantiations.  (This dependence is also true of PPE-ness, I think.)</div><div class="">4. Since we sometimes share DRE nodes, we can’t retroactively alter a node after doing the necessary semantic analysis.</div><div class="">5. AST clients are likely to want to know whether a DRE is not an ODR-use and can only be constant-evaluated.  For example, it’s important that IR-gen not actually emit a reference to a static member variable of a class template if that member variable hasn’t been ODR-used.  Clients should similarly not be confused by an evaluated reference to an uncaptured enclosing local.</div><div class=""><br class=""></div><div class="">I think point #4 tells us that the desired information in #5 can’t actually be directly represented in the AST, though.</div></div></blockquote><div class=""><br class=""></div><div class="">Yes, that's an excellent summary. For concreteness, here's an example where this happens:</div><div class=""><pre style="" class="">void f(int, int);
void f(const int&, double);
auto f() {
  const int n = 0;
  return [=](auto t) { return [=]() { f(n, t); } };
}
void g() { f()(0)(); f()(0.)(); }</pre></div><div class="">Here, the lambda returned by f()(0) does not capture 'n', but the lambda returned by f()(0.) does, and they share a DeclRefExpr for n.<br class=""></div><div class=""><br class=""></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word" class=""><div class="">But as the first example shows, client interests in #5 are much broader than just references to enclosing locals.  IR-gen would benefit from knowing whether an arbitrary expression was constant-evaluable; currently it has hacky code to try to constant-evaluate specific nodes, and it lets all the other nodes fall out from LLVM’s own constant-folding and optimization, but it would clearly be better to, say, know that a particular call is a call to a constexpr function with constant arguments and so we can just directly try to fold it instead of emitting a whole bunch of unnecessary code that will get cleaned up later.  Using “this is an enclosing local” as an incomplete approximation of that isn’t actually useful.</div></div></blockquote><div class=""><br class=""></div><div class="">The only cases where we need to emit different code based on whether an expression is a constant expression (outside unevaluated contexts) are when initializing a variable of static/thread_local storage duration, and when an expression refers to an enclosing local variable that it doesn't capture.</div></div></div></div></div></blockquote><div><br class=""></div><div>You’re forgetting references to static data members, which don’t have to be defined if they have a constant initializer in the class definition and are never ODR-used.  (Of course, in practice people learn to avoid writing code that relies on this because it’s too easy to accidentally ODR-use a variable; but it’s in the spec.)  This is something that can’t as easily be hacked around in IRGen.</div><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class="gmail_extra"><div class="gmail_quote"><div class=""> In all other cases, it's correct and often reasonable to evaluate the expression at runtime.</div></div></div></div></div></blockquote><div><br class=""></div><div>It’s correct, but it’s easy to imagine code where this causes a great deal of unnecessary code to be emitted, optimized, and finally thrown away.</div><div><br class=""></div><div>Also we probably have bugs involving some of the recursive cases of potential results where IRGen will introduce illegal symbol references.  I’m particularly concerned about loads from members.</div><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class="gmail_extra"><div class="gmail_quote"><div class="">In the case where a lambda expression refers to an enclosing local but doesn't capture it, we should emit a copy of that local's value as a constant global.</div></div></div></div></div></blockquote><div><br class=""></div>Sure.<br class=""><div><br class=""></div><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div style="word-wrap:break-word" class=""><div class="">So it seems to me that we just need one bit, called refersToEnclosingVariableOrCapture(), with the meaning that it’s set if the DRE refers to an enclosing local (captured or not) or captured global variable, regardless of whether this reference is an ODR use.</div></div></blockquote><div class=""><br class=""></div><div class="">That seems fine to me.</div></div></div></div>
</div></blockquote></div><br class=""><div class="">Okay.  Alexey, would you mind doing this after you get back from break?</div><div class=""><br class=""></div><div class="">John.</div></body></html>