mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-05 22:02:55 +01:00
Support passing Carbon Optional(T*) to C++ T* parameter. (#6422)
We already did this translation in the other direction, but we had no mapping from `Optional(T)` to anything, so round-tripping a nullable pointer from C++ through Carbon and back to C++ was previously rejected.
This commit is contained in:
@@ -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.
|
||||
@@ -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) {
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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();
|
||||
|
||||
@@ -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"});
|
||||
}
|
||||
@@ -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) {
|
||||
@@ -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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
clang::QualType mapped_type = MapNonWrapperType(context, inst_id, type_id);
|
||||
if (mapped_type.isNull()) {
|
||||
return mapped_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* {
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user