This is an archive of the discontinued LLVM Phabricator instance.

[C++20][Modules][1/8] Track valid import state.
ClosedPublic

Authored by iains on Feb 3 2022, 4:55 AM.

Details

Summary

In C++20 modules imports must be together and at the start of the module.
Rather than growing more ad-hoc flags to test state, this keeps track of the
phase of of a valid module TU (first decl, global module frag, module,
private module frag). If the phasing is broken (with some diagnostic) the
pattern does not conform to a valid C++20 module, and we set the state
accordingly.

We can thus issue diagnostics when imports appear in the wrong places and
decouple the C++20 modules state from other module variants (modules-ts and
clang modules). Additionally, we attempt to diagnose wrong imports before
trying to find the module where possible (the latter will generally emit an
unhelpful diagnostic about the module not being available).

Although this generally simplifies the handling of C++20 module import
diagnostics, the motivation was that, in particular, it allows detecting
invalid imports like:

import module A;

int some_decl();

import module B;

where being in a module purview is insufficient to identify them.

Diff Detail

Event Timeline

iains created this revision.Feb 3 2022, 4:55 AM
iains updated this revision to Diff 408755.Feb 15 2022, 1:17 AM

Rebased onto other modules work.

iains updated this revision to Diff 408803.Feb 15 2022, 3:43 AM

repushing the rebase with clang-format available.

iains published this revision for review.Feb 15 2022, 8:26 AM

This is the first in a series of 8 patches to implement basic C++20 module partition support in clang. This is an enabler, particularly in forming diagnostics.

Herald added a project: Restricted Project. · View Herald TranscriptFeb 15 2022, 8:26 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript

Looks right to me -- I also implemented such a state machine in GCC's parser. There are a bunch of formatting issues to fix of course.

clang/include/clang/Basic/DiagnosticParseKinds.td
1543

"imports must immediately follow a module declaration"? (the contiguous isn't adding anything IMHO)

1544–1547

You could I suppose have a single diagnostuc and select between global & private?

iains added inline comments.Feb 15 2022, 12:03 PM
clang/include/clang/Basic/DiagnosticParseKinds.td
1543

um, maybe I need two diagnostics then - the "contiguous" aspect applies when imports are split by a non-import statement.

Selecting between the two case on the basis of a flag seems unproductive here. Would two diagnostics seem more reasonable to you?

1544–1547

good idea, I'll do that.

urnathan added inline comments.Feb 15 2022, 12:18 PM
clang/include/clang/Basic/DiagnosticParseKinds.td
1543

I don't follow. If the imports are split by a non-import, then the latter ones don't immediately follow the module declaration.

ChuanqiXu added inline comments.Feb 15 2022, 6:50 PM
clang/include/clang/Basic/DiagnosticParseKinds.td
1543

The imports could exist in non-module unit. A example could be found at: http://eel.is/c++draft/basic.lookup.general#example-1. So the wording might not be accurate. I think we could refer to the wording in the standard [[ http://eel.is/c++draft/module.import#1 | [module.import]p1 ]]:

In a module unit, all module-import-declarations and export-declarations exporting module-import-declarations shall appear before all other declarations in the declaration-seq of the translation-unit and of the private-module-fragment (if any).

If I don't misread this, I think it implies that imports could not be the first decl in non-module unit.

clang/test/Modules/cxx20-import-diagnostics-a.cpp
126

It should be allowed to import a module in non-module unit. For example:

import std;
int main() {
    std::cout << "Hello World.\n";
    return 0;
}

I guess we lack a test for this.

BTW, I guess it would be helpful to add the index in the series in the title. Like [C++20][Modules] Track valid import state (1/8).

iains updated this revision to Diff 409178.Feb 16 2022, 2:06 AM
iains marked 2 inline comments as not done.

Address review comments.

iains retitled this revision from [C++20][Modules] Track valid import state. to [C++20][Modules][1/8] Track valid import state..Feb 16 2022, 2:08 AM
iains marked 5 inline comments as done.

also reformatted.

urnathan accepted this revision.Feb 16 2022, 3:51 AM

thanks for indexing the subject lines!

This revision is now accepted and ready to land.Feb 16 2022, 3:51 AM
ChuanqiXu added inline comments.Feb 16 2022, 7:46 PM
clang/include/clang/Sema/Sema.h
2955–2960

I guess it is worth to add comment for these fields. Also I could guess the meaning, I think it is better to comment the semantics accurately.

clang/lib/Parse/ParseObjc.cpp
82

We could remove the IS variable

clang/lib/Parse/Parser.cpp
913

I think it is better to:

SingleDecl = ParseModuleImport(SourceLocation(), ModuleImportState::NotACXX20Module);
2466

Generally, we would add a TODO or FIXME for such comments.
(This is not need to be addressed if the following patch would be landed quickly)

clang/lib/Sema/SemaModule.cpp
144

I feel better to add an assertion here:

assert(SeenGMF == GlobalModuleFragment && "msg);
iains updated this revision to Diff 409521.Feb 17 2022, 1:01 AM

address review comments, rebase

iains marked 5 inline comments as done.Feb 17 2022, 1:07 AM
iains added inline comments.
clang/lib/Parse/ParseObjc.cpp
82

ParseModuleImport takes a reference so that it can update the the ModuleImportState if an invalid state is detected in parsing. So, although the state variable is a dummy here, we cannot remove the IS var.

clang/lib/Parse/Parser.cpp
913

see comment above, ParseModuleImport takes a reference.

2466

I hope that the main changes will be landed soon, but adding the TODO anyway.

clang/lib/Sema/SemaModule.cpp
144

we also have to check that we are in C++20 modules mode, since implicit global module fragments are allowed elsewhere - the test below already guards on C++20 modules.

I've made this change, although the assert seems quite heavyweight.

ChuanqiXu accepted this revision.Feb 17 2022, 1:11 AM

LGTM.

clang/lib/Sema/SemaModule.cpp
144

nit: Is the

(SeenGMF == (GlobalModuleFragment != nullptr))

not equal to:

SeenGMF == GlobalModuleFragment

? Or we could add a cast here:

SeenGMF == (bool) GlobalModuleFragment
iains marked 4 inline comments as done.Feb 17 2022, 1:18 AM
iains added inline comments.
clang/lib/Sema/SemaModule.cpp
144

nit: Is the

SeenGMF == GlobalModuleFragment

clang complains of pointer / integer comparison.
I guess we could use the cast.

iains updated this revision to Diff 409546.Feb 17 2022, 1:54 AM

amended an assert.

This revision was automatically updated to reflect the committed changes.