diff --git a/toolchain/check/BUILD b/toolchain/check/BUILD index d31dc1a01372..efd7bae3895f 100644 --- a/toolchain/check/BUILD +++ b/toolchain/check/BUILD @@ -112,6 +112,7 @@ cc_library( "//toolchain/sem_ir:inst", "//toolchain/sem_ir:typed_insts", "@llvm-project//clang:frontend", + "@llvm-project//clang:sema", "@llvm-project//clang:tooling", "@llvm-project//llvm:Support", ], diff --git a/toolchain/check/check.h b/toolchain/check/check.h index f876c681ae45..50d60e824044 100644 --- a/toolchain/check/check.h +++ b/toolchain/check/check.h @@ -27,6 +27,9 @@ struct Unit { // The unit's SemIR, provided as empty and filled in by CheckParseTrees. SemIR::File* sem_ir; + + // The Clang AST owned by `CompileSubcommand`. + std::unique_ptr* cpp_ast = nullptr; }; // Checks a group of parse trees. This will use imports to decide the order of diff --git a/toolchain/check/check_unit.cpp b/toolchain/check/check_unit.cpp index 0e1525acbc84..d1cdcbcafbd9 100644 --- a/toolchain/check/check_unit.cpp +++ b/toolchain/check/check_unit.cpp @@ -135,8 +135,16 @@ auto CheckUnit::InitPackageScopeAndImports() -> void { ImportCurrentPackage(package_inst_id, namespace_type_id); CARBON_CHECK(context_.scope_stack().PeekIndex() == ScopeIndex::Package); ImportOtherPackages(namespace_type_id); - ImportCppFiles(context_, unit_and_imports_->unit->sem_ir->filename(), - unit_and_imports_->cpp_import_names, fs_); + + const auto& cpp_import_names = unit_and_imports_->cpp_import_names; + if (!cpp_import_names.empty()) { + auto* cpp_ast = unit_and_imports_->unit->cpp_ast; + CARBON_CHECK(cpp_ast); + CARBON_CHECK(!cpp_ast->get()); + *cpp_ast = + ImportCppFiles(context_, unit_and_imports_->unit->sem_ir->filename(), + cpp_import_names, fs_); + } } auto CheckUnit::CollectDirectImports( diff --git a/toolchain/check/import_cpp.cpp b/toolchain/check/import_cpp.cpp index 8cdd79ffb628..7aa5f8275076 100644 --- a/toolchain/check/import_cpp.cpp +++ b/toolchain/check/import_cpp.cpp @@ -9,6 +9,7 @@ #include #include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Sema/Lookup.h" #include "clang/Tooling/Tooling.h" #include "common/raw_string_ostream.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" @@ -21,6 +22,7 @@ #include "toolchain/check/type.h" #include "toolchain/diagnostics/diagnostic.h" #include "toolchain/diagnostics/format_providers.h" +#include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/name_scope.h" namespace Carbon::Check { @@ -61,9 +63,10 @@ static auto GenerateAst(Context& context, llvm::StringRef importing_file_path, diagnostic_options.get()); // TODO: Share compilation flags with ClangRunner. auto ast = clang::tooling::buildASTFromCodeWithArgs( - GenerateCppIncludesHeaderCode(context, imports), {}, - (importing_file_path + ".generated.cpp_imports.h").str(), "clang-tool", - std::make_shared(), + GenerateCppIncludesHeaderCode(context, imports), + // Parse C++ (and not C) + {"-x", "c++"}, (importing_file_path + ".generated.cpp_imports.h").str(), + "clang-tool", std::make_shared(), clang::tooling::getClangStripDependencyFileAdjuster(), clang::tooling::FileContentMappings(), &diagnostics_consumer, fs); // TODO: Implement and use a DynamicRecursiveASTVisitor to traverse the AST. @@ -115,12 +118,14 @@ static auto AddNamespace(Context& context, PackageNameId cpp_package_id, auto ImportCppFiles(Context& context, llvm::StringRef importing_file_path, llvm::ArrayRef imports, llvm::IntrusiveRefCntPtr fs) - -> void { + -> std::unique_ptr { if (imports.empty()) { - return; + return nullptr; } - auto [ast, ast_has_error] = + CARBON_CHECK(!context.sem_ir().cpp_ast()); + + auto [generated_ast, ast_has_error] = GenerateAst(context, importing_file_path, imports, fs); PackageNameId package_id = imports.front().package_id; @@ -131,10 +136,163 @@ 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); + + context.sem_ir().set_cpp_ast(generated_ast.get()); if (ast_has_error) { name_scope.set_has_error(); } + + return std::move(generated_ast); +} + +// Look ups the given name in the Clang AST. Returns the lookup result if lookup +// was successful. +static auto ClangLookup(Context& context, SemIR::LocId loc_id, + SemIR::NameId name_id) + -> std::optional { + std::optional name = + context.names().GetAsStringIfIdentifier(name_id); + if (!name) { + // Special names never exist in C++ code. + return std::nullopt; + } + + clang::ASTUnit* ast = context.sem_ir().cpp_ast(); + CARBON_CHECK(ast); + clang::Sema& sema = ast->getSema(); + + clang::LookupResult lookup( + sema, + clang::DeclarationNameInfo( + clang::DeclarationName( + sema.getPreprocessor().getIdentifierInfo(*name)), + clang::SourceLocation()), + clang::Sema::LookupNameKind::LookupOrdinaryName); + + bool found = sema.LookupQualifiedName( + lookup, ast->getASTContext().getTranslationUnitDecl()); + + if (lookup.isClassLookup()) { + // TODO: To support class lookup, also return the AccessKind for storage. + context.TODO(loc_id, "Unsupported: Lookup in Class"); + return std::nullopt; + } + + if (!found) { + return std::nullopt; + } + + return lookup; +} + +// Imports a function declaration from Clang to Carbon. If successful, returns +// the new Carbon function declaration `InstId`. +static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_id, + SemIR::NameScopeId scope_id, + SemIR::NameId name_id, + const clang::FunctionDecl* clang_decl) + -> SemIR::InstId { + if (clang_decl->isVariadic()) { + context.TODO(loc_id, "Unsupported: Variadic function"); + return SemIR::ErrorInst::SingletonInstId; + } + if (!clang_decl->isGlobal()) { + context.TODO(loc_id, "Unsupported: Non-global function"); + return SemIR::ErrorInst::SingletonInstId; + } + if (clang_decl->getTemplatedKind() != clang::FunctionDecl::TK_NonTemplate) { + context.TODO(loc_id, "Unsupported: Template function"); + return SemIR::ErrorInst::SingletonInstId; + } + if (!clang_decl->param_empty()) { + context.TODO(loc_id, "Unsupported: Function with parameters"); + return SemIR::ErrorInst::SingletonInstId; + } + if (!clang_decl->getReturnType()->isVoidType()) { + context.TODO(loc_id, "Unsupported: Function with non-void return type"); + return SemIR::ErrorInst::SingletonInstId; + } + + auto function_decl = SemIR::FunctionDecl{ + SemIR::TypeId::None, SemIR::FunctionId::None, SemIR::InstBlockId::Empty}; + auto decl_id = AddPlaceholderInst( + context, SemIR::LocIdAndInst(Parse::NodeId::None, function_decl)); + + auto function_info = SemIR::Function{ + {.name_id = name_id, + .parent_scope_id = scope_id, + .generic_id = SemIR::GenericId::None, + .first_param_node_id = Parse::NodeId::None, + .last_param_node_id = Parse::NodeId::None, + .pattern_block_id = SemIR::InstBlockId::Empty, + .implicit_param_patterns_id = SemIR::InstBlockId::Empty, + .param_patterns_id = SemIR::InstBlockId::Empty, + .call_params_id = SemIR::InstBlockId::Empty, + .is_extern = false, + .extern_library_id = SemIR::LibraryNameId::None, + .non_owning_decl_id = SemIR::InstId::None, + .first_owning_decl_id = decl_id, + .definition_id = SemIR::InstId::None}, + {.return_slot_pattern_id = SemIR::InstId::None, + .virtual_modifier = SemIR::FunctionFields::VirtualModifier::None, + .self_param_id = SemIR::InstId::None}}; + + function_decl.function_id = context.functions().Add(function_info); + + function_decl.type_id = GetFunctionType(context, function_decl.function_id, + SemIR::SpecificId::None); + + ReplaceInstBeforeConstantUse(context, decl_id, function_decl); + + return decl_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 { + if (const auto* clang_function_decl = + clang::dyn_cast(clang_decl)) { + return ImportFunctionDecl(context, loc_id, scope_id, name_id, + clang_function_decl); + } + + context.TODO(loc_id, llvm::formatv("Unsupported: Declaration type {0}", + clang_decl->getDeclKindName()) + .str()); + return SemIR::InstId::None; +} + +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); + if (!lookup) { + return SemIR::InstId::None; + } + + DiagnosticAnnotationScope annotate_diagnostics( + &context.emitter(), [&](auto& builder) { + CARBON_DIAGNOSTIC(InCppNameLookup, Note, + "in `Cpp` name lookup for `{0}`", SemIR::NameId); + builder.Note(loc_id, InCppNameLookup, name_id); + }); + + if (!lookup->isSingleResult()) { + context.TODO(loc_id, + llvm::formatv("Unsupported: Lookup succeeded but couldn't " + "find a single result; LookupResultKind: {0}", + lookup->getResultKind()) + .str()); + return SemIR::ErrorInst::SingletonInstId; + } + + return ImportNameDecl(context, loc_id, scope_id, name_id, + lookup->getFoundDecl()); } } // namespace Carbon::Check diff --git a/toolchain/check/import_cpp.h b/toolchain/check/import_cpp.h index 886a983f0a5e..b9303c6569fe 100644 --- a/toolchain/check/import_cpp.h +++ b/toolchain/check/import_cpp.h @@ -11,11 +11,19 @@ namespace Carbon::Check { -// Generates a C++ header that includes the imported cpp files, parses it and -// report errors and warnings. If successful, adds a `Cpp` namespace. +// Generates a C++ header that includes the imported cpp files, parses it, +// generates the AST from it and links `SemIR::File` to it. Report C++ errors +// and warnings. If successful, adds a `Cpp` namespace and returns the AST. auto ImportCppFiles(Context& context, llvm::StringRef importing_file_path, llvm::ArrayRef imports, - llvm::IntrusiveRefCntPtr fs) -> void; + llvm::IntrusiveRefCntPtr fs) + -> std::unique_ptr; + +// Looks up the given name in the Clang AST generated when importing C++ code. +// If successful, generates the instruction and returns the new `InstId`. +auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id, + SemIR::NameScopeId scope_id, SemIR::NameId name_id) + -> SemIR::InstId; } // namespace Carbon::Check diff --git a/toolchain/check/name_lookup.cpp b/toolchain/check/name_lookup.cpp index 2662355cf573..7482e952ee8a 100644 --- a/toolchain/check/name_lookup.cpp +++ b/toolchain/check/name_lookup.cpp @@ -6,9 +6,11 @@ #include "toolchain/check/generic.h" #include "toolchain/check/import.h" +#include "toolchain/check/import_cpp.h" #include "toolchain/check/import_ref.h" #include "toolchain/check/type_completion.h" #include "toolchain/diagnostics/format_providers.h" +#include "toolchain/sem_ir/name_scope.h" namespace Carbon::Check { @@ -150,6 +152,18 @@ auto LookupNameInExactScope(Context& context, SemIR::LocId loc_id, scope.import_ir_scopes(), name_id), SemIR::AccessKind::Public); } + + if (scope.is_cpp_scope()) { + SemIR::InstId imported_inst_id = + ImportNameFromCpp(context, loc_id, scope_id, name_id); + if (imported_inst_id.has_value()) { + SemIR::ScopeLookupResult result = SemIR::ScopeLookupResult::MakeFound( + imported_inst_id, SemIR::AccessKind::Public); + scope.AddRequired({.name_id = name_id, .result = result}); + return result; + } + } + return SemIR::ScopeLookupResult::MakeNotFound(); } diff --git a/toolchain/check/testdata/interop/cpp/no_prelude/function_decl.carbon b/toolchain/check/testdata/interop/cpp/no_prelude/function_decl.carbon index f4deda52baaa..1b8e5e60441e 100644 --- a/toolchain/check/testdata/interop/cpp/no_prelude/function_decl.carbon +++ b/toolchain/check/testdata/interop/cpp/no_prelude/function_decl.carbon @@ -18,8 +18,262 @@ library "[[@TEST_NAME]]"; import Cpp library "function_decl.h"; +fn MyF() { + Cpp.foo(); +} + +// --- fail_import_function_decl_use_different_name.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "function_decl.h"; + +fn MyF() { + // CHECK:STDERR: fail_import_function_decl_use_different_name.carbon:[[@LINE+4]]:3: error: member name `bar` not found in `Cpp` [MemberNameNotFoundInScope] + // CHECK:STDERR: Cpp.bar(); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + Cpp.bar(); +} + +// --- function_special_name_decl.h + +void base(); + +// --- fail_import_function_special_name_decl.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "function_special_name_decl.h"; + +fn MyF() { + // CHECK:STDERR: fail_import_function_special_name_decl.carbon:[[@LINE+4]]:3: error: member name `base` not found in `Cpp` [MemberNameNotFoundInScope] + // CHECK:STDERR: Cpp.base(); + // CHECK:STDERR: ^~~~~~~~ + // CHECK:STDERR: + Cpp.base(); +} + +// --- import_function_escaped_special_name_decl.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "function_special_name_decl.h"; + +fn MyF() { + Cpp.r#base(); +} + +// --- overloaded_function_decl.h + +void foo(); +void foo(int value); + +// --- fail_import_overloaded_function_decl.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "overloaded_function_decl.h"; + +fn F() { + // CHECK:STDERR: fail_import_overloaded_function_decl.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: Lookup succeeded but couldn't find a single result; LookupResultKind: 3` [SemanticsTodo] + // CHECK:STDERR: Cpp.foo(); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: fail_import_overloaded_function_decl.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup] + // CHECK:STDERR: Cpp.foo(); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + Cpp.foo(); +} + +// --- variadic_function_decl.h + +void foo(int...); + +// --- fail_import_variadic_function_decl.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "variadic_function_decl.h"; + +fn F() { + // CHECK:STDERR: fail_import_variadic_function_decl.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: Variadic function` [SemanticsTodo] + // CHECK:STDERR: Cpp.foo(); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: fail_import_variadic_function_decl.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup] + // CHECK:STDERR: Cpp.foo(); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + Cpp.foo(); +} + +// --- non_global_function_decl.h + +static void foo(); + +// --- fail_import_non_global_function_decl.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "non_global_function_decl.h"; + +fn F() { + // CHECK:STDERR: fail_import_non_global_function_decl.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: Non-global function` [SemanticsTodo] + // CHECK:STDERR: Cpp.foo(); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: fail_import_non_global_function_decl.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup] + // CHECK:STDERR: Cpp.foo(); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + Cpp.foo(); +} + +// TODO: Test that template functions are unsupported. +// This is not tested because template functions are not considered a single result when doing lookup. + +// --- parameterized_function_decl.h + +void foo(int); + +// --- fail_import_parameterized_function_decl.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "parameterized_function_decl.h"; + +fn F() { + // CHECK:STDERR: fail_import_parameterized_function_decl.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: Function with parameters` [SemanticsTodo] + // CHECK:STDERR: Cpp.foo(); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: fail_import_parameterized_function_decl.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup] + // CHECK:STDERR: Cpp.foo(); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + Cpp.foo(); +} + +// --- non_void_return_function_decl.h + +int foo(); + +// --- fail_import_non_void_return_function_decl.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "non_void_return_function_decl.h"; + +fn F() { + // CHECK:STDERR: fail_import_non_void_return_function_decl.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: Function with non-void return type` [SemanticsTodo] + // CHECK:STDERR: Cpp.foo(); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: fail_import_non_void_return_function_decl.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup] + // CHECK:STDERR: Cpp.foo(); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + Cpp.foo(); +} + +// --- non_function_decl.h + +class foo; + +// --- fail_import_non_function_decl.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "non_function_decl.h"; + +fn F() { + // CHECK:STDERR: fail_import_non_function_decl.carbon:[[@LINE+11]]:3: error: semantics TODO: `Unsupported: Declaration type CXXRecord` [SemanticsTodo] + // CHECK:STDERR: Cpp.foo(); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: fail_import_non_function_decl.carbon:[[@LINE+8]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup] + // CHECK:STDERR: Cpp.foo(); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + // CHECK:STDERR: fail_import_non_function_decl.carbon:[[@LINE+4]]:3: error: member name `foo` not found in `Cpp` [MemberNameNotFoundInScope] + // CHECK:STDERR: Cpp.foo(); + // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: + Cpp.foo(); +} + // CHECK:STDOUT: --- import_function_decl.carbon // CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %MyF.type: type = fn_type @MyF [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %MyF: %MyF.type = struct_value () [concrete] +// CHECK:STDOUT: %foo.type: type = fn_type @foo [concrete] +// CHECK:STDOUT: %foo: %foo.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .foo = @MyF.%foo.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: .MyF = %MyF.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "function_decl.h" +// CHECK:STDOUT: } +// CHECK:STDOUT: %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @MyF() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Cpp.ref: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {} {} +// CHECK:STDOUT: %foo.ref: %foo.type = name_ref foo, %foo.decl [concrete = constants.%foo] +// CHECK:STDOUT: %foo.call: init %empty_tuple.type = call %foo.ref() +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @foo[](); +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_import_function_decl_use_different_name.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %MyF.type: type = fn_type @MyF [concrete] +// CHECK:STDOUT: %MyF: %MyF.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .bar = +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: .MyF = %MyF.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "function_decl.h" +// CHECK:STDOUT: } +// CHECK:STDOUT: %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @MyF() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Cpp.ref: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %bar.ref: = name_ref bar, [concrete = ] +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_import_function_special_name_decl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %MyF.type: type = fn_type @MyF [concrete] +// CHECK:STDOUT: %MyF: %MyF.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] {} // CHECK:STDOUT: } @@ -27,9 +281,242 @@ import Cpp library "function_decl.h"; // CHECK:STDOUT: file { // CHECK:STDOUT: package: = namespace [concrete] { // CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: .MyF = %MyF.decl // CHECK:STDOUT: } // CHECK:STDOUT: %Cpp.import_cpp = import_cpp { -// CHECK:STDOUT: import Cpp "function_decl.h" +// CHECK:STDOUT: import Cpp "function_special_name_decl.h" +// CHECK:STDOUT: } +// CHECK:STDOUT: %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @MyF() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Cpp.ref: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %base.ref: = name_ref base, [concrete = ] +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- import_function_escaped_special_name_decl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %MyF.type: type = fn_type @MyF [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %MyF: %MyF.type = struct_value () [concrete] +// CHECK:STDOUT: %base.type: type = fn_type @base [concrete] +// CHECK:STDOUT: %base: %base.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .r#base = @MyF.%base.decl // CHECK:STDOUT: } // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: .MyF = %MyF.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "function_special_name_decl.h" +// CHECK:STDOUT: } +// CHECK:STDOUT: %MyF.decl: %MyF.type = fn_decl @MyF [concrete = constants.%MyF] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @MyF() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Cpp.ref: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %base.decl: %base.type = fn_decl @base [concrete = constants.%base] {} {} +// CHECK:STDOUT: %base.ref: %base.type = name_ref r#base, %base.decl [concrete = constants.%base] +// CHECK:STDOUT: %base.call: init %empty_tuple.type = call %base.ref() +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @base[](); +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_import_overloaded_function_decl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .foo = +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "overloaded_function_decl.h" +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Cpp.ref: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %foo.ref: = name_ref foo, [concrete = ] +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_import_variadic_function_decl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .foo = +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "variadic_function_decl.h" +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Cpp.ref: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %foo.ref: = name_ref foo, [concrete = ] +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_import_non_global_function_decl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .foo = +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "non_global_function_decl.h" +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Cpp.ref: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %foo.ref: = name_ref foo, [concrete = ] +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_import_parameterized_function_decl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .foo = +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "parameterized_function_decl.h" +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Cpp.ref: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %foo.ref: = name_ref foo, [concrete = ] +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_import_non_void_return_function_decl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .foo = +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "non_void_return_function_decl.h" +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Cpp.ref: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %foo.ref: = name_ref foo, [concrete = ] +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_import_non_function_decl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .foo = +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "non_function_decl.h" +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Cpp.ref: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %foo.ref: = name_ref foo, [concrete = ] +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index d5e72449730a..b44b7165fb74 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -318,6 +318,7 @@ CARBON_DIAGNOSTIC_KIND(QualifiedDeclInUndefinedInterfaceScope) // Name lookup. CARBON_DIAGNOSTIC_KIND(FromExtendHere) +CARBON_DIAGNOSTIC_KIND(InCppNameLookup) CARBON_DIAGNOSTIC_KIND(InNameLookup) CARBON_DIAGNOSTIC_KIND(NameAmbiguousDueToExtend) CARBON_DIAGNOSTIC_KIND(NameNotFound) diff --git a/toolchain/driver/compile_subcommand.cpp b/toolchain/driver/compile_subcommand.cpp index 6743f38491f1..de9909854b1e 100644 --- a/toolchain/driver/compile_subcommand.cpp +++ b/toolchain/driver/compile_subcommand.cpp @@ -412,6 +412,7 @@ class CompilationUnit { std::optionalconst Parse::TreeAndSubtrees&>> tree_and_subtrees_getter_; std::optional sem_ir_; + std::unique_ptr cpp_ast_; std::unique_ptr llvm_context_; std::unique_ptr module_; }; @@ -509,7 +510,8 @@ auto CompilationUnit::GetCheckUnit(SemIR::CheckIRId check_ir_id) .value_stores = &value_stores_, .timings = timings_ ? &*timings_ : nullptr, .tree_and_subtrees_getter = *tree_and_subtrees_getter_, - .sem_ir = &*sem_ir_}; + .sem_ir = &*sem_ir_, + .cpp_ast = &cpp_ast_}; } auto CompilationUnit::PostCheck() -> void { diff --git a/toolchain/sem_ir/BUILD b/toolchain/sem_ir/BUILD index 7043592ac9f8..48f34e4cedfb 100644 --- a/toolchain/sem_ir/BUILD +++ b/toolchain/sem_ir/BUILD @@ -126,6 +126,7 @@ cc_library( "//toolchain/lex:token_kind", "//toolchain/parse:node_kind", "//toolchain/parse:tree", + "@llvm-project//clang:frontend", "@llvm-project//llvm:Support", ], ) diff --git a/toolchain/sem_ir/file.h b/toolchain/sem_ir/file.h index 257b04fc602f..dd63814a9189 100644 --- a/toolchain/sem_ir/file.h +++ b/toolchain/sem_ir/file.h @@ -5,6 +5,7 @@ #ifndef CARBON_TOOLCHAIN_SEM_IR_FILE_H_ #define CARBON_TOOLCHAIN_SEM_IR_FILE_H_ +#include "clang/Frontend/ASTUnit.h" #include "common/error.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/iterator_range.h" @@ -186,6 +187,10 @@ class File : public Printable { auto import_cpps() const -> const ValueStore& { return import_cpps_; } + auto cpp_ast() -> clang::ASTUnit* { return cpp_ast_; } + // TODO: When the AST can be created before creating `File`, initialize the + // pointer in the constructor and remove this function. + auto set_cpp_ast(clang::ASTUnit* cpp_ast) -> void { cpp_ast_ = cpp_ast; } auto names() const -> NameStoreWrapper { return NameStoreWrapper(&identifiers()); } @@ -301,6 +306,10 @@ class File : public Printable { // List of Cpp imports. ValueStore import_cpps_; + // The Clang AST to use when looking up `Cpp` names. Null if there are no + // `Cpp` imports. + clang::ASTUnit* cpp_ast_ = nullptr; + // Type blocks within the IR. These reference entries in types_. Storage for // the data is provided by allocator_. BlockValueStore type_blocks_; diff --git a/toolchain/sem_ir/name_scope.h b/toolchain/sem_ir/name_scope.h index 0c757d06d352..c8fff3040733 100644 --- a/toolchain/sem_ir/name_scope.h +++ b/toolchain/sem_ir/name_scope.h @@ -212,6 +212,12 @@ class NameScope : public Printable { is_closed_import_ = is_closed_import; } + auto is_cpp_scope() const -> bool { return is_cpp_scope_; } + + auto set_is_cpp_scope(bool is_cpp_scope) -> void { + is_cpp_scope_ = is_cpp_scope; + } + // Returns true if this name scope describes an imported package. auto is_imported_package() const -> bool { return is_closed_import() && parent_scope_id() == NameScopeId::Package; @@ -272,6 +278,9 @@ class NameScope : public Printable { // True if this is a closed namespace created by importing a package. bool is_closed_import_ = false; + // True if this is the `Cpp` namescope used when importing C++ code. + bool is_cpp_scope_ = false; + // Imported IR scopes that compose this namespace. This will be empty for // scopes that correspond to the current package. llvm::SmallVector, 0> import_ir_scopes_;