Add support for int return type in Carbon/C++ interop (#5114)

Adding support for importing C++ functions with `int` return type in
Carbon.

Part of #5063

Here is a demo of the functionality:

```c++
// hello_int.h;

auto foo_int() -> int;
```

```c++
// hello_int.cpp

#include "hello_int.h"

auto foo_int() -> int {
    return 1;
}
```

```carbon
// main.carbon

library "Main";

import Cpp library "hello_int.h";
import Core library "io";

fn Carbon_foo(b: i32) {
  Core.Print(b);
}

fn Run() -> i32 {
  Carbon_foo(Cpp.foo_int());
  return 0;
}
```

```
$ clang -c hello_int.cpp
$ bazel-bin/toolchain/carbon compile main.carbon
$  bazel-bin/toolchain/carbon link hello_int.o main.o --output=demo
$ ./demo
1
```
This commit is contained in:
Ivana Ivanovska
2025-03-24 15:18:20 +00:00
committed by GitHub
parent c0ee446cec
commit ce2ff0a35d
4 changed files with 214 additions and 5 deletions
+3
View File
@@ -240,6 +240,9 @@ class Context {
auto import_ir_insts() -> ValueStore<SemIR::ImportIRInstId>& {
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();
+51 -3
View File
@@ -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<clang::BuiltinType>(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}};
@@ -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> = 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> = 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> = 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: <namespace> = 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> = namespace file.%Core.import, [concrete] {
// CHECK:STDOUT: import Core//prelude
// CHECK:STDOUT: import Core//prelude/...
// CHECK:STDOUT: }
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
// CHECK:STDOUT: .foo_float = <error>
// CHECK:STDOUT: import Cpp//...
// CHECK:STDOUT: }
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: file {
// CHECK:STDOUT: package: <namespace> = 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: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %foo_float.ref: <error> = name_ref foo_float, <error> [concrete = <error>]
// CHECK:STDOUT: return
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -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]