mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-11-10 12:28:25 +00:00
Fix Carbon:: name leakage across files. (#7973)
Split the `Carbon::` namespace into one semantic namespace tree, plus one separate lookup-only namespace tree per Carbon file. Use the semantic namespaces as the parents of entities, but use the lookup-only namespaces when C++ name lookup names a Carbon namespace, in order to use a different C++ name lookup table in each Carbon file. Assisted-by: Gemini via Antigravity
This commit is contained in:
1 parent
0d3b606a56
commit
d08805f1a6
10 files changed
+299
-79
No files matched your search
@@ -94,6 +94,9 @@ auto CheckUnit::Run() -> void {
|
||||
|
||||
if (!ProcessNodeIds()) {
|
||||
context_.sem_ir().set_has_errors(true);
|
||||
|
||||
// TODO: Are there more steps from `FinishRun` we should be doing here?
|
||||
FinishAst(context_);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,15 +9,18 @@
|
||||
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "common/check.h"
|
||||
#include "common/map.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "toolchain/check/cpp/diagnostic_listener.h"
|
||||
#include "toolchain/sem_ir/cpp_domain.h"
|
||||
#include "toolchain/sem_ir/ids.h"
|
||||
|
||||
namespace clang {
|
||||
class ASTContext;
|
||||
class CompilerInstance;
|
||||
class FunctionDecl;
|
||||
class MangleContext;
|
||||
class NamespaceDecl;
|
||||
class Parser;
|
||||
class Sema;
|
||||
} // namespace clang
|
||||
@@ -48,6 +51,17 @@ class CppContext {
|
||||
return carbon_file_locations_;
|
||||
}
|
||||
|
||||
auto carbon_lookup_namespace() const -> clang::NamespaceDecl* {
|
||||
return carbon_lookup_namespace_;
|
||||
}
|
||||
void set_carbon_lookup_namespace(clang::NamespaceDecl* decl) {
|
||||
carbon_lookup_namespace_ = decl;
|
||||
}
|
||||
|
||||
auto lookup_namespaces() -> Map<SemIR::NameScopeId, clang::NamespaceDecl*>& {
|
||||
return lookup_namespaces_;
|
||||
}
|
||||
|
||||
auto placement_new_decl() const -> clang::FunctionDecl* {
|
||||
return placement_new_decl_;
|
||||
}
|
||||
@@ -59,6 +73,12 @@ class CppContext {
|
||||
// The C++ compilation domain.
|
||||
SemIR::CppDomain* domain_;
|
||||
|
||||
// The per-file lookup-only `::Carbon` namespace and child namespaces.
|
||||
// These are only used for C++ name lookup within this Carbon file, and are
|
||||
// never used as the parent `DeclContext` for exported Carbon entities.
|
||||
clang::NamespaceDecl* carbon_lookup_namespace_ = nullptr;
|
||||
Map<SemIR::NameScopeId, clang::NamespaceDecl*> lookup_namespaces_;
|
||||
|
||||
// TODO: All of the below state that is not specific to a particular
|
||||
// Check::Context or SemIR::File should be moved into CppDomain.
|
||||
|
||||
|
||||
@@ -91,25 +91,105 @@ static auto ExportClassToCppInDeclContext(Context& context,
|
||||
return record_decl;
|
||||
}
|
||||
|
||||
auto ExportPackageNamespaceToCpp(Context& context,
|
||||
clang::DeclContext* carbon_namespace,
|
||||
clang::IdentifierInfo* package_identifier)
|
||||
auto CreateSemanticNamespace(Context& context,
|
||||
clang::DeclContext* parent_context,
|
||||
clang::IdentifierInfo* identifier)
|
||||
-> clang::NamespaceDecl* {
|
||||
auto& package_namespaces =
|
||||
context.cpp_context()->domain().package_namespaces();
|
||||
auto [it, inserted] =
|
||||
package_namespaces.try_emplace(package_identifier->getName(), nullptr);
|
||||
if (inserted) {
|
||||
// TODO: Provide a source location.
|
||||
auto* namespace_decl = clang::NamespaceDecl::Create(
|
||||
context.ast_context(), parent_context, /*Inline=*/false,
|
||||
clang::SourceLocation(), clang::SourceLocation(), identifier,
|
||||
/*PrevDecl=*/nullptr, /*Nested=*/false);
|
||||
// This declaration should never be made visible to name lookups. Clear its
|
||||
// set of associated identifier namespaces, which indicates that it should not
|
||||
// be found by any kind of lookup.
|
||||
namespace_decl->clearIdentifierNamespace();
|
||||
parent_context->addHiddenDecl(namespace_decl);
|
||||
return namespace_decl;
|
||||
}
|
||||
|
||||
auto ExportNamespaceToCpp(Context& context, clang::DeclContext* parent_context,
|
||||
clang::IdentifierInfo* identifier)
|
||||
-> clang::NamespaceDecl* {
|
||||
auto& namespaces = context.cpp_context()->domain().semantic_namespaces();
|
||||
auto result = namespaces.Insert(std::pair{parent_context, identifier}, [&] {
|
||||
return CreateSemanticNamespace(context, parent_context, identifier);
|
||||
});
|
||||
return result.value();
|
||||
}
|
||||
|
||||
auto ExportLookupNamespaceToCpp(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::NameScopeId name_scope_id)
|
||||
-> clang::DeclContext* {
|
||||
auto& lookup_namespaces = context.cpp_context()->lookup_namespaces();
|
||||
llvm::SmallVector<SemIR::NameScopeId> name_scope_ids_to_create;
|
||||
|
||||
// Collect all the enclosing Carbon namespaces that don't have a corresponding
|
||||
// C++ namespace.
|
||||
clang::DeclContext* decl_context = nullptr;
|
||||
while (true) {
|
||||
if (auto lookup = lookup_namespaces.Lookup(name_scope_id)) {
|
||||
decl_context = lookup.value();
|
||||
break;
|
||||
}
|
||||
|
||||
const auto& name_scope = context.name_scopes().Get(name_scope_id);
|
||||
if (name_scope.is_cpp_scope()) {
|
||||
decl_context = GetClangDeclContextForScope(context, name_scope_id);
|
||||
CARBON_CHECK(decl_context);
|
||||
break;
|
||||
}
|
||||
|
||||
CARBON_CHECK(context.name_scopes().InstIs<SemIR::Namespace>(name_scope_id),
|
||||
"Parent of namespace is non-namespace {0}",
|
||||
context.name_scopes().GetInstIfValid(name_scope_id).second);
|
||||
|
||||
name_scope_ids_to_create.push_back(name_scope_id);
|
||||
name_scope_id = name_scope.parent_scope_id();
|
||||
CARBON_CHECK(
|
||||
name_scope_id.has_value(),
|
||||
"Reached the top level without finding a scope mapped into C++");
|
||||
}
|
||||
|
||||
// Build each new lookup namespace, from the outermost one inwards. The
|
||||
// enclosing namespaces aren't necessary semantically, but they allow Clang to
|
||||
// produce diagnostics that match the way we want the contents of namespace
|
||||
// `Carbon` to be named.
|
||||
for (auto name_scope_id : llvm::reverse(name_scope_ids_to_create)) {
|
||||
const auto& name_scope = context.name_scopes().Get(name_scope_id);
|
||||
|
||||
auto* identifier_info =
|
||||
GetClangIdentifierInfo(context, name_scope.name_id());
|
||||
if (!identifier_info) {
|
||||
// TODO: Handle keyword package names like `Cpp` and `Core`. These can
|
||||
// be named from C++ via an alias.
|
||||
context.TODO(loc_id, "interop with non-identifier package name");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// TODO: Provide a source location.
|
||||
auto* namespace_decl = clang::NamespaceDecl::Create(
|
||||
context.ast_context(), carbon_namespace, /*Inline=*/false,
|
||||
clang::SourceLocation(), clang::SourceLocation(), package_identifier,
|
||||
context.ast_context(), decl_context, /*Inline=*/false,
|
||||
clang::SourceLocation(), clang::SourceLocation(), identifier_info,
|
||||
/*PrevDecl=*/nullptr, /*Nested=*/false);
|
||||
carbon_namespace->addHiddenDecl(namespace_decl);
|
||||
it->second = namespace_decl;
|
||||
|
||||
// Add the declaration to the enclosing scope. We are providing lookup
|
||||
// results in that enclosing scope, so don't add it to Clang's name lookup
|
||||
// table.
|
||||
decl_context->addHiddenDecl(namespace_decl);
|
||||
|
||||
// Notify Clang that we are providing lookup results in the new namespace
|
||||
// too.
|
||||
namespace_decl->setHasExternalVisibleStorage();
|
||||
|
||||
auto key = SemIR::ClangDeclKey::ForNonFunctionDecl(namespace_decl);
|
||||
context.clang_decls().Add({.key = key, .inst_id = name_scope.inst_id()});
|
||||
lookup_namespaces.Insert(name_scope_id, namespace_decl);
|
||||
|
||||
decl_context = namespace_decl;
|
||||
}
|
||||
it->second->getPrimaryContext()->setHasExternalVisibleStorage();
|
||||
return it->second;
|
||||
|
||||
return decl_context;
|
||||
}
|
||||
|
||||
auto ExportNameScopeToCpp(Context& context, SemIR::LocId loc_id,
|
||||
@@ -136,9 +216,7 @@ auto ExportNameScopeToCpp(Context& context, SemIR::LocId loc_id,
|
||||
if (name_scope.is_imported_package()) {
|
||||
// Imported packages are exported as sub-namespaces of `::Carbon`, not of
|
||||
// the current package's sub-namespace.
|
||||
decl_context =
|
||||
GetClangDeclContextForScope(context, SemIR::NameScopeId::Package)
|
||||
->getParent();
|
||||
decl_context = context.cpp_context()->domain().carbon_namespace();
|
||||
break;
|
||||
}
|
||||
name_scope_id = name_scope.parent_scope_id();
|
||||
@@ -167,17 +245,8 @@ auto ExportNameScopeToCpp(Context& context, SemIR::LocId loc_id,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (name_scope.is_imported_package()) {
|
||||
decl_context =
|
||||
ExportPackageNamespaceToCpp(context, decl_context, identifier_info);
|
||||
} else {
|
||||
// TODO: Provide a source location.
|
||||
auto* namespace_decl = clang::NamespaceDecl::Create(
|
||||
context.ast_context(), decl_context, false, clang::SourceLocation(),
|
||||
clang::SourceLocation(), identifier_info, nullptr, false);
|
||||
decl_context->addHiddenDecl(namespace_decl);
|
||||
decl_context = namespace_decl;
|
||||
}
|
||||
decl_context =
|
||||
ExportNamespaceToCpp(context, decl_context, identifier_info);
|
||||
} else if (auto class_type =
|
||||
context.insts().TryGetAs<SemIR::ClassType>(const_inst_id)) {
|
||||
const auto& class_info = context.classes().Get(class_type->class_id);
|
||||
@@ -188,8 +257,6 @@ auto ExportNameScopeToCpp(Context& context, SemIR::LocId loc_id,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
decl_context->setHasExternalVisibleStorage();
|
||||
|
||||
auto key = SemIR::ClangDeclKey::ForNonFunctionDecl(
|
||||
cast<clang::Decl>(decl_context));
|
||||
auto clang_decl_id = context.clang_decls().Add(
|
||||
|
||||
@@ -17,13 +17,33 @@ class CXXRecordDecl;
|
||||
|
||||
namespace Carbon::Check {
|
||||
|
||||
// Exports a Carbon package into C++ as a sub-namespace of `carbon_namespace`,
|
||||
// or returns the existing namespace if already exported in this C++ domain.
|
||||
auto ExportPackageNamespaceToCpp(Context& context,
|
||||
clang::DeclContext* carbon_namespace,
|
||||
clang::IdentifierInfo* package_identifier)
|
||||
// Creates a namespace to use as a semantic parent for exported C++ entities.
|
||||
// This is used for the root `Carbon` namespace, the package namespaces within
|
||||
// it, and any nested namespaces within those packages. Does not register the
|
||||
// namespace as corresponding to any particular Carbon entity.
|
||||
auto CreateSemanticNamespace(Context& context,
|
||||
clang::DeclContext* parent_context,
|
||||
clang::IdentifierInfo* identifier)
|
||||
-> clang::NamespaceDecl*;
|
||||
|
||||
// Exports a Carbon namespace into C++ as a semantic sub-namespace of
|
||||
// `parent_context` (used as the parent `DeclContext` of exported Carbon
|
||||
// entities), or returns the existing namespace if already exported in this C++
|
||||
// domain.
|
||||
auto ExportNamespaceToCpp(Context& context, clang::DeclContext* parent_context,
|
||||
clang::IdentifierInfo* identifier)
|
||||
-> clang::NamespaceDecl*;
|
||||
|
||||
// Exports a Carbon namespace into C++ as a per-file lookup-only
|
||||
// `NamespaceDecl`, or returns the C++ declaration context if it was imported
|
||||
// from C++. Lookup-only namespaces are only used for C++ name lookup in the
|
||||
// current Carbon file, and are never the parent `DeclContext` of anything
|
||||
// other than child lookup-only namespaces, so that references to Carbon
|
||||
// entities are mangled consistently.
|
||||
auto ExportLookupNamespaceToCpp(Context& context, SemIR::LocId loc_id,
|
||||
SemIR::NameScopeId name_scope_id)
|
||||
-> clang::DeclContext*;
|
||||
|
||||
// Exports a Carbon name scope into C++ as a namespace or class, or returns the
|
||||
// C++ namespace or class declaration that it was imported from.
|
||||
//
|
||||
|
||||
@@ -214,8 +214,8 @@ auto CarbonExternalASTSource::MapInstIdToClangDeclOrType(LookupResult lookup)
|
||||
CARBON_KIND_SWITCH(target_inst) {
|
||||
case CARBON_KIND(SemIR::Namespace namespace_info): {
|
||||
auto* decl_context =
|
||||
ExportNameScopeToCpp(*context_, SemIR::LocId(target_inst_id),
|
||||
namespace_info.name_scope_id);
|
||||
ExportLookupNamespaceToCpp(*context_, SemIR::LocId(target_inst_id),
|
||||
namespace_info.name_scope_id);
|
||||
if (!decl_context) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -258,39 +258,28 @@ auto CarbonExternalASTSource::BuildCarbonNamespace() -> void {
|
||||
auto& ast_context = context_->ast_context();
|
||||
auto* identifier = &ast_context.Idents.get(carbon_namespace_name);
|
||||
|
||||
auto* decl_context = ast_context.getTranslationUnitDecl();
|
||||
auto* tu_decl = ast_context.getTranslationUnitDecl();
|
||||
|
||||
// Check if it already exists.
|
||||
clang::NamespaceDecl* carbon_cpp_namespace = nullptr;
|
||||
auto lookup_result = decl_context->lookup(identifier);
|
||||
if (!lookup_result.empty()) {
|
||||
carbon_cpp_namespace = cast<clang::NamespaceDecl>(lookup_result.front());
|
||||
} else {
|
||||
// Create it if it doesn't exist.
|
||||
carbon_cpp_namespace = clang::NamespaceDecl::Create(
|
||||
ast_context, decl_context, /*Inline=*/false, clang::SourceLocation(),
|
||||
clang::SourceLocation(), identifier, /*PrevDecl=*/nullptr,
|
||||
/*Nested=*/false);
|
||||
decl_context->addDecl(carbon_cpp_namespace);
|
||||
// Create or find the semantic `::Carbon` namespace for this C++ domain, which
|
||||
// is used as the parent `DeclContext` for package sub-namespaces.
|
||||
auto& domain = context_->cpp_context()->domain();
|
||||
auto* semantic_carbon_namespace = domain.carbon_namespace();
|
||||
if (!semantic_carbon_namespace) {
|
||||
semantic_carbon_namespace =
|
||||
CreateSemanticNamespace(*context_, tu_decl, identifier);
|
||||
domain.set_carbon_namespace(semantic_carbon_namespace);
|
||||
}
|
||||
|
||||
// We provide custom lookup results within this namespace.
|
||||
carbon_cpp_namespace->getPrimaryContext()->setHasExternalVisibleStorage();
|
||||
|
||||
// Create or find the sub-namespace for this file's package, which is used as
|
||||
// the parent `DeclContext` for package-scope entities so that the package
|
||||
// name is included in C++ mangled names.
|
||||
// Create or find the semantic sub-namespace for this file's package, which is
|
||||
// used as the parent `DeclContext` for package-scope entities so that the
|
||||
// package name is included in C++ mangled names.
|
||||
auto& package_scope =
|
||||
context_->name_scopes().Get(SemIR::NameScopeId::Package);
|
||||
auto package_name = context_->names().GetIRBaseName(package_scope.name_id());
|
||||
auto* package_identifier = &ast_context.Idents.get(package_name);
|
||||
auto* package_cpp_namespace = ExportPackageNamespaceToCpp(
|
||||
*context_, carbon_cpp_namespace, package_identifier);
|
||||
auto* package_cpp_namespace = ExportNamespaceToCpp(
|
||||
*context_, semantic_carbon_namespace, package_identifier);
|
||||
|
||||
// Register this file's package scope as corresponding to the package
|
||||
// sub-namespace in C++, and also map the `Carbon` namespace itself to the
|
||||
// package scope so that C++ lookups in `Carbon` find package-scope entities
|
||||
// directly (and do not find the current package name).
|
||||
auto package_key =
|
||||
SemIR::ClangDeclKey::ForNonFunctionDecl(package_cpp_namespace);
|
||||
auto package_clang_decl_id = context_->clang_decls().Add(
|
||||
@@ -298,8 +287,22 @@ auto CarbonExternalASTSource::BuildCarbonNamespace() -> void {
|
||||
package_scope.set_clang_decl_context_id(package_clang_decl_id,
|
||||
/*is_cpp_scope=*/false);
|
||||
|
||||
// Create a per-file lookup-only `::Carbon` namespace, mapped to the package
|
||||
// scope so that C++ lookups in `Carbon` find package-scope entities directly
|
||||
// (and do not find the current package name), and are isolated to this file.
|
||||
auto* lookup_carbon_namespace = clang::NamespaceDecl::Create(
|
||||
ast_context, tu_decl, /*Inline=*/false, clang::SourceLocation(),
|
||||
clang::SourceLocation(), identifier, /*PrevDecl=*/nullptr,
|
||||
/*Nested=*/false);
|
||||
tu_decl->addDecl(lookup_carbon_namespace);
|
||||
lookup_carbon_namespace->setHasExternalVisibleStorage();
|
||||
|
||||
context_->cpp_context()->set_carbon_lookup_namespace(lookup_carbon_namespace);
|
||||
context_->cpp_context()->lookup_namespaces().Insert(
|
||||
SemIR::NameScopeId::Package, lookup_carbon_namespace);
|
||||
|
||||
auto carbon_key =
|
||||
SemIR::ClangDeclKey::ForNonFunctionDecl(carbon_cpp_namespace);
|
||||
SemIR::ClangDeclKey::ForNonFunctionDecl(lookup_carbon_namespace);
|
||||
context_->clang_decls().Add(
|
||||
{.key = carbon_key, .inst_id = SemIR::Namespace::PackageInstId});
|
||||
}
|
||||
@@ -394,14 +397,30 @@ auto CarbonExternalASTSource::FindExternalVisibleDeclsByName(
|
||||
}
|
||||
|
||||
case CARBON_KIND(clang::QualType type): {
|
||||
// Create a typedef declaration to model the type result.
|
||||
// Create a typedef declaration to model the type result. We create the
|
||||
// typedef declaration in whatever name context the lookup was performed
|
||||
// in, since we don't have a Carbon declaration to take a name scope from.
|
||||
// But if that context was a namespace, map from the lookup-only namespace
|
||||
// to the corresponding semantic namespace so that lookup-only
|
||||
// NamespaceDecls are never the parent DeclContext of exported entities.
|
||||
// TODO: If the type is a tag type that was declared with this name in
|
||||
// this context, use the tag decl directly.
|
||||
auto* semantic_decl_context = decl_context;
|
||||
if (auto namespace_info = context_->insts().TryGetAs<SemIR::Namespace>(
|
||||
clang_decl->inst_id)) {
|
||||
semantic_decl_context = ExportNameScopeToCpp(
|
||||
*context_, SemIR::LocId(result.scope_result.target_inst_id()),
|
||||
namespace_info->name_scope_id);
|
||||
if (!semantic_decl_context) {
|
||||
SetNoExternalVisibleDeclsForName(decl_context, decl_name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
auto& ast_context = context_->ast_context();
|
||||
auto loc = GetCppLocation(
|
||||
*context_, SemIR::LocId(result.scope_result.target_inst_id()));
|
||||
auto* typedef_decl = clang::TypedefDecl::Create(
|
||||
ast_context, decl_context, loc, loc, identifier,
|
||||
ast_context, semantic_decl_context, loc, loc, identifier,
|
||||
ast_context.getTrivialTypeSourceInfo(type, loc));
|
||||
if (isa<clang::CXXRecordDecl>(decl_context)) {
|
||||
typedef_decl->setAccess(
|
||||
@@ -1097,6 +1116,21 @@ auto FinishAst(Context& context) -> void {
|
||||
*context.cpp_context()->sema().getDiagnostics().getClient());
|
||||
context.emitter().Flush();
|
||||
|
||||
// Hide the per-file lookup-only `::Carbon` namespace from the further lookup
|
||||
// results and remove it from the translation unit's name lookup table if it's
|
||||
// already there, so that subsequent files in a shared C++ AST domain do not
|
||||
// see it.
|
||||
if (auto* lookup_ns = context.cpp_context()->carbon_lookup_namespace()) {
|
||||
lookup_ns->clearIdentifierNamespace();
|
||||
auto* tu_decl = context.ast_context().getTranslationUnitDecl();
|
||||
if (auto* lookup = tu_decl->getLookupPtr()) {
|
||||
if (auto it = lookup->find(lookup_ns->getDeclName());
|
||||
it != lookup->end()) {
|
||||
it->second.remove(lookup_ns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the `CarbonExternalASTSource` installed in `GenerateAst` and
|
||||
// replace it with a `ReadOnlyASTSource`. This is necessary because
|
||||
// the source may be accessed later during lowering, but the
|
||||
|
||||
@@ -71,9 +71,20 @@ template <typename T> void Accept(T) {}
|
||||
''';
|
||||
|
||||
class SameName {}
|
||||
alias TypeAlias = i32;
|
||||
namespace N;
|
||||
class N.SameNested {}
|
||||
|
||||
fn TestPkgA(s: SameName) {
|
||||
inline Cpp '''
|
||||
void CheckPkgA(Carbon::SameName, Carbon::N::SameNested) {
|
||||
Carbon::TypeAlias x = 0;
|
||||
static_assert(sizeof(x) == 4);
|
||||
}
|
||||
''';
|
||||
|
||||
fn TestPkgA(s: SameName, n: N.SameNested) {
|
||||
Cpp.Accept(s);
|
||||
Cpp.CheckPkgA(s, n);
|
||||
}
|
||||
|
||||
// --- pkg_b.carbon
|
||||
@@ -83,9 +94,20 @@ template <typename T> void Accept(T) {}
|
||||
''';
|
||||
|
||||
class SameName {}
|
||||
alias TypeAlias = i64;
|
||||
namespace N;
|
||||
class N.SameNested {}
|
||||
|
||||
fn TestPkgB(s: SameName) {
|
||||
inline Cpp '''
|
||||
void CheckPkgB(Carbon::SameName, Carbon::N::SameNested) {
|
||||
Carbon::TypeAlias x = 0;
|
||||
static_assert(sizeof(x) == 8);
|
||||
}
|
||||
''';
|
||||
|
||||
fn TestPkgB(s: SameName, n: N.SameNested) {
|
||||
Cpp.Accept(s);
|
||||
Cpp.CheckPkgB(s, n);
|
||||
}
|
||||
|
||||
// --- use_both_packages.carbon
|
||||
@@ -93,8 +115,41 @@ library "[[@TEST_NAME]]";
|
||||
import PkgA;
|
||||
import PkgB;
|
||||
import Cpp inline '''
|
||||
// SameName in PkgA and PkgB have distinct C++ DeclContexts (Carbon::PkgA vs
|
||||
// Carbon::PkgB), so overloading on them in C++ succeeds.
|
||||
// SameName and N::SameNested in PkgA and PkgB have distinct C++ DeclContexts
|
||||
// (Carbon::PkgA vs Carbon::PkgB), so overloading on them in C++ succeeds.
|
||||
void OverloadByPackage(Carbon::PkgA::SameName*) {}
|
||||
void OverloadByPackage(Carbon::PkgB::SameName*) {}
|
||||
void OverloadNestedByPackage(Carbon::PkgA::N::SameNested*) {}
|
||||
void OverloadNestedByPackage(Carbon::PkgB::N::SameNested*) {}
|
||||
''';
|
||||
|
||||
// --- export_in_c.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
import Cpp;
|
||||
|
||||
class ExportedClass {}
|
||||
namespace N;
|
||||
class N.ExportedNested {}
|
||||
|
||||
inline Cpp '''
|
||||
using UseExported = Carbon::ExportedClass;
|
||||
using UseNested = Carbon::N::ExportedNested;
|
||||
''';
|
||||
|
||||
// --- fail_carbon_export_does_not_leak.carbon
|
||||
library "[[@TEST_NAME]]";
|
||||
import Cpp;
|
||||
|
||||
namespace N;
|
||||
|
||||
inline Cpp '''
|
||||
// CHECK:STDERR: fail_carbon_export_does_not_leak.carbon:[[@LINE+2]]:23: error: no type named 'ExportedClass' in namespace 'Carbon' [CppInteropParseError]
|
||||
// CHECK:STDERR:
|
||||
using Leak1 = Carbon::ExportedClass;
|
||||
// CHECK:STDERR: fail_carbon_export_does_not_leak.carbon:[[@LINE+2]]:26: error: no type named 'ExportedNested' in namespace 'Carbon::N' [CppInteropParseError]
|
||||
// CHECK:STDERR:
|
||||
using Leak2 = Carbon::N::ExportedNested;
|
||||
// CHECK:STDERR: fail_carbon_export_does_not_leak.carbon:[[@LINE+2]]:23: error: no member named 'PkgA' in namespace 'Carbon' [CppInteropParseError]
|
||||
// CHECK:STDERR:
|
||||
using Leak3 = Carbon::PkgA::SameName;
|
||||
''';
|
||||
@@ -13,7 +13,7 @@
|
||||
// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/function/export/thunk_ast.carbon
|
||||
// CHECK:STDOUT: TranslationUnitDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc>
|
||||
// CHECK:STDOUT: |-EmptyDecl {{0x[a-f0-9]+}} <<carbon Cpp imports>:1:1> col:1
|
||||
// CHECK:STDOUT: |-NamespaceDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> referenced Carbon external-linkage
|
||||
// CHECK:STDOUT: |-NamespaceDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> Carbon external-linkage
|
||||
// CHECK:STDOUT: | `-NamespaceDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> Main external-linkage
|
||||
|
||||
// --- thunk_with_args_and_return.carbon
|
||||
@@ -40,17 +40,18 @@ fn F(i: i32) -> i32 {
|
||||
// CHECK:STDOUT: | | `-DeclRefExpr {{0x[a-f0-9]+}} <col:21> 'int' lvalue Var {{0x[a-f0-9]+}} 'return_storage' 'int'
|
||||
// CHECK:STDOUT: | |-AlwaysInlineAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit always_inline
|
||||
// CHECK:STDOUT: | `-InternalLinkageAttr {{0x[a-f0-9]+}} <<invalid sloc>> Implicit
|
||||
// CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} <line:39:1, line:41:1> line:39:5 in thunk_with_args_and_return.carbon hidden G 'int (int)' external-linkage
|
||||
// CHECK:STDOUT: |-NamespaceDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> referenced Carbon external-linkage
|
||||
// CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} <line:40:1, line:42:1> line:40:5 in thunk_with_args_and_return.carbon hidden G 'int (int)' external-linkage
|
||||
// CHECK:STDOUT: | |-ParmVarDecl {{0x[a-f0-9]+}} <col:7, col:11> col:11 in thunk_with_args_and_return.carbon hidden used i 'int'
|
||||
// CHECK:STDOUT: | `-CompoundStmt {{0x[a-f0-9]+}} <col:14, line:41:1>
|
||||
// CHECK:STDOUT: | `-ReturnStmt {{0x[a-f0-9]+}} <line:40:3, col:21>
|
||||
// CHECK:STDOUT: | `-CompoundStmt {{0x[a-f0-9]+}} <col:14, line:42:1>
|
||||
// CHECK:STDOUT: | `-ReturnStmt {{0x[a-f0-9]+}} <line:41:3, col:21>
|
||||
// CHECK:STDOUT: | `-CallExpr {{0x[a-f0-9]+}} <col:10, col:21> 'int'
|
||||
// CHECK:STDOUT: | |-ImplicitCastExpr {{0x[a-f0-9]+}} <col:10, col:18> 'int (*)(int)' <FunctionToPointerDecay>
|
||||
// CHECK:STDOUT: | | `-DeclRefExpr {{0x[a-f0-9]+}} <col:10, col:18> 'int (int)' lvalue Function {{0x[a-f0-9]+}} 'F' 'int (int)'
|
||||
// CHECK:STDOUT: | | `-NestedNameSpecifier Namespace {{0x[a-f0-9]+}} 'Carbon'
|
||||
// CHECK:STDOUT: | `-ImplicitCastExpr {{0x[a-f0-9]+}} <col:20> 'int' <LValueToRValue>
|
||||
// CHECK:STDOUT: | `-DeclRefExpr {{0x[a-f0-9]+}} <col:20> 'int' lvalue ParmVar {{0x[a-f0-9]+}} 'i' 'int'
|
||||
// CHECK:STDOUT: `-ImportDecl {{0x[a-f0-9]+}} <line:42:4> col:4 in thunk_with_args_and_return.carbon implicit thunk_with_args_and_return.carbon
|
||||
// CHECK:STDOUT: `-ImportDecl {{0x[a-f0-9]+}} <line:43:4> col:4 in thunk_with_args_and_return.carbon implicit thunk_with_args_and_return.carbon
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
// CHECK:STDOUT: |-EmptyDecl {{0x[a-f0-9]+}} <<carbon Cpp imports>:1:1> col:1
|
||||
// CHECK:STDOUT: |-NamespaceDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> Carbon external-linkage
|
||||
// CHECK:STDOUT: | `-NamespaceDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> Main external-linkage
|
||||
// CHECK:STDOUT: |-NamespaceDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> Carbon external-linkage
|
||||
|
||||
// --- thunk_required.h
|
||||
|
||||
@@ -46,11 +47,12 @@ fn F() {
|
||||
Cpp.foo(1 as i16);
|
||||
}
|
||||
|
||||
// CHECK:STDOUT: `-ImportDecl {{0x[a-f0-9]+}} <import_thunk_required.carbon:[[@LINE+4]]:104> col:104 in import_thunk_required.carbon implicit import_thunk_required.carbon
|
||||
// CHECK:STDOUT: `-ImportDecl {{0x[a-f0-9]+}} <import_thunk_required.carbon:[[@LINE+5]]:104> col:104 in import_thunk_required.carbon implicit import_thunk_required.carbon
|
||||
// CHECK:STDOUT: TranslationUnitDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc>
|
||||
// CHECK:STDOUT: |-EmptyDecl {{0x[a-f0-9]+}} <<carbon Cpp imports>:1:1> col:1
|
||||
// CHECK:STDOUT: |-NamespaceDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> Carbon external-linkage
|
||||
// CHECK:STDOUT: | `-NamespaceDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> Main external-linkage
|
||||
// CHECK:STDOUT: |-NamespaceDecl {{0x[a-f0-9]+}} <<invalid sloc>> <invalid sloc> Carbon external-linkage
|
||||
// --- return_thunk_required.h
|
||||
|
||||
auto foo() -> short;
|
||||
|
||||
@@ -64,7 +64,7 @@ void TestLookupFailureInNested() {
|
||||
// second is a Carbon error for declaring a poisoned name. Normally we would
|
||||
// suppress the first error when we produce the second, but we don't do that
|
||||
// when the first error is produced by Clang.
|
||||
// CHECK:STDERR: fail_declare_after_poisoning_with_lookup_failure.carbon:[[@LINE+3]]:19-31: error: no member named 'LaterInNested' in namespace 'Carbon::Main::Nested' [CppInteropParseError]
|
||||
// CHECK:STDERR: fail_declare_after_poisoning_with_lookup_failure.carbon:[[@LINE+3]]:19-31: error: no member named 'LaterInNested' in namespace 'Carbon::Nested' [CppInteropParseError]
|
||||
// CHECK:STDERR:
|
||||
// CHECK:STDERR: fail_declare_after_poisoning_with_lookup_failure.carbon:[[@LINE+1]]:19: error: name `LaterInNested` used before it was declared [NameUseBeforeDecl]
|
||||
Carbon::Nested::LaterInNested();
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#define CARBON_TOOLCHAIN_SEM_IR_CPP_DOMAIN_H_
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "common/check.h"
|
||||
#include "common/map.h"
|
||||
@@ -16,6 +17,8 @@
|
||||
namespace clang {
|
||||
class CodeGenerator;
|
||||
class CompilerInstance;
|
||||
class DeclContext;
|
||||
class IdentifierInfo;
|
||||
class Module;
|
||||
class NamespaceDecl;
|
||||
class Parser;
|
||||
@@ -42,6 +45,14 @@ struct CppInputFile {
|
||||
// parse more code into that domain. May be shared across multiple Carbon files.
|
||||
class CppDomain {
|
||||
public:
|
||||
// A map from parent context and namespace name to semantic namespace, for
|
||||
// cross-file deduplication of exported packages and namespaces. We use the
|
||||
// same semantic C++ namespace for the same Carbon package and namespace path
|
||||
// in every file.
|
||||
using SemanticNamespacesMap =
|
||||
Map<std::pair<clang::DeclContext*, clang::IdentifierInfo*>,
|
||||
clang::NamespaceDecl*>;
|
||||
|
||||
explicit CppDomain(std::shared_ptr<clang::CompilerInstance> clang_instance,
|
||||
std::unique_ptr<clang::Parser> parser,
|
||||
llvm::ArrayRef<CppInputFile> inputs,
|
||||
@@ -68,8 +79,14 @@ class CppDomain {
|
||||
auto header_modules() -> llvm::StringMap<clang::Module*>& {
|
||||
return header_modules_;
|
||||
}
|
||||
auto package_namespaces() -> llvm::StringMap<clang::NamespaceDecl*>& {
|
||||
return package_namespaces_;
|
||||
auto carbon_namespace() const -> clang::NamespaceDecl* {
|
||||
return carbon_namespace_;
|
||||
}
|
||||
auto set_carbon_namespace(clang::NamespaceDecl* carbon_namespace) -> void {
|
||||
carbon_namespace_ = carbon_namespace;
|
||||
}
|
||||
auto semantic_namespaces() -> SemanticNamespacesMap& {
|
||||
return semantic_namespaces_;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -78,7 +95,8 @@ class CppDomain {
|
||||
Map<CheckIRId, clang::CodeGenerator*> code_generators_;
|
||||
Map<CheckIRId, clang::Module*> modules_;
|
||||
llvm::StringMap<clang::Module*> header_modules_;
|
||||
llvm::StringMap<clang::NamespaceDecl*> package_namespaces_;
|
||||
clang::NamespaceDecl* carbon_namespace_ = nullptr;
|
||||
SemanticNamespacesMap semantic_namespaces_;
|
||||
llvm::LLVMContext* llvm_context_ = nullptr;
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user