This is an archive of the discontinued LLVM Phabricator instance.

PR45000: Let Sema::SubstParmVarDecl handle default args of lambdas in initializers
ClosedPublic

Authored by aaronpuchert on Mar 11 2020, 7:40 PM.

Details

Summary

We extend the behavior for local functions and methods of local classes
to lambdas in variable initializers. The initializer is not a separate
scope, but we treat it as such.

We also remove the (faulty) instantiation of default arguments in
TreeTransform::TransformLambdaExpr, because it doesn't do proper
initialization, and if it did, we would do it twice (and thus also emit
eventual errors twice).

Diff Detail

Event Timeline

aaronpuchert created this revision.Mar 11 2020, 7:40 PM
Herald added a project: Restricted Project. · View Herald TranscriptMar 11 2020, 7:40 PM
Herald added a subscriber: cfe-commits. · View Herald Transcript

@rsmith, should I put a testcase under test/SemaTemplate/, perhaps as instantiate-lambda.cpp?

aaronpuchert planned changes to this revision.Mar 11 2020, 8:02 PM

@rsmith, should I put a testcase under test/SemaTemplate/, perhaps as instantiate-lambda.cpp?

I'll probably put it into test/SemaCXX/vartemplate-lambda.cpp like D23096 that added the loop.

Seems the same issue in Sema::SubstParmVarDecl was fixed in rGdc40b618cf397df7369406b3f61e91ccb57fb9f6.

Ping - any update on this one? Are you waiting on @rsmith for input on where to place a testcase?

Ping - any update on this one? Are you waiting on @rsmith for input on where to place a testcase?

No, I just didn't get around to writing the test yet, thanks for reminding me.

Although input from @rsmith would be appreciated. There are two ways to write the test: rely on the assertion to catch the type mismatch or inspect an AST dump and look for an ImplicitCastExpr. I'm tending towards the latter option, because it doesn't rely on assertions. (Which aren't always on.)

I tested a bit more and there is a problem: some errors now appear twice, and I think they shouldn't.

template <typename T>
void f(int x = [](T x = nullptr) -> int { return x; }());

void g() { f<int>(); }

The error is expected, but it should only appear once:

<stdin>:2:23: error: cannot initialize a parameter of type 'int' with an rvalue of type 'nullptr_t'
  void f(int x = [](T x = nullptr) -> int { return x; }());
                      ^   ~~~~~~~

Add test case, loosely based on that in the bug report. By choosing a conversion that should error out we can detect the issue in a pure frontend test.

However, we would expect the error only once. Further investigation is needed.

rsmith added inline comments.Apr 16 2020, 2:05 PM
clang/lib/Sema/TreeTransform.h
12161–12162

This logic shouldn't even be here in the first place.. this should all be handled by SubstParmVarDecl in SemaTemplateInstantiate. It's wrong for TreeTransform to be assuming that TransformFunctionProtoType will have built new function parameters -- it doesn't do so by default, so in the non-template-instantiation case, this is clobbering the default argument information on the original lambda. And in the template instantiation case, this is clobbering the default argument information (including initialization) that SubstParmVarDecl already did.

Please try deleting this loop entirely. If there are still problems (and I think there are: I don't think we handle delayed instantiation for default argument in generic lambdas properly), we should address them by changing how we do default argument instantiation in SubstParmVarDecl. In particular, where that function calls SetParamDefaultArgument after calling SubstExpr, we should presumably instead be calling setUninstantiatedDefaultArg on the parameter if it's a parameter of a generic lambda (we still need to instantiate the default argument with the template arguments of the actual lambda call operator).

rsmith added inline comments.Apr 16 2020, 3:15 PM
clang/lib/Sema/TreeTransform.h
12161–12162

Hmm, what I wrote above isn't entirely true -- TreeTransform does create new parameters by default. But nonetheless, it's the responsibility of TransformFunctionTypeParam to set up the default argument, not the responsibility of this code, so we shouldn't be partially overwriting its results here. (Perhaps we should move the substitution into default arguments from SubstParmVarDecl into TreeTransform::TransformFunctionTypeParam, though I think that can be done separately from this bugfix.)

aaronpuchert marked 2 inline comments as done.Apr 16 2020, 6:34 PM
aaronpuchert added inline comments.
clang/lib/Sema/TreeTransform.h
12161–12162

Thanks for your hints! I was already somewhat suspicious of that loop. Removing it works indeed pretty well, but I'm running into an issue with variable templates. Default arguments are usually only instantiated when needed by a call, which happens in Sema::CheckCXXDefaultArgExpr. Now in the following code, the MultiLevelTemplateArgumentList returned from Sema::getTemplateInstantiationArgs is empty instead of being [int]:

template <typename T>
int k = [](T x = 0.0) -> int { return x; }();

template int k<int>;

Looking at getTemplateInstantiationArgs, that's not very surprising: if we are a DeclContext, we go through the contexts and collect all template arguments from those, but a variable template is not a context. The variable template case is handled right at the beginning, but only if the declaration itself is a variable template.

Meaning that deep inside the initializer of a variable template, we don't get its template parameters.

aaronpuchert marked an inline comment as done.Apr 16 2020, 7:10 PM
aaronpuchert added inline comments.
clang/lib/Sema/TreeTransform.h
12161–12162

Or maybe default arguments need to instantiated immediately similar to the situation from DR1484? It says "Within a template declaration ...", which would seem to apply here. There is no enclosing scope though, so I'm not sure we can call this a local class.

So I guess we need to instantiate in more cases in SubstParmVarDecl, as you indicated.

aaronpuchert planned changes to this revision.Apr 16 2020, 7:23 PM
aaronpuchert added a comment.EditedApr 17 2020, 5:51 AM

Let's have a look at an example. Is this allowed?

template<typename T, int... args>
int x = [](T = T()){ return 0; }(args...);

template int x<int>; // Uses default argument int() = 0.

struct S { S(int); };
template int x<S, 0>; // Default argument not well-formed, but not needed.

Currently we don't allow it, and neither does GCC.

Remove loop in TreeTransform::TransformLambdaExpr and make sure Sema::SubstParmVarDecl handles the situation.

aaronpuchert retitled this revision from PR45000: Use Sema::SetParamDefaultArgument in TransformLambdaExpr to PR45000: Let Sema::SubstParmVarDecl handle default args of lambdas in initializers.Apr 19 2020, 3:01 PM
aaronpuchert edited the summary of this revision. (Show Details)
aaronpuchert marked 2 inline comments as done.Apr 19 2020, 3:04 PM

This seems to be in line with our current behavior, so I hope it's right:

struct S { S(int); };

template<typename T>
auto l = [](T x = T()) { return x; };

template<typename T>
struct Fun {
    T operator()(T x = T()) const { return x; }
};

void f() {
    l<int>();   // Ok.
    l<S>(S(0)); // Error at "T()": no matching constructor for initialization of 'S'
    Fun<S> f;
    f(S(0));    // Ok.
}

Adding @ahatanak and @sepavloff since I'm effectively reverting D23096 now.

aaronpuchert edited the summary of this revision. (Show Details)

Slightly adapt test results: there is an additional error now.

rsmith accepted this revision.Apr 21 2020, 7:24 PM

The responses to the various testcases you posted look OK to me. The language rules aren't clear, and we can revisit this if they get clarified in a different direction, but treating lambdas in variable templates the same as function definitions seems reasonable for now.

Adding @ahatanak and @sepavloff since I'm effectively reverting D23096 now.

This looks reasonable to me, but maybe ping @ahatanak (on email / IRC / wherever) to make sure this isn't breaking something we don't have a test for.

This revision is now accepted and ready to land.Apr 21 2020, 7:24 PM

The test I have passes after applying this patch.

This revision was automatically updated to reflect the committed changes.

This change is generating this crash discussed here: https://bugs.llvm.org/show_bug.cgi?id=49834
@aaronpuchert do you have any fix for it?

This change is generating this crash discussed here: https://bugs.llvm.org/show_bug.cgi?id=49834

Now migrated to https://github.com/llvm/llvm-project/issues/49178.

@aaronpuchert do you have any fix for it?

No, everything I know is in that bug report. I was hoping @rsmith would reply with an idea, but it's quite complicated.

Herald added a project: Restricted Project. · View Herald TranscriptMar 24 2022, 3:38 AM