فهرست منبع

Switch decl_state to an arg for modifier functions (#4027)

With private modifiers, we'll want to start checking modifiers later,
e.g. after a name conflict is detected (and potentially merged). I think
we've agreed to be more explicit about whether the modifier functions
are manipulating state, versus trying to keep the state on the stack a
little longer (moving Pop to the end of these functions).

Removing FileScope from the stack and renaming it to DeclIntroducerStack
to better reflect the usage and behavior. The FileScope mostly reflects
an approach that wasn't ultimately adopted.
Jon Ross-Perkins 1 سال پیش
والد
کامیت
a910eda020

+ 1 - 1
toolchain/check/BUILD

@@ -66,8 +66,8 @@ cc_library(
     hdrs = [
     hdrs = [
         "context.h",
         "context.h",
         "convert.h",
         "convert.h",
+        "decl_introducer_state.h",
         "decl_name_stack.h",
         "decl_name_stack.h",
-        "decl_state.h",
         "diagnostic_helpers.h",
         "diagnostic_helpers.h",
         "eval.h",
         "eval.h",
         "function.h",
         "function.h",

+ 5 - 3
toolchain/check/context.h

@@ -8,8 +8,8 @@
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/FoldingSet.h"
 #include "llvm/ADT/FoldingSet.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/SmallVector.h"
+#include "toolchain/check/decl_introducer_state.h"
 #include "toolchain/check/decl_name_stack.h"
 #include "toolchain/check/decl_name_stack.h"
-#include "toolchain/check/decl_state.h"
 #include "toolchain/check/diagnostic_helpers.h"
 #include "toolchain/check/diagnostic_helpers.h"
 #include "toolchain/check/inst_block_stack.h"
 #include "toolchain/check/inst_block_stack.h"
 #include "toolchain/check/node_stack.h"
 #include "toolchain/check/node_stack.h"
@@ -335,7 +335,9 @@ class Context {
 
 
   auto decl_name_stack() -> DeclNameStack& { return decl_name_stack_; }
   auto decl_name_stack() -> DeclNameStack& { return decl_name_stack_; }
 
 
-  auto decl_state_stack() -> DeclStateStack& { return decl_state_stack_; }
+  auto decl_introducer_state_stack() -> DeclIntroducerStateStack& {
+    return decl_introducer_state_stack_;
+  }
 
 
   auto scope_stack() -> ScopeStack& { return scope_stack_; }
   auto scope_stack() -> ScopeStack& { return scope_stack_; }
 
 
@@ -447,7 +449,7 @@ class Context {
   DeclNameStack decl_name_stack_;
   DeclNameStack decl_name_stack_;
 
 
   // The stack of declarations that could have modifiers.
   // The stack of declarations that could have modifiers.
-  DeclStateStack decl_state_stack_;
+  DeclIntroducerStateStack decl_introducer_state_stack_;
 
 
   // The stack of scopes we are currently within.
   // The stack of scopes we are currently within.
   ScopeStack scope_stack_;
   ScopeStack scope_stack_;

+ 20 - 22
toolchain/check/decl_state.h → toolchain/check/decl_introducer_state.h

@@ -2,20 +2,20 @@
 // Exceptions. See /LICENSE for license information.
 // Exceptions. See /LICENSE for license information.
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
 
-#ifndef CARBON_TOOLCHAIN_CHECK_DECL_STATE_H_
-#define CARBON_TOOLCHAIN_CHECK_DECL_STATE_H_
+#ifndef CARBON_TOOLCHAIN_CHECK_DECL_INTRODUCER_STATE_H_
+#define CARBON_TOOLCHAIN_CHECK_DECL_INTRODUCER_STATE_H_
 
 
 #include "toolchain/check/keyword_modifier_set.h"
 #include "toolchain/check/keyword_modifier_set.h"
 #include "toolchain/parse/node_ids.h"
 #include "toolchain/parse/node_ids.h"
 
 
 namespace Carbon::Check {
 namespace Carbon::Check {
 
 
-// State stored for each declaration we are currently in: the kind of
-// declaration and the keyword modifiers that apply to that declaration.
-struct DeclState {
+// State stored for each declaration we are introducing: the kind of
+// declaration and the keyword modifiers that apply to that declaration
+// introducer.
+struct DeclIntroducerState {
   // The kind of declaration.
   // The kind of declaration.
   enum DeclKind : int8_t {
   enum DeclKind : int8_t {
-    FileScope,
     Adapt,
     Adapt,
     Alias,
     Alias,
     Base,
     Base,
@@ -32,7 +32,7 @@ struct DeclState {
     Var
     Var
   };
   };
 
 
-  explicit DeclState(DeclKind decl_kind) : kind(decl_kind) {}
+  explicit DeclIntroducerState(DeclKind decl_kind) : kind(decl_kind) {}
 
 
   auto modifier_node_id(ModifierOrder order) -> Parse::NodeId {
   auto modifier_node_id(ModifierOrder order) -> Parse::NodeId {
     return ordered_modifier_node_ids[static_cast<int8_t>(order)];
     return ordered_modifier_node_ids[static_cast<int8_t>(order)];
@@ -55,32 +55,30 @@ struct DeclState {
   KeywordModifierSet modifier_set;
   KeywordModifierSet modifier_set;
 };
 };
 
 
-// Stack of `DeclState` values, representing all the declarations we are
-// currently nested within.
-// Invariant: Bottom of the stack always has a "DeclState::FileScope" entry.
-class DeclStateStack {
+// Stack of `DeclIntroducerState` values, representing all the declaration
+// introducers we are currently nested within. Commonly size 0 or 1, as nested
+// introducers are rare.
+class DeclIntroducerStateStack {
  public:
  public:
-  DeclStateStack() { stack_.emplace_back(DeclState::FileScope); }
-
-  // Enters a declaration of kind `k`.
-  auto Push(DeclState::DeclKind k) -> void { stack_.emplace_back(k); }
+  // Begins introducing a declaration of kind `k`.
+  auto Push(DeclIntroducerState::DeclKind k) -> void { stack_.emplace_back(k); }
 
 
   // Gets the state of declaration at the top of the stack -- the innermost
   // Gets the state of declaration at the top of the stack -- the innermost
   // declaration currently being processed.
   // declaration currently being processed.
-  auto innermost() -> DeclState& { return stack_.back(); }
+  auto innermost() -> DeclIntroducerState& { return stack_.back(); }
 
 
-  // Exits a declaration of kind `k`.
-  auto Pop(DeclState::DeclKind k) -> void {
+  // Finishes introducing a declaration of kind `k` and returns the
+  // produced state.
+  auto Pop(DeclIntroducerState::DeclKind k) -> DeclIntroducerState {
     CARBON_CHECK(stack_.back().kind == k)
     CARBON_CHECK(stack_.back().kind == k)
         << "Found: " << stack_.back().kind << " expected: " << k;
         << "Found: " << stack_.back().kind << " expected: " << k;
-    stack_.pop_back();
-    CARBON_CHECK(!stack_.empty());
+    return stack_.pop_back_val();
   }
   }
 
 
  private:
  private:
-  llvm::SmallVector<DeclState> stack_;
+  llvm::SmallVector<DeclIntroducerState> stack_;
 };
 };
 
 
 }  // namespace Carbon::Check
 }  // namespace Carbon::Check
 
 
-#endif  // CARBON_TOOLCHAIN_CHECK_DECL_STATE_H_
+#endif  // CARBON_TOOLCHAIN_CHECK_DECL_INTRODUCER_STATE_H_

+ 6 - 7
toolchain/check/handle_alias.cpp

@@ -14,7 +14,7 @@ namespace Carbon::Check {
 
 
 auto HandleAliasIntroducer(Context& context,
 auto HandleAliasIntroducer(Context& context,
                            Parse::AliasIntroducerId /*node_id*/) -> bool {
                            Parse::AliasIntroducerId /*node_id*/) -> bool {
-  context.decl_state_stack().Push(DeclState::Alias);
+  context.decl_introducer_state_stack().Push(DeclIntroducerState::Alias);
   context.decl_name_stack().PushScopeAndStartName();
   context.decl_name_stack().PushScopeAndStartName();
   return true;
   return true;
 }
 }
@@ -30,15 +30,14 @@ auto HandleAlias(Context& context, Parse::AliasId /*node_id*/) -> bool {
   auto name_context = context.decl_name_stack().FinishName(
   auto name_context = context.decl_name_stack().FinishName(
       PopNameComponentWithoutParams(context, Lex::TokenKind::Alias));
       PopNameComponentWithoutParams(context, Lex::TokenKind::Alias));
 
 
-  LimitModifiersOnDecl(context, KeywordModifierSet::Access,
+  auto introducer =
+      context.decl_introducer_state_stack().Pop(DeclIntroducerState::Alias);
+  LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Access,
                        Lex::TokenKind::Alias);
                        Lex::TokenKind::Alias);
-  auto modifiers = context.decl_state_stack().innermost().modifier_set;
-  if (modifiers.HasAnyOf(KeywordModifierSet::Access)) {
-    context.TODO(context.decl_state_stack().innermost().modifier_node_id(
-                     ModifierOrder::Access),
+  if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Access)) {
+    context.TODO(introducer.modifier_node_id(ModifierOrder::Access),
                  "access modifier");
                  "access modifier");
   }
   }
-  context.decl_state_stack().Pop(DeclState::Alias);
 
 
   auto bind_name_id = context.bind_names().Add(
   auto bind_name_id = context.bind_names().Add(
       {.name_id = name_context.name_id_for_new_inst(),
       {.name_id = name_context.name_id_for_new_inst(),

+ 26 - 24
toolchain/check/handle_class.cpp

@@ -36,7 +36,7 @@ auto HandleClassIntroducer(Context& context, Parse::ClassIntroducerId node_id)
   // Push the bracketing node.
   // Push the bracketing node.
   context.node_stack().Push(node_id);
   context.node_stack().Push(node_id);
   // Optional modifiers and the name follow.
   // Optional modifiers and the name follow.
-  context.decl_state_stack().Push(DeclState::Class);
+  context.decl_introducer_state_stack().Push(DeclIntroducerState::Class);
   context.decl_name_stack().PushScopeAndStartName();
   context.decl_name_stack().PushScopeAndStartName();
   return true;
   return true;
 }
 }
@@ -187,28 +187,30 @@ static auto BuildClassDecl(Context& context, Parse::AnyClassDeclId node_id,
   // Process modifiers.
   // Process modifiers.
   auto [_, parent_scope_inst] =
   auto [_, parent_scope_inst] =
       context.name_scopes().GetInstIfValid(name_context.parent_scope_id);
       context.name_scopes().GetInstIfValid(name_context.parent_scope_id);
-  CheckAccessModifiersOnDecl(context, Lex::TokenKind::Class, parent_scope_inst);
-  LimitModifiersOnDecl(context,
+  auto introducer =
+      context.decl_introducer_state_stack().Pop(DeclIntroducerState::Class);
+  CheckAccessModifiersOnDecl(context, introducer, Lex::TokenKind::Class,
+                             parent_scope_inst);
+  LimitModifiersOnDecl(context, introducer,
                        KeywordModifierSet::Class | KeywordModifierSet::Access |
                        KeywordModifierSet::Class | KeywordModifierSet::Access |
                            KeywordModifierSet::Extern,
                            KeywordModifierSet::Extern,
                        Lex::TokenKind::Class);
                        Lex::TokenKind::Class);
-  RestrictExternModifierOnDecl(context, Lex::TokenKind::Class,
+  RestrictExternModifierOnDecl(context, introducer, Lex::TokenKind::Class,
                                parent_scope_inst, is_definition);
                                parent_scope_inst, is_definition);
 
 
-  auto modifiers = context.decl_state_stack().innermost().modifier_set;
-  if (modifiers.HasAnyOf(KeywordModifierSet::Access)) {
-    context.TODO(context.decl_state_stack().innermost().modifier_node_id(
-                     ModifierOrder::Access),
+  if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Access)) {
+    context.TODO(introducer.modifier_node_id(ModifierOrder::Access),
                  "access modifier");
                  "access modifier");
   }
   }
 
 
-  bool is_extern = modifiers.HasAnyOf(KeywordModifierSet::Extern);
+  bool is_extern = introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extern);
   auto inheritance_kind =
   auto inheritance_kind =
-      modifiers.HasAnyOf(KeywordModifierSet::Abstract) ? SemIR::Class::Abstract
-      : modifiers.HasAnyOf(KeywordModifierSet::Base)   ? SemIR::Class::Base
-                                                       : SemIR::Class::Final;
+      introducer.modifier_set.HasAnyOf(KeywordModifierSet::Abstract)
+          ? SemIR::Class::Abstract
+      : introducer.modifier_set.HasAnyOf(KeywordModifierSet::Base)
+          ? SemIR::Class::Base
+          : SemIR::Class::Final;
 
 
-  context.decl_state_stack().Pop(DeclState::Class);
   auto decl_block_id = context.inst_block_stack().Pop();
   auto decl_block_id = context.inst_block_stack().Pop();
 
 
   // Add the class declaration.
   // Add the class declaration.
@@ -355,7 +357,7 @@ static auto DiagnoseClassSpecificDeclRepeated(Context& context,
 
 
 auto HandleAdaptIntroducer(Context& context,
 auto HandleAdaptIntroducer(Context& context,
                            Parse::AdaptIntroducerId /*node_id*/) -> bool {
                            Parse::AdaptIntroducerId /*node_id*/) -> bool {
-  context.decl_state_stack().Push(DeclState::Adapt);
+  context.decl_introducer_state_stack().Push(DeclIntroducerState::Adapt);
   return true;
   return true;
 }
 }
 
 
@@ -364,10 +366,10 @@ auto HandleAdaptDecl(Context& context, Parse::AdaptDeclId node_id) -> bool {
       context.node_stack().PopExprWithNodeId();
       context.node_stack().PopExprWithNodeId();
 
 
   // Process modifiers. `extend` is permitted, no others are allowed.
   // Process modifiers. `extend` is permitted, no others are allowed.
-  LimitModifiersOnDecl(context, KeywordModifierSet::Extend,
+  auto introducer =
+      context.decl_introducer_state_stack().Pop(DeclIntroducerState::Adapt);
+  LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Extend,
                        Lex::TokenKind::Adapt);
                        Lex::TokenKind::Adapt);
-  auto modifiers = context.decl_state_stack().innermost().modifier_set;
-  context.decl_state_stack().Pop(DeclState::Adapt);
 
 
   auto parent_class_decl =
   auto parent_class_decl =
       GetCurrentScopeAsClassOrDiagnose(context, node_id, Lex::TokenKind::Adapt);
       GetCurrentScopeAsClassOrDiagnose(context, node_id, Lex::TokenKind::Adapt);
@@ -396,7 +398,7 @@ auto HandleAdaptDecl(Context& context, Parse::AdaptDeclId node_id) -> bool {
       node_id, {.adapted_type_id = adapted_type_id});
       node_id, {.adapted_type_id = adapted_type_id});
 
 
   // Extend the class scope with the adapted type's scope if requested.
   // Extend the class scope with the adapted type's scope if requested.
-  if (modifiers.HasAnyOf(KeywordModifierSet::Extend)) {
+  if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) {
     auto extended_scope_id = SemIR::NameScopeId::Invalid;
     auto extended_scope_id = SemIR::NameScopeId::Invalid;
     if (adapted_type_id == SemIR::TypeId::Error) {
     if (adapted_type_id == SemIR::TypeId::Error) {
       // Recover by not extending any scope. We instead set has_error to true
       // Recover by not extending any scope. We instead set has_error to true
@@ -423,7 +425,7 @@ auto HandleAdaptDecl(Context& context, Parse::AdaptDeclId node_id) -> bool {
 
 
 auto HandleBaseIntroducer(Context& context, Parse::BaseIntroducerId /*node_id*/)
 auto HandleBaseIntroducer(Context& context, Parse::BaseIntroducerId /*node_id*/)
     -> bool {
     -> bool {
-  context.decl_state_stack().Push(DeclState::Base);
+  context.decl_introducer_state_stack().Push(DeclIntroducerState::Base);
   return true;
   return true;
 }
 }
 
 
@@ -495,15 +497,15 @@ auto HandleBaseDecl(Context& context, Parse::BaseDeclId node_id) -> bool {
       context.node_stack().PopExprWithNodeId();
       context.node_stack().PopExprWithNodeId();
 
 
   // Process modifiers. `extend` is required, no others are allowed.
   // Process modifiers. `extend` is required, no others are allowed.
-  LimitModifiersOnDecl(context, KeywordModifierSet::Extend,
+  auto introducer =
+      context.decl_introducer_state_stack().Pop(DeclIntroducerState::Base);
+  LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Extend,
                        Lex::TokenKind::Base);
                        Lex::TokenKind::Base);
-  auto modifiers = context.decl_state_stack().innermost().modifier_set;
-  if (!modifiers.HasAnyOf(KeywordModifierSet::Extend)) {
+  if (!introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) {
     CARBON_DIAGNOSTIC(BaseMissingExtend, Error,
     CARBON_DIAGNOSTIC(BaseMissingExtend, Error,
                       "Missing `extend` before `base` declaration in class.");
                       "Missing `extend` before `base` declaration in class.");
     context.emitter().Emit(node_id, BaseMissingExtend);
     context.emitter().Emit(node_id, BaseMissingExtend);
   }
   }
-  context.decl_state_stack().Pop(DeclState::Base);
 
 
   auto parent_class_decl =
   auto parent_class_decl =
       GetCurrentScopeAsClassOrDiagnose(context, node_id, Lex::TokenKind::Base);
       GetCurrentScopeAsClassOrDiagnose(context, node_id, Lex::TokenKind::Base);
@@ -546,7 +548,7 @@ auto HandleBaseDecl(Context& context, Parse::BaseDeclId node_id) -> bool {
       class_info.base_id);
       class_info.base_id);
 
 
   // Extend the class scope with the base class.
   // Extend the class scope with the base class.
-  if (modifiers.HasAnyOf(KeywordModifierSet::Extend)) {
+  if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) {
     auto& class_scope = context.name_scopes().Get(class_info.scope_id);
     auto& class_scope = context.name_scopes().Get(class_info.scope_id);
     if (base_info.scope_id.is_valid()) {
     if (base_info.scope_id.is_valid()) {
       class_scope.extended_scopes.push_back(base_info.scope_id);
       class_scope.extended_scopes.push_back(base_info.scope_id);

+ 4 - 3
toolchain/check/handle_export.cpp

@@ -15,7 +15,7 @@ namespace Carbon::Check {
 
 
 auto HandleExportIntroducer(Context& context,
 auto HandleExportIntroducer(Context& context,
                             Parse::ExportIntroducerId /*node_id*/) -> bool {
                             Parse::ExportIntroducerId /*node_id*/) -> bool {
-  context.decl_state_stack().Push(DeclState::Export);
+  context.decl_introducer_state_stack().Push(DeclIntroducerState::Export);
   // TODO: Probably need to update DeclNameStack to restrict to only namespaces.
   // TODO: Probably need to update DeclNameStack to restrict to only namespaces.
   context.decl_name_stack().PushScopeAndStartName();
   context.decl_name_stack().PushScopeAndStartName();
   return true;
   return true;
@@ -26,9 +26,10 @@ auto HandleExportDecl(Context& context, Parse::ExportDeclId node_id) -> bool {
       PopNameComponentWithoutParams(context, Lex::TokenKind::Export));
       PopNameComponentWithoutParams(context, Lex::TokenKind::Export));
   context.decl_name_stack().PopScope();
   context.decl_name_stack().PopScope();
 
 
-  LimitModifiersOnDecl(context, KeywordModifierSet::None,
+  auto introducer =
+      context.decl_introducer_state_stack().Pop(DeclIntroducerState::Export);
+  LimitModifiersOnDecl(context, introducer, KeywordModifierSet::None,
                        Lex::TokenKind::Export);
                        Lex::TokenKind::Export);
-  context.decl_state_stack().Pop(DeclState::Export);
 
 
   if (name_context.state == DeclNameStack::NameContext::State::Error) {
   if (name_context.state == DeclNameStack::NameContext::State::Error) {
     // Should already be diagnosed.
     // Should already be diagnosed.

+ 23 - 25
toolchain/check/handle_function.cpp

@@ -5,8 +5,8 @@
 #include "toolchain/base/kind_switch.h"
 #include "toolchain/base/kind_switch.h"
 #include "toolchain/check/context.h"
 #include "toolchain/check/context.h"
 #include "toolchain/check/convert.h"
 #include "toolchain/check/convert.h"
+#include "toolchain/check/decl_introducer_state.h"
 #include "toolchain/check/decl_name_stack.h"
 #include "toolchain/check/decl_name_stack.h"
-#include "toolchain/check/decl_state.h"
 #include "toolchain/check/function.h"
 #include "toolchain/check/function.h"
 #include "toolchain/check/handle.h"
 #include "toolchain/check/handle.h"
 #include "toolchain/check/interface.h"
 #include "toolchain/check/interface.h"
@@ -30,7 +30,7 @@ auto HandleFunctionIntroducer(Context& context,
   // Push the bracketing node.
   // Push the bracketing node.
   context.node_stack().Push(node_id);
   context.node_stack().Push(node_id);
   // Optional modifiers and the name follow.
   // Optional modifiers and the name follow.
-  context.decl_state_stack().Push(DeclState::Fn);
+  context.decl_introducer_state_stack().Push(DeclIntroducerState::Fn);
   context.decl_name_stack().PushScopeAndStartName();
   context.decl_name_stack().PushScopeAndStartName();
   return true;
   return true;
 }
 }
@@ -45,24 +45,24 @@ auto HandleReturnType(Context& context, Parse::ReturnTypeId node_id) -> bool {
   return true;
   return true;
 }
 }
 
 
-static auto DiagnoseModifiers(Context& context, bool is_definition,
+static auto DiagnoseModifiers(Context& context, DeclIntroducerState& introducer,
+                              bool is_definition,
                               SemIR::InstId parent_scope_inst_id,
                               SemIR::InstId parent_scope_inst_id,
                               std::optional<SemIR::Inst> parent_scope_inst)
                               std::optional<SemIR::Inst> parent_scope_inst)
-    -> KeywordModifierSet {
-  CheckAccessModifiersOnDecl(context, Lex::TokenKind::Fn, parent_scope_inst);
-  LimitModifiersOnDecl(context,
+    -> void {
+  CheckAccessModifiersOnDecl(context, introducer, Lex::TokenKind::Fn,
+                             parent_scope_inst);
+  LimitModifiersOnDecl(context, introducer,
                        KeywordModifierSet::Access | KeywordModifierSet::Extern |
                        KeywordModifierSet::Access | KeywordModifierSet::Extern |
                            KeywordModifierSet::Method |
                            KeywordModifierSet::Method |
                            KeywordModifierSet::Interface,
                            KeywordModifierSet::Interface,
                        Lex::TokenKind::Fn);
                        Lex::TokenKind::Fn);
-  RestrictExternModifierOnDecl(context, Lex::TokenKind::Fn, parent_scope_inst,
-                               is_definition);
-  CheckMethodModifiersOnFunction(context, parent_scope_inst_id,
+  RestrictExternModifierOnDecl(context, introducer, Lex::TokenKind::Fn,
+                               parent_scope_inst, is_definition);
+  CheckMethodModifiersOnFunction(context, introducer, parent_scope_inst_id,
                                  parent_scope_inst);
                                  parent_scope_inst);
-  RequireDefaultFinalOnlyInInterfaces(context, Lex::TokenKind::Fn,
+  RequireDefaultFinalOnlyInInterfaces(context, introducer, Lex::TokenKind::Fn,
                                       parent_scope_inst);
                                       parent_scope_inst);
-
-  return context.decl_state_stack().innermost().modifier_set;
 }
 }
 
 
 // Returns the return slot usage for a function given the computed usage for two
 // Returns the return slot usage for a function given the computed usage for two
@@ -227,27 +227,25 @@ static auto BuildFunctionDecl(Context& context,
   // Process modifiers.
   // Process modifiers.
   auto [parent_scope_inst_id, parent_scope_inst] =
   auto [parent_scope_inst_id, parent_scope_inst] =
       context.name_scopes().GetInstIfValid(name_context.parent_scope_id);
       context.name_scopes().GetInstIfValid(name_context.parent_scope_id);
-  auto modifiers = DiagnoseModifiers(context, is_definition,
-                                     parent_scope_inst_id, parent_scope_inst);
-  if (modifiers.HasAnyOf(KeywordModifierSet::Access)) {
-    context.TODO(context.decl_state_stack().innermost().modifier_node_id(
-                     ModifierOrder::Access),
+  auto introducer =
+      context.decl_introducer_state_stack().Pop(DeclIntroducerState::Fn);
+  DiagnoseModifiers(context, introducer, is_definition, parent_scope_inst_id,
+                    parent_scope_inst);
+  if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Access)) {
+    context.TODO(introducer.modifier_node_id(ModifierOrder::Access),
                  "access modifier");
                  "access modifier");
   }
   }
-  bool is_extern = modifiers.HasAnyOf(KeywordModifierSet::Extern);
-  if (modifiers.HasAnyOf(KeywordModifierSet::Method)) {
-    context.TODO(context.decl_state_stack().innermost().modifier_node_id(
-                     ModifierOrder::Decl),
+  bool is_extern = introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extern);
+  if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Method)) {
+    context.TODO(introducer.modifier_node_id(ModifierOrder::Decl),
                  "method modifier");
                  "method modifier");
   }
   }
-  if (modifiers.HasAnyOf(KeywordModifierSet::Interface)) {
+  if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Interface)) {
     // TODO: Once we are saving the modifiers for a function, add check that
     // TODO: Once we are saving the modifiers for a function, add check that
     // the function may only be defined if it is marked `default` or `final`.
     // the function may only be defined if it is marked `default` or `final`.
-    context.TODO(context.decl_state_stack().innermost().modifier_node_id(
-                     ModifierOrder::Decl),
+    context.TODO(introducer.modifier_node_id(ModifierOrder::Decl),
                  "interface modifier");
                  "interface modifier");
   }
   }
-  context.decl_state_stack().Pop(DeclState::Fn);
 
 
   // Add the function declaration.
   // Add the function declaration.
   auto function_decl = SemIR::FunctionDecl{
   auto function_decl = SemIR::FunctionDecl{

+ 6 - 8
toolchain/check/handle_impl.cpp

@@ -24,7 +24,7 @@ auto HandleImplIntroducer(Context& context, Parse::ImplIntroducerId node_id)
   context.node_stack().Push(node_id);
   context.node_stack().Push(node_id);
 
 
   // Optional modifiers follow.
   // Optional modifiers follow.
-  context.decl_state_stack().Push(DeclState::Impl);
+  context.decl_introducer_state_stack().Push(DeclIntroducerState::Impl);
 
 
   // An impl doesn't have a name per se, but it makes the processing more
   // An impl doesn't have a name per se, but it makes the processing more
   // consistent to imagine that it does. This also gives us a scope for implicit
   // consistent to imagine that it does. This also gives us a scope for implicit
@@ -192,7 +192,9 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id)
   // Process modifiers.
   // Process modifiers.
   // TODO: Should we somehow permit access specifiers on `impl`s?
   // TODO: Should we somehow permit access specifiers on `impl`s?
   // TODO: Handle `final` modifier.
   // TODO: Handle `final` modifier.
-  LimitModifiersOnDecl(context, KeywordModifierSet::ImplDecl,
+  auto introducer =
+      context.decl_introducer_state_stack().Pop(DeclIntroducerState::Impl);
+  LimitModifiersOnDecl(context, introducer, KeywordModifierSet::ImplDecl,
                        Lex::TokenKind::Impl);
                        Lex::TokenKind::Impl);
 
 
   // Finish processing the name, which should be empty, but might have
   // Finish processing the name, which should be empty, but might have
@@ -214,16 +216,12 @@ static auto BuildImplDecl(Context& context, Parse::AnyImplDeclId node_id)
   auto impl_decl_id = context.AddInst(node_id, impl_decl);
   auto impl_decl_id = context.AddInst(node_id, impl_decl);
 
 
   // For an `extend impl` declaration, mark the impl as extending this `impl`.
   // For an `extend impl` declaration, mark the impl as extending this `impl`.
-  if (context.decl_state_stack().innermost().modifier_set.HasAnyOf(
-          KeywordModifierSet::Extend)) {
-    auto extend_node = context.decl_state_stack().innermost().modifier_node_id(
-        ModifierOrder::Decl);
+  if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Extend)) {
+    auto extend_node = introducer.modifier_node_id(ModifierOrder::Decl);
     ExtendImpl(context, extend_node, node_id, self_type_node, self_type_id,
     ExtendImpl(context, extend_node, node_id, self_type_node, self_type_id,
                params_node, constraint_type_id);
                params_node, constraint_type_id);
   }
   }
 
 
-  context.decl_state_stack().Pop(DeclState::Impl);
-
   return {impl_decl.impl_id, impl_decl_id};
   return {impl_decl.impl_id, impl_decl_id};
 }
 }
 
 

+ 15 - 10
toolchain/check/handle_import_and_package.cpp

@@ -3,7 +3,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
 
 #include "toolchain/check/context.h"
 #include "toolchain/check/context.h"
-#include "toolchain/check/decl_state.h"
+#include "toolchain/check/decl_introducer_state.h"
 #include "toolchain/check/handle.h"
 #include "toolchain/check/handle.h"
 #include "toolchain/check/modifiers.h"
 #include "toolchain/check/modifiers.h"
 
 
@@ -14,43 +14,48 @@ namespace Carbon::Check {
 
 
 auto HandleImportIntroducer(Context& context,
 auto HandleImportIntroducer(Context& context,
                             Parse::ImportIntroducerId /*node_id*/) -> bool {
                             Parse::ImportIntroducerId /*node_id*/) -> bool {
-  context.decl_state_stack().Push(DeclState::Import);
+  context.decl_introducer_state_stack().Push(DeclIntroducerState::Import);
   return true;
   return true;
 }
 }
 
 
 auto HandleImportDecl(Context& context, Parse::ImportDeclId /*node_id*/)
 auto HandleImportDecl(Context& context, Parse::ImportDeclId /*node_id*/)
     -> bool {
     -> bool {
-  LimitModifiersOnDecl(context, KeywordModifierSet::Export,
+  auto introducer =
+      context.decl_introducer_state_stack().Pop(DeclIntroducerState::Import);
+  LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Export,
                        Lex::TokenKind::Import);
                        Lex::TokenKind::Import);
-  context.decl_state_stack().Pop(DeclState::Import);
   return true;
   return true;
 }
 }
 
 
 auto HandleLibraryIntroducer(Context& context,
 auto HandleLibraryIntroducer(Context& context,
                              Parse::LibraryIntroducerId /*node_id*/) -> bool {
                              Parse::LibraryIntroducerId /*node_id*/) -> bool {
-  context.decl_state_stack().Push(DeclState::PackageOrLibrary);
+  context.decl_introducer_state_stack().Push(
+      DeclIntroducerState::PackageOrLibrary);
   return true;
   return true;
 }
 }
 
 
 auto HandleLibraryDecl(Context& context, Parse::LibraryDeclId /*node_id*/)
 auto HandleLibraryDecl(Context& context, Parse::LibraryDeclId /*node_id*/)
     -> bool {
     -> bool {
-  LimitModifiersOnDecl(context, KeywordModifierSet::Impl,
+  auto introducer = context.decl_introducer_state_stack().Pop(
+      DeclIntroducerState::PackageOrLibrary);
+  LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Impl,
                        Lex::TokenKind::Library);
                        Lex::TokenKind::Library);
-  context.decl_state_stack().Pop(DeclState::PackageOrLibrary);
   return true;
   return true;
 }
 }
 
 
 auto HandlePackageIntroducer(Context& context,
 auto HandlePackageIntroducer(Context& context,
                              Parse::PackageIntroducerId /*node_id*/) -> bool {
                              Parse::PackageIntroducerId /*node_id*/) -> bool {
-  context.decl_state_stack().Push(DeclState::PackageOrLibrary);
+  context.decl_introducer_state_stack().Push(
+      DeclIntroducerState::PackageOrLibrary);
   return true;
   return true;
 }
 }
 
 
 auto HandlePackageDecl(Context& context, Parse::PackageDeclId /*node_id*/)
 auto HandlePackageDecl(Context& context, Parse::PackageDeclId /*node_id*/)
     -> bool {
     -> bool {
-  LimitModifiersOnDecl(context, KeywordModifierSet::Impl,
+  auto introducer = context.decl_introducer_state_stack().Pop(
+      DeclIntroducerState::PackageOrLibrary);
+  LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Impl,
                        Lex::TokenKind::Package);
                        Lex::TokenKind::Package);
-  context.decl_state_stack().Pop(DeclState::PackageOrLibrary);
   return true;
   return true;
 }
 }
 
 

+ 7 - 8
toolchain/check/handle_interface.cpp

@@ -19,7 +19,7 @@ auto HandleInterfaceIntroducer(Context& context,
   // Push the bracketing node.
   // Push the bracketing node.
   context.node_stack().Push(node_id);
   context.node_stack().Push(node_id);
   // Optional modifiers and the name follow.
   // Optional modifiers and the name follow.
-  context.decl_state_stack().Push(DeclState::Interface);
+  context.decl_introducer_state_stack().Push(DeclIntroducerState::Interface);
   context.decl_name_stack().PushScopeAndStartName();
   context.decl_name_stack().PushScopeAndStartName();
   return true;
   return true;
 }
 }
@@ -39,18 +39,17 @@ static auto BuildInterfaceDecl(Context& context,
   // Process modifiers.
   // Process modifiers.
   auto [_, parent_scope_inst] =
   auto [_, parent_scope_inst] =
       context.name_scopes().GetInstIfValid(name_context.parent_scope_id);
       context.name_scopes().GetInstIfValid(name_context.parent_scope_id);
-  CheckAccessModifiersOnDecl(context, Lex::TokenKind::Interface,
+  auto introducer =
+      context.decl_introducer_state_stack().Pop(DeclIntroducerState::Interface);
+  CheckAccessModifiersOnDecl(context, introducer, Lex::TokenKind::Interface,
                              parent_scope_inst);
                              parent_scope_inst);
-  LimitModifiersOnDecl(context, KeywordModifierSet::Access,
+  LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Access,
                        Lex::TokenKind::Interface);
                        Lex::TokenKind::Interface);
 
 
-  auto modifiers = context.decl_state_stack().innermost().modifier_set;
-  if (modifiers.HasAnyOf(KeywordModifierSet::Access)) {
-    context.TODO(context.decl_state_stack().innermost().modifier_node_id(
-                     ModifierOrder::Access),
+  if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Access)) {
+    context.TODO(introducer.modifier_node_id(ModifierOrder::Access),
                  "access modifier");
                  "access modifier");
   }
   }
-  context.decl_state_stack().Pop(DeclState::Interface);
 
 
   auto decl_block_id = context.inst_block_stack().Pop();
   auto decl_block_id = context.inst_block_stack().Pop();
 
 

+ 12 - 12
toolchain/check/handle_let.cpp

@@ -15,7 +15,7 @@ namespace Carbon::Check {
 
 
 auto HandleLetIntroducer(Context& context, Parse::LetIntroducerId node_id)
 auto HandleLetIntroducer(Context& context, Parse::LetIntroducerId node_id)
     -> bool {
     -> bool {
-  context.decl_state_stack().Push(DeclState::Let);
+  context.decl_introducer_state_stack().Push(DeclIntroducerState::Let);
   // Push a bracketing node to establish the pattern context.
   // Push a bracketing node to establish the pattern context.
   context.node_stack().Push(node_id);
   context.node_stack().Push(node_id);
   return true;
   return true;
@@ -82,25 +82,25 @@ auto HandleLetDecl(Context& context, Parse::LetDeclId node_id) -> bool {
   auto [parent_scope_inst_id, parent_scope_inst] =
   auto [parent_scope_inst_id, parent_scope_inst] =
       context.name_scopes().GetInstIfValid(
       context.name_scopes().GetInstIfValid(
           context.scope_stack().PeekNameScopeId());
           context.scope_stack().PeekNameScopeId());
-  CheckAccessModifiersOnDecl(context, Lex::TokenKind::Let, parent_scope_inst);
-  RequireDefaultFinalOnlyInInterfaces(context, Lex::TokenKind::Let,
+  auto introducer =
+      context.decl_introducer_state_stack().Pop(DeclIntroducerState::Let);
+  CheckAccessModifiersOnDecl(context, introducer, Lex::TokenKind::Let,
+                             parent_scope_inst);
+  RequireDefaultFinalOnlyInInterfaces(context, introducer, Lex::TokenKind::Let,
                                       parent_scope_inst);
                                       parent_scope_inst);
   LimitModifiersOnDecl(
   LimitModifiersOnDecl(
-      context, KeywordModifierSet::Access | KeywordModifierSet::Interface,
+      context, introducer,
+      KeywordModifierSet::Access | KeywordModifierSet::Interface,
       Lex::TokenKind::Let);
       Lex::TokenKind::Let);
 
 
-  auto modifiers = context.decl_state_stack().innermost().modifier_set;
-  if (modifiers.HasAnyOf(KeywordModifierSet::Access)) {
-    context.TODO(context.decl_state_stack().innermost().modifier_node_id(
-                     ModifierOrder::Access),
+  if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Access)) {
+    context.TODO(introducer.modifier_node_id(ModifierOrder::Access),
                  "access modifier");
                  "access modifier");
   }
   }
-  if (modifiers.HasAnyOf(KeywordModifierSet::Interface)) {
-    context.TODO(context.decl_state_stack().innermost().modifier_node_id(
-                     ModifierOrder::Decl),
+  if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Interface)) {
+    context.TODO(introducer.modifier_node_id(ModifierOrder::Decl),
                  "interface modifier");
                  "interface modifier");
   }
   }
-  context.decl_state_stack().Pop(DeclState::Let);
 
 
   auto pattern = context.insts().GetWithLocId(pattern_id);
   auto pattern = context.insts().GetWithLocId(pattern_id);
   auto interface_scope = context.GetCurrentScopeAs<SemIR::InterfaceDecl>();
   auto interface_scope = context.GetCurrentScopeAs<SemIR::InterfaceDecl>();

+ 1 - 1
toolchain/check/handle_loop_statement.cpp

@@ -77,7 +77,7 @@ auto HandleForHeaderStart(Context& context, Parse::ForHeaderStartId node_id)
 }
 }
 
 
 auto HandleForIn(Context& context, Parse::ForInId node_id) -> bool {
 auto HandleForIn(Context& context, Parse::ForInId node_id) -> bool {
-  context.decl_state_stack().Pop(DeclState::Var);
+  context.decl_introducer_state_stack().Pop(DeclIntroducerState::Var);
   return context.TODO(node_id, "HandleForIn");
   return context.TODO(node_id, "HandleForIn");
 }
 }
 
 

+ 2 - 2
toolchain/check/handle_modifier.cpp

@@ -3,7 +3,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
 
 #include "toolchain/check/context.h"
 #include "toolchain/check/context.h"
-#include "toolchain/check/decl_state.h"
+#include "toolchain/check/decl_introducer_state.h"
 #include "toolchain/check/handle.h"
 #include "toolchain/check/handle.h"
 #include "toolchain/lex/token_kind.h"
 #include "toolchain/lex/token_kind.h"
 
 
@@ -36,7 +36,7 @@ static auto DiagnoseNotAllowedWith(Context& context, Parse::NodeId first_node,
 
 
 static auto HandleModifier(Context& context, Parse::NodeId node_id,
 static auto HandleModifier(Context& context, Parse::NodeId node_id,
                            KeywordModifierSet keyword) -> bool {
                            KeywordModifierSet keyword) -> bool {
-  auto& s = context.decl_state_stack().innermost();
+  auto& s = context.decl_introducer_state_stack().innermost();
 
 
   ModifierOrder order;
   ModifierOrder order;
   KeywordModifierSet later_modifiers;
   KeywordModifierSet later_modifiers;

+ 7 - 4
toolchain/check/handle_namespace.cpp

@@ -3,7 +3,7 @@
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 
 
 #include "toolchain/check/context.h"
 #include "toolchain/check/context.h"
-#include "toolchain/check/decl_state.h"
+#include "toolchain/check/decl_introducer_state.h"
 #include "toolchain/check/handle.h"
 #include "toolchain/check/handle.h"
 #include "toolchain/check/modifiers.h"
 #include "toolchain/check/modifiers.h"
 #include "toolchain/check/name_component.h"
 #include "toolchain/check/name_component.h"
@@ -15,7 +15,7 @@ namespace Carbon::Check {
 auto HandleNamespaceStart(Context& context, Parse::NamespaceStartId /*node_id*/)
 auto HandleNamespaceStart(Context& context, Parse::NamespaceStartId /*node_id*/)
     -> bool {
     -> bool {
   // Optional modifiers and the name follow.
   // Optional modifiers and the name follow.
-  context.decl_state_stack().Push(DeclState::Namespace);
+  context.decl_introducer_state_stack().Push(DeclIntroducerState::Namespace);
   context.decl_name_stack().PushScopeAndStartName();
   context.decl_name_stack().PushScopeAndStartName();
   return true;
   return true;
 }
 }
@@ -23,8 +23,12 @@ auto HandleNamespaceStart(Context& context, Parse::NamespaceStartId /*node_id*/)
 auto HandleNamespace(Context& context, Parse::NamespaceId node_id) -> bool {
 auto HandleNamespace(Context& context, Parse::NamespaceId node_id) -> bool {
   auto name_context = context.decl_name_stack().FinishName(
   auto name_context = context.decl_name_stack().FinishName(
       PopNameComponentWithoutParams(context, Lex::TokenKind::Namespace));
       PopNameComponentWithoutParams(context, Lex::TokenKind::Namespace));
-  LimitModifiersOnDecl(context, KeywordModifierSet::None,
+
+  auto introducer =
+      context.decl_introducer_state_stack().Pop(DeclIntroducerState::Namespace);
+  LimitModifiersOnDecl(context, introducer, KeywordModifierSet::None,
                        Lex::TokenKind::Namespace);
                        Lex::TokenKind::Namespace);
+
   auto namespace_inst = SemIR::Namespace{
   auto namespace_inst = SemIR::Namespace{
       context.GetBuiltinType(SemIR::BuiltinKind::NamespaceType),
       context.GetBuiltinType(SemIR::BuiltinKind::NamespaceType),
       SemIR::NameScopeId::Invalid, SemIR::InstId::Invalid};
       SemIR::NameScopeId::Invalid, SemIR::InstId::Invalid};
@@ -54,7 +58,6 @@ auto HandleNamespace(Context& context, Parse::NamespaceId node_id) -> bool {
   }
   }
 
 
   context.decl_name_stack().PopScope();
   context.decl_name_stack().PopScope();
-  context.decl_state_stack().Pop(DeclState::Namespace);
   return true;
   return true;
 }
 }
 
 

+ 8 - 9
toolchain/check/handle_variable.cpp

@@ -13,7 +13,7 @@ auto HandleVariableIntroducer(Context& context,
                               Parse::VariableIntroducerId node_id) -> bool {
                               Parse::VariableIntroducerId node_id) -> bool {
   // No action, just a bracketing node.
   // No action, just a bracketing node.
   context.node_stack().Push(node_id);
   context.node_stack().Push(node_id);
-  context.decl_state_stack().Push(DeclState::Var);
+  context.decl_introducer_state_stack().Push(DeclIntroducerState::Var);
   return true;
   return true;
 }
 }
 
 
@@ -101,18 +101,17 @@ auto HandleVariableDecl(Context& context, Parse::VariableDeclId node_id)
   // of the name introduced in the declaration. See #2590.
   // of the name introduced in the declaration. See #2590.
   auto [_, parent_scope_inst] = context.name_scopes().GetInstIfValid(
   auto [_, parent_scope_inst] = context.name_scopes().GetInstIfValid(
       context.scope_stack().PeekNameScopeId());
       context.scope_stack().PeekNameScopeId());
-  CheckAccessModifiersOnDecl(context, Lex::TokenKind::Var, parent_scope_inst);
-  LimitModifiersOnDecl(context, KeywordModifierSet::Access,
+  auto introducer =
+      context.decl_introducer_state_stack().Pop(DeclIntroducerState::Var);
+  CheckAccessModifiersOnDecl(context, introducer, Lex::TokenKind::Var,
+                             parent_scope_inst);
+  LimitModifiersOnDecl(context, introducer, KeywordModifierSet::Access,
                        Lex::TokenKind::Var);
                        Lex::TokenKind::Var);
-  auto modifiers = context.decl_state_stack().innermost().modifier_set;
-  if (modifiers.HasAnyOf(KeywordModifierSet::Access)) {
-    context.TODO(context.decl_state_stack().innermost().modifier_node_id(
-                     ModifierOrder::Access),
+  if (introducer.modifier_set.HasAnyOf(KeywordModifierSet::Access)) {
+    context.TODO(introducer.modifier_node_id(ModifierOrder::Access),
                  "access modifier");
                  "access modifier");
   }
   }
 
 
-  context.decl_state_stack().Pop(DeclState::Var);
-
   return true;
   return true;
 }
 }
 
 

+ 32 - 24
toolchain/check/modifiers.cpp

@@ -4,7 +4,7 @@
 
 
 #include "toolchain/check/modifiers.h"
 #include "toolchain/check/modifiers.h"
 
 
-#include "toolchain/check/decl_state.h"
+#include "toolchain/check/decl_introducer_state.h"
 
 
 namespace Carbon::Check {
 namespace Carbon::Check {
 
 
@@ -38,12 +38,12 @@ static auto ModifierOrderAsSet(ModifierOrder order) -> KeywordModifierSet {
   }
   }
 }
 }
 
 
