diff --git a/toolchain/check/context.h b/toolchain/check/context.h index 31a0abcdbe8e..c7a9ed27bca2 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -240,6 +240,9 @@ class Context { auto import_ir_insts() -> ValueStore& { return sem_ir().import_ir_insts(); } + auto ast_context() -> clang::ASTContext& { + return sem_ir().cpp_ast()->getASTContext(); + } auto names() -> SemIR::NameStoreWrapper { return sem_ir().names(); } auto name_scopes() -> SemIR::NameScopeStore& { return sem_ir().name_scopes(); diff --git a/toolchain/check/import_cpp.cpp b/toolchain/check/import_cpp.cpp index 355c946e112d..6dc4c4b46115 100644 --- a/toolchain/check/import_cpp.cpp +++ b/toolchain/check/import_cpp.cpp @@ -19,6 +19,7 @@ #include "toolchain/check/diagnostic_helpers.h" #include "toolchain/check/import.h" #include "toolchain/check/inst.h" +#include "toolchain/check/literal.h" #include "toolchain/check/type.h" #include "toolchain/diagnostics/diagnostic.h" #include "toolchain/diagnostics/format_providers.h" @@ -196,6 +197,52 @@ static auto ClangLookup(Context& context, SemIR::LocId loc_id, return lookup; } +// Returns the return type of the given function declaration. +// Currently only void and 32-bit int are supported. +// TODO: Support more return types. +static auto GetReturnType(Context& context, SemIRLoc loc_id, + const clang::FunctionDecl* clang_decl) + -> SemIR::InstId { + clang::QualType ret_type = clang_decl->getReturnType().getCanonicalType(); + if (ret_type->isVoidType()) { + return SemIR::InstId::None; + } + if (const auto* builtin_type = dyn_cast(ret_type); + builtin_type && builtin_type->getKind() == clang::BuiltinType::Int) { + constexpr int SupportedIntWidth = 32; + uint64_t int_size = context.ast_context().getTypeSize(ret_type); + if (int_size != SupportedIntWidth) { + // TODO: Add tests for this case. + context.TODO(loc_id, + llvm::formatv("Unsupported: return type: {0}, size: {1}", + ret_type.getAsString(), int_size)); + return SemIR::ErrorInst::SingletonInstId; + } + IntId size_id = context.ints().Add(int_size); + // TODO: Fill in a location for the type once available. + SemIR::TypeId type_id = MakeIntType(context, Parse::NodeId::None, + SemIR::IntKind::Signed, size_id); + // TODO: Fill in a location for the type once available. + SemIR::InstId type_inst_id = MakeIntTypeLiteral( + context, Parse::NodeId::None, SemIR::IntKind::Signed, size_id); + + SemIR::InstId return_slot_pattern_id = AddInstInNoBlock( + // TODO: Fill in a location for the return type once available. + context, SemIR::LocIdAndInst::NoLoc(SemIR::ReturnSlotPattern( + {.type_id = type_id, .type_inst_id = type_inst_id}))); + SemIR::InstId param_pattern_id = AddInstInNoBlock( + // TODO: Fill in a location for the return type once available. + context, SemIR::LocIdAndInst::NoLoc(SemIR::OutParamPattern( + {.type_id = type_id, + .subpattern_id = return_slot_pattern_id, + .index = SemIR::CallParamIndex::None}))); + return param_pattern_id; + } + context.TODO(loc_id, llvm::formatv("Unsupported: return type: {0}", + ret_type.getAsString())); + return SemIR::ErrorInst::SingletonInstId; +} + // Imports a function declaration from Clang to Carbon. If successful, returns // the new Carbon function declaration `InstId`. static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_id, @@ -219,8 +266,9 @@ static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_id, context.TODO(loc_id, "Unsupported: Function with parameters"); return SemIR::ErrorInst::SingletonInstId; } - if (!clang_decl->getReturnType()->isVoidType()) { - context.TODO(loc_id, "Unsupported: Function with non-void return type"); + + auto return_slot_pattern_id = GetReturnType(context, loc_id, clang_decl); + if (SemIR::ErrorInst::SingletonInstId == return_slot_pattern_id) { return SemIR::ErrorInst::SingletonInstId; } @@ -244,7 +292,7 @@ static auto ImportFunctionDecl(Context& context, SemIR::LocId loc_id, .first_owning_decl_id = decl_id, .definition_id = SemIR::InstId::None}, {.call_params_id = SemIR::InstBlockId::Empty, - .return_slot_pattern_id = SemIR::InstId::None, + .return_slot_pattern_id = return_slot_pattern_id, .virtual_modifier = SemIR::FunctionFields::VirtualModifier::None, .self_param_id = SemIR::InstId::None, .cpp_decl = clang_decl}}; diff --git a/toolchain/check/testdata/interop/cpp/function_decl.carbon b/toolchain/check/testdata/interop/cpp/function_decl.carbon new file mode 100644 index 000000000000..6febe9f13c03 --- /dev/null +++ b/toolchain/check/testdata/interop/cpp/function_decl.carbon @@ -0,0 +1,157 @@ +// 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 +// +// 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_decl.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/interop/cpp/function_decl.carbon + +// --- int_return_function_decl.h + +auto foo_int() -> int; + +// --- import_int_return_function_decl.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "int_return_function_decl.h"; + +fn Carbon_foo(val: i32); + +fn F() { + Carbon_foo(Cpp.foo_int()); +} + +// --- float_return_function_decl.h + +auto foo_float() -> float; + +// --- fail_import_float_return_function_decl.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "float_return_function_decl.h"; + +fn F() { + // CHECK:STDERR: fail_import_float_return_function_decl.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: return type: float` [SemanticsTodo] + // CHECK:STDERR: Cpp.foo_float(); + // CHECK:STDERR: ^~~~~~~~~~~~~ + // CHECK:STDERR: fail_import_float_return_function_decl.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo_float` [InCppNameLookup] + // CHECK:STDERR: Cpp.foo_float(); + // CHECK:STDERR: ^~~~~~~~~~~~~ + // CHECK:STDERR: + Cpp.foo_float(); +} + +// CHECK:STDOUT: --- import_int_return_function_decl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] +// CHECK:STDOUT: %Carbon_foo.type: type = fn_type @Carbon_foo [concrete] +// CHECK:STDOUT: %Carbon_foo: %Carbon_foo.type = struct_value () [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %foo_int.type: type = fn_type @foo_int [concrete] +// CHECK:STDOUT: %foo_int: %foo_int.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { +// CHECK:STDOUT: .Int = %Core.Int +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .foo_int = @F.%foo_int.decl +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: .Carbon_foo = %Carbon_foo.decl +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "int_return_function_decl.h" +// CHECK:STDOUT: } +// CHECK:STDOUT: %Carbon_foo.decl: %Carbon_foo.type = fn_decl @Carbon_foo [concrete = constants.%Carbon_foo] { +// CHECK:STDOUT: %val.patt: %i32 = binding_pattern val +// CHECK:STDOUT: %val.param_patt: %i32 = value_param_pattern %val.patt, call_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %val.param: %i32 = value_param call_param0 +// CHECK:STDOUT: %.loc6: type = splice_block %i32 [concrete = constants.%i32] { +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] +// CHECK:STDOUT: } +// CHECK:STDOUT: %val: %i32 = bind_name val, %val.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Carbon_foo(%val.param_patt: %i32); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Carbon_foo.ref: %Carbon_foo.type = name_ref Carbon_foo, file.%Carbon_foo.decl [concrete = constants.%Carbon_foo] +// CHECK:STDOUT: %Cpp.ref: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %int_32.1: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] +// CHECK:STDOUT: %i32.1: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] +// CHECK:STDOUT: %int_32.2: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] +// CHECK:STDOUT: %i32.2: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] +// CHECK:STDOUT: %foo_int.decl: %foo_int.type = fn_decl @foo_int [concrete = constants.%foo_int] {} {} +// CHECK:STDOUT: %foo_int.ref: %foo_int.type = name_ref foo_int, %foo_int.decl [concrete = constants.%foo_int] +// CHECK:STDOUT: %foo_int.call: init %i32 = call %foo_int.ref() +// CHECK:STDOUT: %.loc9_26.1: %i32 = value_of_initializer %foo_int.call +// CHECK:STDOUT: %.loc9_26.2: %i32 = converted %foo_int.call, %.loc9_26.1 +// CHECK:STDOUT: %Carbon_foo.call: init %empty_tuple.type = call %Carbon_foo.ref(%.loc9_26.2) +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @foo_int[]() -> %i32; +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_import_float_return_function_decl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .foo_float = +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .Core = imports.%Core +// CHECK:STDOUT: .Cpp = imports.%Cpp +// CHECK:STDOUT: .F = %F.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.import = import Core +// CHECK:STDOUT: %Cpp.import_cpp = import_cpp { +// CHECK:STDOUT: import Cpp "float_return_function_decl.h" +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} {} +// 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_float.ref: = name_ref foo_float, [concrete = ] +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/no_prelude/function_decl.carbon b/toolchain/check/testdata/interop/cpp/no_prelude/function_decl.carbon index f83b9c64fd98..61679f6cd1f8 100644 --- a/toolchain/check/testdata/interop/cpp/no_prelude/function_decl.carbon +++ b/toolchain/check/testdata/interop/cpp/no_prelude/function_decl.carbon @@ -153,8 +153,9 @@ fn F() { } // --- non_void_return_function_decl.h +struct F; -int foo(); +F foo(); // --- fail_import_non_void_return_function_decl.carbon @@ -163,7 +164,7 @@ library "[[@TEST_NAME]]"; import Cpp library "non_void_return_function_decl.h"; fn F() { - // CHECK:STDERR: fail_import_non_void_return_function_decl.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: Function with non-void return type` [SemanticsTodo] + // CHECK:STDERR: fail_import_non_void_return_function_decl.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: return type: struct F` [SemanticsTodo] // CHECK:STDERR: Cpp.foo(); // CHECK:STDERR: ^~~~~~~ // CHECK:STDERR: fail_import_non_void_return_function_decl.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]