Просмотр исходного кода

Support for functions with a `self` parameter. (#3338)

So far, such functions can only be defined; calls are not supported yet.
Richard Smith 2 лет назад
Родитель
Сommit
04ae5a0531

+ 1 - 0
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:

+ 15 - 1
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<SemIR::SelfParameter>()) {
+      // 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,

+ 30 - 8
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<Parse::NodeKind::ParameterList>();
+  SemIR::NodeBlockId implicit_param_refs_id =
+      context.node_stack()
+          .PopIf<Parse::NodeKind::ImplicitParameterList>()
+          .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<SemIR::Parameter>(param_id);
+  // Bring the implicit and explicit parameters into scope.
+  for (auto param_id : llvm::concat<SemIR::NodeId>(
+           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<SemIR::Parameter>()) {
+      context.AddNameToLookup(fn_param->parse_node, fn_param->name_id,
+                              param_id);
+    } else if (auto self_param = param.TryAs<SemIR::SelfParameter>()) {
+      // 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);

+ 18 - 1
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

+ 25 - 3
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<Parse::NodeKind::ImplicitParameterList>()) {
+    context.PushScope();
+  }
   context.node_stack().Push(parse_node);
   context.ParamOrArgStart();
   return true;

+ 34 - 2
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<Parse::NodeKind::PatternBinding>();
+  if (auto self_param =
+          context.nodes().Get(self_param_id).TryAs<SemIR::SelfParameter>()) {
+    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<Parse::NodeKind::SelfValueName>()) {
+    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<Parse::NodeKind::Name>();
 
@@ -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.

+ 13 - 14
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<Parse::NodeKind::PatternBinding>();
-  auto bind_name = context.nodes().GetAs<SemIR::BindName>(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<Parse::NodeKind::PatternBinding>();
+  if (auto bind_name = context.nodes().Get(value_id).TryAs<SemIR::BindName>()) {
+    // 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<SemIR::VarStorage>()) {
-      init_id = Initialize(context, parse_node, var_id, init_id);
+    if (context.nodes().Get(value_id).Is<SemIR::VarStorage>()) {
+      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.

+ 34 - 4
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 <Parse::NodeKind::RawEnumType RequiredParseKind>
+  auto PeekIs() const -> bool {
+    return parse_tree_->node_kind(PeekParseNode()) == RequiredParseKind;
+  }
+
   // Pops the top of the stack without any verification.
   auto PopAndIgnore() -> void { PopEntry<SemIR::NodeId>(); }
 
@@ -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 <Parse::NodeKind::RawEnumType RequiredParseKind>
+  auto PopForSoloParseNodeIf() -> std::optional<Parse::Node> {
+    if (PeekIs<RequiredParseKind>()) {
+      return PopForSoloParseNode<RequiredParseKind>();
+    }
+    return std::nullopt;
+  }
+
   // Pops the top of the stack.
   template <Parse::NodeKind::RawEnumType RequiredParseKind>
   auto PopAndDiscardSoloParseNode() -> void {
@@ -140,14 +156,24 @@ class NodeStack {
     return PopWithParseNode<RequiredParseKind>().second;
   }
 
+  // Pops the top of the stack if it has the given kind, and returns the ID.
+  // Otherwise returns std::nullopt.
+  template <Parse::NodeKind::RawEnumType RequiredParseKind>
+  auto PopIf() -> std::optional<decltype(Pop<RequiredParseKind>())> {
+    if (PeekIs<RequiredParseKind>()) {
+      return Pop<RequiredParseKind>();
+    }
+    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 <Parse::NodeKind::RawEnumType RequiredParseKind>
-  auto Peek() -> auto {
+  auto Peek() const -> auto {
     Entry back = stack_.back();
     RequireParseKind<RequiredParseKind>(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 <Parse::NodeKind::RawEnumType RequiredParseKind>
-  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)

+ 36 - 0
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: <function> = fn_decl @F
+// CHECK:STDOUT:   %G: <function> = 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*);

+ 57 - 0
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: <function> = fn_decl @F
+// CHECK:STDOUT:   %Call: <function> = fn_decl @Call
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Class {
+// CHECK:STDOUT:   %F: <function> = fn_decl @F
+// CHECK:STDOUT:   %.loc10_8.1: type = unbound_field_type Class, i32
+// CHECK:STDOUT:   %.loc10_8.2: <unbound field of class Class> = field "k", member0
+// CHECK:STDOUT:   %k: <unbound field of class Class> = 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: <function> = name_reference "F", @Class.%F
+// CHECK:STDOUT:   return <error>
+// CHECK:STDOUT: }

+ 69 - 0
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: <function> = fn_decl @F
+// CHECK:STDOUT:   %G: <function> = fn_decl @G
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Class {
+// CHECK:STDOUT:   %F: <function> = fn_decl @F
+// CHECK:STDOUT:   %G: <function> = 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: <error> = name_reference "self", <error>
+// CHECK:STDOUT:   return <error>
+// CHECK:STDOUT: }

+ 59 - 0
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: <function> = fn_decl @F
+// CHECK:STDOUT:   %G: <function> = fn_decl @G
+// CHECK:STDOUT: }
+// CHECK:STDOUT:
+// CHECK:STDOUT: class @Class {
+// CHECK:STDOUT:   %F: <function> = fn_decl @F
+// CHECK:STDOUT:   %G: <function> = fn_decl @G
+// CHECK:STDOUT:   %.loc11_8.1: type = unbound_field_type Class, i32
+// CHECK:STDOUT:   %.loc11_8.2: <unbound field of class Class> = field "n", member0
+// CHECK:STDOUT:   %n: <unbound field of class Class> = 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: }

+ 2 - 5
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, <error>
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }

+ 2 - 0
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)

+ 16 - 6
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<SemIR::NodeId> 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<const SemIR::NodeId>(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<SemIR::SelfParameter>()) {
+      arg.setName("self");
     } else {
-      arg.setName(sem_ir().strings().Get(
-          sem_ir().nodes().GetAs<SemIR::Parameter>(node_id).name_id));
+      arg.setName(sem_ir().strings().Get(node.As<SemIR::Parameter>().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<const SemIR::NodeId>(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) {

+ 6 - 0
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);

+ 35 - 0
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: }

+ 1 - 1
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;
     }

+ 7 - 7
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.

+ 4 - 1
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))
 

+ 2 - 2
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'},

+ 3 - 0
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:

+ 2 - 0
toolchain/sem_ir/file.h

@@ -40,6 +40,8 @@ struct Function : public Printable<Function> {
   // 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.

+ 29 - 11
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<Parameter>().name_id);
           continue;
         }
+        case SelfParameter::Kind: {
+          add_node_name(node.As<SelfParameter>().is_addr_self.index
+                            ? "self.addr"
+                            : "self");
+          continue;
+        }
         case VarStorage::Kind: {
           add_node_name_id(node.As<VarStorage>().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;

+ 1 - 0
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)

+ 9 - 0
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");