From b766aa4d16499a5eb0c92c01e7923ccf45140256 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Fri, 16 Oct 2026 21:48:48 +0000 Subject: [PATCH] Support for terse `=>` function bodies (#7895) Treat the function as having a `-> auto` return type. Reject calls to functions that still have a non-deduced `-> auto` return type, and rewrite the function to have the actual return type once it is known. Assisted-by: Gemini via Antigravity --- toolchain/check/eval.cpp | 41 +- toolchain/check/function.cpp | 32 ++ toolchain/check/function.h | 15 + toolchain/check/handle.h | 6 +- toolchain/check/handle_function.cpp | 62 ++- toolchain/check/handle_lambda.cpp | 36 +- toolchain/check/node_id_traversal.cpp | 3 +- toolchain/check/node_stack.h | 2 + .../declaration/fail_todo_no_params.carbon | 117 +---- .../testdata/function/definition/terse.carbon | 473 ++++++++++++++++++ toolchain/check/testdata/lambda/lambda.carbon | 357 ++++++++++--- toolchain/diagnostics/kind.def | 1 + .../handle_document_symbol.cpp | 1 + .../document_symbol/fn_definition.carbon | 26 +- .../testdata/function/definition/terse.carbon | 219 ++++++++ toolchain/lower/testdata/lambda/lambda.carbon | 84 ++-- toolchain/parse/context.cpp | 10 + toolchain/parse/context.h | 4 + toolchain/parse/handle_function.cpp | 5 +- toolchain/parse/handle_lambda.cpp | 18 +- toolchain/parse/node_ids.h | 10 +- toolchain/parse/node_kind.def | 3 + toolchain/parse/state.def | 11 +- .../testdata/function/declaration.carbon | 30 +- .../testdata/function/default_values.carbon | 4 +- .../parse/testdata/function/terse.carbon | 17 +- toolchain/parse/testdata/lambda/lambda.carbon | 8 +- toolchain/parse/tree.h | 2 +- toolchain/parse/typed_nodes.h | 46 +- toolchain/sem_ir/formatter.cpp | 2 + 30 files changed, 1323 insertions(+), 322 deletions(-) create mode 100644 toolchain/check/testdata/function/definition/terse.carbon create mode 100644 toolchain/lower/testdata/function/definition/terse.carbon diff --git a/toolchain/check/eval.cpp b/toolchain/check/eval.cpp index 8139cd59294f..f10d199fc23a 100644 --- a/toolchain/check/eval.cpp +++ b/toolchain/check/eval.cpp @@ -3665,22 +3665,31 @@ class FunctionExecContext : public EvalContext { // Branch control flow to the given block. This replaces the innermost block // in the block stack, but doesn't affect any enclosing blocks. auto BranchTo(SemIR::InstBlockId block_id) -> void { - blocks_.back() = inst_blocks().Get(block_id); + blocks_.back().insts = inst_blocks().Get(block_id); } // Push a new block to be executed immediately. After the block finishes, - // control will resume after the current instruction. - auto PushBlock(SemIR::InstBlockId block_id) -> void { - blocks_.push_back(inst_blocks().Get(block_id)); + // control will resume after the current instruction. If `splice_id` is + // specified, it is the `SpliceBlock` instruction whose block is being + // executed, and its value is set to the value of its result once the block + // finishes. + auto PushBlock(SemIR::InstBlockId block_id, + SemIR::InstId splice_id = SemIR::InstId::None) -> void { + blocks_.push_back( + {.insts = inst_blocks().Get(block_id), .splice_id = splice_id}); } // Pops and returns the next instruction to be executed. auto PopNextInstId() -> SemIR::InstId { - while (blocks_.back().empty()) { - blocks_.pop_back(); + while (blocks_.back().insts.empty()) { + auto splice_id = blocks_.pop_back_val().splice_id; CARBON_CHECK(!blocks_.empty(), "Fell off end of function"); + if (splice_id.has_value()) { + auto splice = insts().GetAs(splice_id); + locals().Update(splice_id, GetConstantValue(splice.result_id)); + } } - return blocks_.back().consume_front(); + return blocks_.back().insts.consume_front(); } // Sets the most recent block argument value provided by a `BranchWithArg`. @@ -3695,11 +3704,19 @@ class FunctionExecContext : public EvalContext { } private: + // A block that is being executed. + struct ExecBlock { + // The remaining instructions in the block. + llvm::ArrayRef insts; + // The `SpliceBlock` instruction that is executing this block, if any. + SemIR::InstId splice_id = SemIR::InstId::None; + }; + // The stack of code blocks that we are currently evaluating. This is kept as // a stack so that we can schedule the function body to execute after the decl // block and so that we can handle `SpliceBlock`s. When the innermost block is // complete, it will be popped and the next outer block will execute. - llvm::SmallVector, 4> blocks_; + llvm::SmallVector blocks_; // The arguments in the function call. llvm::ArrayRef args_; @@ -3850,13 +3867,13 @@ auto TryExecTypedInst(FunctionExecContext& eval_context, template <> auto TryExecTypedInst(FunctionExecContext& eval_context, - SemIR::InstId /*inst_id*/, + SemIR::InstId inst_id, SemIR::Inst inst) -> SemIR::ConstantId { auto splice_block = inst.As(); - eval_context.PushBlock(splice_block.block_id); - // TODO: Copy the values from the result_id instruction to the result of - // the splice_block instruction once the spliced block finishes. + // Once the spliced block finishes, the value of its result is copied to the + // `SpliceBlock` instruction. + eval_context.PushBlock(splice_block.block_id, inst_id); return SemIR::ConstantId::None; } diff --git a/toolchain/check/function.cpp b/toolchain/check/function.cpp index cb29d32cbf45..8088debfbaaa 100644 --- a/toolchain/check/function.cpp +++ b/toolchain/check/function.cpp @@ -98,6 +98,28 @@ auto PopReturnSpecifier(Context& context) -> ReturnSpecifierInfo { .form = context.PopReturnForm()}; } +auto AdjustReturnSpecifierForTerseDefinition( + Context& context, Parse::NodeId start_node_id, + ReturnSpecifierInfo& return_specifier) -> void { + if (return_specifier.has_return_specifier()) { + CARBON_DIAGNOSTIC(ReturnTypeInTerseFunction, Error, + "cannot specify a return {0:type|form} with `=>`", + Diagnostics::BoolAsSelect); + context.emitter().Emit( + return_specifier.node_id, ReturnTypeInTerseFunction, + context.parse_tree().node_kind(return_specifier.node_id) == + Parse::NodeKind::ReturnType); + return; + } + + // The return type is deduced from the body, as if `-> auto` were specified. + // TODO: We always use an initializing form; should we use `->? auto` + // semantics instead? + return_specifier = { + .node_id = start_node_id, + .deduced_return_kind = SemIR::Function::DeducedReturnKind::Init}; +} + auto IsValidBuiltinDeclaration(Context& context, const SemIR::Function& function, SemIR::BuiltinFunctionKind builtin_kind) @@ -840,6 +862,16 @@ auto CheckFunctionReturnOnFinish(Context& context, Parse::NodeId node_id, } } +auto CheckFunctionTerseBody(Context& context, + Parse::AnyTerseFunctionDefinitionId node_id, + SemIR::FunctionId function_id, + SemIR::InstId body_expr_id) -> void { + // A terse body `=> expr` is equivalent to `{ return expr; }`. The return type + // is deduced from the `return`, just like for `-> auto`. + BuildReturnWithExpr(context, SemIR::LocId(body_expr_id), body_expr_id); + CheckFunctionReturnOnFinish(context, node_id, function_id); +} + auto FinishFunctionDefinition(Context& context, SemIR::FunctionId function_id) -> void { context.inst_block_stack().Pop(); diff --git a/toolchain/check/function.h b/toolchain/check/function.h index c63ed788bc84..2b049f98bfe9 100644 --- a/toolchain/check/function.h +++ b/toolchain/check/function.h @@ -55,6 +55,14 @@ struct ReturnSpecifierInfo { // from the node stack, if it has one. auto PopReturnSpecifier(Context& context) -> ReturnSpecifierInfo; +// Adjusts the popped return specifier of a terse `=> expr` function definition +// or function expression starting at `start_node_id`. A return specifier is +// diagnosed, because it can't be combined with `=>`. Otherwise, the return type +// is deduced from the body, as if `-> auto` were specified. +auto AdjustReturnSpecifierForTerseDefinition( + Context& context, Parse::NodeId start_node_id, + ReturnSpecifierInfo& return_specifier) -> void; + // Sets the return type of a function whose return type is deduced, once that // type is known. `type_id` is the deduced type, or the error type if deduction // failed, and `loc_id` is the location it was deduced from. @@ -194,6 +202,13 @@ auto StartFunctionDefinition(Context& context, SemIR::InstId decl_id, auto CheckFunctionReturnOnFinish(Context& context, Parse::NodeId node_id, SemIR::FunctionId function_id) -> void; +// Checks the body of a terse `=> expr` function or lambda definition, which +// behaves like `{ return expr; }`, deducing the return type if necessary. +auto CheckFunctionTerseBody(Context& context, + Parse::AnyTerseFunctionDefinitionId node_id, + SemIR::FunctionId function_id, + SemIR::InstId body_expr_id) -> void; + // Finishes definitions started by `StartFunctionDefinition`. auto FinishFunctionDefinition(Context& context, SemIR::FunctionId function_id) -> void; diff --git a/toolchain/check/handle.h b/toolchain/check/handle.h index db53d8806dfc..7450089f5814 100644 --- a/toolchain/check/handle.h +++ b/toolchain/check/handle.h @@ -22,13 +22,13 @@ namespace Carbon::Check { // HandleFunctionDefinitionResume carry out the same actions as // HandleFunctionDefinitionStart, except that the various context stacks are // cleared out in between. -auto HandleFunctionDefinitionSuspend(Context& context, - Parse::FunctionDefinitionStartId node_id) +auto HandleFunctionDefinitionSuspend( + Context& context, Parse::AnyFunctionDefinitionStartId node_id) -> DeferredDefinitionWorklist::SuspendedFunction; // Handle resuming the definition of a function, after a previous suspension. auto HandleFunctionDefinitionResume( - Context& context, Parse::FunctionDefinitionStartId node_id, + Context& context, Parse::AnyFunctionDefinitionStartId node_id, DeferredDefinitionWorklist::SuspendedFunction&& suspended_fn) -> void; } // namespace Carbon::Check diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index 56905977f70c..b822ea497a12 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -458,6 +458,12 @@ static auto BuildFunctionDecl(Context& context, return_specifier.has_return_specifier()); auto name_context = context.decl_name_stack().FinishName(name); + bool is_terse_definition = context.parse_tree().node_kind(node_id) == + Parse::NodeKind::FunctionTerseDefinitionStart; + if (is_terse_definition) { + AdjustReturnSpecifierForTerseDefinition(context, node_id, return_specifier); + } + context.node_stack() .PopAndDiscardSoloNodeId(); @@ -672,14 +678,14 @@ auto HandleParseNode(Context& context, Parse::FunctionDeclId node_id) -> bool { // built a function ID. This logic is shared between processing regular function // definitions and delayed parsing of inline method definitions. static auto HandleFunctionDefinitionAfterSignature( - Context& context, Parse::FunctionDefinitionStartId node_id, + Context& context, Parse::AnyFunctionDefinitionStartId node_id, SemIR::FunctionId function_id, SemIR::InstId decl_id) -> void { StartFunctionDefinition(context, decl_id, function_id); context.node_stack().Push(node_id, function_id); } -auto HandleFunctionDefinitionSuspend(Context& context, - Parse::FunctionDefinitionStartId node_id) +auto HandleFunctionDefinitionSuspend( + Context& context, Parse::AnyFunctionDefinitionStartId node_id) -> DeferredDefinitionWorklist::SuspendedFunction { // Process the declaration portion of the function. auto [function_id, decl_id] = @@ -690,13 +696,29 @@ auto HandleFunctionDefinitionSuspend(Context& context, } auto HandleFunctionDefinitionResume( - Context& context, Parse::FunctionDefinitionStartId node_id, + Context& context, Parse::AnyFunctionDefinitionStartId node_id, DeferredDefinitionWorklist::SuspendedFunction&& suspended_fn) -> void { context.decl_name_stack().Restore(std::move(suspended_fn.saved_name_state)); HandleFunctionDefinitionAfterSignature( context, node_id, suspended_fn.function_id, suspended_fn.decl_id); } +static auto HandleFunctionDefinitionFinish(Context& context, + Parse::AnyFunctionDeclId fn_node_id, + SemIR::FunctionId function_id) + -> void { + FinishFunctionDefinition(context, function_id); + + // If the function has a deduced return type, we can now perform any delayed + // checks of its signature. + const auto& function = context.functions().Get(function_id); + if (function.has_deduced_return_type()) { + ValidateForEntryPoint(context, fn_node_id, function_id, function); + } + + context.decl_name_stack().PopScope(/*check_unused=*/true); +} + auto HandleParseNode(Context& context, Parse::FunctionDefinitionStartId node_id) -> bool { // Process the declaration portion of the function. @@ -714,16 +736,29 @@ auto HandleParseNode(Context& context, Parse::FunctionDefinitionId node_id) .PopWithNodeId(); CheckFunctionReturnOnFinish(context, node_id, function_id); - FinishFunctionDefinition(context, function_id); + HandleFunctionDefinitionFinish(context, start_node_id, function_id); + return true; +} - // Now that the return type has been deduced, we can validate the entry point. - const auto& function = context.functions().Get(function_id); - if (function.has_deduced_return_type()) { - ValidateForEntryPoint(context, start_node_id, function_id, function); - } +auto HandleParseNode(Context& context, + Parse::FunctionTerseDefinitionStartId node_id) -> bool { + // Process the declaration portion of the function. + auto [function_id, decl_id] = + BuildFunctionDecl(context, node_id, /*is_definition=*/true); + HandleFunctionDefinitionAfterSignature(context, node_id, function_id, + decl_id); + return true; +} - context.decl_name_stack().PopScope(/*check_unused=*/true); +auto HandleParseNode(Context& context, Parse::FunctionTerseDefinitionId node_id) + -> bool { + auto body_expr_id = context.node_stack().PopExpr(); + auto [signature_node_id, function_id] = + context.node_stack() + .PopWithNodeId(); + CheckFunctionTerseBody(context, node_id, function_id, body_expr_id); + HandleFunctionDefinitionFinish(context, signature_node_id, function_id); return true; } @@ -789,9 +824,4 @@ auto HandleParseNode(Context& context, return true; } -auto HandleParseNode(Context& context, Parse::FunctionTerseDefinitionId node_id) - -> bool { - return context.TODO(node_id, "HandleFunctionTerseDefinition"); -} - } // namespace Carbon::Check diff --git a/toolchain/check/handle_lambda.cpp b/toolchain/check/handle_lambda.cpp index fa2c4423aee7..74360d301a37 100644 --- a/toolchain/check/handle_lambda.cpp +++ b/toolchain/check/handle_lambda.cpp @@ -25,9 +25,13 @@ auto HandleParseNode(Context& context, Parse::LambdaIntroducerId node_id) return true; } -auto HandleParseNode(Context& context, Parse::LambdaDefinitionStartId node_id) - -> bool { +static auto HandleLambdaSignature(Context& context, + Parse::AnyLambdaDefinitionStartId node_id, + bool is_terse_definition) -> bool { auto return_specifier = PopReturnSpecifier(context); + if (is_terse_definition) { + AdjustReturnSpecifierForTerseDefinition(context, node_id, return_specifier); + } Parse::NodeId first_param_node_id = Parse::NoneNodeId(); Parse::NodeId last_param_node_id = Parse::NoneNodeId(); @@ -123,10 +127,19 @@ auto HandleParseNode(Context& context, Parse::LambdaDefinitionStartId node_id) return true; } +auto HandleParseNode(Context& context, Parse::LambdaDefinitionStartId node_id) + -> bool { + return HandleLambdaSignature(context, node_id, + /*is_terse_definition=*/false); +} + +auto HandleParseNode(Context& context, + Parse::LambdaTerseDefinitionStartId node_id) -> bool { + return HandleLambdaSignature(context, node_id, + /*is_terse_definition=*/true); +} + auto HandleParseNode(Context& context, Parse::LambdaId node_id) -> bool { - if (context.node_stack().PeekIs(Parse::NodeCategory::Expr)) { - return context.TODO(node_id, "terse lambda body"); - } auto function_id = context.node_stack().Pop(); CheckFunctionReturnOnFinish(context, node_id, function_id); @@ -137,4 +150,17 @@ auto HandleParseNode(Context& context, Parse::LambdaId node_id) -> bool { return true; } +auto HandleParseNode(Context& context, Parse::LambdaTerseDefinitionId node_id) + -> bool { + auto body_expr_id = context.node_stack().PopExpr(); + auto function_id = + context.node_stack().Pop(); + CheckFunctionTerseBody(context, node_id, function_id, body_expr_id); + FinishFunctionDefinition(context, function_id); + context.scope_stack().Pop(/*check_unused=*/true); + context.node_stack().Push( + node_id, context.functions().Get(function_id).first_owning_decl_id); + return true; +} + } // namespace Carbon::Check diff --git a/toolchain/check/node_id_traversal.cpp b/toolchain/check/node_id_traversal.cpp index ae100073a0bf..8cad05009489 100644 --- a/toolchain/check/node_id_traversal.cpp +++ b/toolchain/check/node_id_traversal.cpp @@ -68,7 +68,8 @@ auto NodeIdTraversal::Next() -> std::optional { // If we've reached the start of a deferred definition, skip to the end of // it, and track that we need to check it later. - if (node_id == next_deferred_definition_.start_id()) { + if (node_id == next_deferred_definition_.start_id() && + !context_->parse_tree().node_has_error(node_id)) { const auto& definition_info = context_->parse_tree().deferred_definitions().Get( next_deferred_definition_.index()); diff --git a/toolchain/check/node_stack.h b/toolchain/check/node_stack.h index 619942bb7e2f..48700c56c630 100644 --- a/toolchain/check/node_stack.h +++ b/toolchain/check/node_stack.h @@ -430,8 +430,10 @@ class NodeStack { case Parse::NodeKind::WhileConditionStart: return Id::KindFor(); case Parse::NodeKind::FunctionDefinitionStart: + case Parse::NodeKind::FunctionTerseDefinitionStart: case Parse::NodeKind::BuiltinFunctionDefinitionStart: case Parse::NodeKind::LambdaDefinitionStart: + case Parse::NodeKind::LambdaTerseDefinitionStart: return Id::KindFor(); case Parse::NodeKind::ChoiceDefinitionStart: // TODO: Should we have a separate SemIR::ChoiceId? diff --git a/toolchain/check/testdata/function/declaration/fail_todo_no_params.carbon b/toolchain/check/testdata/function/declaration/fail_todo_no_params.carbon index 4892a016ed32..433062c9ecc2 100644 --- a/toolchain/check/testdata/function/declaration/fail_todo_no_params.carbon +++ b/toolchain/check/testdata/function/declaration/fail_todo_no_params.carbon @@ -2,9 +2,7 @@ // Exceptions. See /LICENSE for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // -// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon -// TODO: Add ranges and switch to "--dump-sem-ir-ranges=only". -// EXTRA-ARGS: --dump-sem-ir-ranges=if-present +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/convert.carbon // // AUTOUPDATE // TIP: To test this file alone, run: @@ -43,11 +41,8 @@ fn A[] -> (); // --- fail_todo_arrow_body.carbon -// TODO: We don't have parsing support for this yet. library "[[@TEST_NAME]]"; -// CHECK:STDERR: fail_todo_arrow_body.carbon:[[@LINE+4]]:1-7: error: semantics TODO: `function with positional parameters` [SemanticsTodo] -// CHECK:STDERR: -// CHECK:STDERR: fail_todo_arrow_body.carbon:[[@LINE+2]]:1-10: error: semantics TODO: `HandleFunctionTerseDefinition` [SemanticsTodo] +// CHECK:STDERR: fail_todo_arrow_body.carbon:[[@LINE+2]]:1-7: error: semantics TODO: `function with positional parameters` [SemanticsTodo] // CHECK:STDERR: fn A => 0; @@ -64,111 +59,3 @@ var generic x: () = (); fn A { A(); } - -// CHECK:STDOUT: --- fail_no_body.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: type: type = facet_type [concrete] -// CHECK:STDOUT: %A.type: type = fn_type @A [concrete] -// CHECK:STDOUT: %A: %A.type = struct_value () [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .A = %A.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @A; -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_brace_body.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: type: type = facet_type [concrete] -// CHECK:STDOUT: %A.type: type = fn_type @A [concrete] -// CHECK:STDOUT: %A: %A.type = struct_value () [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .A = %A.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] {} {} -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @A { -// CHECK:STDOUT: !entry: -// CHECK:STDOUT: return -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_return_type.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: type: type = facet_type [concrete] -// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] -// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] -// CHECK:STDOUT: %.262: Core.Form = init_form %empty_tuple.type [concrete] -// CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] -// CHECK:STDOUT: %return.param_patt: %pattern_type = out_param_pattern [concrete] -// CHECK:STDOUT: %return.patt: %pattern_type = return_slot_pattern %return.param_patt, %empty_tuple.type [concrete] -// CHECK:STDOUT: %A.type: type = fn_type @A [concrete] -// CHECK:STDOUT: %A: %A.type = struct_value () [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .A = %A.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] { -// CHECK:STDOUT: %return.param_patt: %pattern_type = out_param_pattern [concrete = constants.%return.param_patt] -// CHECK:STDOUT: %return.patt: %pattern_type = return_slot_pattern %return.param_patt, %.loc5_10.2 [concrete = constants.%return.patt] -// CHECK:STDOUT: } { -// CHECK:STDOUT: %.loc5_10.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] -// CHECK:STDOUT: %.loc5_10.2: type = converted %.loc5_10.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] -// CHECK:STDOUT: %.loc5_10.3: Core.Form = init_form %.loc5_10.2 [concrete = constants.%.262] -// CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param0 -// CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param -// CHECK:STDOUT: } -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @A() -> out %return.param: %empty_tuple.type; -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_implicit_only.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: constants { -// CHECK:STDOUT: type: type = facet_type [concrete] -// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] -// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] -// CHECK:STDOUT: %.262: Core.Form = init_form %empty_tuple.type [concrete] -// CHECK:STDOUT: %pattern_type: type = pattern_type %empty_tuple.type [concrete] -// CHECK:STDOUT: %return.param_patt: %pattern_type = out_param_pattern [concrete] -// CHECK:STDOUT: %return.patt: %pattern_type = return_slot_pattern %return.param_patt, %empty_tuple.type [concrete] -// CHECK:STDOUT: %A.type: type = fn_type @A [concrete] -// CHECK:STDOUT: %A: %A.type = struct_value () [concrete] -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: file { -// CHECK:STDOUT: package: = namespace [concrete] { -// CHECK:STDOUT: .A = %A.decl -// CHECK:STDOUT: } -// CHECK:STDOUT: %A.decl: %A.type = fn_decl @A [concrete = constants.%A] { -// CHECK:STDOUT: %return.param_patt: %pattern_type = out_param_pattern [concrete = constants.%return.param_patt] -// CHECK:STDOUT: %return.patt: %pattern_type = return_slot_pattern %return.param_patt, %.loc5_12.2 [concrete = constants.%return.patt] -// CHECK:STDOUT: } { -// CHECK:STDOUT: %.loc5_12.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] -// CHECK:STDOUT: %.loc5_12.2: type = converted %.loc5_12.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] -// CHECK:STDOUT: %.loc5_12.3: Core.Form = init_form %.loc5_12.2 [concrete = constants.%.262] -// CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param0 -// CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param -// CHECK:STDOUT: } -// CHECK:STDOUT: } -// CHECK:STDOUT: -// CHECK:STDOUT: fn @A() -> out %return.param: %empty_tuple.type; -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_arrow_body.carbon -// CHECK:STDOUT: -// CHECK:STDOUT: fn @A; -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_invalid_file_generic_regression_test.carbon -// CHECK:STDOUT: diff --git a/toolchain/check/testdata/function/definition/terse.carbon b/toolchain/check/testdata/function/definition/terse.carbon new file mode 100644 index 000000000000..aa17b840bab1 --- /dev/null +++ b/toolchain/check/testdata/function/definition/terse.carbon @@ -0,0 +1,473 @@ +// 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 +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/primitives.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/function/definition/terse.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/function/definition/terse.carbon + +// --- basic.carbon +library "[[@TEST_NAME]]"; + +//@dump-sem-ir-begin +fn Id(x: i32) => x; + +fn Unit() => (); + +fn Pair(x: i32, y: i32) => {.a = x, .b = y}; + +fn TestBasic() { + let a: i32 = Id(42); + Unit(); + let p: {.a: i32, .b: i32} = Pair(a, 10); + let unused b: i32 = p.a; +} +//@dump-sem-ir-end + +eval fn EvalId(x: i32) => x; +musteval fn MustEvalId(x: i32) => x; + +fn TestEval() { + var a: array(i32, EvalId(3)); + var b: array(i32, MustEvalId(4)); + let _: (array(i32, 3), array(i32, 4)) = (a, b); +} + +class WithMethods { + fn Get(self: Self) => self.x; + fn Static() => 7 as i32; + var x: i32; +} + +fn TestMethods(c: WithMethods) { + let unused a: i32 = c.Get(); + let unused b: i32 = WithMethods.Static(); +} + +// --- generic_non_dependent.carbon +library "[[@TEST_NAME]]"; + +fn GenericNonDependent[T: type](unused x: T) => 42 as i32; + +fn TestGenericNonDependent() { + let unused a: i32 = GenericNonDependent(true); +} + +// --- main_run_i32.carbon +library "[[@TEST_NAME]]"; + +fn Run() => 0 as i32; + +// --- main_run_unit.carbon +library "[[@TEST_NAME]]"; + +fn Run() => (); + +// --- fail_main_run_bool.carbon +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_main_run_bool.carbon:[[@LINE+2]]:1-11: error: invalid return type for `Main.Run` function; expected `fn (...)` or `fn (...) -> i32` [InvalidMainRunReturnType] +// CHECK:STDERR: +fn Run() => true; + +// --- fail_return_type.carbon +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_return_type.carbon:[[@LINE+2]]:14-19: error: cannot specify a return type with `=>` [ReturnTypeInTerseFunction] +// CHECK:STDERR: +fn G(x: i32) -> i32 => x; + +// CHECK:STDERR: fail_return_type.carbon:[[@LINE+2]]:18-34: error: cannot specify a return form with `=>` [ReturnTypeInTerseFunction] +// CHECK:STDERR: +fn H(ref x: i32) ->? form(ref i32) => x; + +// --- fail_redecl.carbon +library "[[@TEST_NAME]]"; + +fn DeclaredI32() -> i32; +// CHECK:STDERR: fail_redecl.carbon:[[@LINE+3]]:1-19: error: redeclaration of function with deduced return type [DeducedReturnTypeRedecl] +// CHECK:STDERR: fail_redecl.carbon:[[@LINE-2]]:1-24: note: previously declared here [RedeclPrevDecl] +// CHECK:STDERR: +fn DeclaredI32() => 0 as i32; + +fn DeclaredUnit(); +// CHECK:STDERR: fail_redecl.carbon:[[@LINE+3]]:1-20: error: redeclaration of function with deduced return type [DeducedReturnTypeRedecl] +// CHECK:STDERR: fail_redecl.carbon:[[@LINE-2]]:1-18: note: previously declared here [RedeclPrevDecl] +// CHECK:STDERR: +fn DeclaredUnit() => (); + +fn TerseFirst() => 0 as i32; +// CHECK:STDERR: fail_redecl.carbon:[[@LINE+3]]:1-18: error: redeclaration of function with deduced return type [DeducedReturnTypeRedecl] +// CHECK:STDERR: fail_redecl.carbon:[[@LINE-2]]:1-18: note: previously declared here [RedeclPrevDecl] +// CHECK:STDERR: +fn TerseFirst() => 1 as i32; + +// --- fail_incomplete_return.carbon +library "[[@TEST_NAME]]"; + +class Incomplete; + +// CHECK:STDERR: fail_incomplete_return.carbon:[[@LINE+3]]:39-40: error: function returns incomplete type `Incomplete` [IncompleteTypeInFunctionReturnType] +// CHECK:STDERR: fail_incomplete_return.carbon:[[@LINE-3]]:1-17: note: class was forward declared here [ClassForwardDeclaredHere] +// CHECK:STDERR: +fn DerefIncomplete(p: Incomplete*) => *p; + +// --- fail_unused.carbon +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_unused.carbon:[[@LINE+5]]:15-20: warning: binding `x` unused [UnusedBinding] +// CHECK:STDERR: +// CHECK:STDERR: fail_unused.carbon:[[@LINE+3]]:30-35: error: variable `y` marked `unused` but used [UnusedButUsed] +// CHECK:STDERR: fail_unused.carbon:[[@LINE+2]]:41: note: usage here [UnusedButUsedHere] +// CHECK:STDERR: +fn UnusedTest(x: i32, unused y: i32) => y; + +// --- dependent_return.carbon +library "[[@TEST_NAME]]"; + +fn GenericId[T: Core.Copy](x: T) => x; + +// --- fail_call_before_deduced.carbon +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_call_before_deduced.carbon:[[@LINE+3]]:23-32: error: call to `Recurse` before its return type is known [CallBeforeReturnTypeDeduced] +// CHECK:STDERR: fail_call_before_deduced.carbon:[[@LINE+2]]:1-21: note: return type of `Recurse` is deduced from its definition [ReturnTypeDeducedFromDefinition] +// CHECK:STDERR: +fn Recurse(x: i32) => Recurse(x); + +class EarlyCall { + fn M() => 1 as i32; + // CHECK:STDERR: fail_call_before_deduced.carbon:[[@LINE+3]]:16-18: error: call to `M` before its return type is known [CallBeforeReturnTypeDeduced] + // CHECK:STDERR: fail_call_before_deduced.carbon:[[@LINE-2]]:3-11: note: return type of `M` is deduced from its definition [ReturnTypeDeducedFromDefinition] + // CHECK:STDERR: + var x: i32 = M(); +} + +// --- fail_todo_thunk_before_deduced.carbon +library "[[@TEST_NAME]]"; + +interface I { + fn F() -> i32; +} + +class C { + impl as I { + // CHECK:STDERR: fail_todo_thunk_before_deduced.carbon:[[@LINE+2]]:5-13: error: semantics TODO: `deduced return type for `impl` member` [SemanticsTodo] + // CHECK:STDERR: + fn F() => 0 as i32; + } +} + +// CHECK:STDOUT: --- basic.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: type: type = facet_type [concrete] +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %N.fe9: Core.IntLiteral = symbolic_binding N, 0 [symbolic] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] +// CHECK:STDOUT: %pattern_type.d94: type = pattern_type %i32 [concrete] +// CHECK:STDOUT: %x.param_patt: %pattern_type.d94 = value_param_pattern [concrete] +// CHECK:STDOUT: %x.patt: %pattern_type.d94 = wrapper_binding_pattern x, %x.param_patt [concrete] +// CHECK:STDOUT: %Id.type: type = fn_type @Id [concrete] +// CHECK:STDOUT: %Id: %Id.type = struct_value () [concrete] +// CHECK:STDOUT: %.0b3: Core.Form = init_form %i32 [concrete] +// CHECK:STDOUT: %return.param_patt.299: %pattern_type.d94 = out_param_pattern [concrete] +// CHECK:STDOUT: %return.patt.6fa: %pattern_type.d94 = return_slot_pattern %return.param_patt.299, %i32 [concrete] +// CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.type.a1d: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N.fe9) [symbolic] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.3c1: %Int.as.Copy.impl.Op.type.a1d = struct_value () [symbolic] +// CHECK:STDOUT: %Copy.impl_witness.8af: = impl_witness imports.%Copy.impl_witness_table.f44, @Int.as.Copy.impl(%int_32) [concrete] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.type.648: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.cae: %Int.as.Copy.impl.Op.type.648 = struct_value () [concrete] +// CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.8af) [concrete] +// CHECK:STDOUT: %Copy.WithSelf.Op.type.f76: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] +// CHECK:STDOUT: %.2b9: type = fn_type_with_self_type %Copy.WithSelf.Op.type.f76, %Copy.facet [concrete] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.cae, @Int.as.Copy.impl.Op(%int_32) [concrete] +// CHECK:STDOUT: %Unit.type: type = fn_type @Unit [concrete] +// CHECK:STDOUT: %Unit: %Unit.type = struct_value () [concrete] +// CHECK:STDOUT: %empty_tuple: %empty_tuple.type = tuple_value () [concrete] +// CHECK:STDOUT: %.262: Core.Form = init_form %empty_tuple.type [concrete] +// CHECK:STDOUT: %pattern_type.cb1: type = pattern_type %empty_tuple.type [concrete] +// CHECK:STDOUT: %return.param_patt.900: %pattern_type.cb1 = out_param_pattern [concrete] +// CHECK:STDOUT: %return.patt.427: %pattern_type.cb1 = return_slot_pattern %return.param_patt.900, %empty_tuple.type [concrete] +// CHECK:STDOUT: %y.param_patt: %pattern_type.d94 = value_param_pattern [concrete] +// CHECK:STDOUT: %y.patt: %pattern_type.d94 = wrapper_binding_pattern y, %y.param_patt [concrete] +// CHECK:STDOUT: %Pair.type: type = fn_type @Pair [concrete] +// CHECK:STDOUT: %Pair: %Pair.type = struct_value () [concrete] +// CHECK:STDOUT: %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete] +// CHECK:STDOUT: %.093: Core.Form = init_form %struct_type.a.b [concrete] +// CHECK:STDOUT: %pattern_type.290: type = pattern_type %struct_type.a.b [concrete] +// CHECK:STDOUT: %return.param_patt.cfa: %pattern_type.290 = out_param_pattern [concrete] +// CHECK:STDOUT: %return.patt.380: %pattern_type.290 = return_slot_pattern %return.param_patt.cfa, %struct_type.a.b [concrete] +// CHECK:STDOUT: %TestBasic.type: type = fn_type @TestBasic [concrete] +// CHECK:STDOUT: %TestBasic: %TestBasic.type = struct_value () [concrete] +// CHECK:STDOUT: %a.patt.413: %pattern_type.d94 = value_binding_pattern a [concrete] +// CHECK:STDOUT: %int_42.20e: Core.IntLiteral = int_value 42 [concrete] +// CHECK:STDOUT: %ImplicitAs.type.6b0: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] +// CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.6f2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6 = struct_value () [symbolic] +// CHECK:STDOUT: %ImplicitAs.impl_witness.d73: = impl_witness imports.%ImplicitAs.impl_witness_table.f53, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766 = struct_value () [concrete] +// CHECK:STDOUT: %ImplicitAs.facet.fc6: %ImplicitAs.type.6b0 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.d73) [concrete] +// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.58f: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet.fc6) [concrete] +// CHECK:STDOUT: %.33a: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.58f, %ImplicitAs.facet.fc6 [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.81c: = bound_method %int_42.20e, %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60 [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] +// CHECK:STDOUT: %bound_method.98e: = bound_method %int_42.20e, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] +// CHECK:STDOUT: %int_42.db5: %i32 = int_value 42 [concrete] +// CHECK:STDOUT: %p.patt: %pattern_type.290 = value_binding_pattern p [concrete] +// CHECK:STDOUT: %int_10.64f: Core.IntLiteral = int_value 10 [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.02d: = bound_method %int_10.64f, %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60 [concrete] +// CHECK:STDOUT: %bound_method.002: = bound_method %int_10.64f, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] +// CHECK:STDOUT: %int_10.6a3: %i32 = int_value 10 [concrete] +// CHECK:STDOUT: %b.patt.094d65.1: %pattern_type.d94 = value_binding_pattern b [concrete] +// CHECK:STDOUT: %self.param_patt.0c5: %pattern_type.d94 = ref_param_pattern [concrete] +// CHECK:STDOUT: %self.patt.749: %pattern_type.d94 = wrapper_binding_pattern self, %self.param_patt.0c5 [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc13_41.1 [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.1: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1 = struct_value () [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.1: type = fn_type @Destroy.WithSelf.Op.loc13_41.1 [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.1: %Destroy.WithSelf.Op.type.ef016f.1 = struct_value () [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbceb5.1: type = fn_type @Destroy.WithSelf.SelfDestruct.loc13_41.1 [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3fdb.1: %Destroy.WithSelf.SelfDestruct.type.fbceb5.1 = struct_value () [concrete] +// CHECK:STDOUT: %self.param_patt.490: %pattern_type.290 = ref_param_pattern [concrete] +// CHECK:STDOUT: %self.patt.a39: %pattern_type.290 = wrapper_binding_pattern self, %self.param_patt.490 [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2: type = fn_type @Destroy.WithSelf.SubobjectDestroy.loc13_41.2 [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.d01daf.2: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2 = struct_value () [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.Op.type.ef016f.2: type = fn_type @Destroy.WithSelf.Op.loc13_41.2 [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.Op.403171.2: %Destroy.WithSelf.Op.type.ef016f.2 = struct_value () [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.type.fbceb5.2: type = fn_type @Destroy.WithSelf.SelfDestruct.loc13_41.2 [concrete] +// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.db3fdb.2: %Destroy.WithSelf.SelfDestruct.type.fbceb5.2 = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core.import_ref.72f: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.a1d) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.3c1)] +// CHECK:STDOUT: %Copy.impl_witness_table.f44 = impl_witness_table (%Core.import_ref.72f), @Int.as.Copy.impl [concrete] +// CHECK:STDOUT: %Core.import_ref.e47: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.6f2)] +// CHECK:STDOUT: %ImplicitAs.impl_witness_table.f53 = impl_witness_table (%Core.import_ref.e47), @Core.IntLiteral.as.ImplicitAs.impl [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generated { +// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.1: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.1 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc13_41.1 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.1] { +// CHECK:STDOUT: %self.param_patt: %pattern_type.d94 = ref_param_pattern [concrete = constants.%self.param_patt.0c5] +// CHECK:STDOUT: %self.patt: %pattern_type.d94 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.749] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: ref %i32 = ref_param call_param0 +// CHECK:STDOUT: %self: ref %i32 = wrapper_binding self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.1: %Destroy.WithSelf.Op.type.ef016f.1 = fn_decl @Destroy.WithSelf.Op.loc13_41.1 [concrete = constants.%Destroy.WithSelf.Op.403171.1] { +// CHECK:STDOUT: %self.param_patt: %pattern_type.d94 = ref_param_pattern [concrete = constants.%self.param_patt.0c5] +// CHECK:STDOUT: %self.patt: %pattern_type.d94 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.749] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: ref %i32 = ref_param call_param0 +// CHECK:STDOUT: %self: ref %i32 = wrapper_binding self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.decl.763c71.2: %Destroy.WithSelf.SubobjectDestroy.type.dfcbdb.2 = fn_decl @Destroy.WithSelf.SubobjectDestroy.loc13_41.2 [concrete = constants.%Destroy.WithSelf.SubobjectDestroy.d01daf.2] { +// CHECK:STDOUT: %self.param_patt: %pattern_type.290 = ref_param_pattern [concrete = constants.%self.param_patt.490] +// CHECK:STDOUT: %self.patt: %pattern_type.290 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.a39] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: ref %struct_type.a.b = ref_param call_param0 +// CHECK:STDOUT: %self: ref %struct_type.a.b = wrapper_binding self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %Destroy.WithSelf.Op.decl.5f94cf.2: %Destroy.WithSelf.Op.type.ef016f.2 = fn_decl @Destroy.WithSelf.Op.loc13_41.2 [concrete = constants.%Destroy.WithSelf.Op.403171.2] { +// CHECK:STDOUT: %self.param_patt: %pattern_type.290 = ref_param_pattern [concrete = constants.%self.param_patt.490] +// CHECK:STDOUT: %self.patt: %pattern_type.290 = wrapper_binding_pattern self, %self.param_patt [concrete = constants.%self.patt.a39] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: ref %struct_type.a.b = ref_param call_param0 +// CHECK:STDOUT: %self: ref %struct_type.a.b = wrapper_binding self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %Id.decl: %Id.type = fn_decl @Id [concrete = constants.%Id] { +// CHECK:STDOUT: %x.param_patt: %pattern_type.d94 = value_param_pattern [concrete = constants.%x.param_patt] +// CHECK:STDOUT: %x.patt: %pattern_type.d94 = wrapper_binding_pattern x, %x.param_patt [concrete = constants.%x.patt] +// CHECK:STDOUT: %return.param_patt: %pattern_type.d94 = out_param_pattern [concrete = constants.%return.param_patt.299] +// CHECK:STDOUT: %return.patt: %pattern_type.d94 = return_slot_pattern %return.param_patt, %i32.loc4_18 [concrete = constants.%return.patt.6fa] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %x.param: %i32 = value_param call_param0 +// CHECK:STDOUT: %i32.loc4_10: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %x: %i32 = wrapper_binding x, %x.param +// CHECK:STDOUT: %i32.loc4_18: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %.loc4_18.2: Core.Form = init_form %i32.loc4_18 [concrete = constants.%.0b3] +// CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 +// CHECK:STDOUT: %return: ref %i32 = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %Unit.decl: %Unit.type = fn_decl @Unit [concrete = constants.%Unit] { +// CHECK:STDOUT: %return.param_patt: %pattern_type.cb1 = out_param_pattern [concrete = constants.%return.param_patt.900] +// CHECK:STDOUT: %return.patt: %pattern_type.cb1 = return_slot_pattern %return.param_patt, %.loc6_15.5 [concrete = constants.%return.patt.427] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.loc6_15.5: type = type_literal constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] +// CHECK:STDOUT: %.loc6_15.6: Core.Form = init_form %.loc6_15.5 [concrete = constants.%.262] +// CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param0 +// CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %Pair.decl: %Pair.type = fn_decl @Pair [concrete = constants.%Pair] { +// CHECK:STDOUT: %x.param_patt: %pattern_type.d94 = value_param_pattern [concrete = constants.%x.param_patt] +// CHECK:STDOUT: %x.patt: %pattern_type.d94 = wrapper_binding_pattern x, %x.param_patt [concrete = constants.%x.patt] +// CHECK:STDOUT: %y.param_patt: %pattern_type.d94 = value_param_pattern [concrete = constants.%y.param_patt] +// CHECK:STDOUT: %y.patt: %pattern_type.d94 = wrapper_binding_pattern y, %y.param_patt [concrete = constants.%y.patt] +// CHECK:STDOUT: %return.param_patt: %pattern_type.290 = out_param_pattern [concrete = constants.%return.param_patt.cfa] +// CHECK:STDOUT: %return.patt: %pattern_type.290 = return_slot_pattern %return.param_patt, %.loc8_43.9 [concrete = constants.%return.patt.380] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %x.param: %i32 = value_param call_param0 +// CHECK:STDOUT: %i32.loc8_12: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %x: %i32 = wrapper_binding x, %x.param +// CHECK:STDOUT: %y.param: %i32 = value_param call_param1 +// CHECK:STDOUT: %i32.loc8_20: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %y: %i32 = wrapper_binding y, %y.param +// CHECK:STDOUT: %.loc8_43.9: type = type_literal constants.%struct_type.a.b [concrete = constants.%struct_type.a.b] +// CHECK:STDOUT: %.loc8_43.10: Core.Form = init_form %.loc8_43.9 [concrete = constants.%.093] +// CHECK:STDOUT: %return.param: ref %struct_type.a.b = out_param call_param2 +// CHECK:STDOUT: %return: ref %struct_type.a.b = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %TestBasic.decl: %TestBasic.type = fn_decl @TestBasic [concrete = constants.%TestBasic] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Id(%x.param: %i32) -> out %return.param: %i32 [deduced] { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %x.ref: %i32 = name_ref x, %x +// CHECK:STDOUT: %.loc4_18.1: init %i32 = splice_block %Int.as.Copy.impl.Op.call { +// CHECK:STDOUT: %impl.elem0: %.2b9 = impl_witness_access constants.%Copy.impl_witness.8af, element0 [concrete = constants.%Int.as.Copy.impl.Op.cae] +// CHECK:STDOUT: %bound_method.loc4_18.1: = bound_method %x.ref, %impl.elem0 +// CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] +// CHECK:STDOUT: %bound_method.loc4_18.2: = bound_method %x.ref, %specific_fn +// CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc4_18.2(%x.ref) +// CHECK:STDOUT: } +// CHECK:STDOUT: return %.loc4_18.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Unit() -> out %return.param: %empty_tuple.type [deduced] { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc6_15.1: %empty_tuple.type = tuple_literal () [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc6_15.2: init %empty_tuple.type = splice_block %.loc6_15.4 [concrete = constants.%empty_tuple] { +// CHECK:STDOUT: %.loc6_15.3: init %empty_tuple.type = tuple_init () [concrete = constants.%empty_tuple] +// CHECK:STDOUT: %.loc6_15.4: init %empty_tuple.type = converted %.loc6_15.1, %.loc6_15.3 [concrete = constants.%empty_tuple] +// CHECK:STDOUT: } +// CHECK:STDOUT: return %.loc6_15.2 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Pair(%x.param: %i32, %y.param: %i32) -> out %return.param: %struct_type.a.b [deduced] { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %x.ref: %i32 = name_ref x, %x +// CHECK:STDOUT: %y.ref: %i32 = name_ref y, %y +// CHECK:STDOUT: %.loc8_43.1: %struct_type.a.b = struct_literal (%x.ref, %y.ref) +// CHECK:STDOUT: %.loc8_43.2: init %struct_type.a.b = splice_block %.loc8_43.8 { +// CHECK:STDOUT: %impl.elem0.loc8_34: %.2b9 = impl_witness_access constants.%Copy.impl_witness.8af, element0 [concrete = constants.%Int.as.Copy.impl.Op.cae] +// CHECK:STDOUT: %bound_method.loc8_34.1: = bound_method %x.ref, %impl.elem0.loc8_34 +// CHECK:STDOUT: %specific_fn.loc8_34: = specific_function %impl.elem0.loc8_34, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] +// CHECK:STDOUT: %bound_method.loc8_34.2: = bound_method %x.ref, %specific_fn.loc8_34 +// CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc8_34: init %i32 = call %bound_method.loc8_34.2(%x.ref) +// CHECK:STDOUT: %.loc8_43.3: ref %i32 = struct_access %return.param, element0 +// CHECK:STDOUT: %.loc8_43.4: init %i32 to %.loc8_43.3 = in_place_init %Int.as.Copy.impl.Op.call.loc8_34 +// CHECK:STDOUT: %impl.elem0.loc8_42: %.2b9 = impl_witness_access constants.%Copy.impl_witness.8af, element0 [concrete = constants.%Int.as.Copy.impl.Op.cae] +// CHECK:STDOUT: %bound_method.loc8_42.1: = bound_method %y.ref, %impl.elem0.loc8_42 +// CHECK:STDOUT: %specific_fn.loc8_42: = specific_function %impl.elem0.loc8_42, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] +// CHECK:STDOUT: %bound_method.loc8_42.2: = bound_method %y.ref, %specific_fn.loc8_42 +// CHECK:STDOUT: %Int.as.Copy.impl.Op.call.loc8_42: init %i32 = call %bound_method.loc8_42.2(%y.ref) +// CHECK:STDOUT: %.loc8_43.5: ref %i32 = struct_access %return.param, element1 +// CHECK:STDOUT: %.loc8_43.6: init %i32 to %.loc8_43.5 = in_place_init %Int.as.Copy.impl.Op.call.loc8_42 +// CHECK:STDOUT: %.loc8_43.7: init %struct_type.a.b to %return.param = struct_init (%.loc8_43.4, %.loc8_43.6) +// CHECK:STDOUT: %.loc8_43.8: init %struct_type.a.b = converted %.loc8_43.1, %.loc8_43.7 +// CHECK:STDOUT: } +// CHECK:STDOUT: return %.loc8_43.2 to %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @TestBasic() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Id.ref: %Id.type = name_ref Id, file.%Id.decl [concrete = constants.%Id] +// CHECK:STDOUT: %int_42: Core.IntLiteral = int_value 42 [concrete = constants.%int_42.20e] +// CHECK:STDOUT: %impl.elem0.loc11: %.33a = impl_witness_access constants.%ImplicitAs.impl_witness.d73, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f60] +// CHECK:STDOUT: %bound_method.loc11_19.1: = bound_method %int_42, %impl.elem0.loc11 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.81c] +// CHECK:STDOUT: %specific_fn.loc11: = specific_function %impl.elem0.loc11, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] +// CHECK:STDOUT: %bound_method.loc11_19.2: = bound_method %int_42, %specific_fn.loc11 [concrete = constants.%bound_method.98e] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc11: init %i32 = call %bound_method.loc11_19.2(%int_42) [concrete = constants.%int_42.db5] +// CHECK:STDOUT: %.loc11_19.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc11 [concrete = constants.%int_42.db5] +// CHECK:STDOUT: %.loc11_19.2: %i32 = converted %int_42, %.loc11_19.1 [concrete = constants.%int_42.db5] +// CHECK:STDOUT: %Id.call: init %i32 = call %Id.ref(%.loc11_19.2) +// CHECK:STDOUT: %.loc11_21.1: %i32 = value_of_initializer %Id.call +// CHECK:STDOUT: %.loc11_21.2: %i32 = converted %Id.call, %.loc11_21.1 +// CHECK:STDOUT: %i32.loc11: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %a: %i32 = wrapper_binding a, %.loc11_21.2 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %a.patt: %pattern_type.d94 = value_binding_pattern a [concrete = constants.%a.patt.413] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Unit.ref: %Unit.type = name_ref Unit, file.%Unit.decl [concrete = constants.%Unit] +// CHECK:STDOUT: %Unit.call: init %empty_tuple.type = call %Unit.ref() +// CHECK:STDOUT: %Pair.ref: %Pair.type = name_ref Pair, file.%Pair.decl [concrete = constants.%Pair] +// CHECK:STDOUT: %a.ref: %i32 = name_ref a, %a +// CHECK:STDOUT: %int_10: Core.IntLiteral = int_value 10 [concrete = constants.%int_10.64f] +// CHECK:STDOUT: %.loc13_41.1: ref %struct_type.a.b = temporary_storage +// CHECK:STDOUT: %impl.elem0.loc13: %.33a = impl_witness_access constants.%ImplicitAs.impl_witness.d73, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f60] +// CHECK:STDOUT: %bound_method.loc13_39.1: = bound_method %int_10, %impl.elem0.loc13 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.02d] +// CHECK:STDOUT: %specific_fn.loc13: = specific_function %impl.elem0.loc13, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] +// CHECK:STDOUT: %bound_method.loc13_39.2: = bound_method %int_10, %specific_fn.loc13 [concrete = constants.%bound_method.002] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc13: init %i32 = call %bound_method.loc13_39.2(%int_10) [concrete = constants.%int_10.6a3] +// CHECK:STDOUT: %.loc13_39.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc13 [concrete = constants.%int_10.6a3] +// CHECK:STDOUT: %.loc13_39.2: %i32 = converted %int_10, %.loc13_39.1 [concrete = constants.%int_10.6a3] +// CHECK:STDOUT: %Pair.call: init %struct_type.a.b to %.loc13_41.1 = call %Pair.ref(%a.ref, %.loc13_39.2) +// CHECK:STDOUT: %.loc13_41.2: ref %struct_type.a.b = temporary %.loc13_41.1, %Pair.call +// CHECK:STDOUT: %.loc13_41.3: ref %i32 = struct_access %.loc13_41.2, element0 +// CHECK:STDOUT: %.loc13_41.4: %i32 = acquire_value %.loc13_41.3 +// CHECK:STDOUT: %.loc13_41.5: ref %i32 = struct_access %.loc13_41.2, element1 +// CHECK:STDOUT: %.loc13_41.6: %i32 = acquire_value %.loc13_41.5 +// CHECK:STDOUT: %struct: %struct_type.a.b = struct_value (%.loc13_41.4, %.loc13_41.6) +// CHECK:STDOUT: %.loc13_41.7: %struct_type.a.b = converted %Pair.call, %struct +// CHECK:STDOUT: %.loc13_27: type = splice_block %struct_type.a.b [concrete = constants.%struct_type.a.b] { +// CHECK:STDOUT: %i32.loc13_15: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %i32.loc13_24: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %struct_type.a.b: type = struct_type {.a: %i32, .b: %i32} [concrete = constants.%struct_type.a.b] +// CHECK:STDOUT: } +// CHECK:STDOUT: %p: %struct_type.a.b = wrapper_binding p, %.loc13_41.7 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %p.patt: %pattern_type.290 = value_binding_pattern p [concrete = constants.%p.patt] +// CHECK:STDOUT: } +// CHECK:STDOUT: %p.ref: %struct_type.a.b = name_ref p, %p +// CHECK:STDOUT: %.loc14: %i32 = struct_access %p.ref, element0 +// CHECK:STDOUT: %i32.loc14: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %b: %i32 = wrapper_binding b, %.loc14 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %b.patt: %pattern_type.d94 = value_binding_pattern b [concrete = constants.%b.patt.094d65.1] +// CHECK:STDOUT: } +// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound: = bound_method %.loc13_41.2, constants.%Destroy.WithSelf.SelfDestruct.db3fdb.2 +// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound(%.loc13_41.2) +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc13_41.1(%self.param: ref %i32) = "no_op"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc13_41.1(%self.param: ref %i32) = "no_op"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc13_41.1(%self.param: ref %i32) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.1(%self.param) +// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.1(%self.param) +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Destroy.WithSelf.SubobjectDestroy.loc13_41.2(%self.param: ref %struct_type.a.b) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %.loc13_41.1: ref %i32 = struct_access %self.param, element1 +// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound.loc13_41.1: = bound_method %.loc13_41.1, constants.%Destroy.WithSelf.SelfDestruct.db3fdb.1 +// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call.loc13_41.1: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound.loc13_41.1(%.loc13_41.1) +// CHECK:STDOUT: %.loc13_41.2: ref %i32 = struct_access %self.param, element0 +// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.bound.loc13_41.2: = bound_method %.loc13_41.2, constants.%Destroy.WithSelf.SelfDestruct.db3fdb.1 +// CHECK:STDOUT: %Destroy.WithSelf.SelfDestruct.call.loc13_41.2: init %empty_tuple.type = call %Destroy.WithSelf.SelfDestruct.bound.loc13_41.2(%.loc13_41.2) +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Destroy.WithSelf.Op.loc13_41.2(%self.param: ref %struct_type.a.b) = "no_op"; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Destroy.WithSelf.SelfDestruct.loc13_41.2(%self.param: ref %struct_type.a.b) { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %Destroy.WithSelf.Op.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.Op.decl.5f94cf.2(%self.param) +// CHECK:STDOUT: %Destroy.WithSelf.SubobjectDestroy.call: init %empty_tuple.type = call generated.%Destroy.WithSelf.SubobjectDestroy.decl.763c71.2(%self.param) +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: diff --git a/toolchain/check/testdata/lambda/lambda.carbon b/toolchain/check/testdata/lambda/lambda.carbon index 4cddab267a5e..38b19f52354f 100644 --- a/toolchain/check/testdata/lambda/lambda.carbon +++ b/toolchain/check/testdata/lambda/lambda.carbon @@ -31,6 +31,21 @@ fn TestIdentity() { //@dump-sem-ir-end } +// --- terse_identity.carbon +library "[[@TEST_NAME]]"; + +fn TestTerse() { + //@dump-sem-ir-begin + alias terse_id_i32 = fn (x: i32) => x; + let unused e: i32 = terse_id_i32(42); + let unused f: i32 = (fn (x: i32) => x)(10); + //@dump-sem-ir-end +} + +//@dump-sem-ir-begin +alias terse_id_fn = fn (x: i32) => x; +//@dump-sem-ir-end + // --- generic_identity.carbon library "[[@TEST_NAME]]"; @@ -87,6 +102,15 @@ fn TestDefaultArgument() { let _: i32 = with_default(8); } +// --- terse_with_default_arguments.carbon +library "[[@TEST_NAME]]"; + +fn TestTerseWithDefaultArgument() { + alias terse_with_default = fn (x: i32 = 7) => x; + let _: i32 = terse_with_default(); + let _: i32 = terse_with_default(8); +} + // --- nested.carbon library "[[@TEST_NAME]]"; @@ -98,6 +122,14 @@ fn TestNested() { let _: i32 = outer(); } +// --- terse_nested.carbon +library "[[@TEST_NAME]]"; + +fn TestTerseNested() { + alias terse_outer = fn () => (fn (x: i32) => x)(3); + let _: i32 = terse_outer(); +} + // --- control_flow.carbon library "[[@TEST_NAME]]"; @@ -118,9 +150,11 @@ library "[[@TEST_NAME]]"; fn PassToGeneric[F: type](unused f: F) {} alias id_fn = fn (x: i32) -> i32 { return x; }; +alias terse_id_fn = fn (x: i32) => x; fn TestAsGenericArgument() { PassToGeneric(id_fn); + PassToGeneric(terse_id_fn); } // --- as_template_argument.carbon @@ -154,6 +188,7 @@ library "[[@TEST_NAME]]"; class InClass { var field: i32 = (fn (x: i32) -> i32 { return x; })(1); + var terse_field: i32 = (fn (x: i32) => x)(1); } // --- fail_self.carbon @@ -194,6 +229,12 @@ fn Outer() -> i32 { // CHECK:STDERR: fail_return.carbon:[[@LINE+2]]:4-10: note: there was no return type provided [ReturnTypeOmittedNote] // CHECK:STDERR: (fn () { return 1; }); + // CHECK:STDERR: fail_return.carbon:[[@LINE+2]]:10-15: error: cannot specify a return type with `=>` [ReturnTypeInTerseFunction] + // CHECK:STDERR: + (fn () -> i32 => 0); + // CHECK:STDERR: fail_return.carbon:[[@LINE+2]]:20-36: error: cannot specify a return form with `=>` [ReturnTypeInTerseFunction] + // CHECK:STDERR: + (fn (ref x: i32) ->? form(ref i32) => x); return 0; } @@ -213,6 +254,12 @@ fn F() { // CHECK:STDERR: return y; }); + // CHECK:STDERR: fail_unused.carbon:[[@LINE+5]]:8-13: warning: binding `x` unused [UnusedBinding] + // CHECK:STDERR: + // CHECK:STDERR: fail_unused.carbon:[[@LINE+3]]:23-28: error: variable `y` marked `unused` but used [UnusedButUsed] + // CHECK:STDERR: fail_unused.carbon:[[@LINE+2]]:34: note: usage here [UnusedButUsedHere] + // CHECK:STDERR: + (fn (x: i32, unused y: i32) => y); } // --- fail_break_continue.carbon @@ -260,6 +307,10 @@ fn F() { // CHECK:STDERR: fail_incomplete_type.carbon:[[@LINE+2]]:25: error: missing `return` at end of function with declared return type [MissingReturnStatement] // CHECK:STDERR: (fn () -> Incomplete {}); + // CHECK:STDERR: fail_incomplete_type.carbon:[[@LINE+3]]:27-28: error: function returns incomplete type `Incomplete` [IncompleteTypeInFunctionReturnType] + // CHECK:STDERR: fail_incomplete_type.carbon:[[@LINE-14]]:1-17: note: class was forward declared here [ClassForwardDeclaredHere] + // CHECK:STDERR: + (fn (p: Incomplete*) => *p); } // --- fail_conversion.carbon @@ -282,15 +333,16 @@ fn F() { // CHECK:STDERR: fail_todo_positional_params.carbon:[[@LINE+2]]:4-14: error: semantics TODO: `function with positional parameters` [SemanticsTodo] // CHECK:STDERR: (fn -> i32 { return 0; }); + // CHECK:STDERR: fail_todo_positional_params.carbon:[[@LINE+2]]:4-8: error: semantics TODO: `function with positional parameters` [SemanticsTodo] + // CHECK:STDERR: + (fn => 0); } -// --- fail_todo_terse_body.carbon +// --- dependent_terse_return.carbon library "[[@TEST_NAME]]"; fn F() { - // CHECK:STDERR: fail_todo_terse_body.carbon:[[@LINE+2]]:4-19: error: semantics TODO: `terse lambda body` [SemanticsTodo] - // CHECK:STDERR: - (fn (x: i32) => x); + (fn [T: Core.Copy](x: T) => x); } // CHECK:STDOUT: --- empty.carbon @@ -462,6 +514,189 @@ fn F() { // CHECK:STDOUT: return %Int.as.Copy.impl.Op.call // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: --- terse_identity.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: type: type = facet_type [concrete] +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %N: Core.IntLiteral = symbolic_binding N, 0 [symbolic] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] +// CHECK:STDOUT: %pattern_type.d94: type = pattern_type %i32 [concrete] +// CHECK:STDOUT: %x.param_patt: %pattern_type.d94 = value_param_pattern [concrete] +// CHECK:STDOUT: %x.patt: %pattern_type.d94 = wrapper_binding_pattern x, %x.param_patt [concrete] +// CHECK:STDOUT: %fn.type.975: type = fn_type @fn.loc5 [concrete] +// CHECK:STDOUT: %fn.460: %fn.type.975 = struct_value () [concrete] +// CHECK:STDOUT: %.0b3: Core.Form = init_form %i32 [concrete] +// CHECK:STDOUT: %return.param_patt.299: %pattern_type.d94 = out_param_pattern [concrete] +// CHECK:STDOUT: %return.patt.6fa: %pattern_type.d94 = return_slot_pattern %return.param_patt.299, %i32 [concrete] +// CHECK:STDOUT: %Copy.type: type = facet_type <@Copy> [concrete] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.type.a1d: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%N) [symbolic] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.3c1: %Int.as.Copy.impl.Op.type.a1d = struct_value () [symbolic] +// CHECK:STDOUT: %Copy.impl_witness.8af: = impl_witness imports.%Copy.impl_witness_table.f44, @Int.as.Copy.impl(%int_32) [concrete] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.type.648: type = fn_type @Int.as.Copy.impl.Op, @Int.as.Copy.impl(%int_32) [concrete] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.cae: %Int.as.Copy.impl.Op.type.648 = struct_value () [concrete] +// CHECK:STDOUT: %Copy.facet: %Copy.type = facet_value %i32, (%Copy.impl_witness.8af) [concrete] +// CHECK:STDOUT: %Copy.WithSelf.Op.type.f76: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet) [concrete] +// CHECK:STDOUT: %.2b9: type = fn_type_with_self_type %Copy.WithSelf.Op.type.f76, %Copy.facet [concrete] +// CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.cae, @Int.as.Copy.impl.Op(%int_32) [concrete] +// CHECK:STDOUT: %e.patt: %pattern_type.d94 = value_binding_pattern e [concrete] +// CHECK:STDOUT: %int_42.20e: Core.IntLiteral = int_value 42 [concrete] +// CHECK:STDOUT: %ImplicitAs.type.6b0: type = facet_type <@ImplicitAs, @ImplicitAs(%i32)> [concrete] +// CHECK:STDOUT: %To: Core.IntLiteral = symbolic_binding To, 0 [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%To) [symbolic] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.6f2: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6 = struct_value () [symbolic] +// CHECK:STDOUT: %ImplicitAs.impl_witness.d73: = impl_witness imports.%ImplicitAs.impl_witness_table.f53, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766: type = fn_type @Core.IntLiteral.as.ImplicitAs.impl.Convert, @Core.IntLiteral.as.ImplicitAs.impl(%int_32) [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60: %Core.IntLiteral.as.ImplicitAs.impl.Convert.type.766 = struct_value () [concrete] +// CHECK:STDOUT: %ImplicitAs.facet: %ImplicitAs.type.6b0 = facet_value Core.IntLiteral, (%ImplicitAs.impl_witness.d73) [concrete] +// CHECK:STDOUT: %ImplicitAs.WithSelf.Convert.type.58f: type = fn_type @ImplicitAs.WithSelf.Convert, @ImplicitAs.WithSelf(%i32, %ImplicitAs.facet) [concrete] +// CHECK:STDOUT: %.33a: type = fn_type_with_self_type %ImplicitAs.WithSelf.Convert.type.58f, %ImplicitAs.facet [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.81c: = bound_method %int_42.20e, %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60 [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn: = specific_function %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60, @Core.IntLiteral.as.ImplicitAs.impl.Convert(%int_32) [concrete] +// CHECK:STDOUT: %bound_method.98e: = bound_method %int_42.20e, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] +// CHECK:STDOUT: %int_42.db5: %i32 = int_value 42 [concrete] +// CHECK:STDOUT: %f.patt: %pattern_type.d94 = value_binding_pattern f [concrete] +// CHECK:STDOUT: %fn.type.aea: type = fn_type @fn.loc7 [concrete] +// CHECK:STDOUT: %fn.4d2: %fn.type.aea = struct_value () [concrete] +// CHECK:STDOUT: %int_10.64f: Core.IntLiteral = int_value 10 [concrete] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.02d: = bound_method %int_10.64f, %Core.IntLiteral.as.ImplicitAs.impl.Convert.f60 [concrete] +// CHECK:STDOUT: %bound_method.002: = bound_method %int_10.64f, %Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn [concrete] +// CHECK:STDOUT: %int_10.6a3: %i32 = int_value 10 [concrete] +// CHECK:STDOUT: %fn.type.d0d: type = fn_type @fn.loc12 [concrete] +// CHECK:STDOUT: %fn.5b0: %fn.type.d0d = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: imports { +// CHECK:STDOUT: %Core.import_ref.72f: @Int.as.Copy.impl.%Int.as.Copy.impl.Op.type (%Int.as.Copy.impl.Op.type.a1d) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Int.as.Copy.impl.%Int.as.Copy.impl.Op (constants.%Int.as.Copy.impl.Op.3c1)] +// CHECK:STDOUT: %Copy.impl_witness_table.f44 = impl_witness_table (%Core.import_ref.72f), @Int.as.Copy.impl [concrete] +// CHECK:STDOUT: %Core.import_ref.e47: @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert.type (%Core.IntLiteral.as.ImplicitAs.impl.Convert.type.3c6) = import_ref Core//prelude/parts/int, loc{{\d+_\d+}}, loaded [symbolic = @Core.IntLiteral.as.ImplicitAs.impl.%Core.IntLiteral.as.ImplicitAs.impl.Convert (constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.6f2)] +// CHECK:STDOUT: %ImplicitAs.impl_witness_table.f53 = impl_witness_table (%Core.import_ref.e47), @Core.IntLiteral.as.ImplicitAs.impl [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %fn.decl: %fn.type.d0d = fn_decl @fn.loc12 [concrete = constants.%fn.5b0] { +// CHECK:STDOUT: %x.param_patt: %pattern_type.d94 = value_param_pattern [concrete = constants.%x.param_patt] +// CHECK:STDOUT: %x.patt: %pattern_type.d94 = wrapper_binding_pattern x, %x.param_patt [concrete = constants.%x.patt] +// CHECK:STDOUT: %return.param_patt: %pattern_type.d94 = out_param_pattern [concrete = constants.%return.param_patt.299] +// CHECK:STDOUT: %return.patt: %pattern_type.d94 = return_slot_pattern %return.param_patt, %i32.loc12_36 [concrete = constants.%return.patt.6fa] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %x.param: %i32 = value_param call_param0 +// CHECK:STDOUT: %i32.loc12_28: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %x: %i32 = wrapper_binding x, %x.param +// CHECK:STDOUT: %i32.loc12_36: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %.loc12_36.2: Core.Form = init_form %i32.loc12_36 [concrete = constants.%.0b3] +// CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 +// CHECK:STDOUT: %return: ref %i32 = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %terse_id_fn: %fn.type.d0d = alias_binding terse_id_fn, %fn.decl [concrete = constants.%fn.5b0] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @TestTerse() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %fn.decl.loc5: %fn.type.975 = fn_decl @fn.loc5 [concrete = constants.%fn.460] { +// CHECK:STDOUT: %x.param_patt: %pattern_type.d94 = value_param_pattern [concrete = constants.%x.param_patt] +// CHECK:STDOUT: %x.patt: %pattern_type.d94 = wrapper_binding_pattern x, %x.param_patt [concrete = constants.%x.patt] +// CHECK:STDOUT: %return.param_patt: %pattern_type.d94 = out_param_pattern [concrete = constants.%return.param_patt.299] +// CHECK:STDOUT: %return.patt: %pattern_type.d94 = return_slot_pattern %return.param_patt, %i32.loc5_39 [concrete = constants.%return.patt.6fa] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %x.param: %i32 = value_param call_param0 +// CHECK:STDOUT: %i32.loc5_31: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %x: %i32 = wrapper_binding x, %x.param +// CHECK:STDOUT: %i32.loc5_39: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %.loc5_39.2: Core.Form = init_form %i32.loc5_39 [concrete = constants.%.0b3] +// CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 +// CHECK:STDOUT: %return: ref %i32 = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %terse_id_i32: %fn.type.975 = alias_binding terse_id_i32, %fn.decl.loc5 [concrete = constants.%fn.460] +// CHECK:STDOUT: %terse_id_i32.ref: %fn.type.975 = name_ref terse_id_i32, %terse_id_i32 [concrete = constants.%fn.460] +// CHECK:STDOUT: %int_42: Core.IntLiteral = int_value 42 [concrete = constants.%int_42.20e] +// CHECK:STDOUT: %impl.elem0.loc6: %.33a = impl_witness_access constants.%ImplicitAs.impl_witness.d73, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f60] +// CHECK:STDOUT: %bound_method.loc6_36.1: = bound_method %int_42, %impl.elem0.loc6 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.81c] +// CHECK:STDOUT: %specific_fn.loc6: = specific_function %impl.elem0.loc6, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] +// CHECK:STDOUT: %bound_method.loc6_36.2: = bound_method %int_42, %specific_fn.loc6 [concrete = constants.%bound_method.98e] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6: init %i32 = call %bound_method.loc6_36.2(%int_42) [concrete = constants.%int_42.db5] +// CHECK:STDOUT: %.loc6_36.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc6 [concrete = constants.%int_42.db5] +// CHECK:STDOUT: %.loc6_36.2: %i32 = converted %int_42, %.loc6_36.1 [concrete = constants.%int_42.db5] +// CHECK:STDOUT: %fn.call.loc6: init %i32 = call %terse_id_i32.ref(%.loc6_36.2) +// CHECK:STDOUT: %.loc6_38.1: %i32 = value_of_initializer %fn.call.loc6 +// CHECK:STDOUT: %.loc6_38.2: %i32 = converted %fn.call.loc6, %.loc6_38.1 +// CHECK:STDOUT: %i32.loc6: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %e: %i32 = wrapper_binding e, %.loc6_38.2 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %e.patt: %pattern_type.d94 = value_binding_pattern e [concrete = constants.%e.patt] +// CHECK:STDOUT: } +// CHECK:STDOUT: %fn.decl.loc7: %fn.type.aea = fn_decl @fn.loc7 [concrete = constants.%fn.4d2] { +// CHECK:STDOUT: %x.param_patt: %pattern_type.d94 = value_param_pattern [concrete = constants.%x.param_patt] +// CHECK:STDOUT: %x.patt: %pattern_type.d94 = wrapper_binding_pattern x, %x.param_patt [concrete = constants.%x.patt] +// CHECK:STDOUT: %return.param_patt: %pattern_type.d94 = out_param_pattern [concrete = constants.%return.param_patt.299] +// CHECK:STDOUT: %return.patt: %pattern_type.d94 = return_slot_pattern %return.param_patt, %i32.loc7_39 [concrete = constants.%return.patt.6fa] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %x.param: %i32 = value_param call_param0 +// CHECK:STDOUT: %i32.loc7_31: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %x: %i32 = wrapper_binding x, %x.param +// CHECK:STDOUT: %i32.loc7_39: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %.loc7_39.2: Core.Form = init_form %i32.loc7_39 [concrete = constants.%.0b3] +// CHECK:STDOUT: %return.param: ref %i32 = out_param call_param1 +// CHECK:STDOUT: %return: ref %i32 = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %int_10: Core.IntLiteral = int_value 10 [concrete = constants.%int_10.64f] +// CHECK:STDOUT: %impl.elem0.loc7: %.33a = impl_witness_access constants.%ImplicitAs.impl_witness.d73, element0 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.f60] +// CHECK:STDOUT: %bound_method.loc7_42.1: = bound_method %int_10, %impl.elem0.loc7 [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.bound.02d] +// CHECK:STDOUT: %specific_fn.loc7: = specific_function %impl.elem0.loc7, @Core.IntLiteral.as.ImplicitAs.impl.Convert(constants.%int_32) [concrete = constants.%Core.IntLiteral.as.ImplicitAs.impl.Convert.specific_fn] +// CHECK:STDOUT: %bound_method.loc7_42.2: = bound_method %int_10, %specific_fn.loc7 [concrete = constants.%bound_method.002] +// CHECK:STDOUT: %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7: init %i32 = call %bound_method.loc7_42.2(%int_10) [concrete = constants.%int_10.6a3] +// CHECK:STDOUT: %.loc7_42.1: %i32 = value_of_initializer %Core.IntLiteral.as.ImplicitAs.impl.Convert.call.loc7 [concrete = constants.%int_10.6a3] +// CHECK:STDOUT: %.loc7_42.2: %i32 = converted %int_10, %.loc7_42.1 [concrete = constants.%int_10.6a3] +// CHECK:STDOUT: %fn.call.loc7: init %i32 = call %fn.decl.loc7(%.loc7_42.2) +// CHECK:STDOUT: %.loc7_44.1: %i32 = value_of_initializer %fn.call.loc7 +// CHECK:STDOUT: %.loc7_44.2: %i32 = converted %fn.call.loc7, %.loc7_44.1 +// CHECK:STDOUT: %i32.loc7: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %f: %i32 = wrapper_binding f, %.loc7_44.2 +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %f.patt: %pattern_type.d94 = value_binding_pattern f [concrete = constants.%f.patt] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @fn.loc5(%x.param: %i32) -> out %return.param: %i32 [deduced] { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %x.ref: %i32 = name_ref x, %x +// CHECK:STDOUT: %.loc5_39.1: init %i32 = splice_block %Int.as.Copy.impl.Op.call { +// CHECK:STDOUT: %impl.elem0: %.2b9 = impl_witness_access constants.%Copy.impl_witness.8af, element0 [concrete = constants.%Int.as.Copy.impl.Op.cae] +// CHECK:STDOUT: %bound_method.loc5_39.1: = bound_method %x.ref, %impl.elem0 +// CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] +// CHECK:STDOUT: %bound_method.loc5_39.2: = bound_method %x.ref, %specific_fn +// CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc5_39.2(%x.ref) +// CHECK:STDOUT: } +// CHECK:STDOUT: return %.loc5_39.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @fn.loc7(%x.param: %i32) -> out %return.param: %i32 [deduced] { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %x.ref: %i32 = name_ref x, %x +// CHECK:STDOUT: %.loc7_39.1: init %i32 = splice_block %Int.as.Copy.impl.Op.call { +// CHECK:STDOUT: %impl.elem0: %.2b9 = impl_witness_access constants.%Copy.impl_witness.8af, element0 [concrete = constants.%Int.as.Copy.impl.Op.cae] +// CHECK:STDOUT: %bound_method.loc7_39.1: = bound_method %x.ref, %impl.elem0 +// CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] +// CHECK:STDOUT: %bound_method.loc7_39.2: = bound_method %x.ref, %specific_fn +// CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc7_39.2(%x.ref) +// CHECK:STDOUT: } +// CHECK:STDOUT: return %.loc7_39.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @fn.loc12(%x.param: %i32) -> out %return.param: %i32 [deduced] { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: %x.ref: %i32 = name_ref x, %x +// CHECK:STDOUT: %.loc12_36.1: init %i32 = splice_block %Int.as.Copy.impl.Op.call { +// CHECK:STDOUT: %impl.elem0: %.2b9 = impl_witness_access constants.%Copy.impl_witness.8af, element0 [concrete = constants.%Int.as.Copy.impl.Op.cae] +// CHECK:STDOUT: %bound_method.loc12_36.1: = bound_method %x.ref, %impl.elem0 +// CHECK:STDOUT: %specific_fn: = specific_function %impl.elem0, @Int.as.Copy.impl.Op(constants.%int_32) [concrete = constants.%Int.as.Copy.impl.Op.specific_fn] +// CHECK:STDOUT: %bound_method.loc12_36.2: = bound_method %x.ref, %specific_fn +// CHECK:STDOUT: %Int.as.Copy.impl.Op.call: init %i32 = call %bound_method.loc12_36.2(%x.ref) +// CHECK:STDOUT: } +// CHECK:STDOUT: return %.loc12_36.1 +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: --- generic_identity.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { @@ -523,7 +758,7 @@ fn F() { // CHECK:STDOUT: %x.param_patt.051: %pattern_type.d94 = value_param_pattern [concrete] // CHECK:STDOUT: %x.patt.0f7: %pattern_type.d94 = wrapper_binding_pattern x, %x.param_patt.051 [concrete] // CHECK:STDOUT: %return.patt.6fa: %pattern_type.d94 = return_slot_pattern %return.param_patt.299, %i32 [concrete] -// CHECK:STDOUT: %fn.specific_fn.fed: = specific_function %fn, @fn(%Copy.facet.95d) [concrete] +// CHECK:STDOUT: %fn.specific_fn.656: = specific_function %fn, @fn(%Copy.facet.95d) [concrete] // CHECK:STDOUT: %pattern_type.831: type = pattern_type bool [concrete] // CHECK:STDOUT: %d.patt: %pattern_type.831 = value_binding_pattern d [concrete] // CHECK:STDOUT: %true: bool = bool_literal true [concrete] @@ -534,7 +769,7 @@ fn F() { // CHECK:STDOUT: %.f34: Core.Form = init_form bool [concrete] // CHECK:STDOUT: %return.param_patt.5a1: %pattern_type.831 = out_param_pattern [concrete] // CHECK:STDOUT: %return.patt.c0c: %pattern_type.831 = return_slot_pattern %return.param_patt.5a1, bool [concrete] -// CHECK:STDOUT: %fn.specific_fn.f86: = specific_function %fn, @fn(%Copy.facet.7af) [concrete] +// CHECK:STDOUT: %fn.specific_fn.10a: = specific_function %fn, @fn(%Copy.facet.7af) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.f76: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.95d) [concrete] // CHECK:STDOUT: %.2b9: type = fn_type_with_self_type %Copy.WithSelf.Op.type.f76, %Copy.facet.95d [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.cae, @Int.as.Copy.impl.Op(%int_32) [concrete] @@ -610,7 +845,7 @@ fn F() { // CHECK:STDOUT: %.loc6_43.1: %Copy.type = converted constants.%i32, %Copy.facet.loc6_43.1 [concrete = constants.%Copy.facet.95d] // CHECK:STDOUT: %Copy.facet.loc6_43.2: %Copy.type = facet_value constants.%i32, (constants.%Copy.impl_witness.8af) [concrete = constants.%Copy.facet.95d] // CHECK:STDOUT: %.loc6_43.2: %Copy.type = converted constants.%i32, %Copy.facet.loc6_43.2 [concrete = constants.%Copy.facet.95d] -// CHECK:STDOUT: %fn.specific_fn.loc6: = specific_function %generic_id.ref.loc6, @fn(constants.%Copy.facet.95d) [concrete = constants.%fn.specific_fn.fed] +// CHECK:STDOUT: %fn.specific_fn.loc6: = specific_function %generic_id.ref.loc6, @fn(constants.%Copy.facet.95d) [concrete = constants.%fn.specific_fn.656] // CHECK:STDOUT: %fn.call.loc6: init %i32 = call %fn.specific_fn.loc6(%.loc6_37.2) // CHECK:STDOUT: %.loc6_43.3: %i32 = value_of_initializer %fn.call.loc6 // CHECK:STDOUT: %.loc6_43.4: %i32 = converted %fn.call.loc6, %.loc6_43.3 @@ -625,7 +860,7 @@ fn F() { // CHECK:STDOUT: %.loc7_39.1: %Copy.type = converted bool, %Copy.facet.loc7_39.1 [concrete = constants.%Copy.facet.7af] // CHECK:STDOUT: %Copy.facet.loc7_39.2: %Copy.type = facet_value bool, (constants.%Copy.impl_witness.5e1) [concrete = constants.%Copy.facet.7af] // CHECK:STDOUT: %.loc7_39.2: %Copy.type = converted bool, %Copy.facet.loc7_39.2 [concrete = constants.%Copy.facet.7af] -// CHECK:STDOUT: %fn.specific_fn.loc7: = specific_function %generic_id.ref.loc7, @fn(constants.%Copy.facet.7af) [concrete = constants.%fn.specific_fn.f86] +// CHECK:STDOUT: %fn.specific_fn.loc7: = specific_function %generic_id.ref.loc7, @fn(constants.%Copy.facet.7af) [concrete = constants.%fn.specific_fn.10a] // CHECK:STDOUT: %fn.call.loc7: init bool = call %fn.specific_fn.loc7(%true) // CHECK:STDOUT: %.loc7_39.3: bool = value_of_initializer %fn.call.loc7 // CHECK:STDOUT: %.loc7_39.4: bool = converted %fn.call.loc7, %.loc7_39.3 @@ -739,24 +974,24 @@ fn F() { // CHECK:STDOUT: %.1ce700.2: Core.Form = init_form %T.as_type [symbolic] // CHECK:STDOUT: %return.param_patt.bf4475.2: %pattern_type.2c66b4.2 = out_param_pattern [symbolic] // CHECK:STDOUT: %return.patt.f44: %pattern_type.2c66b4.2 = return_slot_pattern %return.param_patt.bf4475.2, %T.as_type [symbolic] -// CHECK:STDOUT: %fn.type.c16: type = fn_type @fn.loc5 [concrete] -// CHECK:STDOUT: %fn.415: %fn.type.c16 = struct_value () [concrete] +// CHECK:STDOUT: %fn.type.d4a: type = fn_type @fn.loc5 [concrete] +// CHECK:STDOUT: %fn.9dd: %fn.type.d4a = struct_value () [concrete] // CHECK:STDOUT: %require_complete.d83: = require_complete_type %T.as_type [symbolic] // CHECK:STDOUT: %y.param_patt.ec0: %pattern_type.2c66b4.2 = value_param_pattern [symbolic] // CHECK:STDOUT: %y.patt.d6d: %pattern_type.2c66b4.2 = wrapper_binding_pattern y, %y.param_patt.ec0 [symbolic] -// CHECK:STDOUT: %fn.type.48e: type = fn_type @fn.loc6, @fn.loc5(%T.f84) [symbolic] -// CHECK:STDOUT: %fn.7f4: %fn.type.48e = struct_value () [symbolic] +// CHECK:STDOUT: %fn.type.7d1: type = fn_type @fn.loc6, @fn.loc5(%T.f84) [symbolic] +// CHECK:STDOUT: %fn.dd0: %fn.type.7d1 = struct_value () [symbolic] // CHECK:STDOUT: %z.param_patt.b42: %pattern_type.2c66b4.2 = value_param_pattern [symbolic] // CHECK:STDOUT: %z.patt.513: %pattern_type.2c66b4.2 = wrapper_binding_pattern z, %z.param_patt.b42 [symbolic] -// CHECK:STDOUT: %fn.type.3f9: type = fn_type @fn.loc7, @fn.loc6(%T.f84) [symbolic] -// CHECK:STDOUT: %fn.8a9: %fn.type.3f9 = struct_value () [symbolic] +// CHECK:STDOUT: %fn.type.531: type = fn_type @fn.loc7, @fn.loc6(%T.f84) [symbolic] +// CHECK:STDOUT: %fn.dc1: %fn.type.531 = struct_value () [symbolic] // CHECK:STDOUT: %Copy.lookup_impl_witness.d9c: = lookup_impl_witness %T.f84, @Copy [symbolic] // CHECK:STDOUT: %Copy.WithSelf.Op.type.d2f9c3.2: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%T.f84) [symbolic] // CHECK:STDOUT: %.8bf: type = fn_type_with_self_type %Copy.WithSelf.Op.type.d2f9c3.2, %T.f84 [symbolic] // CHECK:STDOUT: %impl.elem0.bb5: %.8bf = impl_witness_access %Copy.lookup_impl_witness.d9c, element0 [symbolic] // CHECK:STDOUT: %specific_impl_fn.2ac: = specific_impl_function %impl.elem0.bb5, @Copy.WithSelf.Op(%T.f84) [symbolic] -// CHECK:STDOUT: %fn.specific_fn.127: = specific_function %fn.8a9, @fn.loc7(%T.f84) [symbolic] -// CHECK:STDOUT: %fn.specific_fn.3ca: = specific_function %fn.7f4, @fn.loc6(%T.f84) [symbolic] +// CHECK:STDOUT: %fn.specific_fn.6d4: = specific_function %fn.dc1, @fn.loc7(%T.f84) [symbolic] +// CHECK:STDOUT: %fn.specific_fn.eed: = specific_function %fn.dd0, @fn.loc6(%T.f84) [symbolic] // CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] // CHECK:STDOUT: %Int.type: type = parameterized_class_name_type @Int [concrete] // CHECK:STDOUT: %Int.generic: %Int.type = struct_value () [concrete] @@ -794,7 +1029,7 @@ fn F() { // CHECK:STDOUT: %x.param_patt.051: %pattern_type.d94 = value_param_pattern [concrete] // CHECK:STDOUT: %x.patt.0f7: %pattern_type.d94 = wrapper_binding_pattern x, %x.param_patt.051 [concrete] // CHECK:STDOUT: %return.patt.6fa: %pattern_type.d94 = return_slot_pattern %return.param_patt.299, %i32 [concrete] -// CHECK:STDOUT: %fn.specific_fn.ac4: = specific_function %fn.415, @fn.loc5(%Copy.facet.95d) [concrete] +// CHECK:STDOUT: %fn.specific_fn.d27: = specific_function %fn.9dd, @fn.loc5(%Copy.facet.95d) [concrete] // CHECK:STDOUT: %pattern_type.831: type = pattern_type bool [concrete] // CHECK:STDOUT: %d.patt: %pattern_type.831 = value_binding_pattern d [concrete] // CHECK:STDOUT: %true: bool = bool_literal true [concrete] @@ -805,28 +1040,28 @@ fn F() { // CHECK:STDOUT: %.f34: Core.Form = init_form bool [concrete] // CHECK:STDOUT: %return.param_patt.5a1: %pattern_type.831 = out_param_pattern [concrete] // CHECK:STDOUT: %return.patt.c0c: %pattern_type.831 = return_slot_pattern %return.param_patt.5a1, bool [concrete] -// CHECK:STDOUT: %fn.specific_fn.c23: = specific_function %fn.415, @fn.loc5(%Copy.facet.7af) [concrete] -// CHECK:STDOUT: %fn.type.7b0: type = fn_type @fn.loc6, @fn.loc5(%Copy.facet.95d) [concrete] -// CHECK:STDOUT: %fn.758: %fn.type.7b0 = struct_value () [concrete] +// CHECK:STDOUT: %fn.specific_fn.8de: = specific_function %fn.9dd, @fn.loc5(%Copy.facet.7af) [concrete] +// CHECK:STDOUT: %fn.type.949: type = fn_type @fn.loc6, @fn.loc5(%Copy.facet.95d) [concrete] +// CHECK:STDOUT: %fn.62e: %fn.type.949 = struct_value () [concrete] // CHECK:STDOUT: %y.param_patt.665: %pattern_type.d94 = value_param_pattern [concrete] // CHECK:STDOUT: %y.patt.e18: %pattern_type.d94 = wrapper_binding_pattern y, %y.param_patt.665 [concrete] -// CHECK:STDOUT: %fn.specific_fn.2f6: = specific_function %fn.758, @fn.loc6(%Copy.facet.95d) [concrete] +// CHECK:STDOUT: %fn.specific_fn.458: = specific_function %fn.62e, @fn.loc6(%Copy.facet.95d) [concrete] // CHECK:STDOUT: %complete_type.127: = complete_type_witness bool [concrete] -// CHECK:STDOUT: %fn.type.641: type = fn_type @fn.loc6, @fn.loc5(%Copy.facet.7af) [concrete] -// CHECK:STDOUT: %fn.3e7: %fn.type.641 = struct_value () [concrete] +// CHECK:STDOUT: %fn.type.319: type = fn_type @fn.loc6, @fn.loc5(%Copy.facet.7af) [concrete] +// CHECK:STDOUT: %fn.1af: %fn.type.319 = struct_value () [concrete] // CHECK:STDOUT: %y.param_patt.077: %pattern_type.831 = value_param_pattern [concrete] // CHECK:STDOUT: %y.patt.252: %pattern_type.831 = wrapper_binding_pattern y, %y.param_patt.077 [concrete] -// CHECK:STDOUT: %fn.specific_fn.421: = specific_function %fn.3e7, @fn.loc6(%Copy.facet.7af) [concrete] -// CHECK:STDOUT: %fn.type.8ae: type = fn_type @fn.loc7, @fn.loc6(%Copy.facet.95d) [concrete] -// CHECK:STDOUT: %fn.c3f: %fn.type.8ae = struct_value () [concrete] +// CHECK:STDOUT: %fn.specific_fn.c2d: = specific_function %fn.1af, @fn.loc6(%Copy.facet.7af) [concrete] +// CHECK:STDOUT: %fn.type.70b: type = fn_type @fn.loc7, @fn.loc6(%Copy.facet.95d) [concrete] +// CHECK:STDOUT: %fn.37d: %fn.type.70b = struct_value () [concrete] // CHECK:STDOUT: %z.param_patt.896: %pattern_type.d94 = value_param_pattern [concrete] // CHECK:STDOUT: %z.patt.660: %pattern_type.d94 = wrapper_binding_pattern z, %z.param_patt.896 [concrete] -// CHECK:STDOUT: %fn.specific_fn.0fa: = specific_function %fn.c3f, @fn.loc7(%Copy.facet.95d) [concrete] -// CHECK:STDOUT: %fn.type.d03: type = fn_type @fn.loc7, @fn.loc6(%Copy.facet.7af) [concrete] -// CHECK:STDOUT: %fn.b92: %fn.type.d03 = struct_value () [concrete] +// CHECK:STDOUT: %fn.specific_fn.6d6: = specific_function %fn.37d, @fn.loc7(%Copy.facet.95d) [concrete] +// CHECK:STDOUT: %fn.type.c1a: type = fn_type @fn.loc7, @fn.loc6(%Copy.facet.7af) [concrete] +// CHECK:STDOUT: %fn.d5a: %fn.type.c1a = struct_value () [concrete] // CHECK:STDOUT: %z.param_patt.a49: %pattern_type.831 = value_param_pattern [concrete] // CHECK:STDOUT: %z.patt.246: %pattern_type.831 = wrapper_binding_pattern z, %z.param_patt.a49 [concrete] -// CHECK:STDOUT: %fn.specific_fn.c7b: = specific_function %fn.b92, @fn.loc7(%Copy.facet.7af) [concrete] +// CHECK:STDOUT: %fn.specific_fn.8ab: = specific_function %fn.d5a, @fn.loc7(%Copy.facet.7af) [concrete] // CHECK:STDOUT: %Copy.WithSelf.Op.type.f76: type = fn_type @Copy.WithSelf.Op, @Copy.WithSelf(%Copy.facet.95d) [concrete] // CHECK:STDOUT: %.2b9: type = fn_type_with_self_type %Copy.WithSelf.Op.type.f76, %Copy.facet.95d [concrete] // CHECK:STDOUT: %Int.as.Copy.impl.Op.specific_fn: = specific_function %Int.as.Copy.impl.Op.cae, @Int.as.Copy.impl.Op(%int_32) [concrete] @@ -859,7 +1094,7 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: fn @TestGenericNested() { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %fn.decl: %fn.type.c16 = fn_decl @fn.loc5 [concrete = constants.%fn.415] { +// CHECK:STDOUT: %fn.decl: %fn.type.d4a = fn_decl @fn.loc5 [concrete = constants.%fn.9dd] { // CHECK:STDOUT: %T.patt.loc5_27.1: %pattern_type.e81 = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc5_27.2 (constants.%T.patt.fe6)] // CHECK:STDOUT: %x.param_patt.loc5_41.1: @fn.loc5.%pattern_type (%pattern_type.2c66b4.2) = value_param_pattern [symbolic = %x.param_patt.loc5_41.2 (constants.%x.param_patt.1f4)] // CHECK:STDOUT: %x.patt.loc5_41.1: @fn.loc5.%pattern_type (%pattern_type.2c66b4.2) = wrapper_binding_pattern x, %x.param_patt.loc5_41.1 [symbolic = %x.patt.loc5_41.2 (constants.%x.patt.cec)] @@ -886,8 +1121,8 @@ fn F() { // CHECK:STDOUT: %return.param: ref @fn.loc5.%T.as_type.loc5_43.1 (%T.as_type) = out_param call_param1 // CHECK:STDOUT: %return: ref @fn.loc5.%T.as_type.loc5_43.1 (%T.as_type) = return_slot %return.param // CHECK:STDOUT: } -// CHECK:STDOUT: %generic_id: %fn.type.c16 = alias_binding generic_id, %fn.decl [concrete = constants.%fn.415] -// CHECK:STDOUT: %generic_id.ref.loc12: %fn.type.c16 = name_ref generic_id, %generic_id [concrete = constants.%fn.415] +// CHECK:STDOUT: %generic_id: %fn.type.d4a = alias_binding generic_id, %fn.decl [concrete = constants.%fn.9dd] +// CHECK:STDOUT: %generic_id.ref.loc12: %fn.type.d4a = name_ref generic_id, %generic_id [concrete = constants.%fn.9dd] // CHECK:STDOUT: %int_42: Core.IntLiteral = int_value 42 [concrete = constants.%int_42.20e] // CHECK:STDOUT: %i32.loc12_40: type = type_literal constants.%i32 [concrete = constants.%i32] // CHECK:STDOUT: %impl.elem0: %.979 = impl_witness_access constants.%As.impl_witness.e09, element0 [concrete = constants.%Core.IntLiteral.as.As.impl.Convert.94f] @@ -901,7 +1136,7 @@ fn F() { // CHECK:STDOUT: %.loc12_43.1: %Copy.type = converted constants.%i32, %Copy.facet.loc12_43.1 [concrete = constants.%Copy.facet.95d] // CHECK:STDOUT: %Copy.facet.loc12_43.2: %Copy.type = facet_value constants.%i32, (constants.%Copy.impl_witness.8af) [concrete = constants.%Copy.facet.95d] // CHECK:STDOUT: %.loc12_43.2: %Copy.type = converted constants.%i32, %Copy.facet.loc12_43.2 [concrete = constants.%Copy.facet.95d] -// CHECK:STDOUT: %fn.specific_fn.loc12: = specific_function %generic_id.ref.loc12, @fn.loc5(constants.%Copy.facet.95d) [concrete = constants.%fn.specific_fn.ac4] +// CHECK:STDOUT: %fn.specific_fn.loc12: = specific_function %generic_id.ref.loc12, @fn.loc5(constants.%Copy.facet.95d) [concrete = constants.%fn.specific_fn.d27] // CHECK:STDOUT: %fn.call.loc12: init %i32 = call %fn.specific_fn.loc12(%.loc12_37.2) // CHECK:STDOUT: %.loc12_43.3: %i32 = value_of_initializer %fn.call.loc12 // CHECK:STDOUT: %.loc12_43.4: %i32 = converted %fn.call.loc12, %.loc12_43.3 @@ -910,13 +1145,13 @@ fn F() { // CHECK:STDOUT: name_binding_decl { // CHECK:STDOUT: %c.patt: %pattern_type.d94 = value_binding_pattern c [concrete = constants.%c.patt] // CHECK:STDOUT: } -// CHECK:STDOUT: %generic_id.ref.loc13: %fn.type.c16 = name_ref generic_id, %generic_id [concrete = constants.%fn.415] +// CHECK:STDOUT: %generic_id.ref.loc13: %fn.type.d4a = name_ref generic_id, %generic_id [concrete = constants.%fn.9dd] // CHECK:STDOUT: %true: bool = bool_literal true [concrete = constants.%true] // CHECK:STDOUT: %Copy.facet.loc13_39.1: %Copy.type = facet_value bool, (constants.%Copy.impl_witness.5e1) [concrete = constants.%Copy.facet.7af] // CHECK:STDOUT: %.loc13_39.1: %Copy.type = converted bool, %Copy.facet.loc13_39.1 [concrete = constants.%Copy.facet.7af] // CHECK:STDOUT: %Copy.facet.loc13_39.2: %Copy.type = facet_value bool, (constants.%Copy.impl_witness.5e1) [concrete = constants.%Copy.facet.7af] // CHECK:STDOUT: %.loc13_39.2: %Copy.type = converted bool, %Copy.facet.loc13_39.2 [concrete = constants.%Copy.facet.7af] -// CHECK:STDOUT: %fn.specific_fn.loc13: = specific_function %generic_id.ref.loc13, @fn.loc5(constants.%Copy.facet.7af) [concrete = constants.%fn.specific_fn.c23] +// CHECK:STDOUT: %fn.specific_fn.loc13: = specific_function %generic_id.ref.loc13, @fn.loc5(constants.%Copy.facet.7af) [concrete = constants.%fn.specific_fn.8de] // CHECK:STDOUT: %fn.call.loc13: init bool = call %fn.specific_fn.loc13(%true) // CHECK:STDOUT: %.loc13_39.3: bool = value_of_initializer %fn.call.loc13 // CHECK:STDOUT: %.loc13_39.4: bool = converted %fn.call.loc13, %.loc13_39.3 @@ -941,13 +1176,13 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.as_type.loc5_43.1 [symbolic = %require_complete (constants.%require_complete.d83)] -// CHECK:STDOUT: %fn.type: type = fn_type @fn.loc6, @fn.loc5(%T.loc5_27.1) [symbolic = %fn.type (constants.%fn.type.48e)] -// CHECK:STDOUT: %fn: @fn.loc5.%fn.type (%fn.type.48e) = struct_value () [symbolic = %fn (constants.%fn.7f4)] -// CHECK:STDOUT: %fn.specific_fn.loc6_26.2: = specific_function %fn, @fn.loc6(%T.loc5_27.1) [symbolic = %fn.specific_fn.loc6_26.2 (constants.%fn.specific_fn.3ca)] +// CHECK:STDOUT: %fn.type: type = fn_type @fn.loc6, @fn.loc5(%T.loc5_27.1) [symbolic = %fn.type (constants.%fn.type.7d1)] +// CHECK:STDOUT: %fn: @fn.loc5.%fn.type (%fn.type.7d1) = struct_value () [symbolic = %fn (constants.%fn.dd0)] +// CHECK:STDOUT: %fn.specific_fn.loc6_26.2: = specific_function %fn, @fn.loc6(%T.loc5_27.1) [symbolic = %fn.specific_fn.loc6_26.2 (constants.%fn.specific_fn.eed)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%x.param: @fn.loc5.%T.as_type.loc5_43.1 (%T.as_type)) -> out %return.param: @fn.loc5.%T.as_type.loc5_43.1 (%T.as_type) { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %fn.decl: @fn.loc5.%fn.type (%fn.type.48e) = fn_decl @fn.loc6 [symbolic = @fn.loc5.%fn (constants.%fn.7f4)] { +// CHECK:STDOUT: %fn.decl: @fn.loc5.%fn.type (%fn.type.7d1) = fn_decl @fn.loc6 [symbolic = @fn.loc5.%fn (constants.%fn.dd0)] { // CHECK:STDOUT: %y.param_patt.loc6_16.1: @fn.loc6.%pattern_type (%pattern_type.2c66b4.2) = value_param_pattern [symbolic = %y.param_patt.loc6_16.2 (constants.%y.param_patt.ec0)] // CHECK:STDOUT: %y.patt.loc6_16.1: @fn.loc6.%pattern_type (%pattern_type.2c66b4.2) = wrapper_binding_pattern y, %y.param_patt.loc6_16.1 [symbolic = %y.patt.loc6_16.2 (constants.%y.patt.d6d)] // CHECK:STDOUT: %return.param_patt.loc6_24.1: @fn.loc6.%pattern_type (%pattern_type.2c66b4.2) = out_param_pattern [symbolic = %return.param_patt.loc6_24.2 (constants.%return.param_patt.bf4475.2)] @@ -972,7 +1207,7 @@ fn F() { // CHECK:STDOUT: %x.ref: @fn.loc5.%T.as_type.loc5_43.1 (%T.as_type) = name_ref x, %x // CHECK:STDOUT: %.loc10_8.1: %Copy.type = converted constants.%T.as_type, constants.%T.f84 [symbolic = %T.loc5_27.1 (constants.%T.f84)] // CHECK:STDOUT: %.loc10_8.2: %Copy.type = converted constants.%T.as_type, constants.%T.f84 [symbolic = %T.loc5_27.1 (constants.%T.f84)] -// CHECK:STDOUT: %fn.specific_fn.loc6_26.1: = specific_function %fn.decl, @fn.loc6(constants.%T.f84) [symbolic = %fn.specific_fn.loc6_26.2 (constants.%fn.specific_fn.3ca)] +// CHECK:STDOUT: %fn.specific_fn.loc6_26.1: = specific_function %fn.decl, @fn.loc6(constants.%T.f84) [symbolic = %fn.specific_fn.loc6_26.2 (constants.%fn.specific_fn.eed)] // CHECK:STDOUT: %.loc5_49.1: ref @fn.loc5.%T.as_type.loc5_43.1 (%T.as_type) = splice_block %return.param {} // CHECK:STDOUT: %fn.call: init @fn.loc5.%T.as_type.loc5_43.1 (%T.as_type) to %.loc5_49.1 = call %fn.specific_fn.loc6_26.1(%x.ref) // CHECK:STDOUT: return %fn.call to %return.param @@ -991,13 +1226,13 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete: = require_complete_type %T.as_type.loc6_18.1 [symbolic = %require_complete (constants.%require_complete.d83)] -// CHECK:STDOUT: %fn.type: type = fn_type @fn.loc7, @fn.loc6(%T) [symbolic = %fn.type (constants.%fn.type.3f9)] -// CHECK:STDOUT: %fn: @fn.loc6.%fn.type (%fn.type.3f9) = struct_value () [symbolic = %fn (constants.%fn.8a9)] -// CHECK:STDOUT: %fn.specific_fn.loc7_28.2: = specific_function %fn, @fn.loc7(%T) [symbolic = %fn.specific_fn.loc7_28.2 (constants.%fn.specific_fn.127)] +// CHECK:STDOUT: %fn.type: type = fn_type @fn.loc7, @fn.loc6(%T) [symbolic = %fn.type (constants.%fn.type.531)] +// CHECK:STDOUT: %fn: @fn.loc6.%fn.type (%fn.type.531) = struct_value () [symbolic = %fn (constants.%fn.dc1)] +// CHECK:STDOUT: %fn.specific_fn.loc7_28.2: = specific_function %fn, @fn.loc7(%T) [symbolic = %fn.specific_fn.loc7_28.2 (constants.%fn.specific_fn.6d4)] // CHECK:STDOUT: // CHECK:STDOUT: fn(%y.param: @fn.loc6.%T.as_type.loc6_18.1 (%T.as_type)) -> out %return.param: @fn.loc6.%T.as_type.loc6_18.1 (%T.as_type) { // CHECK:STDOUT: !entry: -// CHECK:STDOUT: %fn.decl: @fn.loc6.%fn.type (%fn.type.3f9) = fn_decl @fn.loc7 [symbolic = @fn.loc6.%fn (constants.%fn.8a9)] { +// CHECK:STDOUT: %fn.decl: @fn.loc6.%fn.type (%fn.type.531) = fn_decl @fn.loc7 [symbolic = @fn.loc6.%fn (constants.%fn.dc1)] { // CHECK:STDOUT: %z.param_patt.loc7_18.1: @fn.loc7.%pattern_type (%pattern_type.2c66b4.2) = value_param_pattern [symbolic = %z.param_patt.loc7_18.2 (constants.%z.param_patt.b42)] // CHECK:STDOUT: %z.patt.loc7_18.1: @fn.loc7.%pattern_type (%pattern_type.2c66b4.2) = wrapper_binding_pattern z, %z.param_patt.loc7_18.1 [symbolic = %z.patt.loc7_18.2 (constants.%z.patt.513)] // CHECK:STDOUT: %return.param_patt.loc7_26.1: @fn.loc7.%pattern_type (%pattern_type.2c66b4.2) = out_param_pattern [symbolic = %return.param_patt.loc7_26.2 (constants.%return.param_patt.bf4475.2)] @@ -1022,7 +1257,7 @@ fn F() { // CHECK:STDOUT: %y.ref: @fn.loc6.%T.as_type.loc6_18.1 (%T.as_type) = name_ref y, %y // CHECK:STDOUT: %.loc9_10.1: %Copy.type = converted constants.%T.as_type, constants.%T.f84 [symbolic = %T (constants.%T.f84)] // CHECK:STDOUT: %.loc9_10.2: %Copy.type = converted constants.%T.as_type, constants.%T.f84 [symbolic = %T (constants.%T.f84)] -// CHECK:STDOUT: %fn.specific_fn.loc7_28.1: = specific_function %fn.decl, @fn.loc7(constants.%T.f84) [symbolic = %fn.specific_fn.loc7_28.2 (constants.%fn.specific_fn.127)] +// CHECK:STDOUT: %fn.specific_fn.loc7_28.1: = specific_function %fn.decl, @fn.loc7(constants.%T.f84) [symbolic = %fn.specific_fn.loc7_28.2 (constants.%fn.specific_fn.6d4)] // CHECK:STDOUT: %.loc6_24.1: ref @fn.loc6.%T.as_type.loc6_18.1 (%T.as_type) = splice_block %return.param {} // CHECK:STDOUT: %fn.call: init @fn.loc6.%T.as_type.loc6_18.1 (%T.as_type) to %.loc6_24.1 = call %fn.specific_fn.loc7_28.1(%y.ref) // CHECK:STDOUT: return %fn.call to %return.param @@ -1086,9 +1321,9 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%require_complete.d83 -// CHECK:STDOUT: %fn.type => constants.%fn.type.3f9 -// CHECK:STDOUT: %fn => constants.%fn.8a9 -// CHECK:STDOUT: %fn.specific_fn.loc7_28.2 => constants.%fn.specific_fn.127 +// CHECK:STDOUT: %fn.type => constants.%fn.type.531 +// CHECK:STDOUT: %fn => constants.%fn.dc1 +// CHECK:STDOUT: %fn.specific_fn.loc7_28.2 => constants.%fn.specific_fn.6d4 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @fn.loc7(constants.%T.f84) { @@ -1123,9 +1358,9 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.f8a -// CHECK:STDOUT: %fn.type => constants.%fn.type.7b0 -// CHECK:STDOUT: %fn => constants.%fn.758 -// CHECK:STDOUT: %fn.specific_fn.loc6_26.2 => constants.%fn.specific_fn.2f6 +// CHECK:STDOUT: %fn.type => constants.%fn.type.949 +// CHECK:STDOUT: %fn => constants.%fn.62e +// CHECK:STDOUT: %fn.specific_fn.loc6_26.2 => constants.%fn.specific_fn.458 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @fn.loc5(constants.%Copy.facet.7af) { @@ -1141,9 +1376,9 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.127 -// CHECK:STDOUT: %fn.type => constants.%fn.type.641 -// CHECK:STDOUT: %fn => constants.%fn.3e7 -// CHECK:STDOUT: %fn.specific_fn.loc6_26.2 => constants.%fn.specific_fn.421 +// CHECK:STDOUT: %fn.type => constants.%fn.type.319 +// CHECK:STDOUT: %fn => constants.%fn.1af +// CHECK:STDOUT: %fn.specific_fn.loc6_26.2 => constants.%fn.specific_fn.c2d // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @fn.loc6(constants.%Copy.facet.95d) { @@ -1158,9 +1393,9 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.f8a -// CHECK:STDOUT: %fn.type => constants.%fn.type.8ae -// CHECK:STDOUT: %fn => constants.%fn.c3f -// CHECK:STDOUT: %fn.specific_fn.loc7_28.2 => constants.%fn.specific_fn.0fa +// CHECK:STDOUT: %fn.type => constants.%fn.type.70b +// CHECK:STDOUT: %fn => constants.%fn.37d +// CHECK:STDOUT: %fn.specific_fn.loc7_28.2 => constants.%fn.specific_fn.6d6 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @fn.loc6(constants.%Copy.facet.7af) { @@ -1175,9 +1410,9 @@ fn F() { // CHECK:STDOUT: // CHECK:STDOUT: !definition: // CHECK:STDOUT: %require_complete => constants.%complete_type.127 -// CHECK:STDOUT: %fn.type => constants.%fn.type.d03 -// CHECK:STDOUT: %fn => constants.%fn.b92 -// CHECK:STDOUT: %fn.specific_fn.loc7_28.2 => constants.%fn.specific_fn.c7b +// CHECK:STDOUT: %fn.type => constants.%fn.type.c1a +// CHECK:STDOUT: %fn => constants.%fn.d5a +// CHECK:STDOUT: %fn.specific_fn.loc7_28.2 => constants.%fn.specific_fn.8ab // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: specific @fn.loc7(constants.%Copy.facet.95d) { diff --git a/toolchain/diagnostics/kind.def b/toolchain/diagnostics/kind.def index c0c9e7894c16..24d0700d33a8 100644 --- a/toolchain/diagnostics/kind.def +++ b/toolchain/diagnostics/kind.def @@ -285,6 +285,7 @@ CARBON_DIAGNOSTIC_KIND(FunctionRedeclEvaluationModeDiffers) CARBON_DIAGNOSTIC_KIND(InvalidMainRunParameters) CARBON_DIAGNOSTIC_KIND(InvalidMainRunReturnType) CARBON_DIAGNOSTIC_KIND(MissingReturnStatement) +CARBON_DIAGNOSTIC_KIND(ReturnTypeInTerseFunction) CARBON_DIAGNOSTIC_KIND(UnknownBuiltinFunctionName) CARBON_DIAGNOSTIC_KIND(InvalidBuiltinSignature) diff --git a/toolchain/language_server/handle_document_symbol.cpp b/toolchain/language_server/handle_document_symbol.cpp index 2b46b53fda0c..8fb41757b42f 100644 --- a/toolchain/language_server/handle_document_symbol.cpp +++ b/toolchain/language_server/handle_document_symbol.cpp @@ -146,6 +146,7 @@ auto HandleDocumentSymbol( symbol_kind = clang::clangd::SymbolKind::Function; break; case Parse::NodeKind::FunctionDefinitionStart: + case Parse::NodeKind::FunctionTerseDefinitionStart: case Parse::NodeKind::BuiltinFunctionDefinitionStart: symbol_kind = clang::clangd::SymbolKind::Function; break; diff --git a/toolchain/language_server/testdata/document_symbol/fn_definition.carbon b/toolchain/language_server/testdata/document_symbol/fn_definition.carbon index 7e2d4c2a82c5..f3aace90aa1f 100644 --- a/toolchain/language_server/testdata/document_symbol/fn_definition.carbon +++ b/toolchain/language_server/testdata/document_symbol/fn_definition.carbon @@ -11,8 +11,8 @@ // --- fn_definition.carbon fn None(); fn Braced() {} -fn Terse() => 42; -fn Builtin() = "int.make_type_32"; +fn Terse() => (); +fn Builtin() -> type = "int_literal.make_type"; // --- STDIN [[@LSP-CALL:initialize:"capabilities": {}]] @@ -48,29 +48,13 @@ fn Builtin() = "int.make_type_32"; // CHECK:STDOUT: "typeDefinitionProvider": true // CHECK:STDOUT: } // CHECK:STDOUT: } -// CHECK:STDOUT: }Content-Length: 490{{\r}} +// CHECK:STDOUT: }Content-Length: 153{{\r}} // CHECK:STDOUT: {{\r}} // CHECK:STDOUT: { // CHECK:STDOUT: "jsonrpc": "2.0", // CHECK:STDOUT: "method": "textDocument/publishDiagnostics", // CHECK:STDOUT: "params": { -// CHECK:STDOUT: "diagnostics": [ -// CHECK:STDOUT: { -// CHECK:STDOUT: "message": "semantics TODO: `HandleFunctionTerseDefinition`", -// CHECK:STDOUT: "range": { -// CHECK:STDOUT: "end": { -// CHECK:STDOUT: "character": 17, -// CHECK:STDOUT: "line": 2 -// CHECK:STDOUT: }, -// CHECK:STDOUT: "start": { -// CHECK:STDOUT: "character": 0, -// CHECK:STDOUT: "line": 2 -// CHECK:STDOUT: } -// CHECK:STDOUT: }, -// CHECK:STDOUT: "severity": 1, -// CHECK:STDOUT: "source": "carbon" -// CHECK:STDOUT: } -// CHECK:STDOUT: ], +// CHECK:STDOUT: "diagnostics": [], // CHECK:STDOUT: "uri": "file:///fn_definition.carbon" // CHECK:STDOUT: } // CHECK:STDOUT: }Content-Length: 1706{{\r}} @@ -156,7 +140,7 @@ fn Builtin() = "int.make_type_32"; // CHECK:STDOUT: "name": "Builtin", // CHECK:STDOUT: "range": { // CHECK:STDOUT: "end": { -// CHECK:STDOUT: "character": 34, +// CHECK:STDOUT: "character": 47, // CHECK:STDOUT: "line": 3 // CHECK:STDOUT: }, // CHECK:STDOUT: "start": { diff --git a/toolchain/lower/testdata/function/definition/terse.carbon b/toolchain/lower/testdata/function/definition/terse.carbon new file mode 100644 index 000000000000..aa7afe5e9e64 --- /dev/null +++ b/toolchain/lower/testdata/function/definition/terse.carbon @@ -0,0 +1,219 @@ +// 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 +// +// INCLUDE-FILE: toolchain/testing/testdata/min_prelude/int.carbon +// +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/lower/testdata/function/definition/terse.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/function/definition/terse.carbon + +// --- lib.carbon +library "[[@TEST_NAME]]"; + +fn ImportedId(x: i32) => x; + +// --- main.carbon +library "[[@TEST_NAME]]"; + +import library "lib"; + +fn LocalId(x: i32) => x; + +fn Unit() => (); + +fn MakePair(x: i32, y: i32) => {.a = x, .b = y}; + +class C { + fn Get(self: Self) => self.x; + var x: i32; +} + +fn Test(c: C) -> i32 { + Unit(); + let p: {.a: i32, .b: i32} = MakePair(ImportedId(c.Get()), LocalId(2)); + return p.a; +} + +// CHECK:STDOUT: ; --- +// CHECK:STDOUT: ; ModuleID = 'lib.carbon' +// CHECK:STDOUT: source_filename = "lib.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define i32 @_CImportedId.Main(i32 %x) #0 !dbg !23 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret i32 %x, !dbg !26 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.dbg.cu = !{!19} +// CHECK:STDOUT: !llvm.module.flags = !{!2, !3} +// CHECK:STDOUT: +// CHECK:STDOUT: !2 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !3 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !18 = !DIFile(filename: "lib.carbon", directory: "") +// CHECK:STDOUT: !19 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !18, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !20 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +// CHECK:STDOUT: !21 = !{!20, !20} +// CHECK:STDOUT: !22 = !DISubroutineType(types: !21) +// CHECK:STDOUT: !23 = distinct !DISubprogram(name: "ImportedId", linkageName: "_CImportedId.Main", scope: null, file: !18, line: 3, type: !22, spFlags: DISPFlagDefinition, unit: !19, retainedNodes: !27) +// CHECK:STDOUT: !24 = !DILocalVariable(arg: 1, scope: !23, type: !20) +// CHECK:STDOUT: !26 = !DILocation(line: 3, column: 26, scope: !23) +// CHECK:STDOUT: !27 = !{!24} +// CHECK:STDOUT: +// CHECK:STDOUT: ; --- +// CHECK:STDOUT: ; ModuleID = 'main.carbon' +// CHECK:STDOUT: source_filename = "main.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define i32 @_CLocalId.Main(i32 %x) #0 !dbg !41 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret i32 %x, !dbg !44 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CUnit.Main() #0 !dbg !46 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !47 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CMakePair.Main(ptr sret({ i32, i32 }) %return, i32 %x, i32 %y) #0 !dbg !48 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc9_47.3.a = getelementptr inbounds nuw { i32, i32 }, ptr %return, i32 0, i32 0, !dbg !53 +// CHECK:STDOUT: store i32 %x, ptr %.loc9_47.3.a, align 4, !dbg !53 +// CHECK:STDOUT: %.loc9_47.5.b = getelementptr inbounds nuw { i32, i32 }, ptr %return, i32 0, i32 1, !dbg !53 +// CHECK:STDOUT: store i32 %y, ptr %.loc9_47.5.b, align 4, !dbg !53 +// CHECK:STDOUT: ret void, !dbg !53 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define i32 @_CGet.C.Main(ptr %self) #0 !dbg !57 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc12_29.1.x = getelementptr inbounds nuw { i32 }, ptr %self, i32 0, i32 0, !dbg !60 +// CHECK:STDOUT: %.loc12_29.2 = load i32, ptr %.loc12_29.1.x, align 4, !dbg !60 +// CHECK:STDOUT: ret i32 %.loc12_29.2, !dbg !60 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define i32 @_CTest.Main(ptr %c) #0 !dbg !62 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc18_71.1.temp = alloca { i32, i32 }, align 4, !dbg !71 +// CHECK:STDOUT: %struct = alloca { i32, i32 }, align 4, !dbg !71 +// CHECK:STDOUT: call void @_CUnit.Main(), !dbg !67 +// CHECK:STDOUT: %C.Get.call = call i32 @_CGet.C.Main(ptr %c), !dbg !68 +// CHECK:STDOUT: %ImportedId.call = call i32 @_CImportedId.Main(i32 %C.Get.call), !dbg !69 +// CHECK:STDOUT: %LocalId.call = call i32 @_CLocalId.Main(i32 2), !dbg !70 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc18_71.1.temp), !dbg !71 +// CHECK:STDOUT: call void @_CMakePair.Main(ptr %.loc18_71.1.temp, i32 %ImportedId.call, i32 %LocalId.call), !dbg !71 +// CHECK:STDOUT: %.loc18_71.3.a = getelementptr inbounds nuw { i32, i32 }, ptr %.loc18_71.1.temp, i32 0, i32 0, !dbg !71 +// CHECK:STDOUT: %.loc18_71.4 = load i32, ptr %.loc18_71.3.a, align 4, !dbg !71 +// CHECK:STDOUT: %.loc18_71.5.b = getelementptr inbounds nuw { i32, i32 }, ptr %.loc18_71.1.temp, i32 0, i32 1, !dbg !71 +// CHECK:STDOUT: %.loc18_71.6 = load i32, ptr %.loc18_71.5.b, align 4, !dbg !71 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %struct), !dbg !71 +// CHECK:STDOUT: %struct1 = getelementptr inbounds nuw { i32, i32 }, ptr %struct, i32 0, i32 0, !dbg !71 +// CHECK:STDOUT: store i32 %.loc18_71.4, ptr %struct1, align 4, !dbg !71 +// CHECK:STDOUT: %struct2 = getelementptr inbounds nuw { i32, i32 }, ptr %struct, i32 0, i32 1, !dbg !71 +// CHECK:STDOUT: store i32 %.loc18_71.6, ptr %struct2, align 4, !dbg !71 +// CHECK:STDOUT: %.loc19.a = getelementptr inbounds nuw { i32, i32 }, ptr %struct, i32 0, i32 0, !dbg !74 +// CHECK:STDOUT: %.loc19.a.load = load i32, ptr %.loc19.a, align 4, !dbg !74 +// CHECK:STDOUT: call void @"_CSelfDestruct.2508ed710019eba8:core.Destroy.Core"(ptr %.loc18_71.1.temp), !dbg !71 +// CHECK:STDOUT: ret i32 %.loc19.a.load, !dbg !75 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: declare i32 @_CImportedId.Main(i32) +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_CSelfDestruct.72395be3dbbe8946:core.Destroy.Core"(ptr %self) #0 !dbg !77 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret void, !dbg !79 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_CSubobjectDestroy.2508ed710019eba8:core.Destroy.Core"(ptr %self) #0 !dbg !81 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc18_71.1.b = getelementptr inbounds nuw { i32, i32 }, ptr %self, i32 0, i32 1, !dbg !83 +// CHECK:STDOUT: call void @"_CSelfDestruct.72395be3dbbe8946:core.Destroy.Core"(ptr %.loc18_71.1.b), !dbg !83 +// CHECK:STDOUT: %.loc18_71.2.a = getelementptr inbounds nuw { i32, i32 }, ptr %self, i32 0, i32 0, !dbg !83 +// CHECK:STDOUT: call void @"_CSelfDestruct.72395be3dbbe8946:core.Destroy.Core"(ptr %.loc18_71.2.a), !dbg !83 +// CHECK:STDOUT: ret void, !dbg !83 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define weak_odr void @"_CSelfDestruct.2508ed710019eba8:core.Destroy.Core"(ptr %self) #0 !dbg !85 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @"_CSubobjectDestroy.2508ed710019eba8:core.Destroy.Core"(ptr %self), !dbg !87 +// CHECK:STDOUT: ret void, !dbg !87 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) +// CHECK:STDOUT: declare void @llvm.lifetime.start.p0(ptr captures(none)) #1 +// CHECK:STDOUT: +// CHECK:STDOUT: ; uselistorder directives +// CHECK:STDOUT: uselistorder ptr @llvm.lifetime.start.p0, { 1, 0 } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.dbg.cu = !{!29} +// CHECK:STDOUT: !llvm.module.flags = !{!2, !3} +// CHECK:STDOUT: +// CHECK:STDOUT: !2 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !3 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !20 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +// CHECK:STDOUT: !21 = !{!20, !20} +// CHECK:STDOUT: !22 = !DISubroutineType(types: !21) +// CHECK:STDOUT: !28 = !DIFile(filename: "main.carbon", directory: "") +// CHECK:STDOUT: !29 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !28, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !30 = !{null} +// CHECK:STDOUT: !31 = !DISubroutineType(types: !30) +// CHECK:STDOUT: !32 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +// CHECK:STDOUT: !33 = !{!32, !20, !20} +// CHECK:STDOUT: !34 = !DISubroutineType(types: !33) +// CHECK:STDOUT: !35 = !{!20, !32} +// CHECK:STDOUT: !36 = !DISubroutineType(types: !35) +// CHECK:STDOUT: !37 = !{null, !20} +// CHECK:STDOUT: !38 = !DISubroutineType(types: !37) +// CHECK:STDOUT: !39 = !{null, !32} +// CHECK:STDOUT: !40 = !DISubroutineType(types: !39) +// CHECK:STDOUT: !41 = distinct !DISubprogram(name: "LocalId", linkageName: "_CLocalId.Main", scope: null, file: !28, line: 5, type: !22, spFlags: DISPFlagDefinition, unit: !29, retainedNodes: !45) +// CHECK:STDOUT: !42 = !DILocalVariable(arg: 1, scope: !41, type: !20) +// CHECK:STDOUT: !44 = !DILocation(line: 5, column: 23, scope: !41) +// CHECK:STDOUT: !45 = !{!42} +// CHECK:STDOUT: !46 = distinct !DISubprogram(name: "Unit", linkageName: "_CUnit.Main", scope: null, file: !28, line: 7, type: !31, spFlags: DISPFlagDefinition, unit: !29) +// CHECK:STDOUT: !47 = !DILocation(line: 7, column: 14, scope: !46) +// CHECK:STDOUT: !48 = distinct !DISubprogram(name: "MakePair", linkageName: "_CMakePair.Main", scope: null, file: !28, line: 9, type: !34, spFlags: DISPFlagDefinition, unit: !29, retainedNodes: !56) +// CHECK:STDOUT: !49 = !DILocalVariable(arg: 1, scope: !48, type: !20) +// CHECK:STDOUT: !50 = !DILocalVariable(arg: 2, scope: !48, type: !20) +// CHECK:STDOUT: !53 = !DILocation(line: 9, column: 32, scope: !48) +// CHECK:STDOUT: !56 = !{!49, !50} +// CHECK:STDOUT: !57 = distinct !DISubprogram(name: "Get", linkageName: "_CGet.C.Main", scope: null, file: !28, line: 12, type: !36, spFlags: DISPFlagDefinition, unit: !29, retainedNodes: !61) +// CHECK:STDOUT: !58 = !DILocalVariable(arg: 1, scope: !57, type: !32) +// CHECK:STDOUT: !60 = !DILocation(line: 12, column: 25, scope: !57) +// CHECK:STDOUT: !61 = !{!58} +// CHECK:STDOUT: !62 = distinct !DISubprogram(name: "Test", linkageName: "_CTest.Main", scope: null, file: !28, line: 16, type: !36, spFlags: DISPFlagDefinition, unit: !29, retainedNodes: !76) +// CHECK:STDOUT: !63 = !DILocalVariable(arg: 1, scope: !62, type: !32) +// CHECK:STDOUT: !67 = !DILocation(line: 17, column: 3, scope: !62) +// CHECK:STDOUT: !68 = !DILocation(line: 18, column: 51, scope: !62) +// CHECK:STDOUT: !69 = !DILocation(line: 18, column: 40, scope: !62) +// CHECK:STDOUT: !70 = !DILocation(line: 18, column: 61, scope: !62) +// CHECK:STDOUT: !71 = !DILocation(line: 18, column: 31, scope: !62) +// CHECK:STDOUT: !74 = !DILocation(line: 19, column: 10, scope: !62) +// CHECK:STDOUT: !75 = !DILocation(line: 19, column: 3, scope: !62) +// CHECK:STDOUT: !76 = !{!63} +// CHECK:STDOUT: !77 = distinct !DISubprogram(name: "SelfDestruct", linkageName: "_CSelfDestruct.72395be3dbbe8946:core.Destroy.Core", scope: null, file: !28, line: 18, type: !38, spFlags: DISPFlagDefinition, unit: !29, retainedNodes: !80) +// CHECK:STDOUT: !78 = !DILocalVariable(arg: 1, scope: !77, type: !20) +// CHECK:STDOUT: !79 = !DILocation(line: 18, column: 31, scope: !77) +// CHECK:STDOUT: !80 = !{!78} +// CHECK:STDOUT: !81 = distinct !DISubprogram(name: "SubobjectDestroy", linkageName: "_CSubobjectDestroy.2508ed710019eba8:core.Destroy.Core", scope: null, file: !28, line: 18, type: !40, spFlags: DISPFlagDefinition, unit: !29, retainedNodes: !84) +// CHECK:STDOUT: !82 = !DILocalVariable(arg: 1, scope: !81, type: !32) +// CHECK:STDOUT: !83 = !DILocation(line: 18, column: 31, scope: !81) +// CHECK:STDOUT: !84 = !{!82} +// CHECK:STDOUT: !85 = distinct !DISubprogram(name: "SelfDestruct", linkageName: "_CSelfDestruct.2508ed710019eba8:core.Destroy.Core", scope: null, file: !28, line: 18, type: !40, spFlags: DISPFlagDefinition, unit: !29, retainedNodes: !88) +// CHECK:STDOUT: !86 = !DILocalVariable(arg: 1, scope: !85, type: !32) +// CHECK:STDOUT: !87 = !DILocation(line: 18, column: 31, scope: !85) +// CHECK:STDOUT: !88 = !{!86} +// CHECK:STDOUT: diff --git a/toolchain/lower/testdata/lambda/lambda.carbon b/toolchain/lower/testdata/lambda/lambda.carbon index 9248a5160bd3..9795b3d6da8c 100644 --- a/toolchain/lower/testdata/lambda/lambda.carbon +++ b/toolchain/lower/testdata/lambda/lambda.carbon @@ -14,6 +14,7 @@ library "[[@TEST_NAME]]"; alias Imported = fn (x: i32) -> i32 { return x; }; +alias ImportedTerse = fn (x: i32) => x; // --- main.carbon library "[[@TEST_NAME]]"; @@ -24,7 +25,8 @@ fn Test() -> i32 { alias f = fn (x: i32) -> i32 { return x; }; - return (fn (y: i32) -> i32 { return y; })(f(Imported(1))); + alias g = fn (x: i32) => x; + return (fn (y: i32) => y)(g(f(ImportedTerse(Imported(1))))); } // CHECK:STDOUT: ; --- @@ -37,6 +39,12 @@ fn Test() -> i32 { // CHECK:STDOUT: ret i32 %x, !dbg !29 // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define i32 @"_Cfn:enclosed.57eff363c02bd6d7"(i32 %x) #0 !dbg !31 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret i32 %x, !dbg !34 +// CHECK:STDOUT: } +// CHECK:STDOUT: // CHECK:STDOUT: attributes #0 = { nounwind } // CHECK:STDOUT: // CHECK:STDOUT: !llvm.dbg.cu = !{!19} @@ -53,37 +61,51 @@ fn Test() -> i32 { // CHECK:STDOUT: !24 = !DILocalVariable(arg: 1, scope: !23, type: !20) // CHECK:STDOUT: !29 = !DILocation(line: 3, column: 39, scope: !23) // CHECK:STDOUT: !30 = !{!24} +// CHECK:STDOUT: !31 = distinct !DISubprogram(linkageName: "_Cfn:enclosed.57eff363c02bd6d7", scope: null, file: !18, line: 4, type: !22, spFlags: DISPFlagDefinition, unit: !19, retainedNodes: !35) +// CHECK:STDOUT: !32 = !DILocalVariable(arg: 1, scope: !31, type: !20) +// CHECK:STDOUT: !34 = !DILocation(line: 4, column: 38, scope: !31) +// CHECK:STDOUT: !35 = !{!32} // CHECK:STDOUT: // CHECK:STDOUT: ; --- // CHECK:STDOUT: ; ModuleID = 'main.carbon' // CHECK:STDOUT: source_filename = "main.carbon" // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define i32 @_CTest.Main() #0 !dbg !35 { +// CHECK:STDOUT: define i32 @_CTest.Main() #0 !dbg !40 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: %fn.call.loc9_57 = call i32 @"_Cfn:enclosed.3c14c8ca6559e7d0"(i32 1), !dbg !38 -// CHECK:STDOUT: %fn.call.loc9_58 = call i32 @"_Cfn:enclosed.4f93545957a800ba"(i32 %fn.call.loc9_57), !dbg !39 -// CHECK:STDOUT: %fn.call.loc9_59 = call i32 @"_Cfn:enclosed.1e20c3b8ac0c9b5a"(i32 %fn.call.loc9_58), !dbg !40 -// CHECK:STDOUT: ret i32 %fn.call.loc9_59, !dbg !41 +// CHECK:STDOUT: %fn.call.loc10_57 = call i32 @"_Cfn:enclosed.3c14c8ca6559e7d0"(i32 1), !dbg !43 +// CHECK:STDOUT: %fn.call.loc10_58 = call i32 @"_Cfn:enclosed.57eff363c02bd6d7"(i32 %fn.call.loc10_57), !dbg !44 +// CHECK:STDOUT: %fn.call.loc10_59 = call i32 @"_Cfn:enclosed.78e97d08196088b9"(i32 %fn.call.loc10_58), !dbg !45 +// CHECK:STDOUT: %fn.call.loc10_60 = call i32 @"_Cfn:enclosed.0e7773781338b8b4"(i32 %fn.call.loc10_59), !dbg !46 +// CHECK:STDOUT: %fn.call.loc10_61 = call i32 @"_Cfn:enclosed.84edee9c65852fed"(i32 %fn.call.loc10_60), !dbg !47 +// CHECK:STDOUT: ret i32 %fn.call.loc10_61, !dbg !48 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define i32 @"_Cfn:enclosed.4f93545957a800ba"(i32 %x) #0 !dbg !42 { +// CHECK:STDOUT: define i32 @"_Cfn:enclosed.78e97d08196088b9"(i32 %x) #0 !dbg !49 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: ret i32 %x, !dbg !48 +// CHECK:STDOUT: ret i32 %x, !dbg !55 // CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: ; Function Attrs: nounwind -// CHECK:STDOUT: define i32 @"_Cfn:enclosed.1e20c3b8ac0c9b5a"(i32 %y) #0 !dbg !50 { +// CHECK:STDOUT: define i32 @"_Cfn:enclosed.0e7773781338b8b4"(i32 %x) #0 !dbg !57 { // CHECK:STDOUT: entry: -// CHECK:STDOUT: ret i32 %y, !dbg !56 +// CHECK:STDOUT: ret i32 %x, !dbg !60 // CHECK:STDOUT: } // CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define i32 @"_Cfn:enclosed.84edee9c65852fed"(i32 %y) #0 !dbg !62 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: ret i32 %y, !dbg !65 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: declare i32 @"_Cfn:enclosed.57eff363c02bd6d7"(i32) +// CHECK:STDOUT: // CHECK:STDOUT: declare i32 @"_Cfn:enclosed.3c14c8ca6559e7d0"(i32) // CHECK:STDOUT: // CHECK:STDOUT: attributes #0 = { nounwind } // CHECK:STDOUT: -// CHECK:STDOUT: !llvm.dbg.cu = !{!32} +// CHECK:STDOUT: !llvm.dbg.cu = !{!37} // CHECK:STDOUT: !llvm.module.flags = !{!2, !3} // CHECK:STDOUT: // CHECK:STDOUT: !2 = !{i32 7, !"Dwarf Version", i32 5} @@ -91,21 +113,27 @@ fn Test() -> i32 { // CHECK:STDOUT: !20 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) // CHECK:STDOUT: !21 = !{!20, !20} // CHECK:STDOUT: !22 = !DISubroutineType(types: !21) -// CHECK:STDOUT: !31 = !DIFile(filename: "main.carbon", directory: "") -// CHECK:STDOUT: !32 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !31, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) -// CHECK:STDOUT: !33 = !{!20} -// CHECK:STDOUT: !34 = !DISubroutineType(types: !33) -// CHECK:STDOUT: !35 = distinct !DISubprogram(name: "Test", linkageName: "_CTest.Main", scope: null, file: !31, line: 5, type: !34, spFlags: DISPFlagDefinition, unit: !32) -// CHECK:STDOUT: !38 = !DILocation(line: 9, column: 47, scope: !35) -// CHECK:STDOUT: !39 = !DILocation(line: 9, column: 45, scope: !35) -// CHECK:STDOUT: !40 = !DILocation(line: 9, column: 10, scope: !35) -// CHECK:STDOUT: !41 = !DILocation(line: 9, column: 3, scope: !35) -// CHECK:STDOUT: !42 = distinct !DISubprogram(linkageName: "_Cfn:enclosed.4f93545957a800ba", scope: null, file: !31, line: 6, type: !22, spFlags: DISPFlagDefinition, unit: !32, retainedNodes: !49) -// CHECK:STDOUT: !43 = !DILocalVariable(arg: 1, scope: !42, type: !20) -// CHECK:STDOUT: !48 = !DILocation(line: 7, column: 5, scope: !42) -// CHECK:STDOUT: !49 = !{!43} -// CHECK:STDOUT: !50 = distinct !DISubprogram(linkageName: "_Cfn:enclosed.1e20c3b8ac0c9b5a", scope: null, file: !31, line: 9, type: !22, spFlags: DISPFlagDefinition, unit: !32, retainedNodes: !57) -// CHECK:STDOUT: !51 = !DILocalVariable(arg: 1, scope: !50, type: !20) -// CHECK:STDOUT: !56 = !DILocation(line: 9, column: 32, scope: !50) -// CHECK:STDOUT: !57 = !{!51} +// CHECK:STDOUT: !36 = !DIFile(filename: "main.carbon", directory: "") +// CHECK:STDOUT: !37 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !36, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !38 = !{!20} +// CHECK:STDOUT: !39 = !DISubroutineType(types: !38) +// CHECK:STDOUT: !40 = distinct !DISubprogram(name: "Test", linkageName: "_CTest.Main", scope: null, file: !36, line: 5, type: !39, spFlags: DISPFlagDefinition, unit: !37) +// CHECK:STDOUT: !43 = !DILocation(line: 10, column: 47, scope: !40) +// CHECK:STDOUT: !44 = !DILocation(line: 10, column: 33, scope: !40) +// CHECK:STDOUT: !45 = !DILocation(line: 10, column: 31, scope: !40) +// CHECK:STDOUT: !46 = !DILocation(line: 10, column: 29, scope: !40) +// CHECK:STDOUT: !47 = !DILocation(line: 10, column: 10, scope: !40) +// CHECK:STDOUT: !48 = !DILocation(line: 10, column: 3, scope: !40) +// CHECK:STDOUT: !49 = distinct !DISubprogram(linkageName: "_Cfn:enclosed.78e97d08196088b9", scope: null, file: !36, line: 6, type: !22, spFlags: DISPFlagDefinition, unit: !37, retainedNodes: !56) +// CHECK:STDOUT: !50 = !DILocalVariable(arg: 1, scope: !49, type: !20) +// CHECK:STDOUT: !55 = !DILocation(line: 7, column: 5, scope: !49) +// CHECK:STDOUT: !56 = !{!50} +// CHECK:STDOUT: !57 = distinct !DISubprogram(linkageName: "_Cfn:enclosed.0e7773781338b8b4", scope: null, file: !36, line: 9, type: !22, spFlags: DISPFlagDefinition, unit: !37, retainedNodes: !61) +// CHECK:STDOUT: !58 = !DILocalVariable(arg: 1, scope: !57, type: !20) +// CHECK:STDOUT: !60 = !DILocation(line: 9, column: 28, scope: !57) +// CHECK:STDOUT: !61 = !{!58} +// CHECK:STDOUT: !62 = distinct !DISubprogram(linkageName: "_Cfn:enclosed.84edee9c65852fed", scope: null, file: !36, line: 10, type: !22, spFlags: DISPFlagDefinition, unit: !37, retainedNodes: !66) +// CHECK:STDOUT: !63 = !DILocalVariable(arg: 1, scope: !62, type: !20) +// CHECK:STDOUT: !65 = !DILocation(line: 10, column: 26, scope: !62) +// CHECK:STDOUT: !66 = !{!63} // CHECK:STDOUT: diff --git a/toolchain/parse/context.cpp b/toolchain/parse/context.cpp index c19b5566a8e4..434ebafcea49 100644 --- a/toolchain/parse/context.cpp +++ b/toolchain/parse/context.cpp @@ -472,6 +472,16 @@ auto Context::AddFunctionDefinitionStart(Lex::TokenIndex token, bool has_error) } } +auto Context::AddFunctionTerseDefinitionStart(Lex::TokenIndex token, + bool has_error) -> void { + auto start_id = + AddNode(token, has_error); + if (ParsingInDeferredDefinitionScope(*this)) { + deferred_definition_stack_.push_back( + tree_->deferred_definitions_.Add({.start_id = start_id})); + } +} + auto Context::AddFunctionDefinition(Lex::TokenIndex token, bool has_error) -> void { auto definition_id = AddNode(token, has_error); diff --git a/toolchain/parse/context.h b/toolchain/parse/context.h index cbb05f3932e7..cc2e4d2785f7 100644 --- a/toolchain/parse/context.h +++ b/toolchain/parse/context.h @@ -461,6 +461,10 @@ class Context { // definition if necessary. auto AddFunctionDefinitionStart(Lex::TokenIndex token, bool has_error) -> void; + // Adds a function terse definition start node, and begins tracking a deferred + // definition if necessary. + auto AddFunctionTerseDefinitionStart(Lex::TokenIndex token, bool has_error) + -> void; // Adds a function definition node, and ends tracking a deferred definition if // necessary. auto AddFunctionDefinition(Lex::TokenIndex token, bool has_error) -> void; diff --git a/toolchain/parse/handle_function.cpp b/toolchain/parse/handle_function.cpp index 3fb808bccaa9..50d343b80318 100644 --- a/toolchain/parse/handle_function.cpp +++ b/toolchain/parse/handle_function.cpp @@ -74,7 +74,10 @@ auto HandleFunctionSignatureFinish(Context& context) -> void { break; } case Lex::TokenKind::EqualGreater: { - context.AddFunctionDefinitionStart(context.Consume(), state.has_error); + context.AddFunctionTerseDefinitionStart(context.Consume(), + state.has_error); + // Any error is recorded on the FunctionTerseDefinitionStart. + state.has_error = false; context.PushState(state, StateKind::FunctionTerseBodyFinish); context.PushStateForExpr(PrecedenceGroup::ForTopLevelExpr()); break; diff --git a/toolchain/parse/handle_lambda.cpp b/toolchain/parse/handle_lambda.cpp index 1f34a03e40f5..e67123416d9f 100644 --- a/toolchain/parse/handle_lambda.cpp +++ b/toolchain/parse/handle_lambda.cpp @@ -45,10 +45,10 @@ static auto ParseLambdaBody(Context& context, Context::State state, bool has_return_type) -> void { if (context.PositionIs(Lex::TokenKind::EqualGreater)) { // Terse body `=> expr` - context.AddNode(NodeKind::LambdaDefinitionStart, context.Consume(), + context.AddNode(NodeKind::LambdaTerseDefinitionStart, context.Consume(), state.has_error); state.has_error = false; - context.PushState(state, StateKind::LambdaBodyFinish); + context.PushState(state, StateKind::LambdaTerseBodyFinish); context.PushStateForExpr(PrecedenceGroup::ForTopLevelExpr()); } else if (context.PositionIs(Lex::TokenKind::OpenCurlyBrace)) { // Block body `{ ... }` @@ -59,7 +59,7 @@ static auto ParseLambdaBody(Context& context, Context::State state, } else { if (has_return_type) { CARBON_DIAGNOSTIC(ExpectedLambdaBodyAfterReturnType, Error, - "expected `=>` or `{{` after return type"); + "expected `{{` after return type"); context.emitter().Emit(*context.position(), ExpectedLambdaBodyAfterReturnType); } else { @@ -104,11 +104,13 @@ auto HandleLambdaBody(Context& context) -> void { auto HandleLambdaBodyFinish(Context& context) -> void { auto state = context.PopState(); - if (context.tokens().GetKind(state.token) == Lex::TokenKind::OpenCurlyBrace) { - context.AddNode(NodeKind::Lambda, context.Consume(), state.has_error); - } else { - context.AddNode(NodeKind::Lambda, state.token, state.has_error); - } + context.AddNode(NodeKind::Lambda, context.Consume(), state.has_error); +} + +auto HandleLambdaTerseBodyFinish(Context& context) -> void { + auto state = context.PopState(); + context.AddNode(NodeKind::LambdaTerseDefinition, state.token, + state.has_error); } } // namespace Carbon::Parse diff --git a/toolchain/parse/node_ids.h b/toolchain/parse/node_ids.h index 3c944ee5bd60..39c8d50a40db 100644 --- a/toolchain/parse/node_ids.h +++ b/toolchain/parse/node_ids.h @@ -177,10 +177,16 @@ using AnyClassDeclId = ChoiceDefinitionStartId>; using AnyFunctionDeclId = NodeIdOneOf; + FunctionTerseDefinitionStartId, + BuiltinFunctionDefinitionStartId, LambdaDefinitionStartId, + LambdaTerseDefinitionStartId>; +using AnyFunctionDefinitionStartId = + NodeIdOneOf; using AnyFunctionDefinitionId = NodeIdOneOf; +using AnyLambdaDefinitionStartId = + NodeIdOneOf; using AnyImplDeclId = NodeIdOneOf; using AnyInterfaceDeclId = NodeIdOneOf; @@ -197,6 +203,8 @@ using AnyRuntimeBindingPatternName = UnderscoreNameId>; using AnyPrimitiveFormIdId = NodeIdOneOf; +using AnyTerseFunctionDefinitionId = + NodeIdOneOf; // NodeId with kind that is anything but T::Kind. template diff --git a/toolchain/parse/node_kind.def b/toolchain/parse/node_kind.def index 46017d80e9e1..ae9d09ab63fc 100644 --- a/toolchain/parse/node_kind.def +++ b/toolchain/parse/node_kind.def @@ -199,10 +199,13 @@ CARBON_PARSE_NODE_KIND_DECLARATION(ReturnType) CARBON_PARSE_NODE_KIND_DECLARATION(ReturnForm) CARBON_PARSE_NODE_KIND_DECLARATION(FunctionDefinitionStart) CARBON_PARSE_NODE_KIND_DECLARATION(FunctionDefinition) +CARBON_PARSE_NODE_KIND_DECLARATION(FunctionTerseDefinitionStart) CARBON_PARSE_NODE_KIND_DECLARATION(FunctionTerseDefinition) CARBON_PARSE_NODE_KIND_DECLARATION(FunctionDecl) CARBON_PARSE_NODE_KIND_DECLARATION(LambdaDefinitionStart) CARBON_PARSE_NODE_KIND_DECLARATION(Lambda) +CARBON_PARSE_NODE_KIND_DECLARATION(LambdaTerseDefinitionStart) +CARBON_PARSE_NODE_KIND_DECLARATION(LambdaTerseDefinition) CARBON_PARSE_NODE_KIND_DECLARATION(BuiltinFunctionDefinitionStart) CARBON_PARSE_NODE_KIND_DECLARATION(BuiltinName) CARBON_PARSE_NODE_KIND_DECLARATION(BuiltinFunctionDefinition) diff --git a/toolchain/parse/state.def b/toolchain/parse/state.def index 87fe87e08945..825ef59d68ab 100644 --- a/toolchain/parse/state.def +++ b/toolchain/parse/state.def @@ -827,7 +827,7 @@ CARBON_PARSE_STATE(LambdaAfterImplicitParams) // fn <...> => <...> // ^ // 1. Expr -// 2. LambdaBodyFinish +// 2. LambdaTerseBodyFinish // // fn <...> { <...> } // ^ @@ -845,17 +845,22 @@ CARBON_PARSE_STATE(LambdaAfterParams) // fn <...> -> <...> => <...> // ^ // 1. Expr -// 2. LambdaBodyFinish +// 2. LambdaTerseBodyFinish CARBON_PARSE_STATE(LambdaBody) // Finishes a lambda expression. // // fn <...> } // ^ +// (state done) +CARBON_PARSE_STATE(LambdaBodyFinish) + +// Finishes a lambda expression with a terse body. +// // fn <...> => expr // ^ // (state done) -CARBON_PARSE_STATE(LambdaBodyFinish) +CARBON_PARSE_STATE(LambdaTerseBodyFinish) // Handles processing of a function's syntax after `)`, primarily the // possibility a `->` return type is there. Always enqueues signature finish diff --git a/toolchain/parse/testdata/function/declaration.carbon b/toolchain/parse/testdata/function/declaration.carbon index 1edc6b036774..8e89c269b8eb 100644 --- a/toolchain/parse/testdata/function/declaration.carbon +++ b/toolchain/parse/testdata/function/declaration.carbon @@ -157,7 +157,7 @@ fn ComplexReturnForm() ->? X.Y(Z); fn RefReturn() -> ref u32; fn ComplexRefReturn() -> ref X.Y(Z); -let a: auto = fn -> ref u32 => 0; +let a: auto = fn -> ref u32 { return 0; }; // --- fail_ref_return_missing_type.carbon @@ -170,8 +170,8 @@ fn RefReturn() -> ref; fn ValReturn() -> val u32; fn VarReturn() -> var u32; fn ComplexValReturn() -> val X.Y(Z); -let a: auto = fn -> val u32 => 0; -let b: auto = fn -> var u32 => 0; +let a: auto = fn -> val u32 { return 0; }; +let b: auto = fn -> var u32 { return 0; }; // --- fail_val_return_missing_type.carbon @@ -534,9 +534,11 @@ fn ValReturn() -> val; // CHECK:STDOUT: │ │ │ ╭─UnsignedIntTypeLiteral 'u32' // CHECK:STDOUT: │ │ │ ╭─RefPrimitiveForm 'ref' // CHECK:STDOUT: │ │ ├─ReturnType '->' -// CHECK:STDOUT: │ │ ╭─LambdaDefinitionStart '=>' -// CHECK:STDOUT: │ │ ├─IntLiteral '0' -// CHECK:STDOUT: │ ├─Lambda 'fn' +// CHECK:STDOUT: │ │ ╭─LambdaDefinitionStart '{' +// CHECK:STDOUT: │ │ │ ╭─ReturnStatementStart 'return' +// CHECK:STDOUT: │ │ │ ├─IntLiteral '0' +// CHECK:STDOUT: │ │ ├─ReturnStatement ';' +// CHECK:STDOUT: │ ├─Lambda '}' // CHECK:STDOUT: ├─LetDecl ';' // CHECK:STDOUT: ├─FileEnd '' // CHECK:STDOUT: (root) @@ -593,9 +595,11 @@ fn ValReturn() -> val; // CHECK:STDOUT: │ │ │ ╭─UnsignedIntTypeLiteral 'u32' // CHECK:STDOUT: │ │ │ ╭─ValPrimitiveForm 'val' // CHECK:STDOUT: │ │ ├─ReturnType '->' -// CHECK:STDOUT: │ │ ╭─LambdaDefinitionStart '=>' -// CHECK:STDOUT: │ │ ├─IntLiteral '0' -// CHECK:STDOUT: │ ├─Lambda 'fn' +// CHECK:STDOUT: │ │ ╭─LambdaDefinitionStart '{' +// CHECK:STDOUT: │ │ │ ╭─ReturnStatementStart 'return' +// CHECK:STDOUT: │ │ │ ├─IntLiteral '0' +// CHECK:STDOUT: │ │ ├─ReturnStatement ';' +// CHECK:STDOUT: │ ├─Lambda '}' // CHECK:STDOUT: ├─LetDecl ';' // CHECK:STDOUT: │ ╭─LetIntroducer 'let' // CHECK:STDOUT: │ │ ╭─IdentifierNameNotBeforeSignature 'b' @@ -607,9 +611,11 @@ fn ValReturn() -> val; // CHECK:STDOUT: │ │ │ ╭─UnsignedIntTypeLiteral 'u32' // CHECK:STDOUT: │ │ │ ╭─VarPrimitiveForm 'var' // CHECK:STDOUT: │ │ ├─ReturnType '->' -// CHECK:STDOUT: │ │ ╭─LambdaDefinitionStart '=>' -// CHECK:STDOUT: │ │ ├─IntLiteral '0' -// CHECK:STDOUT: │ ├─Lambda 'fn' +// CHECK:STDOUT: │ │ ╭─LambdaDefinitionStart '{' +// CHECK:STDOUT: │ │ │ ╭─ReturnStatementStart 'return' +// CHECK:STDOUT: │ │ │ ├─IntLiteral '0' +// CHECK:STDOUT: │ │ ├─ReturnStatement ';' +// CHECK:STDOUT: │ ├─Lambda '}' // CHECK:STDOUT: ├─LetDecl ';' // CHECK:STDOUT: ├─FileEnd '' // CHECK:STDOUT: (root) diff --git a/toolchain/parse/testdata/function/default_values.carbon b/toolchain/parse/testdata/function/default_values.carbon index 39c7131e6d20..5eef60377c7c 100644 --- a/toolchain/parse/testdata/function/default_values.carbon +++ b/toolchain/parse/testdata/function/default_values.carbon @@ -85,9 +85,9 @@ var b: auto = fn (x: i32 = 0) => x; // CHECK:STDOUT: │ │ │ │ ├─IntLiteral '0' // CHECK:STDOUT: │ │ │ ├─DefaultValuePattern '=' // CHECK:STDOUT: │ │ ├─ExplicitParamList ')' -// CHECK:STDOUT: │ │ ╭─LambdaDefinitionStart '=>' +// CHECK:STDOUT: │ │ ╭─LambdaTerseDefinitionStart '=>' // CHECK:STDOUT: │ │ ├─IdentifierNameExpr 'x' -// CHECK:STDOUT: │ ├─Lambda 'fn' +// CHECK:STDOUT: │ ├─LambdaTerseDefinition 'fn' // CHECK:STDOUT: ├─VariableDecl ';' // CHECK:STDOUT: ├─FileEnd '' // CHECK:STDOUT: (root) diff --git a/toolchain/parse/testdata/function/terse.carbon b/toolchain/parse/testdata/function/terse.carbon index 148f240250a2..1541d090007d 100644 --- a/toolchain/parse/testdata/function/terse.carbon +++ b/toolchain/parse/testdata/function/terse.carbon @@ -10,8 +10,6 @@ fn F(x: i32) => x; -fn G(x: i32) -> i32 => x; - // CHECK:STDOUT: - filename: terse.carbon // CHECK:STDOUT: ╭─FileStart '' // CHECK:STDOUT: │ ╭─FunctionIntroducer 'fn' @@ -22,20 +20,7 @@ fn G(x: i32) -> i32 => x; // CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32' // CHECK:STDOUT: │ │ ├─LetBindingPattern ':' // CHECK:STDOUT: │ ├─ExplicitParamList ')' -// CHECK:STDOUT: │ ╭─FunctionDefinitionStart '=>' -// CHECK:STDOUT: │ ├─IdentifierNameExpr 'x' -// CHECK:STDOUT: ├─FunctionTerseDefinition ';' -// CHECK:STDOUT: │ ╭─FunctionIntroducer 'fn' -// CHECK:STDOUT: │ ├─IdentifierNameMaybeBeforeSignature 'G' -// CHECK:STDOUT: │ │ ╭─ExplicitParamListStart '(' -// CHECK:STDOUT: │ │ │ ╭─IdentifierNameNotBeforeSignature 'x' -// CHECK:STDOUT: │ │ │ ├─BindingPatternTypeStart ':' -// CHECK:STDOUT: │ │ │ ├─IntTypeLiteral 'i32' -// CHECK:STDOUT: │ │ ├─LetBindingPattern ':' -// CHECK:STDOUT: │ ├─ExplicitParamList ')' -// CHECK:STDOUT: │ │ ╭─IntTypeLiteral 'i32' -// CHECK:STDOUT: │ ├─ReturnType '->' -// CHECK:STDOUT: │ ╭─FunctionDefinitionStart '=>' +// CHECK:STDOUT: │ ╭─FunctionTerseDefinitionStart '=>' // CHECK:STDOUT: │ ├─IdentifierNameExpr 'x' // CHECK:STDOUT: ├─FunctionTerseDefinition ';' // CHECK:STDOUT: ├─FileEnd '' diff --git a/toolchain/parse/testdata/lambda/lambda.carbon b/toolchain/parse/testdata/lambda/lambda.carbon index 37cd7762703f..8fc906cba68b 100644 --- a/toolchain/parse/testdata/lambda/lambda.carbon +++ b/toolchain/parse/testdata/lambda/lambda.carbon @@ -29,7 +29,7 @@ var d: auto = fn (ref x: i32) ->? ref i32 { return x; }; require j%-fn->> // CHECK:STDERR: fail_unconsumed_lambda_introducer.carbon:[[@LINE+6]]:1: error: expected expression [ExpectedExpr] // CHECK:STDERR: -// CHECK:STDERR: fail_unconsumed_lambda_introducer.carbon:[[@LINE+4]]:1: error: expected `=>` or `{` after return type [ExpectedLambdaBodyAfterReturnType] +// CHECK:STDERR: fail_unconsumed_lambda_introducer.carbon:[[@LINE+4]]:1: error: expected `{` after return type [ExpectedLambdaBodyAfterReturnType] // CHECK:STDERR: // CHECK:STDERR: fail_unconsumed_lambda_introducer.carbon:[[@LINE+2]]:1: error: expected `impls` in `require` declaration [RequireExpectedImpls] // CHECK:STDERR: @@ -45,7 +45,7 @@ fn F() { // --- fail_expected_body_after_return_type.carbon fn G() { - // CHECK:STDERR: fail_expected_body_after_return_type.carbon:[[@LINE+2]]:27: error: expected `=>` or `{` after return type [ExpectedLambdaBodyAfterReturnType] + // CHECK:STDERR: fail_expected_body_after_return_type.carbon:[[@LINE+2]]:27: error: expected `{` after return type [ExpectedLambdaBodyAfterReturnType] // CHECK:STDERR: var x: auto = fn -> i32 ; } @@ -94,9 +94,9 @@ fn G() { // CHECK:STDOUT: │ │ │ │ ├─IntTypeLiteral 'i32' // CHECK:STDOUT: │ │ │ ├─LetBindingPattern ':' // CHECK:STDOUT: │ │ ├─ExplicitParamList ')' -// CHECK:STDOUT: │ │ ╭─LambdaDefinitionStart '=>' +// CHECK:STDOUT: │ │ ╭─LambdaTerseDefinitionStart '=>' // CHECK:STDOUT: │ │ ├─IdentifierNameExpr 'x' -// CHECK:STDOUT: │ ├─Lambda 'fn' +// CHECK:STDOUT: │ ├─LambdaTerseDefinition 'fn' // CHECK:STDOUT: ├─VariableDecl ';' // CHECK:STDOUT: │ ╭─VariableIntroducer 'var' // CHECK:STDOUT: │ │ ╭─IdentifierNameNotBeforeSignature 'c' diff --git a/toolchain/parse/tree.h b/toolchain/parse/tree.h index e480970966a7..b53a3e72f6a0 100644 --- a/toolchain/parse/tree.h +++ b/toolchain/parse/tree.h @@ -38,7 +38,7 @@ struct DeferredDefinitionIndex : public IndexBase { // for these functions in the parse tree to support this reordering. struct DeferredDefinition { // The node that starts the function definition. - FunctionDefinitionStartId start_id; + AnyFunctionDefinitionStartId start_id; // The function definition node. AnyFunctionDefinitionId definition_id = NodeId::None; // The index of the next method that is not nested within this one. diff --git a/toolchain/parse/typed_nodes.h b/toolchain/parse/typed_nodes.h index 9ac8adb48082..a8acb6ac063a 100644 --- a/toolchain/parse/typed_nodes.h +++ b/toolchain/parse/typed_nodes.h @@ -606,8 +606,8 @@ struct FunctionSignature { using FunctionDecl = FunctionSignature; using FunctionDefinitionStart = - FunctionSignature; + FunctionSignature; // A function definition: `fn F() -> i32 { ... }`. struct FunctionDefinition { @@ -620,13 +620,18 @@ struct FunctionDefinition { Lex::CloseCurlyBraceTokenIndex token; }; -// A terse function definition: `fn F() -> i32 => expr;`. +using FunctionTerseDefinitionStart = + FunctionSignature; + +// A terse function definition: `fn F() => expr;`. struct FunctionTerseDefinition { static constexpr auto Kind = NodeKind::FunctionTerseDefinition.Define( {.category = NodeCategory::Decl, - .bracketed_by = FunctionDefinitionStart::Kind}); + .bracketed_by = FunctionTerseDefinitionStart::Kind, + .child_count = 2}); - FunctionDefinitionStartId signature; + FunctionTerseDefinitionStartId signature; AnyExprId body; Lex::SemiTokenIndex token; }; @@ -788,27 +793,44 @@ struct CodeBlock { using LambdaIntroducer = LeafNode; -struct LambdaDefinitionStart { - static constexpr auto Kind = NodeKind::LambdaDefinitionStart.Define( - {.bracketed_by = LambdaIntroducer::Kind}); +template +struct LambdaSignature { + static constexpr auto Kind = + KindT.Define({.bracketed_by = LambdaIntroducer::Kind}); LambdaIntroducerId introducer; std::optional implicit_params; std::optional explicit_params; std::optional return_type; - Lex::TokenIndex token; + TokenKind token; }; +using LambdaDefinitionStart = LambdaSignature; + struct Lambda { static constexpr auto Kind = NodeKind::Lambda.Define({.category = NodeCategory::Expr, .bracketed_by = LambdaDefinitionStart::Kind}); LambdaDefinitionStartId signature; - std::optional terse_body; llvm::SmallVector body; - // Use a generic token index because the token might be `}` or `fn`. - Lex::TokenIndex token; + Lex::CloseCurlyBraceTokenIndex token; +}; + +using LambdaTerseDefinitionStart = + LambdaSignature; + +struct LambdaTerseDefinition { + static constexpr auto Kind = NodeKind::LambdaTerseDefinition.Define( + {.category = NodeCategory::Expr, + .bracketed_by = LambdaTerseDefinitionStart::Kind, + .child_count = 2}); + + LambdaTerseDefinitionStartId signature; + AnyExprId body; + Lex::FnTokenIndex token; }; // An expression statement: `F(x);`. diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index 34b80c399be4..90835a1b2854 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -169,9 +169,11 @@ static auto IsDefinitionStart(Parse::NodeKind node_kind) -> bool { case Parse::NodeKind::ChoiceDefinitionStart: case Parse::NodeKind::ClassDefinitionStart: case Parse::NodeKind::FunctionDefinitionStart: + case Parse::NodeKind::FunctionTerseDefinitionStart: case Parse::NodeKind::ImplDefinitionStart: case Parse::NodeKind::InterfaceDefinitionStart: case Parse::NodeKind::LambdaDefinitionStart: + case Parse::NodeKind::LambdaTerseDefinitionStart: case Parse::NodeKind::NamedConstraintDefinitionStart: return true; default: