Add support for importing C++ namespaces (#5103)

This adds support for having different C++ `NameScope`s (and not just
the main `Cpp` scope), and we keep a pointer to `clang::DeclContext`
these scopes so we can look up C++ names in the right part of the AST.

C++ Interop Demo with a namespace:

```c++
// hello_world.h

namespace some_namespace {

void hello_world();

}  // namespace some_namespace
```

```c++
// hello_world.cpp

#include <cstdio>

namespace some_namespace {

void hello_world() { printf("Hello World!\n"); }

}  // namespace some_namespace
```

```carbon
// main.carbon

library "Main";

import Cpp library "hello_world.h";

fn Run() -> i32 {
  Cpp.some_namespace.hello_world();
  return 0;
}
```

```shell
$ clang -c hello_world.cpp
$ bazel-bin/toolchain/carbon compile main.carbon
$ bazel-bin/toolchain/carbon link hello_world.o main.o --output=demo
$ ./demo
Hello World!
```

Closes #5102
This commit is contained in:
Boaz Brickner
2025-03-15 20:41:48 +00:00
committed by GitHub
parent fdedbb037d
commit fcd38a4d7f
15 changed files with 585 additions and 88 deletions
+32 -10
View File
@@ -22,6 +22,7 @@
#include "toolchain/check/type.h"
#include "toolchain/diagnostics/diagnostic.h"
#include "toolchain/diagnostics/format_providers.h"
#include "toolchain/parse/node_ids.h"
#include "toolchain/sem_ir/ids.h"
#include "toolchain/sem_ir/name_scope.h"
@@ -102,7 +103,7 @@ static auto AddNamespace(Context& context, PackageNameId cpp_package_id,
{.node_id = import.node_id, .library_id = import.library_id});
}
return AddImportNamespace(
return AddImportNamespaceToScope(
context,
GetSingletonType(context, SemIR::NamespaceType::SingletonInstId),
SemIR::NameId::ForPackageName(cpp_package_id),
@@ -112,7 +113,7 @@ static auto AddNamespace(Context& context, PackageNameId cpp_package_id,
return AddInst<SemIR::ImportCppDecl>(
context, imports.front().node_id, {});
})
.name_scope_id;
.add_result.name_scope_id;
}
auto ImportCppFiles(Context& context, llvm::StringRef importing_file_path,
@@ -136,7 +137,8 @@ auto ImportCppFiles(Context& context, llvm::StringRef importing_file_path,
auto name_scope_id = AddNamespace(context, package_id, imports);
SemIR::NameScope& name_scope = context.name_scopes().Get(name_scope_id);
name_scope.set_is_closed_import(true);
name_scope.set_is_cpp_scope(true);
name_scope.set_cpp_decl_context(
generated_ast->getASTContext().getTranslationUnitDecl());
context.sem_ir().set_cpp_ast(generated_ast.get());
@@ -147,10 +149,10 @@ auto ImportCppFiles(Context& context, llvm::StringRef importing_file_path,
return std::move(generated_ast);
}
// Look ups the given name in the Clang AST. Returns the lookup result if lookup
// was successful.
// Look ups the given name in the Clang AST in a specific scope. Returns the
// lookup result if lookup was successful.
static auto ClangLookup(Context& context, SemIR::LocId loc_id,
SemIR::NameId name_id)
SemIR::NameScopeId scope_id, SemIR::NameId name_id)
-> std::optional<clang::LookupResult> {
std::optional<llvm::StringRef> name =
context.names().GetAsStringIfIdentifier(name_id);
@@ -172,7 +174,7 @@ static auto ClangLookup(Context& context, SemIR::LocId loc_id,
clang::Sema::LookupNameKind::LookupOrdinaryName);
bool found = sema.LookupQualifiedName(
lookup, ast->getASTContext().getTranslationUnitDecl());
lookup, context.name_scopes().Get(scope_id).cpp_decl_context());
if (lookup.isClassLookup()) {
// TODO: To support class lookup, also return the AccessKind for storage.
@@ -250,17 +252,37 @@ static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_id,
return decl_id;
}
// Imports a namespace declaration from Clang to Carbon. If successful, returns
// the new Carbon namespace declaration `InstId`.
static auto ImportNamespaceDecl(Context& context,
SemIR::NameScopeId parent_scope_id,
SemIR::NameId name_id,
clang::NamespaceDecl* clang_decl)
-> SemIR::InstId {
auto result = AddImportNamespace(
context, GetSingletonType(context, SemIR::NamespaceType::SingletonInstId),
name_id, parent_scope_id, /*import_id=*/SemIR::InstId::None);
context.name_scopes()
.Get(result.name_scope_id)
.set_cpp_decl_context(clang_decl);
return result.inst_id;
}
// Imports a declaration from Clang to Carbon. If successful, returns the
// instruction for the new Carbon declaration.
static auto ImportNameDecl(Context& context, SemIR::LocId loc_id,
SemIR::NameScopeId scope_id, SemIR::NameId name_id,
const clang::NamedDecl* clang_decl)
-> SemIR::InstId {
clang::NamedDecl* clang_decl) -> SemIR::InstId {
if (const auto* clang_function_decl =
clang::dyn_cast<clang::FunctionDecl>(clang_decl)) {
return ImportFunctionDecl(context, loc_id, scope_id, name_id,
clang_function_decl);
}
if (auto* clang_namespace_decl =
clang::dyn_cast<clang::NamespaceDecl>(clang_decl)) {
return ImportNamespaceDecl(context, scope_id, name_id,
clang_namespace_decl);
}
context.TODO(loc_id, llvm::formatv("Unsupported: Declaration type {0}",
clang_decl->getDeclKindName())
@@ -271,7 +293,7 @@ static auto ImportNameDecl(Context& context, SemIR::LocId loc_id,
auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id,
SemIR::NameScopeId scope_id, SemIR::NameId name_id)
-> SemIR::InstId {
auto lookup = ClangLookup(context, loc_id, name_id);
auto lookup = ClangLookup(context, loc_id, scope_id, name_id);
if (!lookup) {
return SemIR::InstId::None;
}