From f210f4ab040d00385005c46181d62ce4950df8f4 Mon Sep 17 00:00:00 2001 From: Nicholas Bishop Date: Tue, 24 Feb 2026 15:59:24 -0500 Subject: [PATCH] Add initial support for importing C++ constexprs as Carbon constants (#6770) This allows a C++ constexpr to be used as an argument to a non-type template parameter. https://github.com/carbon-language/carbon-lang/issues/6717 --- toolchain/check/BUILD | 2 + toolchain/check/cpp/constant.cpp | 39 ++++++++++++++++ toolchain/check/cpp/constant.h | 21 +++++++++ toolchain/check/eval_inst.cpp | 44 +++++++++++++++++-- .../testdata/interop/cpp/constexpr.carbon | 35 +++++++++++++++ .../cpp/template/non_type_param.carbon | 9 ++++ toolchain/sem_ir/typed_insts.h | 6 ++- 7 files changed, 150 insertions(+), 6 deletions(-) create mode 100644 toolchain/check/cpp/constant.cpp create mode 100644 toolchain/check/cpp/constant.h create mode 100644 toolchain/check/testdata/interop/cpp/constexpr.carbon diff --git a/toolchain/check/BUILD b/toolchain/check/BUILD index a54f9a55d4d4..badcc1b0c681 100644 --- a/toolchain/check/BUILD +++ b/toolchain/check/BUILD @@ -23,6 +23,7 @@ cc_library( "convert.cpp", "cpp/access.cpp", "cpp/call.cpp", + "cpp/constant.cpp", "cpp/context.cpp", "cpp/custom_type_mapping.cpp", "cpp/generate_ast.cpp", @@ -83,6 +84,7 @@ cc_library( "convert.h", "cpp/access.h", "cpp/call.h", + "cpp/constant.h", "cpp/context.h", "cpp/custom_type_mapping.h", "cpp/generate_ast.h", diff --git a/toolchain/check/cpp/constant.cpp b/toolchain/check/cpp/constant.cpp new file mode 100644 index 000000000000..3faebd138525 --- /dev/null +++ b/toolchain/check/cpp/constant.cpp @@ -0,0 +1,39 @@ +// 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 + +#include "toolchain/check/cpp/constant.h" + +#include "toolchain/check/cpp/import.h" +#include "toolchain/check/eval.h" + +namespace Carbon::Check { + +// TODO: dedup with code in `MapConstant` and `TryEvaluateMacroToConstant`. +auto MapAPValueToConstant(Context& context, SemIR::LocId loc_id, + const clang::APValue& ap_value, clang::QualType type) + -> SemIR::ConstantId { + SemIR::TypeId type_id = ImportCppType(context, loc_id, type).type_id; + if (!type_id.has_value()) { + return SemIR::ConstantId::NotConstant; + } + + if (ap_value.isInt()) { + if (type->isBooleanType()) { + auto value = SemIR::BoolValue::From(!ap_value.getInt().isZero()); + return TryEvalInst( + context, SemIR::BoolLiteral{.type_id = type_id, .value = value}); + } else { + CARBON_CHECK(type->isIntegralOrEnumerationType()); + + IntId int_id = context.ints().Add(ap_value.getInt()); + return TryEvalInst(context, + SemIR::IntValue{.type_id = type_id, .int_id = int_id}); + } + } else { + // TODO: support other types. + return SemIR::ConstantId::NotConstant; + } +} + +} // namespace Carbon::Check diff --git a/toolchain/check/cpp/constant.h b/toolchain/check/cpp/constant.h new file mode 100644 index 000000000000..677ecafd3518 --- /dev/null +++ b/toolchain/check/cpp/constant.h @@ -0,0 +1,21 @@ +// 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 + +#ifndef CARBON_TOOLCHAIN_CHECK_CPP_CONSTANT_H_ +#define CARBON_TOOLCHAIN_CHECK_CPP_CONSTANT_H_ + +#include "clang/AST/APValue.h" +#include "toolchain/check/context.h" +#include "toolchain/sem_ir/ids.h" + +namespace Carbon::Check { + +// Converts an `APValue` to a Carbon `ConstantId`. +auto MapAPValueToConstant(Context& context, SemIR::LocId loc_id, + const clang::APValue& ap_value, clang::QualType type) + -> SemIR::ConstantId; + +} // namespace Carbon::Check + +#endif // CARBON_TOOLCHAIN_CHECK_CPP_CONSTANT_H_ diff --git a/toolchain/check/eval_inst.cpp b/toolchain/check/eval_inst.cpp index cf234b81cda4..5b831be37086 100644 --- a/toolchain/check/eval_inst.cpp +++ b/toolchain/check/eval_inst.cpp @@ -8,6 +8,8 @@ #include "toolchain/base/kind_switch.h" #include "toolchain/check/action.h" +#include "toolchain/check/cpp/constant.h" +#include "toolchain/check/cpp/type_mapping.h" #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/check/facet_type.h" #include "toolchain/check/generic.h" @@ -122,10 +124,44 @@ auto EvalConstantInst(Context& /*context*/, SemIR::ValueBinding /*inst*/) return ConstantEvalResult::NotConstant; } -auto EvalConstantInst(Context& /*context*/, SemIR::AcquireValue /*inst*/) - -> ConstantEvalResult { - // TODO: Handle this once we've decided how to represent constant values of - // reference expressions. +auto EvalConstantInst(Context& context, SemIR::InstId inst_id, + SemIR::AcquireValue inst) -> ConstantEvalResult { + const auto& var_storage = + context.insts().TryGetAs(inst.value_id); + if (!var_storage) { + return ConstantEvalResult::NotConstant; + } + + // Try to map from var storage to a C++ global. + auto var_name_id = SemIR::GetFirstBindingNameFromPatternId( + context.sem_ir(), var_storage->pattern_id); + if (auto cpp_global_var_id = context.sem_ir().cpp_global_vars().Lookup( + {.entity_name_id = var_name_id}); + cpp_global_var_id.has_value()) { + SemIR::ClangDeclId clang_decl_id = + context.sem_ir().cpp_global_vars().Get(cpp_global_var_id).clang_decl_id; + const auto* var_decl = + cast(context.clang_decls().Get(clang_decl_id).key.decl); + + // If the C++ global is constant, map it to a Carbon constant. + if (var_decl->isUsableInConstantExpressions(context.ast_context())) { + if (const auto* ap_value = var_decl->getEvaluatedValue()) { + auto clang_type = MapToCppType(context, inst.type_id); + if (clang_type.isNull()) { + return ConstantEvalResult::TODO; + } + + auto const_id = MapAPValueToConstant(context, SemIR::LocId(inst_id), + *ap_value, clang_type); + if (const_id.has_value() && const_id.is_constant()) { + return ConstantEvalResult::Existing(const_id); + } + } + } + + return ConstantEvalResult::NotConstant; + } + return ConstantEvalResult::TODO; } diff --git a/toolchain/check/testdata/interop/cpp/constexpr.carbon b/toolchain/check/testdata/interop/cpp/constexpr.carbon new file mode 100644 index 000000000000..1ad49245a592 --- /dev/null +++ b/toolchain/check/testdata/interop/cpp/constexpr.carbon @@ -0,0 +1,35 @@ +// 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 +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/constexpr.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/constexpr.carbon + +// --- bool.carbon + +library "[[@TEST_NAME]]"; + +import Cpp inline ''' +constexpr bool b = true; +'''; + +class C(B:! bool) {} +fn F() -> C(Cpp.b); +let x: C(true) = F(); + +// --- int.carbon + +library "[[@TEST_NAME]]"; + +import Cpp inline ''' +constexpr int i = 123; +'''; + +class C(I:! i32) {} +fn F() -> C(Cpp.i); +let x: C(123) = F(); diff --git a/toolchain/check/testdata/interop/cpp/template/non_type_param.carbon b/toolchain/check/testdata/interop/cpp/template/non_type_param.carbon index add5eb54381c..c209e43d2e53 100644 --- a/toolchain/check/testdata/interop/cpp/template/non_type_param.carbon +++ b/toolchain/check/testdata/interop/cpp/template/non_type_param.carbon @@ -16,6 +16,7 @@ struct A {}; template struct Simple {}; Simple<123> MakeSimple123() { return Simple<123>(); } +constexpr int Constant123 = 123; template struct TwoNonType {}; template struct DependentNonType {}; @@ -51,6 +52,14 @@ import Cpp library "templates.h"; var s: Cpp.Simple(100 + 20 + 3) = Cpp.MakeSimple123(); +// --- constant.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "templates.h"; + +var s: Cpp.Simple(Cpp.Constant123) = Cpp.MakeSimple123(); + // --- conversion.carbon library "[[@TEST_NAME]]"; diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 51344629434c..6cd1e6a62dd5 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -105,8 +105,10 @@ struct AccessOptionalMemberAction { // A value acquisition. Used when an expression contains a reference and we want // a value. struct AcquireValue { - static constexpr auto Kind = InstKind::AcquireValue.Define( - {.ir_name = "acquire_value"}); + static constexpr auto Kind = InstKind::AcquireValue.Define({ + .ir_name = "acquire_value", + .constant_needs_inst_id = InstConstantNeedsInstIdKind::DuringEvaluation, + }); TypeId type_id; InstId value_id;