From 2ac425e5911694e1243d139ae94e65fc3455e75f Mon Sep 17 00:00:00 2001 From: Geoff Romer Date: Wed, 29 Jul 2026 09:07:10 -0700 Subject: [PATCH] Test calling `std::make_unique` on a Carbon type from Carbon. (#7563) As a byproduct, this introduces a new top-level directory `integration_tests` for tests like this. This also fixes a latent bug with name mangling on Mac. --- integration_tests/BUILD | 18 ++++++++++++ integration_tests/make_unique_test.carbon | 22 +++++++++++++++ toolchain/check/cpp/export.cpp | 4 ++- toolchain/sem_ir/BUILD | 2 ++ toolchain/sem_ir/mangler.cpp | 34 ++++++++++++++++++++--- toolchain/sem_ir/mangler.h | 11 ++++++++ 6 files changed, 86 insertions(+), 5 deletions(-) create mode 100644 integration_tests/BUILD create mode 100644 integration_tests/make_unique_test.carbon diff --git a/integration_tests/BUILD b/integration_tests/BUILD new file mode 100644 index 000000000000..f858ded72d87 --- /dev/null +++ b/integration_tests/BUILD @@ -0,0 +1,18 @@ +# Part of the Carbon Language project, under the Apache License v2.0 with LLVM +# Exceptions. See /LICENSE for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# This package is for integration tests of the entire Carbon system, +# encompassing things like libraries as well as the toolchain proper. These +# will typically be execution tests, which use the toolchain to build and run a +# test binary, rather than directly checking the toolchain's output. This kind +# of testing should be used sparingly, because it's substantially more costly, +# and not applicable when the toolchain is built as a cross-compiler. + +load("//bazel/carbon_rules:defs.bzl", "carbon_binary") + +# TODO: Change this to `carbon_test` once that exists. +carbon_binary( + name = "make_unique_test", + srcs = ["make_unique_test.carbon"], +) diff --git a/integration_tests/make_unique_test.carbon b/integration_tests/make_unique_test.carbon new file mode 100644 index 000000000000..5b5b313706bc --- /dev/null +++ b/integration_tests/make_unique_test.carbon @@ -0,0 +1,22 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +import Cpp library ""; + +class C { + fn C(i: i32) -> Self { + return {.i = i}; + } + + var i: i32; +} + +fn Run() -> i32 { + var ptr: Cpp.std.unique_ptr(C) = Cpp.std.make_unique(C, 42); + if (not ptr.get().HasValue()) { + return 1; + } + let ref c: C = *ptr.get().Get(); + return if c.i == 42 then 0 else 1; +} diff --git a/toolchain/check/cpp/export.cpp b/toolchain/check/cpp/export.cpp index 399085ec6016..3c1bdfef5ea2 100644 --- a/toolchain/check/cpp/export.cpp +++ b/toolchain/check/cpp/export.cpp @@ -587,7 +587,7 @@ static auto BuildCppFunctionDeclForNonGenericCarbonFn(Context& context, SemIR::Mangler m(context.sem_ir(), context.total_ir_count(), context.mangle_string_fingerprint()); std::string mangled_name = - m.Mangle(target.function_id, SemIR::SpecificId::None); + m.MangleWithPlatform(target.function_id, SemIR::SpecificId::None); function_decl->addAttr( clang::AsmLabelAttr::Create(context.ast_context(), mangled_name)); @@ -1415,6 +1415,8 @@ auto ExportVarToCpp(Context& context, SemIR::InstId inst_id, } // Set the Carbon mangled variable name. + // TODO: do we need to apply the platform mangling, like we do for exported + // functions? SemIR::Mangler m(context.sem_ir(), context.total_ir_count(), context.mangle_string_fingerprint()); std::string mangled_name = m.MangleGlobalVariable(var_storage.pattern_id); diff --git a/toolchain/sem_ir/BUILD b/toolchain/sem_ir/BUILD index afdca88a4bf6..06fc7c26373c 100644 --- a/toolchain/sem_ir/BUILD +++ b/toolchain/sem_ir/BUILD @@ -384,6 +384,8 @@ cc_library( "//common:raw_string_ostream", "//toolchain/base:kind_switch", "@llvm-project//clang:ast", + "@llvm-project//clang:codegen", + "@llvm-project//llvm:Core", "@llvm-project//llvm:Support", ], ) diff --git a/toolchain/sem_ir/mangler.cpp b/toolchain/sem_ir/mangler.cpp index b38643022cf6..4dfc3900323a 100644 --- a/toolchain/sem_ir/mangler.cpp +++ b/toolchain/sem_ir/mangler.cpp @@ -6,7 +6,9 @@ #include +#include "clang/CodeGen/ModuleBuilder.h" #include "common/raw_string_ostream.h" +#include "llvm/IR/Module.h" #include "toolchain/base/kind_switch.h" #include "toolchain/sem_ir/entry_point.h" #include "toolchain/sem_ir/ids.h" @@ -183,12 +185,14 @@ auto Mangler::MangleInverseQualifiedNameScope(llvm::raw_ostream& os, } } -auto Mangler::Mangle(SemIR::FunctionId function_id, - SemIR::SpecificId specific_id) -> std::string { +auto Mangler::MangleImpl(SemIR::FunctionId function_id, + SemIR::SpecificId specific_id, llvm::raw_ostream& os) + -> void { const auto& function = sem_ir().functions().Get(function_id); if (SemIR::IsEntryPoint(sem_ir(), function_id)) { CARBON_CHECK(!specific_id.has_value(), "entry point should not be generic"); - return "main"; + os << "main"; + return; } // Clang should emit C++ function declarations for us. @@ -199,7 +203,6 @@ auto Mangler::Mangle(SemIR::FunctionId function_id, "Shouldn't mangle C++ function"); } - RawStringOstream os; os << "_C"; MangleNameId(os, function.name_id); @@ -251,7 +254,30 @@ auto Mangler::Mangle(SemIR::FunctionId function_id, } MangleSpecificId(os, specific_id); +} +auto Mangler::Mangle(SemIR::FunctionId function_id, + SemIR::SpecificId specific_id) -> std::string { + RawStringOstream os; + MangleImpl(function_id, specific_id, os); + return os.TakeStr(); +} + +auto Mangler::MangleWithPlatform(SemIR::FunctionId function_id, + SemIR::SpecificId specific_id) -> std::string { + RawStringOstream os; + + CARBON_CHECK(sem_ir_.cpp_file()); + // The only platform mangling that's relevant for us is applying a global + // prefix, if the platform has one. + if (char prefix = sem_ir_.cpp_file() + ->GetCodeGenerator() + ->GetModule() + ->getDataLayout() + .getGlobalPrefix()) { + os << prefix; + } + MangleImpl(function_id, specific_id, os); return os.TakeStr(); } diff --git a/toolchain/sem_ir/mangler.h b/toolchain/sem_ir/mangler.h index 120e1eb03b24..ec7523942dec 100644 --- a/toolchain/sem_ir/mangler.h +++ b/toolchain/sem_ir/mangler.h @@ -30,6 +30,14 @@ class Mangler { auto Mangle(SemIR::FunctionId function_id, SemIR::SpecificId specific_id) -> std::string; + // Equivalent to Mangle(), but produces a name that has also gone through + // the platform's mangling. This is needed when the resulting name will be + // used in a way that suppresses platform mangling, such as the value of an + // `asm` label attribute, but it needs to match the mangled name from + // another context that doesn't suppress platform mangling. + auto MangleWithPlatform(SemIR::FunctionId function_id, + SemIR::SpecificId specific_id) -> std::string; + // Produce a deterministically unique mangled name for the given global // variable pattern, or an empty string if the variable doesn't bind any // names, in which case it can't be referenced from another file and should be @@ -42,6 +50,9 @@ class Mangler { SemIR::SpecificId specific_id) -> std::string; private: + auto MangleImpl(SemIR::FunctionId function_id, SemIR::SpecificId specific_id, + llvm::raw_ostream& os) -> void; + // Mangle this `NameId` as an individual name component. auto MangleNameId(llvm::raw_ostream& os, SemIR::NameId name_id) -> void;