-auto ForbidModifiersOnDecl(Context& context, KeywordModifierSet forbidden,
+auto ForbidModifiersOnDecl(Context& context, DeclIntroducerState& introducer,
+                           KeywordModifierSet forbidden,
                            Lex::TokenKind decl_kind,
                            Lex::TokenKind decl_kind,
                            llvm::StringRef context_string,
                            llvm::StringRef context_string,
                            SemIR::LocId context_loc_id) -> void {
                            SemIR::LocId context_loc_id) -> void {
-  auto& s = context.decl_state_stack().innermost();
-  auto not_allowed = s.modifier_set & forbidden;
+  auto not_allowed = introducer.modifier_set & forbidden;
   if (not_allowed.empty()) {
   if (not_allowed.empty()) {
     return;
     return;
   }
   }
@@ -52,16 +52,18 @@ auto ForbidModifiersOnDecl(Context& context, KeywordModifierSet forbidden,
        order_index <= static_cast<int8_t>(ModifierOrder::Last); ++order_index) {
        order_index <= static_cast<int8_t>(ModifierOrder::Last); ++order_index) {
     auto order = static_cast<ModifierOrder>(order_index);
     auto order = static_cast<ModifierOrder>(order_index);
     if (not_allowed.HasAnyOf(ModifierOrderAsSet(order))) {
     if (not_allowed.HasAnyOf(ModifierOrderAsSet(order))) {
-      DiagnoseNotAllowed(context, s.modifier_node_id(order), decl_kind,
+      DiagnoseNotAllowed(context, introducer.modifier_node_id(order), decl_kind,
                          context_string, context_loc_id);
                          context_string, context_loc_id);
-      s.set_modifier_node_id(order, Parse::NodeId::Invalid);
+      introducer.set_modifier_node_id(order, Parse::NodeId::Invalid);
     }
     }
   }
   }
 
 
-  s.modifier_set.Remove(forbidden);
+  introducer.modifier_set.Remove(forbidden);
 }
 }
 
 
