This is an archive of the discontinued LLVM Phabricator instance.

[C++20][Modules] Adjust handling of exports of namespaces and using-decls.
ClosedPublic

Authored by iains on Mar 21 2022, 2:21 AM.

Details

Summary

This adjusts the handling for:

export module M;

export namespace {};

export namespace N {};
export using namespace N;

In the first case, we were allowing empty anonymous namespaces
as part of an extension allowing empty top-level entities, but that seems
inappropriate in this case, since the linkage would be internal for the
anonymous namespace. We now report an error for this.

The second case was producing a warning diagnostic that this was
accepted as an extension - however the C++20 standard does allow this
as well-formed.

In the third case we keep the current practice that this is accepted with a
warning (as an extension). The C++20 standard says it's an error.

We also ensure that using decls are only applied to items with external linkage.

This adjusts error messages for exports involving redeclarations in modules to
be more specific about the reason that the decl has been rejected.

Diff Detail

Event Timeline

iains created this revision.Mar 21 2022, 2:21 AM
Herald added a project: Restricted Project. · View Herald TranscriptMar 21 2022, 2:21 AM
iains added a subscriber: Restricted Project.
iains published this revision for review.Mar 21 2022, 2:25 AM

this covers most of the tests from the standard section 10.3 (one to follow that has an additional dependency on header units)

note the change in the "cannot export redeclaration 'xxxx here since the previous declaration" errors to include a more specific reason than before.

Herald added a project: Restricted Project. · View Herald TranscriptMar 21 2022, 2:25 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript
ChuanqiXu added inline comments.Mar 21 2022, 7:21 PM
clang/include/clang/Basic/DiagnosticSemaKinds.td
7856–7858

I feel this change is not intended?

clang/lib/Sema/SemaDecl.cpp
1682–1689

It looks like that this change isn't reflected in the summary. Although this is not bad, I feel it is not good. From the perspective of the user, I feel the new message here is a little bit more confusing.

clang/lib/Sema/SemaModule.cpp
822–824

Unnecessary change?

830–831

nit: the general style in clang/llvm I see is to use (bool) conversion.

861–862

I think we should remove it. So that the above if could be further merged.

iains marked 2 inline comments as done.Mar 22 2022, 1:14 AM
iains added subscribers: vsapsai, dblaikie.

So the adjustment to the error message is something I am 50/50 about (IMO it makes some messages more useful, but maybe not needed in others).

Without the change we get
"cannot export redeclaration 'xxx' here since the previous declaration is not exported"

So, e.g in C++20 10.2 example 6. every case has the same error message (which was what prompted me to make the change).

With the change here we now get:
cannot export redeclaration 'f' here since the previous declaration has internal linkage
cannot export redeclaration 'S' here since the previous declaration has module linkage

which seems maybe to be more helpful to the user in telling them why.

I hope others can weigh in with an opinion here .. @dblaikie @vsapsai ?

clang/include/clang/Basic/DiagnosticSemaKinds.td
7856–7858

the change is intentional, but we should maybe decide if it is more or less helpful to the user.

clang/lib/Sema/SemaDecl.cpp
1682–1689

it is reflected in the summary:

"
This adjusts error messages for exports involving redeclarations in modules to
be more specific about the reason that the decl has been rejected.
"
Iet us discuss whether this is more/less useful outside of the code.

So the adjustment to the error message is something I am 50/50 about (IMO it makes some messages more useful, but maybe not needed in others).

Without the change we get
"cannot export redeclaration 'xxx' here since the previous declaration is not exported"

So, e.g in C++20 10.2 example 6. every case has the same error message (which was what prompted me to make the change).

With the change here we now get:
cannot export redeclaration 'f' here since the previous declaration has internal linkage
cannot export redeclaration 'S' here since the previous declaration has module linkage

which seems maybe to be more helpful to the user in telling them why.

I hope others can weigh in with an opinion here .. @dblaikie @vsapsai ?

I am OK to wait for opinions from others. Let me talk it a little bit more.

The first feeling I saw the change is that not every C++ programmer knows about linkage. OK, it depends on the environment really and every one might has their own opinion.

