Skip to content

Commit f28f8fa

Browse files
committed
Support importing C++ _Nonnull pointers as function parameters or return values
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.
1 parent 601597e commit f28f8fa

File tree

7 files changed

+707
-145
lines changed

7 files changed

+707
-145
lines changed

toolchain/check/import_cpp.cpp

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -569,10 +569,13 @@ static auto MapRecordType(Context& context, SemIR::LocId loc_id,
569569
.type_id = SemIR::ErrorInst::TypeId};
570570
}
571571

572-
// Maps a C++ type to a Carbon type.
572+
// Maps a C++ non-pointer type to a Carbon type.
573573
// TODO: Support more types.
574-
static auto MapType(Context& context, SemIR::LocId loc_id, clang::QualType type)
575-
-> TypeExpr {
574+
static auto MapNonPointerType(Context& context, SemIR::LocId loc_id,
575+
clang::QualType type) -> TypeExpr {
576+
type = type.getCanonicalType();
577+
CARBON_CHECK(!type->isPointerType());
578+
576579
if (const auto* builtin_type = dyn_cast<clang::BuiltinType>(type)) {
577580
return MapBuiltinType(context, *builtin_type);
578581
}
@@ -585,6 +588,52 @@ static auto MapType(Context& context, SemIR::LocId loc_id, clang::QualType type)
585588
.type_id = SemIR::ErrorInst::TypeId};
586589
}
587590

591+
// Maps a C++ pointer type to a Carbon pointer type.
592+
static auto MapPointerType(Context& context, SemIR::LocId loc_id,
593+
clang::QualType type) -> TypeExpr {
594+
CARBON_CHECK(type->isPointerType());
595+
596+
if (auto nullability = type->getNullability();
597+
!nullability.has_value() ||
598+
*nullability != clang::NullabilityKind::NonNull) {
599+
context.TODO(loc_id, llvm::formatv("Unsupported: nullable pointer: {0}",
600+
type.getAsString()));
601+
return {.inst_id = SemIR::ErrorInst::TypeInstId,
602+
.type_id = SemIR::ErrorInst::TypeId};
603+
}
604+
605+
clang::QualType pointee_type = type->getPointeeType();
606+
607+
if (pointee_type->isAnyPointerType()) {
608+
context.TODO(loc_id,
609+
llvm::formatv("Unsupported: pointer to pointer type: {0}",
610+
pointee_type.getAsString()));
611+
return {.inst_id = SemIR::ErrorInst::TypeInstId,
612+
.type_id = SemIR::ErrorInst::TypeId};
613+
}
614+
615+
TypeExpr pointee_type_expr = MapNonPointerType(context, loc_id, pointee_type);
616+
if (pointee_type_expr.inst_id == SemIR::ErrorInst::InstId ||
617+
pointee_type_expr.type_id == SemIR::ErrorInst::TypeId) {
618+
return {.inst_id = SemIR::ErrorInst::TypeInstId,
619+
.type_id = SemIR::ErrorInst::TypeId};
620+
}
621+
622+
SemIR::TypeId pointer_type_id =
623+
GetPointerType(context, pointee_type_expr.inst_id);
624+
return {.inst_id = context.types().GetInstId(pointer_type_id),
625+
.type_id = pointer_type_id};
626+
}
627+
628+
// Maps a C++ type to a Carbon type.
629+
static auto MapType(Context& context, SemIR::LocId loc_id, clang::QualType type)
630+
-> TypeExpr {
631+
if (type->isPointerType()) {
632+
return MapPointerType(context, loc_id, type);
633+
}
634+
return MapNonPointerType(context, loc_id, type);
635+
}
636+
588637
// Returns a block id for the explicit parameters of the given function
589638
// declaration. If the function declaration has no parameters, it returns
590639
// `SemIR::InstBlockId::Empty`. In the case of an unsupported parameter type, it
@@ -600,7 +649,7 @@ static auto MakeParamPatternsBlockId(Context& context, SemIR::LocId loc_id,
600649
llvm::SmallVector<SemIR::InstId> params;
601650
params.reserve(clang_decl.parameters().size());
602651
for (const clang::ParmVarDecl* param : clang_decl.parameters()) {
603-
clang::QualType param_type = param->getType().getCanonicalType();
652+
clang::QualType param_type = param->getType();
604653

605654
// Mark the start of a region of insts, needed for the type expression
606655
// created later with the call of `EndSubpatternAsExpr()`.
@@ -653,7 +702,7 @@ static auto MakeParamPatternsBlockId(Context& context, SemIR::LocId loc_id,
653702
static auto GetReturnType(Context& context, SemIR::LocId loc_id,
654703
const clang::FunctionDecl* clang_decl)
655704
-> SemIR::InstId {
656-
clang::QualType ret_type = clang_decl->getReturnType().getCanonicalType();
705+
clang::QualType ret_type = clang_decl->getReturnType();
657706
if (ret_type->isVoidType()) {
658707
return SemIR::InstId::None;
659708
}

toolchain/check/testdata/interop/cpp/function/class.carbon

Lines changed: 49 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ fn F() {
3434
// CHECK:STDERR: Cpp.foo({});
3535
// CHECK:STDERR: ^~~~~~~
3636
// CHECK:STDERR:
37-
// CHECK:STDERR: fail_todo_import_decl_value_param_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: class C` [SemanticsTodo]
37+
// CHECK:STDERR: fail_todo_import_decl_value_param_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: C` [SemanticsTodo]
3838
// CHECK:STDERR: Cpp.foo({});
3939
// CHECK:STDERR: ^~~~~~~
4040
// 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() {
6363
// CHECK:STDERR: ^
6464
// CHECK:STDERR:
6565
let c: Cpp.C;
66-
// CHECK:STDERR: fail_todo_import_decl_value_param_type_previously_imported.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: class C` [SemanticsTodo]
66+
// CHECK:STDERR: fail_todo_import_decl_value_param_type_previously_imported.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: C` [SemanticsTodo]
6767
// CHECK:STDERR: Cpp.foo(c);
6868
// CHECK:STDERR: ^~~~~~~
6969
// CHECK:STDERR: fail_todo_import_decl_value_param_type_previously_imported.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
@@ -98,15 +98,15 @@ fn F() {
9898
// CHECK:STDERR: Cpp.foo1({});
9999
// CHECK:STDERR: ^~~~~~~~
100100
// CHECK:STDERR:
101-
// CHECK:STDERR: fail_todo_import_double_decl_value_param_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: class C` [SemanticsTodo]
101+
// CHECK:STDERR: fail_todo_import_double_decl_value_param_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: C` [SemanticsTodo]
102102
// CHECK:STDERR: Cpp.foo1({});
103103
// CHECK:STDERR: ^~~~~~~~
104104
// CHECK:STDERR: fail_todo_import_double_decl_value_param_type.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo1` [InCppNameLookup]
105105
// CHECK:STDERR: Cpp.foo1({});
106106
// CHECK:STDERR: ^~~~~~~~
107107
// CHECK:STDERR:
108108
Cpp.foo1({});
109-
// CHECK:STDERR: fail_todo_import_double_decl_value_param_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: class C` [SemanticsTodo]
109+
// CHECK:STDERR: fail_todo_import_double_decl_value_param_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: C` [SemanticsTodo]
110110
// CHECK:STDERR: Cpp.foo2({});
111111
// CHECK:STDERR: ^~~~~~~~
112112
// 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() {
283283

284284
class C;
285285

286-
auto foo(C*) -> void;
286+
auto foo(C* _Nonnull) -> void;
287287

288288
// --- fail_todo_import_decl_pointer_param_type.carbon
289289

@@ -300,7 +300,7 @@ import Cpp library "decl_pointer_param_type.h";
300300
// CHECK:STDERR:
301301
fn F(c: Cpp.C*) {
302302
//@dump-sem-ir-begin
303-
// CHECK:STDERR: fail_todo_import_decl_pointer_param_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: class C *` [SemanticsTodo]
303+
// CHECK:STDERR: fail_todo_import_decl_pointer_param_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: C * _Nonnull` [SemanticsTodo]
304304
// CHECK:STDERR: Cpp.foo(c);
305305
// CHECK:STDERR: ^~~~~~~
306306
// CHECK:STDERR: fail_todo_import_decl_pointer_param_type.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
@@ -319,23 +319,16 @@ fn F(c: Cpp.C*) {
319319

320320
class C {};
321321

322-
auto foo(C*) -> void;
322+
auto foo(C* _Nonnull) -> void;
323323

324-
// --- fail_todo_import_definition_pointer_param_type.carbon
324+
// --- import_definition_pointer_param_type.carbon
325325

326326
library "[[@TEST_NAME]]";
327327

328328
import Cpp library "definition_pointer_param_type.h";
329329

330330
fn F(c: Cpp.C*) {
331331
//@dump-sem-ir-begin
332-
// CHECK:STDERR: fail_todo_import_definition_pointer_param_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: parameter type: class C *` [SemanticsTodo]
333-
// CHECK:STDERR: Cpp.foo(c);
334-
// CHECK:STDERR: ^~~~~~~
335-
// CHECK:STDERR: fail_todo_import_definition_pointer_param_type.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
336-
// CHECK:STDERR: Cpp.foo(c);
337-
// CHECK:STDERR: ^~~~~~~
338-
// CHECK:STDERR:
339332
Cpp.foo(c);
340333
//@dump-sem-ir-end
341334
}
@@ -364,7 +357,7 @@ fn F() {
364357
// CHECK:STDERR: Cpp.foo();
365358
// CHECK:STDERR: ^~~~~~~
366359
// CHECK:STDERR:
367-
// CHECK:STDERR: fail_todo_import_decl_value_return_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: return type: class C` [SemanticsTodo]
360+
// CHECK:STDERR: fail_todo_import_decl_value_return_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: return type: C` [SemanticsTodo]
368361
// CHECK:STDERR: Cpp.foo();
369362
// CHECK:STDERR: ^~~~~~~
370363
// 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() {
404397

405398
class C;
406399

407-
auto foo() -> C*;
400+
auto foo() -> C* _Nonnull;
408401

409402
// --- fail_todo_import_decl_pointer_return_type.carbon
410403

@@ -414,7 +407,14 @@ import Cpp library "decl_pointer_return_type.h";
414407

415408
fn F() {
416409
//@dump-sem-ir-begin
417-
// CHECK:STDERR: fail_todo_import_decl_pointer_return_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: return type: class C *` [SemanticsTodo]
410+
// CHECK:STDERR: fail_todo_import_decl_pointer_return_type.carbon:[[@LINE+14]]:3: error: semantics TODO: `Unsupported: Record declarations without a definition` [SemanticsTodo]
411+
// CHECK:STDERR: Cpp.foo();
412+
// CHECK:STDERR: ^~~~~~~
413+
// CHECK:STDERR: fail_todo_import_decl_pointer_return_type.carbon:[[@LINE+11]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
414+
// CHECK:STDERR: Cpp.foo();
415+
// CHECK:STDERR: ^~~~~~~
416+
// CHECK:STDERR:
417+
// CHECK:STDERR: fail_todo_import_decl_pointer_return_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: return type: C * _Nonnull` [SemanticsTodo]
418418
// CHECK:STDERR: Cpp.foo();
419419
// CHECK:STDERR: ^~~~~~~
420420
// CHECK:STDERR: fail_todo_import_decl_pointer_return_type.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
@@ -433,23 +433,16 @@ fn F() {
433433

434434
class C {};
435435

436-
auto foo() -> C*;
436+
auto foo() -> C* _Nonnull;
437437

438-
// --- fail_todo_import_definition_pointer_return_type.carbon
438+
// --- import_definition_pointer_return_type.carbon
439439

440440
library "[[@TEST_NAME]]";
441441

442442
import Cpp library "definition_pointer_return_type.h";
443443

444444
fn F() {
445445
//@dump-sem-ir-begin
446-
// CHECK:STDERR: fail_todo_import_definition_pointer_return_type.carbon:[[@LINE+7]]:3: error: semantics TODO: `Unsupported: return type: class C *` [SemanticsTodo]
447-
// CHECK:STDERR: Cpp.foo();
448-
// CHECK:STDERR: ^~~~~~~
449-
// CHECK:STDERR: fail_todo_import_definition_pointer_return_type.carbon:[[@LINE+4]]:3: note: in `Cpp` name lookup for `foo` [InCppNameLookup]
450-
// CHECK:STDERR: Cpp.foo();
451-
// CHECK:STDERR: ^~~~~~~
452-
// CHECK:STDERR:
453446
Cpp.foo();
454447
//@dump-sem-ir-end
455448
}
@@ -904,27 +897,36 @@ fn F() {
904897
// CHECK:STDOUT: <elided>
905898
// CHECK:STDOUT: }
906899
// CHECK:STDOUT:
907-
// CHECK:STDOUT: --- fail_todo_import_definition_pointer_param_type.carbon
900+
// CHECK:STDOUT: --- import_definition_pointer_param_type.carbon
908901
// CHECK:STDOUT:
909902
// CHECK:STDOUT: constants {
910903
// CHECK:STDOUT: %C: type = class_type @C [concrete]
911904
// CHECK:STDOUT: %ptr: type = ptr_type %C [concrete]
905+
// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete]
906+
// CHECK:STDOUT: %foo.type: type = fn_type @foo [concrete]
907+
// CHECK:STDOUT: %foo: %foo.type = struct_value () [concrete]
912908
// CHECK:STDOUT: }
913909
// CHECK:STDOUT:
914910
// CHECK:STDOUT: imports {
915911
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
916912
// CHECK:STDOUT: .C = %C.decl
917-
// CHECK:STDOUT: .foo = <error>
913+
// CHECK:STDOUT: .foo = %foo.decl
918914
// CHECK:STDOUT: import Cpp//...
919915
// CHECK:STDOUT: }
920916
// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
917+
// CHECK:STDOUT: %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {
918+
// CHECK:STDOUT: <elided>
919+
// CHECK:STDOUT: } {
920+
// CHECK:STDOUT: <elided>
921+
// CHECK:STDOUT: }
921922
// CHECK:STDOUT: }
922923
// CHECK:STDOUT:
923924
// CHECK:STDOUT: fn @F(%c.param: %ptr) {
924925
// CHECK:STDOUT: !entry:
925-
// CHECK:STDOUT: %Cpp.ref.loc15: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
926-
// CHECK:STDOUT: %foo.ref: <error> = name_ref foo, <error> [concrete = <error>]
926+
// CHECK:STDOUT: %Cpp.ref.loc8: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
927+
// CHECK:STDOUT: %foo.ref: %foo.type = name_ref foo, imports.%foo.decl [concrete = constants.%foo]
927928
// CHECK:STDOUT: %c.ref: %ptr = name_ref c, %c
929+
// CHECK:STDOUT: %foo.call: init %empty_tuple.type = call %foo.ref(%c.ref)
928930
// CHECK:STDOUT: <elided>
929931
// CHECK:STDOUT: }
930932
// CHECK:STDOUT:
@@ -989,6 +991,7 @@ fn F() {
989991
// CHECK:STDOUT: imports {
990992
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
991993
// CHECK:STDOUT: .foo = <error>
994+
// CHECK:STDOUT: .C = <error>
992995
// CHECK:STDOUT: import Cpp//...
993996
// CHECK:STDOUT: }
994997
// CHECK:STDOUT: }
@@ -1000,22 +1003,34 @@ fn F() {
10001003
// CHECK:STDOUT: <elided>
10011004
// CHECK:STDOUT: }
10021005
// CHECK:STDOUT:
1003-
// CHECK:STDOUT: --- fail_todo_import_definition_pointer_return_type.carbon
1006+
// CHECK:STDOUT: --- import_definition_pointer_return_type.carbon
10041007
// CHECK:STDOUT:
10051008
// CHECK:STDOUT: constants {
1009+
// CHECK:STDOUT: %C: type = class_type @C [concrete]
1010+
// CHECK:STDOUT: %ptr: type = ptr_type %C [concrete]
1011+
// CHECK:STDOUT: %foo.type: type = fn_type @foo [concrete]
1012+
// CHECK:STDOUT: %foo: %foo.type = struct_value () [concrete]
10061013
// CHECK:STDOUT: }
10071014
// CHECK:STDOUT:
10081015
// CHECK:STDOUT: imports {
10091016
// CHECK:STDOUT: %Cpp: <namespace> = namespace file.%Cpp.import_cpp, [concrete] {
1010-
// CHECK:STDOUT: .foo = <error>
1017+
// CHECK:STDOUT: .foo = %foo.decl
1018+
// CHECK:STDOUT: .C = %C.decl
10111019
// CHECK:STDOUT: import Cpp//...
10121020
// CHECK:STDOUT: }
1021+
// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {}
1022+
// CHECK:STDOUT: %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] {
1023+
// CHECK:STDOUT: <elided>
1024+
// CHECK:STDOUT: } {
1025+
// CHECK:STDOUT: <elided>
1026+
// CHECK:STDOUT: }
10131027
// CHECK:STDOUT: }
10141028
// CHECK:STDOUT:
10151029
// CHECK:STDOUT: fn @F() {
10161030
// CHECK:STDOUT: !entry:
10171031
// CHECK:STDOUT: %Cpp.ref: <namespace> = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp]
1018-
// CHECK:STDOUT: %foo.ref: <error> = name_ref foo, <error> [concrete = <error>]
1032+
// CHECK:STDOUT: %foo.ref: %foo.type = name_ref foo, imports.%foo.decl [concrete = constants.%foo]
1033+
// CHECK:STDOUT: %foo.call: init %ptr = call %foo.ref()
10191034
// CHECK:STDOUT: <elided>
10201035
// CHECK:STDOUT: }
10211036
// CHECK:STDOUT:

0 commit comments

Comments
 (0)