<div dir="ltr"><div dir="ltr">On Fri, 2 Aug 2019 at 12:17, Louis Dionne via cfe-dev <<a href="mailto:cfe-dev@lists.llvm.org">cfe-dev@lists.llvm.org</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 style="overflow-wrap: break-word;"><br><div><br><blockquote type="cite"><div>On Aug 2, 2019, at 15:05, Richard Smith <<a href="mailto:richard@metafoo.co.uk" target="_blank">richard@metafoo.co.uk</a>> wrote:</div><br class="gmail-m_5740644895859012947Apple-interchange-newline"><div><div dir="auto"><div>We already have code to consume the token sequence of a default argument without parsing it (we do that when parsing a class definition). So we could delay parsing default arguments until after we parse the parameters and determine whether we are in a generic lambda.</div><div dir="auto"><br></div><div dir="auto">But... I don't think that's enough. There could be a lambda-expression in the type of the parameter, and we might need to parse it and constant-evaluate a call to it in order to determine a parameter's type -- or just to parse at all in the case of naming a member template.</div><div dir="auto"><br></div><div dir="auto">I'm... not certain it's even possible to handle this correctly in general.</div><div dir="auto"><br></div><div dir="auto">Consider:</div><div dir="auto"><br></div><div dir="auto">[](A<B<[]{ return Y<struct S>; }()>::C < D > (E),</div><div dir="auto"><br></div><div dir="auto">At this point, we don't know whether we're still in a template argument list or not -- it depends on whether C is a template. And that depends on the evaluated value of the inner lambda.</div><div dir="auto"><br></div><div dir="auto">We must parse and evaluate the inner lambda in order to keep parsing, but its meaning depends on whether the outer lambda is generic: we can't instantiate Y if the local struct S is a dependent type, which it is if the outer lambda is a generic lambda.</div><div dir="auto"><br></div><div dir="auto">Now, we do know we're either at the start of a template argument or a function parameter, and a type containing a placeholder type is not valid in a template argument, so we could maybe keep going and assume that if we see a placeholder type, we must be in a function parameter so we must be parsing a generic lambda (but be careful about trailing return types...). It's not clear to me if there's some problem that prevents even that strategy from working, but I wouldn't be surprised (note that you can't do any name lookups, because you don't know if prior declaration-shaped things actually introduced any names in general).</div><div dir="auto"><br></div><div dir="auto">I think our first port of call should be the core reflector, to see if maybe we can tweak the rules to make this easier.</div><div dir="auto"><br></div><div dir="auto">One possible approach: lambda expressions shall not appear in a parameter declaration of a generic lambda unless there is a prior auto-typed parameter or the outer lambda has an explicit template parameter list. (That way, we can enter the dependent context when we finish parsing the first auto-typed parameter, and use the normal delay-parsing mechanism to handle default arguments.)</div></div></div></blockquote><div><br></div><div>In that case, would you introduce a new parse scope marked as a TemplateParamScope when you encounter the first 'auto', or would you "change" the current parse scope to be a TemplateParamScope? Because we'll already have injected some declarations into the current parse scope when we encounter the first 'auto', so naively introducing a new (nested) TemplateParamScope doesn't work out of the box. I did try that first approach and got a bunch of problems. I wasn't sure whether it meant the existing code had to be tweaked to correct broken assumptions, or whether it meant that was fundamentally the wrong approach (but you might know).</div></div></div></blockquote><div><br></div><div>Maybe the simplest thing to do would be to always create an enclosing (placeholder) scope and convert that to a TemplateParamScope when we find that it needs to be one, and then update the flags on the inner function parameter scope to match. But it seems messy regardless of what we do.</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 style="overflow-wrap: break-word;"><div><div>Louis</div><br><blockquote type="cite"><div><div dir="auto"><div dir="auto"><br><div class="gmail_quote" dir="auto"><div dir="ltr" class="gmail_attr">On Fri, 2 Aug 2019, 10:59 Louis Dionne via cfe-dev, <<a href="mailto:cfe-dev@lists.llvm.org" target="_blank">cfe-dev@lists.llvm.org</a>> wrote:<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 style="overflow-wrap: break-word;"><div>[CC: key people having touched that code]</div><div><br></div><div>Dear cfe-dev,</div><div><br></div><div>I'm working on fixing <a href="https://llvm.org/PR42851" rel="noreferrer" target="_blank">https://llvm.org/PR42851</a>, a segfault that manifests when parsing a lambda with a default argument that's a lambda:</div><div><br></div><div><font face="Monaco"><span style="font-style:normal">    int main() {</span></font></div><div><font face="Monaco"><span style="font-style:normal">        auto apply = [](auto, void(*)() = []{}) { };</span></font></div><div><font face="Monaco"><span style="font-style:normal">        apply(1); //                      ^^^^ BOOM</span></font></div><div><font face="Monaco"><span style="font-style:normal">    }</span></font></div><div><br></div><div>Long story short, what's happening is that when we parse the parameter-declaration-clause of the lambda, we don't record that we're in a dependent context, i.e. that we're basically parsing a template for the closure type's operator(). We don't know that we're in a template until we actually hit the first parameter declared with 'auto', but by then we've already done some things wrong (e.g. we've injected declarations into a parse scope that is not marked as being a template).</div><div><br></div><div>So what I've come to believe is that we should "tentatively parse" the parameter-declaration-clause of the lambda and try to determine whether it's going to be a generic lambda before we actually go ahead and parse it "for real", with a parse scope that's correctly marked as being dependent (or not). However, we must do that tentative parsing without calling into Sema, because calling into Sema has side effects that we can't undo easily. This means I can't reuse most of the existing machinery for parsing a parameter-declaration-clause.</div><div><br></div><div>I found TentativeParsingAction and I'm aware of a few tentative parsing functions in ParseTentative.cpp (like TryParseParameterDeclarationClause, which doesn't do quite what I need). However, before I invest any more time down that path, I'd like to know whether others think I'm going down the right path.</div><div><br></div><div>Any insights welcome,</div><div>Louis</div><div><br></div></div>_______________________________________________<br>
cfe-dev mailing list<br>
<a href="mailto:cfe-dev@lists.llvm.org" rel="noreferrer" target="_blank">cfe-dev@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev" rel="noreferrer noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev</a><br>
</blockquote></div></div></div>
</div></blockquote></div><br></div>_______________________________________________<br>
cfe-dev mailing list<br>
<a href="mailto:cfe-dev@lists.llvm.org" target="_blank">cfe-dev@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev</a><br>
</blockquote></div></div>