diff --git a/core/prelude/types.carbon b/core/prelude/types.carbon index a20744a95511..ddbe3eaffbc5 100644 --- a/core/prelude/types.carbon +++ b/core/prelude/types.carbon @@ -10,6 +10,7 @@ export import library "prelude/types/float"; 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/optional"; export import library "prelude/types/string"; export import library "prelude/types/uint"; diff --git a/core/prelude/types/maybe_unformed.carbon b/core/prelude/types/maybe_unformed.carbon new file mode 100644 index 000000000000..e04cef1a01c0 --- /dev/null +++ b/core/prelude/types/maybe_unformed.carbon @@ -0,0 +1,13 @@ +// 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/maybe_unformed"; + +import library "prelude/destroy"; + +private fn MakeMaybeUnformed(t: type) -> type = "maybe_unformed.make_type"; + +class MaybeUnformed(T:! type) { + adapt MakeMaybeUnformed(T); +} diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index 9162b095a4c5..c2bbdd8f206d 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -1758,6 +1758,15 @@ static auto MakeConstantForBuiltinCall(EvalContext& eval_context, return context.constant_values().Get(SemIR::BoolType::TypeInstId); } + case SemIR::BuiltinFunctionKind::MaybeUnformedMakeType: { + return MakeConstantResult( + context, + SemIR::MaybeUnformedType{ + .type_id = SemIR::TypeType::TypeId, + .inner_id = context.types().GetAsTypeInstId(arg_ids[0])}, + phase); + } + // Character conversions. case SemIR::BuiltinFunctionKind::CharConvertChecked: { if (phase != Phase::Concrete) { diff --git a/toolchain/check/testdata/builtins/maybe_unformed/make_type.carbon b/toolchain/check/testdata/builtins/maybe_unformed/make_type.carbon new file mode 100644 index 000000000000..be2868e349a2 --- /dev/null +++ b/toolchain/check/testdata/builtins/maybe_unformed/make_type.carbon @@ -0,0 +1,127 @@ +// 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/none.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/builtins/maybe_unformed/make_type.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/builtins/maybe_unformed/make_type.carbon + +// --- types.carbon + +library "[[@TEST_NAME]]"; + +fn Make(t: type) -> type = "maybe_unformed.make_type"; + +// --- use_types.carbon + +library "[[@TEST_NAME]]"; + +import library "types"; + +//@dump-sem-ir-begin +var b: Make(Make({})); +//@dump-sem-ir-end + +// --- runtime_call.carbon + +library "[[@TEST_NAME]]"; + +import library "types"; + +//@dump-sem-ir-begin +let t: type = {}; +let u: type = Make(t); +//@dump-sem-ir-end + +// --- fail_bad_decl.carbon + +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "maybe_unformed.make_type" [InvalidBuiltinSignature] +// CHECK:STDERR: fn NoParam() -> type = "maybe_unformed.make_type"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn NoParam() -> type = "maybe_unformed.make_type"; + +// CHECK:STDERR: fail_bad_decl.carbon:[[@LINE+4]]:1: error: invalid signature for builtin function "maybe_unformed.make_type" [InvalidBuiltinSignature] +// CHECK:STDERR: fn ColonBangParam(T:! type) -> type = "maybe_unformed.make_type"; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn ColonBangParam(T:! type) -> type = "maybe_unformed.make_type"; + +// CHECK:STDOUT: --- use_types.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Make.type: type = fn_type @Make [concrete] +// CHECK:STDOUT: %Make: %Make.type = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %.a8d: type = maybe_unformed_type %empty_struct_type [concrete] +// CHECK:STDOUT: %.2ba: type = maybe_unformed_type %.a8d [concrete] +// CHECK:STDOUT: %pattern_type.ce6: type = pattern_type %.2ba [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Main.Make: %Make.type = import_ref Main//types, Make, loaded [concrete = constants.%Make] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %b.patt: %pattern_type.ce6 = binding_pattern b [concrete] +// CHECK:STDOUT: %b.var_patt: %pattern_type.ce6 = var_pattern %b.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %b.var: ref %.2ba = var %b.var_patt [concrete] +// CHECK:STDOUT: %.loc7_21.1: type = splice_block %.loc7_21.3 [concrete = constants.%.2ba] { +// CHECK:STDOUT: %Make.ref.loc7_8: %Make.type = name_ref Make, imports.%Main.Make [concrete = constants.%Make] +// CHECK:STDOUT: %Make.ref.loc7_13: %Make.type = name_ref Make, imports.%Main.Make [concrete = constants.%Make] +// CHECK:STDOUT: %.loc7_19.1: %empty_struct_type = struct_literal () +// CHECK:STDOUT: %.loc7_19.2: type = converted %.loc7_19.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] +// CHECK:STDOUT: %Make.call.loc7_20: init type = call %Make.ref.loc7_13(%.loc7_19.2) [concrete = constants.%.a8d] +// CHECK:STDOUT: %.loc7_20.1: type = value_of_initializer %Make.call.loc7_20 [concrete = constants.%.a8d] +// CHECK:STDOUT: %.loc7_20.2: type = converted %Make.call.loc7_20, %.loc7_20.1 [concrete = constants.%.a8d] +// CHECK:STDOUT: %Make.call.loc7_21: init type = call %Make.ref.loc7_8(%.loc7_20.2) [concrete = constants.%.2ba] +// CHECK:STDOUT: %.loc7_21.2: type = value_of_initializer %Make.call.loc7_21 [concrete = constants.%.2ba] +// CHECK:STDOUT: %.loc7_21.3: type = converted %Make.call.loc7_21, %.loc7_21.2 [concrete = constants.%.2ba] +// CHECK:STDOUT: } +// CHECK:STDOUT: %b: ref %.2ba = bind_name b, %b.var [concrete = %b.var] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- runtime_call.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %pattern_type: type = pattern_type type [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %Make.type: type = fn_type @Make [concrete] +// CHECK:STDOUT: %Make: %Make.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Main.Make: %Make.type = import_ref Main//types, Make, loaded [concrete = constants.%Make] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %t.patt: %pattern_type = binding_pattern t [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc7: type = converted @__global_init.%.loc7, constants.%empty_struct_type [concrete = constants.%empty_struct_type] +// CHECK:STDOUT: %t: type = bind_name t, %.loc7 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %u.patt: %pattern_type = binding_pattern u [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc8_21.1: type = value_of_initializer @__global_init.%Make.call +// CHECK:STDOUT: %.loc8_21.2: type = converted @__global_init.%Make.call, %.loc8_21.1 +// CHECK:STDOUT: %u: type = bind_name u, %.loc8_21.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @__global_init() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc7: %empty_struct_type = struct_literal () +// CHECK:STDOUT: %Make.ref: %Make.type = name_ref Make, imports.%Main.Make [concrete = constants.%Make] +// CHECK:STDOUT: %t.ref: type = name_ref t, file.%t +// CHECK:STDOUT: %Make.call: init type = call %Make.ref(%t.ref) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/for/actual.carbon b/toolchain/check/testdata/for/actual.carbon index 9bface14c648..ef54947b8f4a 100644 --- a/toolchain/check/testdata/for/actual.carbon +++ b/toolchain/check/testdata/for/actual.carbon @@ -220,28 +220,28 @@ fn Read() { // CHECK:STDOUT: %Core.import_ref.1c9: %Iterate.assoc_type = import_ref Core//prelude/iterate, loc12_18, loaded [concrete = constants.%assoc0.724] // CHECK:STDOUT: %Core.import_ref.ed6: %Iterate.assoc_type = import_ref Core//prelude/iterate, loc13_17, loaded [concrete = constants.%assoc1.02e] // CHECK:STDOUT: %Core.import_ref.9e6: type = import_ref Core//prelude/iterate, loc13_17, loaded [concrete = %CursorType] -// CHECK:STDOUT: %Core.import_ref.f49: @Optional.%Optional.None.type (%Optional.None.type.ef2) = import_ref Core//prelude/iterate, inst139 [indirect], loaded [symbolic = @Optional.%Optional.None (constants.%Optional.None.fd6)] -// CHECK:STDOUT: %Core.import_ref.1a8: @Optional.%Optional.Some.type (%Optional.Some.type.b2c) = import_ref Core//prelude/iterate, inst140 [indirect], loaded [symbolic = @Optional.%Optional.Some (constants.%Optional.Some.d0d)] -// CHECK:STDOUT: %Core.import_ref.36a9: @Optional.as.Destroy.impl.%Optional.as.Destroy.impl.Op.type (%Optional.as.Destroy.impl.Op.type.764) = import_ref Core//prelude/iterate, inst6890 [indirect], loaded [symbolic = @Optional.as.Destroy.impl.%Optional.as.Destroy.impl.Op (constants.%Optional.as.Destroy.impl.Op.bf8)] +// CHECK:STDOUT: %Core.import_ref.f49: @Optional.%Optional.None.type (%Optional.None.type.ef2) = import_ref Core//prelude/iterate, inst140 [indirect], loaded [symbolic = @Optional.%Optional.None (constants.%Optional.None.fd6)] +// CHECK:STDOUT: %Core.import_ref.1a8: @Optional.%Optional.Some.type (%Optional.Some.type.b2c) = import_ref Core//prelude/iterate, inst141 [indirect], loaded [symbolic = @Optional.%Optional.Some (constants.%Optional.Some.d0d)] +// CHECK:STDOUT: %Core.import_ref.36a9: @Optional.as.Destroy.impl.%Optional.as.Destroy.impl.Op.type (%Optional.as.Destroy.impl.Op.type.764) = import_ref Core//prelude/iterate, inst6891 [indirect], loaded [symbolic = @Optional.as.Destroy.impl.%Optional.as.Destroy.impl.Op (constants.%Optional.as.Destroy.impl.Op.bf8)] // CHECK:STDOUT: %Destroy.impl_witness_table.2ff = impl_witness_table (%Core.import_ref.36a9), @Optional.as.Destroy.impl [concrete] -// CHECK:STDOUT: %Core.import_ref.cf4: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.0f9) = import_ref Core//prelude/iterate, inst483 [indirect], loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f06)] +// CHECK:STDOUT: %Core.import_ref.cf4: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.0f9) = import_ref Core//prelude/iterate, inst484 [indirect], loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f06)] // CHECK:STDOUT: %ImplicitAs.impl_witness_table.2b9 = impl_witness_table (%Core.import_ref.cf4), @Core.IntLiteral.as.ImplicitAs.impl [concrete] -// CHECK:STDOUT: %Core.import_ref.741: @Int.as.Destroy.impl.%Int.as.Destroy.impl.Op.type (%Int.as.Destroy.impl.Op.type) = import_ref Core//prelude/iterate, inst451 [indirect], loaded [symbolic = @Int.as.Destroy.impl.%Int.as.Destroy.impl.Op (constants.%Int.as.Destroy.impl.Op)] +// CHECK:STDOUT: %Core.import_ref.741: @Int.as.Destroy.impl.%Int.as.Destroy.impl.Op.type (%Int.as.Destroy.impl.Op.type) = import_ref Core//prelude/iterate, inst452 [indirect], loaded [symbolic = @Int.as.Destroy.impl.%Int.as.Destroy.impl.Op (constants.%Int.as.Destroy.impl.Op)] // CHECK:STDOUT: %Destroy.impl_witness_table.1b4 = impl_witness_table (%Core.import_ref.741), @Int.as.Destroy.impl [concrete] -// CHECK:STDOUT: %Core.import_ref.19a: @OrderedWith.%OrderedWith.assoc_type (%OrderedWith.assoc_type.03c) = import_ref Core//prelude/iterate, inst863 [indirect], loaded [symbolic = @OrderedWith.%assoc0 (constants.%assoc0.5db)] -// CHECK:STDOUT: %Core.import_ref.b2b: @Int.as.OrderedWith.impl.db3.%Int.as.OrderedWith.impl.Less.type (%Int.as.OrderedWith.impl.Less.type.2c7) = import_ref Core//prelude/iterate, inst952 [indirect], loaded [symbolic = @Int.as.OrderedWith.impl.db3.%Int.as.OrderedWith.impl.Less (constants.%Int.as.OrderedWith.impl.Less.a5a)] -// CHECK:STDOUT: %Core.import_ref.ab6 = import_ref Core//prelude/iterate, inst953 [indirect], unloaded -// CHECK:STDOUT: %Core.import_ref.875 = import_ref Core//prelude/iterate, inst954 [indirect], unloaded -// CHECK:STDOUT: %Core.import_ref.82b = import_ref Core//prelude/iterate, inst955 [indirect], unloaded +// CHECK:STDOUT: %Core.import_ref.19a: @OrderedWith.%OrderedWith.assoc_type (%OrderedWith.assoc_type.03c) = import_ref Core//prelude/iterate, inst864 [indirect], loaded [symbolic = @OrderedWith.%assoc0 (constants.%assoc0.5db)] +// CHECK:STDOUT: %Core.import_ref.b2b: @Int.as.OrderedWith.impl.db3.%Int.as.OrderedWith.impl.Less.type (%Int.as.OrderedWith.impl.Less.type.2c7) = import_ref Core//prelude/iterate, inst953 [indirect], loaded [symbolic = @Int.as.OrderedWith.impl.db3.%Int.as.OrderedWith.impl.Less (constants.%Int.as.OrderedWith.impl.Less.a5a)] +// CHECK:STDOUT: %Core.import_ref.ab6 = import_ref Core//prelude/iterate, inst954 [indirect], unloaded +// CHECK:STDOUT: %Core.import_ref.875 = import_ref Core//prelude/iterate, inst955 [indirect], unloaded +// CHECK:STDOUT: %Core.import_ref.82b = import_ref Core//prelude/iterate, inst956 [indirect], unloaded // CHECK:STDOUT: %OrderedWith.impl_witness_table.476 = impl_witness_table (%Core.import_ref.b2b, %Core.import_ref.ab6, %Core.import_ref.875, %Core.import_ref.82b), @Int.as.OrderedWith.impl.db3 [concrete] -// CHECK:STDOUT: %Core.import_ref.13d: @OrderedWith.%OrderedWith.Less.type (%OrderedWith.Less.type.f19) = import_ref Core//prelude/iterate, inst1927 [indirect], loaded [symbolic = @OrderedWith.%OrderedWith.Less (constants.%OrderedWith.Less.02e)] +// CHECK:STDOUT: %Core.import_ref.13d: @OrderedWith.%OrderedWith.Less.type (%OrderedWith.Less.type.f19) = import_ref Core//prelude/iterate, inst1928 [indirect], loaded [symbolic = @OrderedWith.%OrderedWith.Less (constants.%OrderedWith.Less.02e)] // CHECK:STDOUT: %CursorType: type = assoc_const_decl @CursorType [concrete] {} // CHECK:STDOUT: %Core.import_ref.4f9: type = import_ref Core//prelude/iterate, loc12_18, loaded [concrete = %ElementType] // CHECK:STDOUT: %ElementType: type = assoc_const_decl @ElementType [concrete] {} // CHECK:STDOUT: %Core.Optional: %Optional.type = import_ref Core//prelude/types/optional, Optional, loaded [concrete = constants.%Optional.generic] // CHECK:STDOUT: %Core.Destroy: type = import_ref Core//prelude/destroy, Destroy, loaded [concrete = constants.%Destroy.type] // CHECK:STDOUT: %Core.OrderedWith: %OrderedWith.type.270 = import_ref Core//prelude/operators/comparison, OrderedWith, loaded [concrete = constants.%OrderedWith.generic] -// CHECK:STDOUT: %Core.import_ref.d49 = import_ref Core//prelude/iterate, inst6645 [indirect], unloaded +// CHECK:STDOUT: %Core.import_ref.d49 = import_ref Core//prelude/iterate, inst6646 [indirect], unloaded // CHECK:STDOUT: %Core.Inc: type = import_ref Core//prelude/operators/arithmetic, Inc, loaded [concrete = constants.%Inc.type] // CHECK:STDOUT: %Core.ImplicitAs: %ImplicitAs.type.cc7 = import_ref Core//prelude/operators/as, ImplicitAs, loaded [concrete = constants.%ImplicitAs.generic] // CHECK:STDOUT: } diff --git a/toolchain/check/type_completion.cpp b/toolchain/check/type_completion.cpp index ddada02436c1..4968b214b55d 100644 --- a/toolchain/check/type_completion.cpp +++ b/toolchain/check/type_completion.cpp @@ -142,6 +142,10 @@ class TypeCompleter { SemIR::CustomLayoutType inst) const -> SemIR::CompleteTypeInfo; + auto BuildInfoForInst(SemIR::TypeId /*type_id*/, + SemIR::MaybeUnformedType inst) const + -> SemIR::CompleteTypeInfo; + auto BuildInfoForInst(SemIR::TypeId /*type_id*/, SemIR::PartialType inst) const -> SemIR::CompleteTypeInfo; @@ -314,6 +318,10 @@ auto TypeCompleter::AddNestedIncompleteTypes(SemIR::Inst type_inst) -> bool { } break; } + case CARBON_KIND(SemIR::MaybeUnformedType inst): { + Push(context_->types().GetTypeIdForTypeInstId(inst.inner_id)); + break; + } case CARBON_KIND(SemIR::PartialType inst): { Push(context_->types().GetTypeIdForTypeInstId(inst.inner_id)); break; @@ -539,6 +547,14 @@ auto TypeCompleter::BuildInfoForInst(SemIR::TypeId type_id, return {.value_repr = MakePointerValueRepr(type_id)}; } +auto TypeCompleter::BuildInfoForInst(SemIR::TypeId type_id, + SemIR::MaybeUnformedType /*inst*/) const + -> SemIR::CompleteTypeInfo { + // `MaybeUnformed(T)` always has a pointer value representation, regardless of + // `T`'s value representation. + return {.value_repr = MakePointerValueRepr(type_id)}; +} + auto TypeCompleter::BuildInfoForInst(SemIR::TypeId /*type_id*/, SemIR::PartialType inst) const -> SemIR::CompleteTypeInfo { diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index 9068ebdac9a9..921022c1cbce 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -763,8 +763,10 @@ static auto BuildTypeForInst(FileContext& context, SemIR::ClassType inst) return context.GetType(object_repr_id); } -static auto BuildTypeForInst(FileContext& context, SemIR::ConstType inst) - -> llvm::Type* { +template + requires(InstT::Kind.template IsAnyOf< + SemIR::ConstType, SemIR::MaybeUnformedType, SemIR::PartialType>()) +static auto BuildTypeForInst(FileContext& context, InstT inst) -> llvm::Type* { return context.GetType( context.sem_ir().types().GetTypeIdForTypeInstId(inst.inner_id)); } @@ -776,12 +778,6 @@ static auto BuildTypeForInst(FileContext& context, SemIR::CustomLayoutType inst) layout[SemIR::CustomLayoutId::SizeIndex]); } -static auto BuildTypeForInst(FileContext& context, SemIR::PartialType inst) - -> llvm::Type* { - return context.GetType( - context.sem_ir().types().GetTypeIdForTypeInstId(inst.inner_id)); -} - static auto BuildTypeForInst(FileContext& context, SemIR::ImplWitnessAssociatedConstant inst) -> llvm::Type* { diff --git a/toolchain/lower/handle_call.cpp b/toolchain/lower/handle_call.cpp index 34ac95a5735c..4edd85d30f0b 100644 --- a/toolchain/lower/handle_call.cpp +++ b/toolchain/lower/handle_call.cpp @@ -324,6 +324,7 @@ static auto HandleBuiltinCall(FunctionContext& context, SemIR::InstId inst_id, case SemIR::BuiltinFunctionKind::IntLiteralMakeType: case SemIR::BuiltinFunctionKind::IntMakeTypeSigned: case SemIR::BuiltinFunctionKind::IntMakeTypeUnsigned: + case SemIR::BuiltinFunctionKind::MaybeUnformedMakeType: context.SetLocal(inst_id, context.GetTypeAsValue()); return; diff --git a/toolchain/lower/testdata/builtins/maybe_unformed.carbon b/toolchain/lower/testdata/builtins/maybe_unformed.carbon new file mode 100644 index 000000000000..feae4ea6b37d --- /dev/null +++ b/toolchain/lower/testdata/builtins/maybe_unformed.carbon @@ -0,0 +1,100 @@ +// 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/primitives.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/builtins/maybe_unformed.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/builtins/maybe_unformed.carbon + +fn MaybeUnformedBuiltin(t: type) -> type = "maybe_unformed.make_type"; + +fn TakeBuiltin(x: MaybeUnformedBuiltin(i32)) {} +fn MakeBuiltin() -> MaybeUnformedBuiltin(i32); + +fn PassBuiltin() { + // This is passed by pointer even though `i32` would generally be passed in + // registers. + TakeBuiltin(MakeBuiltin()); +} + +// Also test that an adapter wrapped around the builtin behaves the same. + +class MaybeUnformedAdapter(T:! type) { + adapt MaybeUnformedBuiltin(T); +} + +fn TakeAdapter(x: MaybeUnformedAdapter(i32)) {} +fn MakeAdapter() -> MaybeUnformedAdapter(i32); + +fn PassAdapter() { + TakeAdapter(MakeAdapter()); +} + +// CHECK:STDOUT: ; ModuleID = 'maybe_unformed.carbon' +// CHECK:STDOUT: source_filename = "maybe_unformed.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: define void @_CTakeBuiltin.Main(ptr %x) !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !7 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CMakeBuiltin.Main(ptr sret(i32)) +// CHECK:STDOUT: +// CHECK:STDOUT: define void @_CPassBuiltin.Main() !dbg !8 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc21_27.1.temp = alloca i32, align 4, !dbg !9 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc21_27.1.temp), !dbg !9 +// CHECK:STDOUT: call void @_CMakeBuiltin.Main(ptr %.loc21_27.1.temp), !dbg !9 +// CHECK:STDOUT: call void @_CTakeBuiltin.Main(ptr %.loc21_27.1.temp), !dbg !10 +// CHECK:STDOUT: ret void, !dbg !11 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define void @_CTakeAdapter.Main(ptr %x) !dbg !12 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !13 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CMakeAdapter.Main(ptr sret(i32)) +// CHECK:STDOUT: +// CHECK:STDOUT: define void @_CPassAdapter.Main() !dbg !14 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc34_27.1.temp = alloca i32, align 4, !dbg !15 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc34_27.1.temp), !dbg !15 +// CHECK:STDOUT: call void @_CMakeAdapter.Main(ptr %.loc34_27.1.temp), !dbg !15 +// CHECK:STDOUT: call void @_CTakeAdapter.Main(ptr %.loc34_27.1.temp), !dbg !16 +// CHECK:STDOUT: ret void, !dbg !17 +// 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)) #0 +// CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "maybe_unformed.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "TakeBuiltin", linkageName: "_CTakeBuiltin.Main", scope: null, file: !3, line: 15, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{} +// CHECK:STDOUT: !7 = !DILocation(line: 15, column: 1, scope: !4) +// CHECK:STDOUT: !8 = distinct !DISubprogram(name: "PassBuiltin", linkageName: "_CPassBuiltin.Main", scope: null, file: !3, line: 18, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !9 = !DILocation(line: 21, column: 15, scope: !8) +// CHECK:STDOUT: !10 = !DILocation(line: 21, column: 3, scope: !8) +// CHECK:STDOUT: !11 = !DILocation(line: 18, column: 1, scope: !8) +// CHECK:STDOUT: !12 = distinct !DISubprogram(name: "TakeAdapter", linkageName: "_CTakeAdapter.Main", scope: null, file: !3, line: 30, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !13 = !DILocation(line: 30, column: 1, scope: !12) +// CHECK:STDOUT: !14 = distinct !DISubprogram(name: "PassAdapter", linkageName: "_CPassAdapter.Main", scope: null, file: !3, line: 33, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !15 = !DILocation(line: 34, column: 15, scope: !14) +// CHECK:STDOUT: !16 = !DILocation(line: 34, column: 3, scope: !14) +// CHECK:STDOUT: !17 = !DILocation(line: 33, column: 1, scope: !14) diff --git a/toolchain/sem_ir/builtin_function_kind.cpp b/toolchain/sem_ir/builtin_function_kind.cpp index 82efb2d03c76..efb89bf40ca3 100644 --- a/toolchain/sem_ir/builtin_function_kind.cpp +++ b/toolchain/sem_ir/builtin_function_kind.cpp @@ -307,6 +307,10 @@ constexpr BuiltinInfo FloatMakeType = {"float.make_type", constexpr BuiltinInfo BoolMakeType = {"bool.make_type", ValidateSignatureType>}; +// Returns the `MaybeUnformed(T)` type. +constexpr BuiltinInfo MaybeUnformedMakeType = { + "maybe_unformed.make_type", ValidateSignatureType>}; + // Converts between char types, with a diagnostic if the value doesn't fit. constexpr BuiltinInfo CharConvertChecked = { "char.convert_checked", diff --git a/toolchain/sem_ir/builtin_function_kind.def b/toolchain/sem_ir/builtin_function_kind.def index f29b3634d07d..a46d562f30ef 100644 --- a/toolchain/sem_ir/builtin_function_kind.def +++ b/toolchain/sem_ir/builtin_function_kind.def @@ -35,6 +35,7 @@ CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntMakeTypeSigned) CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(IntMakeTypeUnsigned) CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(FloatMakeType) CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(BoolMakeType) +CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(MaybeUnformedMakeType) // Character conversion. CARBON_SEM_IR_BUILTIN_FUNCTION_KIND(CharConvertChecked) diff --git a/toolchain/sem_ir/expr_info.cpp b/toolchain/sem_ir/expr_info.cpp index f0e6be07387e..3e513558b035 100644 --- a/toolchain/sem_ir/expr_info.cpp +++ b/toolchain/sem_ir/expr_info.cpp @@ -138,6 +138,7 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory { case IntType::Kind: case IntValue::Kind: case InterfaceDecl::Kind: + case MaybeUnformedType::Kind: case NamespaceType::Kind: case PartialType::Kind: case PatternType::Kind: diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index 5a3fc3fc4255..b1b44c453e0b 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -75,7 +75,6 @@ CARBON_SEM_IR_INST_KIND(FunctionTypeWithSelfType) CARBON_SEM_IR_INST_KIND(GenericClassType) CARBON_SEM_IR_INST_KIND(GenericInterfaceType) CARBON_SEM_IR_INST_KIND(ImplDecl) -CARBON_SEM_IR_INST_KIND(LookupImplWitness) CARBON_SEM_IR_INST_KIND(ImplWitness) CARBON_SEM_IR_INST_KIND(ImplWitnessAccess) CARBON_SEM_IR_INST_KIND(ImplWitnessAccessSubstituted) @@ -94,6 +93,8 @@ CARBON_SEM_IR_INST_KIND(IntLiteralType) CARBON_SEM_IR_INST_KIND(IntType) CARBON_SEM_IR_INST_KIND(IntValue) CARBON_SEM_IR_INST_KIND(InterfaceDecl) +CARBON_SEM_IR_INST_KIND(LookupImplWitness) +CARBON_SEM_IR_INST_KIND(MaybeUnformedType) CARBON_SEM_IR_INST_KIND(NameBindingDecl) CARBON_SEM_IR_INST_KIND(NameRef) CARBON_SEM_IR_INST_KIND(Namespace) diff --git a/toolchain/sem_ir/stringify.cpp b/toolchain/sem_ir/stringify.cpp index 3b4cdcc82465..85aa1313934f 100644 --- a/toolchain/sem_ir/stringify.cpp +++ b/toolchain/sem_ir/stringify.cpp @@ -521,6 +521,10 @@ class Stringifier { sem_ir_->specific_interfaces().Get(inst.query_specific_interface_id)); } + auto StringifyInst(InstId /*inst_id*/, MaybeUnformedType inst) -> void { + step_stack_->Push(""); + } + auto StringifyInst(InstId /*inst_id*/, NameRef inst) -> void { *out_ << sem_ir_->names().GetFormatted(inst.name_id); } diff --git a/toolchain/sem_ir/type_iterator.cpp b/toolchain/sem_ir/type_iterator.cpp index 8f8564669a18..6388af62a4bb 100644 --- a/toolchain/sem_ir/type_iterator.cpp +++ b/toolchain/sem_ir/type_iterator.cpp @@ -127,6 +127,12 @@ auto TypeIterator::Next() -> Step { PushInstId(const_type.inner_id); break; } + case CARBON_KIND(SemIR::MaybeUnformedType partial_type): { + // We don't stop at `MaybeUnformed` since it is a modifier; just move to + // the inner type. + PushInstId(partial_type.inner_id); + break; + } case CARBON_KIND(SemIR::PartialType partial_type): { // We don't stop at `partial` since it is a modifier; just move to the // inner type. @@ -171,6 +177,8 @@ auto TypeIterator::Next() -> Step { return Step::Error(); default: + // TODO: Rearrange this so that missing instruction kinds are detected + // at compile-time not runtime. CARBON_FATAL("Unhandled type instruction {0}", inst_id); } } diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 96dc076d0321..e98ededbceee 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -1162,6 +1162,21 @@ struct LookupImplWitness { SpecificInterfaceId query_specific_interface_id; }; +// A type that holds an object representation of another type, that may or may +// not be a valid representation. In particular, it may also hold an unformed +// state. +struct MaybeUnformedType { + static constexpr auto Kind = + InstKind::MaybeUnformedType.Define({ + .ir_name = "maybe_unformed_type", + .is_type = InstIsType::Always, + .constant_kind = InstConstantKind::WheneverPossible, + }); + + TypeId type_id; + TypeInstId inner_id; +}; + // A name-binding declaration, i.e. a declaration introduced with `let` or // `var`. struct NameBindingDecl {