From 86b02ee8afa1866397907d1510a1d9f9c2be5b2d Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Wed, 12 Nov 2025 15:23:48 -0800 Subject: [PATCH] Interop support for `nullptr` and `nullptr_t`. (#6353) Add a `Core.CppCompat.NullptrT` type that C++'s `nullptr_t` maps into. Map `nullptr` to an uninitialized constant of that type -- `nullptr` doesn't actually have any defined bits within it, despite having the same representation as `void*`. --- core/prelude/types.carbon | 1 + core/prelude/types/cpp/nullptr.carbon | 49 ++++ toolchain/check/cpp/import.cpp | 37 ++- toolchain/check/cpp/type_mapping.cpp | 3 + toolchain/check/name_lookup.cpp | 59 +++-- toolchain/check/name_lookup.h | 11 +- .../interop/cpp/function/decayed_param.carbon | 200 +++++++++------- toolchain/lower/constant.cpp | 5 + .../lower/testdata/interop/cpp/nullptr.carbon | 222 ++++++++++++++++++ toolchain/sem_ir/expr_info.cpp | 1 + toolchain/sem_ir/inst_kind.def | 1 + toolchain/sem_ir/inst_namer.cpp | 4 + toolchain/sem_ir/type_info.cpp | 39 ++- toolchain/sem_ir/type_info.h | 3 + toolchain/sem_ir/typed_insts.h | 10 + 15 files changed, 521 insertions(+), 124 deletions(-) create mode 100644 core/prelude/types/cpp/nullptr.carbon create mode 100644 toolchain/lower/testdata/interop/cpp/nullptr.carbon diff --git a/core/prelude/types.carbon b/core/prelude/types.carbon index ddbe3eaffbc5..93ecc1956423 100644 --- a/core/prelude/types.carbon +++ b/core/prelude/types.carbon @@ -11,6 +11,7 @@ export import library "prelude/types/float_literal"; export import library "prelude/types/int"; export import library "prelude/types/int_literal"; export import library "prelude/types/maybe_unformed"; +export import library "prelude/types/cpp/nullptr"; export import library "prelude/types/optional"; export import library "prelude/types/string"; export import library "prelude/types/uint"; diff --git a/core/prelude/types/cpp/nullptr.carbon b/core/prelude/types/cpp/nullptr.carbon new file mode 100644 index 000000000000..cd9a720a58c6 --- /dev/null +++ b/core/prelude/types/cpp/nullptr.carbon @@ -0,0 +1,49 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +package Core library "prelude/types/cpp/nullptr"; + +import library "prelude/copy"; +import library "prelude/destroy"; +import library "prelude/operators/as"; +import library "prelude/types/optional"; +import library "prelude/types/maybe_unformed"; + +namespace CppCompat; + +// C++ `std::nullptr_t` as a Carbon type. +// +// The C++ type `decltype(nullptr)`, also known by the library-provided alias +// `std::nullptr_t`, is a fundamental type, not a class type, so it needs a +// custom mapping as part of C++ interoperability. We map it to this class +// type. After a suitable import, this class can be named as +// `Cpp.std.nullptr_t`. This is also the type of the constant `Cpp.nullptr`. +// +// This type supports implicit conversion to any optional pointer type, and +// produces the `None` value of that type. +class CppCompat.NullptrT { + // nullptr_t has the same size and alignment as a pointer, but the + // corresponding pointer is always unformed. + // TODO: Give this type a custom empty value representation. + adapt MaybeUnformed(()*); + + fn Make() -> Self { + returned var s: Self; + return var; + } + + impl as Copy { + fn Op[self: Self]() -> Self { + return Make(); + } + } + + // TODO: impl as EqWith(Self) + + impl forall [T:! type] as ImplicitAs(Optional(T*)) { + fn Convert[self: Self]() -> Optional(T*) { + return Optional(T*).None(); + } + } +} diff --git a/toolchain/check/cpp/import.cpp b/toolchain/check/cpp/import.cpp index e0ab4ff76d30..c013b4f7ea83 100644 --- a/toolchain/check/cpp/import.cpp +++ b/toolchain/check/cpp/import.cpp @@ -46,6 +46,7 @@ #include "toolchain/check/import.h" #include "toolchain/check/inst.h" #include "toolchain/check/literal.h" +#include "toolchain/check/member_access.h" #include "toolchain/check/name_lookup.h" #include "toolchain/check/operator.h" #include "toolchain/check/pattern.h" @@ -1106,6 +1107,12 @@ static auto MapBuiltinIntegerType(Context& context, SemIR::LocId loc_id, return TypeExpr::None; } +static auto MapNullptrType(Context& context, SemIR::LocId loc_id) -> TypeExpr { + return ExprAsType( + context, loc_id, + LookupNameInCore(context, loc_id, {"CppCompat", "NullptrT"})); +} + // Maps a C++ builtin type to a Carbon type. // TODO: Support more builtin types. static auto MapBuiltinType(Context& context, SemIR::LocId loc_id, @@ -1134,6 +1141,8 @@ static auto MapBuiltinType(Context& context, SemIR::LocId loc_id, } else if (type.isVoidType()) { return ExprAsType(context, Parse::NodeId::None, SemIR::CppVoidType::TypeInstId); + } else if (type.isNullPtrType()) { + return MapNullptrType(context, loc_id); } return TypeExpr::None; @@ -2097,10 +2106,10 @@ static auto IsTopCppScope(Context& context, SemIR::NameScopeId scope_id) return name_scope.parent_scope_id() == SemIR::NameScopeId::Package; } -// For builtin names like `Cpp.long`, return the associated types. -static auto LookupBuiltinTypes(Context& context, SemIR::LocId loc_id, - SemIR::NameScopeId scope_id, - SemIR::NameId name_id) -> SemIR::InstId { +// For a builtin name like `Cpp.long`, returns the associated type. +static auto LookupBuiltinName(Context& context, SemIR::LocId loc_id, + SemIR::NameScopeId scope_id, + SemIR::NameId name_id) -> SemIR::InstId { if (!IsTopCppScope(context, scope_id)) { return SemIR::InstId::None; } @@ -2132,6 +2141,12 @@ static auto LookupBuiltinTypes(Context& context, SemIR::LocId loc_id, .Case("void", ast_context.VoidTy) .Default(clang::QualType()); if (builtin_type.isNull()) { + if (*name == "nullptr") { + // Map `Cpp.nullptr` to an uninitialized value of type `Core.CppNullptrT`. + auto type_id = MapNullptrType(context, loc_id).type_id; + return GetOrAddInst( + context, SemIR::LocId::None, {.type_id = type_id}); + } return SemIR::InstId::None; } @@ -2227,14 +2242,14 @@ static auto ImportConstructorsIntoScope(Context& context, SemIR::LocId loc_id, naming_class, std::move(overload_set)); } -// Imports a builtin type from Clang to Carbon and adds the name into the -// scope. -static auto ImportBuiltinTypesIntoScope(Context& context, SemIR::LocId loc_id, - SemIR::NameScopeId scope_id, - SemIR::NameId name_id) +// Attempts to import a builtin name from Clang to Carbon and adds the name into +// the scope. +static auto ImportBuiltinNameIntoScope(Context& context, SemIR::LocId loc_id, + SemIR::NameScopeId scope_id, + SemIR::NameId name_id) -> SemIR::ScopeLookupResult { SemIR::InstId builtin_inst_id = - LookupBuiltinTypes(context, loc_id, scope_id, name_id); + LookupBuiltinName(context, loc_id, scope_id, name_id); if (builtin_inst_id.has_value()) { AddNameToScope(context, scope_id, name_id, SemIR::AccessKind::Public, builtin_inst_id); @@ -2351,7 +2366,7 @@ auto ImportNameFromCpp(Context& context, SemIR::LocId loc_id, } auto lookup = ClangLookupName(context, scope_id, name_id); if (!lookup) { - return ImportBuiltinTypesIntoScope(context, loc_id, scope_id, name_id); + return ImportBuiltinNameIntoScope(context, loc_id, scope_id, name_id); } // Access checks are performed separately by the Carbon name lookup logic. lookup->suppressAccessDiagnostics(); diff --git a/toolchain/check/cpp/type_mapping.cpp b/toolchain/check/cpp/type_mapping.cpp index 0e6fc052dfc4..eb900b5bc9e2 100644 --- a/toolchain/check/cpp/type_mapping.cpp +++ b/toolchain/check/cpp/type_mapping.cpp @@ -149,6 +149,9 @@ static auto TryMapClassType(Context& context, SemIR::ClassType class_type) case SemIR::TypeLiteralInfo::Char: { return context.ast_context().CharTy; } + case SemIR::TypeLiteralInfo::CppNullptrT: { + return context.ast_context().NullPtrTy; + } case SemIR::TypeLiteralInfo::Str: { return LookupCppType(context, {"std", "string_view"}); } diff --git a/toolchain/check/name_lookup.cpp b/toolchain/check/name_lookup.cpp index 4489824e505c..66f07db4eea3 100644 --- a/toolchain/check/name_lookup.cpp +++ b/toolchain/check/name_lookup.cpp @@ -15,6 +15,7 @@ #include "toolchain/check/type_completion.h" #include "toolchain/diagnostics/format_providers.h" #include "toolchain/sem_ir/generic.h" +#include "toolchain/sem_ir/ids.h" #include "toolchain/sem_ir/name_scope.h" namespace Carbon::Check { @@ -496,7 +497,8 @@ auto LookupQualifiedName(Context& context, SemIR::LocId loc_id, // TODO: Consider tracking the Core package in SemIR so we don't need to use // name lookup to find it. static auto GetCorePackage(Context& context, SemIR::LocId loc_id, - llvm::StringRef name) -> SemIR::NameScopeId { + llvm::ArrayRef names) + -> SemIR::NameScopeId { auto packaging = context.parse_tree().packaging_decl(); if (packaging && packaging->names.package_id == PackageNameId::Core) { return SemIR::NameScopeId::Package; @@ -520,33 +522,54 @@ static auto GetCorePackage(Context& context, SemIR::LocId loc_id, CoreNotFound, Error, "`Core.{0}` implicitly referenced here, but package `Core` not found", std::string); - context.emitter().Emit(loc_id, CoreNotFound, name.str()); + context.emitter().Emit(loc_id, CoreNotFound, llvm::join(names, ".")); return SemIR::NameScopeId::None; } auto LookupNameInCore(Context& context, SemIR::LocId loc_id, - llvm::StringRef name) -> SemIR::InstId { - auto core_package_id = GetCorePackage(context, loc_id, name); + llvm::ArrayRef names) -> SemIR::InstId { + CARBON_CHECK(!names.empty()); + + auto core_package_id = GetCorePackage(context, loc_id, names); if (!core_package_id.has_value()) { return SemIR::ErrorInst::InstId; } - auto name_id = SemIR::NameId::ForIdentifier(context.identifiers().Add(name)); - auto scope_result = - LookupNameInExactScope(context, loc_id, name_id, core_package_id, - context.name_scopes().Get(core_package_id)); - if (!scope_result.is_found()) { - CARBON_DIAGNOSTIC( - CoreNameNotFound, Error, - "name `Core.{0}` implicitly referenced here, but not found", - SemIR::NameId); - context.emitter().Emit(loc_id, CoreNameNotFound, name_id); - return SemIR::ErrorInst::InstId; + auto inst_id = SemIR::InstId::None; + for (auto name : names) { + auto name_id = + SemIR::NameId::ForIdentifier(context.identifiers().Add(name)); + + auto scope_id = SemIR::NameScopeId::None; + if (inst_id.has_value()) { + auto namespace_inst = context.insts().TryGetAs(inst_id); + if (namespace_inst) { + scope_id = namespace_inst->name_scope_id; + } + } else { + scope_id = core_package_id; + } + + auto scope_result = + scope_id.has_value() + ? LookupNameInExactScope(context, loc_id, name_id, scope_id, + context.name_scopes().Get(scope_id)) + : SemIR::ScopeLookupResult::MakeNotFound(); + if (!scope_result.is_found()) { + CARBON_DIAGNOSTIC( + CoreNameNotFound, Error, + "name `Core.{0}` implicitly referenced here, but not found", + std::string); + context.emitter().Emit(loc_id, CoreNameNotFound, llvm::join(names, ".")); + return SemIR::ErrorInst::InstId; + } + + // Look through import_refs and aliases. + inst_id = context.constant_values().GetConstantInstId( + scope_result.target_inst_id()); } - // Look through import_refs and aliases. - return context.constant_values().GetConstantInstId( - scope_result.target_inst_id()); + return inst_id; } auto DiagnoseDuplicateName(Context& context, SemIR::NameId name_id, diff --git a/toolchain/check/name_lookup.h b/toolchain/check/name_lookup.h index 6380360a8a9b..0122ce6ace3e 100644 --- a/toolchain/check/name_lookup.h +++ b/toolchain/check/name_lookup.h @@ -94,10 +94,17 @@ auto LookupQualifiedName(Context& context, SemIR::LocId loc_id, std::optional access_info = std::nullopt) -> LookupResult; +// Returns the `InstId` corresponding to a qualified name in the core package, +// or BuiltinErrorInst if not found. +auto LookupNameInCore(Context& context, SemIR::LocId loc_id, + llvm::ArrayRef names) -> SemIR::InstId; + // Returns the `InstId` corresponding to a name in the core package, or // BuiltinErrorInst if not found. -auto LookupNameInCore(Context& context, SemIR::LocId loc_id, - llvm::StringRef name) -> SemIR::InstId; +inline auto LookupNameInCore(Context& context, SemIR::LocId loc_id, + llvm::StringRef name) -> SemIR::InstId { + return LookupNameInCore(context, loc_id, llvm::ArrayRef{name}); +} // Checks whether a name is accessible in the given access context. Produces a // diagnostic if not. diff --git a/toolchain/check/testdata/interop/cpp/function/decayed_param.carbon b/toolchain/check/testdata/interop/cpp/function/decayed_param.carbon index acc3d84f8584..d7bdb12f3b0d 100644 --- a/toolchain/check/testdata/interop/cpp/function/decayed_param.carbon +++ b/toolchain/check/testdata/interop/cpp/function/decayed_param.carbon @@ -29,6 +29,8 @@ fn F() { //@dump-sem-ir-begin var n: array(i32, 42); Cpp.TakesArray(&n[0]); + + Cpp.TakesArray(Cpp.nullptr); //@dump-sem-ir-end } @@ -42,12 +44,6 @@ fn G(n: i32) -> i32; fn F() { //@dump-sem-ir-begin - // CHECK:STDERR: fail_todo_call_params_2.carbon:[[@LINE+4]]:18: error: member name `nullptr` not found in `Cpp` [MemberNameNotFoundInInstScope] - // CHECK:STDERR: Cpp.TakesArray(Cpp.nullptr); - // CHECK:STDERR: ^~~~~~~~~~~ - // CHECK:STDERR: - Cpp.TakesArray(Cpp.nullptr); - // CHECK:STDERR: fail_todo_call_params_2.carbon:[[@LINE+4]]:21: error: call argument of type `` is not supported [CppCallArgTypeNotSupported] // CHECK:STDERR: Cpp.TakesFunction(G); // CHECK:STDERR: ^ @@ -62,18 +58,18 @@ fn F() { var n: array(i32, 42); // CHECK:STDERR: fail_todo_call_params_2.carbon:[[@LINE+8]]:26: error: no matching function for call to 'TakesFunction' [CppInteropParseError] - // CHECK:STDERR: 37 | Cpp.TakesFunction(&n[0]); + // CHECK:STDERR: 31 | Cpp.TakesFunction(&n[0]); // CHECK:STDERR: | ^ - // CHECK:STDERR: fail_todo_call_params_2.carbon:[[@LINE-28]]:10: in file included here [InCppInclude] + // CHECK:STDERR: fail_todo_call_params_2.carbon:[[@LINE-22]]:10: in file included here [InCppInclude] // CHECK:STDERR: ./params.h:3:6: note: candidate function not viable: no known conversion from 'int * _Nonnull' to 'int (*)(int)' for 1st argument [CppInteropParseNote] // CHECK:STDERR: 3 | void TakesFunction(int f(int)); // CHECK:STDERR: | ^ ~~~~~~~~~~ // CHECK:STDERR: Cpp.TakesFunction(&n[0]); - // CHECK:STDERR: fail_todo_call_params_2.carbon:[[@LINE+4]]:21: error: member name `nullptr` not found in `Cpp` [MemberNameNotFoundInInstScope] + // CHECK:STDERR: fail_todo_call_params_2.carbon:[[@LINE+4]]:3: error: semantics TODO: `Unsupported: parameter type: int (*)(int)` [SemanticsTodo] // CHECK:STDERR: Cpp.TakesFunction(Cpp.nullptr); - // CHECK:STDERR: ^~~~~~~~~~~ + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: Cpp.TakesFunction(Cpp.nullptr); //@dump-sem-ir-end @@ -105,42 +101,56 @@ fn F() { // CHECK:STDOUT: %.322: type = fn_type_with_self_type %ImplicitAs.Convert.type.1b6, %ImplicitAs.facet.3ad [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.e9b [concrete] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.e9b, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] -// CHECK:STDOUT: %bound_method: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] +// CHECK:STDOUT: %bound_method.9be: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_0.6a9: %i32 = int_value 0 [concrete] // CHECK:STDOUT: %ptr.235: type = ptr_type %i32 [concrete] // CHECK:STDOUT: %OptionalStorage.type: type = facet_type <@OptionalStorage> [concrete] // CHECK:STDOUT: %T.3fe: %OptionalStorage.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.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: %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: %OptionalStorage.impl_witness.2cf: = impl_witness imports.%OptionalStorage.impl_witness_table.236, @ptr.as.OptionalStorage.impl(%i32) [concrete] -// CHECK:STDOUT: %OptionalStorage.facet.0a3: %OptionalStorage.type = facet_value %ptr.235, (%OptionalStorage.impl_witness.2cf) [concrete] -// CHECK:STDOUT: %Optional.18c: type = class_type @Optional, @Optional(%OptionalStorage.facet.0a3) [concrete] +// CHECK:STDOUT: %OptionalStorage.impl_witness.d16: = impl_witness imports.%OptionalStorage.impl_witness_table.f03, @ptr.as.OptionalStorage.impl(%i32) [concrete] +// CHECK:STDOUT: %OptionalStorage.facet.083: %OptionalStorage.type = facet_value %ptr.235, (%OptionalStorage.impl_witness.d16) [concrete] +// CHECK:STDOUT: %Optional.97d: type = class_type @Optional, @Optional(%OptionalStorage.facet.083) [concrete] // CHECK:STDOUT: %TakesArray.type: type = fn_type @TakesArray [concrete] // CHECK:STDOUT: %TakesArray: %TakesArray.type = struct_value () [concrete] -// CHECK:STDOUT: %ImplicitAs.type.4f4: type = facet_type <@ImplicitAs, @ImplicitAs(%Optional.18c)> [concrete] -// CHECK:STDOUT: %ImplicitAs.Convert.type.7ef: type = fn_type @ImplicitAs.Convert, @ImplicitAs(%Optional.18c) [concrete] +// CHECK:STDOUT: %ImplicitAs.type.534: type = facet_type <@ImplicitAs, @ImplicitAs(%Optional.97d)> [concrete] +// CHECK:STDOUT: %ImplicitAs.Convert.type.8fe: type = fn_type @ImplicitAs.Convert, @ImplicitAs(%Optional.97d) [concrete] // CHECK:STDOUT: %OptionalAs.type.593: type = facet_type <@OptionalAs, @OptionalAs(%T.3fe)> [symbolic] // CHECK:STDOUT: %U.ec3: %OptionalAs.type.593 = symbolic_binding U, 1 [symbolic] // CHECK:STDOUT: %U.binding.as_type.as.ImplicitAs.impl.Convert.type.855: type = fn_type @U.binding.as_type.as.ImplicitAs.impl.Convert.2, @U.binding.as_type.as.ImplicitAs.impl.ea7(%T.3fe, %U.ec3) [symbolic] // CHECK:STDOUT: %U.binding.as_type.as.ImplicitAs.impl.Convert.337: %U.binding.as_type.as.ImplicitAs.impl.Convert.type.855 = struct_value () [symbolic] -// CHECK:STDOUT: %OptionalAs.type.75b: type = facet_type <@OptionalAs, @OptionalAs(%OptionalStorage.facet.0a3)> [concrete] +// CHECK:STDOUT: %OptionalAs.type.07b: type = facet_type <@OptionalAs, @OptionalAs(%OptionalStorage.facet.083)> [concrete] // CHECK:STDOUT: %T.binding.as_type.as.OptionalAs.impl.Convert.type.8c6: type = fn_type @T.binding.as_type.as.OptionalAs.impl.Convert, @T.binding.as_type.as.OptionalAs.impl(%T.3fe) [symbolic] // CHECK:STDOUT: %T.binding.as_type.as.OptionalAs.impl.Convert.180: %T.binding.as_type.as.OptionalAs.impl.Convert.type.8c6 = struct_value () [symbolic] -// CHECK:STDOUT: %OptionalAs.impl_witness.06b: = impl_witness imports.%OptionalAs.impl_witness_table.8ee, @T.binding.as_type.as.OptionalAs.impl(%OptionalStorage.facet.0a3) [concrete] -// CHECK:STDOUT: %OptionalAs.facet: %OptionalAs.type.75b = facet_value %ptr.235, (%OptionalAs.impl_witness.06b) [concrete] -// CHECK:STDOUT: %ImplicitAs.impl_witness.f51: = impl_witness imports.%ImplicitAs.impl_witness_table.da6, @U.binding.as_type.as.ImplicitAs.impl.ea7(%OptionalStorage.facet.0a3, %OptionalAs.facet) [concrete] -// CHECK:STDOUT: %U.binding.as_type.as.ImplicitAs.impl.Convert.type.f95: type = fn_type @U.binding.as_type.as.ImplicitAs.impl.Convert.2, @U.binding.as_type.as.ImplicitAs.impl.ea7(%OptionalStorage.facet.0a3, %OptionalAs.facet) [concrete] -// CHECK:STDOUT: %U.binding.as_type.as.ImplicitAs.impl.Convert.b59: %U.binding.as_type.as.ImplicitAs.impl.Convert.type.f95 = struct_value () [concrete] -// CHECK:STDOUT: %ImplicitAs.facet.59b: %ImplicitAs.type.4f4 = facet_value %ptr.235, (%ImplicitAs.impl_witness.f51) [concrete] -// CHECK:STDOUT: %.4d2: type = fn_type_with_self_type %ImplicitAs.Convert.type.7ef, %ImplicitAs.facet.59b [concrete] -// CHECK:STDOUT: %U.binding.as_type.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %U.binding.as_type.as.ImplicitAs.impl.Convert.b59, @U.binding.as_type.as.ImplicitAs.impl.Convert.2(%OptionalStorage.facet.0a3, %OptionalAs.facet) [concrete] -// CHECK:STDOUT: %facet_value.888: %type_where = facet_value %Optional.18c, () [concrete] -// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.type.b81: type = fn_type @DestroyT.binding.as_type.as.Destroy.impl.Op, @DestroyT.binding.as_type.as.Destroy.impl(%facet_value.888) [concrete] -// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.55c: %DestroyT.binding.as_type.as.Destroy.impl.Op.type.b81 = struct_value () [concrete] -// CHECK:STDOUT: %ptr.a30: type = ptr_type %Optional.18c [concrete] +// CHECK:STDOUT: %OptionalAs.impl_witness.d8d: = impl_witness imports.%OptionalAs.impl_witness_table.8ee, @T.binding.as_type.as.OptionalAs.impl(%OptionalStorage.facet.083) [concrete] +// CHECK:STDOUT: %OptionalAs.facet: %OptionalAs.type.07b = facet_value %ptr.235, (%OptionalAs.impl_witness.d8d) [concrete] +// CHECK:STDOUT: %ImplicitAs.impl_witness.a9a: = impl_witness imports.%ImplicitAs.impl_witness_table.da6, @U.binding.as_type.as.ImplicitAs.impl.ea7(%OptionalStorage.facet.083, %OptionalAs.facet) [concrete] +// CHECK:STDOUT: %U.binding.as_type.as.ImplicitAs.impl.Convert.type.93b: type = fn_type @U.binding.as_type.as.ImplicitAs.impl.Convert.2, @U.binding.as_type.as.ImplicitAs.impl.ea7(%OptionalStorage.facet.083, %OptionalAs.facet) [concrete] +// CHECK:STDOUT: %U.binding.as_type.as.ImplicitAs.impl.Convert.1c9: %U.binding.as_type.as.ImplicitAs.impl.Convert.type.93b = struct_value () [concrete] +// CHECK:STDOUT: %ImplicitAs.facet.461: %ImplicitAs.type.534 = facet_value %ptr.235, (%ImplicitAs.impl_witness.a9a) [concrete] +// CHECK:STDOUT: %.4ad: type = fn_type_with_self_type %ImplicitAs.Convert.type.8fe, %ImplicitAs.facet.461 [concrete] +// CHECK:STDOUT: %U.binding.as_type.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %U.binding.as_type.as.ImplicitAs.impl.Convert.1c9, @U.binding.as_type.as.ImplicitAs.impl.Convert.2(%OptionalStorage.facet.083, %OptionalAs.facet) [concrete] +// CHECK:STDOUT: %Cpp.nullptr_t: type = class_type @NullptrT [concrete] +// CHECK:STDOUT: %uninit: %Cpp.nullptr_t = uninitialized_value [concrete] +// CHECK:STDOUT: %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.type.d80: type = fn_type @Cpp.nullptr_t.as.ImplicitAs.impl.Convert, @Cpp.nullptr_t.as.ImplicitAs.impl(%T.d9f) [symbolic] +// CHECK:STDOUT: %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.2ce: %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.type.d80 = struct_value () [symbolic] +// CHECK:STDOUT: %ImplicitAs.impl_witness.213: = impl_witness imports.%ImplicitAs.impl_witness_table.87f, @Cpp.nullptr_t.as.ImplicitAs.impl(%i32) [concrete] +// CHECK:STDOUT: %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.type.87f: type = fn_type @Cpp.nullptr_t.as.ImplicitAs.impl.Convert, @Cpp.nullptr_t.as.ImplicitAs.impl(%i32) [concrete] +// CHECK:STDOUT: %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.83e: %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.type.87f = struct_value () [concrete] +// CHECK:STDOUT: %ImplicitAs.facet.5e9: %ImplicitAs.type.534 = facet_value %Cpp.nullptr_t, (%ImplicitAs.impl_witness.213) [concrete] +// CHECK:STDOUT: %.28d: type = fn_type_with_self_type %ImplicitAs.Convert.type.8fe, %ImplicitAs.facet.5e9 [concrete] +// CHECK:STDOUT: %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.bound: = bound_method %uninit, %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.83e [concrete] +// CHECK:STDOUT: %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.83e, @Cpp.nullptr_t.as.ImplicitAs.impl.Convert(%i32) [concrete] +// CHECK:STDOUT: %bound_method.ed9: = bound_method %uninit, %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.specific_fn [concrete] +// CHECK:STDOUT: %facet_value.c1c: %type_where = facet_value %Optional.97d, () [concrete] +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.type.b8d: type = fn_type @DestroyT.binding.as_type.as.Destroy.impl.Op, @DestroyT.binding.as_type.as.Destroy.impl(%facet_value.c1c) [concrete] +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.9b6: %DestroyT.binding.as_type.as.Destroy.impl.Op.type.b8d = struct_value () [concrete] +// CHECK:STDOUT: %ptr.334: type = ptr_type %Optional.97d [concrete] // CHECK:STDOUT: %facet_value.5b8: %type_where = facet_value %array_type, () [concrete] // CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.type.1d8: type = fn_type @DestroyT.binding.as_type.as.Destroy.impl.Op, @DestroyT.binding.as_type.as.Destroy.impl(%facet_value.5b8) [concrete] // CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.f36: %DestroyT.binding.as_type.as.Destroy.impl.Op.type.1d8 = struct_value () [concrete] @@ -149,26 +159,27 @@ fn F() { // CHECK:STDOUT: imports { // CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { // CHECK:STDOUT: .TakesArray = %TakesArray.cpp_overload_set.value +// CHECK:STDOUT: .nullptr = @F.%uninit // CHECK:STDOUT: import Cpp//... // CHECK:STDOUT: } // CHECK:STDOUT: %TakesArray.cpp_overload_set.value: %TakesArray.cpp_overload_set.type = cpp_overload_set_value @TakesArray.cpp_overload_set [concrete = constants.%TakesArray.cpp_overload_set.value] // CHECK:STDOUT: %Core.import_ref.e24: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.f51) = import_ref Core//prelude/types/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.2a1)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.132 = impl_witness_table (%Core.import_ref.e24), @Core.IntLiteral.as.ImplicitAs.impl [concrete] // 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.1d4: @ptr.as.OptionalStorage.impl.%ptr.as.OptionalStorage.impl.None.type (%ptr.as.OptionalStorage.impl.None.type.8ed) = import_ref Core//prelude/types/optional, loc{{\d+_\d+}}, loaded [symbolic = @ptr.as.OptionalStorage.impl.%ptr.as.OptionalStorage.impl.None (constants.%ptr.as.OptionalStorage.impl.None.41a)] // 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: %OptionalStorage.impl_witness_table.f03 = impl_witness_table (%Core.import_ref.2fb, %Core.import_ref.1d4, %Core.import_ref.1b2, %Core.import_ref.6a9, %Core.import_ref.971), @ptr.as.OptionalStorage.impl [concrete] // CHECK:STDOUT: %TakesArray.decl: %TakesArray.type = fn_decl @TakesArray [concrete = constants.%TakesArray] { // CHECK:STDOUT: // CHECK:STDOUT: } { // CHECK:STDOUT: -// CHECK:STDOUT: %.loc11_23.1: type = splice_block %Optional [concrete = constants.%Optional.18c] { +// CHECK:STDOUT: %.loc11_23.1: type = splice_block %Optional [concrete = constants.%Optional.97d] { // CHECK:STDOUT: -// CHECK:STDOUT: %OptionalStorage.facet: %OptionalStorage.type = facet_value constants.%ptr.235, (constants.%OptionalStorage.impl_witness.2cf) [concrete = constants.%OptionalStorage.facet.0a3] -// CHECK:STDOUT: %.loc11_23.2: %OptionalStorage.type = converted constants.%ptr.235, %OptionalStorage.facet [concrete = constants.%OptionalStorage.facet.0a3] -// CHECK:STDOUT: %Optional: type = class_type @Optional, @Optional(constants.%OptionalStorage.facet.0a3) [concrete = constants.%Optional.18c] +// CHECK:STDOUT: %OptionalStorage.facet: %OptionalStorage.type = facet_value constants.%ptr.235, (constants.%OptionalStorage.impl_witness.d16) [concrete = constants.%OptionalStorage.facet.083] +// CHECK:STDOUT: %.loc11_23.2: %OptionalStorage.type = converted constants.%ptr.235, %OptionalStorage.facet [concrete = constants.%OptionalStorage.facet.083] +// CHECK:STDOUT: %Optional: type = class_type @Optional, @Optional(constants.%OptionalStorage.facet.083) [concrete = constants.%Optional.97d] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: } @@ -176,6 +187,8 @@ fn F() { // CHECK:STDOUT: %ImplicitAs.impl_witness_table.da6 = impl_witness_table (%Core.import_ref.ec9), @U.binding.as_type.as.ImplicitAs.impl.ea7 [concrete] // CHECK:STDOUT: %Core.import_ref.7fb: @T.binding.as_type.as.OptionalAs.impl.%T.binding.as_type.as.OptionalAs.impl.Convert.type (%T.binding.as_type.as.OptionalAs.impl.Convert.type.8c6) = import_ref Core//prelude/types/optional, loc{{\d+_\d+}}, loaded [symbolic = @T.binding.as_type.as.OptionalAs.impl.%T.binding.as_type.as.OptionalAs.impl.Convert (constants.%T.binding.as_type.as.OptionalAs.impl.Convert.180)] // CHECK:STDOUT: %OptionalAs.impl_witness_table.8ee = impl_witness_table (%Core.import_ref.7fb), @T.binding.as_type.as.OptionalAs.impl [concrete] +// CHECK:STDOUT: %Core.import_ref.54b: @Cpp.nullptr_t.as.ImplicitAs.impl.%Cpp.nullptr_t.as.ImplicitAs.impl.Convert.type (%Cpp.nullptr_t.as.ImplicitAs.impl.Convert.type.d80) = import_ref Core//prelude/types/cpp/nullptr, loc{{\d+_\d+}}, loaded [symbolic = @Cpp.nullptr_t.as.ImplicitAs.impl.%Cpp.nullptr_t.as.ImplicitAs.impl.Convert (constants.%Cpp.nullptr_t.as.ImplicitAs.impl.Convert.2ce)] +// CHECK:STDOUT: %ImplicitAs.impl_witness_table.87f = impl_witness_table (%Core.import_ref.54b), @Cpp.nullptr_t.as.ImplicitAs.impl [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: fn @F() { @@ -192,8 +205,8 @@ fn F() { // CHECK:STDOUT: %array_type: type = array_type %int_42, %i32.loc10 [concrete = constants.%array_type] // CHECK:STDOUT: } // CHECK:STDOUT: %n: ref %array_type = ref_binding n, %n.var -// CHECK:STDOUT: %Cpp.ref: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] -// CHECK:STDOUT: %TakesArray.ref: %TakesArray.cpp_overload_set.type = name_ref TakesArray, imports.%TakesArray.cpp_overload_set.value [concrete = constants.%TakesArray.cpp_overload_set.value] +// CHECK:STDOUT: %Cpp.ref.loc11: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %TakesArray.ref.loc11: %TakesArray.cpp_overload_set.type = name_ref TakesArray, imports.%TakesArray.cpp_overload_set.value [concrete = constants.%TakesArray.cpp_overload_set.value] // CHECK:STDOUT: %n.ref: ref %array_type = name_ref n, %n // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] // CHECK:STDOUT: %int_32.loc11: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] @@ -201,30 +214,50 @@ fn F() { // CHECK:STDOUT: %impl.elem0.loc11_21: %.322 = impl_witness_access constants.%ImplicitAs.impl_witness.bc9, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.e9b] // CHECK:STDOUT: %bound_method.loc11_21.1: = bound_method %int_0, %impl.elem0.loc11_21 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn.loc11_21: = specific_function %impl.elem0.loc11_21, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] -// CHECK:STDOUT: %bound_method.loc11_21.2: = bound_method %int_0, %specific_fn.loc11_21 [concrete = constants.%bound_method] +// CHECK:STDOUT: %bound_method.loc11_21.2: = bound_method %int_0, %specific_fn.loc11_21 [concrete = constants.%bound_method.9be] // CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc11_21.2(%int_0) [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc11_21.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc11_21.2: %i32 = converted %int_0, %.loc11_21.1 [concrete = constants.%int_0.6a9] // CHECK:STDOUT: %.loc11_22: ref %i32 = array_index %n.ref, %.loc11_21.2 // CHECK:STDOUT: %addr.loc11_18.1: %ptr.235 = addr_of %.loc11_22 -// CHECK:STDOUT: %impl.elem0.loc11_18: %.4d2 = impl_witness_access constants.%ImplicitAs.impl_witness.f51, element0 [concrete = constants.%U.binding.as_type.as.ImplicitAs.impl.Convert.b59] +// CHECK:STDOUT: %impl.elem0.loc11_18: %.4ad = impl_witness_access constants.%ImplicitAs.impl_witness.a9a, element0 [concrete = constants.%U.binding.as_type.as.ImplicitAs.impl.Convert.1c9] // CHECK:STDOUT: %bound_method.loc11_18.1: = bound_method %addr.loc11_18.1, %impl.elem0.loc11_18 -// CHECK:STDOUT: %specific_fn.loc11_18: = specific_function %impl.elem0.loc11_18, @U.binding.as_type.as.ImplicitAs.impl.Convert.2(constants.%OptionalStorage.facet.0a3, constants.%OptionalAs.facet) [concrete = constants.%U.binding.as_type.as.ImplicitAs.impl.Convert.specific_fn] +// CHECK:STDOUT: %specific_fn.loc11_18: = specific_function %impl.elem0.loc11_18, @U.binding.as_type.as.ImplicitAs.impl.Convert.2(constants.%OptionalStorage.facet.083, constants.%OptionalAs.facet) [concrete = constants.%U.binding.as_type.as.ImplicitAs.impl.Convert.specific_fn] // CHECK:STDOUT: %bound_method.loc11_18.2: = bound_method %addr.loc11_18.1, %specific_fn.loc11_18 -// CHECK:STDOUT: %U.binding.as_type.as.ImplicitAs.impl.Convert.call: init %Optional.18c = call %bound_method.loc11_18.2(%addr.loc11_18.1) -// CHECK:STDOUT: %.loc11_18.1: init %Optional.18c = converted %addr.loc11_18.1, %U.binding.as_type.as.ImplicitAs.impl.Convert.call -// CHECK:STDOUT: %.loc11_18.2: ref %Optional.18c = temporary_storage -// CHECK:STDOUT: %.loc11_18.3: ref %Optional.18c = temporary %.loc11_18.2, %.loc11_18.1 -// CHECK:STDOUT: %.loc11_18.4: %Optional.18c = acquire_value %.loc11_18.3 -// CHECK:STDOUT: %TakesArray.call: init %empty_tuple.type = call imports.%TakesArray.decl(%.loc11_18.4) -// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.bound.loc11: = bound_method %.loc11_18.3, constants.%DestroyT.binding.as_type.as.Destroy.impl.Op.55c +// CHECK:STDOUT: %U.binding.as_type.as.ImplicitAs.impl.Convert.call: init %Optional.97d = call %bound_method.loc11_18.2(%addr.loc11_18.1) +// CHECK:STDOUT: %.loc11_18.1: init %Optional.97d = converted %addr.loc11_18.1, %U.binding.as_type.as.ImplicitAs.impl.Convert.call +// CHECK:STDOUT: %.loc11_18.2: ref %Optional.97d = temporary_storage +// CHECK:STDOUT: %.loc11_18.3: ref %Optional.97d = temporary %.loc11_18.2, %.loc11_18.1 +// CHECK:STDOUT: %.loc11_18.4: %Optional.97d = acquire_value %.loc11_18.3 +// CHECK:STDOUT: %TakesArray.call.loc11: init %empty_tuple.type = call imports.%TakesArray.decl(%.loc11_18.4) +// CHECK:STDOUT: %Cpp.ref.loc13_3: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %TakesArray.ref.loc13: %TakesArray.cpp_overload_set.type = name_ref TakesArray, imports.%TakesArray.cpp_overload_set.value [concrete = constants.%TakesArray.cpp_overload_set.value] +// CHECK:STDOUT: %Cpp.ref.loc13_18: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] // CHECK:STDOUT: -// CHECK:STDOUT: %bound_method.loc11_18.3: = bound_method %.loc11_18.3, %DestroyT.binding.as_type.as.Destroy.impl.Op.specific_fn.1 -// CHECK:STDOUT: %addr.loc11_18.2: %ptr.a30 = addr_of %.loc11_18.3 +// CHECK:STDOUT: %nullptr.ref: %Cpp.nullptr_t = name_ref nullptr, %uninit [concrete = constants.%uninit] +// CHECK:STDOUT: %impl.elem0.loc13: %.28d = impl_witness_access constants.%ImplicitAs.impl_witness.213, element0 [concrete = constants.%Cpp.nullptr_t.as.ImplicitAs.impl.Convert.83e] +// CHECK:STDOUT: %bound_method.loc13_21.1: = bound_method %nullptr.ref, %impl.elem0.loc13 [concrete = constants.%Cpp.nullptr_t.as.ImplicitAs.impl.Convert.bound] +// CHECK:STDOUT: %specific_fn.loc13: = specific_function %impl.elem0.loc13, @Cpp.nullptr_t.as.ImplicitAs.impl.Convert(constants.%i32) [concrete = constants.%Cpp.nullptr_t.as.ImplicitAs.impl.Convert.specific_fn] +// CHECK:STDOUT: %bound_method.loc13_21.2: = bound_method %nullptr.ref, %specific_fn.loc13 [concrete = constants.%bound_method.ed9] +// CHECK:STDOUT: %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.call: init %Optional.97d = call %bound_method.loc13_21.2(%nullptr.ref) +// CHECK:STDOUT: %.loc13_21.1: init %Optional.97d = converted %nullptr.ref, %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.call +// CHECK:STDOUT: %.loc13_21.2: ref %Optional.97d = temporary_storage +// CHECK:STDOUT: %.loc13_21.3: ref %Optional.97d = temporary %.loc13_21.2, %.loc13_21.1 +// CHECK:STDOUT: %.loc13_21.4: %Optional.97d = acquire_value %.loc13_21.3 +// CHECK:STDOUT: %TakesArray.call.loc13: init %empty_tuple.type = call imports.%TakesArray.decl(%.loc13_21.4) +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.bound.loc13: = bound_method %.loc13_21.3, constants.%DestroyT.binding.as_type.as.Destroy.impl.Op.9b6 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc13_21.3: = bound_method %.loc13_21.3, %DestroyT.binding.as_type.as.Destroy.impl.Op.specific_fn.1 +// CHECK:STDOUT: %addr.loc13: %ptr.334 = addr_of %.loc13_21.3 +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.call.loc13: init %empty_tuple.type = call %bound_method.loc13_21.3(%addr.loc13) +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.bound.loc11: = bound_method %.loc11_18.3, constants.%DestroyT.binding.as_type.as.Destroy.impl.Op.9b6 +// CHECK:STDOUT: +// CHECK:STDOUT: %bound_method.loc11_18.3: = bound_method %.loc11_18.3, %DestroyT.binding.as_type.as.Destroy.impl.Op.specific_fn.2 +// CHECK:STDOUT: %addr.loc11_18.2: %ptr.334 = addr_of %.loc11_18.3 // CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.call.loc11: init %empty_tuple.type = call %bound_method.loc11_18.3(%addr.loc11_18.2) // CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.bound.loc10: = bound_method %n.var, constants.%DestroyT.binding.as_type.as.Destroy.impl.Op.f36 // CHECK:STDOUT: -// CHECK:STDOUT: %bound_method.loc10: = bound_method %n.var, %DestroyT.binding.as_type.as.Destroy.impl.Op.specific_fn.2 +// CHECK:STDOUT: %bound_method.loc10: = bound_method %n.var, %DestroyT.binding.as_type.as.Destroy.impl.Op.specific_fn.3 // CHECK:STDOUT: %addr.loc10: %ptr.830 = addr_of %n.var // CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.call.loc10: init %empty_tuple.type = call %bound_method.loc10(%addr.loc10) // CHECK:STDOUT: @@ -238,8 +271,6 @@ fn F() { // CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] // CHECK:STDOUT: %G.type: type = fn_type @G [concrete] // CHECK:STDOUT: %G: %G.type = struct_value () [concrete] -// CHECK:STDOUT: %TakesArray.cpp_overload_set.type: type = cpp_overload_set_type @TakesArray.cpp_overload_set [concrete] -// CHECK:STDOUT: %TakesArray.cpp_overload_set.value: %TakesArray.cpp_overload_set.type = cpp_overload_set_value @TakesArray.cpp_overload_set [concrete] // CHECK:STDOUT: %TakesFunction.cpp_overload_set.type: type = cpp_overload_set_type @TakesFunction.cpp_overload_set [concrete] // CHECK:STDOUT: %TakesFunction.cpp_overload_set.value: %TakesFunction.cpp_overload_set.type = cpp_overload_set_value @TakesFunction.cpp_overload_set [concrete] // CHECK:STDOUT: %Function.cpp_overload_set.type: type = cpp_overload_set_type @Function.cpp_overload_set [concrete] @@ -264,6 +295,8 @@ fn F() { // CHECK:STDOUT: %bound_method: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] // CHECK:STDOUT: %int_0.6a9: %i32 = int_value 0 [concrete] // CHECK:STDOUT: %ptr.235: type = ptr_type %i32 [concrete] +// CHECK:STDOUT: %Cpp.nullptr_t: type = class_type @NullptrT [concrete] +// CHECK:STDOUT: %uninit: %Cpp.nullptr_t = uninitialized_value [concrete] // CHECK:STDOUT: %type_where: type = facet_type > [concrete] // CHECK:STDOUT: %facet_value: %type_where = facet_value %array_type, () [concrete] // CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.type.1d8: type = fn_type @DestroyT.binding.as_type.as.Destroy.impl.Op, @DestroyT.binding.as_type.as.Destroy.impl(%facet_value) [concrete] @@ -272,13 +305,11 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: imports { // CHECK:STDOUT: %Cpp: = namespace file.%Cpp.import_cpp, [concrete] { -// CHECK:STDOUT: .TakesArray = %TakesArray.cpp_overload_set.value -// CHECK:STDOUT: .nullptr = // CHECK:STDOUT: .TakesFunction = %TakesFunction.cpp_overload_set.value // CHECK:STDOUT: .Function = %Function.cpp_overload_set.value +// CHECK:STDOUT: .nullptr = @F.%uninit // CHECK:STDOUT: import Cpp//... // CHECK:STDOUT: } -// CHECK:STDOUT: %TakesArray.cpp_overload_set.value: %TakesArray.cpp_overload_set.type = cpp_overload_set_value @TakesArray.cpp_overload_set [concrete = constants.%TakesArray.cpp_overload_set.value] // CHECK:STDOUT: %TakesFunction.cpp_overload_set.value: %TakesFunction.cpp_overload_set.type = cpp_overload_set_value @TakesFunction.cpp_overload_set [concrete = constants.%TakesFunction.cpp_overload_set.value] // CHECK:STDOUT: %Function.cpp_overload_set.value: %Function.cpp_overload_set.type = cpp_overload_set_value @Function.cpp_overload_set [concrete = constants.%Function.cpp_overload_set.value] // CHECK:STDOUT: %Core.import_ref.e24: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.f51) = import_ref Core//prelude/types/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.2a1)] @@ -287,53 +318,50 @@ fn F() { // 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: %TakesArray.ref: %TakesArray.cpp_overload_set.type = name_ref TakesArray, imports.%TakesArray.cpp_overload_set.value [concrete = constants.%TakesArray.cpp_overload_set.value] -// CHECK:STDOUT: %Cpp.ref.loc14_18: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] -// CHECK:STDOUT: %nullptr.ref.loc14: = name_ref nullptr, [concrete = ] -// CHECK:STDOUT: %Cpp.ref.loc20: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] -// CHECK:STDOUT: %TakesFunction.ref.loc20: %TakesFunction.cpp_overload_set.type = name_ref TakesFunction, imports.%TakesFunction.cpp_overload_set.value [concrete = constants.%TakesFunction.cpp_overload_set.value] +// CHECK:STDOUT: %Cpp.ref.loc14: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %TakesFunction.ref.loc14: %TakesFunction.cpp_overload_set.type = name_ref TakesFunction, imports.%TakesFunction.cpp_overload_set.value [concrete = constants.%TakesFunction.cpp_overload_set.value] // CHECK:STDOUT: %G.ref: %G.type = name_ref G, file.%G.decl [concrete = constants.%G] -// CHECK:STDOUT: %Cpp.ref.loc26_3: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] -// CHECK:STDOUT: %TakesFunction.ref.loc26: %TakesFunction.cpp_overload_set.type = name_ref TakesFunction, imports.%TakesFunction.cpp_overload_set.value [concrete = constants.%TakesFunction.cpp_overload_set.value] -// CHECK:STDOUT: %Cpp.ref.loc26_21: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %Cpp.ref.loc20_3: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %TakesFunction.ref.loc20: %TakesFunction.cpp_overload_set.type = name_ref TakesFunction, imports.%TakesFunction.cpp_overload_set.value [concrete = constants.%TakesFunction.cpp_overload_set.value] +// CHECK:STDOUT: %Cpp.ref.loc20_21: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] // CHECK:STDOUT: %Function.ref: %Function.cpp_overload_set.type = name_ref Function, imports.%Function.cpp_overload_set.value [concrete = constants.%Function.cpp_overload_set.value] // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %n.patt: %pattern_type.b6e = ref_binding_pattern n [concrete] // CHECK:STDOUT: %n.var_patt: %pattern_type.b6e = var_pattern %n.patt [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: %n.var: ref %array_type = var %n.var_patt -// CHECK:STDOUT: %.loc28: type = splice_block %array_type [concrete = constants.%array_type] { -// CHECK:STDOUT: %int_32.loc28: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] -// CHECK:STDOUT: %i32.loc28: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] +// CHECK:STDOUT: %.loc22: type = splice_block %array_type [concrete = constants.%array_type] { +// CHECK:STDOUT: %int_32.loc22: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] +// CHECK:STDOUT: %i32.loc22: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] // CHECK:STDOUT: %int_42: Core.IntLiteral = int_value 42 [concrete = constants.%int_42] -// CHECK:STDOUT: %array_type: type = array_type %int_42, %i32.loc28 [concrete = constants.%array_type] +// CHECK:STDOUT: %array_type: type = array_type %int_42, %i32.loc22 [concrete = constants.%array_type] // CHECK:STDOUT: } // CHECK:STDOUT: %n: ref %array_type = ref_binding n, %n.var -// CHECK:STDOUT: %Cpp.ref.loc37: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] -// CHECK:STDOUT: %TakesFunction.ref.loc37: %TakesFunction.cpp_overload_set.type = name_ref TakesFunction, imports.%TakesFunction.cpp_overload_set.value [concrete = constants.%TakesFunction.cpp_overload_set.value] +// CHECK:STDOUT: %Cpp.ref.loc31: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %TakesFunction.ref.loc31: %TakesFunction.cpp_overload_set.type = name_ref TakesFunction, imports.%TakesFunction.cpp_overload_set.value [concrete = constants.%TakesFunction.cpp_overload_set.value] // CHECK:STDOUT: %n.ref: ref %array_type = name_ref n, %n // CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] -// CHECK:STDOUT: %int_32.loc37: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] -// CHECK:STDOUT: %i32.loc37: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] +// CHECK:STDOUT: %int_32.loc31: Core.IntLiteral = int_value 32 [concrete = constants.%int_32] +// CHECK:STDOUT: %i32.loc31: type = class_type @Int, @Int(constants.%int_32) [concrete = constants.%i32] // CHECK:STDOUT: %impl.elem0: %.322 = impl_witness_access constants.%ImplicitAs.impl_witness.bc9, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.e9b] -// CHECK:STDOUT: %bound_method.loc37_24.1: = bound_method %int_0, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] +// CHECK:STDOUT: %bound_method.loc31_24.1: = bound_method %int_0, %impl.elem0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound] // CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] -// CHECK:STDOUT: %bound_method.loc37_24.2: = bound_method %int_0, %specific_fn [concrete = constants.%bound_method] -// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc37_24.2(%int_0) [concrete = constants.%int_0.6a9] -// CHECK:STDOUT: %.loc37_24.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_0.6a9] -// CHECK:STDOUT: %.loc37_24.2: %i32 = converted %int_0, %.loc37_24.1 [concrete = constants.%int_0.6a9] -// CHECK:STDOUT: %.loc37_25: ref %i32 = array_index %n.ref, %.loc37_24.2 -// CHECK:STDOUT: %addr.loc37: %ptr.235 = addr_of %.loc37_25 -// CHECK:STDOUT: %Cpp.ref.loc43_3: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] -// CHECK:STDOUT: %TakesFunction.ref.loc43: %TakesFunction.cpp_overload_set.type = name_ref TakesFunction, imports.%TakesFunction.cpp_overload_set.value [concrete = constants.%TakesFunction.cpp_overload_set.value] -// CHECK:STDOUT: %Cpp.ref.loc43_21: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] -// CHECK:STDOUT: %nullptr.ref.loc43: = name_ref nullptr, [concrete = ] +// CHECK:STDOUT: %bound_method.loc31_24.2: = bound_method %int_0, %specific_fn [concrete = constants.%bound_method] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call: init %i32 = call %bound_method.loc31_24.2(%int_0) [concrete = constants.%int_0.6a9] +// CHECK:STDOUT: %.loc31_24.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_0.6a9] +// CHECK:STDOUT: %.loc31_24.2: %i32 = converted %int_0, %.loc31_24.1 [concrete = constants.%int_0.6a9] +// CHECK:STDOUT: %.loc31_25: ref %i32 = array_index %n.ref, %.loc31_24.2 +// CHECK:STDOUT: %addr.loc31: %ptr.235 = addr_of %.loc31_25 +// CHECK:STDOUT: %Cpp.ref.loc37_3: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: %TakesFunction.ref.loc37: %TakesFunction.cpp_overload_set.type = name_ref TakesFunction, imports.%TakesFunction.cpp_overload_set.value [concrete = constants.%TakesFunction.cpp_overload_set.value] +// CHECK:STDOUT: %Cpp.ref.loc37_21: = name_ref Cpp, imports.%Cpp [concrete = imports.%Cpp] +// CHECK:STDOUT: +// CHECK:STDOUT: %nullptr.ref: %Cpp.nullptr_t = name_ref nullptr, %uninit [concrete = constants.%uninit] // CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.bound: = bound_method %n.var, constants.%DestroyT.binding.as_type.as.Destroy.impl.Op.f36 // CHECK:STDOUT: -// CHECK:STDOUT: %bound_method.loc28: = bound_method %n.var, %DestroyT.binding.as_type.as.Destroy.impl.Op.specific_fn -// CHECK:STDOUT: %addr.loc28: %ptr.830 = addr_of %n.var -// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc28(%addr.loc28) +// CHECK:STDOUT: %bound_method.loc22: = bound_method %n.var, %DestroyT.binding.as_type.as.Destroy.impl.Op.specific_fn +// CHECK:STDOUT: %addr.loc22: %ptr.830 = addr_of %n.var +// CHECK:STDOUT: %DestroyT.binding.as_type.as.Destroy.impl.Op.call: init %empty_tuple.type = call %bound_method.loc22(%addr.loc22) // CHECK:STDOUT: // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/lower/constant.cpp b/toolchain/lower/constant.cpp index f336cb286b8b..c64827bfa967 100644 --- a/toolchain/lower/constant.cpp +++ b/toolchain/lower/constant.cpp @@ -283,6 +283,11 @@ static auto EmitAsConstant(ConstantContext& context, SemIR::StringLiteral inst) /*name=*/"", /*address_space=*/0, &context.llvm_module()); } +static auto EmitAsConstant(ConstantContext& context, + SemIR::UninitializedValue inst) -> llvm::Constant* { + return llvm::PoisonValue::get(context.GetType(inst.type_id)); +} + static auto EmitAsConstant(ConstantContext& context, SemIR::VarStorage inst) -> llvm::Constant* { // Create the corresponding global variable declaration. diff --git a/toolchain/lower/testdata/interop/cpp/nullptr.carbon b/toolchain/lower/testdata/interop/cpp/nullptr.carbon new file mode 100644 index 000000000000..dabf7ef6a0a4 --- /dev/null +++ b/toolchain/lower/testdata/interop/cpp/nullptr.carbon @@ -0,0 +1,222 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/full.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/interop/cpp/nullptr.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/interop/cpp/nullptr.carbon + +// --- simple.carbon + +import Cpp inline ''' +using nullptr_t = decltype(nullptr); + +void TakePtr(int *p); +void TakeNullptr(nullptr_t n); + +nullptr_t ReturnNullptr(); +'''; + +fn PassNullptr() { + Cpp.TakePtr(Cpp.nullptr); + + Cpp.TakeNullptr(Cpp.nullptr); +} + +fn ReturnNullptr() -> Cpp.nullptr_t { + return Cpp.nullptr; +} + +fn ReturnNullptrCopy() -> Cpp.nullptr_t { + var a: Cpp.nullptr_t = Cpp.nullptr; + return a; +} + +fn ReturnConvertedNullptrIndirectly() -> Core.Optional(i32*) { + var a: Cpp.nullptr_t = Cpp.ReturnNullptr(); + return a; +} + +fn ReturnConvertedNullptrDirectly() -> Core.Optional(i32*) { + return Cpp.ReturnNullptr(); +} + +fn ConvertNullptrConstant() -> Core.Optional(i32*) { + return Cpp.nullptr; +} + +// CHECK:STDOUT: ; ModuleID = 'simple.carbon' +// CHECK:STDOUT: source_filename = "simple.carbon" +// CHECK:STDOUT: target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" +// CHECK:STDOUT: target triple = "x86_64-unknown-linux-gnu" +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CPassNullptr.Main() #0 !dbg !7 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc12_18.2.temp = alloca ptr, align 8, !dbg !10 +// CHECK:STDOUT: %.loc14_22.1.temp = alloca ptr, align 8, !dbg !11 +// CHECK:STDOUT: %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.call = call ptr @"_CConvert.NullptrT.CppCompat.Core:ImplicitAs.Core.b88d1103f417c6d4"(ptr poison), !dbg !10 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc12_18.2.temp), !dbg !10 +// CHECK:STDOUT: store ptr %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.call, ptr %.loc12_18.2.temp, align 8, !dbg !10 +// CHECK:STDOUT: %.loc12_18.4 = load ptr, ptr %.loc12_18.2.temp, align 8, !dbg !10 +// CHECK:STDOUT: call void @_Z7TakePtrPi(ptr %.loc12_18.4), !dbg !12 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc14_22.1.temp), !dbg !11 +// CHECK:STDOUT: %Cpp.nullptr_t.as.Copy.impl.Op.call = call ptr @"_COp.NullptrT.CppCompat.Core:Copy.Core"(ptr poison), !dbg !11 +// CHECK:STDOUT: store ptr %Cpp.nullptr_t.as.Copy.impl.Op.call, ptr %.loc14_22.1.temp, align 8, !dbg !11 +// CHECK:STDOUT: call void @_Z11TakeNullptrDn.carbon_thunk(ptr %.loc14_22.1.temp), !dbg !13 +// CHECK:STDOUT: ret void, !dbg !14 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_Z7TakePtrPi(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: declare ptr @"_COp.NullptrT.CppCompat.Core:Copy.Core"(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define ptr @_CReturnNullptr.Main() #0 !dbg !15 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %Cpp.nullptr_t.as.Copy.impl.Op.call = call ptr @"_COp.NullptrT.CppCompat.Core:Copy.Core"(ptr poison), !dbg !16 +// CHECK:STDOUT: ret ptr %Cpp.nullptr_t.as.Copy.impl.Op.call, !dbg !17 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define ptr @_CReturnNullptrCopy.Main() #0 !dbg !18 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %a.var = alloca ptr, align 8, !dbg !19 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %a.var), !dbg !19 +// CHECK:STDOUT: %Cpp.nullptr_t.as.Copy.impl.Op.call.loc22 = call ptr @"_COp.NullptrT.CppCompat.Core:Copy.Core"(ptr poison), !dbg !20 +// CHECK:STDOUT: store ptr %Cpp.nullptr_t.as.Copy.impl.Op.call.loc22, ptr %a.var, align 8, !dbg !19 +// CHECK:STDOUT: %.loc23 = load ptr, ptr %a.var, align 8, !dbg !21 +// CHECK:STDOUT: %Cpp.nullptr_t.as.Copy.impl.Op.call.loc23 = call ptr @"_COp.NullptrT.CppCompat.Core:Copy.Core"(ptr %.loc23), !dbg !21 +// CHECK:STDOUT: ret ptr %Cpp.nullptr_t.as.Copy.impl.Op.call.loc23, !dbg !22 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define ptr @_CReturnConvertedNullptrIndirectly.Main() #0 !dbg !23 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %a.var = alloca ptr, align 8, !dbg !24 +// CHECK:STDOUT: %.loc27_44.1.temp = alloca ptr, align 8, !dbg !25 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %a.var), !dbg !24 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc27_44.1.temp), !dbg !25 +// CHECK:STDOUT: call void @_Z13ReturnNullptrv.carbon_thunk(ptr %.loc27_44.1.temp), !dbg !25 +// CHECK:STDOUT: %.loc27_44.2 = load ptr, ptr %.loc27_44.1.temp, align 8, !dbg !25 +// CHECK:STDOUT: store ptr %.loc27_44.2, ptr %a.var, align 8, !dbg !24 +// CHECK:STDOUT: %.loc28_10 = load ptr, ptr %a.var, align 8, !dbg !26 +// CHECK:STDOUT: %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.call = call ptr @"_CConvert.NullptrT.CppCompat.Core:ImplicitAs.Core.b88d1103f417c6d4"(ptr %.loc28_10), !dbg !27 +// CHECK:STDOUT: ret ptr %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.call, !dbg !27 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define ptr @_CReturnConvertedNullptrDirectly.Main() #0 !dbg !28 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc32_28.1.temp = alloca ptr, align 8, !dbg !29 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc32_28.1.temp), !dbg !29 +// CHECK:STDOUT: call void @_Z13ReturnNullptrv.carbon_thunk(ptr %.loc32_28.1.temp), !dbg !29 +// CHECK:STDOUT: %.loc32_28.2 = load ptr, ptr %.loc32_28.1.temp, align 8, !dbg !29 +// CHECK:STDOUT: %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.call = call ptr @"_CConvert.NullptrT.CppCompat.Core:ImplicitAs.Core.b88d1103f417c6d4"(ptr %.loc32_28.2), !dbg !30 +// CHECK:STDOUT: ret ptr %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.call, !dbg !30 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define ptr @_CConvertNullptrConstant.Main() #0 !dbg !31 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.call = call ptr @"_CConvert.NullptrT.CppCompat.Core:ImplicitAs.Core.b88d1103f417c6d4"(ptr poison), !dbg !32 +// CHECK:STDOUT: ret ptr %Cpp.nullptr_t.as.ImplicitAs.impl.Convert.call, !dbg !32 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define linkonce_odr ptr @"_CConvert.NullptrT.CppCompat.Core:ImplicitAs.Core.b88d1103f417c6d4"(ptr %self) #0 !dbg !33 { +// CHECK:STDOUT: %1 = call ptr @_CNone.Optional.Core.e49c05a5c9dc9c86(), !dbg !35 +// CHECK:STDOUT: ret ptr %1, !dbg !36 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: declare ptr @_CMake.NullptrT.CppCompat.Core() +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define linkonce_odr ptr @_CNone.Optional.Core.e49c05a5c9dc9c86() #0 !dbg !37 { +// CHECK:STDOUT: ret ptr null, !dbg !39 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress +// CHECK:STDOUT: define dso_local void @_Z11TakeNullptrDn.carbon_thunk(ptr %n) #2 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %n.addr = alloca ptr, align 8 +// CHECK:STDOUT: store ptr %n, ptr %n.addr, align 8 +// CHECK:STDOUT: %0 = load ptr, ptr %n.addr, align 8 +// CHECK:STDOUT: call void @_Z11TakeNullptrDn(ptr null) +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_Z11TakeNullptrDn(ptr) #3 +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: alwaysinline mustprogress +// CHECK:STDOUT: define dso_local void @_Z13ReturnNullptrv.carbon_thunk(ptr %return) #2 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %return.addr = alloca ptr, align 8 +// CHECK:STDOUT: store ptr %return, ptr %return.addr, align 8 +// CHECK:STDOUT: %0 = load ptr, ptr %return.addr, align 8 +// CHECK:STDOUT: %call = call ptr @_Z13ReturnNullptrv() +// CHECK:STDOUT: store ptr %call, ptr %0, align 8 +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: declare ptr @_Z13ReturnNullptrv() #3 +// CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @"_CConvert.NullptrT.CppCompat.Core:ImplicitAs.Core.b88d1103f417c6d4", { 3, 2, 1, 0 } +// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 5, 4, 3, 2, 1, 0 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: attributes #2 = { alwaysinline mustprogress "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +// CHECK:STDOUT: attributes #3 = { "no-trapping-math"="true" "stack-protector-buffer-size"="0" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1, !2, !3, !4} +// CHECK:STDOUT: !llvm.dbg.cu = !{!5} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = !{i32 1, !"wchar_size", i32 4} +// CHECK:STDOUT: !3 = !{i32 8, !"PIC Level", i32 0} +// CHECK:STDOUT: !4 = !{i32 7, !"PIE Level", i32 2} +// CHECK:STDOUT: !5 = distinct !DICompileUnit(language: DW_LANG_C, file: !6, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !6 = !DIFile(filename: "simple.carbon", directory: "") +// CHECK:STDOUT: !7 = distinct !DISubprogram(name: "PassNullptr", linkageName: "_CPassNullptr.Main", scope: null, file: !6, line: 11, type: !8, spFlags: DISPFlagDefinition, unit: !5) +// CHECK:STDOUT: !8 = !DISubroutineType(types: !9) +// CHECK:STDOUT: !9 = !{} +// CHECK:STDOUT: !10 = !DILocation(line: 12, column: 15, scope: !7) +// CHECK:STDOUT: !11 = !DILocation(line: 14, column: 19, scope: !7) +// CHECK:STDOUT: !12 = !DILocation(line: 12, column: 3, scope: !7) +// CHECK:STDOUT: !13 = !DILocation(line: 14, column: 3, scope: !7) +// CHECK:STDOUT: !14 = !DILocation(line: 11, column: 1, scope: !7) +// CHECK:STDOUT: !15 = distinct !DISubprogram(name: "ReturnNullptr", linkageName: "_CReturnNullptr.Main", scope: null, file: !6, line: 17, type: !8, spFlags: DISPFlagDefinition, unit: !5) +// CHECK:STDOUT: !16 = !DILocation(line: 18, column: 10, scope: !15) +// CHECK:STDOUT: !17 = !DILocation(line: 18, column: 3, scope: !15) +// CHECK:STDOUT: !18 = distinct !DISubprogram(name: "ReturnNullptrCopy", linkageName: "_CReturnNullptrCopy.Main", scope: null, file: !6, line: 21, type: !8, spFlags: DISPFlagDefinition, unit: !5) +// CHECK:STDOUT: !19 = !DILocation(line: 22, column: 3, scope: !18) +// CHECK:STDOUT: !20 = !DILocation(line: 22, column: 26, scope: !18) +// CHECK:STDOUT: !21 = !DILocation(line: 23, column: 10, scope: !18) +// CHECK:STDOUT: !22 = !DILocation(line: 23, column: 3, scope: !18) +// CHECK:STDOUT: !23 = distinct !DISubprogram(name: "ReturnConvertedNullptrIndirectly", linkageName: "_CReturnConvertedNullptrIndirectly.Main", scope: null, file: !6, line: 26, type: !8, spFlags: DISPFlagDefinition, unit: !5) +// CHECK:STDOUT: !24 = !DILocation(line: 27, column: 3, scope: !23) +// CHECK:STDOUT: !25 = !DILocation(line: 27, column: 26, scope: !23) +// CHECK:STDOUT: !26 = !DILocation(line: 28, column: 10, scope: !23) +// CHECK:STDOUT: !27 = !DILocation(line: 28, column: 3, scope: !23) +// CHECK:STDOUT: !28 = distinct !DISubprogram(name: "ReturnConvertedNullptrDirectly", linkageName: "_CReturnConvertedNullptrDirectly.Main", scope: null, file: !6, line: 31, type: !8, spFlags: DISPFlagDefinition, unit: !5) +// CHECK:STDOUT: !29 = !DILocation(line: 32, column: 10, scope: !28) +// CHECK:STDOUT: !30 = !DILocation(line: 32, column: 3, scope: !28) +// CHECK:STDOUT: !31 = distinct !DISubprogram(name: "ConvertNullptrConstant", linkageName: "_CConvertNullptrConstant.Main", scope: null, file: !6, line: 35, type: !8, spFlags: DISPFlagDefinition, unit: !5) +// CHECK:STDOUT: !32 = !DILocation(line: 36, column: 3, scope: !31) +// CHECK:STDOUT: !33 = distinct !DISubprogram(name: "Convert", linkageName: "_CConvert.NullptrT.CppCompat.Core:ImplicitAs.Core.b88d1103f417c6d4", scope: null, file: !34, line: 45, type: !8, spFlags: DISPFlagDefinition, unit: !5) +// CHECK:STDOUT: !34 = !DIFile(filename: "{{.*}}/prelude/types/cpp/nullptr.carbon", directory: "") +// CHECK:STDOUT: !35 = !DILocation(line: 46, column: 14, scope: !33) +// CHECK:STDOUT: !36 = !DILocation(line: 46, column: 7, scope: !33) +// CHECK:STDOUT: !37 = distinct !DISubprogram(name: "None", linkageName: "_CNone.Optional.Core.e49c05a5c9dc9c86", scope: null, file: !38, line: 26, type: !8, spFlags: DISPFlagDefinition, unit: !5) +// CHECK:STDOUT: !38 = !DIFile(filename: "{{.*}}/prelude/types/optional.carbon", directory: "") +// CHECK:STDOUT: !39 = !DILocation(line: 27, column: 5, scope: !37) diff --git a/toolchain/sem_ir/expr_info.cpp b/toolchain/sem_ir/expr_info.cpp index a1019b98e4bd..cc2ddfbdb312 100644 --- a/toolchain/sem_ir/expr_info.cpp +++ b/toolchain/sem_ir/expr_info.cpp @@ -166,6 +166,7 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory { case TypeType::Kind: case UnaryOperatorNot::Kind: case UnboundElementType::Kind: + case UninitializedValue::Kind: case ValueOfInitializer::Kind: case ValueParam::Kind: case VtableType::Kind: diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index b356cfe428cf..37d6143037b0 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -147,6 +147,7 @@ CARBON_SEM_IR_INST_KIND(TypeOfInst) CARBON_SEM_IR_INST_KIND(TypeType) CARBON_SEM_IR_INST_KIND(UnaryOperatorNot) CARBON_SEM_IR_INST_KIND(UnboundElementType) +CARBON_SEM_IR_INST_KIND(UninitializedValue) CARBON_SEM_IR_INST_KIND(ValueAsRef) CARBON_SEM_IR_INST_KIND(ValueBinding) CARBON_SEM_IR_INST_KIND(ValueBindingPattern) diff --git a/toolchain/sem_ir/inst_namer.cpp b/toolchain/sem_ir/inst_namer.cpp index 496992bfcc8d..22cf211693cf 100644 --- a/toolchain/sem_ir/inst_namer.cpp +++ b/toolchain/sem_ir/inst_namer.cpp @@ -1286,6 +1286,10 @@ auto InstNamer::NamingContext::NameInst() -> void { } return; } + case UninitializedValue::Kind: { + AddInstName("uninit"); + return; + } case VarPattern::Kind: { AddInstNameId(GetPrettyNameFromPatternId(sem_ir(), inst_id_), ".var_patt"); diff --git a/toolchain/sem_ir/type_info.cpp b/toolchain/sem_ir/type_info.cpp index 002c178764e8..3cbfc62d07dc 100644 --- a/toolchain/sem_ir/type_info.cpp +++ b/toolchain/sem_ir/type_info.cpp @@ -143,10 +143,19 @@ auto TypeLiteralInfo::ForType(const File& file, ClassType class_type) return {.kind = None}; } - // The class must be declared in the `Core` package. + // The class must be declared in the `Core` package. We check for up to one + // level of enclosing namespace. const auto& class_info = file.classes().Get(class_type.class_id); + auto parent_scope_name_id = SemIR::NameId::None; if (!file.name_scopes().IsInCorePackageRoot(class_info.scope_id)) { - return {.kind = None}; + if (!file.name_scopes().IsInCorePackageRoot(class_info.parent_scope_id)) { + return {.kind = None}; + } + parent_scope_name_id = + file.name_scopes().Get(class_info.parent_scope_id).name_id(); + if (!parent_scope_name_id.has_value()) { + return {.kind = None}; + } } // The class's name must be the name corresponding to a type literal. @@ -155,11 +164,24 @@ auto TypeLiteralInfo::ForType(const File& file, ClassType class_type) return {.kind = None}; } - Kind kind = llvm::StringSwitch(*name_ident) - .Case("Char", Char) - .Case("String", Str) - .Default(None); - return {.kind = kind}; + if (!parent_scope_name_id.has_value()) { + Kind kind = llvm::StringSwitch(*name_ident) + .Case("Char", Char) + .Case("String", Str) + .Default(None); + return {.kind = kind}; + } + + auto parent_name_ident = + file.names().GetAsStringIfIdentifier(parent_scope_name_id); + if (parent_name_ident == "CppCompat") { + Kind kind = llvm::StringSwitch(*name_ident) + .Case("NullptrT", CppNullptrT) + .Default(None); + return {.kind = kind}; + } + + return {.kind = None}; } auto TypeLiteralInfo::PrintLiteral(const File& file, @@ -173,6 +195,9 @@ auto TypeLiteralInfo::PrintLiteral(const File& file, case Char: out << "char"; break; + case CppNullptrT: + out << "Cpp.nullptr_t"; + break; case Str: out << "str"; break; diff --git a/toolchain/sem_ir/type_info.h b/toolchain/sem_ir/type_info.h index fd767cf5cd7d..697e043efde8 100644 --- a/toolchain/sem_ir/type_info.h +++ b/toolchain/sem_ir/type_info.h @@ -235,6 +235,9 @@ struct TypeLiteralInfo { Numeric, // `char` / `Core.Char`. Char, + // `Cpp.nullptr_t` / `Core.CppCompat.NullptrT`. + // TODO: This isn't a type literal. + CppNullptrT, // `str` / `Core.String`. // TODO: Rename `Core.String` to `Core.Str`. Str, diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index a0a0ec0f61ca..ff8f9d622e31 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -1836,6 +1836,16 @@ struct UnboundElementType { TypeInstId element_type_inst_id; }; +// An uninitialized constant value. +struct UninitializedValue { + static constexpr auto Kind = + InstKind::UninitializedValue.Define( + {.ir_name = "uninitialized_value", + .constant_kind = InstConstantKind::Always}); + + TypeId type_id; +}; + // Converts from a value expression to an ephemeral reference expression, in // the case where the value representation of the type is a pointer. For // example, when indexing a value expression of array type, this is used to