Просмотр исходного кода

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
Nicholas Bishop 2 месяцев назад
Родитель
Сommit
f210f4ab04

+ 2 - 0
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",

+ 39 - 0
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

+ 21 - 0
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_

+ 40 - 4
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<SemIR::VarStorage>(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<clang::VarDecl>(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;
 }
 

+ 35 - 0
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();

+ 9 - 0
toolchain/check/testdata/interop/cpp/template/non_type_param.carbon

@@ -16,6 +16,7 @@ struct A {};
 
 template<int N> struct Simple {};
 Simple<123> MakeSimple123() { return Simple<123>(); }
+constexpr int Constant123 = 123;
 
 template<int M, int N> struct TwoNonType {};
 template<typename T, T N> 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]]";

+ 4 - 2
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<Parse::NodeId>(
-      {.ir_name = "acquire_value"});
+  static constexpr auto Kind = InstKind::AcquireValue.Define<Parse::NodeId>({
+      .ir_name = "acquire_value",
+      .constant_needs_inst_id = InstConstantNeedsInstIdKind::DuringEvaluation,
+  });
 
   TypeId type_id;
   InstId value_id;