diff --git a/toolchain/check/import_cpp.cpp b/toolchain/check/import_cpp.cpp index 8a0f94ba22f1..bf057cd5f1fe 100644 --- a/toolchain/check/import_cpp.cpp +++ b/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. -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); } // Maps a C++ builtin type to a Carbon type. // 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}; } @@ -615,7 +612,7 @@ static auto MapRecordType(Context& context, SemIR::LocId loc_id, static auto MapNonWrapperType(Context& context, SemIR::LocId loc_id, clang::QualType type) -> TypeExpr { if (const auto* builtin_type = type->getAs()) { - return MapBuiltinType(context, *builtin_type); + return MapBuiltinType(context, type, *builtin_type); } if (const auto* record_type = type->getAs()) { diff --git a/toolchain/check/testdata/interop/cpp/fail_todo_arithmetic_types_unmapped.carbon b/toolchain/check/testdata/interop/cpp/fail_todo_arithmetic_types_unmapped.carbon new file mode 100644 index 000000000000..55e0b00f0a18 --- /dev/null +++ b/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(); } diff --git a/toolchain/check/testdata/interop/cpp/function/arithmetic_types_bridged.carbon b/toolchain/check/testdata/interop/cpp/function/arithmetic_types_bridged.carbon index 35afdbdb9fde..919ba4c4db06 100644 --- a/toolchain/check/testdata/interop/cpp/function/arithmetic_types_bridged.carbon +++ b/toolchain/check/testdata/interop/cpp/function/arithmetic_types_bridged.carbon @@ -10,6 +10,11 @@ // 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 +// 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 // ============================================================================ diff --git a/toolchain/check/testdata/interop/cpp/function/arithmetic_types_mapped.carbon b/toolchain/check/testdata/interop/cpp/function/arithmetic_types_direct.carbon similarity index 91% rename from toolchain/check/testdata/interop/cpp/function/arithmetic_types_mapped.carbon rename to toolchain/check/testdata/interop/cpp/function/arithmetic_types_direct.carbon index 83fdac30da92..eb3f2a0f2890 100644 --- a/toolchain/check/testdata/interop/cpp/function/arithmetic_types_mapped.carbon +++ b/toolchain/check/testdata/interop/cpp/function/arithmetic_types_direct.carbon @@ -2,13 +2,17 @@ // Exceptions. See /LICENSE for license information. // 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 // 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: 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 @@ -102,6 +106,52 @@ fn F() { //@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 // ============================================================================ @@ -585,6 +635,61 @@ fn F() { // 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: = 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 %int_1.5b8, %Core.IntLiteral.as.ImplicitAs.impl.Convert.319 [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.319, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] +// CHECK:STDOUT: %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 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: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// 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: = 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 %int_1, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] +// CHECK:STDOUT: %specific_fn: = 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 %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: +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: --- import_signed_param.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { diff --git a/toolchain/check/testdata/interop/cpp/include_paths.carbon b/toolchain/check/testdata/interop/cpp/include_paths.carbon index c2061e4bd667..6557639718f6 100644 --- a/toolchain/check/testdata/interop/cpp/include_paths.carbon +++ b/toolchain/check/testdata/interop/cpp/include_paths.carbon @@ -3,6 +3,7 @@ // 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: @@ -18,7 +19,6 @@ #include ptrdiff_t GetSize(); -inline int GetSizeAsInt() { return GetSize(); } // --- import_stddef_indirectly.carbon @@ -26,24 +26,14 @@ library "[[@TEST_NAME]]"; 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]]"; 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;