diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index b97e0a80fe6c9..00a684f296325 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -1755,6 +1755,8 @@ fn check_variances_for_type_defn<'tcx>(
             .collect::<FxHashSet<_>>()
     });
 
+    let ty_generics = tcx.generics_of(item.owner_id);
+
     for (index, _) in variances.iter().enumerate() {
         let parameter = Parameter(index as u32);
 
@@ -1762,13 +1764,27 @@ fn check_variances_for_type_defn<'tcx>(
             continue;
         }
 
-        let param = &hir_generics.params[index];
+        let ty_param = &ty_generics.params[index];
+        let hir_param = &hir_generics.params[index];
+
+        if ty_param.def_id != hir_param.def_id.into() {
+            // valid programs always have lifetimes before types in the generic parameter list
+            // ty_generics are normalized to be in this required order, and variances are built
+            // from ty generics, not from hir generics. but we need hir generics to get
+            // a span out
+            //
+            // if they aren't in the same order, then the user has written invalid code, and already
+            // got an error about it (or I'm wrong about this)
+            tcx.sess
+                .delay_span_bug(hir_param.span, "hir generics and ty generics in different order");
+            continue;
+        }
 
-        match param.name {
+        match hir_param.name {
             hir::ParamName::Error => {}
             _ => {
                 let has_explicit_bounds = explicitly_bounded_params.contains(&parameter);
-                report_bivariance(tcx, param, has_explicit_bounds);
+                report_bivariance(tcx, hir_param, has_explicit_bounds);
             }
         }
     }
diff --git a/tests/ui/generics/issue-83556.rs b/tests/ui/generics/issue-83556.rs
new file mode 100644
index 0000000000000..7cea1c8631fe9
--- /dev/null
+++ b/tests/ui/generics/issue-83556.rs
@@ -0,0 +1,4 @@
+struct Foo<T, 'a>(&'a ());
+//~^ ERROR lifetime parameters must be declared prior to
+
+fn main() {}
diff --git a/tests/ui/generics/issue-83556.stderr b/tests/ui/generics/issue-83556.stderr
new file mode 100644
index 0000000000000..93affaffe60c3
--- /dev/null
+++ b/tests/ui/generics/issue-83556.stderr
@@ -0,0 +1,8 @@
+error: lifetime parameters must be declared prior to type and const parameters
+  --> $DIR/issue-83556.rs:1:15
+   |
+LL | struct Foo<T, 'a>(&'a ());
+   |           ----^^- help: reorder the parameters: lifetimes, then consts and types: `<'a, T>`
+
+error: aborting due to previous error
+