[cfe-dev] local submodule visibility, namespaces and Decl::getOwningModule()

Adrian Prantl via cfe-dev cfe-dev at lists.llvm.org
Wed Oct 24 12:51:43 PDT 2018

> On Oct 23, 2018, at 6:17 PM, Richard Smith <richard at metafoo.co.uk> wrote:
> On Tue, 23 Oct 2018 at 17:29, Adrian Prantl via cfe-dev <cfe-dev at lists.llvm.org <mailto:cfe-dev at lists.llvm.org>> wrote:
>> On Oct 23, 2018, at 5:10 PM, Richard Smith <richard at metafoo.co.uk <mailto:richard at metafoo.co.uk>> wrote:
>> On Mon, 15 Oct 2018 at 10:13, Adrian Prantl via cfe-dev <cfe-dev at lists.llvm.org <mailto:cfe-dev at lists.llvm.org>> wrote:
>> I'm trying to fix a bug in the -gmodules debug info that appears when debugging Clang with local submodule visibility enabled. In my reduced testcase, I have a module A, defining a template "AlignedCharArray<unsigned, unsigned>", and two templates "SmallVector" and "Optional" that use it. In module B, I'm instantiating an Optional that instantiates an AlignedCharArray<4, 16>. In module C, I'm instantiating a SmallVector that also instantiates an AlignedCharArray<4, 16>.
>> The -gmodules debug info format saves space by forward-declaring types that are defined in an already-imported module and specify which module they came from. To point to the module of the definition CGDebugInfo::getParentModuleOrNull(const Decl *D) uses Decl::getOwningModule(). This mechanism works as expected, but only if the TemplateDecl is not inside a namespace.
>> When compiling the attached testcase with -U WITH_NAMESPACE, the owning module for the ClassTemplateSpecializationDecl (of AlignedCharArray<4, 16>) in module B is C. (B imports C, so the C specialization is instantiated first).
>> When compiling the attached testcase with -D WITH_NAMESPACE, the owning module for the ClassTemplateSpecializationDecl (of AlignedCharArray<4, 16>) in module B is A. (A is where the TemplateDecl that is being specialized comes from).
>> I'm sure that this discrepancy must be because a namespace can be spread out over many modules.
>> Sort of. In practice, what's happening is this:
>> When we create a declaration, we inherit its owning module from its lexical parent by default. And we ensure that the lexical parent of the TU declaration corresponds to the module we're currently "in". When a declaration is injected into a lexical declaration context from a different module, such as happens when adding a template specialization to an imported template, that default behavior will pick up the current module at global scope, but will pick up the owning module of the enclosing namespace declaration at namespace scope, which will be the owning module of whichever declaration of the template we picked. Now, this usually doesn't matter, because a template specialization doesn't really *have* a meaningful notion of "owning module". For visibility purposes, the specialization should be visible wherever the corresponding template is visible, and for linkage purposes, it should be considered owned by the same module as the template pattern. So in some sense the bug is in the global-scope case: we should always treat the specialization as being owned by module A.
>> I think there's a chance that you're asking the wrong question -- perhaps you should be trying to determine whether the declaration in question is imported from a module *file*, not what its semantic owning module is. (As an example, suppose we're tracking module ownership but not using PCM files. If we textually enter a modular header of some other module, I think you want to *not* treat declarations in that header as being owned by a module for the purpose of -gmodules, because there is no reason to think there's separate debug information for that header.) You can figure that out by calling Decl::isFromASTFile(), and if you want to know *which* AST file the declaration comes from (because you know that some AST files have debug information and some don't), you can use ASTReader::getOwningModuleFile to find out.
> Thanks for the pointer! That interface sounds promising; I will try to expose it as a
>    virtual ASTSourceDescriptor getOwningModuleFile(Decl *);
> in ExternalASTSource and see if that gets me further.
> A more general name might make sense at the ExternalASTSource level (eg, lldb might want to specify that its declarations come from a certain .dwo file), but that generally sounds reasonable to me.

I experimented with this quite a bit today, and using getOwningModuleFile() solves 50% of my problem. Not bad :-) 
Using it, I can now correctly deduce the Module that contains the previously imported definition of the ClassTemplateSpecialization, and emit debug info as a  forward decl.

But while emitting debug info for the PCM that contains the definition of ClassTemplateSpecializationDecl itself, getOwningModuleFile() errors out because Decl::isFromASTFile() is naturally false and the function only works for imported types; so the template specialization ends up nested in the module that contains the ClassTemplate, not the one that contains the specialization.

Is there a way to determine the module ClassTemplateSpecializationDecl is in while compiling that module? The ClassTemplateSpecializationDecl's DeclContext and LexicalDeclContext both are the NamespaceDecl in the module containing the ClassTemplate. I suppose that while compiling a PCM, I could force every Decl that !isFromASTFile() to be inside that PCM's module, but it would be nice to have something more elegant.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-dev/attachments/20181024/f76c1e24/attachment.html>

More information about the cfe-dev mailing list