From bc8db9406d3108cc27539ab8f2ca44f7b87b7eb4 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 31 Oct 2023 12:59:50 -0700 Subject: [PATCH] Support for `Self` expressions. (#3351) --- toolchain/check/BUILD | 1 + toolchain/check/handle_class.cpp | 14 ++++- toolchain/check/handle_name.cpp | 15 +++++- toolchain/check/node_stack.h | 1 + .../check/testdata/class/self_type.carbon | 53 +++++++++++++++++++ 5 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 toolchain/check/testdata/class/self_type.carbon diff --git a/toolchain/check/BUILD b/toolchain/check/BUILD index 4b0da7b24093..be0cd40a1e21 100644 --- a/toolchain/check/BUILD +++ b/toolchain/check/BUILD @@ -58,6 +58,7 @@ cc_library( "//toolchain/base:value_store", "//toolchain/diagnostics:diagnostic_emitter", "//toolchain/diagnostics:diagnostic_kind", + "//toolchain/lex:token_kind", "//toolchain/lex:tokenized_buffer", "//toolchain/parse:node_kind", "//toolchain/parse:tree", diff --git a/toolchain/check/handle_class.cpp b/toolchain/check/handle_class.cpp index e2b8fe31e239..93a9a9ce5189 100644 --- a/toolchain/check/handle_class.cpp +++ b/toolchain/check/handle_class.cpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include "toolchain/check/context.h" +#include "toolchain/lex/token_kind.h" namespace Carbon::Check { @@ -98,12 +99,21 @@ auto HandleClassDefinitionStart(Context& context, Parse::Node parse_node) } else { class_info.definition_id = class_decl_id; class_info.scope_id = context.name_scopes().Add(); - - // TODO: Introduce `Self`. } // Enter the class scope. context.PushScope(class_decl_id, class_info.scope_id); + + // Introduce `Self`. + // TODO: This will shadow a local variable declared with name `r#Self`, but + // should not. See #2984 and the corresponding code in + // HandleSelfTypeNameExpression. + context.AddNameToLookup( + parse_node, + context.strings().Add( + Lex::TokenKind::SelfTypeIdentifier.fixed_spelling()), + context.sem_ir().GetTypeAllowBuiltinTypes(class_info.self_type_id)); + context.node_block_stack().Push(); context.node_stack().Push(parse_node, class_id); context.args_type_info_stack().Push(); diff --git a/toolchain/check/handle_name.cpp b/toolchain/check/handle_name.cpp index 137a68a4a010..b361112ec998 100644 --- a/toolchain/check/handle_name.cpp +++ b/toolchain/check/handle_name.cpp @@ -5,6 +5,7 @@ #include "llvm/ADT/STLExtras.h" #include "toolchain/check/context.h" #include "toolchain/check/convert.h" +#include "toolchain/lex/token_kind.h" #include "toolchain/sem_ir/node.h" namespace Carbon::Check { @@ -258,7 +259,19 @@ auto HandleQualifiedDeclaration(Context& context, Parse::Node parse_node) auto HandleSelfTypeNameExpression(Context& context, Parse::Node parse_node) -> bool { - return context.TODO(parse_node, "HandleSelfTypeNameExpression"); + // TODO: This will find a local variable declared with name `r#Self`, but + // should not. See #2984 and the corresponding code in + // HandleClassDefinitionStart. + auto name_id = context.strings().Add( + Lex::TokenKind::SelfTypeIdentifier.fixed_spelling()); + 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; } auto HandleSelfValueName(Context& context, Parse::Node parse_node) -> bool { diff --git a/toolchain/check/node_stack.h b/toolchain/check/node_stack.h index c5d9c630ed9d..638d43b4a86c 100644 --- a/toolchain/check/node_stack.h +++ b/toolchain/check/node_stack.h @@ -297,6 +297,7 @@ class NodeStack { case Parse::NodeKind::PostfixOperator: case Parse::NodeKind::PrefixOperator: case Parse::NodeKind::ReturnType: + case Parse::NodeKind::SelfTypeNameExpression: case Parse::NodeKind::SelfValueNameExpression: case Parse::NodeKind::ShortCircuitOperand: case Parse::NodeKind::StructFieldValue: diff --git a/toolchain/check/testdata/class/self_type.carbon b/toolchain/check/testdata/class/self_type.carbon new file mode 100644 index 000000000000..472bc2877d77 --- /dev/null +++ b/toolchain/check/testdata/class/self_type.carbon @@ -0,0 +1,53 @@ +// 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: Self]() -> i32; + var p: Self*; +} + +// TODO: Reenter the class scope when defining methods, so that we can support +// `Self` here too. +// fn Class.F[self: Self]() -> i32 { +fn Class.F[self: Class]() -> i32 { + return (*self.p).F(); +} + +// CHECK:STDOUT: file "self_type.carbon" { +// CHECK:STDOUT: class_declaration @Class, () +// CHECK:STDOUT: %Class: type = class_type @Class +// CHECK:STDOUT: %.loc10: type = struct_type {.p: Class*} +// CHECK:STDOUT: %F: = fn_decl @F +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @Class { +// CHECK:STDOUT: %F: = fn_decl @F +// CHECK:STDOUT: %Self.ref: type = name_reference "Self", file.%Class +// CHECK:STDOUT: %.loc9_14: type = ptr_type Class +// CHECK:STDOUT: %.loc9_8.1: type = unbound_field_type Class, Class* +// CHECK:STDOUT: %.loc9_8.2: = field "p", member0 +// CHECK:STDOUT: %p: = bind_name "p", %.loc9_8.2 +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .F = %F +// CHECK:STDOUT: .p = %p +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F[%self: Class]() -> i32 { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc7: type = ptr_type {.p: Class*} +// CHECK:STDOUT: %self.ref: Class = name_reference "self", %self +// CHECK:STDOUT: %.loc16_16.1: ref Class* = class_field_access %self.ref, member0 +// CHECK:STDOUT: %.loc16_16.2: Class* = bind_value %.loc16_16.1 +// CHECK:STDOUT: %.loc16_11.1: ref Class = dereference %.loc16_16.2 +// CHECK:STDOUT: %.loc16_19: = bound_method %.loc16_11.1, @Class.%F +// CHECK:STDOUT: %.loc16_11.2: Class = bind_value %.loc16_11.1 +// CHECK:STDOUT: %.loc16_21.1: init i32 = call %.loc16_19(%.loc16_11.2) +// CHECK:STDOUT: %.loc16_21.2: ref i32 = temporary_storage +// CHECK:STDOUT: %.loc16_21.3: ref i32 = temporary %.loc16_21.2, %.loc16_21.1 +// CHECK:STDOUT: %.loc16_21.4: i32 = bind_value %.loc16_21.3 +// CHECK:STDOUT: return %.loc16_21.4 +// CHECK:STDOUT: }