diff --git a/toolchain/check/cpp/import.cpp b/toolchain/check/cpp/import.cpp index 3db52a64474d..0c221f2a8b5c 100644 --- a/toolchain/check/cpp/import.cpp +++ b/toolchain/check/cpp/import.cpp @@ -1364,27 +1364,25 @@ static auto CreateFunctionSignatureInsts( Context& context, SemIR::LocId loc_id, clang::FunctionDecl* clang_decl, SemIR::ClangDeclKey::Signature signature) -> std::optional { - context.full_pattern_stack().PushFullPattern( - FullPatternStack::Kind::ImplicitParamList); - std::optional pop = llvm::scope_exit( - [&context] { context.full_pattern_stack().PopFullPattern(); }); + context.full_pattern_stack().StartImplicitParamList(); auto implicit_param_patterns_id = MakeImplicitParamPatternsBlockId(context, loc_id, *clang_decl); if (!implicit_param_patterns_id.has_value()) { return std::nullopt; } context.full_pattern_stack().EndImplicitParamList(); + context.full_pattern_stack().StartExplicitParamList(); auto param_patterns_id = MakeParamPatternsBlockId(context, loc_id, *clang_decl, signature); if (!param_patterns_id.has_value()) { return std::nullopt; } + context.full_pattern_stack().EndExplicitParamList(); auto [return_type_inst_id, return_form_inst_id, return_patterns_id] = GetReturnInfo(context, loc_id, clang_decl); if (return_type_inst_id == SemIR::ErrorInst::TypeInstId) { return std::nullopt; } - pop.reset(); auto match_results = CalleePatternMatch(context, implicit_param_patterns_id, param_patterns_id, diff --git a/toolchain/check/full_pattern_stack.h b/toolchain/check/full_pattern_stack.h index b5f478251913..9c7b777b2c15 100644 --- a/toolchain/check/full_pattern_stack.h +++ b/toolchain/check/full_pattern_stack.h @@ -29,33 +29,85 @@ class FullPatternStack { public: explicit FullPatternStack(LexicalLookup* lookup) : lookup_(lookup) {} - // The kind of a full-pattern. Note that an implicit parameter list and - // adjacent explicit parameter list together form a single full-pattern, - // but we separate them here in order to represent the state transition - // between them. + // The kind of a full-pattern. There are two primary kinds: name binding + // declarations and parameterized entity declarations. However, for efficiency + // we also use this enum to track state transitions within a parameterized + // entity declaration. A parameterized entity declaration always starts and + // finishes in the `NotInEitherParamList` state, and can transition to either + // the `ImplicitParamList` or `ExplicitParamList` state, and then back to the + // `NotInEitherParamList` state. enum class Kind { + // A name-binding declaration, such as a `let` or `var` statement. NameBindingDecl, + + // The implicit parameter list of a function or impl declaration. ImplicitParamList, + + // The explicit parameter list of a function declaration. ExplicitParamList, + + // This kind indicates that we're within the declaration of a parameterized + // entity (such as a function or impl), but not within an explicit or + // implicit parameter list. This is primarily useful for the return part of + // a function declaration, which doesn't contain pattern syntax, but can + // implicitly introduce output parameter patterns. However, the parse tree + // doesn't let us reliably distinguish the return part from the part before + // the parameter lists (or between them), particularly in the case where + // there is no explicit parameter list. + NotInEitherParamList }; + auto empty() const -> bool { return kind_stack_.empty(); } + // The kind of the current full-pattern. auto CurrentKind() const -> Kind { return kind_stack_.back(); } - // Marks the start of a new full-pattern of the specified kind. - auto PushFullPattern(Kind kind) -> void { - kind_stack_.push_back(kind); + // Marks the start of a new full-pattern for a parameterized entity + // declaration, such as a function or impl. The kind is initially + // NotInEitherParamList. + auto PushParameterizedDecl() -> void { + kind_stack_.push_back(Kind::NotInEitherParamList); bind_name_stack_.PushArray(); } - // Marks the end of an implicit parameter list, and the presumptive start - // of the corresponding explicit parameter list. + // Marks the start of a new full-pattern for a name binding declaration. + auto PushNameBindingDecl() -> void { + kind_stack_.push_back(Kind::NameBindingDecl); + bind_name_stack_.PushArray(); + } + + // Marks the start of the current parameterized entity's implicit parameter + // list. + auto StartImplicitParamList() -> void { + CARBON_CHECK(kind_stack_.back() == Kind::NotInEitherParamList, "{0}", + kind_stack_.back()); + kind_stack_.back() = Kind::ImplicitParamList; + } + + // Marks the end of the current parameterized entity's implicit parameter + // list. auto EndImplicitParamList() -> void { CARBON_CHECK(kind_stack_.back() == Kind::ImplicitParamList, "{0}", kind_stack_.back()); + kind_stack_.back() = Kind::NotInEitherParamList; + } + + // Marks the start of the current parameterized entity's explicit parameter + // list. + auto StartExplicitParamList() -> void { + CARBON_CHECK(kind_stack_.back() == Kind::NotInEitherParamList, "{0}", + kind_stack_.back()); kind_stack_.back() = Kind::ExplicitParamList; } + // Marks the end of the current parameterized entity's explicit parameter + // list. + auto EndExplicitParamList() -> void { + CARBON_CHECK(kind_stack_.back() == Kind::ExplicitParamList, "{0}", + kind_stack_.back()); + kind_stack_.back() = Kind::NotInEitherParamList; + } + // Marks the start of the initializer for the current name binding decl. auto StartPatternInitializer() -> void { CARBON_CHECK(kind_stack_.back() == Kind::NameBindingDecl); @@ -98,7 +150,7 @@ class FullPatternStack { // Runs verification that the processing cleanly finished. auto VerifyOnFinish() const -> void { CARBON_CHECK(kind_stack_.empty(), - "full_pattern_stack still has {1} entries", + "full_pattern_stack still has {0} entries", kind_stack_.size()); } diff --git a/toolchain/check/function.cpp b/toolchain/check/function.cpp index 40cc184b988e..1404900cc185 100644 --- a/toolchain/check/function.cpp +++ b/toolchain/check/function.cpp @@ -35,9 +35,11 @@ auto FindSelfPattern(Context& context, auto AddReturnPatterns(Context& context, SemIR::LocId loc_id, Context::FormExpr form_expr) -> SemIR::InstBlockId { llvm::SmallVector return_patterns; - auto form_inst = context.insts().Get(form_expr.form_inst_id); + auto form_inst = context.insts().Get( + context.constant_values().GetConstantInstId(form_expr.form_inst_id)); CARBON_KIND_SWITCH(form_inst) { - case SemIR::RefForm::Kind: { + case SemIR::RefForm::Kind: + case SemIR::ValueForm::Kind: { break; } case CARBON_KIND(SemIR::InitForm _): { @@ -56,6 +58,11 @@ auto AddReturnPatterns(Context& context, SemIR::LocId loc_id, case SemIR::ErrorInst::Kind: { break; } + case SemIR::SymbolicBinding::Kind: + CARBON_CHECK( + context.constant_values().Get(form_expr.form_inst_id).is_symbolic()); + context.TODO(loc_id, "Support symbolic return forms"); + break; default: CARBON_FATAL("unexpected inst kind: {0}", form_inst); } @@ -115,8 +122,7 @@ static auto MakeFunctionSignature(Context& context, SemIR::LocId loc_id, // Build and add a `[ref self: Self]` parameter if needed. if (args.self_type_id.has_value()) { - context.full_pattern_stack().PushFullPattern( - FullPatternStack::Kind::ImplicitParamList); + context.full_pattern_stack().StartImplicitParamList(); BeginSubpattern(context); auto self_type_region_id = EndSubpatternAsExpr( @@ -129,13 +135,11 @@ static auto MakeFunctionSignature(Context& context, SemIR::LocId loc_id, context.inst_blocks().Add({insts.self_param_id}); context.full_pattern_stack().EndImplicitParamList(); - } else { - context.full_pattern_stack().PushFullPattern( - FullPatternStack::Kind::ExplicitParamList); } // Build and add any explicit parameters. We always use value parameters for // now. + context.full_pattern_stack().StartExplicitParamList(); if (args.param_type_ids.empty()) { insts.param_patterns_id = SemIR::InstBlockId::Empty; } else { @@ -151,6 +155,7 @@ static auto MakeFunctionSignature(Context& context, SemIR::LocId loc_id, } insts.param_patterns_id = context.inst_block_stack().Pop(); } + context.full_pattern_stack().EndExplicitParamList(); // Build and add the return type. We always use an initializing form for now. if (args.return_type_id.has_value()) { @@ -168,7 +173,6 @@ static auto MakeFunctionSignature(Context& context, SemIR::LocId loc_id, insts.call_params_id = match_results.call_params_id; insts.call_param_ranges = match_results.param_ranges; - context.full_pattern_stack().PopFullPattern(); auto [pattern_block_id, decl_block_id] = FinishFunctionSignature(context, /*check_unused=*/false); insts.pattern_block_id = pattern_block_id; @@ -428,10 +432,12 @@ auto StartFunctionSignature(Context& context) -> void { context.scope_stack().PushForDeclName(); context.inst_block_stack().Push(); context.pattern_block_stack().Push(); + context.full_pattern_stack().PushParameterizedDecl(); } auto FinishFunctionSignature(Context& context, bool check_unused) -> FinishFunctionSignatureResult { + context.full_pattern_stack().PopFullPattern(); auto pattern_block_id = context.pattern_block_stack().Pop(); auto decl_block_id = context.inst_block_stack().Pop(); context.scope_stack().Pop(check_unused); diff --git a/toolchain/check/handle_binding_pattern.cpp b/toolchain/check/handle_binding_pattern.cpp index fccd30e481f2..d80c23bfadee 100644 --- a/toolchain/check/handle_binding_pattern.cpp +++ b/toolchain/check/handle_binding_pattern.cpp @@ -340,6 +340,9 @@ static auto HandleAnyBindingPattern(Context& context, Parse::NodeId node_id, context.node_stack().Push(node_id, binding_pattern_id); break; } + + case FullPatternStack::Kind::NotInEitherParamList: + CARBON_FATAL("Unreachable"); } return true; } diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index 8ca67215e33c..930cb70231f4 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -49,19 +49,29 @@ auto HandleParseNode(Context& context, Parse::FunctionIntroducerId node_id) return true; } -auto HandleParseNode(Context& context, Parse::ReturnTypeId node_id) -> bool { - auto [type_node_id, type_inst_id] = context.node_stack().PopExprWithNodeId(); - - // Propagate the type expression. - auto form_expr = ReturnExprAsForm(context, type_node_id, type_inst_id); +// Handles a `->` or `->?` return declaration. +static auto HandleReturnDecl(Context& context, Parse::AnyReturnDeclId node_id) + -> bool { + auto [expr_node_id, expr_inst_id] = context.node_stack().PopExprWithNodeId(); + Context::FormExpr form_expr = [&]() { + if (context.parse_tree().node_kind(node_id) == Parse::ReturnTypeId::Kind) { + return ReturnExprAsForm(context, expr_node_id, expr_inst_id); + } else { + return FormExprAsForm(context, expr_node_id, expr_inst_id); + } + }(); context.PushReturnForm(form_expr); auto return_patterns_id = AddReturnPatterns(context, node_id, form_expr); context.node_stack().Push(node_id, return_patterns_id); return true; } +auto HandleParseNode(Context& context, Parse::ReturnTypeId node_id) -> bool { + return HandleReturnDecl(context, node_id); +} + auto HandleParseNode(Context& context, Parse::ReturnFormId node_id) -> bool { - return context.TODO(node_id, "Support ->?"); + return HandleReturnDecl(context, node_id); } // Diagnoses issues with the modifiers, removing modifiers that shouldn't be @@ -486,7 +496,8 @@ static auto BuildFunctionDecl(Context& context, auto return_type_inst_id = SemIR::TypeInstId::None; auto return_form_inst_id = SemIR::InstId::None; if (auto [return_node, maybe_return_patterns_id] = - context.node_stack().PopWithNodeIdIf(); + context.node_stack() + .PopWithNodeIdIf(); maybe_return_patterns_id) { return_patterns_id = *maybe_return_patterns_id; auto return_form = context.PopReturnForm(); diff --git a/toolchain/check/handle_impl.cpp b/toolchain/check/handle_impl.cpp index ad31d620d06b..6043eb8bb422 100644 --- a/toolchain/check/handle_impl.cpp +++ b/toolchain/check/handle_impl.cpp @@ -63,8 +63,7 @@ auto HandleParseNode(Context& context, Parse::ImplIntroducerId node_id) auto HandleParseNode(Context& context, Parse::ForallId /*node_id*/) -> bool { // Push a pattern block for the signature of the `forall`. context.pattern_block_stack().Push(); - context.full_pattern_stack().PushFullPattern( - FullPatternStack::Kind::ImplicitParamList); + context.full_pattern_stack().PushParameterizedDecl(); return true; } diff --git a/toolchain/check/handle_let_and_var.cpp b/toolchain/check/handle_let_and_var.cpp index 1354cc79b0b4..7784a5e15aef 100644 --- a/toolchain/check/handle_let_and_var.cpp +++ b/toolchain/check/handle_let_and_var.cpp @@ -62,8 +62,7 @@ static auto HandleIntroducer(Context& context, Parse::NodeId node_id) -> bool { // Push a bracketing node and pattern block to establish the pattern context. context.node_stack().Push(node_id); context.pattern_block_stack().Push(); - context.full_pattern_stack().PushFullPattern( - FullPatternStack::Kind::NameBindingDecl); + context.full_pattern_stack().PushNameBindingDecl(); BeginSubpattern(context); return true; } @@ -113,6 +112,8 @@ auto HandleParseNode(Context& context, Parse::VariablePatternId node_id) break; case FullPatternStack::Kind::NameBindingDecl: break; + case FullPatternStack::Kind::NotInEitherParamList: + CARBON_FATAL("Unreachable"); } auto pattern_id = AddPatternInst( diff --git a/toolchain/check/handle_loop_statement.cpp b/toolchain/check/handle_loop_statement.cpp index 4ac6e5c5aa2f..ab91e123a493 100644 --- a/toolchain/check/handle_loop_statement.cpp +++ b/toolchain/check/handle_loop_statement.cpp @@ -115,8 +115,7 @@ auto HandleParseNode(Context& context, Parse::ForHeaderStartId node_id) // Begin an implicit let declaration context for the pattern. context.decl_introducer_state_stack().Push(); context.pattern_block_stack().Push(); - context.full_pattern_stack().PushFullPattern( - FullPatternStack::Kind::NameBindingDecl); + context.full_pattern_stack().PushNameBindingDecl(); BeginSubpattern(context); context.node_stack().Push(node_id); diff --git a/toolchain/check/handle_name.cpp b/toolchain/check/handle_name.cpp index 430e55260c81..5b6262adb133 100644 --- a/toolchain/check/handle_name.cpp +++ b/toolchain/check/handle_name.cpp @@ -128,8 +128,7 @@ auto HandleParseNode(Context& context, -> bool { // Push a pattern block stack entry to handle the parameter pattern. context.pattern_block_stack().Push(); - context.full_pattern_stack().PushFullPattern( - FullPatternStack::Kind::ImplicitParamList); + context.full_pattern_stack().PushParameterizedDecl(); // The parent is responsible for binding the name. context.node_stack().Push(node_id, GetIdentifierAsNameId(context, node_id)); return true; diff --git a/toolchain/check/handle_pattern_list.cpp b/toolchain/check/handle_pattern_list.cpp index 075ea630f39a..e96b5574bd9b 100644 --- a/toolchain/check/handle_pattern_list.cpp +++ b/toolchain/check/handle_pattern_list.cpp @@ -21,6 +21,7 @@ static auto HandlePatternListStart(Context& context, Parse::NodeId node_id) auto HandleParseNode(Context& context, Parse::ImplicitParamListStartId node_id) -> bool { + context.full_pattern_stack().StartImplicitParamList(); return HandlePatternListStart(context, node_id); } @@ -31,7 +32,7 @@ auto HandleParseNode(Context& context, Parse::TuplePatternStartId node_id) auto HandleParseNode(Context& context, Parse::ExplicitParamListStartId node_id) -> bool { - context.full_pattern_stack().EndImplicitParamList(); + context.full_pattern_stack().StartExplicitParamList(); return HandlePatternListStart(context, node_id); } @@ -53,12 +54,14 @@ static auto HandleParamListEnd(Context& context, Parse::NodeId node_id, auto HandleParseNode(Context& context, Parse::ImplicitParamListId node_id) -> bool { + context.full_pattern_stack().EndImplicitParamList(); return HandleParamListEnd(context, node_id, Parse::NodeKind::ImplicitParamListStart); } auto HandleParseNode(Context& context, Parse::ExplicitParamListId node_id) -> bool { + context.full_pattern_stack().EndExplicitParamList(); return HandleParamListEnd(context, node_id, Parse::NodeKind::ExplicitParamListStart); } diff --git a/toolchain/check/node_stack.h b/toolchain/check/node_stack.h index 92ff972e535c..f776e7a7c181 100644 --- a/toolchain/check/node_stack.h +++ b/toolchain/check/node_stack.h @@ -403,6 +403,8 @@ class NodeStack { Parse::NodeCategory::Statement | Parse::NodeCategory::Modifier, Id::Kind::None); + set_id_if_category_is(Parse::NodeCategory::ReturnDecl, + Id::KindFor()); return result; } @@ -426,8 +428,6 @@ class NodeStack { case Parse::NodeKind::IfExprIf: case Parse::NodeKind::ImplicitParamList: case Parse::NodeKind::WhileConditionStart: - case Parse::NodeKind::ReturnType: - case Parse::NodeKind::ReturnForm: return Id::KindFor(); case Parse::NodeKind::FunctionDefinitionStart: case Parse::NodeKind::BuiltinFunctionDefinitionStart: diff --git a/toolchain/check/testdata/function/call/form.carbon b/toolchain/check/testdata/function/call/form.carbon index 814f99d8658e..019f451b7db0 100644 --- a/toolchain/check/testdata/function/call/form.carbon +++ b/toolchain/check/testdata/function/call/form.carbon @@ -151,67 +151,84 @@ fn F(Form:! Core.Form()) { let y:? Form = 0; } -// --- fail_todo_ref_return_form.carbon +// --- ref_return_form.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin -// CHECK:STDERR: fail_todo_ref_return_form.carbon:[[@LINE+4]]:8: error: semantics TODO: `Support ->?` [SemanticsTodo] -// CHECK:STDERR: fn F() ->? form(ref i32); -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~ -// CHECK:STDERR: fn F() ->? form(ref i32); //@dump-sem-ir-end fn G() { //@dump-sem-ir-begin - let ref x: i32 = F(); + let ref _: i32 = F(); //@dump-sem-ir-end } -// --- fail_todo_var_return_form.carbon +// --- var_return_form.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin -// CHECK:STDERR: fail_todo_var_return_form.carbon:[[@LINE+4]]:8: error: semantics TODO: `Support ->?` [SemanticsTodo] -// CHECK:STDERR: fn F() ->? form(var i32); -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~ -// CHECK:STDERR: fn F() ->? form(var i32); //@dump-sem-ir-end fn G() { //@dump-sem-ir-begin - let var x: i32 = F(); + let var _: i32 = F(); //@dump-sem-ir-end } -// --- fail_todo_val_return_form.carbon +// --- val_return_form.carbon library "[[@TEST_NAME]]"; //@dump-sem-ir-begin -// CHECK:STDERR: fail_todo_val_return_form.carbon:[[@LINE+4]]:8: error: semantics TODO: `Support ->?` [SemanticsTodo] -// CHECK:STDERR: fn F() ->? form(val i32); -// CHECK:STDERR: ^~~~~~~~~~~~~~~~~ -// CHECK:STDERR: fn F() ->? form(val i32); //@dump-sem-ir-end fn G() { //@dump-sem-ir-begin - let x: i32 = F(); + let _: i32 = F(); //@dump-sem-ir-end } // --- fail_return_form_not_form.carbon library "[[@TEST_NAME]]"; -// CHECK:STDERR: fail_return_form_not_form.carbon:[[@LINE+4]]:8: error: semantics TODO: `Support ->?` [SemanticsTodo] +// CHECK:STDERR: fail_return_form_not_form.carbon:[[@LINE+7]]:12: error: cannot implicitly convert expression of type `type` to `Core.Form` [ConversionFailure] // CHECK:STDERR: fn F() ->? i32; -// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: ^~~ +// CHECK:STDERR: fail_return_form_not_form.carbon:[[@LINE+4]]:12: note: type `type` does not implement interface `Core.ImplicitAs(Core.Form)` [MissingImplInMemberAccessInContext] +// CHECK:STDERR: fn F() ->? i32; +// CHECK:STDERR: ^~~ // CHECK:STDERR: fn F() ->? i32; +// CHECK:STDERR: fail_return_form_not_form.carbon:[[@LINE+11]]:12: error: `ref` tag is not an argument to a `ref` parameter [RefTagNoRefParam] +// CHECK:STDERR: fn F() ->? ref i32; +// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: +// CHECK:STDERR: fail_return_form_not_form.carbon:[[@LINE+7]]:12: error: cannot implicitly convert expression of type `type` to `Core.Form` [ConversionFailure] +// CHECK:STDERR: fn F() ->? ref i32; +// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: fail_return_form_not_form.carbon:[[@LINE+4]]:12: note: type `type` does not implement interface `Core.ImplicitAs(Core.Form)` [MissingImplInMemberAccessInContext] +// CHECK:STDERR: fn F() ->? ref i32; +// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: fn F() ->? ref i32; +// -- fail_todo_form_generic_return.carbon + +// CHECK:STDERR: fail_return_form_not_form.carbon:[[@LINE+11]]:26: error: semantics TODO: `Support symbolic return forms` [SemanticsTodo] +// CHECK:STDERR: fn F(Form:! Core.Form()) ->? Form; +// CHECK:STDERR: ^~~~~~~~ +// CHECK:STDERR: +// CHECK:STDERR: fail_return_form_not_form.carbon:[[@LINE+7]]:1: error: redeclaration differs because of parameter count of 1 [RedeclParamCountDiffers] +// CHECK:STDERR: fn F(Form:! Core.Form()) ->? Form; +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: fail_return_form_not_form.carbon:[[@LINE-23]]:1: note: previously declared with parameter count of 0 [RedeclParamCountPrevious] +// CHECK:STDERR: fn F() ->? i32; +// CHECK:STDERR: ^~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn F(Form:! Core.Form()) ->? Form; + // CHECK:STDOUT: --- ref_form_param.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { @@ -411,9 +428,114 @@ fn F() ->? ref i32; // CHECK:STDOUT: // CHECK:STDOUT: fn @F(%x.param: ref %empty_tuple.type); // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_ref_return_form.carbon +// CHECK:STDOUT: --- ref_return_form.carbon // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_var_return_form.carbon +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] +// CHECK:STDOUT: %.1da: Core.Form = ref_form %i32 [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] +// CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_val_return_form.carbon +// CHECK:STDOUT: file { +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} { +// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %.loc4: Core.Form = ref_form %i32 [concrete = constants.%.1da] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() -> ref %i32; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %_.patt: %pattern_type.7ce = ref_binding_pattern _ [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] +// CHECK:STDOUT: %F.call: ref %i32 = call %F.ref() +// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %_: ref %i32 = ref_binding _, %F.call +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- var_return_form.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] +// CHECK:STDOUT: %.ff5: Core.Form = init_form %i32 [concrete] +// CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %Destroy.Op.type: type = fn_type @Destroy.Op [concrete] +// CHECK:STDOUT: %Destroy.Op: %Destroy.Op.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] { +// CHECK:STDOUT: %return.patt: %pattern_type.7ce = return_slot_pattern [concrete] +// CHECK:STDOUT: %return.param_patt: %pattern_type.7ce = out_param_pattern %return.patt [concrete] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %.loc4: Core.Form = init_form %i32 [concrete = constants.%.ff5] +// CHECK:STDOUT: %return.param: ref %i32 = out_param call_param0 +// CHECK:STDOUT: %return: ref %i32 = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() -> out %return.param: %i32; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %_.patt: %pattern_type.7ce = ref_binding_pattern _ [concrete] +// CHECK:STDOUT: %_.var_patt: %pattern_type.7ce = var_pattern %_.patt [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %_.var: ref %i32 = var %_.var_patt +// CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] +// CHECK:STDOUT: %F.call: init %i32 = call %F.ref() +// CHECK:STDOUT: assign %_.var, %F.call +// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %_: ref %i32 = ref_binding _, %_.var +// CHECK:STDOUT: %Destroy.Op.bound: = bound_method %_.var, constants.%Destroy.Op +// CHECK:STDOUT: %Destroy.Op.call: init %empty_tuple.type = call %Destroy.Op.bound(%_.var) +// CHECK:STDOUT: +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Destroy.Op(%self.param: ref %i32) = "no_op"; +// CHECK:STDOUT: +// CHECK:STDOUT: --- val_return_form.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %int_32: Core.IntLiteral = int_value 32 [concrete] +// CHECK:STDOUT: %i32: type = class_type @Int, @Int(%int_32) [concrete] +// CHECK:STDOUT: %.754: Core.Form = value_form %i32 [concrete] +// CHECK:STDOUT: %F.type: type = fn_type @F [concrete] +// CHECK:STDOUT: %F: %F.type = struct_value () [concrete] +// CHECK:STDOUT: %pattern_type.7ce: type = pattern_type %i32 [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: %F.decl: %F.type = fn_decl @F [concrete = constants.%F] {} { +// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %.loc4: Core.Form = value_form %i32 [concrete = constants.%.754] +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @F() -> val %i32; +// CHECK:STDOUT: +// CHECK:STDOUT: fn @G() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: name_binding_decl { +// CHECK:STDOUT: %_.patt: %pattern_type.7ce = value_binding_pattern _ [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: %F.ref: %F.type = name_ref F, file.%F.decl [concrete = constants.%F] +// CHECK:STDOUT: %F.call: %i32 = call %F.ref() +// CHECK:STDOUT: %i32: type = type_literal constants.%i32 [concrete = constants.%i32] +// CHECK:STDOUT: %_: %i32 = value_binding _, %F.call +// CHECK:STDOUT: +// CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/lower/file_context.cpp b/toolchain/lower/file_context.cpp index 9742e1fc933e..e3f7908e71a9 100644 --- a/toolchain/lower/file_context.cpp +++ b/toolchain/lower/file_context.cpp @@ -519,6 +519,26 @@ auto FileContext::FunctionTypeInfoBuilder::HandleReturnForm( ref_form.type_component_inst_id)); return SetReturnByReference(return_type_id); } + case CARBON_KIND(SemIR::ValueForm val_form): { + auto return_type_id = + context_.sem_ir().types().GetTypeIdForTypeConstantId( + SemIR::GetConstantValueInSpecific( + context_.sem_ir(), specific_id_, + val_form.type_component_inst_id)); + switch ( + SemIR::ValueRepr::ForType(context_.sem_ir(), return_type_id).kind) { + case SemIR::ValueRepr::Unknown: + case SemIR::ValueRepr::Dependent: + return Abort(); + case SemIR::ValueRepr::None: + return SetReturnByCopy(SemIR::TypeId::None); + case SemIR::ValueRepr::Copy: + return SetReturnByCopy(return_type_id); + case SemIR::ValueRepr::Pointer: + case SemIR::ValueRepr::Custom: + return SetReturnByReference(return_type_id); + } + } default: CARBON_FATAL("Unexpected inst kind: {0}", return_form_inst); } @@ -526,12 +546,12 @@ auto FileContext::FunctionTypeInfoBuilder::HandleReturnForm( auto FileContext::FunctionTypeInfoBuilder::HandleParameter( SemIR::CallParamIndex index) -> bool { + const auto& sem_ir = context_.sem_ir(); auto param_pattern_id = call_param_pattern_ids_[index.index]; - auto param_pattern = context_.sem_ir().insts().Get(param_pattern_id); + auto param_pattern = sem_ir.insts().Get(param_pattern_id); auto param_type_id = ExtractScrutineeType( - context_.sem_ir(), - SemIR::GetTypeOfInstInSpecific(context_.sem_ir(), specific_id_, - param_pattern_id)); + sem_ir, + SemIR::GetTypeOfInstInSpecific(sem_ir, specific_id_, param_pattern_id)); // Returns the appropriate LoweredTypes for reference-like parameters. auto ref_lowered_types = [&]() -> LoweredTypes { @@ -545,13 +565,42 @@ auto FileContext::FunctionTypeInfoBuilder::HandleParameter( !param_type_id.AsConstantId().is_symbolic(), "Found symbolic type id after resolution when lowering type {0}.", param_pattern.type_id()); - CARBON_KIND_SWITCH(param_pattern) { + + auto param_kind = param_pattern.kind(); + + // Treat a form parameter pattern like the kind of param pattern that + // corresponds to its form. + if (auto form_param_pattern = + param_pattern.TryAs()) { + auto form_binding_pattern = sem_ir.insts().GetAs( + form_param_pattern->subpattern_id); + auto form_id = + sem_ir.entity_names().Get(form_binding_pattern.entity_name_id).form_id; + CARBON_CHECK(!form_id.is_symbolic(), "TODO"); + auto form_inst_id = sem_ir.constant_values().GetInstId(form_id); + auto form_kind = sem_ir.insts().Get(form_inst_id).kind(); + switch (form_kind) { + case SemIR::InitForm::Kind: + param_kind = SemIR::VarParamPattern::Kind; + break; + case SemIR::RefForm::Kind: + param_kind = SemIR::RefParamPattern::Kind; + break; + case SemIR::ValueForm::Kind: + param_kind = SemIR::ValueParamPattern::Kind; + break; + default: + CARBON_FATAL("Unexpected kind {0} for form inst", form_kind); + } + } + + switch (param_kind) { case SemIR::RefParamPattern::Kind: case SemIR::VarParamPattern::Kind: { return AddLoweredParam(index, param_pattern_id, ref_lowered_types()); } case SemIR::OutParamPattern::Kind: { - switch (SemIR::InitRepr::ForType(context_.sem_ir(), param_type_id).kind) { + switch (SemIR::InitRepr::ForType(sem_ir, param_type_id).kind) { case SemIR::InitRepr::InPlace: return AddLoweredParam(index, param_pattern_id, ref_lowered_types()); case SemIR::InitRepr::ByCopy: @@ -564,8 +613,7 @@ auto FileContext::FunctionTypeInfoBuilder::HandleParameter( } } case SemIR::ValueParamPattern::Kind: { - switch (auto value_rep = - SemIR::ValueRepr::ForType(context_.sem_ir(), param_type_id); + switch (auto value_rep = SemIR::ValueRepr::ForType(sem_ir, param_type_id); value_rep.kind) { case SemIR::ValueRepr::Unknown: return Abort(); diff --git a/toolchain/lower/testdata/function/call/form.carbon b/toolchain/lower/testdata/function/call/form.carbon new file mode 100644 index 000000000000..fa9182affb01 --- /dev/null +++ b/toolchain/lower/testdata/function/call/form.carbon @@ -0,0 +1,247 @@ +// 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/call/form.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/lower/testdata/function/call/form.carbon + +// --- ref_form_param.carbon +library "[[@TEST_NAME]]"; + +fn F(x:? form(ref i32)); + +fn G() { + var y: i32 = 0; + F(ref y); +} + +// --- var_form_param.carbon +library "[[@TEST_NAME]]"; + +fn F(x:? form(var i32)); + +fn G() { + F(0); +} + +// --- val_form_param.carbon +library "[[@TEST_NAME]]"; + +fn F(x:? form(val i32)); + +fn G() { + F(0); +} + +// --- ref_return_form.carbon +library "[[@TEST_NAME]]"; + +fn F() ->? form(ref i32); + +fn G() { + let unused ref x: i32 = F(); +} + +// --- var_return_form.carbon +library "[[@TEST_NAME]]"; + +fn F() ->? form(var i32); + +fn G() { + let unused var x: i32 = F(); +} + +// --- val_return_form.carbon +library "[[@TEST_NAME]]"; + +fn F() ->? form(val i32); + +fn G() { + let unused x: i32 = F(); +} + +// CHECK:STDOUT: ; ModuleID = 'ref_form_param.carbon' +// CHECK:STDOUT: source_filename = "ref_form_param.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CF.Main(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CG.Main() #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %y.var = alloca i32, align 4, !dbg !7 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %y.var), !dbg !7 +// CHECK:STDOUT: store i32 0, ptr %y.var, align 4, !dbg !7 +// CHECK:STDOUT: call void @_CF.Main(ptr %y.var), !dbg !8 +// CHECK:STDOUT: ret void, !dbg !9 +// 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: attributes #0 = { nounwind } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "ref_form_param.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "G", linkageName: "_CG.Main", scope: null, file: !3, line: 5, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{null} +// CHECK:STDOUT: !7 = !DILocation(line: 6, column: 3, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 7, column: 3, scope: !4) +// CHECK:STDOUT: !9 = !DILocation(line: 5, column: 1, scope: !4) +// CHECK:STDOUT: ; ModuleID = 'var_form_param.carbon' +// CHECK:STDOUT: source_filename = "var_form_param.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CF.Main(ptr) +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CG.Main() #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %.loc3_7.1.temp = alloca i32, align 4, !dbg !7 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %.loc3_7.1.temp), !dbg !7 +// CHECK:STDOUT: store i32 0, ptr %.loc3_7.1.temp, align 4, !dbg !7 +// CHECK:STDOUT: call void @_CF.Main(ptr %.loc3_7.1.temp), !dbg !8 +// CHECK:STDOUT: ret void, !dbg !9 +// 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: attributes #0 = { nounwind } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "var_form_param.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "G", linkageName: "_CG.Main", scope: null, file: !3, line: 5, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{null} +// CHECK:STDOUT: !7 = !DILocation(line: 3, column: 6, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 6, column: 3, scope: !4) +// CHECK:STDOUT: !9 = !DILocation(line: 5, column: 1, scope: !4) +// CHECK:STDOUT: ; ModuleID = 'val_form_param.carbon' +// CHECK:STDOUT: source_filename = "val_form_param.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: declare void @_CF.Main(i32) +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CG.Main() #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: call void @_CF.Main(i32 0), !dbg !7 +// CHECK:STDOUT: ret void, !dbg !8 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "val_form_param.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "G", linkageName: "_CG.Main", scope: null, file: !3, line: 5, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{null} +// CHECK:STDOUT: !7 = !DILocation(line: 6, column: 3, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 5, column: 1, scope: !4) +// CHECK:STDOUT: ; ModuleID = 'ref_return_form.carbon' +// CHECK:STDOUT: source_filename = "ref_return_form.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: declare ptr @_CF.Main() +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CG.Main() #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %F.call = call ptr @_CF.Main(), !dbg !7 +// CHECK:STDOUT: ret void, !dbg !8 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "ref_return_form.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "G", linkageName: "_CG.Main", scope: null, file: !3, line: 5, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{null} +// CHECK:STDOUT: !7 = !DILocation(line: 6, column: 27, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 5, column: 1, scope: !4) +// CHECK:STDOUT: ; ModuleID = 'var_return_form.carbon' +// CHECK:STDOUT: source_filename = "var_return_form.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: declare i32 @_CF.Main() +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CG.Main() #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %x.var = alloca i32, align 4, !dbg !7 +// CHECK:STDOUT: call void @llvm.lifetime.start.p0(ptr %x.var), !dbg !7 +// CHECK:STDOUT: %F.call = call i32 @_CF.Main(), !dbg !8 +// CHECK:STDOUT: store i32 %F.call, ptr %x.var, align 4, !dbg !7 +// CHECK:STDOUT: ret void, !dbg !9 +// 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: attributes #0 = { nounwind } +// CHECK:STDOUT: attributes #1 = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "var_return_form.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "G", linkageName: "_CG.Main", scope: null, file: !3, line: 5, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{null} +// CHECK:STDOUT: !7 = !DILocation(line: 6, column: 14, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 6, column: 27, scope: !4) +// CHECK:STDOUT: !9 = !DILocation(line: 5, column: 1, scope: !4) +// CHECK:STDOUT: ; ModuleID = 'val_return_form.carbon' +// CHECK:STDOUT: source_filename = "val_return_form.carbon" +// CHECK:STDOUT: +// CHECK:STDOUT: declare i32 @_CF.Main() +// CHECK:STDOUT: +// CHECK:STDOUT: ; Function Attrs: nounwind +// CHECK:STDOUT: define void @_CG.Main() #0 !dbg !4 { +// CHECK:STDOUT: entry: +// CHECK:STDOUT: %F.call = call i32 @_CF.Main(), !dbg !7 +// CHECK:STDOUT: ret void, !dbg !8 +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: attributes #0 = { nounwind } +// CHECK:STDOUT: +// CHECK:STDOUT: !llvm.module.flags = !{!0, !1} +// CHECK:STDOUT: !llvm.dbg.cu = !{!2} +// CHECK:STDOUT: +// CHECK:STDOUT: !0 = !{i32 7, !"Dwarf Version", i32 5} +// CHECK:STDOUT: !1 = !{i32 2, !"Debug Info Version", i32 3} +// CHECK:STDOUT: !2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !3, producer: "carbon", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +// CHECK:STDOUT: !3 = !DIFile(filename: "val_return_form.carbon", directory: "") +// CHECK:STDOUT: !4 = distinct !DISubprogram(name: "G", linkageName: "_CG.Main", scope: null, file: !3, line: 5, type: !5, spFlags: DISPFlagDefinition, unit: !2) +// CHECK:STDOUT: !5 = !DISubroutineType(types: !6) +// CHECK:STDOUT: !6 = !{null} +// CHECK:STDOUT: !7 = !DILocation(line: 6, column: 23, scope: !4) +// CHECK:STDOUT: !8 = !DILocation(line: 5, column: 1, scope: !4) diff --git a/toolchain/parse/node_category.h b/toolchain/parse/node_category.h index 10b0feb96c93..87bb568a96bd 100644 --- a/toolchain/parse/node_category.h +++ b/toolchain/parse/node_category.h @@ -32,6 +32,7 @@ namespace Carbon::Parse { /* `require impls` or just `require impls` */ \ X(RequireImpls) \ X(Requirement) \ + X(ReturnDecl) \ X(Statement) // We expect this to grow, so are using a bigger size than needed. diff --git a/toolchain/parse/node_ids.h b/toolchain/parse/node_ids.h index 07a567c58863..4d7d6687af0e 100644 --- a/toolchain/parse/node_ids.h +++ b/toolchain/parse/node_ids.h @@ -116,6 +116,7 @@ using AnyObserveOperandId = NodeIdInCategory; using AnyNonExprNameId = NodeIdInCategory; using AnyPackageNameId = NodeIdInCategory; +using AnyReturnDeclId = NodeIdInCategory; namespace Internal { template diff --git a/toolchain/parse/typed_nodes.h b/toolchain/parse/typed_nodes.h index beade4f0a3e8..dbfb166d812e 100644 --- a/toolchain/parse/typed_nodes.h +++ b/toolchain/parse/typed_nodes.h @@ -459,7 +459,8 @@ using FunctionIntroducer = // A return type: `-> i32`. struct ReturnType { - static constexpr auto Kind = NodeKind::ReturnType.Define({.child_count = 1}); + static constexpr auto Kind = NodeKind::ReturnType.Define( + {.category = NodeCategory::ReturnDecl, .child_count = 1}); Lex::MinusGreaterTokenIndex token; AnyExprId type; @@ -467,7 +468,8 @@ struct ReturnType { // A return form: `->? form(var i32)` struct ReturnForm { - static constexpr auto Kind = NodeKind::ReturnForm.Define({.child_count = 1}); + static constexpr auto Kind = NodeKind::ReturnForm.Define( + {.category = NodeCategory::ReturnDecl, .child_count = 1}); Lex::MinusGreaterQuestionTokenIndex token; AnyExprId type; @@ -483,7 +485,7 @@ struct FunctionSignature { FunctionIntroducerId introducer; llvm::SmallVector modifiers; DeclName name; - std::optional> return_type; + std::optional return_type; TokenKind token; }; diff --git a/toolchain/sem_ir/expr_info.cpp b/toolchain/sem_ir/expr_info.cpp index 062dea7cc751..26533f038aa8 100644 --- a/toolchain/sem_ir/expr_info.cpp +++ b/toolchain/sem_ir/expr_info.cpp @@ -77,6 +77,8 @@ static auto GetExprCategoryImpl(const File* ir, InstId inst_id) return ExprCategory::ReprInitializing; case CARBON_KIND(RefForm _): return ExprCategory::DurableRef; + case CARBON_KIND(ValueForm _): + return ExprCategory::Value; case CARBON_KIND(ErrorInst _): return ExprCategory::Error; default: diff --git a/toolchain/sem_ir/formatter.cpp b/toolchain/sem_ir/formatter.cpp index b90bb3f62d4f..f34accf8f5d5 100644 --- a/toolchain/sem_ir/formatter.cpp +++ b/toolchain/sem_ir/formatter.cpp @@ -764,6 +764,11 @@ auto Formatter::FormatFunctionSignature(InstBlockId params_id, FormatInstAsType(ref_form.type_component_inst_id); break; } + case CARBON_KIND(ValueForm val_form): { + out() << "val "; + FormatInstAsType(val_form.type_component_inst_id); + break; + } case CARBON_KIND(ErrorInst _): { FormatInstAsType(return_form_id); break;