diff --git a/toolchain/lowering/testdata/function/declaration/simple.carbon b/toolchain/lowering/testdata/function/declaration/simple.carbon new file mode 100644 index 000000000000..7fbb6ee0efee --- /dev/null +++ b/toolchain/lowering/testdata/function/declaration/simple.carbon @@ -0,0 +1,20 @@ +// 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 +// CHECK:STDOUT: ; ModuleID = 'simple.carbon' +// CHECK:STDOUT: source_filename = "simple.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: %EmptyTupleType = type {} +// CHECK:STDOUT: +// CHECK:STDOUT: declare %EmptyTupleType @F(i32) +// CHECK:STDOUT: +// CHECK:STDOUT: define %EmptyTupleType @G(i32 %n) { +// CHECK:STDOUT: %F = call %EmptyTupleType @F(i32 %n) +// CHECK:STDOUT: ret void +// CHECK:STDOUT: } + +fn F(n: i32); + +fn G(n: i32) { F(n); } diff --git a/toolchain/semantics/semantics_handle_function.cpp b/toolchain/semantics/semantics_handle_function.cpp index 5d2c473b0029..fda64ee3acdf 100644 --- a/toolchain/semantics/semantics_handle_function.cpp +++ b/toolchain/semantics/semantics_handle_function.cpp @@ -6,9 +6,46 @@ namespace Carbon { +// Build a FunctionDeclaration describing the signature of a function. This +// handles the common logic shared by function declaration syntax and function +// definition syntax. +static auto BuildFunctionDeclaration(SemanticsContext& context) + -> std::pair { + SemanticsTypeId return_type_id = SemanticsTypeId::Invalid; + if (context.parse_tree().node_kind(context.node_stack().PeekParseNode()) == + ParseNodeKind::ReturnType) { + return_type_id = + context.node_stack().Pop(ParseNodeKind::ReturnType); + } else { + // Canonicalize the empty tuple for the implicit return. + context.CanonicalizeType(SemanticsNodeId::BuiltinEmptyTupleType); + } + auto param_refs_id = context.node_stack().Pop( + ParseNodeKind::ParameterList); + auto name_context = context.PopDeclarationName(); + auto fn_node = context.node_stack().PopForSoloParseNode( + ParseNodeKind::FunctionIntroducer); + + // TODO: Support out-of-line definitions, which will have a resolved + // name_context. Right now, those become errors in AddNameToLookup. + + // Add the callable. + auto function_id = context.semantics_ir().AddFunction( + {.name_id = name_context.unresolved_name_id, + .param_refs_id = param_refs_id, + .return_type_id = return_type_id, + .body_block_ids = {}}); + auto decl_id = context.AddNode( + SemanticsNode::FunctionDeclaration::Make(fn_node, function_id)); + context.AddNameToLookup(name_context, decl_id); + return {function_id, decl_id}; +} + auto SemanticsHandleFunctionDeclaration(SemanticsContext& context, - ParseTree::Node parse_node) -> bool { - return context.TODO(parse_node, "HandleFunctionDeclaration"); + ParseTree::Node /*parse_node*/) + -> bool { + BuildFunctionDeclaration(context); + return true; } auto SemanticsHandleFunctionDefinition(SemanticsContext& context, @@ -31,57 +68,34 @@ auto SemanticsHandleFunctionDefinition(SemanticsContext& context, } } - context.return_scope_stack().pop_back(); context.PopScope(); context.node_block_stack().Pop(); + context.return_scope_stack().pop_back(); return true; } auto SemanticsHandleFunctionDefinitionStart(SemanticsContext& context, ParseTree::Node parse_node) -> bool { - SemanticsTypeId return_type_id = SemanticsTypeId::Invalid; - if (context.parse_tree().node_kind(context.node_stack().PeekParseNode()) == - ParseNodeKind::ReturnType) { - return_type_id = - context.node_stack().Pop(ParseNodeKind::ReturnType); - } else { - // Canonicalize the empty tuple for the implicit return. - context.CanonicalizeType(SemanticsNodeId::BuiltinEmptyTupleType); - } - auto param_refs_id = context.node_stack().Pop( - ParseNodeKind::ParameterList); - auto name_context = context.PopDeclarationName(); - auto fn_node = context.node_stack().PopForSoloParseNode( - ParseNodeKind::FunctionIntroducer); + // Process the declaration portion of the function. + auto [function_id, decl_id] = BuildFunctionDeclaration(context); + const auto& function = context.semantics_ir().GetFunction(function_id); - // Create the entry block. - auto outer_block = context.node_block_stack().PeekForAdd(); + // Create the function scope and the entry block. + context.return_scope_stack().push_back(decl_id); context.node_block_stack().Push(); - - // TODO: Support out-of-line definitions, which will have a resolved - // name_context. Right now, those become errors in AddNameToLookup. - - // Add the callable. - auto function_id = context.semantics_ir().AddFunction( - {.name_id = name_context.unresolved_name_id, - .param_refs_id = param_refs_id, - .return_type_id = return_type_id, - .body_block_ids = {context.node_block_stack().PeekForAdd()}}); - auto decl_id = context.AddNodeToBlock( - outer_block, - SemanticsNode::FunctionDeclaration::Make(fn_node, function_id)); - context.AddNameToLookup(name_context, decl_id); - context.PushScope(); - for (auto ref_id : context.semantics_ir().GetNodeBlock(param_refs_id)) { + context.AddCurrentCodeBlockToFunction(); + + // Bring the parameters into scope. + for (auto ref_id : + context.semantics_ir().GetNodeBlock(function.param_refs_id)) { auto ref = context.semantics_ir().GetNode(ref_id); auto [name_id, target_id] = ref.GetAsBindName(); context.AddNameToLookup(ref.parse_node(), name_id, target_id); } - context.return_scope_stack().push_back(decl_id); - context.node_stack().Push(parse_node, function_id); + context.node_stack().Push(parse_node, function_id); return true; } diff --git a/toolchain/semantics/testdata/function/declaration/simple.carbon b/toolchain/semantics/testdata/function/declaration/simple.carbon new file mode 100644 index 000000000000..72a24a674786 --- /dev/null +++ b/toolchain/semantics/testdata/function/declaration/simple.carbon @@ -0,0 +1,43 @@ +// 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 +// CHECK:STDOUT: cross_reference_irs_size: 1 +// CHECK:STDOUT: functions: [ +// CHECK:STDOUT: {name: str0, param_refs: block0}, +// CHECK:STDOUT: {name: str1, param_refs: block0, body: {block2}}}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: integer_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: real_literals: [ +// CHECK:STDOUT: ] +// CHECK:STDOUT: strings: [ +// CHECK:STDOUT: F, +// CHECK:STDOUT: G, +// CHECK:STDOUT: ] +// CHECK:STDOUT: types: [ +// CHECK:STDOUT: nodeEmptyTupleType, +// CHECK:STDOUT: ] +// CHECK:STDOUT: nodes: [ +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function0}, +// CHECK:STDOUT: {kind: FunctionDeclaration, arg0: function1}, +// CHECK:STDOUT: {kind: Call, arg0: block0, arg1: function0}, +// CHECK:STDOUT: {kind: Return}, +// CHECK:STDOUT: ] +// CHECK:STDOUT: node_blocks: [ +// CHECK:STDOUT: [ +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+0, +// CHECK:STDOUT: node+1, +// CHECK:STDOUT: ], +// CHECK:STDOUT: [ +// CHECK:STDOUT: node+2, +// CHECK:STDOUT: node+3, +// CHECK:STDOUT: ], +// CHECK:STDOUT: ] + +fn F(); + +fn G() { F(); }