ChuanqiXu added inline comments.
================ Comment at: clang/include/clang/Basic/Module.h:547-548 + // <global> is the default name showed in module map. + if (isGlobalModule()) + return "<global>"; + ---------------- I thought to add an assertion here. But I feel like it is not so necessary and friendly. ================ Comment at: clang/include/clang/Basic/Module.h:537-543 + static StringRef getPrimaryModuleInterfaceName(StringRef Name) { + return Name.split(':').first; + } + /// Get the primary module interface name from a partition. StringRef getPrimaryModuleInterfaceName() const { + return getPrimaryModuleInterfaceName(getTopLevelModuleName()); ---------------- iains wrote: > ChuanqiXu wrote: > > iains wrote: > > > I do not understand the purpose of this change, we already iterated over > > > making the function more efficient. > > > > > > For C++20 modules there cannot be more than one parent - so that the > > > nested call to getTopLevelModule(), via getTopLevelModuleName() is > > > redundant. > > > > > > Is there some other case that needs special handling? > > > (If so then I think we should guard this at a higher level) > > > > > The primary purpose of the change is that we need to get the primary module > > name for getLangOpts().CurrentModule. So we need a static member function > > which takes a StringRef. > > > > Another point might be the correctness. For example, for the private module > > fragment, the current implementation would return the right result while > > the original wouldn't. (Although we didn't use private module fragment > > much...) > > > > For the most case, I admit the current implementation would call more > > function a little more. But I think it is negligible. > (Maybe I am worrying too much - but it does seem that we have quite some > complexity in an operation that we expect to repeat many times per TU). > > ---- > > > The primary purpose of the change is that we need to get the primary module > > name for getLangOpts().CurrentModule. So we need a static member function > > which takes a StringRef. > > That is, of course, fine (and we are stuck in the case that we are building > a partition, with the fact that the primary module is not available). It > seems that there's not a lot we can do there. > > When we have module, we could cache the result of the split, tho - so have a > "PrimaryModuleName" entry in each module and populate it on the first use. > We are still stuck with the string comparison - but at least we could avoid > repeating the split? > As I noted, the reason we did not bother to do this in the first use was that > that use was only once per TU). > > When we are building a consumer of a module, on the other hand, we *do* have > the complete module import graph. > > > Another point might be the correctness. For example, for the private module > > fragment, the current implementation would return the right result while > > the original wouldn't. (Although we didn't use private module fragment > > much...) > > Of course, we must fix correctness issues .. > Since there is only GMF and PMF, perhaps we could reasonably treat those > specially `if (isGlobalModule())` .. and/or `if (isPrivateModule())`? > > > For the most case, I admit the current implementation would call more > > function a little more. But I think it is negligible. > > It is hard to judge at the moment - perhaps we should carry on with what we > have and then instrument some reasonable body of code? > (Maybe I am worrying too much - but it does seem that we have quite some > complexity in an operation that we expect to repeat many times per TU). Never mind! This is the meaning of reviewing. > Of course, we must fix correctness issues .. Since there is only GMF and PMF, perhaps we could reasonably treat those specially if (isGlobalModule()) .. and/or if (isPrivateModule())? Done. After consideration, I think what you here makes sense. Especially now Module::getPrimaryModuleInterfaceName is only used for Sema.getLangOpts().CurrentModule. So it might be better to handle this in Sema or LangOptions. ================ Comment at: clang/lib/Sema/SemaLookup.cpp:1567-1568 + + return M->getPrimaryModuleInterfaceName() == + Module::getPrimaryModuleInterfaceName(LangOpts.CurrentModule); } ---------------- iains wrote: > ChuanqiXu wrote: > > iains wrote: > > > ... of course, "correctness before optimisation", but this test function > > > seems like it would benefit from some refactoring as suggested below. > > > > > > it seems a bit unfortunate that we are placing a string comparison in the > > > "acceptable decl" lookup path, which might be made many times (the > > > previous use of the string compare was in making the module decl - which > > > only happens once in a TU). > > > > > > * Sema has visibility of the import graph of modules (via > > > DirectModuleImports and Exports). > > > * We also know what the module parse state is (FirstDecl, GMF etc) > > > * If were are not parsing a module (i.e. LangOpts.CurrentModule.empty()) > > > the we could return false early? > > > * if ModuleScopes is empty we know we are not (yet) parsing a module > > > * Once we are parsing a module then we can test ModuleScopes.back() to > > > find the current module > > > > > > there are only two users of isInCurrentModule() - so maybe we refactor > > > could make more use of the higher level state information - and / or > > > cache information on the parents of partitions to avoid the complexity > > > of parsing strings each time. > > > > > > what do you think? > > Yeah, string comparison is relatively expensive and we should avoid that. I > > added a search cache here and I thought it should handle the most cases. > > > > For the solution you described, I am not sure if I understood correctly. It > > looks like a quick return path if we found we are not parsing a module? If > > yes, I think the current check could do similar works. > That looks good. > > On the actual test itself: > > 1/ > we have the check of `(M->isGlobalModule() && !M->Parent)` > > So that answers "this decl is part of the global module", since that fires > when we are parsing the GMF. What will we answer, > if `M->isGlobalModule() && M->Parent` ? > it seems that with the changes above we will now answer that the decl is > part of the Parent - not part of the GM? > > 2/ > When we are actually parsing the PMF we have a similar case, no? > (so that there is no Parent pointer set yet, and I think that means that we > would need to examine ModuleScopes to find the Parent module?) > > > 1/ > we have the check of (M->isGlobalModule() && !M->Parent) > > So that answers "this decl is part of the global module", since that fires > when we are parsing the GMF. What will we answer, > if M->isGlobalModule() && M->Parent ? > it seems that with the changes above we will now answer that the decl is part > of the Parent - not part of the GM? Good Catcha! Now in this revision, the function would return false for the case of `M->isGlobalModule() && M->Parent`. Another point I found is that the name of the function is not quite right. Since global module fragment belongs to global module, it wouldn't be within the current module. So I chose a new name `isUsableModule` for it from the wording of `[module.global.frag]p1`. I tried to call it `isVisibleModule` but I found there is already one... I feel `isUsableModule` is not perfectly good... Any suggestion? > 2/ > When we are actually parsing the PMF we have a similar case, no? There might not be a problem. Since PMF belongs to a named module technically. So the current implementation would handle it correctly. ================ Comment at: clang/test/Modules/cxx20-10-1-ex2.cpp:62-69 +// According to [module.import]p7: +// Additionally, when a module-import-declaration in a module unit of some +// module M imports another module unit U of M, it also imports all +// translation units imported by non-exported module-import-declarations in +// the module unit purview of U. +// +// So B is imported in B:X3 due to B:X2 imported B. So n is visible here. ---------------- iains wrote: > ChuanqiXu wrote: > > iains wrote: > > > @rsmith > > > > > > so, I wonder if this is intended, it seems to defeat the mechanisms to > > > avoid circular dependencies. > > > > > > TU1: > > > module M : Impl; // this is said not to generate a circular dependency > > > because M : Impl does not implicitly import M > > > > > > import M; // but now we've done it manually ... and if that module is > > > implicitly exported to importers of the Impl. module interface.... > > > .... > > > > > > TU2: > > > > > > export module M; > > > import : Impl; > > > > > > // ... then boom! we have a circular dep. > > > > > > > > @rsmith should be the right one to answer this. > > > > Personally, I feel the case you described should be avoided. And I found it > > is hard to right cyclic module dependent code in current fashion. Since > > when I compile TU1, the compiler suggests that it failed to found M. And > > when I compile TU2, the compiler suggests that it failed to found M:impl... > > So it looks like we could avoid worrying this now. Maybe the build system > > could give better diagnostic message in the future. I think this might not > > be a big problem. > Right, one cannot actually build the faulty module (actually, in the same way > as one cannot build example 3 from section 10.3 of the std). > > So probably my concern is unfounded - it's just something the user will have > to figure out - we are not required to diagnose the situation (and that would > be hard to do for the compiler anyway). > Agreed. This should be the work for build systems. CHANGES SINCE LAST ACTION https://reviews.llvm.org/D123837/new/ https://reviews.llvm.org/D123837 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits