This is an archive of the discontinued LLVM Phabricator instance.

[ASTImporter] Do not look up lambda classes
ClosedPublic

Authored by martong on Aug 16 2019, 7:16 AM.

Details

Summary

Consider this code:

void f() {
  auto L0 = [](){};
  auto L1 = [](){};
}

First we import L0 then L1. Currently we end up having only one
CXXRecordDecl for the two different lambdas. And that is a problem if
the body of their op() is different. This happens because when we import
L1 then lookup finds the existing L0 and since they are structurally
equivalent we just map the imported L0 to be the counterpart of L1.

We have the same problem in this case:

template <typename F0, typename F1>
void f(F0 L0 = [](){}, F1 L1 = [](){}) {}

In StructuralEquivalenceContext we could distinquish lambdas only by
their source location in these cases. But we the lambdas are actually
structrually equivalent they differn only by the source location.

Thus, the solution is to disable lookup completely if the decl in
the "from" context is a lambda.
However, that could have other problems: what if the lambda is defined
in a header file and included in several TUs? I think we'd have as many
duplicates as many includes we have. I think we could live with that,
because the lambda classes are TU local anyway, we cannot just access
them from another TU.

Diff Detail

Repository
rL LLVM

Event Timeline

martong created this revision.Aug 16 2019, 7:16 AM
Herald added a reviewer: shafik. · View Herald Transcript
Herald added a project: Restricted Project. · View Herald Transcript

Hello Gabor!
I think it's a correct solution for the analyzer: usually, we cannot import a lambda until we have to import some enclosing expression - which means that the lambdas are actually not the same. But I'm not so sure about how it can affect the LLDB logic. @shafik Shafik, could you please take a look?

shafik added a comment.EditedAug 19 2019, 9:54 PM

I am not enthusiastic about this solution but I need to think about it some more.

We can see that p0624r2 added copy assignable lambdas:

bool f1() {
    auto x = []{} = {}; auto x2 = x;

    return x == x2;
}

bool f2() {
    auto x = []{} = {};
    auto xb = []{} = {};

    return x == xb;
}

see godbolt

So I don't think this is a long-term solution, although I don't know what clang is doing to make this work yet.

martong updated this revision to Diff 216378.Aug 21 2019, 5:25 AM
  • Add tests for default constructible and assignable stateless lambdas

I am not enthusiastic about this solution but I need to think about it some more.

We can see that p0624r2 added copy assignable lambdas:

bool f1() {
    auto x = []{} = {}; auto x2 = x;

    return x == x2;
}

bool f2() {
    auto x = []{} = {};
    auto xb = []{} = {};

    return x == xb;
}

see godbolt

So I don't think this is a long-term solution, although I don't know what clang is doing to make this work yet.

Hi Shafik, thank you for looking into this and for the links.
I have added two test cases, which demonstrates that the solution will work even with copy assignable lambdas.

The reason why this works is that when we import "x2" then there is a DeclRefExpr node which points the the lambda class CXXRecordDecl.
And that had been already imported when we imported "x", so it is mapped by MapImported. This means we just return with the mapped definition of the lambda class before the code would reach the part when we skip the lookup.

shafik accepted this revision.Aug 29 2019, 9:14 PM

I was concerned about how this would affect LLDB but after thinking about it I realized that in the DWARF we will just end up with one DW_TAG_class_type.

This revision is now accepted and ready to land.Aug 29 2019, 9:14 PM
This revision was automatically updated to reflect the committed changes.
Herald added a project: Restricted Project. · View Herald TranscriptAug 30 2019, 3:56 AM