-auto CheckAccessModifiersOnDecl(Context& context, Lex::TokenKind decl_kind,
+auto CheckAccessModifiersOnDecl(Context& context,
+                                DeclIntroducerState& introducer,
+                                Lex::TokenKind decl_kind,
                                 std::optional<SemIR::Inst> parent_scope_inst)
                                 std::optional<SemIR::Inst> parent_scope_inst)
     -> void {
     -> void {
   if (parent_scope_inst) {
   if (parent_scope_inst) {
@@ -71,7 +73,7 @@ auto CheckAccessModifiersOnDecl(Context& context, Lex::TokenKind decl_kind,
       // the parents of the target scope to determine whether we're at file
       // the parents of the target scope to determine whether we're at file
       // scope.
       // scope.
       ForbidModifiersOnDecl(
       ForbidModifiersOnDecl(
-          context, KeywordModifierSet::Protected, decl_kind,
+          context, introducer, KeywordModifierSet::Protected, decl_kind,
           " at file scope, `protected` is only allowed on class members");
           " at file scope, `protected` is only allowed on class members");
       return;
       return;
     }
     }
@@ -83,15 +85,17 @@ auto CheckAccessModifiersOnDecl(Context& context, Lex::TokenKind decl_kind,
   }
   }
 
 
   // Otherwise neither `private` nor `protected` allowed.
   // Otherwise neither `private` nor `protected` allowed.
-  ForbidModifiersOnDecl(context, KeywordModifierSet::Protected, decl_kind,
+  ForbidModifiersOnDecl(context, introducer, KeywordModifierSet::Protected,
+                        decl_kind,
                         ", `protected` is only allowed on class members");
                         ", `protected` is only allowed on class members");
   ForbidModifiersOnDecl(
   ForbidModifiersOnDecl(
-      context, KeywordModifierSet::Private, decl_kind,
+      context, introducer, KeywordModifierSet::Private, decl_kind,
       ", `private` is only allowed on class members and at file scope");
       ", `private` is only allowed on class members and at file scope");
 }
 }
 
 
 auto CheckMethodModifiersOnFunction(
 auto CheckMethodModifiersOnFunction(
-    Context& context, SemIR::InstId parent_scope_inst_id,
+    Context& context, DeclIntroducerState& introducer,
+    SemIR::InstId parent_scope_inst_id,
     std::optional<SemIR::Inst> parent_scope_inst) -> void {
     std::optional<SemIR::Inst> parent_scope_inst) -> void {
   const Lex::TokenKind decl_kind = Lex::TokenKind::Fn;
   const Lex::TokenKind decl_kind = Lex::TokenKind::Fn;
   if (parent_scope_inst) {
   if (parent_scope_inst) {
@@ -99,12 +103,14 @@ auto CheckMethodModifiersOnFunction(
       auto inheritance_kind =
       auto inheritance_kind =
           context.classes().Get(class_decl->class_id).inheritance_kind;
           context.classes().Get(class_decl->class_id).inheritance_kind;
       if (inheritance_kind == SemIR::Class::Final) {
       if (inheritance_kind == SemIR::Class::Final) {
-        ForbidModifiersOnDecl(context, KeywordModifierSet::Virtual, decl_kind,
+        ForbidModifiersOnDecl(context, introducer, KeywordModifierSet::Virtual,
+                              decl_kind,
                               " in a non-abstract non-base `class` definition",
                               " in a non-abstract non-base `class` definition",
                               context.insts().GetLocId(parent_scope_inst_id));
                               context.insts().GetLocId(parent_scope_inst_id));
       }
       }
       if (inheritance_kind != SemIR::Class::Abstract) {
       if (inheritance_kind != SemIR::Class::Abstract) {
-        ForbidModifiersOnDecl(context, KeywordModifierSet::Abstract, decl_kind,
+        ForbidModifiersOnDecl(context, introducer, KeywordModifierSet::Abstract,
+                              decl_kind,
                               " in a non-abstract `class` definition",
                               " in a non-abstract `class` definition",
                               context.insts().GetLocId(parent_scope_inst_id));
                               context.insts().GetLocId(parent_scope_inst_id));
       }
       }
@@ -112,32 +118,34 @@ auto CheckMethodModifiersOnFunction(
     }
     }
   }
   }
 
 
-  ForbidModifiersOnDecl(context, KeywordModifierSet::Method, decl_kind,
-                        " outside of a class");
+  ForbidModifiersOnDecl(context, introducer, KeywordModifierSet::Method,
+                        decl_kind, " outside of a class");
 }
 }
 
 
