Эх сурвалжийг харах

Add support for using C++ `bool` type in imported function declarations. (#5860)

C++ in

C++ Interop Demo:

```c++
// hello_world.h

auto hello_world(bool x) -> bool;
```

```c++
// hello_world.cpp

#include "hello_world.h"

#include <cstdio>

auto hello_world(bool x) -> bool {
  printf("bool: %d\n", x);
  return !x;
}
```

```carbon
// main.carbon

library "Main";

import Cpp library "hello_world.h";

fn Run() -> i32 {
  let x: bool = Cpp.hello_world(false);
  if (x) {
    return 0;
  } else {
    return 1;
  }
}
```

```shell
$ clang -c hello_world.cpp
$ bazel-bin/toolchain/carbon compile main.carbon
$ bazel-bin/toolchain/carbon link hello_world.o main.o --output=demo
$ ./demo
bool: 0
```

Before this change (bool is interpreted as a 1 bit integer):
```shell
$ bazel-bin/toolchain/carbon compile main.carbon
... CRASH! ...
clang/include/clang/AST/Type.h:952: const ExtQualsTypeCommonBase *clang::QualType::getCommonPtr() const: Assertion `!isNull() && "Cannot retrieve a NULL type pointer"' failed.
```

Part of #5263.
Boaz Brickner 9 сар өмнө
parent
commit
6d6e0d0418

+ 10 - 4
toolchain/check/import_cpp.cpp

@@ -752,12 +752,18 @@ static auto MakeIntType(Context& context, IntId size_id, bool is_signed)
 // TODO: Support more builtin types.
 static auto MapBuiltinType(Context& context, clang::QualType qual_type,
                            const clang::BuiltinType& type) -> TypeExpr {
+  clang::ASTContext& ast_context = context.ast_context();
+  if (type.isBooleanType()) {
+    CARBON_CHECK(ast_context.hasSameType(qual_type, ast_context.BoolTy));
+    return ExprAsType(context, Parse::NodeId::None,
+                      context.types().GetInstId(GetSingletonType(
+                          context, SemIR::BoolType::TypeInstId)));
+  }
   if (type.isInteger()) {
-    auto width = context.ast_context().getIntWidth(qual_type);
+    auto width = 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)) {
+    auto int_n_type = ast_context.getIntTypeForBitwidth(width, is_signed);
+    if (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.

+ 155 - 1
toolchain/check/testdata/interop/cpp/function/arithmetic_types_bridged.carbon

@@ -2,7 +2,7 @@
 // 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:
@@ -15,6 +15,38 @@
 // calling convention.
 // TODO: Create thunks for these cases.
 
+// ============================================================================
+// bool param
+// ============================================================================
+
+// --- bool_param.h
+
+auto foo(bool a) -> void;
+
+// --- import_bool_param_true.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "bool_param.h";
+
+fn F() {
+  //@dump-sem-ir-begin
+  Cpp.foo(true);
+  //@dump-sem-ir-end
+}
+
+// --- import_bool_param_false.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "bool_param.h";
+
+fn F() {
+  //@dump-sem-ir-begin
+  Cpp.foo(false);
+  //@dump-sem-ir-end
+}
+
 // ============================================================================
 // short param
 // ============================================================================
@@ -326,6 +358,26 @@ fn F() {
   //@dump-sem-ir-end
 }
 
+// ============================================================================
+// bool return
+// ============================================================================
+
+// --- bool_return.h
+
+auto foo_bool() -> bool;
+
+// --- import_bool_return.carbon
+
+library "[[@TEST_NAME]]";
+
+import Cpp library "bool_return.h";
+
+fn F() {
+  //@dump-sem-ir-begin
+  let x: bool = Cpp.foo_bool();
+  //@dump-sem-ir-end
+}
+
 // ============================================================================
 // short return
 // ============================================================================
@@ -373,6 +425,67 @@ fn F() {
   //@dump-sem-ir-end
 }
 
+
+// CHECK:STDOUT: --- import_bool_param_true.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %foo.type: type = fn_type @foo [concrete]
+// CHECK:STDOUT:   %foo: %foo.type = struct_value () [concrete]
+// CHECK:STDOUT:   %true: bool = bool_literal true [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: }
+// 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:   %true: bool = bool_literal true [concrete = constants.%true]
+// CHECK:STDOUT:   %foo.call: init %empty_tuple.type = call %foo.ref(%true)
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: --- import_bool_param_false.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %foo.type: type = fn_type @foo [concrete]
+// CHECK:STDOUT:   %foo: %foo.type = struct_value () [concrete]
+// CHECK:STDOUT:   %false: bool = bool_literal false [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: }
+// 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:   %false: bool = bool_literal false [concrete = constants.%false]
+// CHECK:STDOUT:   %foo.call: init %empty_tuple.type = call %foo.ref(%false)
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: --- import_short_param.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
@@ -961,6 +1074,47 @@ fn F() {
 // CHECK:STDOUT:   <elided>
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
+// CHECK:STDOUT: --- import_bool_return.carbon
+// CHECK:STDOUT:
+// CHECK:STDOUT: constants {
+// CHECK:STDOUT:   %Bool.type: type = fn_type @Bool [concrete]
+// CHECK:STDOUT:   %Bool: %Bool.type = struct_value () [concrete]
+// CHECK:STDOUT:   %pattern_type.831: type = pattern_type bool [concrete]
+// CHECK:STDOUT:   %foo_bool.type: type = fn_type @foo_bool [concrete]
+// CHECK:STDOUT:   %foo_bool: %foo_bool.type = struct_value () [concrete]
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: imports {
+// CHECK:STDOUT:   %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
+// CHECK:STDOUT:     .foo_bool = %foo_bool.decl
+// CHECK:STDOUT:     import Cpp//...
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %foo_bool.decl: %foo_bool.type = fn_decl @foo_bool [concrete = constants.%foo_bool] {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   } {
+// CHECK:STDOUT:     <elided>
+// CHECK:STDOUT:   }
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: fn @F() {
+// CHECK:STDOUT: !entry:
+// CHECK:STDOUT:   name_binding_decl {
+// CHECK:STDOUT:     %x.patt: %pattern_type.831 = binding_pattern x [concrete]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
+// CHECK:STDOUT:   %foo_bool.ref: %foo_bool.type = name_ref foo_bool, imports.%foo_bool.decl [concrete = constants.%foo_bool]
+// CHECK:STDOUT:   %foo_bool.call: init bool = call %foo_bool.ref()
+// CHECK:STDOUT:   %.loc8_10.1: type = splice_block %.loc8_10.3 [concrete = bool] {
+// CHECK:STDOUT:     %Bool.call: init type = call constants.%Bool() [concrete = bool]
+// CHECK:STDOUT:     %.loc8_10.2: type = value_of_initializer %Bool.call [concrete = bool]
+// CHECK:STDOUT:     %.loc8_10.3: type = converted %Bool.call, %.loc8_10.2 [concrete = bool]
+// CHECK:STDOUT:   }
+// CHECK:STDOUT:   %.loc8_30.1: bool = value_of_initializer %foo_bool.call
+// CHECK:STDOUT:   %.loc8_30.2: bool = converted %foo_bool.call, %.loc8_30.1
+// CHECK:STDOUT:   %x: bool = bind_name x, %.loc8_30.2
+// CHECK:STDOUT:   <elided>
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
 // CHECK:STDOUT: --- import_short_return.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {