瀏覽代碼

Interop: support all C++ integer types that map to `intN_t` or `uintN_t`. (#5836)

Expand support for `int` and `short` to cover all the other `intN_t` and
`uintN_t` types too. We achieve this by asking Clang what the `intN_t` /
`uintN_t` type that it would use for the given bitwidth is, and checking
if that's the type we're trying to map.
Richard Smith 9 月之前
父節點
當前提交
c90c6728fd

+ 18 - 21
toolchain/check/import_cpp.cpp

@@ -559,32 +559,29 @@ static auto ImportCXXRecordDecl(Context& context, SemIR::LocId loc_id,
 }
 }
 
 
 // Creates an integer type of the given size.
 // Creates an integer type of the given size.
-static auto MakeIntType(Context& context, IntId size_id) -> TypeExpr {
-  // TODO: Fill in a location for the type once available.
-  auto type_inst_id = MakeIntTypeLiteral(context, Parse::NodeId::None,
-                                         SemIR::IntKind::Signed, size_id);
+static auto MakeIntType(Context& context, IntId size_id, bool is_signed)
+    -> TypeExpr {
+  auto type_inst_id = MakeIntTypeLiteral(
+      context, Parse::NodeId::None,
+      is_signed ? SemIR::IntKind::Signed : SemIR::IntKind::Unsigned, size_id);
   return ExprAsType(context, Parse::NodeId::None, type_inst_id);
   return ExprAsType(context, Parse::NodeId::None, type_inst_id);
 }
 }
 
 
 // Maps a C++ builtin type to a Carbon type.
 // Maps a C++ builtin type to a Carbon type.
 // TODO: Support more builtin types.
 // TODO: Support more builtin types.
-static auto MapBuiltinType(Context& context, const clang::BuiltinType& type)
-    -> TypeExpr {
-  // TODO: Refactor to avoid duplication.
-  switch (type.getKind()) {
-    case clang::BuiltinType::Short:
-      if (context.ast_context().getTypeSize(&type) == 16) {
-        return MakeIntType(context, context.ints().Add(16));
-      }
-      break;
-    case clang::BuiltinType::Int:
-      if (context.ast_context().getTypeSize(&type) == 32) {
-        return MakeIntType(context, context.ints().Add(32));
-      }
-      break;
-    default:
-      break;
+static auto MapBuiltinType(Context& context, clang::QualType qual_type,
+                           const clang::BuiltinType& type) -> TypeExpr {
+  if (type.isInteger()) {
+    auto width = context.ast_context().getIntWidth(qual_type);
+    bool is_signed = type.isSignedInteger();
+    auto int_n_type =
+        context.ast_context().getIntTypeForBitwidth(width, is_signed);
+    if (context.ast_context().hasSameType(qual_type, int_n_type)) {
+      return MakeIntType(context, context.ints().Add(width), is_signed);
+    }
+    // TODO: Handle integer types that map to named aliases.
   }
   }
+
   return {.inst_id = SemIR::TypeInstId::None, .type_id = SemIR::TypeId::None};
   return {.inst_id = SemIR::TypeInstId::None, .type_id = SemIR::TypeId::None};
 }
 }
 
 
