diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs
index c0a059b92aa9f..c7e26013f1cbe 100644
--- a/compiler/rustc_lint/src/levels.rs
+++ b/compiler/rustc_lint/src/levels.rs
@@ -11,7 +11,8 @@ use rustc_middle::hir::map::Map;
 use rustc_middle::lint::LevelAndSource;
 use rustc_middle::lint::LintDiagnosticBuilder;
 use rustc_middle::lint::{
-    struct_lint_level, LintLevelMap, LintLevelSets, LintLevelSource, LintSet,
+    struct_lint_level, LintLevelMap, LintLevelSets, LintLevelSource, LintSet, LintStackIndex,
+    COMMAND_LINE,
 };
 use rustc_middle::ty::query::Providers;
 use rustc_middle::ty::TyCtxt;
@@ -50,15 +51,15 @@ fn lint_levels(tcx: TyCtxt<'_>, (): ()) -> LintLevelMap {
 pub struct LintLevelsBuilder<'s> {
     sess: &'s Session,
     sets: LintLevelSets,
-    id_to_set: FxHashMap<HirId, u32>,
-    cur: u32,
+    id_to_set: FxHashMap<HirId, LintStackIndex>,
+    cur: LintStackIndex,
     warn_about_weird_lints: bool,
     store: &'s LintStore,
     crate_attrs: &'s [ast::Attribute],
 }
 
 pub struct BuilderPush {
-    prev: u32,
+    prev: LintStackIndex,
     pub changed: bool,
 }
 
@@ -72,7 +73,7 @@ impl<'s> LintLevelsBuilder<'s> {
         let mut builder = LintLevelsBuilder {
             sess,
             sets: LintLevelSets::new(),
-            cur: 0,
+            cur: COMMAND_LINE,
             id_to_set: Default::default(),
             warn_about_weird_lints,
             store,
@@ -120,7 +121,7 @@ impl<'s> LintLevelsBuilder<'s> {
             }
         }
 
-        self.sets.list.push(LintSet::CommandLine { specs });
+        self.cur = self.sets.list.push(LintSet { specs, parent: COMMAND_LINE });
     }
 
     /// Attempts to insert the `id` to `level_src` map entry. If unsuccessful
@@ -523,8 +524,7 @@ impl<'s> LintLevelsBuilder<'s> {
 
         let prev = self.cur;
         if !specs.is_empty() {
-            self.cur = self.sets.list.len() as u32;
-            self.sets.list.push(LintSet::Node { specs, parent: prev });
+            self.cur = self.sets.list.push(LintSet { specs, parent: prev });
         }
 
         BuilderPush { prev, changed: prev != self.cur }
diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs
index 7ac7720a7c563..560581cf641a5 100644
--- a/compiler/rustc_middle/src/lint.rs
+++ b/compiler/rustc_middle/src/lint.rs
@@ -5,6 +5,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_errors::{DiagnosticBuilder, DiagnosticId};
 use rustc_hir::HirId;
+use rustc_index::vec::IndexVec;
 use rustc_session::lint::{
     builtin::{self, FORBIDDEN_LINT_GROUPS},
     FutureIncompatibilityReason, Level, Lint, LintId,
@@ -51,35 +52,37 @@ impl LintLevelSource {
 /// A tuple of a lint level and its source.
 pub type LevelAndSource = (Level, LintLevelSource);
 
-#[derive(Debug)]
+#[derive(Debug, HashStable)]
 pub struct LintLevelSets {
-    pub list: Vec<LintSet>,
+    pub list: IndexVec<LintStackIndex, LintSet>,
     pub lint_cap: Level,
 }
 
-#[derive(Debug)]
-pub enum LintSet {
-    CommandLine {
-        // -A,-W,-D flags, a `Symbol` for the flag itself and `Level` for which
-        // flag.
-        specs: FxHashMap<LintId, LevelAndSource>,
-    },
-
-    Node {
-        specs: FxHashMap<LintId, LevelAndSource>,
-        parent: u32,
-    },
+rustc_index::newtype_index! {
+    #[derive(HashStable)]
+    pub struct LintStackIndex {
+        const COMMAND_LINE = 0,
+    }
+}
+
+#[derive(Debug, HashStable)]
+pub struct LintSet {
+    // -A,-W,-D flags, a `Symbol` for the flag itself and `Level` for which
+    // flag.
+    pub specs: FxHashMap<LintId, LevelAndSource>,
+
+    pub parent: LintStackIndex,
 }
 
 impl LintLevelSets {
     pub fn new() -> Self {
-        LintLevelSets { list: Vec::new(), lint_cap: Level::Forbid }
+        LintLevelSets { list: IndexVec::new(), lint_cap: Level::Forbid }
     }
 
     pub fn get_lint_level(
         &self,
         lint: &'static Lint,
-        idx: u32,
+        idx: LintStackIndex,
         aux: Option<&FxHashMap<LintId, LevelAndSource>>,
         sess: &Session,
     ) -> LevelAndSource {
@@ -122,7 +125,7 @@ impl LintLevelSets {
     pub fn get_lint_id_level(
         &self,
         id: LintId,
-        mut idx: u32,
+        mut idx: LintStackIndex,
         aux: Option<&FxHashMap<LintId, LevelAndSource>>,
     ) -> (Option<Level>, LintLevelSource) {
         if let Some(specs) = aux {
@@ -131,20 +134,14 @@ impl LintLevelSets {
             }
         }
         loop {
-            match self.list[idx as usize] {
-                LintSet::CommandLine { ref specs } => {
-                    if let Some(&(level, src)) = specs.get(&id) {
-                        return (Some(level), src);
-                    }
-                    return (None, LintLevelSource::Default);
-                }
-                LintSet::Node { ref specs, parent } => {
-                    if let Some(&(level, src)) = specs.get(&id) {
-                        return (Some(level), src);
-                    }
-                    idx = parent;
-                }
+            let LintSet { ref specs, parent } = self.list[idx];
+            if let Some(&(level, src)) = specs.get(&id) {
+                return (Some(level), src);
+            }
+            if idx == COMMAND_LINE {
+                return (None, LintLevelSource::Default);
             }
+            idx = parent;
         }
     }
 }
@@ -152,7 +149,7 @@ impl LintLevelSets {
 #[derive(Debug)]
 pub struct LintLevelMap {
     pub sets: LintLevelSets,
-    pub id_to_set: FxHashMap<HirId, u32>,
+    pub id_to_set: FxHashMap<HirId, LintStackIndex>,
 }
 
 impl LintLevelMap {
@@ -180,29 +177,7 @@ impl<'a> HashStable<StableHashingContext<'a>> for LintLevelMap {
 
         id_to_set.hash_stable(hcx, hasher);
 
-        let LintLevelSets { ref list, lint_cap } = *sets;
-
-        lint_cap.hash_stable(hcx, hasher);
-
-        hcx.while_hashing_spans(true, |hcx| {
-            list.len().hash_stable(hcx, hasher);
-
-            // We are working under the assumption here that the list of
-            // lint-sets is built in a deterministic order.
-            for lint_set in list {
-                ::std::mem::discriminant(lint_set).hash_stable(hcx, hasher);
-
-                match *lint_set {
-                    LintSet::CommandLine { ref specs } => {
-                        specs.hash_stable(hcx, hasher);
-                    }
-                    LintSet::Node { ref specs, parent } => {
-                        specs.hash_stable(hcx, hasher);
-                        parent.hash_stable(hcx, hasher);
-                    }
-                }
-            }
-        })
+        hcx.while_hashing_spans(true, |hcx| sets.hash_stable(hcx, hasher))
     }
 }