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
+56 -5
View File
@@ -700,10 +700,13 @@ static auto MapRecordType(Context& context, SemIR::LocId loc_id,
.type_id = SemIR::ErrorInst::TypeId};
}
// Maps a C++ type to a Carbon type.
// Maps a C++ non-pointer type to a Carbon type.
// TODO: Support more types.
static auto MapType(Context& context, SemIR::LocId loc_id, clang::QualType type)
-> TypeExpr {
static auto MapNonPointerType(Context& context, SemIR::LocId loc_id,
clang::QualType type) -> TypeExpr {
type = type.getCanonicalType();
CARBON_CHECK(!type->isPointerType());
if (const auto* builtin_type = dyn_cast<clang::BuiltinType>(type)) {
return MapBuiltinType(context, *builtin_type);
}
@@ -716,6 +719,54 @@ static auto MapType(Context& context, SemIR::LocId loc_id, clang::QualType type)
.type_id = SemIR::ErrorInst::TypeId};
}
// Maps a C++ pointer type to a Carbon pointer type.
static auto MapPointerType(Context& context, SemIR::LocId loc_id,
clang::QualType type) -> TypeExpr {
CARBON_CHECK(type->isPointerType());
if (auto nullability = type->getNullability();
!nullability.has_value() ||
*nullability != clang::NullabilityKind::NonNull) {
context.TODO(loc_id, llvm::formatv("Unsupported: nullable pointer: {0}",
type.getAsString()));
return {.inst_id = SemIR::ErrorInst::TypeInstId,
.type_id = SemIR::ErrorInst::TypeId};
}
clang::QualType pointee_type = type->getPointeeType();
if (pointee_type->isAnyPointerType()) {
context.TODO(loc_id,
llvm::formatv("Unsupported: pointer to pointer type: {0}",
pointee_type.getAsString()));
return {.inst_id = SemIR::ErrorInst::TypeInstId,
.type_id = SemIR::ErrorInst::TypeId};
}
TypeExpr pointee_type_expr = MapNonPointerType(context, loc_id, pointee_type);
if (pointee_type_expr.inst_id == SemIR::ErrorInst::InstId ||
pointee_type_expr.type_id == SemIR::ErrorInst::TypeId) {
return {.inst_id = SemIR::ErrorInst::TypeInstId,
.type_id = SemIR::ErrorInst::TypeId};
}
SemIR::TypeId pointer_type_id =
GetPointerType(context, pointee_type_expr.inst_id);
return {.inst_id = context.types().GetInstId(pointer_type_id),
.type_id = pointer_type_id};
}
// Maps a C++ type to a Carbon type. `type` should not be canonicalized because
// we check for pointer nullability and nullability will be lost by
// canonicalization.
static auto MapType(Context& context, SemIR::LocId loc_id, clang::QualType type)
-> TypeExpr {
if (type->isPointerType()) {
return MapPointerType(context, loc_id, type);
}
return MapNonPointerType(context, loc_id, type);
}
// Returns a block id for the explicit parameters of the given function
// declaration. If the function declaration has no parameters, it returns
// `SemIR::InstBlockId::Empty`. In the case of an unsupported parameter type, it
@@ -731,7 +782,7 @@ static auto MakeParamPatternsBlockId(Context& context, SemIR::LocId loc_id,
llvm::SmallVector<SemIR::InstId> params;
params.reserve(clang_decl.parameters().size());
for (const clang::ParmVarDecl* param : clang_decl.parameters()) {
clang::QualType param_type = param->getType().getCanonicalType();
clang::QualType param_type = param->getType();
// Mark the start of a region of insts, needed for the type expression
// created later with the call of `EndSubpatternAsExpr()`.
@@ -784,7 +835,7 @@ static auto MakeParamPatternsBlockId(Context& context, SemIR::LocId loc_id,
static auto GetReturnType(Context& context, SemIR::LocId loc_id,
const clang::FunctionDecl* clang_decl)
-> SemIR::InstId {
clang::QualType ret_type = clang_decl->getReturnType().getCanonicalType();
clang::QualType ret_type = clang_decl->getReturnType();
if (ret_type->isVoidType()) {
return SemIR::InstId::None;
}