@@ -615,7 +612,7 @@ static auto MapRecordType(Context& context, SemIR::LocId loc_id,
 static auto MapNonWrapperType(Context& context, SemIR::LocId loc_id,
 static auto MapNonWrapperType(Context& context, SemIR::LocId loc_id,
                               clang::QualType type) -> TypeExpr {
                               clang::QualType type) -> TypeExpr {
   if (const auto* builtin_type = type->getAs<clang::BuiltinType>()) {
   if (const auto* builtin_type = type->getAs<clang::BuiltinType>()) {
-    return MapBuiltinType(context, *builtin_type);
+    return MapBuiltinType(context, type, *builtin_type);
   }
   }
 
 
   if (const auto* record_type = type->getAs<clang::RecordType>()) {
   if (const auto* record_type = type->getAs<clang::RecordType>()) {

+ 39 - 0
toolchain/check/testdata/interop/cpp/fail_todo_arithmetic_types_unmapped.carbon

@@ -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-FILE: toolchain/testing/testdata/min_prelude/uint.carbon
+// EXTRA-ARGS: --target=x86_64-linux-gnu
+//
+// AUTOUPDATE
+// TIP: To test this file alone, run:
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/fail_todo_arithmetic_types_unmapped.carbon
+// TIP: To dump output, run:
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/fail_todo_arithmetic_types_unmapped.carbon
+
+// Ensure we get a diagnostic when using a type that doesn't yet have a mapping
+// from C++ to Carbon.
+
+// --- u64_types.h
+
+unsigned long GetUL();
+
+unsigned long long GetULL();
+
+// --- fail_todo_use_u64_types.carbon
+
+import Cpp library "u64_types.h";
+
+fn CallGetUL() -> u64 { return Cpp.GetUL(); }
+
+// TODO: Eventually, `unsigned long long` should map to
+// `Cpp.unsigned_long_long`, which should implicitly convert to `u64`.
+// We should switch to testing a different type when that case works.
+// CHECK:STDERR: fail_todo_use_u64_types.carbon:[[@LINE+7]]:33: error: semantics TODO: `Unsupported: return type: unsigned long long` [SemanticsTodo]
+// CHECK:STDERR: fn CallGetULL() -> u64 { return Cpp.GetULL(); }
+// CHECK:STDERR:                                 ^~~~~~~~~~
+// CHECK:STDERR: fail_todo_use_u64_types.carbon:[[@LINE+4]]:33: note: in `Cpp` name lookup for `GetULL` [InCppNameLookup]
+// CHECK:STDERR: fn CallGetULL() -> u64 { return Cpp.GetULL(); }
+// CHECK:STDERR:                                 ^~~~~~~~~~
+// CHECK:STDERR:
+fn CallGetULL() -> u64 { return Cpp.GetULL(); }

+ 5 - 0
toolchain/check/testdata/interop/cpp/function/arithmetic_types_bridged.carbon

@@ -10,6 +10,11 @@
 // TIP: To dump output, run:
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/function/arithmetic_types_bridged.carbon
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/function/arithmetic_types_bridged.carbon
 
 
+// Tests for the case where we need a thunk when calling a C++ function from
+// Carbon, because the parameter and return types might have a non-trivial
+// calling convention.
+// TODO: Create thunks for these cases.
+
 // ============================================================================
 // ============================================================================
 // short param
 // short param
 // ============================================================================
 // ============================================================================

+ 108 - 3
toolchain/check/testdata/interop/cpp/function/arithmetic_types_mapped.carbon → toolchain/check/testdata/interop/cpp/function/arithmetic_types_direct.carbon

@@ -2,13 +2,17 @@
 // Exceptions. See /LICENSE for license information.
 // Exceptions. See /LICENSE for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //
-// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon
+// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon
 //
 //
 // AUTOUPDATE
 // AUTOUPDATE
 // TIP: To test this file alone, run:
 // TIP: To test this file alone, run:
-// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/function/arithmetic_types_mapped.carbon
+// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/interop/cpp/function/arithmetic_types_direct.carbon
 // TIP: To dump output, run:
 // TIP: To dump output, run:
-// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/function/arithmetic_types_mapped.carbon
+// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/function/arithmetic_types_direct.carbon
+
+// Tests for the case where we can directly call a C++ function from Carbon,
+// because the parameter and return types are known to have a sufficiently
+// trivial calling convention.
 
 
 // ============================================================================
 // ============================================================================
 // int param
 // int param
@@ -102,6 +106,52 @@ fn F() {
   //@dump-sem-ir-end
   //@dump-sem-ir-end
 }
 }
 
 
+// ============================================================================
+// unsigned int param
+// ============================================================================
+
+// --- unsigned_int_param.h
+
+auto foo(unsigned int a) -> void;
+
+// --- import_unsigned_int_param.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "unsigned_int_param.h";
+
+fn F() {
+  //@dump-sem-ir-begin
+  Cpp.foo(1);
+  //@dump-sem-ir-end
+}
+
+// --- fail_import_unsigned_int_param_overflow.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "unsigned_int_param.h";
+
+fn F() {
+  Cpp.foo(0);
+
+  // CHECK:STDERR: fail_import_unsigned_int_param_overflow.carbon:[[@LINE+5]]:11: error: negative integer value -1 converted to unsigned type `u32` [NegativeIntInUnsignedType]
+  // CHECK:STDERR:   Cpp.foo(-1);
+  // CHECK:STDERR:           ^~
+  // CHECK:STDERR: fail_import_unsigned_int_param_overflow.carbon: note: initializing function parameter [InCallToFunctionParam]
+  // CHECK:STDERR:
+  Cpp.foo(-1);
+
+  Cpp.foo(0x0_FFFF_FFFF);
+
+  // CHECK:STDERR: fail_import_unsigned_int_param_overflow.carbon:[[@LINE+5]]:11: error: integer value 4294967296 too large for type `u32` [IntTooLargeForType]
+  // CHECK:STDERR:   Cpp.foo(0x1_0000_0000);
+  // CHECK:STDERR:           ^~~~~~~~~~~~~
+  // CHECK:STDERR: fail_import_unsigned_int_param_overflow.carbon: note: initializing function parameter [InCallToFunctionParam]
+  // CHECK:STDERR:
+  Cpp.foo(0x1_0000_0000);
+}
+
 // ============================================================================
 // ============================================================================
 // signed param
 // signed param
 // ============================================================================
 // ============================================================================
@@ -585,6 +635,61 @@ fn F() {
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- import_unsigned_int_param.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %int_32: Core.IntLiteral = int_value 32 [concrete]
+// CHECK:STDOUT:   %u32: type = class_type @UInt, @UInt(%int_32) [concrete]
+// CHECK:STDOUT:   %foo.type: type = fn_type @foo [concrete]
+// CHECK:STDOUT:   %foo: %foo.type = struct_value () [concrete]
+// CHECK:STDOUT:   %int_1.5b8: Core.IntLiteral = int_value 1 [concrete]
+// CHECK:STDOUT:   %ImplicitAs.type.9b4: type = facet_type <@ImplicitAs, @ImplicitAs(%u32)> [concrete]
+// CHECK:STDOUT:   %ImplicitAs.Convert.type.92a: type = fn_type @ImplicitAs.Convert, @ImplicitAs(%u32) [concrete]
+// CHECK:STDOUT:   %To: Core.IntLiteral = bind_symbolic_name To, 0 [symbolic]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.30e: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.d1a: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.30e = struct_value () [symbolic]
+// CHECK:STDOUT:   %ImplicitAs.impl_witness.ac5: <witness> = impl_witness imports.%ImplicitAs.impl_witness_table.bb8, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.feb: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.319: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.feb = struct_value () [concrete]
+// CHECK:STDOUT:   %ImplicitAs.facet: %ImplicitAs.type.9b4 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.ac5) [concrete]
+// CHECK:STDOUT:   %.bd1: type = fn_type_with_self_type %ImplicitAs.Convert.type.92a, %ImplicitAs.facet [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: <bound method> = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.319 [concrete]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: <specific function> = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.319, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete]
+// CHECK:STDOUT:   %bound_method: <bound method> = bound_method %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete]
+// CHECK:STDOUT:   %int_1.c1d: %u32 = int_value 1 [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .foo = %foo.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Core.import_ref.c3d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.30e) = import_ref Core//prelude/parts/uint, loc16_40, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.d1a)]
+// CHECK:STDOUT:   %ImplicitAs.impl_witness_table.bb8 = impl_witness_table (%Core.import_ref.c3d), @Core.IntLiteral.as.ImplicitAs.impl [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %foo.ref: %foo.type = name_ref foo, imports.%foo.decl [concrete = constants.%foo]
+// CHECK:STDOUT:   %int_1: Core.IntLiteral = int_value 1 [concrete = constants.%int_1.5b8]
+// CHECK:STDOUT:   %impl.elem0: %.bd1 = impl_witness_access constants.%ImplicitAs.impl_witness.ac5, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.319]
+// CHECK:STDOUT:   %bound_method.loc8_11.1: <bound method> = bound_method %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound]
+// CHECK:STDOUT:   %specific_fn: <specific function> = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn]
+// CHECK:STDOUT:   %bound_method.loc8_11.2: <bound method> = bound_method %int_1, %specific_fn [concrete = constants.%bound_method]
+// CHECK:STDOUT:   %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %u32 = call %bound_method.loc8_11.2(%int_1) [concrete = constants.%int_1.c1d]
+// CHECK:STDOUT:   %.loc8_11.1: %u32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_1.c1d]
+// CHECK:STDOUT:   %.loc8_11.2: %u32 = converted %int_1, %.loc8_11.1 [concrete = constants.%int_1.c1d]
+// CHECK:STDOUT:   %foo.call: init %empty_tuple.type = call %foo.ref(%.loc8_11.2)
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: --- import_signed_param.carbon
 // CHECK:STDOUT: --- import_signed_param.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
 // CHECK:STDOUT: constants {

+ 4 - 14
toolchain/check/testdata/interop/cpp/include_paths.carbon

@@ -3,6 +3,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 //
 //
 // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/uint.carbon
 // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/uint.carbon
+// EXTRA-ARGS: --target=x86_64-linux-gnu
 //
 //
 // AUTOUPDATE
 // AUTOUPDATE
 // TIP: To test this file alone, run:
 // TIP: To test this file alone, run:
@@ -18,7 +19,6 @@
 #include <stddef.h>
 #include <stddef.h>
 
 
 ptrdiff_t GetSize();
 ptrdiff_t GetSize();
-inline int GetSizeAsInt() { return GetSize(); }
 
 
 // --- import_stddef_indirectly.carbon
 // --- import_stddef_indirectly.carbon
 
 
@@ -26,24 +26,14 @@ library "[[@TEST_NAME]]";
 
 
 import Cpp library "include_stddef.h";
 import Cpp library "include_stddef.h";
 
 
-// TODO: `fn CallGetSize() -> Cpp.ptrdiff_t {`
-fn CallGetSize() -> i32 {
-  // TODO: Call `Cpp.GetSize` directly once we can import the types `long` and `long long`.
-  return Cpp.GetSizeAsInt() as i32;
+fn CallGetSize() -> Cpp.ptrdiff_t {
+  return Cpp.GetSize();
 }
 }
 
 
-// --- fail_todo_import_stddef_directly.carbon
+// --- import_stddef_directly.carbon
 
 
 library "[[@TEST_NAME]]";
 library "[[@TEST_NAME]]";
 
 
 import Cpp library "stddef.h";
 import Cpp library "stddef.h";
 
 
-// TODO: Once we can import `unsigned long` / `unsigned long long`, this should work.
-// CHECK:STDERR: fail_todo_import_stddef_directly.carbon:[[@LINE+7]]:8: error: semantics TODO: `Unsupported: Type declaration: size_t` [SemanticsTodo]
-// CHECK:STDERR: var n: Cpp.size_t = 42;
-// CHECK:STDERR:        ^~~~~~~~~~
-// CHECK:STDERR: fail_todo_import_stddef_directly.carbon:[[@LINE+4]]:8: note: in `Cpp` name lookup for `size_t` [InCppNameLookup]
-// CHECK:STDERR: var n: Cpp.size_t = 42;
-// CHECK:STDERR:        ^~~~~~~~~~
-// CHECK:STDERR:
 var n: Cpp.size_t = 42;
 var n: Cpp.size_t = 42;