Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions examples/interop/cpp/hello_world.carbon
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,17 @@ fn HelloPutchar() {

// Demonstrate passing a null-terminated string to a C++ function.
fn HelloStdio() {
// TODO: Requires mapping from Optional(const char*) into C++.
// TODO: There should be a better way to interact with functions that expect a
// null-terminated string.
// Cpp.puts(Cpp.std.data("Hello world!\n\0"));
Cpp.puts(Cpp.std.data("Hello world!\0"));

// TODO: Requires variadic function support.
// Cpp.printf("Hello world!\n\0");
}

// Demonstrate passing a string as a void pointer to a C++ function.
fn HelloWrite() {
// TODO: Requires mapping from Optional(const char*) into a C++ const void*.
// TODO: Requires conversion from `const char*` to `const void*`.
// let s: str = "Hello world!\n";
// Cpp.write(1, Cpp.std.data(s), Cpp.std.size(s));
}
Expand Down
167 changes: 111 additions & 56 deletions toolchain/check/cpp/type_mapping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,25 @@

namespace Carbon::Check {

// A function that wraps a C++ type to form another C++ type. Note that this is
// a raw function pointer; we don't currently use any lambda captures here. This
// can be replaced by a `std::function` if captures are found to be needed.
using WrapFn = auto (*)(Context& context, clang::QualType inner_type)
-> clang::QualType;

// Represents a type that requires a subtype to be mapped into a Clang type
// before it can be mapped.
struct WrappedType {
// The type contained in this wrapped type.
SemIR::TypeId inner_type_id;
// A function to construct the wrapped type from the mapped unwrapped type.
WrapFn wrap_fn;
};

// Possible results from attempting to map a type. A null QualType indicates
// that the type couldn't be mapped.
using TryMapTypeResult = std::variant<clang::QualType, WrappedType>;

// Find the bit width of an integer literal. Following the C++ standard rules
// for assigning a type to a decimal integer literal, the first signed integer
// in which the value could fit among bit widths of 32, 64 and 128 is selected.
Expand All @@ -44,9 +63,12 @@ static auto FindIntLiteralBitWidth(Context& context, SemIR::InstId arg_id)
// TODO: Add tests for these cases.
return IntId::None;
}
auto arg = context.insts().GetAs<SemIR::IntValue>(
auto arg = context.insts().TryGetAs<SemIR::IntValue>(
context.constant_values().GetInstId(arg_const_id));
llvm::APInt arg_val = context.ints().Get(arg.int_id);
if (!arg) {
return IntId::None;
}
llvm::APInt arg_val = context.ints().Get(arg->int_id);
int arg_non_sign_bits = arg_val.getSignificantBits() - 1;

if (arg_non_sign_bits >= 128) {
Expand All @@ -55,7 +77,7 @@ static auto FindIntLiteralBitWidth(Context& context, SemIR::InstId arg_id)
"integer type; requires {1} bits, but max is 128",
TypedInt, int);
context.emitter().Emit(arg_id, IntTooLargeForCppType,
{.type = arg.type_id, .value = arg_val},
{.type = arg->type_id, .value = arg_val},
arg_non_sign_bits + 1);
return IntId::None;
}
Expand Down Expand Up @@ -103,7 +125,7 @@ static auto LookupCppType(
// Maps a Carbon class type to a C++ type. Returns a null `QualType` if the
// type is not supported.
static auto TryMapClassType(Context& context, SemIR::ClassType class_type)
-> clang::QualType {
-> TryMapTypeResult {
clang::ASTContext& ast_context = context.ast_context();

// If the class was imported from C++, return the original C++ type.
Expand All @@ -126,10 +148,10 @@ static auto TryMapClassType(Context& context, SemIR::ClassType class_type)
break;
}
case SemIR::RecognizedTypeInfo::Numeric: {
// Carbon supports large bit width beyond C++ builtins; we don't need to
// translate those.
// Carbon supports large bit width beyond C++ builtins; we don't translate
// those into integer types.
if (!type_info.numeric.bit_width_id.is_embedded_value()) {
return clang::QualType();
break;
}
int bit_width = type_info.numeric.bit_width_id.AsValue();

Expand Down Expand Up @@ -164,6 +186,25 @@ static auto TryMapClassType(Context& context, SemIR::ClassType class_type)
case SemIR::RecognizedTypeInfo::CppVoidBase: {
return ast_context.VoidTy;
}
case SemIR::RecognizedTypeInfo::Optional: {
auto args = context.inst_blocks().GetOrEmpty(type_info.args_id);
if (args.size() == 1) {
auto arg_id = args[0];
if (auto facet = context.insts().TryGetAs<SemIR::FacetValue>(arg_id)) {
arg_id = facet->type_inst_id;
}
if (auto pointer_type =
context.insts().TryGetAs<SemIR::PointerType>(arg_id)) {
return WrappedType{
.inner_type_id = context.types().GetTypeIdForTypeInstId(
pointer_type->pointee_id),
.wrap_fn = [](Context& context, clang::QualType inner_type) {
return context.ast_context().getPointerType(inner_type);
}};
}
}
break;
}
case SemIR::RecognizedTypeInfo::Str: {
return LookupCppType(context, {"std", "string_view"});
}
Expand All @@ -175,12 +216,13 @@ static auto TryMapClassType(Context& context, SemIR::ClassType class_type)
return clang::QualType();
}

// Maps a non-wrapper (no const or pointer) Carbon type to a C++ type. Returns a
// null QualType if the type is not supported.
// Maps a Carbon type to a C++ type. Either returns the mapped type, a null type
// as a placeholder indicating the type can't be mapped, or a `WrappedType`
// representing a type that needs more work before it can be mapped.
// TODO: Have both Carbon -> C++ and C++ -> Carbon mappings in a single place
// to keep them in sync.
static auto MapNonWrapperType(Context& context, SemIR::InstId inst_id,
SemIR::TypeId type_id) -> clang::QualType {
static auto TryMapType(Context& context, SemIR::TypeId type_id)
-> TryMapTypeResult {
auto type_inst = context.types().GetAsInst(type_id);

CARBON_KIND_SWITCH(type_inst) {
Expand All @@ -193,67 +235,62 @@ static auto MapNonWrapperType(Context& context, SemIR::InstId inst_id,
case CARBON_KIND(SemIR::ClassType class_type): {
return TryMapClassType(context, class_type);
}
case SemIR::IntLiteralType::Kind: {
IntId bit_width_id = FindIntLiteralBitWidth(context, inst_id);
if (bit_width_id == IntId::None) {
return clang::QualType();
}
return context.ast_context().getIntTypeForBitwidth(bit_width_id.AsValue(),
true);
case CARBON_KIND(SemIR::ConstType const_type): {
return WrappedType{
.inner_type_id =
context.types().GetTypeIdForTypeInstId(const_type.inner_id),
.wrap_fn = [](Context& /*context*/, clang::QualType inner_type) {
return inner_type.withConst();
}};
}
case SemIR::FloatLiteralType::Kind: {
return context.ast_context().DoubleTy;
}
case CARBON_KIND(SemIR::PointerType pointer_type): {
return WrappedType{
.inner_type_id =
context.types().GetTypeIdForTypeInstId(pointer_type.pointee_id),
.wrap_fn = [](Context& context, clang::QualType inner_type) {
auto pointer_type =
context.ast_context().getPointerType(inner_type);
return context.ast_context().getAttributedType(
clang::attr::TypeNonNull, pointer_type, pointer_type);
}};
}

default: {
return clang::QualType();
}
}

return clang::QualType();
}

// Maps a Carbon type to a C++ type. Accepts an InstId, representing a value
// whose type is mapped to a C++ type. Returns `clang::QualType` if the mapping
// Maps a Carbon type to a C++ type. Returns `clang::QualType` if the mapping
// succeeds, or `clang::QualType::isNull()` if the type is not supported.
// TODO: unify this with the C++ to Carbon type mapping function.
static auto MapToCppType(Context& context, SemIR::InstId inst_id)
static auto MapToCppType(Context& context, SemIR::TypeId type_id)
-> clang::QualType {
auto type_id = context.insts().Get(inst_id).type_id();
llvm::SmallVector<SemIR::TypeId> wrapper_types;
llvm::SmallVector<WrapFn> wrap_fns;
while (true) {
SemIR::TypeId orig_type_id = type_id;
if (auto const_type = context.types().TryGetAs<SemIR::ConstType>(type_id);
const_type) {
type_id = context.types().GetTypeIdForTypeInstId(const_type->inner_id);
} else if (auto pointer_type =
context.types().TryGetAs<SemIR::PointerType>(type_id);
pointer_type) {
type_id =
context.types().GetTypeIdForTypeInstId(pointer_type->pointee_id);
} else {
break;
}
wrapper_types.push_back(orig_type_id);
}

clang::QualType mapped_type = MapNonWrapperType(context, inst_id, type_id);
if (mapped_type.isNull()) {
return mapped_type;
}
CARBON_KIND_SWITCH(TryMapType(context, type_id)) {
case CARBON_KIND(clang::QualType type): {
for (auto wrap_fn : llvm::reverse(wrap_fns)) {
if (type.isNull()) {
break;
}
type = wrap_fn(context, type);
}
return type;
}

for (auto wrapper_type_id : llvm::reverse(wrapper_types)) {
if (auto const_type =
context.types().TryGetAs<SemIR::ConstType>(wrapper_type_id);
const_type) {
mapped_type.addConst();
} else if (context.types().TryGetAs<SemIR::PointerType>(wrapper_type_id)) {
auto pointer_type = context.ast_context().getPointerType(mapped_type);
mapped_type = context.ast_context().getAttributedType(
clang::attr::TypeNonNull, pointer_type, pointer_type);
} else {
return clang::QualType();
case CARBON_KIND(WrappedType wrapped): {
wrap_fns.push_back(wrapped.wrap_fn);
type_id = wrapped.inner_type_id;
break;
}
}
}

return mapped_type;
}

auto InventClangArg(Context& context, SemIR::InstId arg_id) -> clang::Expr* {
Expand Down Expand Up @@ -296,7 +333,25 @@ auto InventClangArg(Context& context, SemIR::InstId arg_id) -> clang::Expr* {
return nullptr;
}

clang::QualType arg_cpp_type = MapToCppType(context, arg_id);
clang::QualType arg_cpp_type;

// Special case: if the argument is an integer literal, look at its value.
// TODO: Consider producing a `clang::IntegerLiteral` in this case instead, so
// that C++ overloads that behave differently for zero-valued int literals can
// recognize it.
auto type_id = context.insts().Get(arg_id).type_id();
if (context.types().Is<SemIR::IntLiteralType>(type_id)) {
IntId bit_width_id = FindIntLiteralBitWidth(context, arg_id);
if (bit_width_id != IntId::None) {
arg_cpp_type = context.ast_context().getIntTypeForBitwidth(
bit_width_id.AsValue(), true);
}
}

if (arg_cpp_type.isNull()) {
arg_cpp_type = MapToCppType(context, type_id);
}

if (arg_cpp_type.isNull()) {
CARBON_DIAGNOSTIC(CppCallArgTypeNotSupported, Error,
"call argument of type {0} is not supported",
Expand Down
Loading
Loading