diff --git a/examples/interop/cpp/hello_world.carbon b/examples/interop/cpp/hello_world.carbon index 99edd7a42f5ce..b0ed5b5ac1aed 100644 --- a/examples/interop/cpp/hello_world.carbon +++ b/examples/interop/cpp/hello_world.carbon @@ -27,10 +27,9 @@ 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"); @@ -38,7 +37,7 @@ fn HelloStdio() { // 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)); } diff --git a/toolchain/check/cpp/type_mapping.cpp b/toolchain/check/cpp/type_mapping.cpp index ef0955e0b9a1a..2155938c6f91d 100644 --- a/toolchain/check/cpp/type_mapping.cpp +++ b/toolchain/check/cpp/type_mapping.cpp @@ -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; + // 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( + auto arg = context.insts().TryGetAs( 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(arg_id)) { + arg_id = facet->type_inst_id; + } + if (auto pointer_type = + context.insts().TryGetAs(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 wrapper_types; + llvm::SmallVector wrap_fns; while (true) { - SemIR::TypeId orig_type_id = type_id; - if (auto const_type = context.types().TryGetAs(type_id); - const_type) { - type_id = context.types().GetTypeIdForTypeInstId(const_type->inner_id); - } else if (auto pointer_type = - context.types().TryGetAs(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(wrapper_type_id); - const_type) { - mapped_type.addConst(); - } else if (context.types().TryGetAs(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(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", diff --git a/toolchain/check/testdata/interop/cpp/function/pointer.carbon b/toolchain/check/testdata/interop/cpp/function/pointer.carbon index 6d88c52169caa..f3204378dee7a 100644 --- a/toolchain/check/testdata/interop/cpp/function/pointer.carbon +++ b/toolchain/check/testdata/interop/cpp/function/pointer.carbon @@ -296,7 +296,9 @@ struct S {}; auto foo(S*) -> void; -// --- import_nullable_pointer_param.carbon +auto get() -> S*; + +// --- non_nullable_pointer_arg_to_pointer_param.carbon library "[[@TEST_NAME]]"; @@ -309,7 +311,7 @@ fn F() { //@dump-sem-ir-end } -// --- fail_todo_import_null_pointer_param.carbon +// --- null_pointer_arg_to_pointer_param.carbon library "[[@TEST_NAME]]"; @@ -317,14 +319,35 @@ import Cpp library "nullable_pointer_param.h"; fn F() { //@dump-sem-ir-begin - // CHECK:STDERR: fail_todo_import_null_pointer_param.carbon:[[@LINE+4]]:11: error: call argument of type `Core.Optional(Cpp.S* as Core.OptionalStorage)` is not supported [CppCallArgTypeNotSupported] - // CHECK:STDERR: Cpp.foo(Core.Optional(Cpp.S*).None()); - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: Cpp.foo(Core.Optional(Cpp.S*).None()); //@dump-sem-ir-end } +// --- nonnull_pointer_arg_to_pointer_param.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "nullable_pointer_param.h"; + +fn F() { + //@dump-sem-ir-begin + var s: Cpp.S = {}; + Cpp.foo(Core.Optional(Cpp.S*).Some(&s)); + //@dump-sem-ir-end +} + +// --- forward_nullable_pointer.carbon + +library "[[@TEST_NAME]]"; + +import Cpp library "nullable_pointer_param.h"; + +fn F() { + //@dump-sem-ir-begin + Cpp.foo(Cpp.get()); + //@dump-sem-ir-end +} + // ============================================================================ // Deduced pointer type as template argument // ============================================================================ @@ -1281,7 +1304,7 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- import_nullable_pointer_param.carbon +// CHECK:STDOUT: --- non_nullable_pointer_arg_to_pointer_param.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] @@ -1403,9 +1426,10 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_import_null_pointer_param.carbon +// CHECK:STDOUT: --- null_pointer_arg_to_pointer_param.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %foo.cpp_overload_set.type: type = cpp_overload_set_type @foo.cpp_overload_set [concrete] // CHECK:STDOUT: %foo.cpp_overload_set.value: %foo.cpp_overload_set.type = cpp_overload_set_value @foo.cpp_overload_set [concrete] // CHECK:STDOUT: %Optional.type: type = generic_class_type @Optional [concrete] @@ -1421,17 +1445,25 @@ fn F() { // CHECK:STDOUT: %MaybeUnformed.cff: type = class_type @MaybeUnformed, @MaybeUnformed(%ptr.4f0) [symbolic] // CHECK:STDOUT: %ptr.as.OptionalStorage.impl.None.type.8ed: type = fn_type @ptr.as.OptionalStorage.impl.None, @ptr.as.OptionalStorage.impl(%T.d9f) [symbolic] // CHECK:STDOUT: %ptr.as.OptionalStorage.impl.None.41a: %ptr.as.OptionalStorage.impl.None.type.8ed = struct_value () [symbolic] +// CHECK:STDOUT: %type_where: type = facet_type > [concrete] +// CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %OptionalStorage.impl_witness.bed: = impl_witness imports.%OptionalStorage.impl_witness_table.f52, @ptr.as.OptionalStorage.impl(%S) [concrete] // CHECK:STDOUT: %OptionalStorage.facet: %OptionalStorage.type = facet_value %ptr.5c7, (%OptionalStorage.impl_witness.bed) [concrete] // CHECK:STDOUT: %Optional.91c: type = class_type @Optional, @Optional(%OptionalStorage.facet) [concrete] // CHECK:STDOUT: %Optional.None.type.410: type = fn_type @Optional.None, @Optional(%OptionalStorage.facet) [concrete] // CHECK:STDOUT: %Optional.None.d01: %Optional.None.type.410 = struct_value () [concrete] // CHECK:STDOUT: %Optional.None.specific_fn: = specific_function %Optional.None.d01, @Optional.None(%OptionalStorage.facet) [concrete] +// CHECK:STDOUT: %foo.type: type = fn_type @foo [concrete] +// CHECK:STDOUT: %foo: %foo.type = struct_value () [concrete] +// CHECK:STDOUT: %facet_value.ef0: %type_where = facet_value %Optional.91c, () [concrete] +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.type.e5d: type = fn_type @DestroyT.binding.as_type.as.Destroy.impl.Op, @DestroyT.binding.as_type.as.Destroy.impl(%facet_value.ef0) [concrete] +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.6b6: %DestroyT.binding.as_type.as.Destroy.impl.Op.type.e5d = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Optional = %Core.Optional +// CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } @@ -1450,24 +1482,252 @@ fn F() { // CHECK:STDOUT: %Core.import_ref.6a9 = import_ref Core//prelude/types/optional, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: %Core.import_ref.971 = import_ref Core//prelude/types/optional, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: %OptionalStorage.impl_witness_table.f52 = impl_witness_table (%Core.import_ref.2fb, %Core.import_ref.1d4, %Core.import_ref.720, %Core.import_ref.6a9, %Core.import_ref.971), @ptr.as.OptionalStorage.impl [concrete] +// CHECK:STDOUT: %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: %.loc8_39.1: type = splice_block %Optional [concrete = constants.%Optional.91c] { +// CHECK:STDOUT: %OptionalStorage.facet: %OptionalStorage.type = facet_value constants.%ptr.5c7, (constants.%OptionalStorage.impl_witness.bed) [concrete = constants.%OptionalStorage.facet] +// CHECK:STDOUT: %.loc8_39.2: %OptionalStorage.type = converted constants.%ptr.5c7, %OptionalStorage.facet [concrete = constants.%OptionalStorage.facet] +// CHECK:STDOUT: %Optional: type = class_type @Optional, @Optional(constants.%OptionalStorage.facet) [concrete = constants.%Optional.91c] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %Cpp.ref.loc12_3: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %Cpp.ref.loc8_3: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] // CHECK:STDOUT: %foo.ref: %foo.cpp_overload_set.type = name_ref foo, imports.%foo.cpp_overload_set.value [concrete = constants.%foo.cpp_overload_set.value] // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Optional.ref: %Optional.type = name_ref Optional, imports.%Core.Optional [concrete = constants.%Optional.generic] -// CHECK:STDOUT: %Cpp.ref.loc12_25: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %Cpp.ref.loc8_25: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] // CHECK:STDOUT: %S.ref: type = name_ref S, imports.%S.decl [concrete = constants.%S] // CHECK:STDOUT: %ptr: type = ptr_type %S.ref [concrete = constants.%ptr.5c7] // CHECK:STDOUT: %OptionalStorage.facet: %OptionalStorage.type = facet_value %ptr, (constants.%OptionalStorage.impl_witness.bed) [concrete = constants.%OptionalStorage.facet] -// CHECK:STDOUT: %.loc12_31: %OptionalStorage.type = converted %ptr, %OptionalStorage.facet [concrete = constants.%OptionalStorage.facet] +// CHECK:STDOUT: %.loc8_31: %OptionalStorage.type = converted %ptr, %OptionalStorage.facet [concrete = constants.%OptionalStorage.facet] // CHECK:STDOUT: %Optional: type = class_type @Optional, @Optional(constants.%OptionalStorage.facet) [concrete = constants.%Optional.91c] -// CHECK:STDOUT: %.loc12_32: %Optional.None.type.410 = specific_constant imports.%Core.import_ref.f1d, @Optional(constants.%OptionalStorage.facet) [concrete = constants.%Optional.None.d01] -// CHECK:STDOUT: %None.ref: %Optional.None.type.410 = name_ref None, %.loc12_32 [concrete = constants.%Optional.None.d01] +// CHECK:STDOUT: %.loc8_32: %Optional.None.type.410 = specific_constant imports.%Core.import_ref.f1d, @Optional(constants.%OptionalStorage.facet) [concrete = constants.%Optional.None.d01] +// CHECK:STDOUT: %None.ref: %Optional.None.type.410 = name_ref None, %.loc8_32 [concrete = constants.%Optional.None.d01] // CHECK:STDOUT: %Optional.None.specific_fn: = specific_function %None.ref, @Optional.None(constants.%OptionalStorage.facet) [concrete = constants.%Optional.None.specific_fn] // CHECK:STDOUT: %Optional.None.call: init %Optional.91c = call %Optional.None.specific_fn() +// CHECK:STDOUT: %.loc8_38.1: ref %Optional.91c = temporary_storage +// CHECK:STDOUT: %.loc8_38.2: ref %Optional.91c = temporary %.loc8_38.1, %Optional.None.call +// CHECK:STDOUT: %.loc8_38.3: %Optional.91c = acquire_value %.loc8_38.2 +// CHECK:STDOUT: %foo.call: init %empty_tuple.type = call imports.%foo.decl(%.loc8_38.3) +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.bound: = bound_method %.loc8_38.2, constants.%DestroyT.binding.as_type.as.Destroy.impl.Op.6b6 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method: = bound_method %.loc8_38.2, %DestroyT.binding.as_type.as.Destroy.impl.Op.specific_fn +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%.loc8_38.2) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- nonnull_pointer_arg_to_pointer_param.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %S: type = class_type @S [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %pattern_type.7da: type = pattern_type %S [concrete] +// CHECK:STDOUT: %empty_struct: %empty_struct_type = struct_value () [concrete] +// CHECK:STDOUT: %S.val: %S = struct_value () [concrete] +// CHECK:STDOUT: %foo.cpp_overload_set.type: type = cpp_overload_set_type @foo.cpp_overload_set [concrete] +// CHECK:STDOUT: %foo.cpp_overload_set.value: %foo.cpp_overload_set.type = cpp_overload_set_value @foo.cpp_overload_set [concrete] +// CHECK:STDOUT: %Optional.type: type = generic_class_type @Optional [concrete] +// CHECK:STDOUT: %Optional.generic: %Optional.type = struct_value () [concrete] +// CHECK:STDOUT: %OptionalStorage.type: type = facet_type <@OptionalStorage> [concrete] +// CHECK:STDOUT: %T.3fe: %OptionalStorage.type = symbolic_binding T, 0 [symbolic] +// CHECK:STDOUT: %Optional.Some.type.32b: type = fn_type @Optional.Some, @Optional(%T.3fe) [symbolic] +// CHECK:STDOUT: %Optional.Some.249: %Optional.Some.type.32b = struct_value () [symbolic] +// CHECK:STDOUT: %ptr.5c7: type = ptr_type %S [concrete] +// CHECK:STDOUT: %T.d9f: type = symbolic_binding T, 0 [symbolic] +// CHECK:STDOUT: %ptr.4f0: type = ptr_type %T.d9f [symbolic] +// CHECK:STDOUT: %MaybeUnformed.cff: type = class_type @MaybeUnformed, @MaybeUnformed(%ptr.4f0) [symbolic] +// CHECK:STDOUT: %ptr.as.OptionalStorage.impl.Some.type.911: type = fn_type @ptr.as.OptionalStorage.impl.Some, @ptr.as.OptionalStorage.impl(%T.d9f) [symbolic] +// CHECK:STDOUT: %ptr.as.OptionalStorage.impl.Some.2a0: %ptr.as.OptionalStorage.impl.Some.type.911 = struct_value () [symbolic] +// CHECK:STDOUT: %type_where: type = facet_type > [concrete] +// CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] +// CHECK:STDOUT: %OptionalStorage.impl_witness.fef: = impl_witness imports.%OptionalStorage.impl_witness_table.236, @ptr.as.OptionalStorage.impl(%S) [concrete] +// CHECK:STDOUT: %OptionalStorage.facet: %OptionalStorage.type = facet_value %ptr.5c7, (%OptionalStorage.impl_witness.fef) [concrete] +// CHECK:STDOUT: %Optional.454: type = class_type @Optional, @Optional(%OptionalStorage.facet) [concrete] +// CHECK:STDOUT: %Optional.Some.type.602: type = fn_type @Optional.Some, @Optional(%OptionalStorage.facet) [concrete] +// CHECK:STDOUT: %Optional.Some.872: %Optional.Some.type.602 = struct_value () [concrete] +// CHECK:STDOUT: %Optional.Some.specific_fn: = specific_function %Optional.Some.872, @Optional.Some(%OptionalStorage.facet) [concrete] +// CHECK:STDOUT: %foo.type: type = fn_type @foo [concrete] +// CHECK:STDOUT: %foo: %foo.type = struct_value () [concrete] +// CHECK:STDOUT: %facet_value.542: %type_where = facet_value %Optional.454, () [concrete] +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.type.6e8: type = fn_type @DestroyT.binding.as_type.as.Destroy.impl.Op, @DestroyT.binding.as_type.as.Destroy.impl(%facet_value.542) [concrete] +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.de7: %DestroyT.binding.as_type.as.Destroy.impl.Op.type.6e8 = struct_value () [concrete] +// CHECK:STDOUT: %facet_value.7bd: %type_where = facet_value %S, () [concrete] +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.type.552: type = fn_type @DestroyT.binding.as_type.as.Destroy.impl.Op, @DestroyT.binding.as_type.as.Destroy.impl(%facet_value.7bd) [concrete] +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.572: %DestroyT.binding.as_type.as.Destroy.impl.Op.type.552 = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { +// CHECK:STDOUT: .Optional = %Core.Optional +// CHECK:STDOUT: .Destroy = %Core.Destroy +// CHECK:STDOUT: import Core//prelude +// CHECK:STDOUT: import Core//prelude/... +// CHECK:STDOUT: } +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .S = %S.decl +// CHECK:STDOUT: .foo = %foo.cpp_overload_set.value +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: %S.decl: type = class_decl @S [concrete = constants.%S] {} {} +// CHECK:STDOUT: %foo.cpp_overload_set.value: %foo.cpp_overload_set.type = cpp_overload_set_value @foo.cpp_overload_set [concrete = constants.%foo.cpp_overload_set.value] +// CHECK:STDOUT: %Core.Optional: %Optional.type = import_ref Core//prelude/types/optional, Optional, loaded [concrete = constants.%Optional.generic] +// CHECK:STDOUT: %Core.import_ref.c9e: @Optional.%Optional.Some.type (%Optional.Some.type.32b) = import_ref Core//prelude/types/optional, loc{{\d+_\d+}}, loaded [symbolic = @Optional.%Optional.Some (constants.%Optional.Some.249)] +// CHECK:STDOUT: %Core.import_ref.2fb: type = import_ref Core//prelude/types/optional, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.OptionalStorage.impl.%MaybeUnformed (constants.%MaybeUnformed.cff)] +// CHECK:STDOUT: %Core.import_ref.a7c = import_ref Core//prelude/types/optional, loc{{\d+_\d+}}, unloaded +// CHECK:STDOUT: %Core.import_ref.1b2: @ptr.as.OptionalStorage.impl.%ptr.as.OptionalStorage.impl.Some.type (%ptr.as.OptionalStorage.impl.Some.type.911) = import_ref Core//prelude/types/optional, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.OptionalStorage.impl.%ptr.as.OptionalStorage.impl.Some (constants.%ptr.as.OptionalStorage.impl.Some.2a0)] +// CHECK:STDOUT: %Core.import_ref.6a9 = import_ref Core//prelude/types/optional, loc{{\d+_\d+}}, unloaded +// CHECK:STDOUT: %Core.import_ref.971 = import_ref Core//prelude/types/optional, loc{{\d+_\d+}}, unloaded +// CHECK:STDOUT: %OptionalStorage.impl_witness_table.236 = impl_witness_table (%Core.import_ref.2fb, %Core.import_ref.a7c, %Core.import_ref.1b2, %Core.import_ref.6a9, %Core.import_ref.971), @ptr.as.OptionalStorage.impl [concrete] +// CHECK:STDOUT: %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: %.loc9_41.1: type = splice_block %Optional [concrete = constants.%Optional.454] { +// CHECK:STDOUT: %OptionalStorage.facet: %OptionalStorage.type = facet_value constants.%ptr.5c7, (constants.%OptionalStorage.impl_witness.fef) [concrete = constants.%OptionalStorage.facet] +// CHECK:STDOUT: %.loc9_41.2: %OptionalStorage.type = converted constants.%ptr.5c7, %OptionalStorage.facet [concrete = constants.%OptionalStorage.facet] +// CHECK:STDOUT: %Optional: type = class_type @Optional, @Optional(constants.%OptionalStorage.facet) [concrete = constants.%Optional.454] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/destroy, Destroy, loaded [concrete = constants.%Destroy.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %s.patt: %pattern_type.7da = ref_binding_pattern s [concrete] +// CHECK:STDOUT: %s.var_patt: %pattern_type.7da = var_pattern %s.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %s.var: ref %S = var %s.var_patt +// CHECK:STDOUT: %.loc8_19.1: %empty_struct_type = struct_literal () [concrete = constants.%empty_struct] +// CHECK:STDOUT: %.loc8_19.2: init %S = class_init (), %s.var [concrete = constants.%S.val] +// CHECK:STDOUT: %.loc8_3: init %S = converted %.loc8_19.1, %.loc8_19.2 [concrete = constants.%S.val] +// CHECK:STDOUT: assign %s.var, %.loc8_3 +// CHECK:STDOUT: %.loc8_13: type = splice_block %S.ref.loc8 [concrete = constants.%S] { +// CHECK:STDOUT: %Cpp.ref.loc8: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %S.ref.loc8: type = name_ref S, imports.%S.decl [concrete = constants.%S] +// CHECK:STDOUT: } +// CHECK:STDOUT: %s: ref %S = ref_binding s, %s.var +// CHECK:STDOUT: %Cpp.ref.loc9_3: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %foo.ref: %foo.cpp_overload_set.type = name_ref foo, imports.%foo.cpp_overload_set.value [concrete = constants.%foo.cpp_overload_set.value] +// CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] +// CHECK:STDOUT: %Optional.ref: %Optional.type = name_ref Optional, imports.%Core.Optional [concrete = constants.%Optional.generic] +// CHECK:STDOUT: %Cpp.ref.loc9_25: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %S.ref.loc9: type = name_ref S, imports.%S.decl [concrete = constants.%S] +// CHECK:STDOUT: %ptr: type = ptr_type %S.ref.loc9 [concrete = constants.%ptr.5c7] +// CHECK:STDOUT: %OptionalStorage.facet.loc9_31: %OptionalStorage.type = facet_value %ptr, (constants.%OptionalStorage.impl_witness.fef) [concrete = constants.%OptionalStorage.facet] +// CHECK:STDOUT: %.loc9_31: %OptionalStorage.type = converted %ptr, %OptionalStorage.facet.loc9_31 [concrete = constants.%OptionalStorage.facet] +// CHECK:STDOUT: %Optional: type = class_type @Optional, @Optional(constants.%OptionalStorage.facet) [concrete = constants.%Optional.454] +// CHECK:STDOUT: %.loc9_32: %Optional.Some.type.602 = specific_constant imports.%Core.import_ref.c9e, @Optional(constants.%OptionalStorage.facet) [concrete = constants.%Optional.Some.872] +// CHECK:STDOUT: %Some.ref: %Optional.Some.type.602 = name_ref Some, %.loc9_32 [concrete = constants.%Optional.Some.872] +// CHECK:STDOUT: %s.ref: ref %S = name_ref s, %s +// CHECK:STDOUT: %addr: %ptr.5c7 = addr_of %s.ref +// CHECK:STDOUT: %OptionalStorage.facet.loc9_40.1: %OptionalStorage.type = facet_value constants.%ptr.5c7, (constants.%OptionalStorage.impl_witness.fef) [concrete = constants.%OptionalStorage.facet] +// CHECK:STDOUT: %.loc9_40.1: %OptionalStorage.type = converted constants.%ptr.5c7, %OptionalStorage.facet.loc9_40.1 [concrete = constants.%OptionalStorage.facet] +// CHECK:STDOUT: %OptionalStorage.facet.loc9_40.2: %OptionalStorage.type = facet_value constants.%ptr.5c7, (constants.%OptionalStorage.impl_witness.fef) [concrete = constants.%OptionalStorage.facet] +// CHECK:STDOUT: %.loc9_40.2: %OptionalStorage.type = converted constants.%ptr.5c7, %OptionalStorage.facet.loc9_40.2 [concrete = constants.%OptionalStorage.facet] +// CHECK:STDOUT: %Optional.Some.specific_fn: = specific_function %Some.ref, @Optional.Some(constants.%OptionalStorage.facet) [concrete = constants.%Optional.Some.specific_fn] +// CHECK:STDOUT: %Optional.Some.call: init %Optional.454 = call %Optional.Some.specific_fn(%addr) +// CHECK:STDOUT: %.loc9_40.3: ref %Optional.454 = temporary_storage +// CHECK:STDOUT: %.loc9_40.4: ref %Optional.454 = temporary %.loc9_40.3, %Optional.Some.call +// CHECK:STDOUT: %.loc9_40.5: %Optional.454 = acquire_value %.loc9_40.4 +// CHECK:STDOUT: %foo.call: init %empty_tuple.type = call imports.%foo.decl(%.loc9_40.5) +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.bound.loc9: = bound_method %.loc9_40.4, constants.%DestroyT.binding.as_type.as.Destroy.impl.Op.de7 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc9: = bound_method %.loc9_40.4, %DestroyT.binding.as_type.as.Destroy.impl.Op.specific_fn.1 +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.call.loc9: init %empty_tuple.type = call %bound_method.loc9(%.loc9_40.4) +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.bound.loc8: = bound_method %s.var, constants.%DestroyT.binding.as_type.as.Destroy.impl.Op.572 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc8: = bound_method %s.var, %DestroyT.binding.as_type.as.Destroy.impl.Op.specific_fn.2 +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.call.loc8: init %empty_tuple.type = call %bound_method.loc8(%s.var) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- forward_nullable_pointer.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %foo.cpp_overload_set.type: type = cpp_overload_set_type @foo.cpp_overload_set [concrete] +// CHECK:STDOUT: %foo.cpp_overload_set.value: %foo.cpp_overload_set.type = cpp_overload_set_value @foo.cpp_overload_set [concrete] +// CHECK:STDOUT: %get.cpp_overload_set.type: type = cpp_overload_set_type @get.cpp_overload_set [concrete] +// CHECK:STDOUT: %get.cpp_overload_set.value: %get.cpp_overload_set.type = cpp_overload_set_value @get.cpp_overload_set [concrete] +// CHECK:STDOUT: %S: type = class_type @S [concrete] +// CHECK:STDOUT: %ptr.5c7: type = ptr_type %S [concrete] +// CHECK:STDOUT: %OptionalStorage.type: type = facet_type <@OptionalStorage> [concrete] +// CHECK:STDOUT: %T.d9f: type = symbolic_binding T, 0 [symbolic] +// CHECK:STDOUT: %ptr.4f0: type = ptr_type %T.d9f [symbolic] +// CHECK:STDOUT: %MaybeUnformed.cff: type = class_type @MaybeUnformed, @MaybeUnformed(%ptr.4f0) [symbolic] +// CHECK:STDOUT: %type_where: type = facet_type > [concrete] +// CHECK:STDOUT: %OptionalStorage.impl_witness.81d: = impl_witness imports.%OptionalStorage.impl_witness_table.2f8, @ptr.as.OptionalStorage.impl(%S) [concrete] +// CHECK:STDOUT: %OptionalStorage.facet: %OptionalStorage.type = facet_value %ptr.5c7, (%OptionalStorage.impl_witness.81d) [concrete] +// CHECK:STDOUT: %Optional.4c2: type = class_type @Optional, @Optional(%OptionalStorage.facet) [concrete] +// CHECK:STDOUT: %get.type: type = fn_type @get [concrete] +// CHECK:STDOUT: %get: %get.type = struct_value () [concrete] +// CHECK:STDOUT: %foo.type: type = fn_type @foo [concrete] +// CHECK:STDOUT: %foo: %foo.type = struct_value () [concrete] +// CHECK:STDOUT: %facet_value.69a: %type_where = facet_value %Optional.4c2, () [concrete] +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.type.fc8: type = fn_type @DestroyT.binding.as_type.as.Destroy.impl.Op, @DestroyT.binding.as_type.as.Destroy.impl(%facet_value.69a) [concrete] +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.da5: %DestroyT.binding.as_type.as.Destroy.impl.Op.type.fc8 = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { +// CHECK:STDOUT: .foo = %foo.cpp_overload_set.value +// CHECK:STDOUT: .get = %get.cpp_overload_set.value +// CHECK:STDOUT: import Cpp//... +// CHECK:STDOUT: } +// CHECK:STDOUT: %foo.cpp_overload_set.value: %foo.cpp_overload_set.type = cpp_overload_set_value @foo.cpp_overload_set [concrete = constants.%foo.cpp_overload_set.value] +// CHECK:STDOUT: %get.cpp_overload_set.value: %get.cpp_overload_set.type = cpp_overload_set_value @get.cpp_overload_set [concrete = constants.%get.cpp_overload_set.value] +// CHECK:STDOUT: %Core.import_ref.2fb: type = import_ref Core//prelude/types/optional, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.OptionalStorage.impl.%MaybeUnformed (constants.%MaybeUnformed.cff)] +// CHECK:STDOUT: %Core.import_ref.a7c = import_ref Core//prelude/types/optional, loc{{\d+_\d+}}, unloaded +// CHECK:STDOUT: %Core.import_ref.720 = import_ref Core//prelude/types/optional, loc{{\d+_\d+}}, unloaded +// CHECK:STDOUT: %Core.import_ref.6a9 = import_ref Core//prelude/types/optional, loc{{\d+_\d+}}, unloaded +// CHECK:STDOUT: %Core.import_ref.971 = import_ref Core//prelude/types/optional, loc{{\d+_\d+}}, unloaded +// CHECK:STDOUT: %OptionalStorage.impl_witness_table.2f8 = impl_witness_table (%Core.import_ref.2fb, %Core.import_ref.a7c, %Core.import_ref.720, %Core.import_ref.6a9, %Core.import_ref.971), @ptr.as.OptionalStorage.impl [concrete] +// CHECK:STDOUT: %get.decl: %get.type = fn_decl @get [concrete = constants.%get] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: %OptionalStorage.facet: %OptionalStorage.type = facet_value constants.%ptr.5c7, (constants.%OptionalStorage.impl_witness.81d) [concrete = constants.%OptionalStorage.facet] +// CHECK:STDOUT: %.loc8: %OptionalStorage.type = converted constants.%ptr.5c7, %OptionalStorage.facet [concrete = constants.%OptionalStorage.facet] +// CHECK:STDOUT: %Optional: type = class_type @Optional, @Optional(constants.%OptionalStorage.facet) [concrete = constants.%Optional.4c2] +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: %.loc8_20.1: type = splice_block %Optional [concrete = constants.%Optional.4c2] { +// CHECK:STDOUT: %OptionalStorage.facet: %OptionalStorage.type = facet_value constants.%ptr.5c7, (constants.%OptionalStorage.impl_witness.81d) [concrete = constants.%OptionalStorage.facet] +// CHECK:STDOUT: %.loc8_20.2: %OptionalStorage.type = converted constants.%ptr.5c7, %OptionalStorage.facet [concrete = constants.%OptionalStorage.facet] +// CHECK:STDOUT: %Optional: type = class_type @Optional, @Optional(constants.%OptionalStorage.facet) [concrete = constants.%Optional.4c2] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Cpp.ref.loc8_3: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %foo.ref: %foo.cpp_overload_set.type = name_ref foo, imports.%foo.cpp_overload_set.value [concrete = constants.%foo.cpp_overload_set.value] +// CHECK:STDOUT: %Cpp.ref.loc8_11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %get.ref: %get.cpp_overload_set.type = name_ref get, imports.%get.cpp_overload_set.value [concrete = constants.%get.cpp_overload_set.value] +// CHECK:STDOUT: %get.call: init %Optional.4c2 = call imports.%get.decl() +// CHECK:STDOUT: %.loc8_19.1: ref %Optional.4c2 = temporary_storage +// CHECK:STDOUT: %.loc8_19.2: ref %Optional.4c2 = temporary %.loc8_19.1, %get.call +// CHECK:STDOUT: %.loc8_19.3: %Optional.4c2 = acquire_value %.loc8_19.2 +// CHECK:STDOUT: %foo.call: init %empty_tuple.type = call imports.%foo.decl(%.loc8_19.3) +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.bound: = bound_method %.loc8_19.2, constants.%DestroyT.binding.as_type.as.Destroy.impl.Op.da5 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method: = bound_method %.loc8_19.2, %DestroyT.binding.as_type.as.Destroy.impl.Op.specific_fn +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%.loc8_19.2) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/check/testdata/interop/cpp/function/void_pointer.carbon b/toolchain/check/testdata/interop/cpp/function/void_pointer.carbon index 63c58ef2453ca..7b3e7444737a3 100644 --- a/toolchain/check/testdata/interop/cpp/function/void_pointer.carbon +++ b/toolchain/check/testdata/interop/cpp/function/void_pointer.carbon @@ -52,7 +52,7 @@ fn F(input: Cpp.void*) { //@dump-sem-ir-end } -// --- fail_todo_null_param.carbon +// --- null_param.carbon library "[[@TEST_NAME]]"; @@ -62,10 +62,6 @@ auto foo(void* x) -> void; fn F() { //@dump-sem-ir-begin - // CHECK:STDERR: fail_todo_null_param.carbon:[[@LINE+4]]:11: error: call argument of type `Core.Optional(Cpp.void* as Core.OptionalStorage)` is not supported [CppCallArgTypeNotSupported] - // CHECK:STDERR: Cpp.foo(Core.Optional(Cpp.void*).None()); - // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // CHECK:STDERR: Cpp.foo(Core.Optional(Cpp.void*).None()); //@dump-sem-ir-end } @@ -291,9 +287,10 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_null_param.carbon +// CHECK:STDOUT: --- null_param.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] // CHECK:STDOUT: %foo.cpp_overload_set.type: type = cpp_overload_set_type @foo.cpp_overload_set [concrete] // CHECK:STDOUT: %foo.cpp_overload_set.value: %foo.cpp_overload_set.type = cpp_overload_set_value @foo.cpp_overload_set [concrete] // CHECK:STDOUT: %Optional.type: type = generic_class_type @Optional [concrete] @@ -309,18 +306,26 @@ fn F() { // CHECK:STDOUT: %MaybeUnformed.cff: type = class_type @MaybeUnformed, @MaybeUnformed(%ptr.4f0) [symbolic] // CHECK:STDOUT: %ptr.as.OptionalStorage.impl.None.type.8ed: type = fn_type @ptr.as.OptionalStorage.impl.None, @ptr.as.OptionalStorage.impl(%T.d9f) [symbolic] // CHECK:STDOUT: %ptr.as.OptionalStorage.impl.None.41a: %ptr.as.OptionalStorage.impl.None.type.8ed = struct_value () [symbolic] +// CHECK:STDOUT: %type_where: type = facet_type > [concrete] +// CHECK:STDOUT: %Destroy.type: type = facet_type <@Destroy> [concrete] // CHECK:STDOUT: %OptionalStorage.impl_witness.ab6: = impl_witness imports.%OptionalStorage.impl_witness_table.f52, @ptr.as.OptionalStorage.impl(%Cpp.void) [concrete] // CHECK:STDOUT: %OptionalStorage.facet: %OptionalStorage.type = facet_value %ptr.874, (%OptionalStorage.impl_witness.ab6) [concrete] // CHECK:STDOUT: %Optional.082: type = class_type @Optional, @Optional(%OptionalStorage.facet) [concrete] // CHECK:STDOUT: %Optional.None.type.fe0: type = fn_type @Optional.None, @Optional(%OptionalStorage.facet) [concrete] // CHECK:STDOUT: %Optional.None.a64: %Optional.None.type.fe0 = struct_value () [concrete] // CHECK:STDOUT: %Optional.None.specific_fn: = specific_function %Optional.None.a64, @Optional.None(%OptionalStorage.facet) [concrete] +// CHECK:STDOUT: %foo.type: type = fn_type @foo [concrete] +// CHECK:STDOUT: %foo: %foo.type = struct_value () [concrete] +// CHECK:STDOUT: %facet_value.377: %type_where = facet_value %Optional.082, () [concrete] +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.type.8df: type = fn_type @DestroyT.binding.as_type.as.Destroy.impl.Op, @DestroyT.binding.as_type.as.Destroy.impl(%facet_value.377) [concrete] +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.fca: %DestroyT.binding.as_type.as.Destroy.impl.Op.type.8df = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Core: = namespace file.%Core.import, [concrete] { // CHECK:STDOUT: .Optional = %Core.Optional // CHECK:STDOUT: .CppCompat = %CppCompat.c59 +// CHECK:STDOUT: .Destroy = %Core.Destroy // CHECK:STDOUT: import Core//prelude // CHECK:STDOUT: import Core//prelude/... // CHECK:STDOUT: } @@ -345,24 +350,44 @@ fn F() { // CHECK:STDOUT: %Core.import_ref.6a9 = import_ref Core//prelude/types/optional, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: %Core.import_ref.971 = import_ref Core//prelude/types/optional, loc{{\d+_\d+}}, unloaded // CHECK:STDOUT: %OptionalStorage.impl_witness_table.f52 = impl_witness_table (%Core.import_ref.2fb, %Core.import_ref.1d4, %Core.import_ref.720, %Core.import_ref.6a9, %Core.import_ref.971), @ptr.as.OptionalStorage.impl [concrete] +// CHECK:STDOUT: %foo.decl: %foo.type = fn_decl @foo [concrete = constants.%foo] { +// CHECK:STDOUT: +// CHECK:STDOUT: } { +// CHECK:STDOUT: +// CHECK:STDOUT: %.loc10_42.1: type = splice_block %Optional [concrete = constants.%Optional.082] { +// CHECK:STDOUT: %OptionalStorage.facet: %OptionalStorage.type = facet_value constants.%ptr.874, (constants.%OptionalStorage.impl_witness.ab6) [concrete = constants.%OptionalStorage.facet] +// CHECK:STDOUT: %.loc10_42.2: %OptionalStorage.type = converted constants.%ptr.874, %OptionalStorage.facet [concrete = constants.%OptionalStorage.facet] +// CHECK:STDOUT: %Optional: type = class_type @Optional, @Optional(constants.%OptionalStorage.facet) [concrete = constants.%Optional.082] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %Cpp.ref.loc14_3: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %Cpp.ref.loc10_3: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] // CHECK:STDOUT: %foo.ref: %foo.cpp_overload_set.type = name_ref foo, imports.%foo.cpp_overload_set.value [concrete = constants.%foo.cpp_overload_set.value] // CHECK:STDOUT: %Core.ref: = name_ref Core, imports.%Core [concrete = imports.%Core] // CHECK:STDOUT: %Optional.ref: %Optional.type = name_ref Optional, imports.%Core.Optional [concrete = constants.%Optional.generic] -// CHECK:STDOUT: %Cpp.ref.loc14_25: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %Cpp.ref.loc10_25: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] // CHECK:STDOUT: %void.ref: type = name_ref void, constants.%Cpp.void [concrete = constants.%Cpp.void] // CHECK:STDOUT: %ptr: type = ptr_type %void.ref [concrete = constants.%ptr.874] // CHECK:STDOUT: %OptionalStorage.facet: %OptionalStorage.type = facet_value %ptr, (constants.%OptionalStorage.impl_witness.ab6) [concrete = constants.%OptionalStorage.facet] -// CHECK:STDOUT: %.loc14_34: %OptionalStorage.type = converted %ptr, %OptionalStorage.facet [concrete = constants.%OptionalStorage.facet] +// CHECK:STDOUT: %.loc10_34: %OptionalStorage.type = converted %ptr, %OptionalStorage.facet [concrete = constants.%OptionalStorage.facet] // CHECK:STDOUT: %Optional: type = class_type @Optional, @Optional(constants.%OptionalStorage.facet) [concrete = constants.%Optional.082] -// CHECK:STDOUT: %.loc14_35: %Optional.None.type.fe0 = specific_constant imports.%Core.import_ref.f1d, @Optional(constants.%OptionalStorage.facet) [concrete = constants.%Optional.None.a64] -// CHECK:STDOUT: %None.ref: %Optional.None.type.fe0 = name_ref None, %.loc14_35 [concrete = constants.%Optional.None.a64] +// CHECK:STDOUT: %.loc10_35: %Optional.None.type.fe0 = specific_constant imports.%Core.import_ref.f1d, @Optional(constants.%OptionalStorage.facet) [concrete = constants.%Optional.None.a64] +// CHECK:STDOUT: %None.ref: %Optional.None.type.fe0 = name_ref None, %.loc10_35 [concrete = constants.%Optional.None.a64] // CHECK:STDOUT: %Optional.None.specific_fn: = specific_function %None.ref, @Optional.None(constants.%OptionalStorage.facet) [concrete = constants.%Optional.None.specific_fn] // CHECK:STDOUT: %Optional.None.call: init %Optional.082 = call %Optional.None.specific_fn() +// CHECK:STDOUT: %.loc10_41.1: ref %Optional.082 = temporary_storage +// CHECK:STDOUT: %.loc10_41.2: ref %Optional.082 = temporary %.loc10_41.1, %Optional.None.call +// CHECK:STDOUT: %.loc10_41.3: %Optional.082 = acquire_value %.loc10_41.2 +// CHECK:STDOUT: %foo.call: init %empty_tuple.type = call imports.%foo.decl(%.loc10_41.3) +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.bound: = bound_method %.loc10_41.2, constants.%DestroyT.binding.as_type.as.Destroy.impl.Op.fca +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method: = bound_method %.loc10_41.2, %DestroyT.binding.as_type.as.Destroy.impl.Op.specific_fn +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method(%.loc10_41.2) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/sem_ir/inst_namer.cpp b/toolchain/sem_ir/inst_namer.cpp index bc927a908ef92..94a0880f50212 100644 --- a/toolchain/sem_ir/inst_namer.cpp +++ b/toolchain/sem_ir/inst_namer.cpp @@ -871,10 +871,13 @@ auto InstNamer::NamingContext::NameInst() -> void { case CARBON_KIND(ClassType inst): { if (auto type_info = RecognizedTypeInfo::ForType(sem_ir(), inst); type_info.is_valid()) { - AddInstName(type_info.GetLiteralAsString(sem_ir())); - } else { - AddEntityNameAndMaybePush(inst.class_id); + RawStringOstream out; + if (type_info.PrintLiteral(sem_ir(), out)) { + AddInstName(out.TakeStr()); + return; + } } + AddEntityNameAndMaybePush(inst.class_id); return; } case CompleteTypeWitness::Kind: { diff --git a/toolchain/sem_ir/stringify.cpp b/toolchain/sem_ir/stringify.cpp index 99490ebdb91e2..84f7d0ae4bbbc 100644 --- a/toolchain/sem_ir/stringify.cpp +++ b/toolchain/sem_ir/stringify.cpp @@ -302,8 +302,9 @@ class Stringifier { const auto& class_info = sem_ir_->classes().Get(inst.class_id); if (auto type_info = RecognizedTypeInfo::ForType(*sem_ir_, inst); type_info.is_valid()) { - type_info.PrintLiteral(*sem_ir_, *out_); - return; + if (type_info.PrintLiteral(*sem_ir_, *out_)) { + return; + } } step_stack_->PushEntityName(class_info, inst.specific_id); } @@ -793,8 +794,9 @@ auto StringifySpecific(const File& sem_ir, SpecificId specific_id) .specific_id = specific_id}); type_info.is_valid()) { RawStringOstream out; - type_info.PrintLiteral(sem_ir, out); - return out.TakeStr(); + if (type_info.PrintLiteral(sem_ir, out)) { + return out.TakeStr(); + } } step_stack.PushEntityName(class_info, specific_id); break; diff --git a/toolchain/sem_ir/type_info.cpp b/toolchain/sem_ir/type_info.cpp index 777d7f0c6557d..d9a2bd61d7d0f 100644 --- a/toolchain/sem_ir/type_info.cpp +++ b/toolchain/sem_ir/type_info.cpp @@ -126,21 +126,22 @@ auto NumericTypeLiteralInfo::PrintLiteral(const File& file, file.ints().Get(bit_width_id).print(out, /*isSigned=*/false); } -auto NumericTypeLiteralInfo::GetLiteralAsString(const File& file) const - -> std::string { - RawStringOstream out; - PrintLiteral(file, out); - return out.TakeStr(); +// Returns whether this kind of recognized type should have a generic argument +// list. +static auto ExpectsArgs(RecognizedTypeInfo::Kind kind) -> bool { + return kind == RecognizedTypeInfo::Optional; } auto RecognizedTypeInfo::ForType(const File& file, ClassType class_type) -> RecognizedTypeInfo { + auto args_id = SemIR::InstBlockId::None; + if (class_type.specific_id.has_value()) { auto numeric = NumericTypeLiteralInfo::ForType(file, class_type); if (numeric.is_valid()) { return {.kind = Numeric, .numeric = numeric}; } - return {.kind = None}; + args_id = file.specifics().Get(class_type.specific_id).args_id; } // The class must be declared in the `Core` package. We check for up to one @@ -167,9 +168,12 @@ auto RecognizedTypeInfo::ForType(const File& file, ClassType class_type) if (!parent_scope_name_id.has_value()) { Kind kind = llvm::StringSwitch(*name_ident) .Case("Char", Char) + .Case("Optional", Optional) .Case("String", Str) .Default(None); - return {.kind = kind}; + if (ExpectsArgs(kind) == args_id.has_value()) { + return {.kind = kind, .args_id = args_id}; + } } auto parent_name_ident = @@ -180,51 +184,55 @@ auto RecognizedTypeInfo::ForType(const File& file, ClassType class_type) .Case("NullptrT", CppNullptrT) .Case("VoidBase", CppVoidBase) .Default(None); - return {.kind = kind}; + if (ExpectsArgs(kind) == args_id.has_value()) { + return {.kind = kind, .args_id = args_id}; + } } return {.kind = None}; } auto RecognizedTypeInfo::PrintLiteral(const File& file, - llvm::raw_ostream& out) const -> void { + llvm::raw_ostream& out) const -> bool { switch (kind) { case None: CARBON_FATAL("Printing invalid type literal"); case Numeric: numeric.PrintLiteral(file, out); - break; + return true; case Char: out << "char"; - break; + return true; case CppLong32: if (file.clang_ast_unit()) { const clang::ASTContext& ast_context = file.clang_ast_unit()->getASTContext(); if (ast_context.getIntWidth(ast_context.LongTy) == 32) { out << "Cpp.long"; - break; + return true; } } - out << "Core.CppCompat.Long32"; break; case CppNullptrT: - out << "Cpp.nullptr_t"; + if (file.clang_ast_unit()) { + out << "Cpp.nullptr_t"; + return true; + } break; case CppVoidBase: - out << "Cpp.void"; + if (file.clang_ast_unit()) { + out << "Cpp.void"; + return true; + } + break; + case Optional: break; case Str: out << "str"; - break; + return true; } -} -auto RecognizedTypeInfo::GetLiteralAsString(const File& file) const - -> std::string { - RawStringOstream out; - PrintLiteral(file, out); - return out.TakeStr(); + return false; } } // namespace Carbon::SemIR diff --git a/toolchain/sem_ir/type_info.h b/toolchain/sem_ir/type_info.h index 7179167cf2b53..78a80a45815ba 100644 --- a/toolchain/sem_ir/type_info.h +++ b/toolchain/sem_ir/type_info.h @@ -215,9 +215,6 @@ struct NumericTypeLiteralInfo { // Prints the numeric type literal that corresponds to this type. auto PrintLiteral(const File& file, llvm::raw_ostream& out) const -> void; - // Gets a string containing the literal. - auto GetLiteralAsString(const File& file) const -> std::string; - // Returns whether this is a valid numeric type literal. auto is_valid() const -> bool { return kind != None; } @@ -242,6 +239,8 @@ struct RecognizedTypeInfo { CppNullptrT, // `Cpp.void` / `Core.CppCompat.VoidBase`. CppVoidBase, + // `Core.Optional(...)`. + Optional, // `str` / `Core.String`. // TODO: Rename `Core.String` to `Core.Str`. Str, @@ -251,11 +250,10 @@ struct RecognizedTypeInfo { static auto ForType(const File& file, ClassType class_type) -> RecognizedTypeInfo; - // Prints the type literal that corresponds to this type. - auto PrintLiteral(const File& file, llvm::raw_ostream& out) const -> void; - - // Gets a string containing the literal. - auto GetLiteralAsString(const File& file) const -> std::string; + // Prints the type literal or special type name that corresponds to this type, + // if there is one. Returns true if the type was printed, or false if this + // type doesn't have special syntax and should be printed directly. + auto PrintLiteral(const File& file, llvm::raw_ostream& out) const -> bool; // Returns whether this is a valid type literal. auto is_valid() const -> bool { return kind != None; } @@ -264,6 +262,8 @@ struct RecognizedTypeInfo { Kind kind; // If this is a numeric literal, additional information about the literal. NumericTypeLiteralInfo numeric = NumericTypeLiteralInfo::Invalid; + // If this is a generic type, the arguments. + InstBlockId args_id = InstBlockId::None; }; inline constexpr NumericTypeLiteralInfo NumericTypeLiteralInfo::Invalid = {