Another thought is that 10.2.6 (http://eel.is/c++draft/module.interface#6) doesn't talk anything about linkage:

A redeclaration of an entity X is implicitly exported if X was introduced by an exported declaration; otherwise it shall not be exported.

So it looks like confusing to talk about linkage this time. In my imagination, there might be a such situation:

A programmer met the error when he tries to export a redeclaration which is internal linkage (maybe a simple const variable). Then the message told him the internal linkage is not allowed to re-export. Then he removes the const specifier. Now he meets the error again. It tells that we couldn't export redeclaration which is module linkage. I guess he would feel bad. Then he might try to export the first declaration to get passed. However, the const specifier is lost in the case. And in the current message, I guess he would add export to the first declaration directly after he reads the message.

iains marked 2 inline comments as done.Mar 22 2022, 2:12 AM

So the adjustment to the error message is something I am 50/50 about (IMO it makes some messages more useful, but maybe not needed in others).

Without the change we get
"cannot export redeclaration 'xxx' here since the previous declaration is not exported"

So, e.g in C++20 10.2 example 6. every case has the same error message (which was what prompted me to make the change).

With the change here we now get:
cannot export redeclaration 'f' here since the previous declaration has internal linkage
cannot export redeclaration 'S' here since the previous declaration has module linkage

which seems maybe to be more helpful to the user in telling them why.

I hope others can weigh in with an opinion here .. @dblaikie @vsapsai ?

I am OK to wait for opinions from others. Let me talk it a little bit more.

The first feeling I saw the change is that not every C++ programmer knows about linkage. OK, it depends on the environment really and every one might has their own opinion.

Another thought is that 10.2.6 (http://eel.is/c++draft/module.interface#6) doesn't talk anything about linkage:

A redeclaration of an entity X is implicitly exported if X was introduced by an exported declaration; otherwise it shall not be exported.

So it looks like confusing to talk about linkage this time. In my imagination, there might be a such situation:

A programmer met the error when he tries to export a redeclaration which is internal linkage (maybe a simple const variable). Then the message told him the internal linkage is not allowed to re-export. Then he removes the const specifier. Now he meets the error again. It tells that we couldn't export redeclaration which is module linkage. I guess he would feel bad. Then he might try to export the first declaration to get passed. However, the const specifier is lost in the case. And in the current message, I guess he would add export to the first declaration directly after he reads the message.

yes, that seems like a reasonable counter argument, and perhaps talking about linkage in a user message is a bit "implementor speak"..
... as I said, I was kind of 50/50 about this - I'll wait 48h for any other comments and then remove the change if there are no votes in favour.

With the change here we now get:
cannot export redeclaration 'f' here since the previous declaration has internal linkage
cannot export redeclaration 'S' here since the previous declaration has module linkage

which seems maybe to be more helpful to the user in telling them why.

I hope others can weigh in with an opinion here .. @dblaikie @vsapsai ?

Yes, this is more helpful -- tell the user what the compiler thinks it is, rather than what it thinks it is not.

The first feeling I saw the change is that not every C++ programmer knows about linkage. OK, it depends on the environment really and every one might has their own opinion.

You may be confusing object-file linkage with the linkage concepts of C++, which are specified in [basic.link]? Sadly we have to live with the overloaded term.

Another thought is that 10.2.6 (http://eel.is/c++draft/module.interface#6) doesn't talk anything about linkage:

A redeclaration of an entity X is implicitly exported if X was introduced by an exported declaration; otherwise it shall not be exported.

So it looks like confusing to talk about linkage this time. In my imagination, there might be a such situation:

A programmer met the error when he tries to export a redeclaration which is internal linkage (maybe a simple const variable). Then the message told him the internal linkage is not allowed to re-export. Then he removes the const specifier. Now he meets the error again. It tells that we couldn't export redeclaration which is module linkage. I guess he would feel bad. Then he might try to export the first declaration to get passed. However, the const specifier is lost in the case. And in the current message, I guess he would add export to the first declaration directly after he reads the message.

further, with attachment, the original error 'cannot export as previous was not exported' is not correct in general. Consider:

module;
Pretend this is from #include, ok?
void Foo ();
module bob;
extern "C++" export void Foo ();
can export even though prior was not exported

SOrry, I don't have much context here - the more informative (module/internal linkage) diagnostic does seem better to me than saying "is not exported", even if it's a bit esoteric for some users. We do have other diagnostics that mention linkage, I'm sure (because it's necessary/useful to describe certain things).

Without much context on this patch: is the diagnostic change a necessary part of the patch? (is the diagnostic new regardless of which wording option is chosen? or is this affecting an existing diagnostic?) - if it's possible to separate the diagnostic change from the rest of the patch that's probably a good thing to do regardless of the choice. If not, yeah, I think going with the more explicit/nuanced diagnostic wording seems better to me at a glance.

The first feeling I saw the change is that not every C++ programmer knows about linkage. OK, it depends on the environment really and every one might has their own opinion.

You may be confusing object-file linkage with the linkage concepts of C++, which are specified in [basic.link]? Sadly we have to live with the overloaded term.

Another thought is that 10.2.6 (http://eel.is/c++draft/module.interface#6) doesn't talk anything about linkage:

A redeclaration of an entity X is implicitly exported if X was introduced by an exported declaration; otherwise it shall not be exported.

So it looks like confusing to talk about linkage this time. In my imagination, there might be a such situation:

A programmer met the error when he tries to export a redeclaration which is internal linkage (maybe a simple const variable). Then the message told him the internal linkage is not allowed to re-export. Then he removes the const specifier. Now he meets the error again. It tells that we couldn't export redeclaration which is module linkage. I guess he would feel bad. Then he might try to export the first declaration to get passed. However, the const specifier is lost in the case. And in the current message, I guess he would add export to the first declaration directly after he reads the message.

further, with attachment, the original error 'cannot export as previous was not exported' is not correct in general. Consider:

module;
Pretend this is from #include, ok?
void Foo ();
module bob;
extern "C++" export void Foo ();
can export even though prior was not exported

I think the example is invalid since it violates [[ http://eel.is/c++draft/module.interface#6 | [module.interface]p6 ]] explicitly. If this is intended behavior or by design, we should change the wording.

I think the example is invalid since it violates [[ http://eel.is/c++draft/module.interface#6 | [module.interface]p6 ]] explicitly. If this is intended behavior or by design, we should change the wording.

That wording failed to be updated when the linkage-decl changes went in. I'll add a note to DR 2541. Linkage specifications, module purview, and module attachment

ChuanqiXu accepted this revision.Mar 23 2022, 7:13 PM

I think the example is invalid since it violates [[ http://eel.is/c++draft/module.interface#6 | [module.interface]p6 ]] explicitly. If this is intended behavior or by design, we should change the wording.

That wording failed to be updated when the linkage-decl changes went in. I'll add a note to DR 2541. Linkage specifications, module purview, and module attachment

OK, if the wording is not consistent with the design, I have no further objections.

This revision is now accepted and ready to land.Mar 23 2022, 7:13 PM
iains added a comment.Mar 24 2022, 1:12 PM

SOrry, I don't have much context here - the more informative (module/internal linkage) diagnostic does seem better to me than saying "is not exported", even if it's a bit esoteric for some users. We do have other diagnostics that mention linkage, I'm sure (because it's necessary/useful to describe certain things).

Without much context on this patch: is the diagnostic change a necessary part of the patch? (is the diagnostic new regardless of which wording option is chosen? or is this affecting an existing diagnostic?) - if it's possible to separate the diagnostic change from the rest of the patch that's probably a good thing to do regardless of the choice. If not, yeah, I think going with the more explicit/nuanced diagnostic wording seems better to me at a glance.

The diagnostic change was made so that the test cases introduced by the patch could have (diagnostic) wording that reflected the examples in the standard. As such, it seemed reasonable to make the change at the same time. I can split it out of course if that still seems better given the clarification.

iains updated this revision to Diff 418038.Mar 24 2022, 2:02 PM
iains marked 3 inline comments as done.

rebased, addressed review comments.

clang/lib/Sema/SemaModule.cpp
861–862

we can remove the else ; but we cannot merge the !HasName into the if (that changes the logic of the expression such that empty & unnamed namespace decls get passed to checkExportedDeclContext()) - which emits a diagnostic not expected by the standard.

For me, the 'else ;' makes it clear what the alternative is doing (and there's a comment as well) - but if the general opinions is to remove it, that's also fine.

iains added a comment.EditedApr 6 2022, 3:09 AM

@dblaikie - is the explanation for the change in diagnostics at the same time OK? (if not, I am happy to split the patch, but either way I'd like to land what is acceptable soonish).

dblaikie accepted this revision.Apr 6 2022, 1:23 PM

@dblaikie - is the explanation for the change in diagnostics at the same time OK? (if not, I am happy to split the patch, but either way I'd like to land what is acceptable soonish).

I think it'll be OK as-is.

erichkeane added inline comments.
clang/lib/Sema/SemaModule.cpp
861–862

I'd much prefer we did NOT do the empty else/semicolon here. This ends up causing a warning with GCC ( warning: suggest braces around empty body in an ‘else’ statement [-Wempty-body] ) and is jsut strange. I don't mind a comment, but the else/; is strange.

iains marked an inline comment as done.Apr 11 2022, 3:13 AM
iains added inline comments.
clang/lib/Sema/SemaModule.cpp
861–862

I committed:
rG92fed06f800a: [C++20][Modules] Remove an empty statement [NFC].
to resolve this.