Chuanqi Xu 7201cae106
[C++20] [Modules] Support module level lookup (#122887)
Close https://github.com/llvm/llvm-project/issues/90154

This patch is also an optimization to the lookup process to utilize the
information provided by `export` keyword.

Previously, in the lookup process, the `export` keyword only takes part
in the check part, it doesn't get involved in the lookup process. That
said, previously, in a name lookup for 'name', we would load all of
declarations with the name 'name' and check if these declarations are
valid or not. It works well. But it is inefficient since it may load
declarations that may not be wanted.

Note that this patch actually did a trick in the lookup process instead
of bring module information to DeclarationName or considering module
information when deciding if two declarations are the same. So it may
not be a surprise to me if there are missing cases. But it is not a
regression. It should be already the case. Issue reports are welcomed.

In this patch, I tried to split the big lookup table into a lookup table
as before and a module local lookup table, which takes a combination of
the ID of the DeclContext and hash value of the primary module name as
the key. And refactored `DeclContext::lookup()` method to take the
module information. So that a lookup in a DeclContext won't load
declarations that are local to **other** modules.

And also I think it is already beneficial to split the big lookup table
since it may reduce the conflicts during lookups in the hash table.

BTW, this patch introduced a **regression** for a reachability rule in
C++20 but it was false-negative. See
'clang/test/CXX/module/module.interface/p7.cpp' for details.

This patch is not expected to introduce any other
regressions for non-c++20-modules users since the module local lookup
table should be empty for them.

---

On the API side, this patch unfortunately add a maybe-confusing argument
`Module *NamedModule` to
`ExternalASTSource::FindExternalVisibleDeclsByName()`. People may think
we can get the information from the first argument `const DeclContext
*DC`. But sadly there are declarations (e.g., namespace) can appear in
multiple different modules as a single declaration. So we have to add
additional information to indicate this.
2025-01-15 15:15:35 +08:00

89 lines
2.6 KiB
C++

// RUN: rm -rf %t
// RUN: mkdir -p %t
// RUN: echo '#ifndef FOO_H' > %t/foo.h
// RUN: echo '#define FOO_H' >> %t/foo.h
// RUN: echo 'extern int in_header;' >> %t/foo.h
// RUN: echo '#endif' >> %t/foo.h
// RUN: %clang_cc1 -std=c++2a -I%t -emit-module-interface -DINTERFACE %s -o %t.pcm
// RUN: %clang_cc1 -std=c++2a -I%t -fmodule-file=A=%t.pcm -DIMPLEMENTATION %s -verify -fno-modules-error-recovery
// RUN: %clang_cc1 -std=c++2a -I%t -fmodule-file=A=%t.pcm %s -verify -fno-modules-error-recovery
#ifdef INTERFACE
module;
#include "foo.h"
// FIXME: The following need to be moved to a header file. The global module
// fragment is only permitted to contain preprocessor directives.
int global_module_fragment;
export module A;
export int exported;
int not_exported;
static int internal;
module :private;
int not_exported_private;
static int internal_private;
#else
#ifdef IMPLEMENTATION
module;
#endif
void test_early() {
in_header = 1; // expected-error {{use of undeclared identifier 'in_header'}}
// expected-note@* {{not visible}}
global_module_fragment = 1; // expected-error {{use of undeclared identifier 'global_module_fragment'}}
exported = 1; // expected-error {{use of undeclared identifier 'exported'}}
not_exported = 1; // expected-error {{use of undeclared identifier 'not_exported'}}
// FIXME: We need better diagnostic message for static variable.
internal = 1; // expected-error {{use of undeclared identifier 'internal'}}
not_exported_private = 1; // expected-error {{undeclared identifier}}
internal_private = 1; // expected-error {{undeclared identifier}}
}
#ifdef IMPLEMENTATION
module A;
#else
import A;
#endif
void test_late() {
in_header = 1; // expected-error {{missing '#include "foo.h"'; 'in_header' must be declared before it is used}}
// expected-note@* {{not visible}}
global_module_fragment = 1; // expected-error {{missing '#include'; 'global_module_fragment' must be declared before it is used}}
exported = 1;
not_exported = 1;
#ifndef IMPLEMENTATION
// expected-error@-2 {{use of undeclared identifier 'not_exported'; did you mean 'exported'?}}
// expected-note@p2.cpp:18 {{'exported' declared here}}
#endif
internal = 1;
#ifndef IMPLEMENTATION
// expected-error@-2 {{declaration of 'internal' must be imported from module 'A' before it is required}}
// expected-note@p2.cpp:20 {{declaration here is not visible}}
#endif
not_exported_private = 1;
#ifndef IMPLEMENTATION
// FIXME: should not be visible here
// expected-error@-3 {{undeclared identifier}}
#endif
internal_private = 1;
#ifndef IMPLEMENTATION
// FIXME: should not be visible here
// expected-error@-3 {{undeclared identifier}}
#endif
}
#endif