Support importing C++ _Nonnull pointers as function parameters or return values (#5773)

We avoid using canonical type before knowing it's not a pointer because
we need the nullability attribute.
No support for pointers to pointers, yet.

C++ Interop Demo:

```c++
// hello_world.h

auto hello_world_param(int* _Nonnull i) -> void;
auto hello_world_return() -> int* _Nonnull;
```

```c++
// hello_world.cpp

#include "hello_world.h"
#include <cstdio>

auto hello_world_param(int* _Nonnull i) -> void {
  printf("hello_world: %d\n", *i);
}

static int x = 5;
auto hello_world_return() -> int* _Nonnull { return &x; }
```

```carbon
// main.carbon

library "Main";

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

fn Run() -> i32 {
  var i: i32 = 10;
  Cpp.hello_world_param(&i);

  let p: i32* = Cpp.hello_world_return();
  Core.Print(*p);

  return 0;
}
```

```shell
$ clang -c hello_world.cpp
$ ./bazel-bin/toolchain/install/prefix_root/bin/carbon compile main.carbon
$ ./bazel-bin/toolchain/install/prefix_root/bin/carbon link hello_world.o main.o --output=demo
$ ./demo
hello_world: 10
5
```

Part of #5772.
This commit is contained in:
Boaz Brickner
2025-07-10 08:00:37 +00:00
committed by GitHub
parent 110af3bfe4
commit a5ddc3e3cd
7 changed files with 899 additions and 145 deletions
+49 -34
View File
@@ -34,7 +34,7 @@ fn F() {
// CHECK:STDERR: Cpp.foo({});
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR:
// CHECK:STDERR: fail_todo_import_decl_value_param_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: struct S` [SemanticsTodo]
// CHECK:STDERR: fail_todo_import_decl_value_param_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: S` [SemanticsTodo]
// CHECK:STDERR: Cpp.foo({});
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR: fail_todo_import_decl_value_param_type.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
@@ -63,7 +63,7 @@ fn F() {
// CHECK:STDERR: ^
// CHECK:STDERR:
let s: Cpp.S;
// CHECK:STDERR: fail_todo_import_decl_value_param_type_previously_imported.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: struct S` [SemanticsTodo]
// CHECK:STDERR: fail_todo_import_decl_value_param_type_previously_imported.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: S` [SemanticsTodo]
// CHECK:STDERR: Cpp.foo(s);
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR: fail_todo_import_decl_value_param_type_previously_imported.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
@@ -98,7 +98,7 @@ fn F() {
// CHECK:STDERR: Cpp.foo1({});
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR:
// CHECK:STDERR: fail_todo_import_double_decl_value_param_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: struct S` [SemanticsTodo]
// CHECK:STDERR: fail_todo_import_double_decl_value_param_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: S` [SemanticsTodo]
// CHECK:STDERR: Cpp.foo1({});
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR: fail_todo_import_double_decl_value_param_type.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo1` [InCppNameLookup]
@@ -106,7 +106,7 @@ fn F() {
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR:
Cpp.foo1({});
// CHECK:STDERR: fail_todo_import_double_decl_value_param_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: struct S` [SemanticsTodo]
// CHECK:STDERR: fail_todo_import_double_decl_value_param_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: S` [SemanticsTodo]
// CHECK:STDERR: Cpp.foo2({});
// CHECK:STDERR: ^~~~~~~~
// CHECK:STDERR: fail_todo_import_double_decl_value_param_type.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo2` [InCppNameLookup]
@@ -283,7 +283,7 @@ fn F() {
struct S;
auto foo(S*) -> void;
auto foo(S* _Nonnull) -> void;
// --- fail_todo_import_decl_pointer_param_type.carbon
@@ -300,7 +300,7 @@ import Cpp library "decl_pointer_param_type.h";
// CHECK:STDERR:
fn F(s: Cpp.S*) {
//@dump-sem-ir-begin
// CHECK:STDERR: fail_todo_import_decl_pointer_param_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: struct S *` [SemanticsTodo]
// CHECK:STDERR: fail_todo_import_decl_pointer_param_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: S * _Nonnull` [SemanticsTodo]
// CHECK:STDERR: Cpp.foo(s);
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR: fail_todo_import_decl_pointer_param_type.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
@@ -319,9 +319,9 @@ fn F(s: Cpp.S*) {
struct S {};
auto foo(S*) -> void;
auto foo(S* _Nonnull) -> void;
// --- fail_todo_import_definition_pointer_param_type.carbon
// --- import_definition_pointer_param_type.carbon
library "[[@TEST_NAME]]";
@@ -329,13 +329,6 @@ import Cpp library "definition_pointer_param_type.h";
fn F(s: Cpp.S*) {
//@dump-sem-ir-begin
// CHECK:STDERR: fail_todo_import_definition_pointer_param_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: struct S *` [SemanticsTodo]
// CHECK:STDERR: Cpp.foo(s);
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR: fail_todo_import_definition_pointer_param_type.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
// CHECK:STDERR: Cpp.foo(s);
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR:
Cpp.foo(s);
//@dump-sem-ir-end
}
@@ -364,7 +357,7 @@ fn F() {
// CHECK:STDERR: Cpp.foo();
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR:
// CHECK:STDERR: fail_todo_import_decl_value_return_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: return type: struct S` [SemanticsTodo]
// CHECK:STDERR: fail_todo_import_decl_value_return_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: return type: S` [SemanticsTodo]
// CHECK:STDERR: Cpp.foo();
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR: fail_todo_import_decl_value_return_type.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
@@ -404,7 +397,7 @@ fn F() {
struct S;
auto foo() -> S*;
auto foo() -> S* _Nonnull;
// --- fail_todo_import_decl_pointer_return_type.carbon
@@ -414,7 +407,14 @@ import Cpp library "decl_pointer_return_type.h";
fn F() {
//@dump-sem-ir-begin
// CHECK:STDERR: fail_todo_import_decl_pointer_return_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: return type: struct S *` [SemanticsTodo]
// CHECK:STDERR: fail_todo_import_decl_pointer_return_type.carbon:[[@LINE+14]]:3: error: semantics TODO: `Unsupported: Record declarations without a definition` [SemanticsTodo]
// CHECK:STDERR: Cpp.foo();
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR: fail_todo_import_decl_pointer_return_type.carbon:[[@LINE+11]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
// CHECK:STDERR: Cpp.foo();
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR:
// CHECK:STDERR: fail_todo_import_decl_pointer_return_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: return type: S * _Nonnull` [SemanticsTodo]
// CHECK:STDERR: Cpp.foo();
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR: fail_todo_import_decl_pointer_return_type.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
@@ -433,9 +433,9 @@ fn F() {
struct S {};
auto foo() -> S*;
auto foo() -> S* _Nonnull;
// --- fail_todo_import_definition_pointer_return_type.carbon
// --- import_definition_pointer_return_type.carbon
library "[[@TEST_NAME]]";
@@ -443,13 +443,6 @@ import Cpp library "definition_pointer_return_type.h";
fn F() {
//@dump-sem-ir-begin
// CHECK:STDERR: fail_todo_import_definition_pointer_return_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: return type: struct S *` [SemanticsTodo]
// CHECK:STDERR: Cpp.foo();
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR: fail_todo_import_definition_pointer_return_type.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
// CHECK:STDERR: Cpp.foo();
// CHECK:STDERR: ^~~~~~~
// CHECK:STDERR:
Cpp.foo();
//@dump-sem-ir-end
}
@@ -904,27 +897,36 @@ fn F() {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_todo_import_definition_pointer_param_type.carbon
// CHECK:STDOUT: --- import_definition_pointer_param_type.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %S: type = class_type @S [concrete]
// CHECK:STDOUT: %ptr: type = ptr_type %S [concrete]
// 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: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
// CHECK:STDOUT: .S = %S.decl
// CHECK:STDOUT: .foo = <error>
// CHECK:STDOUT: .foo = %foo.decl
// CHECK:STDOUT: import Cpp//...
// CHECK:STDOUT: }
// CHECK:STDOUT: %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
// 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(%s.param: %ptr) {
// CHECK:STDOUT: !entry:
// CHECK:STDOUT: %Cpp.ref.loc15: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
// CHECK:STDOUT: %foo.ref: <error> = name_ref foo, <error> [concrete = <error>]
// CHECK:STDOUT: %Cpp.ref.loc8: <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: %s.ref: %ptr = name_ref s, %s
// CHECK:STDOUT: %foo.call: init %empty_tuple.type = call %foo.ref(%s.ref)
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
@@ -989,6 +991,7 @@ fn F() {
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
// CHECK:STDOUT: .foo = <error>
// CHECK:STDOUT: .S = <error>
// CHECK:STDOUT: import Cpp//...
// CHECK:STDOUT: }
// CHECK:STDOUT: }
@@ -1000,22 +1003,34 @@ fn F() {
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: --- fail_todo_import_definition_pointer_return_type.carbon
// CHECK:STDOUT: --- import_definition_pointer_return_type.carbon
// CHECK:STDOUT:
// CHECK:STDOUT: constants {
// CHECK:STDOUT: %S: type = class_type @S [concrete]
// CHECK:STDOUT: %ptr: type = ptr_type %S [concrete]
// CHECK:STDOUT: %foo.type: type = fn_type @foo [concrete]
// CHECK:STDOUT: %foo: %foo.type = struct_value () [concrete]
// CHECK:STDOUT: }
// CHECK:STDOUT:
// CHECK:STDOUT: imports {
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
// CHECK:STDOUT: .foo = <error>
// CHECK:STDOUT: .foo = %foo.decl
// CHECK:STDOUT: .S = %S.decl
// CHECK:STDOUT: import Cpp//...
// CHECK:STDOUT: }
// CHECK:STDOUT: %S.decl: type = class_decl @S [concrete = constants.%S] {} {}
// 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: <error> = name_ref foo, <error> [concrete = <error>]
// CHECK:STDOUT: %foo.ref: %foo.type = name_ref foo, imports.%foo.decl [concrete = constants.%foo]
// CHECK:STDOUT: %foo.call: init %ptr = call %foo.ref()
// CHECK:STDOUT: <elided>
// CHECK:STDOUT: }
// CHECK:STDOUT: