This is an archive of the discontinued LLVM Phabricator instance.

[Sema] Implement DR2233
AbandonedPublic

Authored by tambre on May 12 2020, 12:49 PM.

Details

Reviewers
rsmith
rjmccall
Summary

When a parameter pack is expanded the expanded parameters don't have default values.
Thus if a parameter pack is expanded after any arguments with a default value, the function is incorrectly rejected in CheckCXXDefaultArguments().

Fix this by removing default arguments for parameters before the last non-empty parameter pack.
By doing this we implement DR2233, which supersedes DR777.

Fixes PR23029.

Diff Detail

Event Timeline

tambre created this revision.May 12 2020, 12:49 PM
Herald added a project: Restricted Project. · View Herald TranscriptMay 12 2020, 12:49 PM
Herald added a subscriber: cfe-commits. · View Herald Transcript
tambre updated this revision to Diff 263759.May 13 2020, 9:47 AM

Simplify code, improve comments.

Quuxplusone added inline comments.
clang/test/CXX/drs/dr7xx.cpp
221–222

Is this even supposed to compile? The only valid specializations of f require T... to be an empty pack, which violates temp.res/8.3.

The comment mentions DR777, but DR777 doesn't explain the circumstances under which its wording change matters. It seems only to apply to templates that are already ill-formed by temp.res/8.3.

I wrote this comment but apparently never submitted it on Phabricator, sorry.

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
1987

I don't think this simple comparison works; the relationship can be complicated because there might be multiple packs in play. By the same token, I think it's possible that there can be multiple interleavings of defaultable parameters with packs. I think you need to scan backwards looking for a parameter without a default argument that was instantiated from a pack expansion and then remove any earlier parameters with defaults.

That is, assuming this is a reasonable implementation approach at all compared to just teaching other parts of the compiler about this case, which I think Richard needs to weigh in on.

rjmccall added inline comments.May 21 2020, 10:54 PM
clang/test/CXX/drs/dr7xx.cpp
221–222

Yeah, Richard made this point in DR2233, and the wording was weakened to allow it, in a way that essentially makes the earlier default arguments dead.

clang/test/CXX/drs/dr7xx.cpp
221–222

Huh. Ick. Indeed the other vendors seem to be implementing DR777/DR2233, so I guess Clang ought to catch up even if it's a silly direction to go in. :( I do see a small bit of implementation divergence in https://godbolt.org/z/ZMCvAX

template<class... Ts>
int f(int i=1, Ts... ts) { return (i + ... + ts); }

template<>
int f<int>(int i, int j) { return 42; }

GCC rejects as ill-formed. MSVC makes the specialization callable with 2 arguments only. EDG (ICC) makes the specialization callable with 0 or 2 arguments (and does crazy things when you call it with 0 arguments).

tambre updated this revision to Diff 265850.May 23 2020, 4:56 AM
tambre marked 5 inline comments as done.

Handle multiple parameter packs interleaved with default values.
Mark DR777 as superseded by DR2233. Mark DR2233 as resolved.
Moved tests from dr7xx.cpp to dr22xx.cpp. Added note in dr7xx.cpp about DR777 being superseded.
Add more tests that cover bugs observed in other implementations and the deficiency in my first implementation.

tambre updated this revision to Diff 265855.May 23 2020, 6:14 AM
tambre marked an inline comment as done.

Improve comment

tambre retitled this revision from [Sema] Remove default values for arguments prior to a parameter pack if the pack is used to [Sema] Implement DR2233.May 23 2020, 6:17 AM
tambre edited the summary of this revision. (Show Details)
tambre added a comment.EditedMay 23 2020, 6:20 AM

Thanks for the reviews!
I believe this now handles all cases and with this we'll be standards-conforming in regard to DR777 and DR2233.

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
1987

Good point, my current change doesn't work in the following case:

template<class... Ts, class... As>
void g(int i = 1, Ts... ts, int j = 3, As... as)
{
}

void testg()
{
	g<int>(2, 3, 4, 5);
}

Fixed and added that as a test case.

I think this approach is fine, as keeping info for each parameter for how they were expanded doesn't seem reasonable. Cases such as this ought to be checked and handled early during the actual template instantiation not during later checking of the instantiated function declaration.

clang/test/CXX/drs/dr7xx.cpp
221–222

MSVC seems to be the most standard-conforming in this regard. And now with the revised patch Clang is on par. :)

GCC bug
IntelliSense bug

I also submitted bug reports to ICC, but those seem to need moderator approval before they're public.

A couple of minor requests. Otherwise this looks good to me, although I really would like @rsmith to sign off on this approach, especially given his involvement with the DRs.

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
1985

There should be a space in "in between", or just use "between".

I don't think the line about "if a parameter pack is expanded..." is true as written. What you're trying to say is something like "It's okay to remove earlier default arguments because the expanded parameters are required to have matching arguments, which they only can if all the earlier parameters have arguments as well."

Unfortunately, I think this approach basically can't work, because we need to consider inheriting default arguments from a previous (or subsequent!) declaration before dropping default arguments preceding a pack. I think we will instead need to detect this situation when issuing the diagnostic for a parameter with a default argument followed by a parameter without one, and will need to make sure that all the parts of Clang that look at default arguments can cope with them being discontiguous.

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
1978

This code is only reached when substituting into non-member functions, so this change would not address the corresponding problem for member function templates or for member functions of class templates. It would be better to handle this in InitFunctionInstantiation, which is called for all function instantiations. (But as noted below, doing this during instantiation doesn't seem to be correct.)

In any case, we should have some testcases for member functions.

2090

We need to do this lookup and the merging below in CheckFunctionDeclaration before we drop any default arguments. Consider this pathological-but-valid testcase (that Clang currently accepts and the other compilers incorrectly reject):

template<typename ...T> void f() {
    void g(int, int = 0);
    void g(int = 0, T...);
    g();
}
void h() { f<int>(); }

Here, we only find out that the second g redeclares the first one during instantiation, and only after that do we inherit the =0 for the second parameter of the first g onto the second parameter of the second g.

And that's not even the worst testcase of this kind. Consider:

template<typename ...T> void f() {
    void g(int = 0, T...);
    void g(int, int = 0);
    g();
}
void h() { f<int>(); }

This is valid under DR2233. The first declaration of g provides a default argument for the first parameter, the second provides a default argument for the second parameter, and the call to g() is valid because both parameters have default arguments. So it is not correct to discard the default arguments when instantiating the first declaration of g.

tambre abandoned this revision.May 30 2020, 2:49 AM

Unfortunately, I think this approach basically can't work, because we need to consider inheriting default arguments from a previous (or subsequent!) declaration before dropping default arguments preceding a pack. I think we will instead need to detect this situation when issuing the diagnostic for a parameter with a default argument followed by a parameter without one, and will need to make sure that all the parts of Clang that look at default arguments can cope with them being discontiguous.

Thanks for reviewing!
Abandoning, as I lack the motivation to implement the proper solution you laid out.