From a1e843718e1f9cf9b886d23130034f5620a05db8 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 6 Aug 2026 06:32:37 -0700 Subject: [PATCH] Create modules for header imports. (#7613) Make multiple imports of the same header only parse it once per C++ domain. Reuse of the same header in `--share-cpp-ast` mode now reuses the representation. Importing a Carbon file with C++ dependencies now makes those transitive C++ dependencies in the same C++ domain visible too. Assisted-by: Gemini via Antigravity --- toolchain/check/BUILD | 2 - toolchain/check/check.cpp | 6 +- toolchain/check/check.h | 2 - toolchain/check/check_unit.cpp | 2 + toolchain/check/check_unit.h | 7 +- toolchain/check/cpp/context.cpp | 3 +- toolchain/check/cpp/context.h | 10 +- toolchain/check/cpp/generate_ast.cpp | 238 ++++++++++++------ toolchain/check/cpp/generate_ast.h | 12 +- toolchain/check/cpp/import.cpp | 20 +- toolchain/check/cpp/import.h | 8 +- .../cpp/basics/import/bad_import.carbon | 16 +- .../interop/cpp/basics/import/indirect.carbon | 73 ++++-- .../cpp/function/import/thunk_ast.carbon | 36 +-- toolchain/sem_ir/BUILD | 10 +- .../cpp/domain.cpp => sem_ir/cpp_domain.cpp} | 6 +- .../cpp/domain.h => sem_ir/cpp_domain.h} | 26 +- toolchain/sem_ir/cpp_file.cpp | 5 +- toolchain/sem_ir/cpp_file.h | 6 +- 19 files changed, 324 insertions(+), 164 deletions(-) rename toolchain/{check/cpp/domain.cpp => sem_ir/cpp_domain.cpp} (91%) rename toolchain/{check/cpp/domain.h => sem_ir/cpp_domain.h} (75%) diff --git a/toolchain/check/BUILD b/toolchain/check/BUILD index 160e16cf7ac7..8c8c88af7070 100644 --- a/toolchain/check/BUILD +++ b/toolchain/check/BUILD @@ -27,7 +27,6 @@ cc_library( "cpp/context.cpp", "cpp/custom_type_mapping.cpp", "cpp/diagnostic_consumer.cpp", - "cpp/domain.cpp", "cpp/export.cpp", "cpp/generate_ast.cpp", "cpp/impl_lookup.cpp", @@ -94,7 +93,6 @@ cc_library( "cpp/custom_type_mapping.h", "cpp/diagnostic_consumer.h", "cpp/diagnostic_listener.h", - "cpp/domain.h", "cpp/export.h", "cpp/generate_ast.h", "cpp/impl_lookup.h", diff --git a/toolchain/check/check.cpp b/toolchain/check/check.cpp index b6de302f1077..03a1d3a84287 100644 --- a/toolchain/check/check.cpp +++ b/toolchain/check/check.cpp @@ -16,7 +16,6 @@ #include "common/pretty_stack_trace_function.h" #include "toolchain/check/check_unit.h" #include "toolchain/check/context.h" -#include "toolchain/check/cpp/domain.h" #include "toolchain/check/cpp/generate_ast.h" #include "toolchain/check/cpp/import.h" #include "toolchain/check/diagnostic_emitter.h" @@ -27,6 +26,7 @@ #include "toolchain/lex/token_kind.h" #include "toolchain/parse/node_ids.h" #include "toolchain/parse/tree.h" +#include "toolchain/sem_ir/cpp_domain.h" #include "toolchain/sem_ir/file.h" #include "toolchain/sem_ir/formatter.h" #include "toolchain/sem_ir/read_only_ast_source.h" @@ -421,7 +421,7 @@ auto CheckParseTrees( // (`options.share_cpp_ast`), there is only a single shared domain. This // variable is created early so the domains outlive the `UnitAndImports` that // reference them. - llvm::SmallVector> cpp_domains; + llvm::SmallVector> cpp_domains; // UnitAndImports is big due to its SmallVectors, so we default to 0 on the // stack. @@ -507,7 +507,7 @@ auto CheckParseTrees( // Create C++ domains for Cpp imports. if (options.share_cpp_ast) { - llvm::SmallVector inputs; + llvm::SmallVector inputs; for (auto& unit_info : unit_infos) { if (unit_info.cpp_imports.empty()) { continue; diff --git a/toolchain/check/check.h b/toolchain/check/check.h index 21a460fbbf6f..6e19017a367e 100644 --- a/toolchain/check/check.h +++ b/toolchain/check/check.h @@ -33,8 +33,6 @@ struct Unit { int total_ir_count; }; -class CppDomain; - struct CheckParseTreesOptions { // Options must be set individually, not through initialization. explicit CheckParseTreesOptions() = default; diff --git a/toolchain/check/check_unit.cpp b/toolchain/check/check_unit.cpp index be8ab124ecd0..d363f4f5bb25 100644 --- a/toolchain/check/check_unit.cpp +++ b/toolchain/check/check_unit.cpp @@ -159,6 +159,8 @@ auto CheckUnit::InitPackageScopeAndImports() -> void { CARBON_CHECK(context_.scope_stack().PeekIndex() == ScopeIndex::Package); ImportOtherPackages(namespace_type_id); + // Do this last: this also picks up indirect C++ imports through Carbon + // imports. ImportCpp(context_, unit_and_imports_->cpp_imports, unit_and_imports_->cpp_domain); } diff --git a/toolchain/check/check_unit.h b/toolchain/check/check_unit.h index 47deae313b9c..db9e7ad93af5 100644 --- a/toolchain/check/check_unit.h +++ b/toolchain/check/check_unit.h @@ -17,10 +17,13 @@ namespace clang { class CompilerInvocation; } +namespace Carbon::SemIR { +class CppDomain; +} // namespace Carbon::SemIR + namespace Carbon::Check { struct UnitAndImports; -class CppDomain; // A file's imports corresponding to a single package, for // `UnitAndImports::package_imports`. @@ -103,7 +106,7 @@ struct UnitAndImports { llvm::SmallVector cpp_imports; // The C++ domain for this unit. - CppDomain* cpp_domain = nullptr; + SemIR::CppDomain* cpp_domain = nullptr; // The remaining number of imports which must be checked before this unit can // be processed. diff --git a/toolchain/check/cpp/context.cpp b/toolchain/check/cpp/context.cpp index ae0bf2acadd5..ccf3e6e08a8b 100644 --- a/toolchain/check/cpp/context.cpp +++ b/toolchain/check/cpp/context.cpp @@ -6,11 +6,10 @@ #include "clang/AST/Mangle.h" #include "clang/Frontend/CompilerInstance.h" -#include "toolchain/check/cpp/domain.h" namespace Carbon::Check { -CppContext::CppContext(CppDomain& domain, +CppContext::CppContext(SemIR::CppDomain& domain, std::unique_ptr listener) : domain_(&domain), diagnostic_listener_(std::move(listener)) {} diff --git a/toolchain/check/cpp/context.h b/toolchain/check/cpp/context.h index 94518cc4dfd3..17f03ab05fb8 100644 --- a/toolchain/check/cpp/context.h +++ b/toolchain/check/cpp/context.h @@ -11,7 +11,7 @@ #include "common/check.h" #include "llvm/ADT/SmallVector.h" #include "toolchain/check/cpp/diagnostic_listener.h" -#include "toolchain/check/cpp/domain.h" +#include "toolchain/sem_ir/cpp_domain.h" namespace clang { class ASTContext; @@ -31,7 +31,7 @@ namespace Carbon::Check { // declarations, and similar values. class CppContext { public: - explicit CppContext(CppDomain& domain, + explicit CppContext(SemIR::CppDomain& domain, std::unique_ptr listener); ~CppContext(); @@ -39,8 +39,8 @@ class CppContext { auto sema() -> clang::Sema&; auto parser() -> clang::Parser& { return domain_->parser(); } - auto domain() -> CppDomain& { return *domain_; } - auto domain() const -> const CppDomain& { return *domain_; } + auto domain() -> SemIR::CppDomain& { return *domain_; } + auto domain() const -> const SemIR::CppDomain& { return *domain_; } auto clang_mangle_context() -> clang::MangleContext&; @@ -57,7 +57,7 @@ class CppContext { private: // The C++ compilation domain. - CppDomain* domain_; + SemIR::CppDomain* domain_; // TODO: All of the below state that is not specific to a particular // Check::Context or SemIR::File should be moved into CppDomain. diff --git a/toolchain/check/cpp/generate_ast.cpp b/toolchain/check/cpp/generate_ast.cpp index 8f02c78a3dad..974faa7d6d62 100644 --- a/toolchain/check/cpp/generate_ast.cpp +++ b/toolchain/check/cpp/generate_ast.cpp @@ -12,6 +12,7 @@ #include "clang/AST/Mangle.h" #include "clang/Basic/DiagnosticParse.h" #include "clang/Basic/FileManager.h" +#include "clang/Basic/Module.h" #include "clang/CodeGen/ModuleBuilder.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" @@ -35,7 +36,6 @@ #include "toolchain/check/cpp/access.h" #include "toolchain/check/cpp/diagnostic_consumer.h" #include "toolchain/check/cpp/diagnostic_listener.h" -#include "toolchain/check/cpp/domain.h" #include "toolchain/check/cpp/export.h" #include "toolchain/check/cpp/import.h" #include "toolchain/check/cpp/location.h" @@ -47,7 +47,9 @@ #include "toolchain/diagnostics/emitter.h" #include "toolchain/diagnostics/format_providers.h" #include "toolchain/parse/node_ids.h" +#include "toolchain/sem_ir/cpp_domain.h" #include "toolchain/sem_ir/cpp_file.h" +#include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/read_only_ast_source.h" #include "toolchain/sem_ir/typed_insts.h" @@ -82,37 +84,6 @@ static auto AppendInlineCode(Context& context, llvm::raw_ostream& out, out << code << "\n"; } -// Generates C++ file contents to #include all requested imports. -static auto GenerateCppIncludesHeaderCode( - Context& context, llvm::ArrayRef imports) - -> std::string { - RawStringOstream code_stream; - for (const Parse::Tree::PackagingNames& import : imports) { - if (import.inline_body_id.has_value()) { - // Expand `import Cpp inline "code";` directly into the specified code. - auto code_token = context.parse_tree().node_token(import.inline_body_id); - AppendInlineCode(context, code_stream, code_token, - context.string_literal_values().Get( - context.tokens().GetStringLiteralValue(code_token))); - // TODO: Inject a clang pragma here to produce an error if there are - // unclosed scopes at the end of this inline C++ fragment. - } else if (import.library_id.has_value()) { - // Translate `import Cpp library "foo.h";` into `#include "foo.h"`. - GenerateLineMarker(context, code_stream, - context.tokens().GetLineNumber( - context.parse_tree().node_token(import.node_id))); - auto name = context.string_literal_values().Get(import.library_id); - if (name.starts_with('<') && name.ends_with('>')) { - code_stream << "#include <" - << FormatEscaped(name.drop_front().drop_back()) << ">\n"; - } else { - code_stream << "#include \"" << FormatEscaped(name) << "\"\n"; - } - } - } - return code_stream.TakeStr(); -} - namespace { // A wrapper around a clang::CompilerInvocation that allows us to make a shallow @@ -619,7 +590,8 @@ static auto ParseTopLevelDecls(clang::Parser& parser, } // Generate a Clang module corresponding to the current Carbon file. -static auto CreateModuleForFile(CppDomain& domain, const SemIR::File& file) +static auto CreateModuleForCarbonFile(SemIR::CppDomain& domain, + const SemIR::File& file) -> clang::Module* { // TODO: Consider creating a parent module to hold all Carbon modules. // Consider naming the module after the package and library rather than using @@ -628,8 +600,36 @@ static auto CreateModuleForFile(CppDomain& domain, const SemIR::File& file) .getPreprocessor() .getHeaderSearchInfo() .getModuleMap(); - return module_map.createModule(file.filename(), /*Parent=*/nullptr, - /*IsFramework=*/false, /*IsExplicit=*/true); + auto* module = + module_map.createModule(file.filename(), /*Parent=*/nullptr, + /*IsFramework=*/false, /*IsExplicit=*/true); + auto insert_result = domain.file_modules().Insert(file.check_ir_id(), module); + CARBON_CHECK(insert_result.is_inserted()); + return module; +} + +// Generates a Clang module corresponding to the given C++ header name. Note +// that this is separate from Clang's header -> module mapping. Even if a C++ +// header is imported into Carbon, C++-side #includes of the same header are +// still treated as textual inclusions. +// Returns the module and a bool indicating whether it was newly created. +static auto GetOrCreateModuleForHeader(SemIR::CppDomain& domain, + llvm::StringRef header_name) + -> std::pair { + auto [it, added] = domain.header_modules().insert({header_name, nullptr}); + if (!added) { + CARBON_CHECK(it->second); + return {it->second, false}; + } + + auto& module_map = domain.clang_instance() + .getPreprocessor() + .getHeaderSearchInfo() + .getModuleMap(); + it->second = module_map.createModule(header_name, /*Parent=*/nullptr, + /*IsFramework=*/false, + /*IsExplicit=*/true); + return {it->second, true}; } // Parse the tokens that have been injected into the preprocessor in the given @@ -659,37 +659,22 @@ static auto InjectBuffer(CppContext& cpp_context, llvm::StringRef contents, return file_id; } -// Injects code to import the given set of headers into Clang and parses it as -// top-level declarations. -static auto ParseImports(Context& context, - llvm::ArrayRef imports) - -> void { - auto* cpp_context = context.cpp_context(); - CARBON_CHECK(cpp_context); - - // Inject the imports-as-#includes buffer. - auto file_id = InjectBuffer(*cpp_context, - GenerateCppIncludesHeaderCode(context, imports), - "", clang::SourceLocation()); - - // Enter the module for this file. - auto& preprocessor = cpp_context->sema().getPreprocessor(); - auto* mod = CreateModuleForFile(cpp_context->domain(), context.sem_ir()); - auto loc = preprocessor.getSourceManager().getLocForStartOfFile(file_id); +// Instruct the Clang preprocessor and Sema to enter the scope of the given +// module. +static auto EnterModule(CppContext& cpp_context, clang::Module* mod, + clang::SourceLocation loc) -> void { + auto& preprocessor = cpp_context.sema().getPreprocessor(); preprocessor.EnterSubmodule(mod, loc, /*ForPragma=*/false); preprocessor.EnterAnnotationToken(loc, clang::tok::annot_module_begin, mod); - - ParseInjectedTokens(*cpp_context); + ParseInjectedTokens(cpp_context); } // Leave the current Clang module. -static auto LeaveModule(Context& context, clang::SourceLocation loc) -> void { +static auto LeaveModule(CppContext& cpp_context, clang::SourceLocation loc) + -> void { CARBON_CHECK(loc.isValid()); - auto* cpp_context = context.cpp_context(); - CARBON_CHECK(cpp_context); - - auto& preprocessor = cpp_context->sema().getPreprocessor(); + auto& preprocessor = cpp_context.sema().getPreprocessor(); auto* mod = preprocessor.LeaveSubmodule(/*ForPragma=*/false); CARBON_CHECK(mod); @@ -698,25 +683,134 @@ static auto LeaveModule(Context& context, clang::SourceLocation loc) -> void { // it sees a `module;` directive in the source. So recover from this by // leaving modules until we find the preprocessor's module. while (true) { - auto* sema_mod = cpp_context->sema().getCurrentModule(); + auto* sema_mod = cpp_context.sema().getCurrentModule(); CARBON_CHECK(sema_mod, "Sema prematurely exited Carbon module"); preprocessor.EnterAnnotationToken(loc, clang::tok::annot_module_end, sema_mod); - ParseInjectedTokens(*cpp_context); + ParseInjectedTokens(cpp_context); if (sema_mod == mod) { break; } } } +// Imports the module `import_mod` into the current Clang state. +static auto ImportModule(CppContext& cpp_context, clang::Module* import_mod, + clang::SourceLocation loc) -> void { + CARBON_CHECK(import_mod); + cpp_context.sema().getModuleLoader().makeModuleVisible( + import_mod, clang::Module::AllVisible, loc); + cpp_context.sema().getPreprocessor().makeModuleVisible(import_mod, loc); + cpp_context.sema().makeModuleVisible(import_mod, loc); +} + +// Imports the header specified by the given import declaration. +static auto ImportHeader(Context& context, clang::Module* mod, + const Parse::Tree::PackagingNames& import) -> void { + auto* cpp_context = context.cpp_context(); + CARBON_CHECK(cpp_context); + + clang::SourceLocation import_loc = GetCppLocation(context, import.node_id); + + // Import the corresponding module. + auto name = context.string_literal_values().Get(import.library_id); + auto [header_mod, added] = + GetOrCreateModuleForHeader(cpp_context->domain(), name); + + // Re-export the header. + // TODO: Only do this if the header is `export import`ed. For now we don't + // syntactically allow `export` on `import Cpp ...` declarations. + mod->Exports.push_back({header_mod, false}); + + // If this is the first time we've seen an import of this header, build + // the contents of its module now. + if (added) { + EnterModule(*cpp_context, header_mod, import_loc); + + // The header module re-exports everything it imports. + header_mod->Exports.push_back({nullptr, true}); + + RawStringOstream code_stream; + GenerateLineMarker(context, code_stream, + context.tokens().GetLineNumber( + context.parse_tree().node_token(import.node_id))); + if (name.starts_with('<') && name.ends_with('>')) { + code_stream << "#include <" + << FormatEscaped(name.drop_front().drop_back()) << ">\n"; + } else { + code_stream << "#include \"" << FormatEscaped(name) << "\"\n"; + } + InjectBuffer(*cpp_context, code_stream.TakeStr(), "
", + clang::SourceLocation()); + ParseInjectedTokens(*cpp_context); + + LeaveModule(*cpp_context, import_loc); + } + + ImportModule(*cpp_context, header_mod, import_loc); +} + +// Injects code to import the given set of headers into Clang and parses it as +// top-level declarations. +static auto ParseImports(Context& context, + llvm::ArrayRef imports) + -> void { + auto* cpp_context = context.cpp_context(); + CARBON_CHECK(cpp_context); + + auto& preprocessor = cpp_context->sema().getPreprocessor(); + auto filename = context.sem_ir().filename(); + + // Enter the module for this file. Generate a placeholder empty buffer so we + // can provide a location for entering the module. + auto file_id = + InjectBuffer(*cpp_context, "", filename, clang::SourceLocation()); + auto loc = preprocessor.getSourceManager().getLocForStartOfFile(file_id); + auto* mod = + CreateModuleForCarbonFile(cpp_context->domain(), context.sem_ir()); + EnterModule(*cpp_context, mod, loc); + + // Import the modules for all the imported IRs. + for (const auto& import_ir : context.import_irs().values()) { + if (!import_ir.sem_ir) { + continue; + } + if (auto lookup = cpp_context->domain().file_modules().Lookup( + import_ir.sem_ir->check_ir_id())) { + auto* import_mod = lookup.value(); + ImportModule(*cpp_context, import_mod, loc); + if (import_ir.is_export) { + mod->Exports.push_back({import_mod, false}); + } else { + mod->Imports.push_back(import_mod); + } + } + } + + // For each imported C++ header, generate a module and include the header into + // that module. For imported inline code, parse the code directly. + for (const Parse::Tree::PackagingNames& import : imports) { + if (import.inline_body_id.has_value()) { + // `import Cpp inline "foo";` behaves the same as `inline Cpp "foo";`. + auto code_token = context.parse_tree().node_token(import.inline_body_id); + InjectAstFromInlineCode( + context, import.inline_body_id, + context.string_literal_values().Get( + context.tokens().GetStringLiteralValue(code_token))); + } else if (import.library_id.has_value()) { + ImportHeader(context, mod, import); + } + } +} + namespace { // An action and a set of registered Clang callbacks used to generate an AST // from a set of Cpp imports. class GenerateASTAction : public clang::ASTFrontendAction { public: - explicit GenerateASTAction(llvm::ArrayRef inputs, + explicit GenerateASTAction(llvm::ArrayRef inputs, llvm::LLVMContext* llvm_context) : inputs_(inputs), llvm_context_(llvm_context) {} @@ -795,7 +889,7 @@ class GenerateASTAction : public clang::ASTFrontendAction { } private: - llvm::ArrayRef inputs_; + llvm::ArrayRef inputs_; llvm::LLVMContext* llvm_context_; llvm::SmallVector code_generators_; std::unique_ptr parser_; @@ -807,11 +901,11 @@ class GenerateASTAction : public clang::ASTFrontendAction { // creating a diagnostics engine, and parsing a dummy main file containing a // semicolon. Returns the initialized state, or null on failure. auto InitializeCppDomain( - Diagnostics::Consumer& consumer, llvm::ArrayRef inputs, + Diagnostics::Consumer& consumer, llvm::ArrayRef inputs, llvm::IntrusiveRefCntPtr fs, llvm::LLVMContext* llvm_context, std::shared_ptr base_invocation) - -> std::unique_ptr { + -> std::unique_ptr { std::shared_ptr clang_instance; llvm::IntrusiveRefCntPtr diags; @@ -901,14 +995,14 @@ auto InitializeCppDomain( CARBON_CHECK(parser); CARBON_CHECK(action.code_generators().size() == inputs.size()); - return std::make_unique(std::move(clang_instance), - std::move(parser), inputs, - action.code_generators(), llvm_context); + return std::make_unique( + std::move(clang_instance), std::move(parser), inputs, + action.code_generators(), llvm_context); } auto GenerateAst(Context& context, llvm::ArrayRef imports, - CppDomain& domain) -> bool { + SemIR::CppDomain& domain) -> bool { CARBON_CHECK(!context.cpp_context()); CARBON_CHECK(!context.sem_ir().cpp_file()); @@ -927,7 +1021,7 @@ auto GenerateAst(Context& context, // Set up CppFile for the current SemIR::File. context.sem_ir().set_cpp_file(std::make_unique( clang_instance, std::move(mangle_context), domain.llvm_context(), - domain.GetCodeGenerator(context.sem_ir().check_ir_id()))); + domain.GetCodeGenerator(context.sem_ir().check_ir_id()), &domain)); // Set up CppContext for the current Context. context.set_cpp_context(std::make_unique( @@ -981,7 +1075,7 @@ auto FinishAst(Context& context) -> void { // actually included, and then crash because it picked an invalid location! // There is always at least one token in a file with a `Cpp` import, so this // location adjustment is safe. - LeaveModule(context, + LeaveModule(*context.cpp_context(), GetCppLocation(context, end_loc_id).getLocWithOffset(-1)); // Finalize the per-Context AST fragment. The final ActOnEndOfTranslationUnit @@ -1010,7 +1104,7 @@ auto FinishAst(Context& context) -> void { context.set_cpp_context(nullptr); } -auto FinalizeCppDomain(CppDomain& domain) -> void { +auto FinalizeCppDomain(SemIR::CppDomain& domain) -> void { if (domain.clang_instance_ptr()) { domain.clang_instance().getSema().ActOnEndOfTranslationUnit(); FlushDiagnosticConsumer( diff --git a/toolchain/check/cpp/generate_ast.h b/toolchain/check/cpp/generate_ast.h index 733c8eddba06..f3340334e762 100644 --- a/toolchain/check/cpp/generate_ast.h +++ b/toolchain/check/cpp/generate_ast.h @@ -12,8 +12,8 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/VirtualFileSystem.h" #include "toolchain/check/context.h" -#include "toolchain/check/cpp/domain.h" #include "toolchain/parse/tree.h" +#include "toolchain/sem_ir/cpp_domain.h" #include "toolchain/sem_ir/ids.h" namespace clang { @@ -30,24 +30,22 @@ class Consumer; namespace Carbon::Check { -class CppDomain; - // Initializes a Clang compilation instance, which can be used to parse C++ code // within one or more Carbon files. Returns the initialized state, or null on // failure. auto InitializeCppDomain( - Diagnostics::Consumer& consumer, llvm::ArrayRef inputs, + Diagnostics::Consumer& consumer, llvm::ArrayRef inputs, llvm::IntrusiveRefCntPtr fs, llvm::LLVMContext* llvm_context, std::shared_ptr base_invocation) - -> std::unique_ptr; + -> std::unique_ptr; // Generates a Clang AST for the given C++ imports and sets it as the context's // `cpp_context` and the SemIR's `cpp_file`. Returns a bool that represents // whether compilation was successful. auto GenerateAst(Context& context, llvm::ArrayRef imports, - CppDomain& domain) -> bool; + SemIR::CppDomain& domain) -> bool; // Injects C++ code from `inline Cpp` into the active Clang AST context. // Returns a bool representing whether parsing was successful. @@ -59,7 +57,7 @@ auto InjectAstFromInlineCode(Context& context, SemIR::LocId loc_id, auto FinishAst(Context& context) -> void; // Finalizes a C++ domain at the end of checking all files. -auto FinalizeCppDomain(CppDomain& domain) -> void; +auto FinalizeCppDomain(SemIR::CppDomain& domain) -> void; } // namespace Carbon::Check diff --git a/toolchain/check/cpp/import.cpp b/toolchain/check/cpp/import.cpp index 8281bf66e4d8..1baa0dd98d09 100644 --- a/toolchain/check/cpp/import.cpp +++ b/toolchain/check/cpp/import.cpp @@ -39,7 +39,6 @@ #include "toolchain/check/core_identifier.h" #include "toolchain/check/cpp/access.h" #include "toolchain/check/cpp/custom_type_mapping.h" -#include "toolchain/check/cpp/domain.h" #include "toolchain/check/cpp/generate_ast.h" #include "toolchain/check/cpp/location.h" #include "toolchain/check/cpp/macros.h" @@ -62,6 +61,7 @@ #include "toolchain/parse/node_ids.h" #include "toolchain/sem_ir/clang_decl.h" #include "toolchain/sem_ir/class.h" +#include "toolchain/sem_ir/cpp_domain.h" #include "toolchain/sem_ir/cpp_file.h" #include "toolchain/sem_ir/cpp_overload_set.h" #include "toolchain/sem_ir/function.h" @@ -119,7 +119,7 @@ static auto AddNamespace(Context& context, PackageNameId cpp_package_id, auto ImportCpp(Context& context, llvm::ArrayRef imports, - CppDomain* domain) -> void { + SemIR::CppDomain* domain) -> void { if (imports.empty()) { // TODO: Consider always having a (non-null) AST even if there are no Cpp // imports. @@ -260,6 +260,22 @@ auto FindCorrespondingClangDeclKey(Context& context, SemIR::LocId loc_id, } CARBON_CHECK(clang_decl_id.has_value()); auto key = file.clang_decls().Get(clang_decl_id).key; + + // Easy case: files are from the same domain. We can just reuse the decl + // pointer. + if (context.sem_ir().cpp_file() && file.cpp_file() && + context.sem_ir().cpp_file()->cpp_domain() == + file.cpp_file()->cpp_domain()) { + if (key.signature_id.has_value()) { + key.signature_id = context.sem_ir().clang_decl_signatures().Add( + file.clang_decl_signatures().Get(key.signature_id)); + } + if (ImportCppDecl(context, loc_id, key) != SemIR::ErrorInst::InstId) { + return key; + } + return std::nullopt; + } + const auto* decl = key.decl; auto* corresponding = FindCorrespondingDecl(context, loc_id, decl); if (!corresponding) { diff --git a/toolchain/check/cpp/import.h b/toolchain/check/cpp/import.h index 1d6663f025af..1eda3ac1df12 100644 --- a/toolchain/check/cpp/import.h +++ b/toolchain/check/cpp/import.h @@ -23,9 +23,11 @@ class IdentifierInfo; class VarDecl; } // namespace clang -namespace Carbon::Check { - +namespace Carbon::SemIR { class CppDomain; +} // namespace Carbon::SemIR + +namespace Carbon::Check { // Returns whether the given function is an object member function. This is true // if it's a non-static member function and not a constructor. Object member @@ -39,7 +41,7 @@ auto IsObjectMemberFunction(const clang::FunctionDecl& decl) -> bool; // non-null unless there was an error initializing Clang. auto ImportCpp(Context& context, llvm::ArrayRef imports, - CppDomain* domain) -> void; + SemIR::CppDomain* domain) -> void; // Given a clang declaration ID that was previously imported into another file, // returns the corresponding clang declaration key in the current context. diff --git a/toolchain/check/testdata/interop/cpp/basics/import/bad_import.carbon b/toolchain/check/testdata/interop/cpp/basics/import/bad_import.carbon index d559b238b5de..d8135a86fd54 100644 --- a/toolchain/check/testdata/interop/cpp/basics/import/bad_import.carbon +++ b/toolchain/check/testdata/interop/cpp/basics/import/bad_import.carbon @@ -52,18 +52,26 @@ library "[[@TEST_NAME]]"; // CHECK:STDERR: import Cpp library "\"foo.h\""; -// --- todo_fail_unterminated_import_inline.carbon +// --- fail_unterminated_import_inline.carbon library "[[@TEST_NAME]]"; import Cpp inline '''c++ +// CHECK:STDERR: :1:2: error: expected '}' [CppInteropParseError] +// CHECK:STDERR: 1 | ; +// CHECK:STDERR: | ^ +// CHECK:STDERR: fail_unterminated_import_inline.carbon:[[@LINE+4]]:10: note: to match this '{' [CppInteropParseNote] +// CHECK:STDERR: 12 | void f() { +// CHECK:STDERR: | ^ +// CHECK:STDERR: void f() { '''; -// TODO: We should diagnose that the inline C++ code didn't leave us at the top -// level. - import Cpp inline '''c++ +// CHECK:STDERR: fail_unterminated_import_inline.carbon:[[@LINE+4]]:1: error: extraneous closing brace ('}') [CppInteropParseError] +// CHECK:STDERR: 20 | } +// CHECK:STDERR: | ^ +// CHECK:STDERR: } '''; diff --git a/toolchain/check/testdata/interop/cpp/basics/import/indirect.carbon b/toolchain/check/testdata/interop/cpp/basics/import/indirect.carbon index 1d0d9ac233b2..f158747581ed 100644 --- a/toolchain/check/testdata/interop/cpp/basics/import/indirect.carbon +++ b/toolchain/check/testdata/interop/cpp/basics/import/indirect.carbon @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon +// EXTRA-ARGS: --share-cpp-ast // // AUTOUPDATE // TIP: To test this file alone, run: @@ -47,6 +48,7 @@ alias AX = Cpp.A.X; library "[[@TEST_NAME]]"; +// TODO: Should be `export import Cpp ...`. import Cpp library "shared.h"; import library "direct_import"; @@ -62,25 +64,42 @@ fn Function() -> i32 { //@dump-sem-ir-end } -// --- fail_todo_not_included.carbon +// --- not_included.carbon library "[[@TEST_NAME]]"; import Cpp; -// CHECK:STDERR: fail_todo_not_included.carbon:[[@LINE+6]]:1: in import [InImport] -// CHECK:STDERR: direct_import.carbon:4:10: in file included here [InCppInclude] -// CHECK:STDERR: ./shared.h:3:10: error: semantics TODO: `use of imported C++ declaration with no corresponding local import` [SemanticsTodo] -// CHECK:STDERR: struct X { -// CHECK:STDERR: ^ -// CHECK:STDERR: import library "direct_import"; fn Field() -> i32 { - // TODO: This should eventually work, by importing the C++ AST from - // `direct_import`. return F().x; } +// --- fail_not_leaked.carbon + +library "[[@TEST_NAME]]"; + +import Cpp; +inline Cpp ''' +namespace A { + struct X; +} +'''; + +fn TryToUseNotLeaked() { + // TODO: Clang's diagnostic for using a class whose definition is not + // reachable is confusing. + // CHECK:STDERR: fail_not_leaked.carbon:[[@LINE+8]]:17: error: binding pattern has incomplete type `X` in name binding declaration [IncompleteTypeInBindingDecl] + // CHECK:STDERR: var unused x: Cpp.A.X = 0; + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: direct_import.carbon:4:10: in file included here [InCppInclude] + // CHECK:STDERR: ./shared.h:3:10: note: class was forward declared here [ClassForwardDeclaredHere] + // CHECK:STDERR: struct X { + // CHECK:STDERR: ^ + // CHECK:STDERR: + var unused x: Cpp.A.X = 0; +} + // --- fail_todo_no_cpp_import.carbon library "[[@TEST_NAME]]"; @@ -177,34 +196,34 @@ fn UseZ() -> Cpp.A.Z { // CHECK:STDOUT: fn @Field() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, imports.%Main.F [concrete = constants.%F] -// CHECK:STDOUT: %.loc9_12.1: ref %X = temporary_storage -// CHECK:STDOUT: %F.call: init %X to %.loc9_12.1 = call %F.ref() -// CHECK:STDOUT: %.loc9_12.2: ref %X = temporary %.loc9_12.1, %F.call +// CHECK:STDOUT: %.loc10_12.1: ref %X = temporary_storage +// CHECK:STDOUT: %F.call: init %X to %.loc10_12.1 = call %F.ref() +// CHECK:STDOUT: %.loc10_12.2: ref %X = temporary %.loc10_12.1, %F.call // CHECK:STDOUT: %x.ref: %X.elem = name_ref x, @X.%.1 [concrete = @X.%.1] -// CHECK:STDOUT: %.loc9_13.1: ref %i32 = class_element_access %.loc9_12.2, element0 -// CHECK:STDOUT: %.loc9_13.2: %i32 = acquire_value %.loc9_13.1 +// CHECK:STDOUT: %.loc10_13.1: ref %i32 = class_element_access %.loc10_12.2, element0 +// CHECK:STDOUT: %.loc10_13.2: %i32 = acquire_value %.loc10_13.1 // CHECK:STDOUT: %impl.elem0: %.7a6 = impl_witness_access constants.%Copy.impl_witness.0e0, element0 [concrete = constants.%Int.as.Copy.impl.Op.4f6] -// CHECK:STDOUT: %bound_method.loc9_13.1: = bound_method %.loc9_13.2, %impl.elem0 +// CHECK:STDOUT: %bound_method.loc10_13.1: = bound_method %.loc10_13.2, %impl.elem0 // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] -// CHECK:STDOUT: %bound_method.loc9_13.2: = bound_method %.loc9_13.2, %specific_fn -// CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc9_13.2(%.loc9_13.2) -// CHECK:STDOUT: %X.cpp_destructor.bound: = bound_method %.loc9_12.2, constants.%X.cpp_destructor -// CHECK:STDOUT: %X.cpp_destructor.call: init %empty_tuple.type = call %X.cpp_destructor.bound(%.loc9_12.2) +// CHECK:STDOUT: %bound_method.loc10_13.2: = bound_method %.loc10_13.2, %specific_fn +// CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc10_13.2(%.loc10_13.2) +// CHECK:STDOUT: %X.cpp_destructor.bound: = bound_method %.loc10_12.2, constants.%X.cpp_destructor +// CHECK:STDOUT: %X.cpp_destructor.call: init %empty_tuple.type = call %X.cpp_destructor.bound(%.loc10_12.2) // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @Function() -> out %return.param: %i32 { // CHECK:STDOUT: !entry: // CHECK:STDOUT: %F.ref: %F.type = name_ref F, imports.%Main.F [concrete = constants.%F] -// CHECK:STDOUT: %.loc15_12.1: ref %X = temporary_storage -// CHECK:STDOUT: %F.call: init %X to %.loc15_12.1 = call %F.ref() -// CHECK:STDOUT: %.loc15_12.2: ref %X = temporary %.loc15_12.1, %F.call +// CHECK:STDOUT: %.loc16_12.1: ref %X = temporary_storage +// CHECK:STDOUT: %F.call: init %X to %.loc16_12.1 = call %F.ref() +// CHECK:STDOUT: %.loc16_12.2: ref %X = temporary %.loc16_12.1, %F.call // CHECK:STDOUT: %g.ref: %X.g.cpp_overload_set.type = name_ref g, imports.%X.g.cpp_overload_set.value [concrete = constants.%X.g.cpp_overload_set.value] -// CHECK:STDOUT: %bound_method: = bound_method %.loc15_12.2, %g.ref -// CHECK:STDOUT: %.loc15_12.3: %X = acquire_value %.loc15_12.2 -// CHECK:STDOUT: %X.g.call: init %i32 = call imports.%X.g.decl(%.loc15_12.3) -// CHECK:STDOUT: %X.cpp_destructor.bound: = bound_method %.loc15_12.2, constants.%X.cpp_destructor -// CHECK:STDOUT: %X.cpp_destructor.call: init %empty_tuple.type = call %X.cpp_destructor.bound(%.loc15_12.2) +// CHECK:STDOUT: %bound_method: = bound_method %.loc16_12.2, %g.ref +// CHECK:STDOUT: %.loc16_12.3: %X = acquire_value %.loc16_12.2 +// CHECK:STDOUT: %X.g.call: init %i32 = call imports.%X.g.decl(%.loc16_12.3) +// CHECK:STDOUT: %X.cpp_destructor.bound: = bound_method %.loc16_12.2, constants.%X.cpp_destructor +// CHECK:STDOUT: %X.cpp_destructor.call: init %empty_tuple.type = call %X.cpp_destructor.bound(%.loc16_12.2) // CHECK:STDOUT: return %X.g.call // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/function/import/thunk_ast.carbon b/toolchain/check/testdata/interop/cpp/function/import/thunk_ast.carbon index 2dd56cd84241..10369b269618 100644 --- a/toolchain/check/testdata/interop/cpp/function/import/thunk_ast.carbon +++ b/toolchain/check/testdata/interop/cpp/function/import/thunk_ast.carbon @@ -18,9 +18,16 @@ // --- thunk_required.h auto foo(short a) -> void; -// CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} <./thunk_required.h:[[@LINE-1]]:1, col:22> col:6 in import_thunk_required.carbon hidden used foo 'auto (short) -> void' external-linkage -// CHECK:STDOUT: | `-ParmVarDecl {{0x[a-f0-9]+}} col:16 in import_thunk_required.carbon hidden a 'short' -// CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} col:6 in import_thunk_required.carbon hidden foo__carbon_thunk 'void (short * _Nonnull)' inline internal-linkage +// CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} <./thunk_required.h:[[@LINE-1]]:1, col:22> col:6 in thunk_required.h hidden used foo 'auto (short) -> void' external-linkage +// CHECK:STDOUT: | `-ParmVarDecl {{0x[a-f0-9]+}} col:16 in thunk_required.h hidden a 'short' + +// --- import_thunk_required.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "thunk_required.h"; +// CHECK:STDOUT: |-ImportDecl {{0x[a-f0-9]+}} col:38 in thunk_required.h implicit thunk_required.h +// CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} <./thunk_required.h:2:6> col:6 in import_thunk_required.carbon hidden foo__carbon_thunk 'void (short * _Nonnull)' inline internal-linkage // CHECK:STDOUT: | |-ParmVarDecl {{0x[a-f0-9]+}} col:6 in import_thunk_required.carbon hidden used a 'short * _Nonnull':'short *' // CHECK:STDOUT: | |-ReturnStmt {{0x[a-f0-9]+}} // CHECK:STDOUT: | | `-CallExpr {{0x[a-f0-9]+}} 'void' @@ -34,12 +41,6 @@ auto foo(short a) -> void; // CHECK:STDOUT: | |-InternalLinkageAttr {{0x[a-f0-9]+}} <> Implicit // CHECK:STDOUT: | `-AsmLabelAttr {{0x[a-f0-9]+}} Implicit "_Z3foos.carbon_thunk._" -// --- import_thunk_required.carbon - -library "[[@TEST_NAME]]"; - -import Cpp library "thunk_required.h"; - fn F() { Cpp.foo(1 as i16); } @@ -51,8 +52,15 @@ fn F() { // --- return_thunk_required.h auto foo() -> short; -// CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} <./return_thunk_required.h:[[@LINE-1]]:1, col:15> col:6 in import_return_thunk_required.carbon hidden used foo 'auto () -> short' external-linkage -// CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} col:6 in import_return_thunk_required.carbon hidden foo__carbon_thunk 'void (short * _Nonnull)' inline internal-linkage +// CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} <./return_thunk_required.h:[[@LINE-1]]:1, col:15> col:6 in return_thunk_required.h hidden used foo 'auto () -> short' external-linkage + +// --- import_return_thunk_required.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "return_thunk_required.h"; +// CHECK:STDOUT: |-ImportDecl {{0x[a-f0-9]+}} col:45 in return_thunk_required.h implicit return_thunk_required.h +// CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} <./return_thunk_required.h:2:6> col:6 in import_return_thunk_required.carbon hidden foo__carbon_thunk 'void (short * _Nonnull)' inline internal-linkage // CHECK:STDOUT: | |-ParmVarDecl {{0x[a-f0-9]+}} col:6 in import_return_thunk_required.carbon hidden used return 'short * _Nonnull':'short *' // CHECK:STDOUT: | |-CXXNewExpr {{0x[a-f0-9]+}} 'short *' global Function {{0x[a-f0-9]+}} 'operator new' 'void *(__size_t, void *) noexcept' // CHECK:STDOUT: | | |-CallExpr {{0x[a-f0-9]+}} 'short' @@ -63,12 +71,6 @@ auto foo() -> short; // CHECK:STDOUT: | |-InternalLinkageAttr {{0x[a-f0-9]+}} <> Implicit // CHECK:STDOUT: | `-AsmLabelAttr {{0x[a-f0-9]+}} Implicit "_Z3foov.carbon_thunk." -// --- import_return_thunk_required.carbon - -library "[[@TEST_NAME]]"; - -import Cpp library "return_thunk_required.h"; - fn F() -> i16 { return Cpp.foo(); } diff --git a/toolchain/sem_ir/BUILD b/toolchain/sem_ir/BUILD index f83e2c076a4d..38da198eff6a 100644 --- a/toolchain/sem_ir/BUILD +++ b/toolchain/sem_ir/BUILD @@ -67,9 +67,14 @@ cc_library( cc_library( name = "cpp_file", - hdrs = ["cpp_file.h"], + hdrs = [ + "cpp_domain.h", + "cpp_file.h", + ], deps = [ + ":typed_insts", "//common:check", + "//common:map", "@llvm-project//clang:ast", "@llvm-project//clang:basic", "@llvm-project//clang:codegen", @@ -90,6 +95,7 @@ cc_library( "class.cpp", "constant.cpp", "core_interface.cpp", + "cpp_domain.cpp", "cpp_file.cpp", "cpp_initializer_list.cpp", "cpp_overload_set.cpp", @@ -125,6 +131,7 @@ cc_library( "constant.h", "copy_on_write_block.h", "core_interface.h", + "cpp_domain.h", "cpp_initializer_list.h", "cpp_overload_set.h", "declared_facet_type.h", @@ -188,6 +195,7 @@ cc_library( "@llvm-project//clang:ast", "@llvm-project//clang:basic", "@llvm-project//clang:frontend", + "@llvm-project//clang:parse", "@llvm-project//clang:sema", "@llvm-project//llvm:Support", ], diff --git a/toolchain/check/cpp/domain.cpp b/toolchain/sem_ir/cpp_domain.cpp similarity index 91% rename from toolchain/check/cpp/domain.cpp rename to toolchain/sem_ir/cpp_domain.cpp index 527c7328706a..a578ad954243 100644 --- a/toolchain/check/cpp/domain.cpp +++ b/toolchain/sem_ir/cpp_domain.cpp @@ -2,11 +2,11 @@ // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -#include "toolchain/check/cpp/domain.h" +#include "toolchain/sem_ir/cpp_domain.h" #include "clang/Parse/Parser.h" -namespace Carbon::Check { +namespace Carbon::SemIR { CppDomain::CppDomain(std::shared_ptr clang_instance, std::unique_ptr parser, @@ -27,4 +27,4 @@ CppDomain::CppDomain(std::shared_ptr clang_instance, CppDomain::~CppDomain() = default; -} // namespace Carbon::Check +} // namespace Carbon::SemIR diff --git a/toolchain/check/cpp/domain.h b/toolchain/sem_ir/cpp_domain.h similarity index 75% rename from toolchain/check/cpp/domain.h rename to toolchain/sem_ir/cpp_domain.h index a2fd38b2f6bc..45bf8fdadc52 100644 --- a/toolchain/check/cpp/domain.h +++ b/toolchain/sem_ir/cpp_domain.h @@ -2,19 +2,21 @@ // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -#ifndef CARBON_TOOLCHAIN_CHECK_CPP_DOMAIN_H_ -#define CARBON_TOOLCHAIN_CHECK_CPP_DOMAIN_H_ +#ifndef CARBON_TOOLCHAIN_SEM_IR_CPP_DOMAIN_H_ +#define CARBON_TOOLCHAIN_SEM_IR_CPP_DOMAIN_H_ #include #include "common/check.h" #include "common/map.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringMap.h" #include "toolchain/sem_ir/ids.h" namespace clang { class CodeGenerator; class CompilerInstance; +class Module; class Parser; } // namespace clang @@ -22,12 +24,12 @@ namespace llvm { class LLVMContext; } // namespace llvm -namespace Carbon::Check { +namespace Carbon::SemIR { // An input Carbon file and its CheckIRId for C++ domain code generation. struct CppInputFile { // The ID used to identify this file within SemIR. - SemIR::CheckIRId check_ir_id; + CheckIRId check_ir_id; // The Carbon source filename for this input. llvm::StringRef filename; // Whether this input IR will be lowered. If not, we don't need to build a @@ -55,20 +57,26 @@ class CppDomain { auto parser() const -> clang::Parser& { return *parser_; } auto llvm_context() const -> llvm::LLVMContext* { return llvm_context_; } - auto GetCodeGenerator(SemIR::CheckIRId check_ir_id) const - -> clang::CodeGenerator* { + auto GetCodeGenerator(CheckIRId check_ir_id) const -> clang::CodeGenerator* { auto res = code_generators_.Lookup(check_ir_id); CARBON_CHECK(res, "No CodeGenerator found for CheckIRId {0}", check_ir_id); return res.value(); } + auto file_modules() -> Map& { return modules_; } + auto header_modules() -> llvm::StringMap& { + return header_modules_; + } + private: std::shared_ptr clang_instance_; std::unique_ptr parser_; - Map code_generators_; + Map code_generators_; + Map modules_; + llvm::StringMap header_modules_; llvm::LLVMContext* llvm_context_ = nullptr; }; -} // namespace Carbon::Check +} // namespace Carbon::SemIR -#endif // CARBON_TOOLCHAIN_CHECK_CPP_DOMAIN_H_ +#endif // CARBON_TOOLCHAIN_SEM_IR_CPP_DOMAIN_H_ diff --git a/toolchain/sem_ir/cpp_file.cpp b/toolchain/sem_ir/cpp_file.cpp index dabbfcc62b6c..36df842f5952 100644 --- a/toolchain/sem_ir/cpp_file.cpp +++ b/toolchain/sem_ir/cpp_file.cpp @@ -14,11 +14,12 @@ namespace Carbon::SemIR { CppFile::CppFile(std::shared_ptr clang, std::unique_ptr mangle_context, llvm::LLVMContext* llvm_context, - clang::CodeGenerator* code_generator) + clang::CodeGenerator* code_generator, CppDomain* cpp_domain) : clang_(std::move(clang)), mangle_context_(std::move(mangle_context)), llvm_context_(llvm_context), - code_generator_(code_generator) {} + code_generator_(code_generator), + cpp_domain_(cpp_domain) {} CppFile::~CppFile() = default; diff --git a/toolchain/sem_ir/cpp_file.h b/toolchain/sem_ir/cpp_file.h index a046f1af71fb..9357647d86cf 100644 --- a/toolchain/sem_ir/cpp_file.h +++ b/toolchain/sem_ir/cpp_file.h @@ -24,6 +24,8 @@ class LLVMContext; namespace Carbon::SemIR { +class CppDomain; + // The result of compiling the C++ portion of a `File`, including both any // imported C++ headers and any inline C++ fragments. class CppFile { @@ -31,7 +33,7 @@ class CppFile { explicit CppFile(std::shared_ptr clang, std::unique_ptr mangle_context, llvm::LLVMContext* llvm_context, - clang::CodeGenerator* code_generator); + clang::CodeGenerator* code_generator, CppDomain* cpp_domain); ~CppFile(); // Access to compilation options. @@ -55,12 +57,14 @@ class CppFile { auto code_generator() const -> clang::CodeGenerator* { return code_generator_; } + auto cpp_domain() const -> CppDomain* { return cpp_domain_; } private: std::shared_ptr clang_; std::unique_ptr mangle_context_; llvm::LLVMContext* llvm_context_; clang::CodeGenerator* code_generator_; + CppDomain* cpp_domain_; }; } // namespace Carbon::SemIR