From 04ae5a0531c2da317ccf4ed8fcee36ac5ed00320 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 26 Oct 2023 14:01:46 -0700 Subject: [PATCH] Support for functions with a `self` parameter. (#3338) So far, such functions can only be defined; calls are not supported yet. --- toolchain/check/context.cpp | 1 + toolchain/check/handle_call_expression.cpp | 16 ++++- toolchain/check/handle_function.cpp | 38 +++++++--- toolchain/check/handle_name.cpp | 19 ++++- toolchain/check/handle_parameter_list.cpp | 28 +++++++- toolchain/check/handle_pattern_binding.cpp | 36 +++++++++- toolchain/check/handle_variable.cpp | 27 ++++---- toolchain/check/node_stack.h | 38 ++++++++-- .../testdata/class/fail_addr_not_self.carbon | 36 ++++++++++ .../check/testdata/class/fail_method.carbon | 57 +++++++++++++++ .../check/testdata/class/fail_self.carbon | 69 +++++++++++++++++++ toolchain/check/testdata/class/self.carbon | 59 ++++++++++++++++ .../function/call/fail_not_callable.carbon | 7 +- toolchain/diagnostics/diagnostic_kind.def | 2 + toolchain/lower/file_context.cpp | 22 ++++-- toolchain/lower/handle.cpp | 6 ++ toolchain/lower/testdata/class/self.carbon | 35 ++++++++++ toolchain/parse/handle_expression.cpp | 2 +- toolchain/parse/handle_pattern.cpp | 14 ++-- toolchain/parse/node_kind.def | 5 +- .../testdata/class/fn_definitions.carbon | 4 +- toolchain/sem_ir/file.cpp | 3 + toolchain/sem_ir/file.h | 2 + toolchain/sem_ir/formatter.cpp | 40 ++++++++--- toolchain/sem_ir/node_kind.def | 1 + toolchain/sem_ir/typed_nodes.h | 9 +++ 26 files changed, 510 insertions(+), 66 deletions(-) create mode 100644 toolchain/check/testdata/class/fail_addr_not_self.carbon create mode 100644 toolchain/check/testdata/class/fail_method.carbon create mode 100644 toolchain/check/testdata/class/fail_self.carbon create mode 100644 toolchain/check/testdata/class/self.carbon create mode 100644 toolchain/lower/testdata/class/self.carbon diff --git a/toolchain/check/context.cpp b/toolchain/check/context.cpp index b390cd4e6bfc..d0bc6afc9982 100644 --- a/toolchain/check/context.cpp +++ b/toolchain/check/context.cpp @@ -675,6 +675,7 @@ class TypeCompleter { case SemIR::RealLiteral::Kind: case SemIR::Return::Kind: case SemIR::ReturnExpression::Kind: + case SemIR::SelfParameter::Kind: case SemIR::SpliceBlock::Kind: case SemIR::StringLiteral::Kind: case SemIR::StructAccess::Kind: diff --git a/toolchain/check/handle_call_expression.cpp b/toolchain/check/handle_call_expression.cpp index ff1de50692ac..c96841cefdbe 100644 --- a/toolchain/check/handle_call_expression.cpp +++ b/toolchain/check/handle_call_expression.cpp @@ -23,7 +23,7 @@ auto HandleCallExpression(Context& context, Parse::Node parse_node) -> bool { if (!function_name) { // TODO: Work on error. context.TODO(parse_node, "Not a callable name"); - context.node_stack().Push(parse_node, callee_id); + context.node_stack().Push(parse_node, SemIR::NodeId::BuiltinError); context.ParamOrArgPop(); return true; } @@ -47,6 +47,20 @@ auto HandleCallExpression(Context& context, Parse::Node parse_node) -> bool { context.ParamOrArgSave(temp_id); } + for (auto implicit_param_id : + context.node_blocks().Get(callable.implicit_param_refs_id)) { + auto param = context.nodes().Get(implicit_param_id); + if (auto self_param = param.TryAs()) { + // TODO: Handle `self` parameter. + } + + // TODO: Form argument values for implicit parameters. + context.TODO(parse_node, "Call with implicit parameters"); + context.node_stack().Push(parse_node, SemIR::NodeId::BuiltinError); + context.ParamOrArgPop(); + return true; + } + // Convert the arguments to match the parameters. auto refs_id = context.ParamOrArgPop(); if (!ConvertCallArgs(context, call_expr_parse_node, refs_id, diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index b61464a4f4ae..e23195996306 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -51,6 +51,10 @@ static auto BuildFunctionDeclaration(Context& context, bool is_definition) SemIR::NodeBlockId param_refs_id = context.node_stack().Pop(); + SemIR::NodeBlockId implicit_param_refs_id = + context.node_stack() + .PopIf() + .value_or(SemIR::NodeBlockId::Empty); auto name_context = context.declaration_name_stack().Pop(); auto fn_node = context.node_stack() @@ -80,6 +84,7 @@ static auto BuildFunctionDeclaration(Context& context, bool is_definition) if (is_definition) { auto& function_info = context.functions().Get(function_decl.function_id); + function_info.implicit_param_refs_id = implicit_param_refs_id; function_info.param_refs_id = param_refs_id; function_info.return_type_id = return_type_id; function_info.return_slot_id = return_slot_id; @@ -97,6 +102,7 @@ static auto BuildFunctionDeclaration(Context& context, bool is_definition) DeclarationNameStack::NameContext::State::Unresolved ? name_context.unresolved_name_id : StringId::Invalid, + .implicit_param_refs_id = implicit_param_refs_id, .param_refs_id = param_refs_id, .return_type_id = return_type_id, .return_slot_id = return_slot_id}); @@ -107,7 +113,8 @@ static auto BuildFunctionDeclaration(Context& context, bool is_definition) if (SemIR::IsEntryPoint(context.sem_ir(), function_decl.function_id)) { // TODO: Update this once valid signatures for the entry point are decided. - if (!context.node_blocks().Get(param_refs_id).empty() || + if (!context.node_blocks().Get(implicit_param_refs_id).empty() || + !context.node_blocks().Get(param_refs_id).empty() || (return_slot_id.is_valid() && return_type_id != context.GetBuiltinType(SemIR::BuiltinKind::BoolType) && @@ -181,22 +188,37 @@ auto HandleFunctionDefinitionStart(Context& context, Parse::Node parse_node) context.PushScope(decl_id); context.AddCurrentCodeBlockToFunction(); - // Bring the parameters into scope. - for (auto param_id : context.node_blocks().Get(function.param_refs_id)) { - auto param = context.nodes().GetAs(param_id); + // Bring the implicit and explicit parameters into scope. + for (auto param_id : llvm::concat( + context.node_blocks().Get(function.implicit_param_refs_id), + context.node_blocks().Get(function.param_refs_id))) { + auto param = context.nodes().Get(param_id); // The parameter types need to be complete. - context.TryToCompleteType(param.type_id, [&] { + context.TryToCompleteType(param.type_id(), [&] { CARBON_DIAGNOSTIC( IncompleteTypeInFunctionParam, Error, "Parameter has incomplete type `{0}` in function definition.", std::string); return context.emitter().Build( - param.parse_node, IncompleteTypeInFunctionParam, - context.sem_ir().StringifyType(param.type_id, true)); + param.parse_node(), IncompleteTypeInFunctionParam, + context.sem_ir().StringifyType(param.type_id(), true)); }); - context.AddNameToLookup(param.parse_node, param.name_id, param_id); + if (auto fn_param = param.TryAs()) { + context.AddNameToLookup(fn_param->parse_node, fn_param->name_id, + param_id); + } else if (auto self_param = param.TryAs()) { + // TODO: This will shadow a local variable named `r#self`, but should + // not. See #2984 and the corresponding code in + // HandleSelfTypeNameExpression. + context.AddNameToLookup(self_param->parse_node, + context.strings().Add(SemIR::SelfParameter::Name), + param_id); + } else { + CARBON_FATAL() << "Unexpected kind of parameter in function definition " + << param; + } } context.node_stack().Push(parse_node, function_id); diff --git a/toolchain/check/handle_name.cpp b/toolchain/check/handle_name.cpp index 982d745d7675..ddc80a479bf8 100644 --- a/toolchain/check/handle_name.cpp +++ b/toolchain/check/handle_name.cpp @@ -235,7 +235,24 @@ auto HandleSelfTypeNameExpression(Context& context, Parse::Node parse_node) } auto HandleSelfValueName(Context& context, Parse::Node parse_node) -> bool { - return context.TODO(parse_node, "HandleSelfValueName"); + context.node_stack().Push(parse_node); + return true; +} + +auto HandleSelfValueNameExpression(Context& context, Parse::Node parse_node) + -> bool { + // TODO: This will find a local variable declared with name `r#self`, but + // should not. See #2984 and the corresponding code in + // HandleFunctionDefinitionStart. + auto name_id = context.strings().Add(SemIR::SelfParameter::Name); + auto value_id = + context.LookupName(parse_node, name_id, SemIR::NameScopeId::Invalid, + /*print_diagnostics=*/true); + auto value = context.nodes().Get(value_id); + context.AddNodeAndPush( + parse_node, + SemIR::NameReference{parse_node, value.type_id(), name_id, value_id}); + return true; } } // namespace Carbon::Check diff --git a/toolchain/check/handle_parameter_list.cpp b/toolchain/check/handle_parameter_list.cpp index ab71f44a35ce..a50457cfbf68 100644 --- a/toolchain/check/handle_parameter_list.cpp +++ b/toolchain/check/handle_parameter_list.cpp @@ -8,12 +8,23 @@ namespace Carbon::Check { auto HandleImplicitParameterList(Context& context, Parse::Node parse_node) -> bool { - return context.TODO(parse_node, "HandleImplicitParameterList"); + auto refs_id = + context.ParamOrArgEnd(Parse::NodeKind::ImplicitParameterListStart); + context.node_stack() + .PopAndDiscardSoloParseNode< + Parse::NodeKind::ImplicitParameterListStart>(); + context.node_stack().Push(parse_node, refs_id); + // The implicit parameter list's scope extends to the end of the following + // parameter list. + return true; } auto HandleImplicitParameterListStart(Context& context, Parse::Node parse_node) -> bool { - return context.TODO(parse_node, "HandleImplicitParameterListStart"); + context.PushScope(); + context.node_stack().Push(parse_node); + context.ParamOrArgStart(); + return true; } auto HandleParameterList(Context& context, Parse::Node parse_node) -> bool { @@ -33,7 +44,18 @@ auto HandleParameterListComma(Context& context, Parse::Node /*parse_node*/) auto HandleParameterListStart(Context& context, Parse::Node parse_node) -> bool { - context.PushScope(); + // A parameter list following an implicit parameter list shares the same + // scope. + // + // TODO: For a declaration like + // + // fn A(T:! type).B(U:! T).C(x: X(U)) { ... } + // + // ... all the earlier parameter should be in scope in the later parameter + // lists too. + if (!context.node_stack().PeekIs()) { + context.PushScope(); + } context.node_stack().Push(parse_node); context.ParamOrArgStart(); return true; diff --git a/toolchain/check/handle_pattern_binding.cpp b/toolchain/check/handle_pattern_binding.cpp index dbcf2049b8cc..094fd4d842f0 100644 --- a/toolchain/check/handle_pattern_binding.cpp +++ b/toolchain/check/handle_pattern_binding.cpp @@ -9,7 +9,18 @@ namespace Carbon::Check { auto HandleAddress(Context& context, Parse::Node parse_node) -> bool { - return context.TODO(parse_node, "HandleAddress"); + auto self_param_id = + context.node_stack().Peek(); + if (auto self_param = + context.nodes().Get(self_param_id).TryAs()) { + self_param->is_addr_self = SemIR::BoolValue::True; + context.nodes().Set(self_param_id, *self_param); + } else { + CARBON_DIAGNOSTIC(AddrOnNonSelfParameter, Error, + "`addr` can only be applied to a `self` parameter"); + context.emitter().Emit(parse_node, AddrOnNonSelfParameter); + } + return true; } auto HandleGenericPatternBinding(Context& context, Parse::Node parse_node) @@ -23,7 +34,27 @@ auto HandlePatternBinding(Context& context, Parse::Node parse_node) -> bool { auto type_node_copy = type_node; auto cast_type_id = ExpressionAsType(context, type_node, parsed_type_id); - // Get the name. + // A `self` binding doesn't have a name. + if (auto self_node = + context.node_stack() + .PopForSoloParseNodeIf()) { + if (context.parse_tree().node_kind(context.node_stack().PeekParseNode()) != + Parse::NodeKind::ImplicitParameterListStart) { + CARBON_DIAGNOSTIC( + SelfOutsideImplicitParameterList, Error, + "`self` can only be declared in an implicit parameter list"); + context.emitter().Emit(parse_node, SelfOutsideImplicitParameterList); + } + context.AddNodeAndPush( + parse_node, + SemIR::SelfParameter{*self_node, cast_type_id, + /*is_addr_self=*/SemIR::BoolValue::False}); + return true; + } + + // TODO: Handle `_` bindings. + + // Every other kind of pattern binding has a name. auto [name_node, name_id] = context.node_stack().PopWithParseNode(); @@ -74,6 +105,7 @@ auto HandlePatternBinding(Context& context, Parse::Node parse_node) -> bool { break; } + case Parse::NodeKind::ImplicitParameterListStart: case Parse::NodeKind::ParameterListStart: // Parameters can have incomplete types in a function declaration, but not // in a function definition. We don't know which kind we have here. diff --git a/toolchain/check/handle_variable.cpp b/toolchain/check/handle_variable.cpp index 15bc3a6ebb15..90372bc5c000 100644 --- a/toolchain/check/handle_variable.cpp +++ b/toolchain/check/handle_variable.cpp @@ -22,25 +22,24 @@ auto HandleVariableDeclaration(Context& context, Parse::Node parse_node) } // Extract the name binding. - SemIR::NodeId bind_name_id = - context.node_stack().Pop(); - auto bind_name = context.nodes().GetAs(bind_name_id); - - // Form a corresponding name in the current context, and bind the name to the - // variable. - context.declaration_name_stack().AddNameToLookup( - context.declaration_name_stack().MakeUnqualifiedName(bind_name.parse_node, - bind_name.name_id), - bind_name_id); + auto value_id = context.node_stack().Pop(); + if (auto bind_name = context.nodes().Get(value_id).TryAs()) { + // Form a corresponding name in the current context, and bind the name to + // the variable. + context.declaration_name_stack().AddNameToLookup( + context.declaration_name_stack().MakeUnqualifiedName( + bind_name->parse_node, bind_name->name_id), + value_id); + value_id = bind_name->value_id; + } // If there was an initializer, assign it to the storage. if (has_init) { - auto var_id = bind_name.value_id; - if (context.nodes().Get(var_id).Is()) { - init_id = Initialize(context, parse_node, var_id, init_id); + if (context.nodes().Get(value_id).Is()) { + init_id = Initialize(context, parse_node, value_id, init_id); // TODO: Consider using different node kinds for assignment versus // initialization. - context.AddNode(SemIR::Assign{parse_node, var_id, init_id}); + context.AddNode(SemIR::Assign{parse_node, value_id, init_id}); } else { // TODO: In a class scope, we should instead save the initializer // somewhere so that we can use it as a default. diff --git a/toolchain/check/node_stack.h b/toolchain/check/node_stack.h index f22d096bc151..c5d9c630ed9d 100644 --- a/toolchain/check/node_stack.h +++ b/toolchain/check/node_stack.h @@ -63,6 +63,12 @@ class NodeStack { stack_.push_back(Entry(parse_node, id)); } + // Returns whether the node on the top of the stack is the specified kind. + template + auto PeekIs() const -> bool { + return parse_tree_->node_kind(PeekParseNode()) == RequiredParseKind; + } + // Pops the top of the stack without any verification. auto PopAndIgnore() -> void { PopEntry(); } @@ -76,6 +82,16 @@ class NodeStack { return back.parse_node; } + // Pops the top of the stack if it is the given kind, and returns the + // parse_node. Otherwise, returns std::nullopt. + template + auto PopForSoloParseNodeIf() -> std::optional { + if (PeekIs()) { + return PopForSoloParseNode(); + } + return std::nullopt; + } + // Pops the top of the stack. template auto PopAndDiscardSoloParseNode() -> void { @@ -140,14 +156,24 @@ class NodeStack { return PopWithParseNode().second; } + // Pops the top of the stack if it has the given kind, and returns the ID. + // Otherwise returns std::nullopt. + template + auto PopIf() -> std::optional())> { + if (PeekIs()) { + return Pop(); + } + return std::nullopt; + } + // Peeks at the parse_node of the given depth in the stack, or by default the // top node. - auto PeekParseNode() -> Parse::Node { return stack_.back().parse_node; } + auto PeekParseNode() const -> Parse::Node { return stack_.back().parse_node; } // Peeks at the ID of node at the given depth in the stack, or by default the // top node. template - auto Peek() -> auto { + auto Peek() const -> auto { Entry back = stack_.back(); RequireParseKind(back.parse_node); constexpr IdKind RequiredIdKind = @@ -271,6 +297,7 @@ class NodeStack { case Parse::NodeKind::PostfixOperator: case Parse::NodeKind::PrefixOperator: case Parse::NodeKind::ReturnType: + case Parse::NodeKind::SelfValueNameExpression: case Parse::NodeKind::ShortCircuitOperand: case Parse::NodeKind::StructFieldValue: case Parse::NodeKind::StructLiteral: @@ -280,6 +307,7 @@ class NodeStack { return IdKind::NodeId; case Parse::NodeKind::IfCondition: case Parse::NodeKind::IfExpressionIf: + case Parse::NodeKind::ImplicitParameterList: case Parse::NodeKind::ParameterList: case Parse::NodeKind::WhileCondition: case Parse::NodeKind::WhileConditionStart: @@ -295,11 +323,13 @@ class NodeStack { case Parse::NodeKind::CodeBlockStart: case Parse::NodeKind::FunctionIntroducer: case Parse::NodeKind::IfStatementElse: + case Parse::NodeKind::ImplicitParameterListStart: case Parse::NodeKind::LetIntroducer: case Parse::NodeKind::ParameterListStart: case Parse::NodeKind::ParenExpressionOrTupleLiteralStart: case Parse::NodeKind::QualifiedDeclaration: case Parse::NodeKind::ReturnStatementStart: + case Parse::NodeKind::SelfValueName: case Parse::NodeKind::StructLiteralOrStructTypeLiteralStart: case Parse::NodeKind::VariableInitializer: case Parse::NodeKind::VariableIntroducer: @@ -353,14 +383,14 @@ class NodeStack { } // Require a Parse::NodeKind be mapped to a particular IdKind. - auto RequireIdKind(Parse::NodeKind parse_kind, IdKind id_kind) -> void { + auto RequireIdKind(Parse::NodeKind parse_kind, IdKind id_kind) const -> void { CARBON_CHECK(ParseNodeKindToIdKind(parse_kind) == id_kind) << "Unexpected IdKind mapping for " << parse_kind; } // Require an entry to have the given Parse::NodeKind. template - auto RequireParseKind(Parse::Node parse_node) -> void { + auto RequireParseKind(Parse::Node parse_node) const -> void { auto actual_kind = parse_tree_->node_kind(parse_node); CARBON_CHECK(RequiredParseKind == actual_kind) << "Expected " << Parse::NodeKind::Create(RequiredParseKind) diff --git a/toolchain/check/testdata/class/fail_addr_not_self.carbon b/toolchain/check/testdata/class/fail_addr_not_self.carbon new file mode 100644 index 000000000000..410bd5fe868f --- /dev/null +++ b/toolchain/check/testdata/class/fail_addr_not_self.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 +// +// AUTOUPDATE + +class Class { + // CHECK:STDERR: fail_addr_not_self.carbon:[[@LINE+3]]:8: ERROR: `addr` can only be applied to a `self` parameter + // CHECK:STDERR: fn F[addr a: Class*](); + // CHECK:STDERR: ^ + fn F[addr a: Class*](); + + // CHECK:STDERR: fail_addr_not_self.carbon:[[@LINE+3]]:8: ERROR: `addr` can only be applied to a `self` parameter + // CHECK:STDERR: fn G(addr b: Class*); + // CHECK:STDERR: ^ + fn G(addr b: Class*); +} + +// CHECK:STDOUT: file "fail_addr_not_self.carbon" { +// CHECK:STDOUT: class_declaration @Class, () +// CHECK:STDOUT: %Class: type = class_type @Class +// CHECK:STDOUT: %.loc17: type = struct_type {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Class { +// CHECK:STDOUT: %F: = fn_decl @F +// CHECK:STDOUT: %G: = fn_decl @G +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: .G = %G +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F[%a: Class*](); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G(%b: Class*); diff --git a/toolchain/check/testdata/class/fail_method.carbon b/toolchain/check/testdata/class/fail_method.carbon new file mode 100644 index 000000000000..5a71ebdd02de --- /dev/null +++ b/toolchain/check/testdata/class/fail_method.carbon @@ -0,0 +1,57 @@ +// 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 +// +// AUTOUPDATE + +class Class { + fn F[self: Class]() -> i32; + + var k: i32; +} + +fn Class.F[self: Class]() -> i32 { + return self.k; +} + +fn Call(c: Class) -> i32 { + // CHECK:STDERR: fail_method.carbon:[[@LINE+3]]:14: ERROR: Semantics TODO: `Call with implicit parameters`. + // CHECK:STDERR: return c.F(); + // CHECK:STDERR: ^ + return c.F(); +} + +// CHECK:STDOUT: file "fail_method.carbon" { +// CHECK:STDOUT: class_declaration @Class, () +// CHECK:STDOUT: %Class: type = class_type @Class +// CHECK:STDOUT: %.loc11: type = struct_type {.k: i32} +// CHECK:STDOUT: %F: = fn_decl @F +// CHECK:STDOUT: %Call: = fn_decl @Call +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Class { +// CHECK:STDOUT: %F: = fn_decl @F +// CHECK:STDOUT: %.loc10_8.1: type = unbound_field_type Class, i32 +// CHECK:STDOUT: %.loc10_8.2: = field "k", member0 +// CHECK:STDOUT: %k: = bind_name "k", %.loc10_8.2 +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: .k = %k +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F[%self: Class]() -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc7: type = ptr_type {.k: i32} +// CHECK:STDOUT: %self.ref: Class = name_reference "self", %self +// CHECK:STDOUT: %.loc14_14.1: ref i32 = class_field_access %self.ref, member0 +// CHECK:STDOUT: %.loc14_14.2: i32 = bind_value %.loc14_14.1 +// CHECK:STDOUT: return %.loc14_14.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Call(%c: Class) -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %c.ref: Class = name_reference "c", %c +// CHECK:STDOUT: %F.ref: = name_reference "F", @Class.%F +// CHECK:STDOUT: return +// CHECK:STDOUT: } diff --git a/toolchain/check/testdata/class/fail_self.carbon b/toolchain/check/testdata/class/fail_self.carbon new file mode 100644 index 000000000000..238debb2725d --- /dev/null +++ b/toolchain/check/testdata/class/fail_self.carbon @@ -0,0 +1,69 @@ +// 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 +// +// AUTOUPDATE + +class Class { + // CHECK:STDERR: fail_self.carbon:[[@LINE+3]]:12: ERROR: `self` can only be declared in an implicit parameter list + // CHECK:STDERR: fn F(self: Class); + // CHECK:STDERR: ^ + fn F(self: Class); + + // CHECK:STDERR: fail_self.carbon:[[@LINE+6]]:10: ERROR: Function returns incomplete type `Class`. + // CHECK:STDERR: fn G() -> Class; + // CHECK:STDERR: ^ + // CHECK:STDERR: fail_self.carbon:[[@LINE-9]]:1: Class is incomplete within its definition. + // CHECK:STDERR: class Class { + // CHECK:STDERR: ^ + fn G() -> Class; +} + +// CHECK:STDERR: fail_self.carbon:[[@LINE+3]]:16: ERROR: `self` can only be declared in an implicit parameter list +// CHECK:STDERR: fn Class.F(self: Class) { +// CHECK:STDERR: ^ +fn Class.F(self: Class) { +} + +fn Class.G() -> Class { + // CHECK:STDERR: fail_self.carbon:[[@LINE+3]]:11: ERROR: `self` can only be declared in an implicit parameter list + // CHECK:STDERR: var self: Class; + // CHECK:STDERR: ^ + var self: Class; + // CHECK:STDERR: fail_self.carbon:[[@LINE+3]]:10: ERROR: Name `self` not found. + // CHECK:STDERR: return self; + // CHECK:STDERR: ^ + return self; +} + +// CHECK:STDOUT: file "fail_self.carbon" { +// CHECK:STDOUT: class_declaration @Class, () +// CHECK:STDOUT: %Class: type = class_type @Class +// CHECK:STDOUT: %.loc20: type = struct_type {} +// CHECK:STDOUT: %F: = fn_decl @F +// CHECK:STDOUT: %G: = fn_decl @G +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Class { +// CHECK:STDOUT: %F: = fn_decl @F +// CHECK:STDOUT: %G: = fn_decl @G +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: .G = %G +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F(%self: Class) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc20: type = tuple_type () +// CHECK:STDOUT: %.loc7: type = ptr_type {} +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G() -> %return: Class { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Class.ref: type = name_reference "Class", file.%Class +// CHECK:STDOUT: %self: Class = self_parameter false +// CHECK:STDOUT: %self.ref: = name_reference "self", +// CHECK:STDOUT: return +// CHECK:STDOUT: } diff --git a/toolchain/check/testdata/class/self.carbon b/toolchain/check/testdata/class/self.carbon new file mode 100644 index 000000000000..a2a43b40e45e --- /dev/null +++ b/toolchain/check/testdata/class/self.carbon @@ -0,0 +1,59 @@ +// 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 +// +// AUTOUPDATE + +class Class { + fn F[self: Class]() -> i32; + fn G[addr self: Class*]() -> i32; + + var n: i32; +} + +fn Class.F[self: Class]() -> i32 { + return self.n; +} + +fn Class.G[addr self: Class*]() -> i32 { + return (*self).n; +} + +// CHECK:STDOUT: file "self.carbon" { +// CHECK:STDOUT: class_declaration @Class, () +// CHECK:STDOUT: %Class: type = class_type @Class +// CHECK:STDOUT: %.loc12: type = struct_type {.n: i32} +// CHECK:STDOUT: %F: = fn_decl @F +// CHECK:STDOUT: %G: = fn_decl @G +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Class { +// CHECK:STDOUT: %F: = fn_decl @F +// CHECK:STDOUT: %G: = fn_decl @G +// CHECK:STDOUT: %.loc11_8.1: type = unbound_field_type Class, i32 +// CHECK:STDOUT: %.loc11_8.2: = field "n", member0 +// CHECK:STDOUT: %n: = bind_name "n", %.loc11_8.2 +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: .G = %G +// CHECK:STDOUT: .n = %n +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F[%self: Class]() -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc7: type = ptr_type {.n: i32} +// CHECK:STDOUT: %self.ref: Class = name_reference "self", %self +// CHECK:STDOUT: %.loc15_14.1: ref i32 = class_field_access %self.ref, member0 +// CHECK:STDOUT: %.loc15_14.2: i32 = bind_value %.loc15_14.1 +// CHECK:STDOUT: return %.loc15_14.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G[%self.addr: Class*]() -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %self.ref: Class* = name_reference "self", %self.addr +// CHECK:STDOUT: %.loc19_11: ref Class = dereference %self.ref +// CHECK:STDOUT: %.loc19_17.1: ref i32 = class_field_access %.loc19_11, member0 +// CHECK:STDOUT: %.loc19_17.2: i32 = bind_value %.loc19_17.1 +// CHECK:STDOUT: return %.loc19_17.2 +// CHECK:STDOUT: } diff --git a/toolchain/check/testdata/function/call/fail_not_callable.carbon b/toolchain/check/testdata/function/call/fail_not_callable.carbon index fbf5ddb10517..4395006adb90 100644 --- a/toolchain/check/testdata/function/call/fail_not_callable.carbon +++ b/toolchain/check/testdata/function/call/fail_not_callable.carbon @@ -5,12 +5,9 @@ // AUTOUPDATE fn Run() { - // CHECK:STDERR: fail_not_callable.carbon:[[@LINE+6]]:24: ERROR: Semantics TODO: `Not a callable name`. + // CHECK:STDERR: fail_not_callable.carbon:[[@LINE+3]]:24: ERROR: Semantics TODO: `Not a callable name`. // CHECK:STDERR: var x: i32 = "hello"(); // CHECK:STDERR: ^ - // CHECK:STDERR: fail_not_callable.carbon:[[@LINE+3]]:25: ERROR: Cannot implicitly convert from `String` to `i32`. - // CHECK:STDERR: var x: i32 = "hello"(); - // CHECK:STDERR: ^ var x: i32 = "hello"(); } @@ -23,7 +20,7 @@ fn Run() { // CHECK:STDOUT: %x.var: ref i32 = var "x" // CHECK:STDOUT: %x: ref i32 = bind_name "x", %x.var // CHECK:STDOUT: %.1: type = ptr_type String -// CHECK:STDOUT: %.loc14: String = string_literal "hello" +// CHECK:STDOUT: %.loc11: String = string_literal "hello" // CHECK:STDOUT: assign %x.var, // CHECK:STDOUT: return // CHECK:STDOUT: } diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index c60887593e6c..666bc8019cd4 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -111,6 +111,7 @@ CARBON_DIAGNOSTIC_KIND(SemanticsTodo) CARBON_DIAGNOSTIC_KIND(AddressOfEphemeralReference) CARBON_DIAGNOSTIC_KIND(AddressOfNonReference) +CARBON_DIAGNOSTIC_KIND(AddrOnNonSelfParameter) CARBON_DIAGNOSTIC_KIND(ArrayInitFromLiteralArgCountMismatch) CARBON_DIAGNOSTIC_KIND(ArrayInitFromExpressionArgCountMismatch) CARBON_DIAGNOSTIC_KIND(AssignmentToNonAssignable) @@ -144,6 +145,7 @@ CARBON_DIAGNOSTIC_KIND(InvalidArrayExpression) CARBON_DIAGNOSTIC_KIND(TypeNotIndexable) CARBON_DIAGNOSTIC_KIND(IndexOutOfBounds) CARBON_DIAGNOSTIC_KIND(InvalidMainRunSignature) +CARBON_DIAGNOSTIC_KIND(SelfOutsideImplicitParameterList) CARBON_DIAGNOSTIC_KIND(StructInitElementCountMismatch) CARBON_DIAGNOSTIC_KIND(StructInitFieldNameMismatch) CARBON_DIAGNOSTIC_KIND(TupleIndexIntegerLiteral) diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index 01f59020259b..fce28b6035d9 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -60,6 +60,8 @@ auto FileContext::BuildFunctionDeclaration(SemIR::FunctionId function_id) -> llvm::Function* { const auto& function = sem_ir().functions().Get(function_id); const bool has_return_slot = function.return_slot_id.is_valid(); + auto implicit_param_refs = + sem_ir().node_blocks().Get(function.implicit_param_refs_id); auto param_refs = sem_ir().node_blocks().Get(function.param_refs_id); SemIR::InitializingRepresentation return_rep = @@ -76,13 +78,16 @@ auto FileContext::BuildFunctionDeclaration(SemIR::FunctionId function_id) // out a mechanism to compute the mapping between parameters and arguments on // demand. llvm::SmallVector param_node_ids; - param_types.reserve(has_return_slot + param_refs.size()); - param_node_ids.reserve(has_return_slot + param_refs.size()); + auto max_llvm_params = + has_return_slot + implicit_param_refs.size() + param_refs.size(); + param_types.reserve(max_llvm_params); + param_node_ids.reserve(max_llvm_params); if (has_return_slot) { param_types.push_back(GetType(function.return_type_id)->getPointerTo()); param_node_ids.push_back(function.return_slot_id); } - for (auto param_ref_id : param_refs) { + for (auto param_ref_id : + llvm::concat(implicit_param_refs, param_refs)) { auto param_type_id = sem_ir().nodes().Get(param_ref_id).type_id(); switch (auto value_rep = SemIR::GetValueRepresentation(sem_ir(), param_type_id); @@ -126,13 +131,15 @@ auto FileContext::BuildFunctionDeclaration(SemIR::FunctionId function_id) // Set up parameters and the return slot. for (auto [node_id, arg] : llvm::zip_equal(param_node_ids, llvm_function->args())) { + auto node = sem_ir().nodes().Get(node_id); if (node_id == function.return_slot_id) { arg.setName("return"); arg.addAttr(llvm::Attribute::getWithStructRetType( llvm_context(), GetType(function.return_type_id))); + } else if (node.Is()) { + arg.setName("self"); } else { - arg.setName(sem_ir().strings().Get( - sem_ir().nodes().GetAs(node_id).name_id)); + arg.setName(sem_ir().strings().Get(node.As().name_id)); } } @@ -157,6 +164,8 @@ auto FileContext::BuildFunctionDefinition(SemIR::FunctionId function_id) // TODO: This duplicates the mapping between semantics nodes and LLVM // function parameters that was already computed in BuildFunctionDeclaration. // We should only do that once. + auto implicit_param_refs = + sem_ir().node_blocks().Get(function.implicit_param_refs_id); auto param_refs = sem_ir().node_blocks().Get(function.param_refs_id); int param_index = 0; if (has_return_slot) { @@ -164,7 +173,8 @@ auto FileContext::BuildFunctionDefinition(SemIR::FunctionId function_id) llvm_function->getArg(param_index)); ++param_index; } - for (auto param_ref_id : param_refs) { + for (auto param_ref_id : + llvm::concat(implicit_param_refs, param_refs)) { auto param_type_id = sem_ir().nodes().Get(param_ref_id).type_id(); if (SemIR::GetValueRepresentation(sem_ir(), param_type_id).kind == SemIR::ValueRepresentation::None) { diff --git a/toolchain/lower/handle.cpp b/toolchain/lower/handle.cpp index 60c00f6c8d55..3f95488aa19a 100644 --- a/toolchain/lower/handle.cpp +++ b/toolchain/lower/handle.cpp @@ -378,6 +378,12 @@ auto HandleReturnExpression(FunctionContext& context, SemIR::NodeId /*node_id*/, } } +auto HandleSelfParameter(FunctionContext& /*context*/, + SemIR::NodeId /*node_id*/, + SemIR::SelfParameter /*node*/) -> void { + CARBON_FATAL() << "Parameters should be lowered by `BuildFunctionDefinition`"; +} + auto HandleSpliceBlock(FunctionContext& context, SemIR::NodeId node_id, SemIR::SpliceBlock node) -> void { context.LowerBlock(node.block_id); diff --git a/toolchain/lower/testdata/class/self.carbon b/toolchain/lower/testdata/class/self.carbon new file mode 100644 index 000000000000..7eb464bc7d0e --- /dev/null +++ b/toolchain/lower/testdata/class/self.carbon @@ -0,0 +1,35 @@ +// 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 +// +// AUTOUPDATE + +class C { + var a: i32; + + fn Get[self: C]() -> i32; + fn Set[addr self: C*](); +} + +fn C.Get[self: C]() -> i32 { + return self.a; +} + +fn C.Set[addr self: C*]() { + (*self).a = 1; +} + +// CHECK:STDOUT: ; ModuleID = 'self.carbon' +// CHECK:STDOUT: source_filename = "self.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: define i32 @Get(ptr %self) { +// CHECK:STDOUT: %a = getelementptr inbounds { i32 }, ptr %self, i32 0, i32 0 +// CHECK:STDOUT: %1 = load i32, ptr %a, align 4 +// CHECK:STDOUT: ret i32 %1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: define void @Set(ptr %self) { +// CHECK:STDOUT: %a = getelementptr inbounds { i32 }, ptr %self, i32 0, i32 0 +// CHECK:STDOUT: store i32 1, ptr %a, align 4 +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } diff --git a/toolchain/parse/handle_expression.cpp b/toolchain/parse/handle_expression.cpp index c194feac95f0..549239803c14 100644 --- a/toolchain/parse/handle_expression.cpp +++ b/toolchain/parse/handle_expression.cpp @@ -109,7 +109,7 @@ auto HandleExpressionInPostfix(Context& context) -> void { break; } case Lex::TokenKind::SelfValueIdentifier: { - context.AddLeafNode(NodeKind::SelfValueName, context.Consume()); + context.AddLeafNode(NodeKind::SelfValueNameExpression, context.Consume()); context.PushState(state); break; } diff --git a/toolchain/parse/handle_pattern.cpp b/toolchain/parse/handle_pattern.cpp index be0f658c9e89..c5656e9713bd 100644 --- a/toolchain/parse/handle_pattern.cpp +++ b/toolchain/parse/handle_pattern.cpp @@ -51,17 +51,17 @@ static auto HandlePattern(Context& context, Context::PatternKind pattern_kind) context.PushState(state); }; - // The first item should be an identifier or, in an implicit parameter list, - // `self`. + // The first item should be an identifier or `self`. bool has_name = false; if (auto identifier = context.ConsumeIf(Lex::TokenKind::Identifier)) { context.AddLeafNode(NodeKind::Name, *identifier); has_name = true; - } else if (pattern_kind == Context::PatternKind::ImplicitParameter) { - if (auto self = context.ConsumeIf(Lex::TokenKind::SelfValueIdentifier)) { - context.AddLeafNode(NodeKind::SelfValueName, *self); - has_name = true; - } + } else if (auto self = + context.ConsumeIf(Lex::TokenKind::SelfValueIdentifier)) { + // Checking will validate the `self` is only declared in the implicit + // parameter list of a function. + context.AddLeafNode(NodeKind::SelfValueName, *self); + has_name = true; } if (!has_name) { // Add a placeholder for the name. diff --git a/toolchain/parse/node_kind.def b/toolchain/parse/node_kind.def index 68d8e56410de..25dd98de6347 100644 --- a/toolchain/parse/node_kind.def +++ b/toolchain/parse/node_kind.def @@ -181,7 +181,7 @@ CARBON_PARSE_NODE_KIND_BRACKET(ArrayExpression, ArrayExpressionSemi, CARBON_TOKEN(CloseSquareBracket)) // A pattern binding, such as `name: Type`: -// Name +// Name or SelfValueName // _external_: type expression // [Generic]PatternBinding // _optional_ Address @@ -574,10 +574,13 @@ CARBON_PARSE_NODE_KIND_BRACKET(NamedConstraintDeclaration, // The `self` value and `Self` type identifier keywords. Typically of the form // `self: Self`: // SelfValueName +// SelfValueNameExpression // SelfTypeNameExpression // PatternBinding CARBON_PARSE_NODE_KIND_CHILD_COUNT(SelfValueName, 0, CARBON_TOKEN(SelfValueIdentifier)) +CARBON_PARSE_NODE_KIND_CHILD_COUNT(SelfValueNameExpression, 0, + CARBON_TOKEN(SelfValueIdentifier)) CARBON_PARSE_NODE_KIND_CHILD_COUNT(SelfTypeNameExpression, 0, CARBON_TOKEN(SelfTypeIdentifier)) diff --git a/toolchain/parse/testdata/class/fn_definitions.carbon b/toolchain/parse/testdata/class/fn_definitions.carbon index fb2a7bcb29b7..2e0ece98eeb3 100644 --- a/toolchain/parse/testdata/class/fn_definitions.carbon +++ b/toolchain/parse/testdata/class/fn_definitions.carbon @@ -47,7 +47,7 @@ class Foo { // CHECK:STDOUT: {kind: 'ReturnType', text: '->', subtree_size: 2}, // CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 12}, // CHECK:STDOUT: {kind: 'ReturnStatementStart', text: 'return'}, -// CHECK:STDOUT: {kind: 'SelfValueName', text: 'self'}, +// CHECK:STDOUT: {kind: 'SelfValueNameExpression', text: 'self'}, // CHECK:STDOUT: {kind: 'Name', text: 'x'}, // CHECK:STDOUT: {kind: 'MemberAccessExpression', text: '.', subtree_size: 3}, // CHECK:STDOUT: {kind: 'ReturnStatement', text: ';', subtree_size: 5}, @@ -64,7 +64,7 @@ class Foo { // CHECK:STDOUT: {kind: 'ParameterListStart', text: '('}, // CHECK:STDOUT: {kind: 'ParameterList', text: ')', subtree_size: 2}, // CHECK:STDOUT: {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 12}, -// CHECK:STDOUT: {kind: 'SelfValueName', text: 'self'}, +// CHECK:STDOUT: {kind: 'SelfValueNameExpression', text: 'self'}, // CHECK:STDOUT: {kind: 'Name', text: 'x'}, // CHECK:STDOUT: {kind: 'PointerMemberAccessExpression', text: '->', subtree_size: 3}, // CHECK:STDOUT: {kind: 'Literal', text: '1'}, diff --git a/toolchain/sem_ir/file.cpp b/toolchain/sem_ir/file.cpp index 5e22e067bccd..604b65da1b9c 100644 --- a/toolchain/sem_ir/file.cpp +++ b/toolchain/sem_ir/file.cpp @@ -208,6 +208,7 @@ static auto GetTypePrecedence(NodeKind kind) -> int { case RealLiteral::Kind: case Return::Kind: case ReturnExpression::Kind: + case SelfParameter::Kind: case SpliceBlock::Kind: case StringLiteral::Kind: case StructAccess::Kind: @@ -407,6 +408,7 @@ auto File::StringifyTypeExpression(NodeId outer_node_id, case RealLiteral::Kind: case Return::Kind: case ReturnExpression::Kind: + case SelfParameter::Kind: case SpliceBlock::Kind: case StringLiteral::Kind: case StructAccess::Kind: @@ -495,6 +497,7 @@ auto GetExpressionCategory(const File& file, NodeId node_id) case Parameter::Kind: case PointerType::Kind: case RealLiteral::Kind: + case SelfParameter::Kind: case StringLiteral::Kind: case StructValue::Kind: case StructType::Kind: diff --git a/toolchain/sem_ir/file.h b/toolchain/sem_ir/file.h index 79553fefc6a2..08ab1f9927d1 100644 --- a/toolchain/sem_ir/file.h +++ b/toolchain/sem_ir/file.h @@ -40,6 +40,8 @@ struct Function : public Printable { // The definition, if the function has been defined or is currently being // defined. This is a FunctionDeclaration. NodeId definition_id = NodeId::Invalid; + // A block containing a single reference node per implicit parameter. + NodeBlockId implicit_param_refs_id; // A block containing a single reference node per parameter. NodeBlockId param_refs_id; // The return type. This will be invalid if the return type wasn't specified. diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index ea3ff75010c9..39f8559e585b 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -54,6 +54,7 @@ class NodeNamer { GetScopeInfo(fn_scope).name = globals.AllocateName( *this, fn_loc, fn.name_id.is_valid() ? sem_ir.strings().Get(fn.name_id).str() : ""); + CollectNamesInBlock(fn_scope, fn.implicit_param_refs_id); CollectNamesInBlock(fn_scope, fn.param_refs_id); if (fn.return_slot_id.is_valid()) { nodes[fn.return_slot_id.index] = { @@ -434,6 +435,12 @@ class NodeNamer { add_node_name_id(node.As().name_id); continue; } + case SelfParameter::Kind: { + add_node_name(node.As().is_addr_self.index + ? "self.addr" + : "self"); + continue; + } case VarStorage::Kind: { add_node_name_id(node.As().name_id, ".var"); continue; @@ -517,22 +524,19 @@ class Formatter { out_ << "\nfn "; FormatFunctionName(id); - out_ << "("; llvm::SaveAndRestore function_scope(scope_, node_namer_.GetScopeFor(id)); - llvm::ListSeparator sep; - for (const NodeId param_id : sem_ir_.node_blocks().Get(fn.param_refs_id)) { - out_ << sep; - if (!param_id.is_valid()) { - out_ << "invalid"; - continue; - } - FormatNodeName(param_id); - out_ << ": "; - FormatType(sem_ir_.nodes().Get(param_id).type_id()); + if (fn.implicit_param_refs_id != SemIR::NodeBlockId::Empty) { + out_ << "["; + FormatParameterList(fn.implicit_param_refs_id); + out_ << "]"; } + + out_ << "("; + FormatParameterList(fn.param_refs_id); out_ << ")"; + if (fn.return_type_id.is_valid()) { out_ << " -> "; if (fn.return_slot_id.is_valid()) { @@ -560,6 +564,20 @@ class Formatter { } } + auto FormatParameterList(NodeBlockId param_refs_id) -> void { + llvm::ListSeparator sep; + for (const NodeId param_id : sem_ir_.node_blocks().Get(param_refs_id)) { + out_ << sep; + if (!param_id.is_valid()) { + out_ << "invalid"; + continue; + } + FormatNodeName(param_id); + out_ << ": "; + FormatType(sem_ir_.nodes().Get(param_id).type_id()); + } + } + auto FormatCodeBlock(NodeBlockId block_id) -> void { if (!block_id.is_valid()) { return; diff --git a/toolchain/sem_ir/node_kind.def b/toolchain/sem_ir/node_kind.def index b79414c16548..771f4b931325 100644 --- a/toolchain/sem_ir/node_kind.def +++ b/toolchain/sem_ir/node_kind.def @@ -50,6 +50,7 @@ CARBON_SEM_IR_NODE_KIND(PointerType) CARBON_SEM_IR_NODE_KIND(RealLiteral) CARBON_SEM_IR_NODE_KIND(ReturnExpression) CARBON_SEM_IR_NODE_KIND(Return) +CARBON_SEM_IR_NODE_KIND(SelfParameter) CARBON_SEM_IR_NODE_KIND(SpliceBlock) CARBON_SEM_IR_NODE_KIND(StringLiteral) CARBON_SEM_IR_NODE_KIND(StructAccess) diff --git a/toolchain/sem_ir/typed_nodes.h b/toolchain/sem_ir/typed_nodes.h index bae697f9bdb7..fd2e69ec1f4e 100644 --- a/toolchain/sem_ir/typed_nodes.h +++ b/toolchain/sem_ir/typed_nodes.h @@ -342,6 +342,15 @@ struct ReturnExpression { NodeId expr_id; }; +struct SelfParameter { + static constexpr auto Kind = NodeKind::SelfParameter.Define("self_parameter"); + static constexpr llvm::StringLiteral Name = "self"; + + Parse::Node parse_node; + TypeId type_id; + BoolValue is_addr_self; +}; + struct SpliceBlock { static constexpr auto Kind = NodeKind::SpliceBlock.Define("splice_block");