mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-06 09:34:42 +01:00
Support for functions with a self parameter. (#3338)
So far, such functions can only be defined; calls are not supported yet.
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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*);
|
||||
@@ -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,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,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: }
|
||||
@@ -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: }
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,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: }
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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'},
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user