-auto RestrictExternModifierOnDecl(Context& context, Lex::TokenKind decl_kind,
+auto RestrictExternModifierOnDecl(Context& context,
+                                  DeclIntroducerState& introducer,
+                                  Lex::TokenKind decl_kind,
                                   std::optional<SemIR::Inst> parent_scope_inst,
                                   std::optional<SemIR::Inst> parent_scope_inst,
                                   bool is_definition) -> void {
                                   bool is_definition) -> void {
   if (is_definition) {
   if (is_definition) {
-    ForbidModifiersOnDecl(context, KeywordModifierSet::Extern, decl_kind,
-                          " that provides a definition");
+    ForbidModifiersOnDecl(context, introducer, KeywordModifierSet::Extern,
+                          decl_kind, " that provides a definition");
   }
   }
   if (parent_scope_inst && !parent_scope_inst->Is<SemIR::Namespace>()) {
   if (parent_scope_inst && !parent_scope_inst->Is<SemIR::Namespace>()) {
-    ForbidModifiersOnDecl(context, KeywordModifierSet::Extern, decl_kind,
-                          " that is a member");
+    ForbidModifiersOnDecl(context, introducer, KeywordModifierSet::Extern,
+                          decl_kind, " that is a member");
   }
   }
 }
 }
 
 
 auto RequireDefaultFinalOnlyInInterfaces(
 auto RequireDefaultFinalOnlyInInterfaces(
-    Context& context, Lex::TokenKind decl_kind,
+    Context& context, DeclIntroducerState& introducer, Lex::TokenKind decl_kind,
     std::optional<SemIR::Inst> parent_scope_inst) -> void {
     std::optional<SemIR::Inst> parent_scope_inst) -> void {
   if (parent_scope_inst && parent_scope_inst->Is<SemIR::InterfaceDecl>()) {
   if (parent_scope_inst && parent_scope_inst->Is<SemIR::InterfaceDecl>()) {
     // Both `default` and `final` allowed in an interface definition.
     // Both `default` and `final` allowed in an interface definition.
     return;
     return;
   }
   }
-  ForbidModifiersOnDecl(context, KeywordModifierSet::Interface, decl_kind,
-                        " outside of an interface");
+  ForbidModifiersOnDecl(context, introducer, KeywordModifierSet::Interface,
+                        decl_kind, " outside of an interface");
 }
 }
 
 
 }  // namespace Carbon::Check
 }  // namespace Carbon::Check

