From 216c499cbfb3170d0c2348c5a0b3d63b1768a990 Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Fri, 14 Mar 2025 15:54:12 -0700 Subject: [PATCH] Add declaration checking for `fn destroy` (#5127) This adds support for the `destroy` name, and checking of `fn destroy` structure. It does not add support for the actual destructor calls. --- toolchain/check/handle_function.cpp | 110 ++- toolchain/check/handle_name.cpp | 77 +- .../testdata/class/no_prelude/destroy.carbon | 656 ++++++++++++++++-- toolchain/diagnostics/diagnostic_kind.def | 6 + toolchain/sem_ir/ids.h | 2 + toolchain/sem_ir/name.cpp | 2 + 6 files changed, 762 insertions(+), 91 deletions(-) diff --git a/toolchain/check/handle_function.cpp b/toolchain/check/handle_function.cpp index ebb8e3ff8c0e..9edb4108cab9 100644 --- a/toolchain/check/handle_function.cpp +++ b/toolchain/check/handle_function.cpp @@ -23,6 +23,7 @@ #include "toolchain/check/name_lookup.h" #include "toolchain/check/type.h" #include "toolchain/check/type_completion.h" +#include "toolchain/lex/token_kind.h" #include "toolchain/parse/node_ids.h" #include "toolchain/sem_ir/builtin_function_kind.h" #include "toolchain/sem_ir/entry_point.h" @@ -125,7 +126,7 @@ static auto DiagnoseModifiers(Context& context, if (!self_param_id.has_value() && introducer.modifier_set.HasAnyOf(KeywordModifierSet::Method)) { CARBON_DIAGNOSTIC(VirtualWithoutSelf, Error, "virtual class function"); - context.emitter().Build(node_id, VirtualWithoutSelf).Emit(); + context.emitter().Emit(node_id, VirtualWithoutSelf); introducer.modifier_set.Remove(KeywordModifierSet::Method); } } @@ -329,7 +330,7 @@ static auto RequestVtableIfVirtual( if (virtual_modifier == SemIR::Function::VirtualModifier::Impl && !class_info.base_id.has_value()) { CARBON_DIAGNOSTIC(ImplWithoutBase, Error, "impl without base class"); - context.emitter().Build(node_id, ImplWithoutBase).Emit(); + context.emitter().Emit(node_id, ImplWithoutBase); } // TODO: If this is an `impl` function, check there's a matching base // function that's impl or virtual. @@ -337,6 +338,92 @@ static auto RequestVtableIfVirtual( context.vtable_stack().AddInstId(decl_id); } +// Validates the `destroy` function's signature. May replace invalid values for +// recovery. +static auto ValidateIfDestroy(Context& context, bool is_redecl, + std::optional parent_scope_inst, + SemIR::Function& function_info) -> void { + if (function_info.name_id != SemIR::NameId::Destroy) { + return; + } + + // For recovery, always force explicit parameters to be empty. We do this + // before any of the returns for simplicity. + auto orig_param_patterns_id = function_info.param_patterns_id; + function_info.param_patterns_id = SemIR::InstBlockId::Empty; + + // Use differences on merge to diagnose remaining issues. + if (is_redecl) { + return; + } + + if (!parent_scope_inst || !parent_scope_inst->Is()) { + CARBON_DIAGNOSTIC(DestroyFunctionOutsideClass, Error, + "declaring `fn destroy` in non-class scope"); + context.emitter().Emit(function_info.latest_decl_id(), + DestroyFunctionOutsideClass); + return; + } + + if (!function_info.self_param_id.has_value()) { + CARBON_DIAGNOSTIC(DestroyFunctionMissingSelf, Error, + "missing implicit `self` parameter"); + context.emitter().Emit(function_info.latest_decl_id(), + DestroyFunctionMissingSelf); + return; + } + + // `self` must be the only implicit parameter. + if (auto block = + context.inst_blocks().Get(function_info.implicit_param_patterns_id); + block.size() > 1) { + // Point at the first non-`self` parameter. + auto param_id = block[function_info.self_param_id == block[0] ? 1 : 0]; + CARBON_DIAGNOSTIC(DestroyFunctionUnexpectedImplicitParam, Error, + "unexpected implicit parameter"); + context.emitter().Emit(param_id, DestroyFunctionUnexpectedImplicitParam); + return; + } + + CARBON_CHECK( + orig_param_patterns_id.has_value(), + "TODO: Positional parameters are currently rejected as part of requiring " + "implicit parameters, because it's an invalid parse tree; add a " + "diagnostic once this is testable"); + + if (orig_param_patterns_id != SemIR::InstBlockId::Empty) { + CARBON_DIAGNOSTIC(DestroyFunctionNonEmptyExplicitParams, Error, + "unexpected parameter"); + context.emitter().Emit(context.inst_blocks().Get(orig_param_patterns_id)[0], + DestroyFunctionNonEmptyExplicitParams); + return; + } + + if (auto return_type_id = + function_info.GetDeclaredReturnType(context.sem_ir()); + return_type_id.has_value() && + return_type_id != GetTupleType(context, {})) { + CARBON_DIAGNOSTIC(DestroyFunctionIncorrectReturnType, Error, + "incorrect return type; must be unspecified or `()`"); + context.emitter().Emit(function_info.return_slot_pattern_id, + DestroyFunctionIncorrectReturnType); + return; + } +} + +// Diagnoses when positional params aren't supported. Reassigns the pattern +// block if needed. +static auto DiagnosePositionalParams(Context& context, + SemIR::Function& function_info) -> void { + if (function_info.param_patterns_id.has_value()) { + return; + } + + context.TODO(function_info.latest_decl_id(), + "function with positional parameters"); + function_info.param_patterns_id = SemIR::InstBlockId::Empty; +} + // Build a FunctionDecl describing the signature of a function. This // handles the common logic shared by function declaration syntax and function // definition syntax. @@ -352,17 +439,14 @@ static auto BuildFunctionDecl(Context& context, } auto name = PopNameComponent(context, return_slot_pattern_id); - if (!name.param_patterns_id.has_value()) { - context.TODO(node_id, "function with positional parameters"); - name.param_patterns_id = SemIR::InstBlockId::Empty; - } - auto self_param_id = - FindSelfPattern(context, name.implicit_param_patterns_id); - auto name_context = context.decl_name_stack().FinishName(name); + context.node_stack() .PopAndDiscardSoloNodeId(); + auto self_param_id = + FindSelfPattern(context, name.implicit_param_patterns_id); + // Process modifiers. auto [parent_scope_inst_id, parent_scope_inst] = context.name_scopes().GetInstIfValid(name_context.parent_scope_id); @@ -394,6 +478,14 @@ static auto BuildFunctionDecl(Context& context, function_info.definition_id = decl_id; } + // Analyze standard function signatures before positional parameters, so that + // we can have more specific diagnostics and recovery. + bool is_redecl = + name_context.state == DeclNameStack::NameContext::State::Resolved; + ValidateIfDestroy(context, is_redecl, parent_scope_inst, function_info); + + DiagnosePositionalParams(context, function_info); + TryMergeRedecl(context, node_id, name_context, function_decl, function_info, is_definition); diff --git a/toolchain/check/handle_name.cpp b/toolchain/check/handle_name.cpp index 8fbe1bee64dd..15a10a9b4c52 100644 --- a/toolchain/check/handle_name.cpp +++ b/toolchain/check/handle_name.cpp @@ -91,8 +91,12 @@ auto HandleParseNode(Context& context, Parse::PointerMemberAccessExprId node_id) return true; } -static auto GetIdentifierAsName(Context& context, Parse::NodeId node_id) - -> SemIR::NameId { +// Returns the `NameId` for an identifier node. +static auto GetIdentifierAsNameId( + Context& context, Parse::NodeIdOneOf + node_id) -> SemIR::NameId { CARBON_CHECK(!context.parse_tree().node_has_error(node_id), "TODO: Support checking error parse nodes"); auto token = context.parse_tree().node_token(node_id); @@ -129,7 +133,7 @@ static auto HandleNameAsExpr(Context& context, Parse::NodeId node_id, auto HandleParseNode(Context& context, Parse::IdentifierNameNotBeforeParamsId node_id) -> bool { // The parent is responsible for binding the name. - context.node_stack().Push(node_id, GetIdentifierAsName(context, node_id)); + context.node_stack().Push(node_id, GetIdentifierAsNameId(context, node_id)); return true; } @@ -140,26 +144,48 @@ auto HandleParseNode(Context& context, context.full_pattern_stack().PushFullPattern( FullPatternStack::Kind::ImplicitParamList); // The parent is responsible for binding the name. - context.node_stack().Push(node_id, GetIdentifierAsName(context, node_id)); + context.node_stack().Push(node_id, GetIdentifierAsNameId(context, node_id)); return true; } auto HandleParseNode(Context& context, Parse::IdentifierNameExprId node_id) -> bool { - auto name_id = GetIdentifierAsName(context, node_id); + auto name_id = GetIdentifierAsNameId(context, node_id); context.node_stack().Push(node_id, HandleNameAsExpr(context, node_id, name_id)); return true; } +// Returns the `NameId` for a keyword node. +static auto GetKeywordAsNameId( + Context& context, Parse::NodeIdOneOf + node_id) -> SemIR::NameId { + auto token = context.parse_tree().node_token(node_id); + switch (auto token_kind = context.tokens().GetKind(token)) { + case Lex::TokenKind::Destroy: + return SemIR::NameId::Destroy; + default: + CARBON_FATAL("Unexpected token kind: {0}", token_kind); + } +} + auto HandleParseNode(Context& context, Parse::KeywordNameNotBeforeParamsId node_id) -> bool { - return context.TODO(node_id, "KeywordNameNotBeforeParamsId"); + // The parent is responsible for binding the name. + context.node_stack().Push(node_id, GetKeywordAsNameId(context, node_id)); + return true; } auto HandleParseNode(Context& context, Parse::KeywordNameBeforeParamsId node_id) -> bool { - return context.TODO(node_id, "KeywordNameBeforeParamsId"); + // Push a pattern block stack entry to handle the parameter pattern. + context.pattern_block_stack().Push(); + context.full_pattern_stack().PushFullPattern( + FullPatternStack::Kind::ImplicitParamList); + // The parent is responsible for binding the name. + context.node_stack().Push(node_id, GetKeywordAsNameId(context, node_id)); + return true; } auto HandleParseNode(Context& context, Parse::BaseNameId node_id) -> bool { @@ -191,18 +217,34 @@ auto HandleParseNode(Context& context, Parse::SelfValueNameExprId node_id) return true; } -auto HandleParseNode(Context& context, - Parse::IdentifierNameQualifierWithParamsId /*node_id*/) - -> bool { +// Common logic for name qualifiers. +static auto ApplyNameQualifier(Context& context) -> bool { context.decl_name_stack().ApplyNameQualifier(PopNameComponent(context)); return true; } +auto HandleParseNode(Context& context, + Parse::IdentifierNameQualifierWithParamsId /*node_id*/) + -> bool { + return ApplyNameQualifier(context); +} + auto HandleParseNode(Context& context, Parse::IdentifierNameQualifierWithoutParamsId /*node_id*/) -> bool { - context.decl_name_stack().ApplyNameQualifier(PopNameComponent(context)); - return true; + return ApplyNameQualifier(context); +} + +auto HandleParseNode(Context& context, + Parse::KeywordNameQualifierWithParamsId /*node_id*/) + -> bool { + return ApplyNameQualifier(context); +} + +auto HandleParseNode(Context& context, + Parse::KeywordNameQualifierWithoutParamsId /*node_id*/) + -> bool { + return ApplyNameQualifier(context); } auto HandleParseNode(Context& context, Parse::DesignatorExprId node_id) @@ -241,17 +283,6 @@ auto HandleParseNode(Context& context, Parse::DesignatorExprId node_id) return true; } -auto HandleParseNode(Context& context, - Parse::KeywordNameQualifierWithParamsId node_id) -> bool { - return context.TODO(node_id, "KeywordNameQualifierWithParamsId"); -} - -auto HandleParseNode(Context& context, - Parse::KeywordNameQualifierWithoutParamsId node_id) - -> bool { - return context.TODO(node_id, "KeywordNameQualifierWithoutParamsId"); -} - auto HandleParseNode(Context& context, Parse::PackageExprId node_id) -> bool { AddInstAndPush( context, node_id, diff --git a/toolchain/check/testdata/class/no_prelude/destroy.carbon b/toolchain/check/testdata/class/no_prelude/destroy.carbon index 58ffe95a4f3e..c4839c19ce3e 100644 --- a/toolchain/check/testdata/class/no_prelude/destroy.carbon +++ b/toolchain/check/testdata/class/no_prelude/destroy.carbon @@ -8,93 +8,430 @@ // TIP: To dump output, run: // TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/no_prelude/destroy.carbon -// --- fail_todo_basic.carbon +// --- self.carbon library "[[@TEST_NAME]]"; class C { - // CHECK:STDERR: fail_todo_basic.carbon:[[@LINE+4]]:6: error: semantics TODO: `KeywordNameBeforeParamsId` [SemanticsTodo] - // CHECK:STDERR: fn destroy[self: Self](); - // CHECK:STDERR: ^~~~~~~ - // CHECK:STDERR: fn destroy[self: Self](); } -// --- fail_todo_addr.carbon +// --- addr_self.carbon library "[[@TEST_NAME]]"; class C { - // CHECK:STDERR: fail_todo_addr.carbon:[[@LINE+4]]:6: error: semantics TODO: `KeywordNameBeforeParamsId` [SemanticsTodo] - // CHECK:STDERR: fn destroy[addr self: Self*](); - // CHECK:STDERR: ^~~~~~~ - // CHECK:STDERR: fn destroy[addr self: Self*](); } -// --- fail_todo_class_function.carbon +// --- explicit_return.carbon library "[[@TEST_NAME]]"; class C { - // CHECK:STDERR: fail_todo_class_function.carbon:[[@LINE+4]]:6: error: semantics TODO: `KeywordNameBeforeParamsId` [SemanticsTodo] + fn destroy[self: Self]() -> (); +} + +// --- fail_class_function.carbon + +library "[[@TEST_NAME]]"; + +class C { + // CHECK:STDERR: fail_class_function.carbon:[[@LINE+4]]:3: error: missing implicit `self` parameter [DestroyFunctionMissingSelf] // CHECK:STDERR: fn destroy(); - // CHECK:STDERR: ^~~~~~~ + // CHECK:STDERR: ^~~~~~~~~~~~~ // CHECK:STDERR: fn destroy(); } -// --- fail_todo_destroy_in_namespace.carbon +// --- fail_extra_implicit_params_second.carbon + +library "[[@TEST_NAME]]"; + +class C { + // CHECK:STDERR: fail_extra_implicit_params_second.carbon:[[@LINE+4]]:26: error: unexpected implicit parameter [DestroyFunctionUnexpectedImplicitParam] + // CHECK:STDERR: fn destroy[self: Self, T:! type](); + // CHECK:STDERR: ^ + // CHECK:STDERR: + fn destroy[self: Self, T:! type](); +} + +// --- fail_extra_implicit_params_first.carbon + +library "[[@TEST_NAME]]"; + +class C { + // CHECK:STDERR: fail_extra_implicit_params_first.carbon:[[@LINE+4]]:14: error: unexpected implicit parameter [DestroyFunctionUnexpectedImplicitParam] + // CHECK:STDERR: fn destroy[T:! type, self: Self](); + // CHECK:STDERR: ^ + // CHECK:STDERR: + fn destroy[T:! type, self: Self](); +} + +// --- fail_positional_params.carbon + +library "[[@TEST_NAME]]"; + +class C { + // CHECK:STDERR: fail_positional_params.carbon:[[@LINE+8]]:25: error: a `(` for parameters is required after implicit parameters [ParamsRequiredAfterImplicit] + // CHECK:STDERR: fn destroy[self: Self]; + // CHECK:STDERR: ^ + // CHECK:STDERR: + // CHECK:STDERR: fail_positional_params.carbon:[[@LINE+4]]:3: error: semantics TODO: `handle invalid parse trees in `check`` [SemanticsTodo] + // CHECK:STDERR: fn destroy[self: Self]; + // CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ + // CHECK:STDERR: + fn destroy[self: Self]; +} + +// --- fail_explicit_params.carbon + +library "[[@TEST_NAME]]"; + +class C { + // CHECK:STDERR: fail_explicit_params.carbon:[[@LINE+4]]:26: error: unexpected parameter [DestroyFunctionNonEmptyExplicitParams] + // CHECK:STDERR: fn destroy[self: Self](x: ()); + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + fn destroy[self: Self](x: ()); +} + +// --- fail_return_type.carbon + +library "[[@TEST_NAME]]"; + +class C { + // CHECK:STDERR: fail_return_type.carbon:[[@LINE+4]]:28: error: incorrect return type; must be unspecified or `()` [DestroyFunctionIncorrectReturnType] + // CHECK:STDERR: fn destroy[self: Self]() -> {}; + // CHECK:STDERR: ^~~~~ + // CHECK:STDERR: + fn destroy[self: Self]() -> {}; +} + +// --- fail_out_of_line_missing_params.carbon + +library "[[@TEST_NAME]]"; + + +class C { + fn destroy[self: Self](); +} + +// CHECK:STDERR: fail_out_of_line_missing_params.carbon:[[@LINE+7]]:1: error: redeclaration differs because of missing implicit parameter list [RedeclParamListDiffers] +// CHECK:STDERR: fn C.destroy {} +// CHECK:STDERR: ^~~~~~~~~~~~~~ +// CHECK:STDERR: fail_out_of_line_missing_params.carbon:[[@LINE-6]]:3: note: previously declared with implicit parameter list [RedeclParamListPrevious] +// CHECK:STDERR: fn destroy[self: Self](); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn C.destroy {} + +// --- fail_destroy_in_file_scope.carbon + +library "[[@TEST_NAME]]"; + +// CHECK:STDERR: fail_destroy_in_file_scope.carbon:[[@LINE+4]]:1: error: declaring `fn destroy` in non-class scope [DestroyFunctionOutsideClass] +// CHECK:STDERR: fn destroy[self: ()](); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn destroy[self: ()](); + +// --- fail_destroy_in_namespace_scope.carbon library "[[@TEST_NAME]]"; namespace NS; -// CHECK:STDERR: fail_todo_destroy_in_namespace.carbon:[[@LINE+4]]:7: error: semantics TODO: `KeywordNameBeforeParamsId` [SemanticsTodo] +// CHECK:STDERR: fail_destroy_in_namespace_scope.carbon:[[@LINE+4]]:1: error: declaring `fn destroy` in non-class scope [DestroyFunctionOutsideClass] // CHECK:STDERR: fn NS.destroy(); -// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: ^~~~~~~~~~~~~~~~ // CHECK:STDERR: fn NS.destroy(); -// --- fail_todo_missing_params.carbon +// --- fail_invalid_qualifier_with_params.carbon library "[[@TEST_NAME]]"; - class C { - // CHECK:STDERR: fail_todo_missing_params.carbon:[[@LINE+4]]:6: error: semantics TODO: `KeywordNameNotBeforeParamsId` [SemanticsTodo] - // CHECK:STDERR: fn destroy; - // CHECK:STDERR: ^~~~~~~ - // CHECK:STDERR: - fn destroy; + fn destroy[self: Self](); } -fn C.destroy {} - -// --- fail_todo_wrong_scope.carbon - -library "[[@TEST_NAME]]"; - - -// CHECK:STDERR: fail_todo_wrong_scope.carbon:[[@LINE+4]]:4: error: semantics TODO: `KeywordNameBeforeParamsId` [SemanticsTodo] -// CHECK:STDERR: fn destroy(); -// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: fail_invalid_qualifier_with_params.carbon:[[@LINE+7]]:6: error: name qualifiers are only allowed for entities that provide a scope [QualifiedNameInNonScope] +// CHECK:STDERR: fn C.destroy[self: Self]().Foo() {} +// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: fail_invalid_qualifier_with_params.carbon:[[@LINE-6]]:3: note: referenced non-scope entity declared here [QualifiedNameNonScopeEntity] +// CHECK:STDERR: fn destroy[self: Self](); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ // CHECK:STDERR: -fn destroy(); +fn C.destroy[self: Self]().Foo() {} -// --- fail_invalid_qualifier.carbon +// --- fail_invalid_qualifier_no_params.carbon + +library "[[@TEST_NAME]]"; class C { - // CHECK:STDERR: fail_invalid_qualifier.carbon:[[@LINE+4]]:6: error: semantics TODO: `KeywordNameBeforeParamsId` [SemanticsTodo] - // CHECK:STDERR: fn destroy(); - // CHECK:STDERR: ^~~~~~~ - // CHECK:STDERR: - fn destroy(); + fn destroy[self: Self](); } -fn C.destroy.destroy() {} +// CHECK:STDERR: fail_invalid_qualifier_no_params.carbon:[[@LINE+7]]:6: error: name qualifiers are only allowed for entities that provide a scope [QualifiedNameInNonScope] +// CHECK:STDERR: fn C.destroy.Foo() {} +// CHECK:STDERR: ^~~~~~~ +// CHECK:STDERR: fail_invalid_qualifier_no_params.carbon:[[@LINE-6]]:3: note: referenced non-scope entity declared here [QualifiedNameNonScopeEntity] +// CHECK:STDERR: fn destroy[self: Self](); +// CHECK:STDERR: ^~~~~~~~~~~~~~~~~~~~~~~~~ +// CHECK:STDERR: +fn C.destroy.Foo() {} -// CHECK:STDOUT: --- fail_todo_basic.carbon +// CHECK:STDOUT: --- self.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] +// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] { +// CHECK:STDOUT: %self.patt: %C = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %C = value_param_pattern %self.patt, call_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %C = value_param call_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] +// CHECK:STDOUT: %self: %C = bind_name self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: .destroy = %destroy.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @destroy[%self.param_patt: %C](); +// CHECK:STDOUT: +// CHECK:STDOUT: --- addr_self.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %ptr: type = ptr_type %C [concrete] +// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] +// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] { +// CHECK:STDOUT: %self.patt: %ptr = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %ptr = value_param_pattern %self.patt, call_param0 +// CHECK:STDOUT: %.loc5_14: auto = addr_pattern %self.param_patt +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %ptr = value_param call_param0 +// CHECK:STDOUT: %.loc5_29: type = splice_block %ptr [concrete = constants.%ptr] { +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] +// CHECK:STDOUT: %ptr: type = ptr_type %C [concrete = constants.%ptr] +// CHECK:STDOUT: } +// CHECK:STDOUT: %self: %ptr = bind_name self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: .destroy = %destroy.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @destroy[addr %self.param_patt: %ptr](); +// CHECK:STDOUT: +// CHECK:STDOUT: --- explicit_return.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] +// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] { +// CHECK:STDOUT: %self.patt: %C = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %C = value_param_pattern %self.patt, call_param0 +// CHECK:STDOUT: %return.patt: %empty_tuple.type = return_slot_pattern +// CHECK:STDOUT: %return.param_patt: %empty_tuple.type = out_param_pattern %return.patt, call_param1 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.loc5_32.1: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %.loc5_32.2: type = converted %.loc5_32.1, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] +// CHECK:STDOUT: %self.param: %C = value_param call_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] +// CHECK:STDOUT: %self: %C = bind_name self, %self.param +// CHECK:STDOUT: %return.param: ref %empty_tuple.type = out_param call_param1 +// CHECK:STDOUT: %return: ref %empty_tuple.type = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: .destroy = %destroy.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @destroy[%self.param_patt: %C]() -> %empty_tuple.type; +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_class_function.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] +// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] {} {} +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: .destroy = %destroy.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @destroy(); +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_extra_implicit_params_second.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic] +// CHECK:STDOUT: %T.patt: type = symbolic_binding_pattern T, 0 [symbolic] +// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] +// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] { +// CHECK:STDOUT: %self.patt: %C = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %C = value_param_pattern %self.patt, call_param0 +// CHECK:STDOUT: %T.patt.loc9_26.1: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc9_26.2 (constants.%T.patt)] +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %C = value_param call_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] +// CHECK:STDOUT: %self: %C = bind_name self, %self.param +// CHECK:STDOUT: %T.loc9_26.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc9_26.1 (constants.%T)] +// CHECK:STDOUT: } +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: .destroy = %destroy.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @destroy(%T.loc9_26.2: type) { +// CHECK:STDOUT: %T.loc9_26.1: type = bind_symbolic_name T, 0 [symbolic = %T.loc9_26.1 (constants.%T)] +// CHECK:STDOUT: %T.patt.loc9_26.2: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc9_26.2 (constants.%T.patt)] +// CHECK:STDOUT: +// CHECK:STDOUT: fn[%self.param_patt: %C, %T.patt.loc9_26.1: type](); +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @destroy(constants.%T) { +// CHECK:STDOUT: %T.loc9_26.1 => constants.%T +// CHECK:STDOUT: %T.patt.loc9_26.2 => constants.%T +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_extra_implicit_params_first.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %T: type = bind_symbolic_name T, 0 [symbolic] +// CHECK:STDOUT: %T.patt: type = symbolic_binding_pattern T, 0 [symbolic] +// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] +// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] { +// CHECK:STDOUT: %T.patt.loc9_14.1: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc9_14.2 (constants.%T.patt)] +// CHECK:STDOUT: %self.patt: %C = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %C = value_param_pattern %self.patt, call_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %T.loc9_14.2: type = bind_symbolic_name T, 0 [symbolic = %T.loc9_14.1 (constants.%T)] +// CHECK:STDOUT: %self.param: %C = value_param call_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] +// CHECK:STDOUT: %self: %C = bind_name self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: .destroy = %destroy.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: generic fn @destroy(%T.loc9_14.2: type) { +// CHECK:STDOUT: %T.loc9_14.1: type = bind_symbolic_name T, 0 [symbolic = %T.loc9_14.1 (constants.%T)] +// CHECK:STDOUT: %T.patt.loc9_14.2: type = symbolic_binding_pattern T, 0 [symbolic = %T.patt.loc9_14.2 (constants.%T.patt)] +// CHECK:STDOUT: +// CHECK:STDOUT: fn[%T.patt.loc9_14.1: type, %self.param_patt: %C](); +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: specific @destroy(constants.%T) { +// CHECK:STDOUT: %T.loc9_14.1 => constants.%T +// CHECK:STDOUT: %T.patt.loc9_14.2 => constants.%T +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_positional_params.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] @@ -109,71 +446,272 @@ fn C.destroy.destroy() {} // CHECK:STDOUT: .Self = constants.%C // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_addr.carbon +// CHECK:STDOUT: --- fail_explicit_params.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] +// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: file {} +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} +// CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { -// CHECK:STDOUT: complete_type_witness = invalid +// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] { +// CHECK:STDOUT: %self.patt: %C = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %C = value_param_pattern %self.patt, call_param0 +// CHECK:STDOUT: %x.patt: %empty_tuple.type = binding_pattern x +// CHECK:STDOUT: %x.param_patt: %empty_tuple.type = value_param_pattern %x.patt, call_param1 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %C = value_param call_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] +// CHECK:STDOUT: %self: %C = bind_name self, %self.param +// CHECK:STDOUT: %x.param: %empty_tuple.type = value_param call_param1 +// CHECK:STDOUT: %.loc9_30.1: type = splice_block %.loc9_30.3 [concrete = constants.%empty_tuple.type] { +// CHECK:STDOUT: %.loc9_30.2: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %.loc9_30.3: type = converted %.loc9_30.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %x: %empty_tuple.type = bind_name x, %x.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: .destroy = %destroy.decl // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_class_function.carbon +// CHECK:STDOUT: fn @destroy[%self.param_patt: %C](); +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_return_type.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] +// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: file {} +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} +// CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { -// CHECK:STDOUT: complete_type_witness = invalid +// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] { +// CHECK:STDOUT: %self.patt: %C = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %C = value_param_pattern %self.patt, call_param0 +// CHECK:STDOUT: %return.patt: %empty_struct_type = return_slot_pattern +// CHECK:STDOUT: %return.param_patt: %empty_struct_type = out_param_pattern %return.patt, call_param1 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %.loc9_32.1: %empty_struct_type = struct_literal () +// CHECK:STDOUT: %.loc9_32.2: type = converted %.loc9_32.1, constants.%empty_struct_type [concrete = constants.%empty_struct_type] +// CHECK:STDOUT: %self.param: %C = value_param call_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] +// CHECK:STDOUT: %self: %C = bind_name self, %self.param +// CHECK:STDOUT: %return.param: ref %empty_struct_type = out_param call_param1 +// CHECK:STDOUT: %return: ref %empty_struct_type = return_slot %return.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: .destroy = %destroy.decl // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_destroy_in_namespace.carbon +// CHECK:STDOUT: fn @destroy[%self.param_patt: %C]() -> %empty_struct_type; // CHECK:STDOUT: -// CHECK:STDOUT: file {} -// CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_missing_params.carbon +// CHECK:STDOUT: --- fail_out_of_line_missing_params.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %destroy.type.bd30b6.1: type = fn_type @destroy.1 [concrete] +// CHECK:STDOUT: %destroy.357925.1: %destroy.type.bd30b6.1 = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: %destroy.type.bd30b6.2: type = fn_type @destroy.2 [concrete] +// CHECK:STDOUT: %destroy.357925.2: %destroy.type.bd30b6.2 = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: file {} +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} +// CHECK:STDOUT: %destroy.decl: %destroy.type.bd30b6.2 = fn_decl @destroy.2 [concrete = constants.%destroy.357925.2] {} {} +// CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { -// CHECK:STDOUT: complete_type_witness = invalid +// CHECK:STDOUT: %destroy.decl: %destroy.type.bd30b6.1 = fn_decl @destroy.1 [concrete = constants.%destroy.357925.1] { +// CHECK:STDOUT: %self.patt: %C = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %C = value_param_pattern %self.patt, call_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %C = value_param call_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] +// CHECK:STDOUT: %self: %C = bind_name self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: .destroy = %destroy.decl // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_todo_wrong_scope.carbon +// CHECK:STDOUT: fn @destroy.1[%self.param_patt: %C](); // CHECK:STDOUT: -// CHECK:STDOUT: file {} +// CHECK:STDOUT: fn @destroy.2() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: --- fail_invalid_qualifier.carbon +// CHECK:STDOUT: --- fail_destroy_in_file_scope.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %empty_tuple.type: type = tuple_type () [concrete] +// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] +// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .destroy = %destroy.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] { +// CHECK:STDOUT: %self.patt: %empty_tuple.type = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %empty_tuple.type = value_param_pattern %self.patt, call_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %empty_tuple.type = value_param call_param0 +// CHECK:STDOUT: %.loc8_19.1: type = splice_block %.loc8_19.3 [concrete = constants.%empty_tuple.type] { +// CHECK:STDOUT: %.loc8_19.2: %empty_tuple.type = tuple_literal () +// CHECK:STDOUT: %.loc8_19.3: type = converted %.loc8_19.2, constants.%empty_tuple.type [concrete = constants.%empty_tuple.type] +// CHECK:STDOUT: } +// CHECK:STDOUT: %self: %empty_tuple.type = bind_name self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @destroy[%self.param_patt: %empty_tuple.type](); +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_destroy_in_namespace_scope.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] +// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .NS = %NS +// CHECK:STDOUT: } +// CHECK:STDOUT: %NS: = namespace [concrete] { +// CHECK:STDOUT: .destroy = %destroy.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @destroy(); +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_invalid_qualifier_with_params.carbon // CHECK:STDOUT: // CHECK:STDOUT: constants { // CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] +// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] +// CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] // CHECK:STDOUT: } // CHECK:STDOUT: -// CHECK:STDOUT: file {} +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} +// CHECK:STDOUT: %Foo.decl: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] {} { +// CHECK:STDOUT: %self.param: %C = value_param call_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] +// CHECK:STDOUT: %self: %C = bind_name self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: } // CHECK:STDOUT: // CHECK:STDOUT: class @C { -// CHECK:STDOUT: complete_type_witness = invalid +// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] { +// CHECK:STDOUT: %self.patt: %C = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %C = value_param_pattern %self.patt, call_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %C = value_param call_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] +// CHECK:STDOUT: %self: %C = bind_name self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type // CHECK:STDOUT: // CHECK:STDOUT: !members: // CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: .destroy = %destroy.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @destroy[%self.param_patt: %C](); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Foo() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: --- fail_invalid_qualifier_no_params.carbon +// CHECK:STDOUT: +// CHECK:STDOUT: constants { +// CHECK:STDOUT: %C: type = class_type @C [concrete] +// CHECK:STDOUT: %destroy.type: type = fn_type @destroy [concrete] +// CHECK:STDOUT: %destroy: %destroy.type = struct_value () [concrete] +// CHECK:STDOUT: %empty_struct_type: type = struct_type {} [concrete] +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete] +// CHECK:STDOUT: %Foo.type: type = fn_type @Foo [concrete] +// CHECK:STDOUT: %Foo: %Foo.type = struct_value () [concrete] +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: file { +// CHECK:STDOUT: package: = namespace [concrete] { +// CHECK:STDOUT: .C = %C.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: %C.decl: type = class_decl @C [concrete = constants.%C] {} {} +// CHECK:STDOUT: %Foo.decl: %Foo.type = fn_decl @Foo [concrete = constants.%Foo] {} {} +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: class @C { +// CHECK:STDOUT: %destroy.decl: %destroy.type = fn_decl @destroy [concrete = constants.%destroy] { +// CHECK:STDOUT: %self.patt: %C = binding_pattern self +// CHECK:STDOUT: %self.param_patt: %C = value_param_pattern %self.patt, call_param0 +// CHECK:STDOUT: } { +// CHECK:STDOUT: %self.param: %C = value_param call_param0 +// CHECK:STDOUT: %Self.ref: type = name_ref Self, constants.%C [concrete = constants.%C] +// CHECK:STDOUT: %self: %C = bind_name self, %self.param +// CHECK:STDOUT: } +// CHECK:STDOUT: %complete_type: = complete_type_witness %empty_struct_type [concrete = constants.%complete_type] +// CHECK:STDOUT: complete_type_witness = %complete_type +// CHECK:STDOUT: +// CHECK:STDOUT: !members: +// CHECK:STDOUT: .Self = constants.%C +// CHECK:STDOUT: .destroy = %destroy.decl +// CHECK:STDOUT: } +// CHECK:STDOUT: +// CHECK:STDOUT: fn @destroy[%self.param_patt: %C](); +// CHECK:STDOUT: +// CHECK:STDOUT: fn @Foo() { +// CHECK:STDOUT: !entry: +// CHECK:STDOUT: return // CHECK:STDOUT: } // CHECK:STDOUT: diff --git a/toolchain/diagnostics/diagnostic_kind.def b/toolchain/diagnostics/diagnostic_kind.def index 34eb4ab8845e..21832a5d89cf 100644 --- a/toolchain/diagnostics/diagnostic_kind.def +++ b/toolchain/diagnostics/diagnostic_kind.def @@ -237,6 +237,12 @@ CARBON_DIAGNOSTIC_KIND(MissingReturnStatement) CARBON_DIAGNOSTIC_KIND(UnknownBuiltinFunctionName) CARBON_DIAGNOSTIC_KIND(InvalidBuiltinSignature) +CARBON_DIAGNOSTIC_KIND(DestroyFunctionOutsideClass) +CARBON_DIAGNOSTIC_KIND(DestroyFunctionMissingSelf) +CARBON_DIAGNOSTIC_KIND(DestroyFunctionUnexpectedImplicitParam) +CARBON_DIAGNOSTIC_KIND(DestroyFunctionNonEmptyExplicitParams) +CARBON_DIAGNOSTIC_KIND(DestroyFunctionIncorrectReturnType) + // Class checking. CARBON_DIAGNOSTIC_KIND(AdaptDeclRepeated) CARBON_DIAGNOSTIC_KIND(AdaptWithBase) diff --git a/toolchain/sem_ir/ids.h b/toolchain/sem_ir/ids.h index 807219dc9db8..4513560cc14f 100644 --- a/toolchain/sem_ir/ids.h +++ b/toolchain/sem_ir/ids.h @@ -424,6 +424,8 @@ struct FloatKind : public IdBase { X(ChoiceDiscriminant) \ /* The name of the package `Core`. */ \ X(Core) \ + /* The name of `destroy`. */ \ + X(Destroy) \ /* The name of `package`. */ \ X(PackageNamespace) \ /* The name of `.Self`. */ \ diff --git a/toolchain/sem_ir/name.cpp b/toolchain/sem_ir/name.cpp index 9484e7993798..39444b6c2ea4 100644 --- a/toolchain/sem_ir/name.cpp +++ b/toolchain/sem_ir/name.cpp @@ -23,6 +23,8 @@ static auto GetSpecialName(NameId name_id, bool for_ir) -> llvm::StringRef { return "discriminant"; case NameId::SpecialNameId::Core: return "Core"; + case NameId::SpecialNameId::Destroy: + return "destroy"; case NameId::SpecialNameId::PackageNamespace: return "package"; case NameId::SpecialNameId::PeriodSelf: