From bf9219d30ef03b541b26ed1cb15d92558503a369 Mon Sep 17 00:00:00 2001 From: Geoff Romer Date: Thu, 26 Feb 2026 15:01:24 -0800 Subject: [PATCH] Check support for form literals and `:?` bindings (#6747) Co-authored-by: Jon Ross-Perkins Co-authored-by: Carbon Infra Bot --- toolchain/check/context.h | 9 + toolchain/check/convert.cpp | 56 ++- toolchain/check/convert.h | 20 +- toolchain/check/cpp/import.cpp | 12 +- toolchain/check/eval_inst.cpp | 12 + toolchain/check/function.cpp | 2 +- toolchain/check/handle_binding_pattern.cpp | 101 ++++- toolchain/check/handle_form_literal.cpp | 53 ++- toolchain/check/handle_function.cpp | 2 +- toolchain/check/merge.cpp | 2 + toolchain/check/pattern.cpp | 29 +- toolchain/check/pattern.h | 1 + toolchain/check/pattern_match.cpp | 331 +++++++------- .../basics/raw_sem_ir/cpp_interop.carbon | 8 +- .../basics/raw_sem_ir/multifile.carbon | 2 +- .../multifile_with_textual_ir.carbon | 2 +- .../basics/raw_sem_ir/one_file.carbon | 128 +++--- .../one_file_with_textual_ir.carbon | 2 +- .../check/testdata/function/call/form.carbon | 419 ++++++++++++++++++ .../testdata/impl/fail_todo_form_thunk.carbon | 36 ++ toolchain/check/thunk.cpp | 16 +- toolchain/check/type.cpp | 5 + toolchain/check/type.h | 4 + toolchain/diagnostics/kind.def | 1 + toolchain/lower/handle.cpp | 5 + .../testdata/function/declaration.carbon | 22 + toolchain/sem_ir/entity_name.h | 9 +- toolchain/sem_ir/expr_info.cpp | 19 + toolchain/sem_ir/inst_categories.h | 32 +- toolchain/sem_ir/inst_kind.def | 5 + toolchain/sem_ir/stringify.cpp | 5 + toolchain/sem_ir/type_iterator.cpp | 1 + toolchain/sem_ir/typed_insts.h | 77 +++- 33 files changed, 1115 insertions(+), 313 deletions(-) create mode 100644 toolchain/check/testdata/function/call/form.carbon create mode 100644 toolchain/check/testdata/impl/fail_todo_form_thunk.carbon diff --git a/toolchain/check/context.h b/toolchain/check/context.h index b48ee2de5da1..24d6beb63271 100644 --- a/toolchain/check/context.h +++ b/toolchain/check/context.h @@ -256,7 +256,11 @@ class Context { } // Data about a form expression. + // + // TODO: consider moving this out of Context. struct FormExpr { + static const FormExpr Error; + // The inst ID of the form expression itself. This is always a form inst, // such as InitForm or RefForm. // TODO: Consider creating an AnyForm inst category to refer to those insts. @@ -532,6 +536,11 @@ class Context { CoreIdentifierCache core_identifiers_; }; +inline constexpr Context::FormExpr Context::FormExpr::Error = { + .form_inst_id = SemIR::ErrorInst::InstId, + .type_component_inst_id = SemIR::ErrorInst::TypeInstId, + .type_component_id = SemIR::ErrorInst::TypeId}; + } // namespace Carbon::Check #endif // CARBON_TOOLCHAIN_CHECK_CONTEXT_H_ diff --git a/toolchain/check/convert.cpp b/toolchain/check/convert.cpp index 3321693e7193..db672b6a04b9 100644 --- a/toolchain/check/convert.cpp +++ b/toolchain/check/convert.cpp @@ -819,6 +819,7 @@ static auto IsValidExprCategoryForConversionTarget( return category == SemIR::ExprCategory::DurableRef; case ConversionTarget::CppThunkRef: return category == SemIR::ExprCategory::EphemeralRef; + case ConversionTarget::NoOp: case ConversionTarget::ExplicitAs: case ConversionTarget::ExplicitUnsafeAs: return true; @@ -1721,6 +1722,11 @@ auto Convert(Context& context, SemIR::LocId loc_id, SemIR::InstId expr_id, return SemIR::ErrorInst::InstId; } + if (target.kind == ConversionTarget::NoOp) { + CARBON_CHECK(target.type_id == sem_ir.insts().Get(expr_id).type_id()); + return expr_id; + } + // Diagnose unnecessary `ref` tags early, so that they're not obscured by // conversions. if (starting_category == SemIR::ExprCategory::RefTagged && @@ -2006,15 +2012,15 @@ static auto DiagnoseTypeExprEvaluationFailure(Context& context, auto ExprAsType(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id, bool diagnose) -> TypeExpr { - auto type_inst_id = + auto type_as_inst_id = ConvertToValueOfType(context, loc_id, value_id, SemIR::TypeType::TypeId); - if (type_inst_id == SemIR::ErrorInst::TypeInstId) { + if (type_as_inst_id == SemIR::ErrorInst::InstId) { return {.inst_id = SemIR::ErrorInst::TypeInstId, .type_id = SemIR::ErrorInst::TypeId}; } - auto type_const_id = context.constant_values().Get(type_inst_id); - if (!type_const_id.is_constant()) { + auto type_as_const_id = context.constant_values().Get(type_as_inst_id); + if (!type_as_const_id.is_constant()) { if (diagnose) { DiagnoseTypeExprEvaluationFailure(context, loc_id); } @@ -2022,28 +2028,48 @@ auto ExprAsType(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id, .type_id = SemIR::ErrorInst::TypeId}; } - return {.inst_id = context.types().GetAsTypeInstId(type_inst_id), - .type_id = context.types().GetTypeIdForTypeConstantId(type_const_id)}; + return { + .inst_id = context.types().GetAsTypeInstId(type_as_inst_id), + .type_id = context.types().GetTypeIdForTypeConstantId(type_as_const_id)}; } -auto ExprAsReturnForm(Context& context, SemIR::LocId loc_id, +auto FormExprAsForm(Context& context, SemIR::LocId loc_id, + SemIR::InstId value_id) -> Context::FormExpr { + auto form_inst_id = + ConvertToValueOfType(context, loc_id, value_id, SemIR::FormType::TypeId); + if (form_inst_id == SemIR::ErrorInst::InstId) { + return Context::FormExpr::Error; + } + + auto form_const_id = context.constant_values().Get(form_inst_id); + if (!form_const_id.is_constant()) { + CARBON_DIAGNOSTIC(FormExprEvaluationFailure, Error, + "cannot evaluate form expression"); + context.emitter().Emit(loc_id, FormExprEvaluationFailure); + return Context::FormExpr::Error; + } + + auto type_id = GetTypeComponent(context, form_inst_id); + auto type_inst_id = context.types().GetTypeInstId(type_id); + return {.form_inst_id = form_inst_id, + .type_component_inst_id = type_inst_id, + .type_component_id = type_id}; +} + +auto ReturnExprAsForm(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id) -> Context::FormExpr { - constexpr Context::FormExpr ErrorFormExpr = { - .form_inst_id = SemIR::ErrorInst::InstId, - .type_component_inst_id = SemIR::ErrorInst::TypeInstId, - .type_component_id = SemIR::ErrorInst::TypeId}; auto form_inst_id = SemIR::InstId::None; auto type_inst_id = SemIR::InstId::None; if (auto ref_tag = context.insts().TryGetAs(value_id)) { type_inst_id = ConvertToValueOfType(context, loc_id, ref_tag->expr_id, SemIR::TypeType::TypeId); if (type_inst_id == SemIR::ErrorInst::InstId) { - return ErrorFormExpr; + return Context::FormExpr::Error; } if (!context.constant_values().Get(type_inst_id).is_constant()) { DiagnoseTypeExprEvaluationFailure(context, SemIR::LocId(ref_tag->expr_id)); - return ErrorFormExpr; + return Context::FormExpr::Error; } form_inst_id = AddInst( context, @@ -2056,11 +2082,11 @@ auto ExprAsReturnForm(Context& context, SemIR::LocId loc_id, type_inst_id = ConvertToValueOfType(context, loc_id, value_id, SemIR::TypeType::TypeId); if (type_inst_id == SemIR::ErrorInst::InstId) { - return ErrorFormExpr; + return Context::FormExpr::Error; } if (!context.constant_values().Get(type_inst_id).is_constant()) { DiagnoseTypeExprEvaluationFailure(context, loc_id); - return ErrorFormExpr; + return Context::FormExpr::Error; } form_inst_id = AddInst( context, diff --git a/toolchain/check/convert.h b/toolchain/check/convert.h index b73f29e55563..f5eba07a56cc 100644 --- a/toolchain/check/convert.h +++ b/toolchain/check/convert.h @@ -15,6 +15,9 @@ namespace Carbon::Check { // Description of the target of a conversion. struct ConversionTarget { enum Kind : int8_t { + // Perform no conversion. The source expression must already have type + // `type_id`. + NoOp, // Convert to a value of type `type_id`. Value, // Convert to either a value or a reference of type `type_id`. @@ -187,9 +190,20 @@ inline constexpr TypeExpr TypeExpr::None = {.inst_id = SemIR::TypeInstId::None, auto ExprAsType(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id, bool diagnose = true) -> TypeExpr; -// Converts an expression for use as a form. If the expression is a type -// expression, it is interpreted as an initializing form. -auto ExprAsReturnForm(Context& context, SemIR::LocId loc_id, +// Converts an expression in a form position for use as a form. +// +// Note that the right-hand side of a `->` return type declaration is not +// a form position for this purpose, because it uses a special syntax to specify +// forms. `ReturnExprAsForm` should be used instead in that case. +// +// `diagnose` has the same effect as in `ExprAsType`. +auto FormExprAsForm(Context& context, SemIR::LocId loc_id, + SemIR::InstId value_id) -> Context::FormExpr; + +// Evaluates an expression in the return-type position (following `->`, not +// `->?`) for use as a form, following the special-case language rules for +// evaluating an expression in that position. +auto ReturnExprAsForm(Context& context, SemIR::LocId loc_id, SemIR::InstId value_id) -> Context::FormExpr; // Handles an expression whose result value is unused. diff --git a/toolchain/check/cpp/import.cpp b/toolchain/check/cpp/import.cpp index 6d89156c5088..62f2428a9708 100644 --- a/toolchain/check/cpp/import.cpp +++ b/toolchain/check/cpp/import.cpp @@ -1252,9 +1252,7 @@ static auto GetReturnTypeExpr(Context& context, SemIR::LocId loc_id, if (!orig_type_inst_id.has_value()) { context.TODO(loc_id, llvm::formatv("Unsupported: return type: {0}", orig_ret_type.getAsString())); - return {.form_inst_id = SemIR::ErrorInst::InstId, - .type_component_inst_id = SemIR::ErrorInst::TypeInstId, - .type_component_id = SemIR::ErrorInst::TypeId}; + return Context::FormExpr::Error; } Context::FormExpr result = { .form_inst_id = is_reference ? make_ref_form(orig_type_inst_id) @@ -1708,10 +1706,10 @@ static auto ImportVarDecl(Context& context, SemIR::LocId loc_id, SemIR::NameId var_name_id = AddIdentifierName(context, var_decl->getName()); // Create an entity name to identify this variable. - SemIR::EntityNameId entity_name_id = - context.entity_names().AddSymbolicBindingName( - var_name_id, GetParentNameScopeId(context, var_decl), - SemIR::CompileTimeBindIndex::None, false, /*is_unused=*/false); + SemIR::EntityNameId entity_name_id = context.entity_names().Add( + {.name_id = var_name_id, + .parent_scope_id = GetParentNameScopeId(context, var_decl), + .is_unused = false}); // Create `RefBindingPattern` and `VarPattern`. Mirror the behavior of // import_ref and don't create a `NameBindingDecl` here; we'd never use it for diff --git a/toolchain/check/eval_inst.cpp b/toolchain/check/eval_inst.cpp index f0a1d989170b..ac98d58896d6 100644 --- a/toolchain/check/eval_inst.cpp +++ b/toolchain/check/eval_inst.cpp @@ -730,6 +730,18 @@ auto EvalConstantInst(Context& /*context*/, SemIR::TupleLiteral inst) .type_id = inst.type_id, .elements_id = inst.elements_id}); } +auto EvalConstantInst(Context& context, SemIR::TypeComponentOf inst) + -> ConstantEvalResult { + auto form_constant_inst_id = + context.constant_values().GetConstantInstId(inst.form_inst_id); + if (auto primitive_form = context.insts().TryGetAs( + form_constant_inst_id)) { + return ConstantEvalResult::Existing( + context.constant_values().Get(primitive_form->type_component_id)); + } + return ConstantEvalResult::NewSamePhase(inst); +} + auto EvalConstantInst(Context& context, SemIR::TypeLiteral inst) -> ConstantEvalResult { return ConstantEvalResult::Existing( diff --git a/toolchain/check/function.cpp b/toolchain/check/function.cpp index 8f2f9e1fbe44..0c5b03194c55 100644 --- a/toolchain/check/function.cpp +++ b/toolchain/check/function.cpp @@ -142,7 +142,7 @@ auto MakeBuiltinFunction(Context& context, SemIR::LocId loc_id, .type_component_inst_id = SemIR::TypeInstId::None, .type_component_id = SemIR::TypeId::None}; if (signature.return_type_id.has_value()) { - return_form = ExprAsReturnForm( + return_form = ReturnExprAsForm( context, loc_id, context.types().GetTypeInstId(signature.return_type_id)); return_patterns_id = AddReturnPatterns(context, loc_id, return_form); diff --git a/toolchain/check/handle_binding_pattern.cpp b/toolchain/check/handle_binding_pattern.cpp index a02e0ee037ad..f9f32803cbce 100644 --- a/toolchain/check/handle_binding_pattern.cpp +++ b/toolchain/check/handle_binding_pattern.cpp @@ -43,6 +43,8 @@ static auto GetPatternInstKind(Parse::NodeKind node_kind, bool is_ref) : SemIR::InstKind::ValueBindingPattern; case Parse::NodeKind::VarBindingPattern: return SemIR::InstKind::RefBindingPattern; + case Parse::NodeKind::FormBindingPattern: + return SemIR::InstKind::FormBindingPattern; default: CARBON_FATAL("Unexpected node kind: {0}", node_kind); } @@ -104,17 +106,56 @@ static auto IsValidParamForIntroducer(Context& context, Parse::NodeId node_id, } } +namespace { +// Information about the expression in the type position of a binding pattern, +// i.e. the position following the `:`/`:?`/`:!` separator. Note that this +// expression may be interpreted as a type or a form, depending on the binding +// kind. +struct BindingPatternTypeInfo { + // The parse node representing the expression. + Parse::AnyExprId node_id; + // The inst representing the converted value of that expression. For a `:?` + // binding the expression is converted to type `Core.Form`; otherwise it is + // converted to type `type`. + SemIR::InstId inst_id; + // For a `:?` binding this is the type component of the form denoted by + // `inst_id`. Otherwise this is the type denoted by `inst_id`. + SemIR::TypeId type_component_id; +}; +} // namespace + +// Handle the type position of a binding pattern. +static auto HandleAnyBindingPatternType(Context& context, + Parse::NodeKind node_kind) + -> BindingPatternTypeInfo { + auto [node_id, original_inst_id] = context.node_stack().PopExprWithNodeId(); + + if (node_kind == Parse::FormBindingPattern::Kind) { + auto as_form = FormExprAsForm(context, node_id, original_inst_id); + return {.node_id = node_id, + .inst_id = as_form.form_inst_id, + .type_component_id = as_form.type_component_id}; + } else { + auto as_type = ExprAsType(context, node_id, original_inst_id); + return {.node_id = node_id, + .inst_id = as_type.inst_id, + .type_component_id = as_type.type_id}; + } +} + // TODO: make this function shorter by factoring pieces out. static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id, Parse::NodeKind node_kind, bool is_unused = false) -> bool { - // TODO: split this into smaller, more focused functions. - auto [type_node, parsed_type_id] = context.node_stack().PopExprWithNodeId(); - auto [cast_type_inst_id, cast_type_id] = - ExprAsType(context, type_node, parsed_type_id); + auto type_expr = HandleAnyBindingPatternType(context, node_kind); + if (context.types() + .GetAsInst(type_expr.type_component_id) + .Is()) { + return context.TODO(node_id, "Support symbolic form bindings"); + } SemIR::ExprRegionId type_expr_region_id = - EndSubpatternAsExpr(context, cast_type_inst_id); + EndSubpatternAsExpr(context, type_expr.inst_id); // The name in a generic binding may be wrapped in `template`. bool is_generic = node_kind == Parse::NodeKind::CompileTimeBindingPattern; @@ -139,9 +180,10 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id, auto make_binding_pattern = [&]() -> SemIR::InstId { // TODO: Eventually the name will need to support associations with other // scopes, but right now we don't support qualified names here. - auto binding = AddBindingPattern(context, name_node, name_id, cast_type_id, - type_expr_region_id, pattern_inst_kind, - is_template, is_unused); + auto binding = AddBindingPattern( + context, name_node, name_id, type_expr.type_component_id, + context.constant_values().Get(type_expr.inst_id), type_expr_region_id, + pattern_inst_kind, is_template, is_unused); // TODO: If `is_generic`, then `binding.bind_id is a SymbolicBinding. Subst // the `.Self` of type `type` in the `cast_type_id` type (a `FacetType`) @@ -168,7 +210,8 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id, "binding pattern has abstract type {0} in `var` " "pattern", SemIR::TypeId); - builder.Context(type_node, AbstractTypeInVarPattern, cast_type_id); + builder.Context(type_expr.node_id, AbstractTypeInVarPattern, + type_expr.type_component_id); }; // A `self` binding can only appear in an implicit parameter list. @@ -209,7 +252,8 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id, // to fail since `Self` is an incomplete type. if (node_kind == Parse::NodeKind::VarBindingPattern) { auto [unqualified_type_id, qualifiers] = - context.types().GetUnqualifiedTypeAndQualifiers(cast_type_id); + context.types().GetUnqualifiedTypeAndQualifiers( + type_expr.type_component_id); if ((qualifiers & SemIR::TypeQualifiers::Partial) != SemIR::TypeQualifiers::Partial && context.types().Is(unqualified_type_id)) { @@ -222,7 +266,7 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id, abstract_diagnostic_context); DiagnoseAbstractClass(context, class_type.class_id, /*direct_use=*/true); - cast_type_id = SemIR::ErrorInst::TypeId; + type_expr.type_component_id = SemIR::ErrorInst::TypeId; } } } @@ -233,7 +277,8 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id, // unless it's nested inside a `var` pattern (because then the // enclosing `var` pattern is), or it's a compile-time binding pattern // (because then it's not passed to the `Call` inst). - if (node_kind == Parse::NodeKind::LetBindingPattern) { + if (node_kind == Parse::NodeKind::LetBindingPattern || + node_kind == Parse::NodeKind::FormBindingPattern) { auto type_id = context.insts().GetAttachedType(result_inst_id); if (is_ref) { result_inst_id = AddPatternInst( @@ -241,6 +286,12 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id, {.type_id = type_id, .subpattern_id = result_inst_id, .index = context.full_pattern_stack().NextCallParamIndex()}); + } else if (node_kind == Parse::NodeKind::FormBindingPattern) { + result_inst_id = AddPatternInst( + context, node_id, + {.type_id = type_id, + .subpattern_id = result_inst_id, + .index = context.full_pattern_stack().NextCallParamIndex()}); } else { result_inst_id = AddPatternInst( context, node_id, @@ -259,19 +310,20 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id, "binding pattern has incomplete type {0} in name " "binding declaration", InstIdAsType); - builder.Context(type_node, IncompleteTypeInBindingDecl, - cast_type_inst_id); + builder.Context(type_expr.node_id, IncompleteTypeInBindingDecl, + type_expr.inst_id); }; if (node_kind == Parse::NodeKind::VarBindingPattern) { - if (!RequireConcreteType(context, cast_type_id, type_node, - incomplete_diagnostic_context, - abstract_diagnostic_context)) { - cast_type_id = SemIR::ErrorInst::TypeId; + if (!RequireConcreteType( + context, type_expr.type_component_id, type_expr.node_id, + incomplete_diagnostic_context, abstract_diagnostic_context)) { + type_expr.type_component_id = SemIR::ErrorInst::TypeId; } } else { - if (!RequireCompleteType(context, cast_type_id, type_node, + if (!RequireCompleteType(context, type_expr.type_component_id, + type_expr.node_id, incomplete_diagnostic_context)) { - cast_type_id = SemIR::ErrorInst::TypeId; + type_expr.type_component_id = SemIR::ErrorInst::TypeId; } } @@ -286,9 +338,9 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id, .Lookup(binding_pattern_id) .value() .bind_name_id; - RegisterReturnedVar(context, - introducer.modifier_node_id(ModifierOrder::Decl), - type_node, cast_type_id, bind_id, name_id); + RegisterReturnedVar( + context, introducer.modifier_node_id(ModifierOrder::Decl), + type_expr.node_id, type_expr.type_component_id, bind_id, name_id); } } context.node_stack().Push(node_id, binding_pattern_id); @@ -312,7 +364,8 @@ auto HandleParseNode(Context& context, Parse::VarBindingPatternId node_id) auto HandleParseNode(Context& context, Parse::FormBindingPatternId node_id) -> bool { - return context.TODO(node_id, "Implement :? support"); + return HandleAnyBindingPattern(context, node_id, + Parse::NodeKind::FormBindingPattern); } auto HandleParseNode(Context& context, diff --git a/toolchain/check/handle_form_literal.cpp b/toolchain/check/handle_form_literal.cpp index 2811db2e41b0..227a6186fad9 100644 --- a/toolchain/check/handle_form_literal.cpp +++ b/toolchain/check/handle_form_literal.cpp @@ -3,38 +3,63 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" +#include "toolchain/check/convert.h" #include "toolchain/check/handle.h" +#include "toolchain/check/inst.h" #include "toolchain/parse/node_category.h" namespace Carbon::Check { auto HandleParseNode(Context& context, Parse::RefPrimitiveFormId node_id) -> bool { - return context.TODO(node_id, "Implement form literals"); + auto [type_node_id, type_inst_id] = context.node_stack().PopExprWithNodeId(); + auto type_expr = ExprAsType(context, type_node_id, type_inst_id); + auto inst_id = + AddInst(context, node_id, + {.type_id = SemIR::FormType::TypeId, + .type_component_inst_id = type_expr.inst_id}); + context.node_stack().Push(node_id, inst_id); + return true; } auto HandleParseNode(Context& context, Parse::ValPrimitiveFormId node_id) -> bool { - return context.TODO(node_id, "Implement form literals"); + auto [type_node_id, type_inst_id] = context.node_stack().PopExprWithNodeId(); + auto type_expr = ExprAsType(context, type_node_id, type_inst_id); + auto inst_id = + AddInst(context, node_id, + {.type_id = SemIR::FormType::TypeId, + .type_component_inst_id = type_expr.inst_id}); + context.node_stack().Push(node_id, inst_id); + return true; } auto HandleParseNode(Context& context, Parse::VarPrimitiveFormId node_id) -> bool { - return context.TODO(node_id, "Implement form literals"); + auto [type_node_id, type_inst_id] = context.node_stack().PopExprWithNodeId(); + auto type_expr = ExprAsType(context, type_node_id, type_inst_id); + auto inst_id = + AddInst(context, node_id, + {.type_id = SemIR::FormType::TypeId, + .type_component_inst_id = type_expr.inst_id, + .index = SemIR::CallParamIndex::None}); + context.node_stack().Push(node_id, inst_id); + return true; } -auto HandleParseNode(Context& context, Parse::FormLiteralKeywordId node_id) +auto HandleParseNode(Context& /*context*/, + Parse::FormLiteralKeywordId /*node_id*/) -> bool { + return true; +} + +auto HandleParseNode(Context& /*context*/, + Parse::FormLiteralOpenParenId /*node_id*/) -> bool { + return true; +} + +auto HandleParseNode(Context& /*context*/, Parse::FormLiteralId /*node_id*/) -> bool { - return context.TODO(node_id, "Implement form literals"); -} - -auto HandleParseNode(Context& context, Parse::FormLiteralOpenParenId node_id) - -> bool { - return context.TODO(node_id, "Implement form literals"); -} - -auto HandleParseNode(Context& context, Parse::FormLiteralId node_id) -> bool { - return context.TODO(node_id, "Implement form literals"); + return true; } } // namespace Carbon::Check diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index 143bf5e93801..f10ca31ffcc5 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -53,7 +53,7 @@ auto HandleParseNode(Context& context, Parse::ReturnTypeId node_id) -> bool { auto [type_node_id, type_inst_id] = context.node_stack().PopExprWithNodeId(); // Propagate the type expression. - auto form_expr = ExprAsReturnForm(context, type_node_id, type_inst_id); + auto form_expr = ReturnExprAsForm(context, type_node_id, type_inst_id); context.PushReturnForm(form_expr); auto return_patterns_id = AddReturnPatterns(context, node_id, form_expr); context.node_stack().Push(node_id, return_patterns_id); diff --git a/toolchain/check/merge.cpp b/toolchain/check/merge.cpp index 46b992ea38f3..f888f187fe22 100644 --- a/toolchain/check/merge.cpp +++ b/toolchain/check/merge.cpp @@ -249,6 +249,7 @@ static auto CheckRedeclParam(Context& context, bool is_implicit_param, } switch (new_param_pattern.kind()) { + case SemIR::FormParamPattern::Kind: case SemIR::OutParamPattern::Kind: case SemIR::RefParamPattern::Kind: case SemIR::ValueParamPattern::Kind: @@ -267,6 +268,7 @@ static auto CheckRedeclParam(Context& context, bool is_implicit_param, .new_id = new_param_pattern.As().subpattern_id}); break; + case SemIR::FormBindingPattern::Kind: case SemIR::RefBindingPattern::Kind: case SemIR::SymbolicBindingPattern::Kind: case SemIR::ValueBindingPattern::Kind: { diff --git a/toolchain/check/pattern.cpp b/toolchain/check/pattern.cpp index 2338571f476d..68486057f9eb 100644 --- a/toolchain/check/pattern.cpp +++ b/toolchain/check/pattern.cpp @@ -48,11 +48,15 @@ auto EndSubpatternAsNonExpr(Context& context) -> void { auto AddBindingPattern(Context& context, SemIR::LocId name_loc, SemIR::NameId name_id, SemIR::TypeId type_id, + SemIR::ConstantId form_id, SemIR::ExprRegionId type_region_id, SemIR::InstKind pattern_kind, bool is_template, bool is_unused) -> BindingPatternInfo { SemIR::InstKind bind_name_kind; switch (pattern_kind) { + case SemIR::InstKind::FormBindingPattern: + bind_name_kind = SemIR::InstKind::FormBinding; + break; case SemIR::InstKind::RefBindingPattern: bind_name_kind = SemIR::InstKind::RefBinding; break; @@ -63,15 +67,23 @@ auto AddBindingPattern(Context& context, SemIR::LocId name_loc, bind_name_kind = SemIR::InstKind::ValueBinding; break; default: - CARBON_FATAL("pattern_kind is not a binding pattern kind"); + CARBON_FATAL("pattern_kind {0} is not a binding pattern kind", + pattern_kind); } bool is_generic = pattern_kind == SemIR::SymbolicBindingPattern::Kind; - auto entity_name_id = context.entity_names().AddSymbolicBindingName( - name_id, context.scope_stack().PeekNameScopeId(), - is_generic ? context.scope_stack().AddCompileTimeBinding() - : SemIR::CompileTimeBindIndex::None, - is_template, is_unused || name_id == SemIR::NameId::Underscore); + SemIR::EntityName entity_name = { + .name_id = name_id, + .parent_scope_id = context.scope_stack().PeekNameScopeId(), + .is_unused = is_unused || name_id == SemIR::NameId::Underscore}; + if (is_generic) { + entity_name.bind_index_value = + context.scope_stack().AddCompileTimeBinding().index; + entity_name.is_template = is_template; + } else if (pattern_kind == SemIR::InstKind::FormBindingPattern) { + entity_name.form_id = form_id; + } + auto entity_name_id = context.entity_names().Add(entity_name); auto bind_id = AddInstInNoBlock( context, @@ -145,8 +157,9 @@ auto AddParamPattern(Context& context, SemIR::LocId loc_id, const auto& binding_pattern_kind = is_ref ? SemIR::RefBindingPattern::Kind : SemIR::ValueBindingPattern::Kind; SemIR::InstId pattern_id = - AddBindingPattern(context, loc_id, name_id, type_id, type_expr_region_id, - binding_pattern_kind, + AddBindingPattern(context, loc_id, name_id, type_id, + /*form_id=*/SemIR::ConstantId::None, + type_expr_region_id, binding_pattern_kind, /*is_template=*/false, /*is_unused=*/false) .pattern_id; diff --git a/toolchain/check/pattern.h b/toolchain/check/pattern.h index 6571e392c8ac..bf452ee202b6 100644 --- a/toolchain/check/pattern.h +++ b/toolchain/check/pattern.h @@ -44,6 +44,7 @@ struct BindingPatternInfo { // bindings. auto AddBindingPattern(Context& context, SemIR::LocId name_loc, SemIR::NameId name_id, SemIR::TypeId type_id, + SemIR::ConstantId form_id, SemIR::ExprRegionId type_region_id, SemIR::InstKind pattern_kind, bool is_template, bool is_unused) -> BindingPatternInfo; diff --git a/toolchain/check/pattern_match.cpp b/toolchain/check/pattern_match.cpp index 513dcc24fe74..0c5d1cde337f 100644 --- a/toolchain/check/pattern_match.cpp +++ b/toolchain/check/pattern_match.cpp @@ -97,21 +97,11 @@ class MatchContext { auto EmitPatternMatch(Context& context, MatchContext::WorkItem entry) -> void; // Implementations of `EmitPatternMatch` for particular pattern inst kinds. - // The pattern argument is always equal to - // `context.insts().Get(entry.pattern_id)`. auto DoEmitPatternMatch(Context& context, SemIR::AnyBindingPattern binding_pattern, WorkItem entry) -> void; auto DoEmitPatternMatch(Context& context, - SemIR::ValueParamPattern param_pattern, - WorkItem entry) -> void; - template - requires std::is_same_v || - std::is_same_v - auto DoEmitPatternMatch(Context& context, RefParamPatternT param_pattern, - WorkItem entry) -> void; - auto DoEmitPatternMatch(Context& context, - SemIR::OutParamPattern param_pattern, WorkItem entry) + SemIR::AnyParamPattern param_pattern, WorkItem entry) -> void; auto DoEmitPatternMatch(Context& context, SemIR::ReturnSlotPattern return_slot_pattern, @@ -121,6 +111,15 @@ class MatchContext { auto DoEmitPatternMatch(Context& context, SemIR::TuplePattern tuple_pattern, WorkItem entry) -> void; + // Performs the core logic of matching a variable pattern whose type is + // `pattern_type_id`, but returns the scrutinee that its subpattern should be + // matched with, rather than pushing it onto the worklist. This is factored + // out so it can be reused when handling a `FormBindingPattern` or + // `FormParamPattern` with an initializing form. + auto DoEmitVarPatternMatchImpl(Context& context, + SemIR::TypeId pattern_type_id, + WorkItem entry) const -> SemIR::InstId; + // The stack of work to be processed. llvm::SmallVector stack_; @@ -241,6 +240,75 @@ static auto InsertHere(Context& context, SemIR::ExprRegionId region_id) return region.result_id; } +// Returns the kind of conversion to perform on the scrutinee when matching the +// given pattern. `form_kind` is the form of the pattern, if known; it only +// affects the behavior of `FormBindingPattern` and `FormParamPattern`, +// and it must be set in the `FormParamPattern` case. +static auto ConversionKindFor( + Context& context, SemIR::Inst pattern, MatchContext::WorkItem entry, + std::optional form_kind = std::nullopt) + -> ConversionTarget::Kind { + CARBON_KIND_SWITCH(pattern) { + case SemIR::OutParamPattern::Kind: + case SemIR::VarParamPattern::Kind: + return ConversionTarget::NoOp; + case SemIR::RefBindingPattern::Kind: + return ConversionTarget::DurableRef; + case SemIR::RefParamPattern::Kind: + return entry.allow_unmarked_ref ? ConversionTarget::UnmarkedRefParam + : ConversionTarget::RefParam; + case SemIR::SymbolicBindingPattern::Kind: + case SemIR::ValueBindingPattern::Kind: + case SemIR::ValueParamPattern::Kind: + return ConversionTarget::Value; + case CARBON_KIND(SemIR::FormBindingPattern form_binding_pattern): { + if (!form_kind) { + auto form_id = context.entity_names() + .Get(form_binding_pattern.entity_name_id) + .form_id; + auto form_inst_id = context.constant_values().GetInstId(form_id); + form_kind = context.insts().Get(form_inst_id).kind(); + } + + switch (*form_kind) { + case SemIR::InitForm::Kind: + context.TODO(entry.pattern_id, "Support local initializing forms"); + [[fallthrough]]; + case SemIR::RefForm::Kind: + return ConversionTarget::DurableRef; + case SemIR::SymbolicBinding::Kind: + context.TODO(entry.pattern_id, "Support symbolic form bindings"); + [[fallthrough]]; + case SemIR::ValueForm::Kind: + return ConversionTarget::Value; + default: + CARBON_FATAL("Unexpected form kind {0}", form_kind); + } + } + case SemIR::FormParamPattern::Kind: { + CARBON_CHECK(form_kind); + switch (*form_kind) { + case SemIR::InitForm::Kind: + return ConversionTarget::NoOp; + case SemIR::RefForm::Kind: + // TODO: Figure out rules for when the argument must have a `ref` tag. + return entry.allow_unmarked_ref ? ConversionTarget::UnmarkedRefParam + : ConversionTarget::RefParam; + case SemIR::SymbolicBinding::Kind: + context.TODO(entry.pattern_id, "Support symbolic form params"); + [[fallthrough]]; + case SemIR::ErrorInst::Kind: + case SemIR::ValueForm::Kind: + return ConversionTarget::Value; + default: + CARBON_FATAL("Unexpected form kind {0}", form_kind); + } + } + default: + CARBON_FATAL("Unexpected pattern kind in {0}", pattern); + } +} + auto MatchContext::DoEmitPatternMatch(Context& context, SemIR::AnyBindingPattern binding_pattern, MatchContext::WorkItem entry) -> void { @@ -262,17 +330,7 @@ auto MatchContext::DoEmitPatternMatch(Context& context, InsertHere(context, type_expr_region_id); auto value_id = SemIR::InstId::None; if (kind_ == MatchKind::Local) { - auto conversion_kind = [&binding_pattern]() -> ConversionTarget::Kind { - switch (binding_pattern.kind) { - case SemIR::SymbolicBindingPattern::Kind: - case SemIR::ValueBindingPattern::Kind: - return ConversionTarget::Value; - case SemIR::RefBindingPattern::Kind: - return ConversionTarget::DurableRef; - default: - CARBON_FATAL("Unexpected inst kind {0}", binding_pattern.kind); - } - }(); + auto conversion_kind = ConversionKindFor(context, binding_pattern, entry); if (!bind_name_id.has_value()) { // TODO: Is this appropriate, or should we perform a conversion based on @@ -298,9 +356,67 @@ auto MatchContext::DoEmitPatternMatch(Context& context, } } +// Returns the inst kind to use for the parameter corresponding to the given +// parameter pattern. If the pattern is a `FormParamPattern`, `form_kind` +// must be the pattern's form; otherwise it is ignored. +static auto ParamKindFor( + Context& context, SemIR::Inst param_pattern, MatchContext::WorkItem entry, + std::optional form_kind = std::nullopt) + -> SemIR::InstKind { + switch (param_pattern.kind()) { + case SemIR::OutParamPattern::Kind: + return SemIR::OutParam::Kind; + case SemIR::RefParamPattern::Kind: + case SemIR::VarParamPattern::Kind: + return SemIR::RefParam::Kind; + case SemIR::ValueParamPattern::Kind: + return SemIR::ValueParam::Kind; + case SemIR::FormParamPattern::Kind: + CARBON_CHECK(form_kind); + switch (*form_kind) { + case SemIR::InitForm::Kind: + case SemIR::RefForm::Kind: + return SemIR::RefParam::Kind; + case SemIR::SymbolicBinding::Kind: + context.TODO(entry.pattern_id, "Support symbolic form params"); + [[fallthrough]]; + case SemIR::ErrorInst::Kind: + case SemIR::ValueForm::Kind: + return SemIR::ValueParam::Kind; + default: + CARBON_FATAL("Unexpected form kind {0}", form_kind); + } + default: + CARBON_FATAL("Unexpected param pattern kind: {0}", param_pattern); + } +} + auto MatchContext::DoEmitPatternMatch(Context& context, - SemIR::ValueParamPattern param_pattern, + SemIR::AnyParamPattern param_pattern, WorkItem entry) -> void { + // If this is a FormParamPattern, determine its form. + std::optional form_kind; + if (param_pattern.kind == SemIR::FormParamPattern::Kind) { + if (param_pattern.subpattern_id == SemIR::ErrorInst::InstId) { + form_kind = SemIR::ErrorInst::Kind; + } else { + auto binding_pattern = context.insts().GetAs( + param_pattern.subpattern_id); + auto form_id = + context.entity_names().Get(binding_pattern.entity_name_id).form_id; + auto form_inst_id = context.constant_values().GetInstId(form_id); + form_kind = context.insts().Get(form_inst_id).kind(); + + // If the form is initializing, match this as a `VarPattern` before + // matching it as a parameter pattern. + if (form_kind == SemIR::InitForm::Kind) { + auto new_scrutinee_id = + DoEmitVarPatternMatchImpl(context, param_pattern.type_id, entry); + entry.scrutinee_id = new_scrutinee_id; + } + } + } + switch (kind_) { case MatchKind::Caller: { CARBON_CHECK( @@ -311,25 +427,31 @@ auto MatchContext::DoEmitPatternMatch(Context& context, if (entry.scrutinee_id == SemIR::ErrorInst::InstId) { call_args_.push_back(SemIR::ErrorInst::InstId); } else { - call_args_.push_back(ConvertToValueOfType( + auto scrutinee_type_id = ExtractScrutineeType( + context.sem_ir(), + SemIR::GetTypeOfInstInSpecific( + context.sem_ir(), callee_specific_id_, entry.pattern_id)); + call_args_.push_back(Convert( context, SemIR::LocId(entry.scrutinee_id), entry.scrutinee_id, - ExtractScrutineeType( - context.sem_ir(), - SemIR::GetTypeOfInstInSpecific( - context.sem_ir(), callee_specific_id_, entry.pattern_id)))); + {.kind = + ConversionKindFor(context, param_pattern, entry, form_kind), + .type_id = scrutinee_type_id})); } // Do not traverse farther, because the caller side of the pattern // ends here. break; } case MatchKind::Callee: { - auto param_id = AddInst( - context, SemIR::LocId(entry.pattern_id), - {.type_id = - ExtractScrutineeType(context.sem_ir(), param_pattern.type_id), - .index = param_pattern.index, - .pretty_name_id = SemIR::GetPrettyNameFromPatternId( - context.sem_ir(), entry.pattern_id)}); + SemIR::AnyParam param = { + .kind = ParamKindFor(context, param_pattern, entry, form_kind), + .type_id = + ExtractScrutineeType(context.sem_ir(), param_pattern.type_id), + .index = param_pattern.index, + .pretty_name_id = SemIR::GetPrettyNameFromPatternId( + context.sem_ir(), entry.pattern_id)}; + auto param_id = + AddInst(context, SemIR::LocIdAndInst::UncheckedLoc( + SemIR::LocId(entry.pattern_id), param)); AddWork({.pattern_id = param_pattern.subpattern_id, .scrutinee_id = param_id}); call_params_.push_back(param_id); @@ -342,100 +464,6 @@ auto MatchContext::DoEmitPatternMatch(Context& context, } } -template - requires std::is_same_v || - std::is_same_v -auto MatchContext::DoEmitPatternMatch(Context& context, - RefParamPatternT param_pattern, - WorkItem entry) -> void { - switch (kind_) { - case MatchKind::Caller: { - CARBON_CHECK( - static_cast(param_pattern.index.index) == call_args_.size(), - "Parameters out of order; expecting {0} but got {1}", - call_args_.size(), param_pattern.index.index); - CARBON_CHECK(entry.scrutinee_id.has_value()); - - if (std::is_same_v) { - call_args_.push_back(entry.scrutinee_id); - break; - } - auto scrutinee_type_id = ExtractScrutineeType( - context.sem_ir(), - SemIR::GetTypeOfInstInSpecific(context.sem_ir(), callee_specific_id_, - entry.pattern_id)); - call_args_.push_back(Convert( - context, SemIR::LocId(entry.scrutinee_id), entry.scrutinee_id, - {.kind = entry.allow_unmarked_ref ? ConversionTarget::UnmarkedRefParam - : ConversionTarget::RefParam, - .type_id = scrutinee_type_id})); - // Do not traverse farther, because the caller side of the pattern - // ends here. - break; - } - case MatchKind::Callee: { - auto param_id = AddInst( - context, SemIR::LocId(entry.pattern_id), - {.type_id = - ExtractScrutineeType(context.sem_ir(), param_pattern.type_id), - .index = param_pattern.index, - .pretty_name_id = SemIR::GetPrettyNameFromPatternId( - context.sem_ir(), entry.pattern_id)}); - AddWork({.pattern_id = param_pattern.subpattern_id, - .scrutinee_id = param_id}); - call_params_.push_back(param_id); - call_param_patterns_.push_back(entry.pattern_id); - break; - } - case MatchKind::Local: { - CARBON_FATAL("Found RefParamPattern during local pattern match"); - } - } -} - -auto MatchContext::DoEmitPatternMatch(Context& context, - SemIR::OutParamPattern param_pattern, - WorkItem entry) -> void { - switch (kind_) { - case MatchKind::Caller: { - CARBON_CHECK( - static_cast(param_pattern.index.index) == call_args_.size(), - "Parameters out of order; expecting {0} but got {1}", - call_args_.size(), param_pattern.index.index); - CARBON_CHECK(entry.scrutinee_id.has_value()); - CARBON_CHECK( - context.insts().Get(entry.scrutinee_id).type_id() == - ExtractScrutineeType( - context.sem_ir(), - SemIR::GetTypeOfInstInSpecific( - context.sem_ir(), callee_specific_id_, entry.pattern_id))); - call_args_.push_back(entry.scrutinee_id); - // Do not traverse farther, because the caller side of the pattern - // ends here. - break; - } - case MatchKind::Callee: { - // TODO: Consider ways to address near-duplication with the - // other ParamPattern cases. - auto param_id = AddInst( - context, SemIR::LocId(entry.pattern_id), - {.type_id = - ExtractScrutineeType(context.sem_ir(), param_pattern.type_id), - .index = param_pattern.index, - .pretty_name_id = SemIR::GetPrettyNameFromPatternId( - context.sem_ir(), entry.pattern_id)}); - AddWork({.pattern_id = param_pattern.subpattern_id, - .scrutinee_id = param_id}); - call_param_patterns_.push_back(entry.pattern_id); - call_params_.push_back(param_id); - break; - } - case MatchKind::Local: { - CARBON_FATAL("Found OutParamPattern during local pattern match"); - } - } -} - auto MatchContext::DoEmitPatternMatch( Context& context, SemIR::ReturnSlotPattern return_slot_pattern, WorkItem entry) -> void { @@ -457,15 +485,23 @@ auto MatchContext::DoEmitPatternMatch( auto MatchContext::DoEmitPatternMatch(Context& context, SemIR::VarPattern var_pattern, WorkItem entry) -> void { + auto new_scrutinee_id = + DoEmitVarPatternMatchImpl(context, var_pattern.type_id, entry); + AddWork({.pattern_id = var_pattern.subpattern_id, + .scrutinee_id = new_scrutinee_id}); +} + +auto MatchContext::DoEmitVarPatternMatchImpl(Context& context, + SemIR::TypeId pattern_type_id, + WorkItem entry) const + -> SemIR::InstId { auto storage_id = SemIR::InstId::None; switch (kind_) { case MatchKind::Callee: { // We're emitting pattern-match IR for the callee, but we're still on // the caller side of the pattern, so we traverse without emitting any // insts. - AddWork({.pattern_id = var_pattern.subpattern_id, - .scrutinee_id = SemIR::InstId::None}); - return; + return SemIR::InstId::None; } case MatchKind::Local: { // In a `var`/`let` declaration, the `VarStorage` inst is created before @@ -478,8 +514,7 @@ auto MatchContext::DoEmitPatternMatch(Context& context, case MatchKind::Caller: { storage_id = AddInst( context, SemIR::LocId(entry.pattern_id), - {.type_id = - ExtractScrutineeType(context.sem_ir(), var_pattern.type_id)}); + {.type_id = ExtractScrutineeType(context.sem_ir(), pattern_type_id)}); CARBON_CHECK(entry.scrutinee_id.has_value()); break; } @@ -511,11 +546,10 @@ auto MatchContext::DoEmitPatternMatch(Context& context, {.lhs_id = storage_id, .rhs_id = init_id}); } } - AddWork( - {.pattern_id = var_pattern.subpattern_id, .scrutinee_id = storage_id}); if (context.scope_stack().PeekIndex() == ScopeIndex::Package) { context.global_init().Suspend(); } + return storage_id; } auto MatchContext::DoEmitPatternMatch(Context& context, @@ -606,27 +640,18 @@ auto MatchContext::EmitPatternMatch(Context& context, CARBON_KIND_SWITCH(pattern) { case SemIR::RefBindingPattern::Kind: case SemIR::SymbolicBindingPattern::Kind: - case SemIR::ValueBindingPattern::Kind: { + case SemIR::ValueBindingPattern::Kind: + case SemIR::FormBindingPattern::Kind: DoEmitPatternMatch(context, pattern.As(), entry); break; - } - case CARBON_KIND(SemIR::ValueParamPattern param_pattern): { - DoEmitPatternMatch(context, param_pattern, entry); + case SemIR::FormParamPattern::Kind: + case SemIR::RefParamPattern::Kind: + case SemIR::ValueParamPattern::Kind: + case SemIR::VarParamPattern::Kind: + case SemIR::OutParamPattern::Kind: + DoEmitPatternMatch(context, pattern.As(), entry); break; - } - case CARBON_KIND(SemIR::RefParamPattern param_pattern): { - DoEmitPatternMatch(context, param_pattern, entry); - break; - } - case CARBON_KIND(SemIR::VarParamPattern param_pattern): { - DoEmitPatternMatch(context, param_pattern, entry); - break; - } - case CARBON_KIND(SemIR::OutParamPattern param_pattern): { - DoEmitPatternMatch(context, param_pattern, entry); - break; - } case CARBON_KIND(SemIR::ReturnSlotPattern return_slot_pattern): { DoEmitPatternMatch(context, return_slot_pattern, entry); break; diff --git a/toolchain/check/testdata/basics/raw_sem_ir/cpp_interop.carbon b/toolchain/check/testdata/basics/raw_sem_ir/cpp_interop.carbon index 6b1f2efaca82..c6ef99d58b19 100644 --- a/toolchain/check/testdata/basics/raw_sem_ir/cpp_interop.carbon +++ b/toolchain/check/testdata/basics/raw_sem_ir/cpp_interop.carbon @@ -66,10 +66,10 @@ fn G(x: Cpp.X) { // CHECK:STDOUT: name_scope60000001: {inst: inst60000011, parent_scope: name_scope0, has_error: false, extended_scopes: [], names: {name2: inst60000014, name3: inst6000002A, name4: inst6000004E}} // CHECK:STDOUT: name_scope60000002: {inst: inst60000014, parent_scope: name_scope60000001, has_error: false, extended_scopes: [], names: {}} // CHECK:STDOUT: entity_names: -// CHECK:STDOUT: entity_name60000000: {name: name1, parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000001: {name: name1, parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000002: {name: name1, parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000003: {name: name4, parent_scope: name_scope60000001, index: -1, is_template: 0, is_unused: 0} +// CHECK:STDOUT: entity_name60000000: {name: name1, parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000001: {name: name1, parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000002: {name: name1, parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000003: {name: name4, parent_scope: name_scope60000001, index: -1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: cpp_global_vars: // CHECK:STDOUT: cpp_global_var60000000: {key: {entity_name_id: entity_name60000003}, clang_decl_id: clang_decl_id60000007} // CHECK:STDOUT: functions: diff --git a/toolchain/check/testdata/basics/raw_sem_ir/multifile.carbon b/toolchain/check/testdata/basics/raw_sem_ir/multifile.carbon index 25eb486c8f31..489f3929cd8a 100644 --- a/toolchain/check/testdata/basics/raw_sem_ir/multifile.carbon +++ b/toolchain/check/testdata/basics/raw_sem_ir/multifile.carbon @@ -121,7 +121,7 @@ fn B() { // CHECK:STDOUT: name_scope0: {inst: instF, parent_scope: name_scope, has_error: false, extended_scopes: [], names: {name1: inst50000011, name0: inst50000012}} // CHECK:STDOUT: name_scope50000001: {inst: inst50000011, parent_scope: name_scope0, has_error: false, extended_scopes: [], names: {name1: inst50000017}} // CHECK:STDOUT: entity_names: -// CHECK:STDOUT: entity_name50000000: {name: name1, parent_scope: name_scope50000001, index: -1, is_template: 0, is_unused: 0} +// CHECK:STDOUT: entity_name50000000: {name: name1, parent_scope: name_scope50000001, index: -1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: cpp_global_vars: {} // CHECK:STDOUT: functions: // CHECK:STDOUT: function50000000: {name: name0, parent_scope: name_scope0, call_param_patterns_id: inst_block_empty, call_params_id: inst_block_empty, body: [inst_block50000006]} diff --git a/toolchain/check/testdata/basics/raw_sem_ir/multifile_with_textual_ir.carbon b/toolchain/check/testdata/basics/raw_sem_ir/multifile_with_textual_ir.carbon index 98a8d6003e40..d353d8cf7126 100644 --- a/toolchain/check/testdata/basics/raw_sem_ir/multifile_with_textual_ir.carbon +++ b/toolchain/check/testdata/basics/raw_sem_ir/multifile_with_textual_ir.carbon @@ -140,7 +140,7 @@ fn B() { // CHECK:STDOUT: name_scope0: {inst: instF, parent_scope: name_scope, has_error: false, extended_scopes: [], names: {name1: inst50000011, name0: inst50000012}} // CHECK:STDOUT: name_scope50000001: {inst: inst50000011, parent_scope: name_scope0, has_error: false, extended_scopes: [], names: {name1: inst50000017}} // CHECK:STDOUT: entity_names: -// CHECK:STDOUT: entity_name50000000: {name: name1, parent_scope: name_scope50000001, index: -1, is_template: 0, is_unused: 0} +// CHECK:STDOUT: entity_name50000000: {name: name1, parent_scope: name_scope50000001, index: -1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: cpp_global_vars: {} // CHECK:STDOUT: functions: // CHECK:STDOUT: function50000000: {name: name0, parent_scope: name_scope0, call_param_patterns_id: inst_block_empty, call_params_id: inst_block_empty, body: [inst_block50000006]} diff --git a/toolchain/check/testdata/basics/raw_sem_ir/one_file.carbon b/toolchain/check/testdata/basics/raw_sem_ir/one_file.carbon index 957ed9edbd21..050f7fb20144 100644 --- a/toolchain/check/testdata/basics/raw_sem_ir/one_file.carbon +++ b/toolchain/check/testdata/basics/raw_sem_ir/one_file.carbon @@ -270,70 +270,70 @@ fn Foo[T:! type](p: T*) -> (T*, ()) { // CHECK:STDOUT: name_scope6000000C: {inst: inst600000DB, parent_scope: name_scope60000001, has_error: false, extended_scopes: [], names: {}} // CHECK:STDOUT: name_scope6000000D: {inst: inst6000011F, parent_scope: name_scope60000001, has_error: false, extended_scopes: [], names: {}} // CHECK:STDOUT: entity_names: -// CHECK:STDOUT: entity_name60000000: {name: name(PeriodSelf), parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000001: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000002: {name: name2, parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000003: {name: name3, parent_scope: name_scope60000001, index: -1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000004: {name: name(SelfType), parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000005: {name: name4, parent_scope: name_scope60000003, index: -1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000006: {name: name(SelfType), parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000007: {name: name(SelfType), parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000008: {name: name(SelfValue), parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000009: {name: name(SelfType), parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name6000000A: {name: name(SelfType), parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name6000000B: {name: name(SelfType), parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name6000000C: {name: name(SelfType), parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name6000000D: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name6000000E: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name6000000F: {name: name(SelfValue), parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000010: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000011: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000012: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000013: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000014: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000015: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000016: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000017: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000018: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000019: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name6000001A: {name: name(SelfValue), parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name6000001B: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name6000001C: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name6000001D: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name6000001E: {name: name(SelfValue), parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name6000001F: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000020: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000021: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000022: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000023: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000024: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000025: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000026: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000027: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000028: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000029: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name6000002A: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name6000002B: {name: name6, parent_scope: name_scope, index: 2, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name6000002C: {name: name6, parent_scope: name_scope, index: 2, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name6000002D: {name: name(SelfValue), parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name6000002E: {name: name6, parent_scope: name_scope, index: 2, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name6000002F: {name: name6, parent_scope: name_scope, index: 2, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000030: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000031: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000032: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000033: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000034: {name: name6, parent_scope: name_scope, index: 2, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000035: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000036: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000037: {name: name6, parent_scope: name_scope, index: 2, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000038: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name60000039: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name6000003A: {name: name6, parent_scope: name_scope, index: 2, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name6000003B: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name6000003C: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name6000003D: {name: name6, parent_scope: name_scope, index: 2, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name6000003E: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0} -// CHECK:STDOUT: entity_name6000003F: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0} +// CHECK:STDOUT: entity_name60000000: {name: name(PeriodSelf), parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000001: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000002: {name: name2, parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000003: {name: name3, parent_scope: name_scope60000001, index: -1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000004: {name: name(SelfType), parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000005: {name: name4, parent_scope: name_scope60000003, index: -1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000006: {name: name(SelfType), parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000007: {name: name(SelfType), parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000008: {name: name(SelfValue), parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000009: {name: name(SelfType), parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name6000000A: {name: name(SelfType), parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name6000000B: {name: name(SelfType), parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name6000000C: {name: name(SelfType), parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name6000000D: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name6000000E: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name6000000F: {name: name(SelfValue), parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000010: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000011: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000012: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000013: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000014: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000015: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000016: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000017: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000018: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000019: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name6000001A: {name: name(SelfValue), parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name6000001B: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name6000001C: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name6000001D: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name6000001E: {name: name(SelfValue), parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name6000001F: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000020: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000021: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000022: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000023: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000024: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000025: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000026: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000027: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000028: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000029: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name6000002A: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name6000002B: {name: name6, parent_scope: name_scope, index: 2, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name6000002C: {name: name6, parent_scope: name_scope, index: 2, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name6000002D: {name: name(SelfValue), parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name6000002E: {name: name6, parent_scope: name_scope, index: 2, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name6000002F: {name: name6, parent_scope: name_scope, index: 2, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000030: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000031: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000032: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000033: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000034: {name: name6, parent_scope: name_scope, index: 2, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000035: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000036: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000037: {name: name6, parent_scope: name_scope, index: 2, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000038: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name60000039: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name6000003A: {name: name6, parent_scope: name_scope, index: 2, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name6000003B: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name6000003C: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name6000003D: {name: name6, parent_scope: name_scope, index: 2, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name6000003E: {name: name5, parent_scope: name_scope, index: 1, is_template: 0, is_unused: 0, form: constant} +// CHECK:STDOUT: entity_name6000003F: {name: name1, parent_scope: name_scope, index: 0, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: cpp_global_vars: {} // CHECK:STDOUT: functions: // CHECK:STDOUT: function60000000: {name: name0, parent_scope: name_scope0, call_param_patterns_id: inst_block60000011, call_params_id: inst_block60000012, return_type_inst_id: inst60000030, return_form_inst_id: inst60000032, return_patterns_id: inst_block60000010, body: [inst_block60000019]} diff --git a/toolchain/check/testdata/basics/raw_sem_ir/one_file_with_textual_ir.carbon b/toolchain/check/testdata/basics/raw_sem_ir/one_file_with_textual_ir.carbon index 6f707cdd3bf5..7a3c92377341 100644 --- a/toolchain/check/testdata/basics/raw_sem_ir/one_file_with_textual_ir.carbon +++ b/toolchain/check/testdata/basics/raw_sem_ir/one_file_with_textual_ir.carbon @@ -31,7 +31,7 @@ fn Foo(n: ()) -> ((), ()) { // CHECK:STDOUT: name_scopes: // CHECK:STDOUT: name_scope0: {inst: instF, parent_scope: name_scope, has_error: false, extended_scopes: [], names: {name0: inst6000002A}} // CHECK:STDOUT: entity_names: -// CHECK:STDOUT: entity_name60000000: {name: name1, parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0} +// CHECK:STDOUT: entity_name60000000: {name: name1, parent_scope: name_scope, index: -1, is_template: 0, is_unused: 0, form: constant} // CHECK:STDOUT: cpp_global_vars: {} // CHECK:STDOUT: functions: // CHECK:STDOUT: function60000000: {name: name0, parent_scope: name_scope0, call_param_patterns_id: inst_block6000000C, call_params_id: inst_block6000000D, return_type_inst_id: inst60000020, return_form_inst_id: inst60000021, return_patterns_id: inst_block6000000B, body: [inst_block60000010]} diff --git a/toolchain/check/testdata/function/call/form.carbon b/toolchain/check/testdata/function/call/form.carbon new file mode 100644 index 000000000000..3c37636acad1 --- /dev/null +++ b/toolchain/check/testdata/function/call/form.carbon @@ -0,0 +1,419 @@ +// 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/int.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/call/form.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/call/form.carbon + +// --- ref_form_param.carbon +library "[[@TEST_NAME]]"; + +//@dump-sem-ir-begin +fn F(x:? form(ref i32)); +//@dump-sem-ir-end + +fn G() { + var y: i32 = 0; + //@dump-sem-ir-begin + F(ref y); + //@dump-sem-ir-end +} + +// --- var_form_param.carbon +library "[[@TEST_NAME]]"; + +//@dump-sem-ir-begin +fn F(x:? form(var i32)); +//@dump-sem-ir-end + +fn G() { + //@dump-sem-ir-begin + F(0); + //@dump-sem-ir-end +} + +// --- val_form_param.carbon +library "[[@TEST_NAME]]"; + +//@dump-sem-ir-begin +fn F(x:? form(val i32)); +//@dump-sem-ir-end + +fn G() { + //@dump-sem-ir-begin + F(0); + //@dump-sem-ir-end +} + +// --- type_component_needs_conversion.carbon +library "[[@TEST_NAME]]"; + +//@dump-sem-ir-begin +fn F(x:? form(ref ())); +//@dump-sem-ir-end + +// --- fail_param_form_not_constant.carbon +library "[[@TEST_NAME]]"; + +fn F() -> Core.Form(); +// CHECK:STDERR: fail_param_form_not_constant.carbon:[[@LINE+4]]:10: error: cannot evaluate form expression [FormExprEvaluationFailure] +// CHECK:STDERR: fn G(x:? F()); +// CHECK:STDERR: ^~~ +// CHECK:STDERR: +fn G(x:? F()); + +// --- fail_form_param_not_form.carbon +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_form_param_not_form.carbon:[[@LINE+7]]:10: error: cannot implicitly convert expression of type `type` to `Core.Form` [ConversionFailure] +// CHECK:STDERR: fn F(x:? i32); +// CHECK:STDERR: ^~~ +// CHECK:STDERR: fail_form_param_not_form.carbon:[[@LINE+4]]:10: note: type `type` does not implement interface `Core.ImplicitAs(Core.Form)` [MissingImplInMemberAccessInContext] +// CHECK:STDERR: fn F(x:? i32); +// CHECK:STDERR: ^~~ +// CHECK:STDERR: +fn F(x:? i32); +// CHECK:STDERR: fail_form_param_not_form.carbon:[[@LINE+8]]:6: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] +// CHECK:STDERR: fn G(ref x:? i32); +// CHECK:STDERR: ^~~~~ +// CHECK:STDERR: +// CHECK:STDERR: fail_form_param_not_form.carbon:[[@LINE+4]]:6: error: expected `:` binding after `ref` [ExpectedRuntimeBindingPatternAfterRef] +// CHECK:STDERR: fn G(ref x:? i32); +// CHECK:STDERR: ^~~ +// CHECK:STDERR: +fn G(ref x:? i32); + +// --- fail_missing_ref_tag.carbon +library "[[@TEST_NAME]]"; + +fn F(x:? form(ref i32)); + +fn G() { + var y: i32 = 0; + // CHECK:STDERR: fail_missing_ref_tag.carbon:[[@LINE+7]]:5: error: argument to `ref` parameter not marked with `ref` [RefParamNoRefTag] + // CHECK:STDERR: F(y); + // CHECK:STDERR: ^ + // CHECK:STDERR: fail_missing_ref_tag.carbon:[[@LINE-7]]:6: note: initializing function parameter [InCallToFunctionParam] + // CHECK:STDERR: fn F(x:? form(ref i32)); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + F(y); +} + +// --- fail_todo_form_generic_param.carbon +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_todo_form_generic_param.carbon:[[@LINE+4]]:26: error: semantics TODO: `Support symbolic form bindings` [SemanticsTodo] +// CHECK:STDERR: fn F(Form:! Core.Form(), x:? Form); +// CHECK:STDERR: ^~~~~~~~ +// CHECK:STDERR: +fn F(Form:! Core.Form(), x:? Form); + +fn G() { + var x: i32; + F(form(var i32), x); +} + +// --- fail_todo_local_init_form_binding.carbon +library "[[@TEST_NAME]]"; + +fn F() { + // CHECK:STDERR: fail_todo_local_init_form_binding.carbon:[[@LINE+12]]:7: error: semantics TODO: `Support local initializing forms` [SemanticsTodo] + // CHECK:STDERR: let x:? form(var i32) = 0; + // CHECK:STDERR: ^ + // CHECK:STDERR: + // CHECK:STDERR: fail_todo_local_init_form_binding.carbon:[[@LINE+8]]:27: error: cannot bind durable reference to non-reference value of type `i32` [ConversionFailureNonRefToRef] + // CHECK:STDERR: let x:? form(var i32) = 0; + // CHECK:STDERR: ^ + // CHECK:STDERR: + // CHECK:STDERR: fail_todo_local_init_form_binding.carbon:[[@LINE+4]]:7: warning: binding `x` unused [UnusedBinding] + // CHECK:STDERR: let x:? form(var i32) = 0; + // CHECK:STDERR: ^ + // CHECK:STDERR: + let x:? form(var i32) = 0; +} + +// --- fail_todo_local_symbolic_form_binding.carbon +library "[[@TEST_NAME]]"; + +// TODO: this code should fail for a different reason, but right now we don't +// have a way to write correct code that reaches the TODO we're exercising here. +fn F(Form:! Core.Form()) { + // CHECK:STDERR: fail_todo_local_symbolic_form_binding.carbon:[[@LINE+4]]:7: error: semantics TODO: `Support symbolic form bindings` [SemanticsTodo] + // CHECK:STDERR: let y:? Form = 0; + // CHECK:STDERR: ^~~~~~~~ + // CHECK:STDERR: + let y:? Form = 0; +} + +// --- fail_todo_ref_return_form.carbon +library "[[@TEST_NAME]]"; + +//@dump-sem-ir-begin +// CHECK:STDERR: fail_todo_ref_return_form.carbon:[[@LINE+4]]:8: error: semantics TODO: `Support ->?` [SemanticsTodo] +// CHECK:STDERR: fn F() ->? form(ref i32); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn F() ->? form(ref i32); +//@dump-sem-ir-end + +fn G() { + //@dump-sem-ir-begin + let ref x: i32 = F(); + //@dump-sem-ir-end +} + +// --- fail_todo_var_return_form.carbon +library "[[@TEST_NAME]]"; + +//@dump-sem-ir-begin +// CHECK:STDERR: fail_todo_var_return_form.carbon:[[@LINE+4]]:8: error: semantics TODO: `Support ->?` [SemanticsTodo] +// CHECK:STDERR: fn F() ->? form(var i32); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn F() ->? form(var i32); +//@dump-sem-ir-end + +fn G() { + //@dump-sem-ir-begin + let var x: i32 = F(); + //@dump-sem-ir-end +} + +// --- fail_todo_val_return_form.carbon +library "[[@TEST_NAME]]"; + +//@dump-sem-ir-begin +// CHECK:STDERR: fail_todo_val_return_form.carbon:[[@LINE+4]]:8: error: semantics TODO: `Support ->?` [SemanticsTodo] +// CHECK:STDERR: fn F() ->? form(val i32); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn F() ->? form(val i32); +//@dump-sem-ir-end + +fn G() { + //@dump-sem-ir-begin + let x: i32 = F(); + //@dump-sem-ir-end +} + +// --- fail_return_form_not_form.carbon +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_return_form_not_form.carbon:[[@LINE+4]]:8: error: semantics TODO: `Support ->?` [SemanticsTodo] +// CHECK:STDERR: fn F() ->? i32; +// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: +fn F() ->? i32; +fn F() ->? ref i32; + +// CHECK:STDOUT: --- ref_form_param.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] +// CHECK:STDOUT: %.1da: Core.Form = ref_form %i32 [concrete] +// CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %.loc4_6.1: %pattern_type.7ce = form_binding_pattern x [concrete] +// CHECK:STDOUT: %.loc4_7: %pattern_type.7ce = form_param_pattern %.loc4_6.1, call_param0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %x.param: ref %i32 = ref_param call_param0 +// CHECK:STDOUT: %.loc4_15.1: Core.Form = splice_block %.loc4_15.2 [concrete = constants.%.1da] { +// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %.loc4_15.2: Core.Form = ref_form %i32 [concrete = constants.%.1da] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc4_6.2: ref %i32 = form_binding x, %x.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(%x.param: ref %i32); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: +// CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] +// CHECK:STDOUT: %y.ref: ref %i32 = name_ref y, %y +// CHECK:STDOUT: %.loc10: %i32 = ref_tag %y.ref +// CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref(%.loc10) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- var_form_param.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] +// CHECK:STDOUT: %.324: Core.Form = init_form %i32, call_param [concrete] +// CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete] +// CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] +// CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] +// CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] +// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] +// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs(%i32, %ImplicitAs.facet) [concrete] +// CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @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: %int_0.6a9: %i32 = int_value 0 [concrete] +// CHECK:STDOUT: %DestroyOp.type: type = fn_type @DestroyOp [concrete] +// CHECK:STDOUT: %DestroyOp: %DestroyOp.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] +// CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %.loc4_6.1: %pattern_type.7ce = form_binding_pattern x [concrete] +// CHECK:STDOUT: %.loc4_7: %pattern_type.7ce = form_param_pattern %.loc4_6.1, call_param0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %x.param: ref %i32 = ref_param call_param0 +// CHECK:STDOUT: %.loc4_15.1: Core.Form = splice_block %.loc4_15.2 [concrete = constants.%.324] { +// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %.loc4_15.2: Core.Form = init_form %i32, call_param [concrete = constants.%.324] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc4_6.2: ref %i32 = form_binding x, %x.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(%x.param: ref %i32); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] +// CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] +// CHECK:STDOUT: %.loc4_7.1: ref %i32 = temporary_storage +// CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] +// CHECK:STDOUT: %bound_method.loc4_7.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.loc4_7.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.loc4_7.2(%int_0) [concrete = constants.%int_0.6a9] +// CHECK:STDOUT: %.loc4_7.2: init %i32 = converted %int_0, %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_0.6a9] +// CHECK:STDOUT: %.loc4_7.3: ref %i32 = temporary %.loc4_7.1, %.loc4_7.2 +// CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref(%.loc4_7.3) +// CHECK:STDOUT: %DestroyOp.bound: = bound_method %.loc4_7.3, constants.%DestroyOp +// CHECK:STDOUT: %DestroyOp.call: init %empty_tuple.type = call %DestroyOp.bound(%.loc4_7.3) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @DestroyOp(%self.param: ref %i32) = "no_op"; +// CHECK:STDOUT: +// CHECK:STDOUT: --- val_form_param.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] +// CHECK:STDOUT: %.754: Core.Form = value_form %i32 [concrete] +// CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %int_0.5c6: Core.IntLiteral = int_value 0 [concrete] +// CHECK:STDOUT: %ImplicitAs.type.e8c: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] +// CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6 = struct_value () [symbolic] +// CHECK:STDOUT: %ImplicitAs.impl_witness.6bc: = impl_witness imports.%ImplicitAs.impl_witness_table.74f, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.e0d = struct_value () [concrete] +// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.e8c = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.6bc) [concrete] +// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.b37: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs(%i32, %ImplicitAs.facet) [concrete] +// CHECK:STDOUT: %.545: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.b37, %ImplicitAs.facet [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound: = bound_method %int_0.5c6, %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5 [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5, @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: %int_0.6a9: %i32 = int_value 0 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core.import_ref.42d: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.4e6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.3c2)] +// CHECK:STDOUT: %ImplicitAs.impl_witness_table.74f = impl_witness_table (%Core.import_ref.42d), @Core.IntLiteral.as.ImplicitAs.impl [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %.loc4_6.1: %pattern_type.7ce = form_binding_pattern x [concrete] +// CHECK:STDOUT: %.loc4_7: %pattern_type.7ce = form_param_pattern %.loc4_6.1, call_param0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %x.param: %i32 = value_param call_param0 +// CHECK:STDOUT: %.loc4_15.1: Core.Form = splice_block %.loc4_15.2 [concrete = constants.%.754] { +// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %.loc4_15.2: Core.Form = value_form %i32 [concrete = constants.%.754] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc4_6.2: %i32 = form_binding x, %x.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(%x.param: %i32); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] +// CHECK:STDOUT: %int_0: Core.IntLiteral = int_value 0 [concrete = constants.%int_0.5c6] +// CHECK:STDOUT: %impl.elem0: %.545 = impl_witness_access constants.%ImplicitAs.impl_witness.6bc, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.0b5] +// CHECK:STDOUT: %bound_method.loc9_5.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.loc9_5.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.loc9_5.2(%int_0) [concrete = constants.%int_0.6a9] +// CHECK:STDOUT: %.loc9_5.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call [concrete = constants.%int_0.6a9] +// CHECK:STDOUT: %.loc9_5.2: %i32 = converted %int_0, %.loc9_5.1 [concrete = constants.%int_0.6a9] +// CHECK:STDOUT: %F.call: init %empty_tuple.type = call %F.ref(%.loc9_5.2) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- type_component_needs_conversion.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] +// CHECK:STDOUT: %.9f9: Core.Form = ref_form %empty_tuple.type [concrete] +// CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %.loc4_6.1: %pattern_type = form_binding_pattern x [concrete] +// CHECK:STDOUT: %.loc4_7: %pattern_type = form_param_pattern %.loc4_6.1, call_param0 [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %x.param: ref %empty_tuple.type = ref_param call_param0 +// CHECK:STDOUT: %.loc4_15.1: Core.Form = splice_block %.loc4_15.2 [concrete = constants.%.9f9] { +// CHECK:STDOUT: %.loc4_20.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc4_20.2: type = converted %.loc4_20.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] +// CHECK:STDOUT: %.loc4_15.2: Core.Form = ref_form %.loc4_20.2 [concrete = constants.%.9f9] +// CHECK:STDOUT: } +// CHECK:STDOUT: %.loc4_6.2: ref %empty_tuple.type = form_binding x, %x.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(%x.param: ref %empty_tuple.type); +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_ref_return_form.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_var_return_form.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_todo_val_return_form.carbon +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/impl/fail_todo_form_thunk.carbon b/toolchain/check/testdata/impl/fail_todo_form_thunk.carbon new file mode 100644 index 000000000000..a73325883da3 --- /dev/null +++ b/toolchain/check/testdata/impl/fail_todo_form_thunk.carbon @@ -0,0 +1,36 @@ +// 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/impl/fail_todo_form_thunk.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/impl/fail_todo_form_thunk.carbon + +interface I { + let T:! type; + // CHECK:STDERR: fail_todo_form_thunk.carbon:[[@LINE+7]]:8: error: semantics TODO: `Support for cloning form bindings` [SemanticsTodo] + // CHECK:STDERR: fn F(x:? form(ref ()), y: T); + // CHECK:STDERR: ^ + // CHECK:STDERR: + // CHECK:STDERR: fail_todo_form_thunk.carbon:[[@LINE+3]]:8: error: value expression passed to reference parameter [ValueForRefParam] + // CHECK:STDERR: fn F(x:? form(ref ()), y: T); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~ + fn F(x:? form(ref ()), y: T); +} + +class C { + impl as I where .T = () { + // CHECK:STDERR: fail_todo_form_thunk.carbon:[[@LINE+7]]:10: note: initializing function parameter [InCallToFunctionParam] + // CHECK:STDERR: fn F(x:? form(ref ()), ()); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~ + // CHECK:STDERR: fail_todo_form_thunk.carbon:[[@LINE-8]]:3: note: while building thunk to match the signature of this function [ThunkSignature] + // CHECK:STDERR: fn F(x:? form(ref ()), y: T); + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + fn F(x:? form(ref ()), ()); + } +} diff --git a/toolchain/check/thunk.cpp b/toolchain/check/thunk.cpp index 798efaca894d..07809dbcb8e2 100644 --- a/toolchain/check/thunk.cpp +++ b/toolchain/check/thunk.cpp @@ -62,7 +62,12 @@ static auto CloneBindingPattern(Context& context, SemIR::InstId pattern_id, auto entity_name = context.entity_names().Get(pattern.entity_name_id); CARBON_CHECK((pattern.kind == SemIR::SymbolicBindingPattern::Kind) == entity_name.bind_index().has_value()); - + CARBON_CHECK((pattern.kind == SemIR::FormBindingPattern::Kind) == + entity_name.form_id.has_value()); + if (pattern.kind == SemIR::FormBindingPattern::Kind) { + context.TODO(pattern_id, "Support for cloning form bindings"); + return SemIR::ErrorInst::InstId; + } // Get the transformed type of the binding. if (new_pattern_type_id == SemIR::ErrorInst::TypeId) { return SemIR::ErrorInst::InstId; @@ -75,10 +80,11 @@ static auto CloneBindingPattern(Context& context, SemIR::InstId pattern_id, {.block_ids = {SemIR::InstBlockId::Empty}, .result_id = type_inst_id}); // Rebuild the binding pattern. - return AddBindingPattern(context, SemIR::LocId(pattern_id), - entity_name.name_id, type_id, type_expr_region_id, - pattern.kind, entity_name.is_template, - /*is_unused=*/false) + return AddBindingPattern( + context, SemIR::LocId(pattern_id), entity_name.name_id, type_id, + /*form_id=*/SemIR::ConstantId::None, type_expr_region_id, + pattern.kind, entity_name.is_template, + /*is_unused=*/false) .pattern_id; } diff --git a/toolchain/check/type.cpp b/toolchain/check/type.cpp index 90df07fd8354..ebd61262f3f9 100644 --- a/toolchain/check/type.cpp +++ b/toolchain/check/type.cpp @@ -137,6 +137,11 @@ auto GetConstType(Context& context, SemIR::TypeInstId inner_type_id) return GetTypeImpl(context, inner_type_id); } +auto GetTypeComponent(Context& context, SemIR::InstId form_inst_id) + -> SemIR::TypeId { + return GetTypeImpl(context, form_inst_id); +} + auto GetQualifiedType(Context& context, SemIR::TypeId type_id, SemIR::TypeQualifiers quals) -> SemIR::TypeId { if (quals.HasAnyOf(SemIR::TypeQualifiers::Const)) { diff --git a/toolchain/check/type.h b/toolchain/check/type.h index d4578961b409..f337aa2749ac 100644 --- a/toolchain/check/type.h +++ b/toolchain/check/type.h @@ -125,6 +125,10 @@ auto GetTupleType(Context& context, llvm::ArrayRef type_inst_ids) auto GetPatternType(Context& context, SemIR::TypeId scrutinee_type_id) -> SemIR::TypeId; +// Returns the type component of the given form value. +auto GetTypeComponent(Context& context, SemIR::InstId form_inst_id) + -> SemIR::TypeId; + // Returns an unbound element type. auto GetUnboundElementType(Context& context, SemIR::TypeInstId class_type_id, SemIR::TypeInstId element_type_id) -> SemIR::TypeId; diff --git a/toolchain/diagnostics/kind.def b/toolchain/diagnostics/kind.def index 8b115fc4b77c..8ab22855b9cd 100644 --- a/toolchain/diagnostics/kind.def +++ b/toolchain/diagnostics/kind.def @@ -437,6 +437,7 @@ CARBON_DIAGNOSTIC_KIND(DerefOfType) CARBON_DIAGNOSTIC_KIND(CompileTimeBindingInVarDecl) CARBON_DIAGNOSTIC_KIND(CompoundMemberAccessDoesNotUseBase) CARBON_DIAGNOSTIC_KIND(EvalRequiresConstantValue) +CARBON_DIAGNOSTIC_KIND(FormExprEvaluationFailure) CARBON_DIAGNOSTIC_KIND(NameDeclDuplicate) CARBON_DIAGNOSTIC_KIND(NameDeclPrevious) CARBON_DIAGNOSTIC_KIND(NameUseBeforeDecl) diff --git a/toolchain/lower/handle.cpp b/toolchain/lower/handle.cpp index dffe55e187f2..9fab2e912634 100644 --- a/toolchain/lower/handle.cpp +++ b/toolchain/lower/handle.cpp @@ -119,6 +119,11 @@ auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, context.SetLocal(inst_id, context.GetValue(inst.value_id)); } +auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, + SemIR::FormBinding inst) -> void { + context.SetLocal(inst_id, context.GetValue(inst.value_id)); +} + auto HandleInst(FunctionContext& context, SemIR::InstId inst_id, SemIR::BlockArg inst) -> void { context.SetLocal( diff --git a/toolchain/parse/testdata/function/declaration.carbon b/toolchain/parse/testdata/function/declaration.carbon index a57deeb4f81d..342913304402 100644 --- a/toolchain/parse/testdata/function/declaration.carbon +++ b/toolchain/parse/testdata/function/declaration.carbon @@ -167,6 +167,14 @@ fn (a tokens c d e f g h i j k l m n o p q r s t u v w x y z); fn FormParam(x:? Form); fn ComplexFormParam(x:? X.Y(Z)); +// --- fail_ref_on_form_parameter.carbon + +// CHECK:STDERR: fail_ref_on_form_parameter.carbon:[[@LINE+4]]:6: error: expected `:` binding after `ref` [ExpectedRuntimeBindingPatternAfterRef] +// CHECK:STDERR: fn F(ref x:? Form); +// CHECK:STDERR: ^~~ +// CHECK:STDERR: +fn F(ref x:? Form); + // --- return_form.carbon fn SimpleReturnForm() ->? Form; @@ -457,6 +465,20 @@ fn ComplexReturnForm() ->? X.Y(Z); // CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 13}, // CHECK:STDOUT: {kind: 'FileEnd', text: ''}, // CHECK:STDOUT: ] +// CHECK:STDOUT: - filename: fail_ref_on_form_parameter.carbon +// CHECK:STDOUT: parse_tree: [ +// CHECK:STDOUT: {kind: 'FileStart', text: ''}, +// CHECK:STDOUT: {kind: 'FunctionIntroducer', text: 'fn'}, +// CHECK:STDOUT: {kind: 'IdentifierNameMaybeBeforeSignature', text: 'F'}, +// CHECK:STDOUT: {kind: 'ExplicitParamListStart', text: '('}, +// CHECK:STDOUT: {kind: 'IdentifierNameNotBeforeSignature', text: 'x'}, +// CHECK:STDOUT: {kind: 'RefBindingName', text: 'ref', has_error: yes, subtree_size: 2}, +// CHECK:STDOUT: {kind: 'IdentifierNameExpr', text: 'Form'}, +// CHECK:STDOUT: {kind: 'FormBindingPattern', text: ':?', has_error: yes, subtree_size: 4}, +// CHECK:STDOUT: {kind: 'ExplicitParamList', text: ')', has_error: yes, subtree_size: 6}, +// CHECK:STDOUT: {kind: 'FunctionDecl', text: ';', subtree_size: 9}, +// CHECK:STDOUT: {kind: 'FileEnd', text: ''}, +// CHECK:STDOUT: ] // CHECK:STDOUT: - filename: return_form.carbon // CHECK:STDOUT: parse_tree: [ // CHECK:STDOUT: {kind: 'FileStart', text: ''}, diff --git a/toolchain/sem_ir/entity_name.h b/toolchain/sem_ir/entity_name.h index 6dfdb7474d1e..dcbb1484d853 100644 --- a/toolchain/sem_ir/entity_name.h +++ b/toolchain/sem_ir/entity_name.h @@ -17,7 +17,7 @@ struct EntityName : public Printable { auto Print(llvm::raw_ostream& out) const -> void { out << "{name: " << name_id << ", parent_scope: " << parent_scope_id << ", index: " << bind_index_value << ", is_template: " << is_template - << ", is_unused: " << is_unused << "}"; + << ", is_unused: " << is_unused << ", form: " << form_id << "}"; } friend auto CarbonHashtableEq(const EntityName& lhs, const EntityName& rhs) @@ -58,6 +58,13 @@ struct EntityName : public Printable { bool is_template : 1 = false; // Whether this binding is marked unused. bool is_unused : 1 = false; + + // The declared form of the binding. This is guaranteed to be set for + // `:?` bindings, and may be set for other binding kinds as well. + // + // TODO: Unify this with the previous three fields, which also represent form + // information. + ConstantId form_id = ConstantId::None; }; // Value store for EntityName. In addition to the regular ValueStore diff --git a/toolchain/sem_ir/expr_info.cpp b/toolchain/sem_ir/expr_info.cpp index 6f7fdb898c80..d7d04f97c478 100644 --- a/toolchain/sem_ir/expr_info.cpp +++ b/toolchain/sem_ir/expr_info.cpp @@ -91,6 +91,25 @@ static auto GetExprCategoryImpl(const File* ir, InstId inst_id) return ExprCategory::ReprInitializing; } } + } else if constexpr (std::same_as) { + auto form_id = ir->entity_names().Get(inst.entity_name_id).form_id; + if (form_id.is_symbolic()) { + return ExprCategory::Dependent; + } + auto form_inst_id = ir->constant_values().GetInstId(form_id); + auto form_inst = ir->insts().Get(form_inst_id); + CARBON_KIND_SWITCH(form_inst) { + case InitForm::Kind: + // A `var` binding pattern produces a `ref` binding. + case RefForm::Kind: + return ExprCategory::DurableRef; + case ValueForm::Kind: + return ExprCategory::Value; + case ErrorInst::Kind: + return ExprCategory::Error; + default: + CARBON_FATAL("Unexpected kind for form inst {0}", form_inst); + } } else { static_assert( TypedInstT::Kind.expr_category().TryAsComputedCategory() != diff --git a/toolchain/sem_ir/inst_categories.h b/toolchain/sem_ir/inst_categories.h index 0c467ca8b6fa..ea484439f506 100644 --- a/toolchain/sem_ir/inst_categories.h +++ b/toolchain/sem_ir/inst_categories.h @@ -65,8 +65,8 @@ struct AnyAggregateValue { // Common representation for various `*binding_pattern` nodes. struct AnyBindingPattern { // TODO: Also handle TemplateBindingPattern once it exists. - using CategoryInfo = CategoryOf; + using CategoryInfo = CategoryOf; InstKind kind; @@ -83,8 +83,8 @@ struct AnyBindingPattern { // Common representation for various `bind*` nodes. struct AnyBinding { // TODO: Also handle BindTemplateName once it exists. - using CategoryInfo = - CategoryOf; + using CategoryInfo = CategoryOf; InstKind kind; TypeId type_id; @@ -98,8 +98,8 @@ struct AnyBinding { // Common representation for various `bind*` nodes, and `export name`. struct AnyBindingOrExportDecl { // TODO: Also handle BindTemplateName once it exists. - using CategoryInfo = CategoryOf; + using CategoryInfo = CategoryOf; InstKind kind; TypeId type_id; @@ -159,8 +159,9 @@ struct AnyParam { // A pattern that represents a `Call` parameter. It delegates to subpattern_id // in pattern matching. struct AnyParamPattern { - using CategoryInfo = CategoryOf; + using CategoryInfo = + CategoryOf; InstKind kind; @@ -171,6 +172,21 @@ struct AnyParamPattern { CallParamIndex index; }; +// An inst that represents a primitive form. +struct AnyPrimitiveForm { + using CategoryInfo = CategoryOf; + + InstKind kind; + + // Always FormType. + TypeId type_id; + + // The type component of the form. + TypeInstId type_component_id; + + AnyRawId arg1; +}; + // A type qualifier that wraps another type and has the same object // representation. Qualifiers are arranged so that adding a qualifier is // generally safe, and removing a qualifier is not necessarily safe or correct. diff --git a/toolchain/sem_ir/inst_kind.def b/toolchain/sem_ir/inst_kind.def index 41f81f2827f9..089459860592 100644 --- a/toolchain/sem_ir/inst_kind.def +++ b/toolchain/sem_ir/inst_kind.def @@ -69,6 +69,9 @@ CARBON_SEM_IR_INST_KIND(FloatLiteralType) CARBON_SEM_IR_INST_KIND(FloatLiteralValue) CARBON_SEM_IR_INST_KIND(FloatType) CARBON_SEM_IR_INST_KIND(FloatValue) +CARBON_SEM_IR_INST_KIND(FormBinding) +CARBON_SEM_IR_INST_KIND(FormBindingPattern) +CARBON_SEM_IR_INST_KIND(FormParamPattern) CARBON_SEM_IR_INST_KIND(FormType) CARBON_SEM_IR_INST_KIND(FunctionDecl) CARBON_SEM_IR_INST_KIND(FunctionType) @@ -151,6 +154,7 @@ CARBON_SEM_IR_INST_KIND(TupleLiteral) CARBON_SEM_IR_INST_KIND(TuplePattern) CARBON_SEM_IR_INST_KIND(TupleType) CARBON_SEM_IR_INST_KIND(TupleValue) +CARBON_SEM_IR_INST_KIND(TypeComponentOf) CARBON_SEM_IR_INST_KIND(TypeLiteral) CARBON_SEM_IR_INST_KIND(TypeOfInst) CARBON_SEM_IR_INST_KIND(TypeType) @@ -160,6 +164,7 @@ CARBON_SEM_IR_INST_KIND(UninitializedValue) CARBON_SEM_IR_INST_KIND(ValueAsRef) CARBON_SEM_IR_INST_KIND(ValueBinding) CARBON_SEM_IR_INST_KIND(ValueBindingPattern) +CARBON_SEM_IR_INST_KIND(ValueForm) CARBON_SEM_IR_INST_KIND(ValueOfInitializer) CARBON_SEM_IR_INST_KIND(ValueParam) CARBON_SEM_IR_INST_KIND(ValueParamPattern) diff --git a/toolchain/sem_ir/stringify.cpp b/toolchain/sem_ir/stringify.cpp index 93b9c4594b97..435b16d686e1 100644 --- a/toolchain/sem_ir/stringify.cpp +++ b/toolchain/sem_ir/stringify.cpp @@ -667,6 +667,11 @@ class Stringifier { } } + auto StringifyInst(InstId /*inst_id*/, TypeComponentOf inst) -> void { + *out_ << "Push(inst.form_inst_id, ">"); + } + auto StringifyInst(InstId inst_id, TypeOfInst /*inst*/) -> void { // Print the constant value if we've already computed the inst. auto const_inst_id = sem_ir_->constant_values().GetConstantInstId(inst_id); diff --git a/toolchain/sem_ir/type_iterator.cpp b/toolchain/sem_ir/type_iterator.cpp index 9c6f7d80d46d..6cb14613f51f 100644 --- a/toolchain/sem_ir/type_iterator.cpp +++ b/toolchain/sem_ir/type_iterator.cpp @@ -98,6 +98,7 @@ auto TypeIterator::ProcessTypeId(TypeId type_id) -> std::optional { case FacetType::Kind: case FloatLiteralType::Kind: case FloatType::Kind: + case FormType::Kind: case FunctionType::Kind: case FunctionTypeWithSelfType::Kind: case GenericClassType::Kind: diff --git a/toolchain/sem_ir/typed_insts.h b/toolchain/sem_ir/typed_insts.h index d03c4651440a..896a6650c4e6 100644 --- a/toolchain/sem_ir/typed_insts.h +++ b/toolchain/sem_ir/typed_insts.h @@ -746,6 +746,51 @@ struct FloatValue { FloatId float_id; }; +// A form binding, such as the `x` declared by `x:? F`. See `AnyBinding` for +// member documentation. +struct FormBinding { + static constexpr auto Kind = + InstKind::FormBinding.Define( + {.ir_name = "form_binding", + .expr_category = ComputedExprCategory::DependsOnOperands, + .constant_kind = InstConstantKind::Never}); + + TypeId type_id; + EntityNameId entity_name_id; + InstId value_id; +}; + +// A form binding pattern, such as `x:? F`. See `AnyBindingPattern` for member +// documentation. +struct FormBindingPattern { + static constexpr auto Kind = + InstKind::FormBindingPattern.Define( + {.ir_name = "form_binding_pattern", + .expr_category = ExprCategory::Pattern, + .constant_kind = InstConstantKind::AlwaysUnique, + .is_lowered = false}); + + TypeId type_id; + // Note that the EntityName's `form_id` represents the scrutinee form, so it + // doesn't directly correspond to `type_id` (which is a pattern type). + EntityNameId entity_name_id; +}; + +// A pattern that represents a form-parameterized parameter, such as `x:? F`. +// See `AnyParamPattern` for member documentation. +struct FormParamPattern { + // TODO: Replace `Parse::NodeId` with `Parse::FormBindingPattern`. + static constexpr auto Kind = InstKind::FormParamPattern.Define( + {.ir_name = "form_param_pattern", + .expr_category = ExprCategory::Pattern, + .constant_kind = InstConstantKind::AlwaysUnique, + .is_lowered = false}); + + TypeId type_id; + InstId subpattern_id; + CallParamIndex index; +}; + // The type `Core.Form`. struct FormType : public SingletonTypeInst { // `FormType` is always set complete in file.cpp. @@ -1395,7 +1440,8 @@ struct RefBindingPattern { struct RefForm { static constexpr auto Kind = - InstKind::RefForm.Define( + InstKind::RefForm.Define>( {.ir_name = "ref_form", .constant_kind = InstConstantKind::Always, .is_lowered = false}); @@ -1726,7 +1772,7 @@ struct SpliceInst { static constexpr auto Kind = InstKind::SpliceInst.Define( {.ir_name = "splice_inst", // TODO: The expression category is in general dependent on - // instantiation. Add ExprCategory::Dependent to model this. + // instantiation. Use ExprCategory::Dependent to model this. .expr_category = ExprCategory::Value}); TypeId type_id; @@ -1955,6 +2001,21 @@ struct TupleValue { InstBlockId elements_id; }; +// Extracts the type component of `form_inst_id`, which must have type +// `Core.Form`. +struct TypeComponentOf { + static constexpr auto Kind = + InstKind::TypeComponentOf.Define( + {.ir_name = "type_component_of", + .is_type = InstIsType::Always, + .constant_kind = InstConstantKind::SymbolicOnly, + .is_lowered = false}); + + // Always TypeType. + TypeId type_id; + InstId form_inst_id; +}; + // A type literal, such as `bool` or `type` or `i32`. The constant value of this // instruction will be the type value that the type literal evaluates to, which // is typically either a builtin type or a class defined in the prelude. @@ -2072,6 +2133,18 @@ struct ValueBindingPattern { EntityNameId entity_name_id; }; +// A primitive value form. +struct ValueForm { + static constexpr auto Kind = + InstKind::ValueForm.Define( + {.ir_name = "value_form", + .constant_kind = InstConstantKind::Always, + .is_lowered = false}); + + TypeId type_id; + TypeInstId type_component_inst_id; +}; + // Converts an initializing expression to a value expression, in the case // where the initializing representation is the same as the value // representation.