+ 17 - 11
toolchain/check/modifiers.h

@@ -10,11 +10,12 @@
 namespace Carbon::Check {
 namespace Carbon::Check {
 
 
 // Reports a diagnostic if access control modifiers on this are not allowed for
 // Reports a diagnostic if access control modifiers on this are not allowed for
-// a declaration in `parent_scope_inst`, and updates the declaration state in
-// `context`.
+// a declaration in `parent_scope_inst`, and updates `introducer`.
 //
 //
 // `parent_scope_inst` may be nullopt for a declaration in a block scope.
 // `parent_scope_inst` may be nullopt for a declaration in a block scope.
-auto CheckAccessModifiersOnDecl(Context& context, Lex::TokenKind decl_kind,
+auto CheckAccessModifiersOnDecl(Context& context,
+                                DeclIntroducerState& introducer,
+                                Lex::TokenKind decl_kind,
                                 std::optional<SemIR::Inst> parent_scope_inst)
                                 std::optional<SemIR::Inst> parent_scope_inst)
     -> void;
     -> void;
 
 
@@ -24,25 +25,28 @@ auto CheckAccessModifiersOnDecl(Context& context, Lex::TokenKind decl_kind,
 //
 //
 // `parent_scope_inst` may be nullopt for a declaration in a block scope.
 // `parent_scope_inst` may be nullopt for a declaration in a block scope.
 auto CheckMethodModifiersOnFunction(
 auto CheckMethodModifiersOnFunction(
-    Context& context, SemIR::InstId parent_scope_inst_id,
+    Context& context, DeclIntroducerState& introducer,
+    SemIR::InstId parent_scope_inst_id,
     std::optional<SemIR::Inst> parent_scope_inst) -> void;
     std::optional<SemIR::Inst> parent_scope_inst) -> void;
 
 
 // Like `LimitModifiersOnDecl`, except says which modifiers are forbidden, and a
 // Like `LimitModifiersOnDecl`, except says which modifiers are forbidden, and a
 // `context_string` (and optional `context_loc_id`) specifying the context in
 // `context_string` (and optional `context_loc_id`) specifying the context in
 // which those modifiers are forbidden.
 // which those modifiers are forbidden.
 // TODO: Take another look at diagnostic phrasing for callers.
 // TODO: Take another look at diagnostic phrasing for callers.
-auto ForbidModifiersOnDecl(Context& context, KeywordModifierSet forbidden,
+auto ForbidModifiersOnDecl(Context& context, DeclIntroducerState& introducer,
+                           KeywordModifierSet forbidden,
                            Lex::TokenKind decl_kind,
                            Lex::TokenKind decl_kind,
                            llvm::StringRef context_string,
                            llvm::StringRef context_string,
                            SemIR::LocId context_loc_id = SemIR::LocId::Invalid)
                            SemIR::LocId context_loc_id = SemIR::LocId::Invalid)
     -> void;
     -> void;
 
 
 // Reports a diagnostic (using `decl_kind`) if modifiers on this declaration are
 // Reports a diagnostic (using `decl_kind`) if modifiers on this declaration are
-// not in `allowed`. Updates the declaration state in
-// `context.decl_state_stack()`.
-inline auto LimitModifiersOnDecl(Context& context, KeywordModifierSet allowed,
+// not in `allowed`. Updates `introducer`.
+inline auto LimitModifiersOnDecl(Context& context,
+                                 DeclIntroducerState& introducer,
+                                 KeywordModifierSet allowed,
                                  Lex::TokenKind decl_kind) -> void {
                                  Lex::TokenKind decl_kind) -> void {
-  ForbidModifiersOnDecl(context, ~allowed, decl_kind, "");
+  ForbidModifiersOnDecl(context, introducer, ~allowed, decl_kind, "");
 }
 }
 
 
 // Restricts the `extern` modifier to only be used on namespace-scoped
 // Restricts the `extern` modifier to only be used on namespace-scoped
@@ -51,7 +55,9 @@ inline auto LimitModifiersOnDecl(Context& context, KeywordModifierSet allowed,
 // - `extern` on a scoped entity.
 // - `extern` on a scoped entity.
 //
 //
 // `parent_scope_inst` may be nullopt for a declaration in a block scope.
 // `parent_scope_inst` may be nullopt for a declaration in a block scope.
-auto RestrictExternModifierOnDecl(Context& context, Lex::TokenKind decl_kind,
+auto RestrictExternModifierOnDecl(Context& context,
+                                  DeclIntroducerState& introducer,
+                                  Lex::TokenKind decl_kind,
                                   std::optional<SemIR::Inst> parent_scope_inst,
                                   std::optional<SemIR::Inst> parent_scope_inst,
                                   bool is_definition) -> void;
                                   bool is_definition) -> void;
 
 
@@ -61,7 +67,7 @@ auto RestrictExternModifierOnDecl(Context& context, Lex::TokenKind decl_kind,
 //
 //
 // `parent_scope_inst` may be nullopt for a declaration in a block scope.
 // `parent_scope_inst` may be nullopt for a declaration in a block scope.
 auto RequireDefaultFinalOnlyInInterfaces(
 auto RequireDefaultFinalOnlyInInterfaces(
-    Context& context, Lex::TokenKind decl_kind,
+    Context& context, DeclIntroducerState& introducer, Lex::TokenKind decl_kind,
     std::optional<SemIR::Inst> parent_scope_inst) -> void;
     std::optional<SemIR::Inst> parent_scope_inst) -> void;
 
 
 }  // namespace Carbon::Check
 }  // namespace Carbon::Check