Jelajahi Sumber

Drop fn destroy support (#6136)

`fn destroy` is being removed per decision on #6124. It seems like the
relevant decision will result in no more keyword-based function names,
so this is removing all related support.
Jon Ross-Perkins 7 bulan lalu
induk
melakukan
705c95d6e0

+ 2 - 2
core/prelude/destroy.carbon

@@ -7,8 +7,8 @@ package Core library "prelude/destroy";
 import library "prelude/types/bool";
 import library "prelude/types/int_literal";
 
-// Object destruction, including running `fn destroy()`. Note this is distinct
-// from memory deallocation.
+// Destroys objects. Note this is distinct from memory deallocation.
+// TODO: Switch to `Destructor`+`Destroy` model.
 interface Destroy {
   fn Op[addr self: Self*]();
 }

+ 1 - 84
toolchain/check/handle_function.cpp

@@ -64,9 +64,7 @@ auto HandleParseNode(Context& context, Parse::ReturnTypeId node_id) -> bool {
   // not on the pattern stacks yet. They are only needed in that case if we have
   // a return type, which we now know that we do.
   if (context.node_stack().PeekNodeKind() ==
-          Parse::NodeKind::IdentifierNameNotBeforeParams ||
-      context.node_stack().PeekNodeKind() ==
-          Parse::NodeKind::KeywordNameNotBeforeParams) {
+      Parse::NodeKind::IdentifierNameNotBeforeParams) {
     context.pattern_block_stack().Push();
     context.full_pattern_stack().PushFullPattern(
         FullPatternStack::Kind::ExplicitParamList);
@@ -365,81 +363,6 @@ 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<SemIR::Inst> 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<SemIR::ClassDecl>()) {
-    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;
-  }
-
-  if (!orig_param_patterns_id.has_value()) {
-    CARBON_DIAGNOSTIC(DestroyFunctionPositionalParams, Error,
-                      "missing empty explicit parameter list");
-    context.emitter().Emit(function_info.latest_decl_id(),
-                           DestroyFunctionPositionalParams);
-    return;
-  }
-
-  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,
@@ -505,12 +428,6 @@ 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,

+ 0 - 44
toolchain/check/handle_name.cpp

@@ -140,38 +140,6 @@ auto HandleParseNode(Context& context, Parse::IdentifierNameExprId node_id)
   return true;
 }
 
-// Returns the `NameId` for a keyword node.
-static auto GetKeywordAsNameId(
-    Context& context, Parse::NodeIdOneOf<Parse::KeywordNameNotBeforeParamsId,
-                                         Parse::KeywordNameBeforeParamsId>
-                          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 {
-  // 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 {
-  // 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 {
   context.node_stack().Push(node_id, SemIR::NameId::Base);
   return true;
@@ -219,18 +187,6 @@ auto HandleParseNode(Context& context,
   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)
     -> bool {
   SemIR::NameId name_id = context.node_stack().PopName();

+ 0 - 2
toolchain/check/node_stack.h

@@ -496,8 +496,6 @@ class NodeStack {
       case Parse::NodeKind::ImportIntroducer:
       case Parse::NodeKind::IndexExprStart:
       case Parse::NodeKind::InvalidParseStart:
-      case Parse::NodeKind::KeywordNameQualifierWithParams:
-      case Parse::NodeKind::KeywordNameQualifierWithoutParams:
       case Parse::NodeKind::LibraryIntroducer:
       case Parse::NodeKind::LibrarySpecifier:
       case Parse::NodeKind::InlineImportSpecifier:

+ 2 - 2
toolchain/check/scope_stack.h

@@ -295,8 +295,8 @@ class ScopeStack {
   // A stack for scope context.
   llvm::SmallVector<ScopeStackEntry> scope_stack_;
 
-  // A stack of `destroy` functions to call. This only has entries inside of
-  // function bodies, where destruction on scope exit is required.
+  // A stack of instances to destroy. This only has entries inside of function
+  // bodies, where destruction on scope exit is required.
   ArrayStack<SemIR::InstId> destroy_id_stack_;
 
   // Information about non-lexical scopes. This is a subset of the entries and

+ 144 - 202
toolchain/check/testdata/class/destroy_calls.carbon

@@ -10,50 +10,36 @@
 // TIP: To dump output, run:
 // TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/destroy_calls.carbon
 
-// --- types.carbon
-
-library "[[@TEST_NAME]]";
-
-class NoAddr {
-  fn Make() -> NoAddr;
-  fn destroy[self: Self]();
-}
-
-class ExplicitReturn {
-  fn Make() -> ExplicitReturn;
-  fn destroy[self: Self]() -> ();
-}
-
-class WithAddr {
-  fn Make() -> WithAddr;
-  fn destroy[addr self: Self*]();
-}
-
 // --- implicit_return.carbon
 
 library "[[@TEST_NAME]]";
-import library "types";
+
+class A {}
+class B {}
+class C {}
 
 //@dump-sem-ir-begin
 fn F() {
-  var no_addr: NoAddr;
-  var explicit_return: ExplicitReturn;
-  var with_addr: WithAddr;
+  var a: A;
+  var b: B;
+  var c: C;
 }
 //@dump-sem-ir-end
 
 // --- nested_scope.carbon
 
 library "[[@TEST_NAME]]";
-import library "types";
+
+class A {}
+class B {}
+class C {}
 
 //@dump-sem-ir-begin
 fn F() {
-  var no_addr: NoAddr;
-  var explicit_return: ExplicitReturn;
-  var with_addr: WithAddr;
+  var a: A;
+  var b: B;
   if (true) {
-    var in_scope: NoAddr;
+    var c: C;
   }
 }
 //@dump-sem-ir-end
@@ -61,43 +47,21 @@ fn F() {
 // --- temp.carbon
 
 library "[[@TEST_NAME]]";
-import library "types";
+
+class A { fn Make() -> A; }
+class B { fn Make() -> B; }
+class C { fn Make() -> C; }
 
 //@dump-sem-ir-begin
 fn F() {
   // TODO: The scoping of these destroy calls is incorrect. Maybe we need to
   // establish statement scopes?
-  NoAddr.Make();
-  ExplicitReturn.Make();
-  WithAddr.Make();
+  A.Make();
+  B.Make();
+  C.Make();
 }
 //@dump-sem-ir-end
 
-// --- fail_recovery.carbon
-
-library "[[@TEST_NAME]]";
-
-class NoSelf {
-  // CHECK:STDERR: fail_recovery.carbon:[[@LINE+4]]:3: error: missing implicit `self` parameter [DestroyFunctionMissingSelf]
-  // CHECK:STDERR:   fn destroy();
-  // CHECK:STDERR:   ^~~~~~~~~~~~~
-  // CHECK:STDERR:
-  fn destroy();
-}
-
-class Args {
-  // CHECK:STDERR: fail_recovery.carbon:[[@LINE+4]]:26: error: unexpected parameter [DestroyFunctionNonEmptyExplicitParams]
-  // CHECK:STDERR:   fn destroy[self: Self](x: ());
-  // CHECK:STDERR:                          ^~~~~
-  // CHECK:STDERR:
-  fn destroy[self: Self](x: ());
-}
-
-fn F() {
-  var a: NoSelf;
-  var b: Args;
-}
-
 // --- generic_class.carbon
 library "[[@TEST_NAME]]";
 
@@ -127,30 +91,27 @@ fn G() { F({}); }
 // CHECK:STDOUT: --- implicit_return.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
+// CHECK:STDOUT:   %A: type = class_type @A [concrete]
+// CHECK:STDOUT:   %ptr.6db: type = ptr_type %A [concrete]
+// CHECK:STDOUT:   %A.as.Destroy.impl.Op.type: type = fn_type @A.as.Destroy.impl.Op [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %A.as.Destroy.impl.Op: %A.as.Destroy.impl.Op.type = struct_value () [concrete]
+// CHECK:STDOUT:   %B: type = class_type @B [concrete]
+// CHECK:STDOUT:   %ptr.e79: type = ptr_type %B [concrete]
+// CHECK:STDOUT:   %B.as.Destroy.impl.Op.type: type = fn_type @B.as.Destroy.impl.Op [concrete]
+// CHECK:STDOUT:   %B.as.Destroy.impl.Op: %B.as.Destroy.impl.Op.type = struct_value () [concrete]
+// CHECK:STDOUT:   %C: type = class_type @C [concrete]
+// CHECK:STDOUT:   %ptr.019: type = ptr_type %C [concrete]
+// CHECK:STDOUT:   %C.as.Destroy.impl.Op.type: type = fn_type @C.as.Destroy.impl.Op [concrete]
+// CHECK:STDOUT:   %C.as.Destroy.impl.Op: %C.as.Destroy.impl.Op.type = struct_value () [concrete]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
 // CHECK:STDOUT:   %F: %F.type = struct_value () [concrete]
-// CHECK:STDOUT:   %NoAddr: type = class_type @NoAddr [concrete]
-// CHECK:STDOUT:   %pattern_type.88f: type = pattern_type %NoAddr [concrete]
-// CHECK:STDOUT:   %ExplicitReturn: type = class_type @ExplicitReturn [concrete]
-// CHECK:STDOUT:   %pattern_type.611: type = pattern_type %ExplicitReturn [concrete]
-// CHECK:STDOUT:   %WithAddr: type = class_type @WithAddr [concrete]
-// CHECK:STDOUT:   %pattern_type.f93: type = pattern_type %WithAddr [concrete]
-// CHECK:STDOUT:   %WithAddr.as.Destroy.impl.Op.type: type = fn_type @WithAddr.as.Destroy.impl.Op [concrete]
-// CHECK:STDOUT:   %WithAddr.as.Destroy.impl.Op: %WithAddr.as.Destroy.impl.Op.type = struct_value () [concrete]
-// CHECK:STDOUT:   %ptr.b4e: type = ptr_type %WithAddr [concrete]
-// CHECK:STDOUT:   %ExplicitReturn.as.Destroy.impl.Op.type: type = fn_type @ExplicitReturn.as.Destroy.impl.Op [concrete]
-// CHECK:STDOUT:   %ExplicitReturn.as.Destroy.impl.Op: %ExplicitReturn.as.Destroy.impl.Op.type = struct_value () [concrete]
-// CHECK:STDOUT:   %ptr.b0a: type = ptr_type %ExplicitReturn [concrete]
-// CHECK:STDOUT:   %NoAddr.as.Destroy.impl.Op.type: type = fn_type @NoAddr.as.Destroy.impl.Op [concrete]
-// CHECK:STDOUT:   %NoAddr.as.Destroy.impl.Op: %NoAddr.as.Destroy.impl.Op.type = struct_value () [concrete]
-// CHECK:STDOUT:   %ptr.4b8: type = ptr_type %NoAddr [concrete]
+// CHECK:STDOUT:   %pattern_type.c10: type = pattern_type %A [concrete]
+// CHECK:STDOUT:   %pattern_type.049: type = pattern_type %B [concrete]
+// CHECK:STDOUT:   %pattern_type.c48: type = pattern_type %C [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %Main.NoAddr: type = import_ref Main//types, NoAddr, loaded [concrete = constants.%NoAddr]
-// CHECK:STDOUT:   %Main.ExplicitReturn: type = import_ref Main//types, ExplicitReturn, loaded [concrete = constants.%ExplicitReturn]
-// CHECK:STDOUT:   %Main.WithAddr: type = import_ref Main//types, WithAddr, loaded [concrete = constants.%WithAddr]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -160,66 +121,63 @@ fn G() { F({}); }
 // CHECK:STDOUT: fn @F() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   name_binding_decl {
-// CHECK:STDOUT:     %no_addr.patt: %pattern_type.88f = binding_pattern no_addr [concrete]
-// CHECK:STDOUT:     %no_addr.var_patt: %pattern_type.88f = var_pattern %no_addr.patt [concrete]
+// CHECK:STDOUT:     %a.patt: %pattern_type.c10 = binding_pattern a [concrete]
+// CHECK:STDOUT:     %a.var_patt: %pattern_type.c10 = var_pattern %a.patt [concrete]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %no_addr.var: ref %NoAddr = var %no_addr.var_patt
-// CHECK:STDOUT:   %NoAddr.ref: type = name_ref NoAddr, imports.%Main.NoAddr [concrete = constants.%NoAddr]
-// CHECK:STDOUT:   %no_addr: ref %NoAddr = bind_name no_addr, %no_addr.var
+// CHECK:STDOUT:   %a.var: ref %A = var %a.var_patt
+// CHECK:STDOUT:   %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A]
+// CHECK:STDOUT:   %a: ref %A = bind_name a, %a.var
 // CHECK:STDOUT:   name_binding_decl {
-// CHECK:STDOUT:     %explicit_return.patt: %pattern_type.611 = binding_pattern explicit_return [concrete]
-// CHECK:STDOUT:     %explicit_return.var_patt: %pattern_type.611 = var_pattern %explicit_return.patt [concrete]
+// CHECK:STDOUT:     %b.patt: %pattern_type.049 = binding_pattern b [concrete]
+// CHECK:STDOUT:     %b.var_patt: %pattern_type.049 = var_pattern %b.patt [concrete]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %explicit_return.var: ref %ExplicitReturn = var %explicit_return.var_patt
-// CHECK:STDOUT:   %ExplicitReturn.ref: type = name_ref ExplicitReturn, imports.%Main.ExplicitReturn [concrete = constants.%ExplicitReturn]
-// CHECK:STDOUT:   %explicit_return: ref %ExplicitReturn = bind_name explicit_return, %explicit_return.var
+// CHECK:STDOUT:   %b.var: ref %B = var %b.var_patt
+// CHECK:STDOUT:   %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B]
+// CHECK:STDOUT:   %b: ref %B = bind_name b, %b.var
 // CHECK:STDOUT:   name_binding_decl {
-// CHECK:STDOUT:     %with_addr.patt: %pattern_type.f93 = binding_pattern with_addr [concrete]
-// CHECK:STDOUT:     %with_addr.var_patt: %pattern_type.f93 = var_pattern %with_addr.patt [concrete]
+// CHECK:STDOUT:     %c.patt: %pattern_type.c48 = binding_pattern c [concrete]
+// CHECK:STDOUT:     %c.var_patt: %pattern_type.c48 = var_pattern %c.patt [concrete]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %with_addr.var: ref %WithAddr = var %with_addr.var_patt
-// CHECK:STDOUT:   %WithAddr.ref: type = name_ref WithAddr, imports.%Main.WithAddr [concrete = constants.%WithAddr]
-// CHECK:STDOUT:   %with_addr: ref %WithAddr = bind_name with_addr, %with_addr.var
-// CHECK:STDOUT:   %WithAddr.as.Destroy.impl.Op.bound: <bound method> = bound_method %with_addr.var, constants.%WithAddr.as.Destroy.impl.Op
-// CHECK:STDOUT:   %addr.loc9: %ptr.b4e = addr_of %with_addr.var
-// CHECK:STDOUT:   %WithAddr.as.Destroy.impl.Op.call: init %empty_tuple.type = call %WithAddr.as.Destroy.impl.Op.bound(%addr.loc9)
-// CHECK:STDOUT:   %ExplicitReturn.as.Destroy.impl.Op.bound: <bound method> = bound_method %explicit_return.var, constants.%ExplicitReturn.as.Destroy.impl.Op
-// CHECK:STDOUT:   %addr.loc8: %ptr.b0a = addr_of %explicit_return.var
-// CHECK:STDOUT:   %ExplicitReturn.as.Destroy.impl.Op.call: init %empty_tuple.type = call %ExplicitReturn.as.Destroy.impl.Op.bound(%addr.loc8)
-// CHECK:STDOUT:   %NoAddr.as.Destroy.impl.Op.bound: <bound method> = bound_method %no_addr.var, constants.%NoAddr.as.Destroy.impl.Op
-// CHECK:STDOUT:   %addr.loc7: %ptr.4b8 = addr_of %no_addr.var
-// CHECK:STDOUT:   %NoAddr.as.Destroy.impl.Op.call: init %empty_tuple.type = call %NoAddr.as.Destroy.impl.Op.bound(%addr.loc7)
+// CHECK:STDOUT:   %c.var: ref %C = var %c.var_patt
+// CHECK:STDOUT:   %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C]
+// CHECK:STDOUT:   %c: ref %C = bind_name c, %c.var
+// CHECK:STDOUT:   %C.as.Destroy.impl.Op.bound: <bound method> = bound_method %c.var, constants.%C.as.Destroy.impl.Op
+// CHECK:STDOUT:   %addr.loc12: %ptr.019 = addr_of %c.var
+// CHECK:STDOUT:   %C.as.Destroy.impl.Op.call: init %empty_tuple.type = call %C.as.Destroy.impl.Op.bound(%addr.loc12)
+// CHECK:STDOUT:   %B.as.Destroy.impl.Op.bound: <bound method> = bound_method %b.var, constants.%B.as.Destroy.impl.Op
+// CHECK:STDOUT:   %addr.loc11: %ptr.e79 = addr_of %b.var
+// CHECK:STDOUT:   %B.as.Destroy.impl.Op.call: init %empty_tuple.type = call %B.as.Destroy.impl.Op.bound(%addr.loc11)
+// CHECK:STDOUT:   %A.as.Destroy.impl.Op.bound: <bound method> = bound_method %a.var, constants.%A.as.Destroy.impl.Op
+// CHECK:STDOUT:   %addr.loc10: %ptr.6db = addr_of %a.var
+// CHECK:STDOUT:   %A.as.Destroy.impl.Op.call: init %empty_tuple.type = call %A.as.Destroy.impl.Op.bound(%addr.loc10)
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- nested_scope.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
+// CHECK:STDOUT:   %A: type = class_type @A [concrete]
+// CHECK:STDOUT:   %ptr.6db: type = ptr_type %A [concrete]
+// CHECK:STDOUT:   %A.as.Destroy.impl.Op.type: type = fn_type @A.as.Destroy.impl.Op [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %A.as.Destroy.impl.Op: %A.as.Destroy.impl.Op.type = struct_value () [concrete]
+// CHECK:STDOUT:   %B: type = class_type @B [concrete]
+// CHECK:STDOUT:   %ptr.e79: type = ptr_type %B [concrete]
+// CHECK:STDOUT:   %B.as.Destroy.impl.Op.type: type = fn_type @B.as.Destroy.impl.Op [concrete]
+// CHECK:STDOUT:   %B.as.Destroy.impl.Op: %B.as.Destroy.impl.Op.type = struct_value () [concrete]
+// CHECK:STDOUT:   %C: type = class_type @C [concrete]
+// CHECK:STDOUT:   %ptr.019: type = ptr_type %C [concrete]
+// CHECK:STDOUT:   %C.as.Destroy.impl.Op.type: type = fn_type @C.as.Destroy.impl.Op [concrete]
+// CHECK:STDOUT:   %C.as.Destroy.impl.Op: %C.as.Destroy.impl.Op.type = struct_value () [concrete]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
 // CHECK:STDOUT:   %F: %F.type = struct_value () [concrete]
-// CHECK:STDOUT:   %NoAddr: type = class_type @NoAddr [concrete]
-// CHECK:STDOUT:   %pattern_type.88f: type = pattern_type %NoAddr [concrete]
-// CHECK:STDOUT:   %ExplicitReturn: type = class_type @ExplicitReturn [concrete]
-// CHECK:STDOUT:   %pattern_type.611: type = pattern_type %ExplicitReturn [concrete]
-// CHECK:STDOUT:   %WithAddr: type = class_type @WithAddr [concrete]
-// CHECK:STDOUT:   %pattern_type.f93: type = pattern_type %WithAddr [concrete]
+// CHECK:STDOUT:   %pattern_type.c10: type = pattern_type %A [concrete]
+// CHECK:STDOUT:   %pattern_type.049: type = pattern_type %B [concrete]
 // CHECK:STDOUT:   %true: bool = bool_literal true [concrete]
-// CHECK:STDOUT:   %NoAddr.as.Destroy.impl.Op.type: type = fn_type @NoAddr.as.Destroy.impl.Op [concrete]
-// CHECK:STDOUT:   %NoAddr.as.Destroy.impl.Op: %NoAddr.as.Destroy.impl.Op.type = struct_value () [concrete]
-// CHECK:STDOUT:   %ptr.4b8: type = ptr_type %NoAddr [concrete]
-// CHECK:STDOUT:   %WithAddr.as.Destroy.impl.Op.type: type = fn_type @WithAddr.as.Destroy.impl.Op [concrete]
-// CHECK:STDOUT:   %WithAddr.as.Destroy.impl.Op: %WithAddr.as.Destroy.impl.Op.type = struct_value () [concrete]
-// CHECK:STDOUT:   %ptr.b4e: type = ptr_type %WithAddr [concrete]
-// CHECK:STDOUT:   %ExplicitReturn.as.Destroy.impl.Op.type: type = fn_type @ExplicitReturn.as.Destroy.impl.Op [concrete]
-// CHECK:STDOUT:   %ExplicitReturn.as.Destroy.impl.Op: %ExplicitReturn.as.Destroy.impl.Op.type = struct_value () [concrete]
-// CHECK:STDOUT:   %ptr.b0a: type = ptr_type %ExplicitReturn [concrete]
+// CHECK:STDOUT:   %pattern_type.c48: type = pattern_type %C [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %Main.NoAddr: type = import_ref Main//types, NoAddr, loaded [concrete = constants.%NoAddr]
-// CHECK:STDOUT:   %Main.ExplicitReturn: type = import_ref Main//types, ExplicitReturn, loaded [concrete = constants.%ExplicitReturn]
-// CHECK:STDOUT:   %Main.WithAddr: type = import_ref Main//types, WithAddr, loaded [concrete = constants.%WithAddr]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -229,88 +187,72 @@ fn G() { F({}); }
 // CHECK:STDOUT: fn @F() {
 // CHECK:STDOUT: !entry:
 // CHECK:STDOUT:   name_binding_decl {
-// CHECK:STDOUT:     %no_addr.patt: %pattern_type.88f = binding_pattern no_addr [concrete]
-// CHECK:STDOUT:     %no_addr.var_patt: %pattern_type.88f = var_pattern %no_addr.patt [concrete]
-// CHECK:STDOUT:   }
-// CHECK:STDOUT:   %no_addr.var: ref %NoAddr = var %no_addr.var_patt
-// CHECK:STDOUT:   %NoAddr.ref.loc7: type = name_ref NoAddr, imports.%Main.NoAddr [concrete = constants.%NoAddr]
-// CHECK:STDOUT:   %no_addr: ref %NoAddr = bind_name no_addr, %no_addr.var
-// CHECK:STDOUT:   name_binding_decl {
-// CHECK:STDOUT:     %explicit_return.patt: %pattern_type.611 = binding_pattern explicit_return [concrete]
-// CHECK:STDOUT:     %explicit_return.var_patt: %pattern_type.611 = var_pattern %explicit_return.patt [concrete]
+// CHECK:STDOUT:     %a.patt: %pattern_type.c10 = binding_pattern a [concrete]
+// CHECK:STDOUT:     %a.var_patt: %pattern_type.c10 = var_pattern %a.patt [concrete]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %explicit_return.var: ref %ExplicitReturn = var %explicit_return.var_patt
-// CHECK:STDOUT:   %ExplicitReturn.ref: type = name_ref ExplicitReturn, imports.%Main.ExplicitReturn [concrete = constants.%ExplicitReturn]
-// CHECK:STDOUT:   %explicit_return: ref %ExplicitReturn = bind_name explicit_return, %explicit_return.var
+// CHECK:STDOUT:   %a.var: ref %A = var %a.var_patt
+// CHECK:STDOUT:   %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A]
+// CHECK:STDOUT:   %a: ref %A = bind_name a, %a.var
 // CHECK:STDOUT:   name_binding_decl {
-// CHECK:STDOUT:     %with_addr.patt: %pattern_type.f93 = binding_pattern with_addr [concrete]
-// CHECK:STDOUT:     %with_addr.var_patt: %pattern_type.f93 = var_pattern %with_addr.patt [concrete]
+// CHECK:STDOUT:     %b.patt: %pattern_type.049 = binding_pattern b [concrete]
+// CHECK:STDOUT:     %b.var_patt: %pattern_type.049 = var_pattern %b.patt [concrete]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %with_addr.var: ref %WithAddr = var %with_addr.var_patt
-// CHECK:STDOUT:   %WithAddr.ref: type = name_ref WithAddr, imports.%Main.WithAddr [concrete = constants.%WithAddr]
-// CHECK:STDOUT:   %with_addr: ref %WithAddr = bind_name with_addr, %with_addr.var
+// CHECK:STDOUT:   %b.var: ref %B = var %b.var_patt
+// CHECK:STDOUT:   %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B]
+// CHECK:STDOUT:   %b: ref %B = bind_name b, %b.var
 // CHECK:STDOUT:   %true: bool = bool_literal true [concrete = constants.%true]
 // CHECK:STDOUT:   if %true br !if.then else br !if.else
 // CHECK:STDOUT:
 // CHECK:STDOUT: !if.then:
 // CHECK:STDOUT:   name_binding_decl {
-// CHECK:STDOUT:     %in_scope.patt: %pattern_type.88f = binding_pattern in_scope [concrete]
-// CHECK:STDOUT:     %in_scope.var_patt: %pattern_type.88f = var_pattern %in_scope.patt [concrete]
+// CHECK:STDOUT:     %c.patt: %pattern_type.c48 = binding_pattern c [concrete]
+// CHECK:STDOUT:     %c.var_patt: %pattern_type.c48 = var_pattern %c.patt [concrete]
 // CHECK:STDOUT:   }
-// CHECK:STDOUT:   %in_scope.var: ref %NoAddr = var %in_scope.var_patt
-// CHECK:STDOUT:   %NoAddr.ref.loc11: type = name_ref NoAddr, imports.%Main.NoAddr [concrete = constants.%NoAddr]
-// CHECK:STDOUT:   %in_scope: ref %NoAddr = bind_name in_scope, %in_scope.var
+// CHECK:STDOUT:   %c.var: ref %C = var %c.var_patt
+// CHECK:STDOUT:   %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C]
+// CHECK:STDOUT:   %c: ref %C = bind_name c, %c.var
 // CHECK:STDOUT:   br !if.else
 // CHECK:STDOUT:
 // CHECK:STDOUT: !if.else:
-// CHECK:STDOUT:   %NoAddr.as.Destroy.impl.Op.bound.loc11: <bound method> = bound_method %in_scope.var, constants.%NoAddr.as.Destroy.impl.Op
-// CHECK:STDOUT:   %addr.loc11: %ptr.4b8 = addr_of %in_scope.var
-// CHECK:STDOUT:   %NoAddr.as.Destroy.impl.Op.call.loc11: init %empty_tuple.type = call %NoAddr.as.Destroy.impl.Op.bound.loc11(%addr.loc11)
-// CHECK:STDOUT:   %WithAddr.as.Destroy.impl.Op.bound: <bound method> = bound_method %with_addr.var, constants.%WithAddr.as.Destroy.impl.Op
-// CHECK:STDOUT:   %addr.loc9: %ptr.b4e = addr_of %with_addr.var
-// CHECK:STDOUT:   %WithAddr.as.Destroy.impl.Op.call: init %empty_tuple.type = call %WithAddr.as.Destroy.impl.Op.bound(%addr.loc9)
-// CHECK:STDOUT:   %ExplicitReturn.as.Destroy.impl.Op.bound: <bound method> = bound_method %explicit_return.var, constants.%ExplicitReturn.as.Destroy.impl.Op
-// CHECK:STDOUT:   %addr.loc8: %ptr.b0a = addr_of %explicit_return.var
-// CHECK:STDOUT:   %ExplicitReturn.as.Destroy.impl.Op.call: init %empty_tuple.type = call %ExplicitReturn.as.Destroy.impl.Op.bound(%addr.loc8)
-// CHECK:STDOUT:   %NoAddr.as.Destroy.impl.Op.bound.loc7: <bound method> = bound_method %no_addr.var, constants.%NoAddr.as.Destroy.impl.Op
-// CHECK:STDOUT:   %addr.loc7: %ptr.4b8 = addr_of %no_addr.var
-// CHECK:STDOUT:   %NoAddr.as.Destroy.impl.Op.call.loc7: init %empty_tuple.type = call %NoAddr.as.Destroy.impl.Op.bound.loc7(%addr.loc7)
+// CHECK:STDOUT:   %C.as.Destroy.impl.Op.bound: <bound method> = bound_method %c.var, constants.%C.as.Destroy.impl.Op
+// CHECK:STDOUT:   %addr.loc13: %ptr.019 = addr_of %c.var
+// CHECK:STDOUT:   %C.as.Destroy.impl.Op.call: init %empty_tuple.type = call %C.as.Destroy.impl.Op.bound(%addr.loc13)
+// CHECK:STDOUT:   %B.as.Destroy.impl.Op.bound: <bound method> = bound_method %b.var, constants.%B.as.Destroy.impl.Op
+// CHECK:STDOUT:   %addr.loc11: %ptr.e79 = addr_of %b.var
+// CHECK:STDOUT:   %B.as.Destroy.impl.Op.call: init %empty_tuple.type = call %B.as.Destroy.impl.Op.bound(%addr.loc11)
+// CHECK:STDOUT:   %A.as.Destroy.impl.Op.bound: <bound method> = bound_method %a.var, constants.%A.as.Destroy.impl.Op
+// CHECK:STDOUT:   %addr.loc10: %ptr.6db = addr_of %a.var
+// CHECK:STDOUT:   %A.as.Destroy.impl.Op.call: init %empty_tuple.type = call %A.as.Destroy.impl.Op.bound(%addr.loc10)
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: --- temp.carbon
 // CHECK:STDOUT:
 // CHECK:STDOUT: constants {
-// CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
+// CHECK:STDOUT:   %A: type = class_type @A [concrete]
+// CHECK:STDOUT:   %A.Make.type: type = fn_type @A.Make [concrete]
 // CHECK:STDOUT:   %empty_tuple.type: type = tuple_type () [concrete]
+// CHECK:STDOUT:   %A.Make: %A.Make.type = struct_value () [concrete]
+// CHECK:STDOUT:   %ptr.6db: type = ptr_type %A [concrete]
+// CHECK:STDOUT:   %A.as.Destroy.impl.Op.type: type = fn_type @A.as.Destroy.impl.Op [concrete]
+// CHECK:STDOUT:   %A.as.Destroy.impl.Op: %A.as.Destroy.impl.Op.type = struct_value () [concrete]
+// CHECK:STDOUT:   %B: type = class_type @B [concrete]
+// CHECK:STDOUT:   %B.Make.type: type = fn_type @B.Make [concrete]
+// CHECK:STDOUT:   %B.Make: %B.Make.type = struct_value () [concrete]
+// CHECK:STDOUT:   %ptr.e79: type = ptr_type %B [concrete]
+// CHECK:STDOUT:   %B.as.Destroy.impl.Op.type: type = fn_type @B.as.Destroy.impl.Op [concrete]
+// CHECK:STDOUT:   %B.as.Destroy.impl.Op: %B.as.Destroy.impl.Op.type = struct_value () [concrete]
+// CHECK:STDOUT:   %C: type = class_type @C [concrete]
+// CHECK:STDOUT:   %C.Make.type: type = fn_type @C.Make [concrete]
+// CHECK:STDOUT:   %C.Make: %C.Make.type = struct_value () [concrete]
+// CHECK:STDOUT:   %ptr.019: type = ptr_type %C [concrete]
+// CHECK:STDOUT:   %C.as.Destroy.impl.Op.type: type = fn_type @C.as.Destroy.impl.Op [concrete]
+// CHECK:STDOUT:   %C.as.Destroy.impl.Op: %C.as.Destroy.impl.Op.type = struct_value () [concrete]
+// CHECK:STDOUT:   %F.type: type = fn_type @F [concrete]
 // CHECK:STDOUT:   %F: %F.type = struct_value () [concrete]
-// CHECK:STDOUT:   %NoAddr: type = class_type @NoAddr [concrete]
-// CHECK:STDOUT:   %NoAddr.Make.type: type = fn_type @NoAddr.Make [concrete]
-// CHECK:STDOUT:   %NoAddr.Make: %NoAddr.Make.type = struct_value () [concrete]
-// CHECK:STDOUT:   %ExplicitReturn: type = class_type @ExplicitReturn [concrete]
-// CHECK:STDOUT:   %ExplicitReturn.Make.type: type = fn_type @ExplicitReturn.Make [concrete]
-// CHECK:STDOUT:   %ExplicitReturn.Make: %ExplicitReturn.Make.type = struct_value () [concrete]
-// CHECK:STDOUT:   %WithAddr: type = class_type @WithAddr [concrete]
-// CHECK:STDOUT:   %WithAddr.Make.type: type = fn_type @WithAddr.Make [concrete]
-// CHECK:STDOUT:   %WithAddr.Make: %WithAddr.Make.type = struct_value () [concrete]
-// CHECK:STDOUT:   %WithAddr.as.Destroy.impl.Op.type: type = fn_type @WithAddr.as.Destroy.impl.Op [concrete]
-// CHECK:STDOUT:   %WithAddr.as.Destroy.impl.Op: %WithAddr.as.Destroy.impl.Op.type = struct_value () [concrete]
-// CHECK:STDOUT:   %ptr.b4e: type = ptr_type %WithAddr [concrete]
-// CHECK:STDOUT:   %ExplicitReturn.as.Destroy.impl.Op.type: type = fn_type @ExplicitReturn.as.Destroy.impl.Op [concrete]
-// CHECK:STDOUT:   %ExplicitReturn.as.Destroy.impl.Op: %ExplicitReturn.as.Destroy.impl.Op.type = struct_value () [concrete]
-// CHECK:STDOUT:   %ptr.b0a: type = ptr_type %ExplicitReturn [concrete]
-// CHECK:STDOUT:   %NoAddr.as.Destroy.impl.Op.type: type = fn_type @NoAddr.as.Destroy.impl.Op [concrete]
-// CHECK:STDOUT:   %NoAddr.as.Destroy.impl.Op: %NoAddr.as.Destroy.impl.Op.type = struct_value () [concrete]
-// CHECK:STDOUT:   %ptr.4b8: type = ptr_type %NoAddr [concrete]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: imports {
-// CHECK:STDOUT:   %Main.NoAddr: type = import_ref Main//types, NoAddr, loaded [concrete = constants.%NoAddr]
-// CHECK:STDOUT:   %Main.ExplicitReturn: type = import_ref Main//types, ExplicitReturn, loaded [concrete = constants.%ExplicitReturn]
-// CHECK:STDOUT:   %Main.WithAddr: type = import_ref Main//types, WithAddr, loaded [concrete = constants.%WithAddr]
-// CHECK:STDOUT:   %Main.import_ref.784: %NoAddr.Make.type = import_ref Main//types, loc5_22, loaded [concrete = constants.%NoAddr.Make]
-// CHECK:STDOUT:   %Main.import_ref.8e0: %ExplicitReturn.Make.type = import_ref Main//types, loc10_30, loaded [concrete = constants.%ExplicitReturn.Make]
-// CHECK:STDOUT:   %Main.import_ref.974: %WithAddr.Make.type = import_ref Main//types, loc15_24, loaded [concrete = constants.%WithAddr.Make]
 // CHECK:STDOUT: }
 // CHECK:STDOUT:
 // CHECK:STDOUT: file {
@@ -319,30 +261,30 @@ fn G() { F({}); }
 // CHECK:STDOUT:
 // CHECK:STDOUT: fn @F() {
 // CHECK:STDOUT: !entry:
-// CHECK:STDOUT:   %NoAddr.ref: type = name_ref NoAddr, imports.%Main.NoAddr [concrete = constants.%NoAddr]
-// CHECK:STDOUT:   %Make.ref.loc9: %NoAddr.Make.type = name_ref Make, imports.%Main.import_ref.784 [concrete = constants.%NoAddr.Make]
-// CHECK:STDOUT:   %.loc9_15.1: ref %NoAddr = temporary_storage
-// CHECK:STDOUT:   %NoAddr.Make.call: init %NoAddr = call %Make.ref.loc9() to %.loc9_15.1
-// CHECK:STDOUT:   %.loc9_15.2: ref %NoAddr = temporary %.loc9_15.1, %NoAddr.Make.call
-// CHECK:STDOUT:   %ExplicitReturn.ref: type = name_ref ExplicitReturn, imports.%Main.ExplicitReturn [concrete = constants.%ExplicitReturn]
-// CHECK:STDOUT:   %Make.ref.loc10: %ExplicitReturn.Make.type = name_ref Make, imports.%Main.import_ref.8e0 [concrete = constants.%ExplicitReturn.Make]
-// CHECK:STDOUT:   %.loc10_23.1: ref %ExplicitReturn = temporary_storage
-// CHECK:STDOUT:   %ExplicitReturn.Make.call: init %ExplicitReturn = call %Make.ref.loc10() to %.loc10_23.1
-// CHECK:STDOUT:   %.loc10_23.2: ref %ExplicitReturn = temporary %.loc10_23.1, %ExplicitReturn.Make.call
-// CHECK:STDOUT:   %WithAddr.ref: type = name_ref WithAddr, imports.%Main.WithAddr [concrete = constants.%WithAddr]
-// CHECK:STDOUT:   %Make.ref.loc11: %WithAddr.Make.type = name_ref Make, imports.%Main.import_ref.974 [concrete = constants.%WithAddr.Make]
-// CHECK:STDOUT:   %.loc11_17.1: ref %WithAddr = temporary_storage
-// CHECK:STDOUT:   %WithAddr.Make.call: init %WithAddr = call %Make.ref.loc11() to %.loc11_17.1
-// CHECK:STDOUT:   %.loc11_17.2: ref %WithAddr = temporary %.loc11_17.1, %WithAddr.Make.call
-// CHECK:STDOUT:   %WithAddr.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc11_17.2, constants.%WithAddr.as.Destroy.impl.Op
-// CHECK:STDOUT:   %addr.loc11: %ptr.b4e = addr_of %.loc11_17.2
-// CHECK:STDOUT:   %WithAddr.as.Destroy.impl.Op.call: init %empty_tuple.type = call %WithAddr.as.Destroy.impl.Op.bound(%addr.loc11)
-// CHECK:STDOUT:   %ExplicitReturn.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc10_23.2, constants.%ExplicitReturn.as.Destroy.impl.Op
-// CHECK:STDOUT:   %addr.loc10: %ptr.b0a = addr_of %.loc10_23.2
-// CHECK:STDOUT:   %ExplicitReturn.as.Destroy.impl.Op.call: init %empty_tuple.type = call %ExplicitReturn.as.Destroy.impl.Op.bound(%addr.loc10)
-// CHECK:STDOUT:   %NoAddr.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc9_15.2, constants.%NoAddr.as.Destroy.impl.Op
-// CHECK:STDOUT:   %addr.loc9: %ptr.4b8 = addr_of %.loc9_15.2
-// CHECK:STDOUT:   %NoAddr.as.Destroy.impl.Op.call: init %empty_tuple.type = call %NoAddr.as.Destroy.impl.Op.bound(%addr.loc9)
+// CHECK:STDOUT:   %A.ref: type = name_ref A, file.%A.decl [concrete = constants.%A]
+// CHECK:STDOUT:   %Make.ref.loc12: %A.Make.type = name_ref Make, @A.%A.Make.decl [concrete = constants.%A.Make]
+// CHECK:STDOUT:   %.loc12_10.1: ref %A = temporary_storage
+// CHECK:STDOUT:   %A.Make.call: init %A = call %Make.ref.loc12() to %.loc12_10.1
+// CHECK:STDOUT:   %.loc12_10.2: ref %A = temporary %.loc12_10.1, %A.Make.call
+// CHECK:STDOUT:   %B.ref: type = name_ref B, file.%B.decl [concrete = constants.%B]
+// CHECK:STDOUT:   %Make.ref.loc13: %B.Make.type = name_ref Make, @B.%B.Make.decl [concrete = constants.%B.Make]
+// CHECK:STDOUT:   %.loc13_10.1: ref %B = temporary_storage
+// CHECK:STDOUT:   %B.Make.call: init %B = call %Make.ref.loc13() to %.loc13_10.1
+// CHECK:STDOUT:   %.loc13_10.2: ref %B = temporary %.loc13_10.1, %B.Make.call
+// CHECK:STDOUT:   %C.ref: type = name_ref C, file.%C.decl [concrete = constants.%C]
+// CHECK:STDOUT:   %Make.ref.loc14: %C.Make.type = name_ref Make, @C.%C.Make.decl [concrete = constants.%C.Make]
+// CHECK:STDOUT:   %.loc14_10.1: ref %C = temporary_storage
+// CHECK:STDOUT:   %C.Make.call: init %C = call %Make.ref.loc14() to %.loc14_10.1
+// CHECK:STDOUT:   %.loc14_10.2: ref %C = temporary %.loc14_10.1, %C.Make.call
+// CHECK:STDOUT:   %C.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc14_10.2, constants.%C.as.Destroy.impl.Op
+// CHECK:STDOUT:   %addr.loc14: %ptr.019 = addr_of %.loc14_10.2
+// CHECK:STDOUT:   %C.as.Destroy.impl.Op.call: init %empty_tuple.type = call %C.as.Destroy.impl.Op.bound(%addr.loc14)
+// CHECK:STDOUT:   %B.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc13_10.2, constants.%B.as.Destroy.impl.Op
+// CHECK:STDOUT:   %addr.loc13: %ptr.e79 = addr_of %.loc13_10.2
+// CHECK:STDOUT:   %B.as.Destroy.impl.Op.call: init %empty_tuple.type = call %B.as.Destroy.impl.Op.bound(%addr.loc13)
+// CHECK:STDOUT:   %A.as.Destroy.impl.Op.bound: <bound method> = bound_method %.loc12_10.2, constants.%A.as.Destroy.impl.Op
+// CHECK:STDOUT:   %addr.loc12: %ptr.6db = addr_of %.loc12_10.2
+// CHECK:STDOUT:   %A.as.Destroy.impl.Op.call: init %empty_tuple.type = call %A.as.Destroy.impl.Op.bound(%addr.loc12)
 // CHECK:STDOUT:   return
 // CHECK:STDOUT: }
 // CHECK:STDOUT:

+ 0 - 219
toolchain/check/testdata/class/destroy_decl.carbon

@@ -1,219 +0,0 @@
-// 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/destroy.carbon
-//
-// AUTOUPDATE
-// TIP: To test this file alone, run:
-// TIP:   bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/check/testdata/class/destroy_decl.carbon
-// TIP: To dump output, run:
-// TIP:   bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/check/testdata/class/destroy_decl.carbon
-
-// --- self.carbon
-
-library "[[@TEST_NAME]]";
-
-class C {
-  fn destroy[self: Self]();
-}
-
-// --- declare_impl.carbon
-
-library "[[@TEST_NAME]]";
-
-class C {
-  impl as Core.Destroy;
-}
-
-// --- fail_define_impl.carbon
-
-library "[[@TEST_NAME]]";
-
-// CHECK:STDERR: fail_define_impl.carbon:[[@LINE+3]]:1: error: redefinition of `impl Self as Core.Destroy` [ImplRedefinition]
-// CHECK:STDERR: class C {
-// CHECK:STDERR: ^~~~~~~~~
-class C {
-  // CHECK:STDERR: fail_define_impl.carbon:[[@LINE+4]]:3: note: previous definition was here [ImplPreviousDefinition]
-  // CHECK:STDERR:   impl as Core.Destroy {
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~~~~~~
-  // CHECK:STDERR:
-  impl as Core.Destroy {
-    fn Op[addr self: Self*]() {}
-  }
-}
-
-// --- addr_self.carbon
-
-library "[[@TEST_NAME]]";
-
-class C {
-  fn destroy[addr self: Self*]();
-}
-
-// --- explicit_return.carbon
-
-library "[[@TEST_NAME]]";
-
-class C {
-  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:
-  fn destroy();
-}
-
-// --- 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+4]]:3: error: missing empty explicit parameter list [DestroyFunctionPositionalParams]
-  // 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_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:
-fn NS.destroy();
-
-// --- fail_invalid_qualifier_with_params.carbon
-
-library "[[@TEST_NAME]]";
-
-class C {
-  fn destroy[self: Self]();
-}
-
-// 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 C.destroy[self: Self]().Foo() {}
-
-// --- fail_invalid_qualifier_no_params.carbon
-
-library "[[@TEST_NAME]]";
-
-class C {
-  fn destroy[self: Self]();
-}
-
-// 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() {}
-
-// --- fail_return_type_after_no_params.carbon
-
-library "[[@TEST_NAME]]";
-
-// Return type handling special-cases the "no params" case.
-class C {
-  // CHECK:STDERR: fail_return_type_after_no_params.carbon:[[@LINE+4]]:3: error: missing implicit `self` parameter [DestroyFunctionMissingSelf]
-  // CHECK:STDERR:   fn destroy -> ();
-  // CHECK:STDERR:   ^~~~~~~~~~~~~~~~~
-  // CHECK:STDERR:
-  fn destroy -> ();
-}

+ 0 - 7
toolchain/diagnostics/diagnostic_kind.def

@@ -268,13 +268,6 @@ CARBON_DIAGNOSTIC_KIND(InvalidBuiltinSignature)
 CARBON_DIAGNOSTIC_KIND(ThunkSignature)
 CARBON_DIAGNOSTIC_KIND(ThunkCallee)
 
-CARBON_DIAGNOSTIC_KIND(DestroyFunctionOutsideClass)
-CARBON_DIAGNOSTIC_KIND(DestroyFunctionMissingSelf)
-CARBON_DIAGNOSTIC_KIND(DestroyFunctionUnexpectedImplicitParam)
-CARBON_DIAGNOSTIC_KIND(DestroyFunctionPositionalParams)
-CARBON_DIAGNOSTIC_KIND(DestroyFunctionNonEmptyExplicitParams)
-CARBON_DIAGNOSTIC_KIND(DestroyFunctionIncorrectReturnType)
-
 // Class checking.
 CARBON_DIAGNOSTIC_KIND(AdaptDeclRepeated)
 CARBON_DIAGNOSTIC_KIND(AdaptWithBase)

+ 0 - 1
toolchain/lex/token_kind.def

@@ -181,7 +181,6 @@ CARBON_KEYWORD_TOKEN(Const,               "const")
 CARBON_KEYWORD_TOKEN(Continue,            "continue")
 CARBON_KEYWORD_TOKEN(Core,                "Core")
 CARBON_KEYWORD_TOKEN(Default,             "default")
-CARBON_KEYWORD_TOKEN(Destroy,             "destroy")
 CARBON_KEYWORD_TOKEN(Else,                "else")
 CARBON_KEYWORD_TOKEN(Extend,              "extend")
 CARBON_KEYWORD_TOKEN(Extern,              "extern")

+ 1 - 1
toolchain/lower/testdata/function/definition/destroy.carbon

@@ -16,7 +16,7 @@
 library "[[@TEST_NAME]]";
 
 class HasDestroy {
-  // TODO: Use `fn destroy` instead once lowering supports it.
+  // TODO: Change for `Destruction`+`Destroy`.
   impl as Core.Destroy {
     fn Op[self: Self]();
   }

+ 4 - 12
toolchain/parse/handle_decl_name_and_params.cpp

@@ -55,13 +55,6 @@ auto HandleDeclNameAndParams(Context& context) -> void {
     return;
   }
 
-  if (auto keyword = context.ConsumeIf(Lex::TokenKind::Destroy)) {
-    HandleName(context, state, *keyword, NodeKind::KeywordNameNotBeforeParams,
-               NodeKind::KeywordNameQualifierWithoutParams,
-               NodeKind::KeywordNameBeforeParams);
-    return;
-  }
-
   Lex::TokenIndex token = *context.position();
   if (context.tokens().GetKind(token) == Lex::TokenKind::FileEnd) {
     // The end of file is an unhelpful diagnostic location. Instead, use the
@@ -100,11 +93,10 @@ auto HandleDeclNameAndParamsAfterParams(Context& context) -> void {
   auto state = context.PopState();
 
   if (auto period = context.ConsumeIf(Lex::TokenKind::Period)) {
-    auto start_kind = context.tree().node_kind(NodeId(state.subtree_start));
-    auto node_kind = start_kind == NodeKind::IdentifierNameBeforeParams
-                         ? NodeKind::IdentifierNameQualifierWithParams
-                         : NodeKind::KeywordNameQualifierWithParams;
-    context.AddNode(node_kind, *period, state.has_error);
+    CARBON_CHECK(context.tree().node_kind(NodeId(state.subtree_start)) ==
+                 NodeKind::IdentifierNameBeforeParams);
+    context.AddNode(NodeKind::IdentifierNameQualifierWithParams, *period,
+                    state.has_error);
     context.PushState(StateKind::DeclNameAndParams);
   }
 }

+ 0 - 5
toolchain/parse/node_kind.def

@@ -98,9 +98,6 @@ CARBON_PARSE_NODE_KIND(EmptyDecl)
 CARBON_PARSE_NODE_KIND(IdentifierNameNotBeforeParams)
 CARBON_PARSE_NODE_KIND(IdentifierNameBeforeParams)
 
-CARBON_PARSE_NODE_KIND(KeywordNameNotBeforeParams)
-CARBON_PARSE_NODE_KIND(KeywordNameBeforeParams)
-
 CARBON_PARSE_NODE_KIND(IdentifierNameExpr)
 
 CARBON_PARSE_NODE_KIND(SelfValueName)
@@ -133,8 +130,6 @@ CARBON_PARSE_NODE_KIND(InlineImportBody)
 
 CARBON_PARSE_NODE_KIND(IdentifierNameQualifierWithParams)
 CARBON_PARSE_NODE_KIND(IdentifierNameQualifierWithoutParams)
-CARBON_PARSE_NODE_KIND(KeywordNameQualifierWithParams)
-CARBON_PARSE_NODE_KIND(KeywordNameQualifierWithoutParams)
 
 CARBON_PARSE_NODE_KIND(ExportIntroducer)
 CARBON_PARSE_NODE_KIND(ExportDecl)

+ 0 - 86
toolchain/parse/testdata/function/declaration.carbon

@@ -36,26 +36,6 @@ fn foo(a: i32, b: i32);
 
 fn foo() -> u32;
 
-// --- keyword.carbon
-
-fn destroy() {}
-
-// --- keyword_no_params.carbon
-
-fn destroy {}
-
-// --- keyword_in_qualified.carbon
-
-fn MyClass.destroy() {}
-
-// --- keyword_decl_qualified_no_params.carbon
-
-fn destroy.Foo() {}
-
-// --- keyword_decl_qualified_with_params.carbon
-
-fn destroy[self: Self]().Foo() {}
-
 // --- impl_fn.carbon
 
 override fn F();
@@ -276,72 +256,6 @@ fn (a tokens c d e f g h i j k l m n o p q r s t u v w x y z);
 // CHECK:STDOUT:     {kind: 'FunctionDecl', text: ';', subtree_size: 7},
 // CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
 // CHECK:STDOUT:   ]
-// CHECK:STDOUT: - filename: keyword.carbon
-// CHECK:STDOUT:   parse_tree: [
-// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
-// CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
-// CHECK:STDOUT:         {kind: 'KeywordNameBeforeParams', text: 'destroy'},
-// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
-// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
-// CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 5},
-// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 6},
-// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
-// CHECK:STDOUT:   ]
-// CHECK:STDOUT: - filename: keyword_no_params.carbon
-// CHECK:STDOUT:   parse_tree: [
-// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
-// CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
-// CHECK:STDOUT:         {kind: 'KeywordNameNotBeforeParams', text: 'destroy'},
-// CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 3},
-// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 4},
-// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
-// CHECK:STDOUT:   ]
-// CHECK:STDOUT: - filename: keyword_in_qualified.carbon
-// CHECK:STDOUT:   parse_tree: [
-// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
-// CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
-// CHECK:STDOUT:           {kind: 'IdentifierNameNotBeforeParams', text: 'MyClass'},
-// CHECK:STDOUT:         {kind: 'IdentifierNameQualifierWithoutParams', text: '.', subtree_size: 2},
-// CHECK:STDOUT:         {kind: 'KeywordNameBeforeParams', text: 'destroy'},
-// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
-// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
-// CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7},
-// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 8},
-// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
-// CHECK:STDOUT:   ]
-// CHECK:STDOUT: - filename: keyword_decl_qualified_no_params.carbon
-// CHECK:STDOUT:   parse_tree: [
-// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
-// CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
-// CHECK:STDOUT:           {kind: 'KeywordNameNotBeforeParams', text: 'destroy'},
-// CHECK:STDOUT:         {kind: 'KeywordNameQualifierWithoutParams', text: '.', subtree_size: 2},
-// CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'Foo'},
-// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
-// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
-// CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 7},
-// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 8},
-// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
-// CHECK:STDOUT:   ]
-// CHECK:STDOUT: - filename: keyword_decl_qualified_with_params.carbon
-// CHECK:STDOUT:   parse_tree: [
-// CHECK:STDOUT:     {kind: 'FileStart', text: ''},
-// CHECK:STDOUT:         {kind: 'FunctionIntroducer', text: 'fn'},
-// CHECK:STDOUT:           {kind: 'KeywordNameBeforeParams', text: 'destroy'},
-// CHECK:STDOUT:             {kind: 'ImplicitParamListStart', text: '['},
-// CHECK:STDOUT:               {kind: 'SelfValueName', text: 'self'},
-// CHECK:STDOUT:               {kind: 'SelfTypeNameExpr', text: 'Self'},
-// CHECK:STDOUT:             {kind: 'LetBindingPattern', text: ':', subtree_size: 3},
-// CHECK:STDOUT:           {kind: 'ImplicitParamList', text: ']', subtree_size: 5},
-// CHECK:STDOUT:             {kind: 'ExplicitParamListStart', text: '('},
-// CHECK:STDOUT:           {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
-// CHECK:STDOUT:         {kind: 'KeywordNameQualifierWithParams', text: '.', subtree_size: 9},
-// CHECK:STDOUT:         {kind: 'IdentifierNameBeforeParams', text: 'Foo'},
-// CHECK:STDOUT:           {kind: 'ExplicitParamListStart', text: '('},
-// CHECK:STDOUT:         {kind: 'ExplicitParamList', text: ')', subtree_size: 2},
-// CHECK:STDOUT:       {kind: 'FunctionDefinitionStart', text: '{', subtree_size: 14},
-// CHECK:STDOUT:     {kind: 'FunctionDefinition', text: '}', subtree_size: 15},
-// CHECK:STDOUT:     {kind: 'FileEnd', text: ''},
-// CHECK:STDOUT:   ]
 // CHECK:STDOUT: - filename: impl_fn.carbon
 // CHECK:STDOUT:   parse_tree: [
 // CHECK:STDOUT:     {kind: 'FileStart', text: ''},

+ 2 - 26
toolchain/parse/typed_nodes.h

@@ -132,18 +132,12 @@ using EmptyDecl =
 using IdentifierNameBeforeParams =
     LeafNode<NodeKind::IdentifierNameBeforeParams, Lex::IdentifierTokenIndex,
              NodeCategory::MemberName | NodeCategory::NonExprName>;
-using KeywordNameBeforeParams =
-    LeafNode<NodeKind::KeywordNameBeforeParams, Lex::TokenIndex,
-             NodeCategory::MemberName | NodeCategory::NonExprName>;
 
 // A name in a non-expression context, such as a declaration, that is known
 // to not be followed by parameters.
 using IdentifierNameNotBeforeParams =
     LeafNode<NodeKind::IdentifierNameNotBeforeParams, Lex::IdentifierTokenIndex,
              NodeCategory::MemberName | NodeCategory::NonExprName>;
-using KeywordNameNotBeforeParams =
-    LeafNode<NodeKind::KeywordNameNotBeforeParams, Lex::TokenIndex,
-             NodeCategory::MemberName | NodeCategory::NonExprName>;
 
 // A name in an expression context.
 using IdentifierNameExpr =
@@ -184,15 +178,6 @@ struct IdentifierNameQualifierWithParams {
   std::optional<ExplicitParamListId> params;
   Lex::PeriodTokenIndex token;
 };
-struct KeywordNameQualifierWithParams {
-  static constexpr auto Kind = NodeKind::KeywordNameQualifierWithParams.Define(
-      {.bracketed_by = KeywordNameBeforeParams::Kind});
-
-  KeywordNameBeforeParamsId name;
-  std::optional<ImplicitParamListId> implicit_params;
-  std::optional<ExplicitParamListId> params;
-  Lex::PeriodTokenIndex token;
-};
 
 // A name qualifier without parameters, such as `A.`.
 struct IdentifierNameQualifierWithoutParams {
@@ -203,21 +188,12 @@ struct IdentifierNameQualifierWithoutParams {
   IdentifierNameNotBeforeParamsId name;
   Lex::PeriodTokenIndex token;
 };
-struct KeywordNameQualifierWithoutParams {
-  static constexpr auto Kind =
-      NodeKind::KeywordNameQualifierWithoutParams.Define(
-          {.bracketed_by = KeywordNameNotBeforeParams::Kind});
-
-  KeywordNameNotBeforeParamsId name;
-  Lex::PeriodTokenIndex token;
-};
 
 // A complete name in a declaration: `A.C(T:! type).F(n: i32)`.
 // Note that this includes the parameters of the entity itself.
 struct DeclName {
-  llvm::SmallVector<NodeIdOneOf<
-      IdentifierNameQualifierWithParams, IdentifierNameQualifierWithoutParams,
-      KeywordNameQualifierWithParams, KeywordNameQualifierWithoutParams>>
+  llvm::SmallVector<NodeIdOneOf<IdentifierNameQualifierWithParams,
+                                IdentifierNameQualifierWithoutParams>>
       qualifiers;
   AnyNonExprNameId name;
   std::optional<ImplicitParamListId> implicit_params;

+ 2 - 2
toolchain/parse/typed_nodes_test.cpp

@@ -293,8 +293,8 @@ NodeIdForKind error: wrong kind IdentifierNameBeforeParams, expected ImplicitPar
 Optional [^:]*: missing
 NodeIdInCategory NonExprName: kind IdentifierNameBeforeParams consumed
 Vector: begin
-NodeIdOneOf IdentifierNameQualifierWithParams or IdentifierNameQualifierWithoutParams or KeywordNameQualifierWithParams or KeywordNameQualifierWithoutParams: IdentifierNameQualifierWithoutParams consumed
-NodeIdOneOf error: wrong kind AbstractModifier, expected IdentifierNameQualifierWithParams or IdentifierNameQualifierWithoutParams or KeywordNameQualifierWithParams or KeywordNameQualifierWithoutParams
+NodeIdOneOf IdentifierNameQualifierWithParams or IdentifierNameQualifierWithoutParams: IdentifierNameQualifierWithoutParams consumed
+NodeIdOneOf error: wrong kind AbstractModifier, expected IdentifierNameQualifierWithParams or IdentifierNameQualifierWithoutParams
 Vector: end
 Aggregate [^:]*: success
 Vector: begin

+ 0 - 2
toolchain/sem_ir/ids.h

@@ -566,8 +566,6 @@ constexpr FloatKind FloatKind::PPCFloat128 = FloatKind(6);
   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`. */                                     \

+ 0 - 2
toolchain/sem_ir/name.cpp

@@ -23,8 +23,6 @@ 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:

+ 2 - 2
toolchain/testing/testdata/min_prelude/parts/destroy.carbon

@@ -12,8 +12,8 @@ package Core library "prelude/parts/destroy";
 import library "prelude/parts/bool";
 import library "prelude/parts/int_literal";
 
-// Object destruction, including running `fn destroy()`. Note this is distinct
-// from memory deallocation.
+// Destroys objects. Note this is distinct from memory deallocation.
+// TODO: Switch to `Destructor`+`Destroy` model.
 interface Destroy {
   fn Op[addr self: Self*]();
 }