diff --git a/toolchain/base/clang_invocation.cpp b/toolchain/base/clang_invocation.cpp index 1677c5a9d0a1..eefc3ad89d2f 100644 --- a/toolchain/base/clang_invocation.cpp +++ b/toolchain/base/clang_invocation.cpp @@ -100,6 +100,10 @@ auto BuildClangInvocation(Diagnostics::Consumer& consumer, } if (invocation) { + // Track submodule visibility in the preprocessor and Sema. + invocation->getLangOpts().Modules = true; + invocation->getLangOpts().ModulesLocalVisibility = true; + // Do not emit Clang's name and version as the creator of the output file. invocation->getCodeGenOpts().EmitVersionIdentMetadata = false; invocation->getCodeGenOpts().DiscardValueNames = false; diff --git a/toolchain/check/cpp/generate_ast.cpp b/toolchain/check/cpp/generate_ast.cpp index ff3efedeacc2..36780e94e290 100644 --- a/toolchain/check/cpp/generate_ast.cpp +++ b/toolchain/check/cpp/generate_ast.cpp @@ -486,8 +486,7 @@ auto CarbonExternalASTSource::CompleteType(clang::TagDecl* tag_decl) -> void { bool is_virtual = false; bool is_base_of_class = true; clang::CXXBaseSpecifier base( - clang::SourceRange(base_loc, base_loc), is_virtual, is_base_of_class, - clang::AS_public, + base_loc, is_virtual, is_base_of_class, clang::AS_public, context_->ast_context().getTrivialTypeSourceInfo(base_type, base_loc), /*EllipsisLoc=*/clang::SourceLocation()); clang::CXXBaseSpecifier* bases[1] = {&base}; @@ -617,30 +616,96 @@ static auto ParseTopLevelDecls(clang::Parser& parser, } } -// Injects the C++ code in `buffer` into the Clang preprocessor and parses it -// as top-level declarations. Returns true on success, false if entering the -// source file fails. -static auto InjectAndParse(Context& context, - std::unique_ptr buffer) -> bool { +// Generate a Clang module corresponding to the current Carbon file. +static auto CreateModuleForFile(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 + // the filename. + auto& module_map = domain.clang_instance() + .getPreprocessor() + .getHeaderSearchInfo() + .getModuleMap(); + return module_map.createModule(file.filename(), /*Parent=*/nullptr, + /*IsFramework=*/false, /*IsExplicit=*/true); +} + +// Parse the tokens that have been injected into the preprocessor in the given +// context. +static auto ParseInjectedTokens(CppContext& cpp_context) -> void { + clang::Sema& sema = cpp_context.sema(); + clang::Parser& parser = cpp_context.parser(); + CARBON_CHECK(parser.getCurToken().is(clang::tok::eof)); + parser.ConsumeToken(); + ParseTopLevelDecls(parser, sema.getASTConsumer()); +} + +// Injects the C++ code in `buffer` into the Clang preprocessor. Returns the +// file ID of the injected buffer. +static auto InjectBuffer(CppContext& cpp_context, llvm::StringRef contents, + llvm::StringRef name, clang::SourceLocation import_loc) + -> clang::FileID { + auto buffer = llvm::MemoryBuffer::getMemBufferCopy(contents, name); + + clang::Preprocessor& preprocessor = cpp_context.sema().getPreprocessor(); + clang::FileID file_id = + preprocessor.getSourceManager().createFileID(std::move(buffer)); + if (preprocessor.EnterSourceFile(file_id, nullptr, import_loc)) { + CARBON_FATAL("Failed to enter buffer"); + } + + 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); - clang::Sema& sema = cpp_context->sema(); - clang::Preprocessor& preprocessor = sema.getPreprocessor(); - clang::Parser& parser = cpp_context->parser(); + // Inject the imports-as-#includes buffer. + auto file_id = InjectBuffer(*cpp_context, + GenerateCppIncludesHeaderCode(context, imports), + "", clang::SourceLocation()); - clang::FileID file_id = - preprocessor.getSourceManager().createFileID(std::move(buffer)); - if (preprocessor.EnterSourceFile(file_id, nullptr, clang::SourceLocation())) { - return false; + // 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); + preprocessor.EnterSubmodule(mod, loc, /*ForPragma=*/false); + preprocessor.EnterAnnotationToken(loc, clang::tok::annot_module_begin, mod); + + ParseInjectedTokens(*cpp_context); +} + +// Leave the current Clang module. +static auto LeaveModule(Context& 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* mod = preprocessor.LeaveSubmodule(/*ForPragma=*/false); + CARBON_CHECK(mod); + + // We *should* only need to enter one annotation token, but Clang has some + // error recovery where Sema enters and never leaves an additional module if + // 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(); + CARBON_CHECK(sema_mod, "Sema prematurely exited Carbon module"); + + preprocessor.EnterAnnotationToken(loc, clang::tok::annot_module_end, + sema_mod); + ParseInjectedTokens(*cpp_context); + if (sema_mod == mod) { + break; + } } - - if (parser.getCurToken().is(clang::tok::eof)) { - parser.ConsumeToken(); - } - ParseTopLevelDecls(parser, sema.getASTConsumer()); - - return true; } namespace { @@ -783,6 +848,11 @@ auto InitializeCppDomain( auto& ast = clang_instance->getASTContext(); + // Create an AST reader before we set up our own source. Clang does this + // automatically later if we don't do it now, and will overwrite our external + // source with its own when it does so. + clang_instance->createASTReader(); + // Always build a multiplex source, even if there's only one child // source. During lowering, the `CarbonExternalASTSource` can no longer be // used (because it uses `Check::Context`), so a `ReadOnlyASTSource` is @@ -857,11 +927,9 @@ auto GenerateAst(Context& context, // Map the package scope to the Carbon namespace. ast_source->BuildCarbonNamespace(); - // Inject the imports-as-#includes buffer. - std::string includes = GenerateCppIncludesHeaderCode(context, imports); - auto buffer = - llvm::MemoryBuffer::getMemBufferCopy(includes, ""); - return InjectAndParse(context, std::move(buffer)); + // Parse the imports-as-#includes buffer. + ParseImports(context, imports); + return true; } auto InjectAstFromInlineCode(Context& context, SemIR::LocId loc_id, @@ -874,11 +942,11 @@ auto InjectAstFromInlineCode(Context& context, SemIR::LocId loc_id, context.parse_tree().node_token(loc_id.node_id()), source_code); - auto buffer = llvm::MemoryBuffer::getMemBufferCopy(code_stream.TakeStr(), - ""); // Clang will have generated a suitable error if this fails. There's nothing // more to do here. - InjectAndParse(context, std::move(buffer)); + InjectBuffer(*cpp_context, code_stream.TakeStr(), "", + GetCppLocation(context, loc_id)); + ParseInjectedTokens(*cpp_context); } auto FinishAst(Context& context) -> void { @@ -886,6 +954,19 @@ auto FinishAst(Context& context) -> void { return; } + // Leave the module we entered to encapsulate the contents of this Carbon + // file. + auto end_loc_id = + SemIR::LocId(*(context.sem_ir().parse_tree().postorder().end() - 1)); + // Shuffle the end of file location back by one character to work around a + // Clang bug: if we give Clang the end-of-file location, it will replace the + // location with the include location without checking whether the file was + // 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, + GetCppLocation(context, end_loc_id).getLocWithOffset(-1)); + // Finalize the per-Context AST fragment. The final ActOnEndOfTranslationUnit // call for the CppDomain is performed in FinalizeCppDomain once all files // sharing the domain have been checked. 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 7848e7ac6a9b..d559b238b5de 100644 --- a/toolchain/check/testdata/interop/cpp/basics/import/bad_import.carbon +++ b/toolchain/check/testdata/interop/cpp/basics/import/bad_import.carbon @@ -20,21 +20,33 @@ library "[[@TEST_NAME]]"; // CHECK:STDERR: import Cpp library ""; -// --- fail_import_cpp_library_file_with_quotes.carbon +// --- fail_import_cpp_library_nonexistent.carbon library "[[@TEST_NAME]]"; -// CHECK:STDERR: fail_import_cpp_library_file_with_quotes.carbon:[[@LINE+14]]:10: error: '\' file not found [CppInteropParseError] -// CHECK:STDERR: 18 | #include "\"foo.h\"" +// CHECK:STDERR: fail_import_cpp_library_nonexistent.carbon:[[@LINE+4]]:10: error: 'does_not_exist.h' file not found [CppInteropParseError] +// CHECK:STDERR: 8 | #include "does_not_exist.h" +// CHECK:STDERR: | ^~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +import Cpp library "does_not_exist.h"; + +// --- fail_todo_import_cpp_library_file_with_quotes.carbon + +library "[[@TEST_NAME]]"; + +// TODO: Translate the file to a `#include` that includes the named file +// properly. `\` escapes aren't processed in regular `#include`s. +// CHECK:STDERR: fail_todo_import_cpp_library_file_with_quotes.carbon:[[@LINE+14]]:10: error: '\' file not found [CppInteropParseError] +// CHECK:STDERR: 20 | #include "\"foo.h\"" // CHECK:STDERR: | ^~~ // CHECK:STDERR: -// CHECK:STDERR: fail_import_cpp_library_file_with_quotes.carbon:[[@LINE+10]]:13: error: invalid suffix on literal; C++11 requires a space between literal and identifier [CppInteropParseError] -// CHECK:STDERR: 18 | #include "\"foo.h\"" +// CHECK:STDERR: fail_todo_import_cpp_library_file_with_quotes.carbon:[[@LINE+10]]:13: error: invalid suffix on literal; C++11 requires a space between literal and identifier [CppInteropParseError] +// CHECK:STDERR: 20 | #include "\"foo.h\"" // CHECK:STDERR: | ^ // CHECK:STDERR: | {{}} // CHECK:STDERR: -// CHECK:STDERR: fail_import_cpp_library_file_with_quotes.carbon:[[@LINE+5]]:13: warning: extra tokens at end of #include directive [CppInteropParseWarning] -// CHECK:STDERR: 18 | #include "\"foo.h\"" +// CHECK:STDERR: fail_todo_import_cpp_library_file_with_quotes.carbon:[[@LINE+5]]:13: warning: extra tokens at end of #include directive [CppInteropParseWarning] +// CHECK:STDERR: 20 | #include "\"foo.h\"" // CHECK:STDERR: | ^ // CHECK:STDERR: | // // CHECK:STDERR: @@ -42,6 +54,8 @@ import Cpp library "\"foo.h\""; // --- todo_fail_unterminated_import_inline.carbon +library "[[@TEST_NAME]]"; + import Cpp inline '''c++ void f() { '''; diff --git a/toolchain/check/testdata/interop/cpp/basics/share_ast.carbon b/toolchain/check/testdata/interop/cpp/basics/share_ast.carbon index 32c473922980..d3efea075c01 100644 --- a/toolchain/check/testdata/interop/cpp/basics/share_ast.carbon +++ b/toolchain/check/testdata/interop/cpp/basics/share_ast.carbon @@ -33,12 +33,16 @@ fn CallA() -> i32 { return Cpp.GetA(); } -// --- todo_a_leaks.carbon +// --- fail_a_does_not_leak.carbon library "[[@TEST_NAME]]"; import Cpp; fn CallLeakedA() -> i32 { - // TODO: This should not be found as we have not imported "a.h". + // This should not be found as we have not imported "a.h". + // CHECK:STDERR: fail_a_does_not_leak.carbon:[[@LINE+4]]:10: error: member name `GetA` not found in `Cpp` + // CHECK:STDERR: return Cpp.GetA(); + // CHECK:STDERR: ^~~~~~~~ + // CHECK:STDERR: return Cpp.GetA(); } diff --git a/toolchain/check/testdata/interop/cpp/function/export/thunk_ast.carbon b/toolchain/check/testdata/interop/cpp/function/export/thunk_ast.carbon index d5ae1866e152..0e77bb335775 100644 --- a/toolchain/check/testdata/interop/cpp/function/export/thunk_ast.carbon +++ b/toolchain/check/testdata/interop/cpp/function/export/thunk_ast.carbon @@ -38,16 +38,17 @@ fn F(i: i32) -> i32 { // CHECK:STDOUT: | | `-DeclRefExpr {{0x[a-f0-9]+}} 'int' lvalue Var {{0x[a-f0-9]+}} 'return_storage' 'int' // CHECK:STDOUT: | |-AlwaysInlineAttr {{0x[a-f0-9]+}} <> Implicit always_inline // CHECK:STDOUT: | `-InternalLinkageAttr {{0x[a-f0-9]+}} <> Implicit -// CHECK:STDOUT: `-FunctionDecl {{0x[a-f0-9]+}} line:37:5 G 'int (int)' external-linkage -// CHECK:STDOUT: |-ParmVarDecl {{0x[a-f0-9]+}} col:11 used i 'int' -// CHECK:STDOUT: `-CompoundStmt {{0x[a-f0-9]+}} -// CHECK:STDOUT: `-ReturnStmt {{0x[a-f0-9]+}} -// CHECK:STDOUT: `-CallExpr {{0x[a-f0-9]+}} 'int' -// CHECK:STDOUT: |-ImplicitCastExpr {{0x[a-f0-9]+}} 'int (*)(int)' -// CHECK:STDOUT: | `-DeclRefExpr {{0x[a-f0-9]+}} '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]+}} 'int' -// CHECK:STDOUT: `-DeclRefExpr {{0x[a-f0-9]+}} 'int' lvalue ParmVar {{0x[a-f0-9]+}} 'i' 'int' +// CHECK:STDOUT: |-FunctionDecl {{0x[a-f0-9]+}} line:38:5 in thunk_with_args_and_return.carbon hidden G 'int (int)' external-linkage +// CHECK:STDOUT: | |-ParmVarDecl {{0x[a-f0-9]+}} col:11 in thunk_with_args_and_return.carbon hidden used i 'int' +// CHECK:STDOUT: | `-CompoundStmt {{0x[a-f0-9]+}} +// CHECK:STDOUT: | `-ReturnStmt {{0x[a-f0-9]+}} +// CHECK:STDOUT: | `-CallExpr {{0x[a-f0-9]+}} 'int' +// CHECK:STDOUT: | |-ImplicitCastExpr {{0x[a-f0-9]+}} 'int (*)(int)' +// CHECK:STDOUT: | | `-DeclRefExpr {{0x[a-f0-9]+}} '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]+}} 'int' +// CHECK:STDOUT: | `-DeclRefExpr {{0x[a-f0-9]+}} 'int' lvalue ParmVar {{0x[a-f0-9]+}} 'i' 'int' +// CHECK:STDOUT: `-ImportDecl {{0x[a-f0-9]+}} col:4 in thunk_with_args_and_return.carbon implicit thunk_with_args_and_return.carbon return i; } 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 1d4a05d52438..2dd56cd84241 100644 --- a/toolchain/check/testdata/interop/cpp/function/import/thunk_ast.carbon +++ b/toolchain/check/testdata/interop/cpp/function/import/thunk_ast.carbon @@ -18,24 +18,21 @@ // --- 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 used foo 'auto (short) -> void' external-linkage -// CHECK:STDOUT: | `-ParmVarDecl {{0x[a-f0-9]+}} col:16 a 'short' -// CHECK:STDOUT: `-FunctionDecl {{0x[a-f0-9]+}} col:6 foo__carbon_thunk 'void (short * _Nonnull)' inline internal-linkage -// CHECK:STDOUT: |-ParmVarDecl {{0x[a-f0-9]+}} col:6 used a 'short * _Nonnull':'short *' -// CHECK:STDOUT: |-ReturnStmt {{0x[a-f0-9]+}} -// CHECK:STDOUT: | `-CallExpr {{0x[a-f0-9]+}} 'void' -// CHECK:STDOUT: | |-ImplicitCastExpr {{0x[a-f0-9]+}} 'auto (*)(short) -> void' -// CHECK:STDOUT: | | `-DeclRefExpr {{0x[a-f0-9]+}} 'auto (short) -> void' Function {{0x[a-f0-9]+}} 'foo' 'auto (short) -> void' -// CHECK:STDOUT: | `-ImplicitCastExpr {{0x[a-f0-9]+}} 'short' -// CHECK:STDOUT: | `-UnaryOperator {{0x[a-f0-9]+}} 'short' lvalue prefix '*' cannot overflow -// CHECK:STDOUT: | `-ImplicitCastExpr {{0x[a-f0-9]+}} 'short * _Nonnull':'short *' -// CHECK:STDOUT: | `-DeclRefExpr {{0x[a-f0-9]+}} 'short * _Nonnull':'short *' lvalue ParmVar {{0x[a-f0-9]+}} 'a' 'short * _Nonnull':'short *' -// CHECK:STDOUT: |-AlwaysInlineAttr {{0x[a-f0-9]+}} <> Implicit always_inline -// CHECK:STDOUT: |-InternalLinkageAttr {{0x[a-f0-9]+}} <> Implicit -// CHECK:STDOUT: `-AsmLabelAttr {{0x[a-f0-9]+}} Implicit "_Z3foos.carbon_thunk._" -// CHECK:STDOUT: TranslationUnitDecl {{0x[a-f0-9]+}} <> -// CHECK:STDOUT: |-EmptyDecl {{0x[a-f0-9]+}} <:1:1> col:1 -// CHECK:STDOUT: |-NamespaceDecl {{0x[a-f0-9]+}} <> Carbon external-linkage +// 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: | |-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' +// CHECK:STDOUT: | | |-ImplicitCastExpr {{0x[a-f0-9]+}} 'auto (*)(short) -> void' +// CHECK:STDOUT: | | | `-DeclRefExpr {{0x[a-f0-9]+}} 'auto (short) -> void' Function {{0x[a-f0-9]+}} 'foo' 'auto (short) -> void' +// CHECK:STDOUT: | | `-ImplicitCastExpr {{0x[a-f0-9]+}} 'short' +// CHECK:STDOUT: | | `-UnaryOperator {{0x[a-f0-9]+}} 'short' lvalue prefix '*' cannot overflow +// CHECK:STDOUT: | | `-ImplicitCastExpr {{0x[a-f0-9]+}} 'short * _Nonnull':'short *' +// CHECK:STDOUT: | | `-DeclRefExpr {{0x[a-f0-9]+}} 'short * _Nonnull':'short *' lvalue ParmVar {{0x[a-f0-9]+}} 'a' 'short * _Nonnull':'short *' +// CHECK:STDOUT: | |-AlwaysInlineAttr {{0x[a-f0-9]+}} <> Implicit always_inline +// CHECK:STDOUT: | |-InternalLinkageAttr {{0x[a-f0-9]+}} <> Implicit +// CHECK:STDOUT: | `-AsmLabelAttr {{0x[a-f0-9]+}} Implicit "_Z3foos.carbon_thunk._" // --- import_thunk_required.carbon @@ -47,20 +44,24 @@ fn F() { Cpp.foo(1 as i16); } +// CHECK:STDOUT: `-ImportDecl {{0x[a-f0-9]+}} col:104 in import_thunk_required.carbon implicit import_thunk_required.carbon +// CHECK:STDOUT: TranslationUnitDecl {{0x[a-f0-9]+}} <> +// CHECK:STDOUT: |-EmptyDecl {{0x[a-f0-9]+}} <:1:1> col:1 +// CHECK:STDOUT: |-NamespaceDecl {{0x[a-f0-9]+}} <> Carbon external-linkage // --- 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 used foo 'auto () -> short' external-linkage -// CHECK:STDOUT: `-FunctionDecl {{0x[a-f0-9]+}} col:6 foo__carbon_thunk 'void (short * _Nonnull)' inline internal-linkage -// CHECK:STDOUT: |-ParmVarDecl {{0x[a-f0-9]+}} col:6 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' -// CHECK:STDOUT: | | `-ImplicitCastExpr {{0x[a-f0-9]+}} 'auto (*)() -> short' -// CHECK:STDOUT: | | `-DeclRefExpr {{0x[a-f0-9]+}} 'auto () -> short' Function {{0x[a-f0-9]+}} 'foo' 'auto () -> short' -// CHECK:STDOUT: | `-DeclRefExpr {{0x[a-f0-9]+}} 'short * _Nonnull':'short *' lvalue ParmVar {{0x[a-f0-9]+}} 'return' 'short * _Nonnull':'short *' -// CHECK:STDOUT: |-AlwaysInlineAttr {{0x[a-f0-9]+}} <> Implicit always_inline -// CHECK:STDOUT: |-InternalLinkageAttr {{0x[a-f0-9]+}} <> Implicit -// CHECK:STDOUT: `-AsmLabelAttr {{0x[a-f0-9]+}} Implicit "_Z3foov.carbon_thunk." +// 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: | |-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' +// CHECK:STDOUT: | | | `-ImplicitCastExpr {{0x[a-f0-9]+}} 'auto (*)() -> short' +// CHECK:STDOUT: | | | `-DeclRefExpr {{0x[a-f0-9]+}} 'auto () -> short' Function {{0x[a-f0-9]+}} 'foo' 'auto () -> short' +// CHECK:STDOUT: | | `-DeclRefExpr {{0x[a-f0-9]+}} 'short * _Nonnull':'short *' lvalue ParmVar {{0x[a-f0-9]+}} 'return' 'short * _Nonnull':'short *' +// CHECK:STDOUT: | |-AlwaysInlineAttr {{0x[a-f0-9]+}} <> Implicit always_inline +// CHECK:STDOUT: | |-InternalLinkageAttr {{0x[a-f0-9]+}} <> Implicit +// CHECK:STDOUT: | `-AsmLabelAttr {{0x[a-f0-9]+}} Implicit "_Z3foov.carbon_thunk." // --- import_return_thunk_required.carbon @@ -71,3 +72,5 @@ import Cpp library "return_thunk_required.h"; fn F() -> i16 { return Cpp.foo(); } + +// CHECK:STDOUT: `-ImportDecl {{0x[a-f0-9]+}} col:191 in import_return_thunk_required.carbon implicit import_return_thunk_required.carbon diff --git a/toolchain/lower/testdata/interop/cpp/clang_code_generator_callbacks.carbon b/toolchain/lower/testdata/interop/cpp/clang_code_generator_callbacks.carbon index 4a17079c1bd0..efdaca435cd7 100644 --- a/toolchain/lower/testdata/interop/cpp/clang_code_generator_callbacks.carbon +++ b/toolchain/lower/testdata/interop/cpp/clang_code_generator_callbacks.carbon @@ -121,6 +121,9 @@ void f2() { // CHECK:STDOUT: ret void // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @a, { 1, 0 } +// CHECK:STDOUT: // CHECK:STDOUT: attributes #0 = { mustprogress nounwind uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } // CHECK:STDOUT: // CHECK:STDOUT: !llvm.dbg.cu = !{!0}