From b39a0f0c8c6a3e821160f693d4f5df1ed9d60a3d Mon Sep 17 00:00:00 2001 From: David Blaikie Date: Thu, 26 Jun 2025 16:55:48 -0700 Subject: [PATCH] Basic SemIR `partial` support (#5736) This adds something similar to the level of `const` support - that it's a type, but not the conversions and limitations on usage that are needed. --------- Co-authored-by: Chandler Carruth --- toolchain/check/eval_inst.cpp | 5 + toolchain/check/handle_operator.cpp | 8 +- toolchain/check/testdata/class/partial.carbon | 344 ++++++++++++++++++ toolchain/check/type_completion.cpp | 16 + toolchain/lower/file_context.cpp | 6 + toolchain/sem_ir/expr_info.cpp | 1 + toolchain/sem_ir/inst_kind.def | 1 + toolchain/sem_ir/stringify.cpp | 6 + toolchain/sem_ir/type.cpp | 1 + toolchain/sem_ir/type_iterator.cpp | 6 + toolchain/sem_ir/typed_insts.h | 12 + 11 files changed, 405 insertions(+), 1 deletion(-) create mode 100644 toolchain/check/testdata/class/partial.carbon diff --git a/toolchain/check/eval_inst.cpp b/toolchain/check/eval_inst.cpp index c717d7b776e0..727b852a97ae 100644 --- a/toolchain/check/eval_inst.cpp +++ b/toolchain/check/eval_inst.cpp @@ -167,6 +167,11 @@ auto EvalConstantInst(Context& context, SemIR::ConstType inst) return ConstantEvalResult::NewSamePhase(inst); } +auto EvalConstantInst(Context& /*context*/, SemIR::PartialType inst) + -> ConstantEvalResult { + return ConstantEvalResult::NewSamePhase(inst); +} + auto EvalConstantInst(Context& context, SemIR::Converted inst) -> ConstantEvalResult { // A conversion evaluates to the result of the conversion. diff --git a/toolchain/check/handle_operator.cpp b/toolchain/check/handle_operator.cpp index 881a20d63512..86eb512dbdd4 100644 --- a/toolchain/check/handle_operator.cpp +++ b/toolchain/check/handle_operator.cpp @@ -312,7 +312,13 @@ auto HandleParseNode(Context& context, Parse::PrefixOperatorNotId node_id) auto HandleParseNode(Context& context, Parse::PrefixOperatorPartialId node_id) -> bool { - return context.TODO(node_id, "partial operator"); + auto value_id = context.node_stack().PopExpr(); + auto inner_type = ExprAsType(context, node_id, value_id); + // TODO: Add diagnostics for partial applied to non-base/abstract types. + AddInstAndPush( + context, node_id, + {.type_id = SemIR::TypeType::TypeId, .inner_id = inner_type.inst_id}); + return true; } auto HandleParseNode(Context& context, Parse::PrefixOperatorPlusPlusId node_id) diff --git a/toolchain/check/testdata/class/partial.carbon b/toolchain/check/testdata/class/partial.carbon new file mode 100644 index 000000000000..d10e207fea57 --- /dev/null +++ b/toolchain/check/testdata/class/partial.carbon @@ -0,0 +1,344 @@ +// 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/convert.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/partial.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/partial.carbon + +// --- base.carbon +library "[[@TEST_NAME]]"; + +base class C { } + +//@dump-sem-ir-begin +fn A(p: partial C); +//@dump-sem-ir-end + +// --- abstract.carbon +library "[[@TEST_NAME]]"; + +abstract class C { } + +//@dump-sem-ir-begin +fn A(p: partial C); +//@dump-sem-ir-end + +// --- todo_fail_partial_nondynamic.carbon +library "[[@TEST_NAME]]"; + +class C { } + +//@dump-sem-ir-begin +fn G(p: partial C); +//@dump-sem-ir-end + +// --- todo_fail_partial_final.carbon +library "[[@TEST_NAME]]"; + +base class Base { } +class Derived { + extend base: Base; +} + +//@dump-sem-ir-begin +fn G(p: partial Derived); +//@dump-sem-ir-end + +// --- todo_fail_partial_decl.carbon +library "[[@TEST_NAME]]"; + +class C; + +//@dump-sem-ir-begin +fn G(p: partial C); +//@dump-sem-ir-end + +// --- todo_fail_partial_tuple.carbon +library "[[@TEST_NAME]]"; + +class C; + +//@dump-sem-ir-begin +fn G(p: partial (C, C)); +//@dump-sem-ir-end + +// --- todo_fail_partial_struct.carbon +library "[[@TEST_NAME]]"; + +class C; + +//@dump-sem-ir-begin +fn G(p: partial {.x: C}); +//@dump-sem-ir-end + +// --- todo_fail_duplicate.carbon +library "[[@TEST_NAME]]"; + +base class C { } + +//@dump-sem-ir-begin +fn F(p: partial (partial C)); +//@dump-sem-ir-end + +// --- fail_convert_to_nonpartial.carbon +library "[[@TEST_NAME]]"; + +base class C { } + +fn G(p: partial C*) -> C* { + // CHECK:STDERR: fail_convert_to_nonpartial.carbon:[[@LINE+7]]:3: error: cannot implicitly convert expression of type `partial C*` to `C*` [ConversionFailure] + // CHECK:STDERR: return p; + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: fail_convert_to_nonpartial.carbon:[[@LINE+4]]:3: note: type `partial C*` does not implement interface `Core.ImplicitAs(C*)` [MissingImplInMemberAccessNote] + // CHECK:STDERR: return p; + // CHECK:STDERR: ^~~~~~~~~ + // CHECK:STDERR: + return p; +} + +// CHECK:STDOUT: --- base.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %.43f: type = partial_type %C [concrete] +// CHECK:STDOUT: %pattern_type: type = pattern_type %.43f [concrete] +// CHECK:STDOUT: %A.type: type = fn_type @A [concrete] +// CHECK:STDOUT: %A: %A.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] { +// CHECK:STDOUT: %p.patt: %pattern_type = binding_pattern p [concrete] +// CHECK:STDOUT: %p.param_patt: %pattern_type = value_param_pattern %p.patt, call_param0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %p.param: %.43f = value_param call_param0 +// CHECK:STDOUT: %.loc6_9.1: type = splice_block %.loc6_9.2 [concrete = constants.%.43f] { +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %.loc6_9.2: type = partial_type %C.ref [concrete = constants.%.43f] +// CHECK:STDOUT: } +// CHECK:STDOUT: %p: %.43f = bind_name p, %p.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @A(%p.param: %.43f); +// CHECK:STDOUT: +// CHECK:STDOUT: --- abstract.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %.43f: type = partial_type %C [concrete] +// CHECK:STDOUT: %pattern_type: type = pattern_type %.43f [concrete] +// CHECK:STDOUT: %A.type: type = fn_type @A [concrete] +// CHECK:STDOUT: %A: %A.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] { +// CHECK:STDOUT: %p.patt: %pattern_type = binding_pattern p [concrete] +// CHECK:STDOUT: %p.param_patt: %pattern_type = value_param_pattern %p.patt, call_param0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %p.param: %.43f = value_param call_param0 +// CHECK:STDOUT: %.loc6_9.1: type = splice_block %.loc6_9.2 [concrete = constants.%.43f] { +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %.loc6_9.2: type = partial_type %C.ref [concrete = constants.%.43f] +// CHECK:STDOUT: } +// CHECK:STDOUT: %p: %.43f = bind_name p, %p.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @A(%p.param: %.43f); +// CHECK:STDOUT: +// CHECK:STDOUT: --- todo_fail_partial_nondynamic.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %.43f: type = partial_type %C [concrete] +// CHECK:STDOUT: %pattern_type: type = pattern_type %.43f [concrete] +// CHECK:STDOUT: %G.type: type = fn_type @G [concrete] +// CHECK:STDOUT: %G: %G.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { +// CHECK:STDOUT: %p.patt: %pattern_type = binding_pattern p [concrete] +// CHECK:STDOUT: %p.param_patt: %pattern_type = value_param_pattern %p.patt, call_param0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %p.param: %.43f = value_param call_param0 +// CHECK:STDOUT: %.loc6_9.1: type = splice_block %.loc6_9.2 [concrete = constants.%.43f] { +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %.loc6_9.2: type = partial_type %C.ref [concrete = constants.%.43f] +// CHECK:STDOUT: } +// CHECK:STDOUT: %p: %.43f = bind_name p, %p.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G(%p.param: %.43f); +// CHECK:STDOUT: +// CHECK:STDOUT: --- todo_fail_partial_final.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %Derived: type = class_type @Derived [concrete] +// CHECK:STDOUT: %.604: type = partial_type %Derived [concrete] +// CHECK:STDOUT: %pattern_type: type = pattern_type %.604 [concrete] +// CHECK:STDOUT: %G.type: type = fn_type @G [concrete] +// CHECK:STDOUT: %G: %G.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { +// CHECK:STDOUT: %p.patt: %pattern_type = binding_pattern p [concrete] +// CHECK:STDOUT: %p.param_patt: %pattern_type = value_param_pattern %p.patt, call_param0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %p.param: %.604 = value_param call_param0 +// CHECK:STDOUT: %.loc9_9.1: type = splice_block %.loc9_9.2 [concrete = constants.%.604] { +// CHECK:STDOUT: %Derived.ref: type = name_ref Derived, file.%Derived.decl [concrete = constants.%Derived] +// CHECK:STDOUT: %.loc9_9.2: type = partial_type %Derived.ref [concrete = constants.%.604] +// CHECK:STDOUT: } +// CHECK:STDOUT: %p: %.604 = bind_name p, %p.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G(%p.param: %.604); +// CHECK:STDOUT: +// CHECK:STDOUT: --- todo_fail_partial_decl.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %.43f: type = partial_type %C [concrete] +// CHECK:STDOUT: %pattern_type: type = pattern_type %.43f [concrete] +// CHECK:STDOUT: %G.type: type = fn_type @G [concrete] +// CHECK:STDOUT: %G: %G.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { +// CHECK:STDOUT: %p.patt: %pattern_type = binding_pattern p [concrete] +// CHECK:STDOUT: %p.param_patt: %pattern_type = value_param_pattern %p.patt, call_param0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %p.param: %.43f = value_param call_param0 +// CHECK:STDOUT: %.loc6_9.1: type = splice_block %.loc6_9.2 [concrete = constants.%.43f] { +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %.loc6_9.2: type = partial_type %C.ref [concrete = constants.%.43f] +// CHECK:STDOUT: } +// CHECK:STDOUT: %p: %.43f = bind_name p, %p.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G(%p.param: %.43f); +// CHECK:STDOUT: +// CHECK:STDOUT: --- todo_fail_partial_tuple.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %tuple.type.24b: type = tuple_type (type, type) [concrete] +// CHECK:STDOUT: %tuple.type.56b: type = tuple_type (%C, %C) [concrete] +// CHECK:STDOUT: %.62c: type = partial_type %tuple.type.56b [concrete] +// CHECK:STDOUT: %pattern_type: type = pattern_type %.62c [concrete] +// CHECK:STDOUT: %G.type: type = fn_type @G [concrete] +// CHECK:STDOUT: %G: %G.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { +// CHECK:STDOUT: %p.patt: %pattern_type = binding_pattern p [concrete] +// CHECK:STDOUT: %p.param_patt: %pattern_type = value_param_pattern %p.patt, call_param0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %p.param: %.62c = value_param call_param0 +// CHECK:STDOUT: %.loc6_9.1: type = splice_block %.loc6_9.3 [concrete = constants.%.62c] { +// CHECK:STDOUT: %C.ref.loc6_18: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %C.ref.loc6_21: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %.loc6_22: %tuple.type.24b = tuple_literal (%C.ref.loc6_18, %C.ref.loc6_21) +// CHECK:STDOUT: %.loc6_9.2: type = converted %.loc6_22, constants.%tuple.type.56b [concrete = constants.%tuple.type.56b] +// CHECK:STDOUT: %.loc6_9.3: type = partial_type %.loc6_9.2 [concrete = constants.%.62c] +// CHECK:STDOUT: } +// CHECK:STDOUT: %p: %.62c = bind_name p, %p.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G(%p.param: %.62c); +// CHECK:STDOUT: +// CHECK:STDOUT: --- todo_fail_partial_struct.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %struct_type.x: type = struct_type {.x: %C} [concrete] +// CHECK:STDOUT: %.1ed: type = partial_type %struct_type.x [concrete] +// CHECK:STDOUT: %pattern_type: type = pattern_type %.1ed [concrete] +// CHECK:STDOUT: %G.type: type = fn_type @G [concrete] +// CHECK:STDOUT: %G: %G.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %G.decl: %G.type = fn_decl @G [concrete = constants.%G] { +// CHECK:STDOUT: %p.patt: %pattern_type = binding_pattern p [concrete] +// CHECK:STDOUT: %p.param_patt: %pattern_type = value_param_pattern %p.patt, call_param0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %p.param: %.1ed = value_param call_param0 +// CHECK:STDOUT: %.loc6_9.1: type = splice_block %.loc6_9.2 [concrete = constants.%.1ed] { +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %struct_type.x: type = struct_type {.x: %C} [concrete = constants.%struct_type.x] +// CHECK:STDOUT: %.loc6_9.2: type = partial_type %struct_type.x [concrete = constants.%.1ed] +// CHECK:STDOUT: } +// CHECK:STDOUT: %p: %.1ed = bind_name p, %p.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G(%p.param: %.1ed); +// CHECK:STDOUT: +// CHECK:STDOUT: --- todo_fail_duplicate.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %.43f: type = partial_type %C [concrete] +// CHECK:STDOUT: %.a34: type = partial_type %.43f [concrete] +// CHECK:STDOUT: %pattern_type: type = pattern_type %.a34 [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %p.patt: %pattern_type = binding_pattern p [concrete] +// CHECK:STDOUT: %p.param_patt: %pattern_type = value_param_pattern %p.patt, call_param0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %p.param: %.a34 = value_param call_param0 +// CHECK:STDOUT: %.loc6_9.1: type = splice_block %.loc6_9.2 [concrete = constants.%.a34] { +// CHECK:STDOUT: %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C] +// CHECK:STDOUT: %.loc6_18: type = partial_type %C.ref [concrete = constants.%.43f] +// CHECK:STDOUT: %.loc6_9.2: type = partial_type %.loc6_18 [concrete = constants.%.a34] +// CHECK:STDOUT: } +// CHECK:STDOUT: %p: %.a34 = bind_name p, %p.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(%p.param: %.a34); +// CHECK:STDOUT: diff --git a/toolchain/check/type_completion.cpp b/toolchain/check/type_completion.cpp index 0d136b53ce2f..95631e508942 100644 --- a/toolchain/check/type_completion.cpp +++ b/toolchain/check/type_completion.cpp @@ -140,6 +140,10 @@ class TypeCompleter { auto BuildInfoForInst(SemIR::TypeId /*type_id*/, SemIR::ConstType inst) const -> SemIR::CompleteTypeInfo; + auto BuildInfoForInst(SemIR::TypeId /*type_id*/, + SemIR::PartialType inst) const + -> SemIR::CompleteTypeInfo; + auto BuildInfoForInst(SemIR::TypeId /*type_id*/, SemIR::ImplWitnessAssociatedConstant inst) const -> SemIR::CompleteTypeInfo; @@ -290,6 +294,10 @@ auto TypeCompleter::AddNestedIncompleteTypes(SemIR::Inst type_inst) -> bool { Push(context_->types().GetTypeIdForTypeInstId(inst.inner_id)); break; } + case CARBON_KIND(SemIR::PartialType inst): { + Push(context_->types().GetTypeIdForTypeInstId(inst.inner_id)); + break; + } case CARBON_KIND(SemIR::FacetType inst): { auto identified_id = RequireIdentifiedFacetType(*context_, inst); const auto& identified = @@ -512,6 +520,14 @@ auto TypeCompleter::BuildInfoForInst(SemIR::TypeId /*type_id*/, return GetNestedInfo(context_->types().GetTypeIdForTypeInstId(inst.inner_id)); } +auto TypeCompleter::BuildInfoForInst(SemIR::TypeId /*type_id*/, + SemIR::PartialType inst) const + -> SemIR::CompleteTypeInfo { + // The value representation of `partial T` is the same as that of `T`. + // Objects are not modifiable through their value representations. + return GetNestedInfo(context_->types().GetTypeIdForTypeInstId(inst.inner_id)); +} + auto TypeCompleter::BuildInfoForInst( SemIR::TypeId /*type_id*/, SemIR::ImplWitnessAssociatedConstant inst) const -> SemIR::CompleteTypeInfo { diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index c657881699d7..52ed84297445 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -760,6 +760,12 @@ static auto BuildTypeForInst(FileContext& context, SemIR::ConstType inst) context.sem_ir().types().GetTypeIdForTypeInstId(inst.inner_id)); } +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/sem_ir/expr_info.cpp b/toolchain/sem_ir/expr_info.cpp index ee167eb7b638..7fb36b657518 100644 --- a/toolchain/sem_ir/expr_info.cpp +++ b/toolchain/sem_ir/expr_info.cpp @@ -134,6 +134,7 @@ auto GetExprCategory(const File& file, InstId inst_id) -> ExprCategory { case InterfaceDecl::Kind: case LegacyFloatType::Kind: case NamespaceType::Kind: + case PartialType::Kind: case PatternType::Kind: case PointerType::Kind: case RefineTypeAction::Kind: diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index 61e23ff27774..e33236f6c799 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -94,6 +94,7 @@ CARBON_SEM_IR_INST_KIND(Namespace) CARBON_SEM_IR_INST_KIND(NamespaceType) CARBON_SEM_IR_INST_KIND(OutParam) CARBON_SEM_IR_INST_KIND(OutParamPattern) +CARBON_SEM_IR_INST_KIND(PartialType) CARBON_SEM_IR_INST_KIND(PatternType) CARBON_SEM_IR_INST_KIND(PointerType) CARBON_SEM_IR_INST_KIND(RefParam) diff --git a/toolchain/sem_ir/stringify.cpp b/toolchain/sem_ir/stringify.cpp index 4b509fd41a12..22f17356ac13 100644 --- a/toolchain/sem_ir/stringify.cpp +++ b/toolchain/sem_ir/stringify.cpp @@ -514,6 +514,12 @@ class Stringifier { name_scope.name_id()); } + auto StringifyInst(InstId /*inst_id*/, PartialType inst) -> void { + *out_ << "partial "; + + step_stack_->PushInstId(inst.inner_id); + } + auto StringifyInst(InstId /*inst_id*/, PatternType inst) -> void { *out_ << "Push(inst.scrutinee_type_inst_id, ">"); diff --git a/toolchain/sem_ir/type.cpp b/toolchain/sem_ir/type.cpp index f430cb059ba7..2a57bdd569f5 100644 --- a/toolchain/sem_ir/type.cpp +++ b/toolchain/sem_ir/type.cpp @@ -77,6 +77,7 @@ auto TypeStore::GetUnqualifiedType(TypeId type_id) const -> TypeId { if (auto const_type = TryGetAs(type_id)) { return file_->types().GetTypeIdForTypeInstId(const_type->inner_id); } + // TODO: Look through PartialType when this is reachable/testable return type_id; } diff --git a/toolchain/sem_ir/type_iterator.cpp b/toolchain/sem_ir/type_iterator.cpp index 9ebe17721d6f..b2fd1d6153c8 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::PartialType partial_type): { + // We don't stop at `partial` since it is a modifier; just move to the + // inner type. + PushInstId(partial_type.inner_id); + break; + } case CARBON_KIND(SemIR::ImplWitnessAssociatedConstant assoc): { Push(assoc.type_id); break; diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index 4adba43ea228..297789a62092 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -512,6 +512,18 @@ struct ConstType { TypeInstId inner_id; }; +struct PartialType { + static constexpr auto Kind = + InstKind::PartialType.Define( + {.ir_name = "partial_type", + .is_type = InstIsType::Always, + .constant_kind = InstConstantKind::Conditional, + .deduce_through = true}); + + TypeId type_id; + TypeInstId inner_id; +}; + // Records that a type conversion `original as new_type` was done, producing the // result. struct Converted {