From 8cd12bf8fa1d40edaf580dd112c9b44c4dd8dcd3 Mon Sep 17 00:00:00 2001
From: lcnr <rust@lcnr.de>
Date: Thu, 10 Apr 2025 20:23:43 +0200
Subject: [PATCH] check types of const param default

---
 .../rustc_hir_analysis/src/check/wfcheck.rs   | 33 +++++++++++++++++++
 .../defaults/concrete-const-param-type.rs     | 13 ++++++++
 .../defaults/concrete-const-param-type.stderr | 25 ++++++++++++++
 3 files changed, 71 insertions(+)
 create mode 100644 tests/ui/const-generics/defaults/concrete-const-param-type.rs
 create mode 100644 tests/ui/const-generics/defaults/concrete-const-param-type.stderr

diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index 33d5a86beb3b0..8bb215b6b2793 100644
--- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -1488,6 +1488,39 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
                         .then(|| WellFormedLoc::Ty(param.def_id.expect_local())),
                     default,
                 );
+            } else {
+                // If we've got a generic const parameter we still want to check its
+                // type is correct in case both it and the param type are fully concrete.
+                let GenericArgKind::Const(ct) = default.unpack() else {
+                    continue;
+                };
+
+                let ct_ty = match ct.kind() {
+                    ty::ConstKind::Infer(_)
+                    | ty::ConstKind::Placeholder(_)
+                    | ty::ConstKind::Bound(_, _) => unreachable!(),
+                    ty::ConstKind::Error(_) | ty::ConstKind::Expr(_) => continue,
+                    ty::ConstKind::Value(cv) => cv.ty,
+                    ty::ConstKind::Unevaluated(uv) => {
+                        infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args)
+                    }
+                    ty::ConstKind::Param(param_ct) => param_ct.find_ty_from_env(wfcx.param_env),
+                };
+
+                let param_ty = tcx.type_of(param.def_id).instantiate_identity();
+                if !ct_ty.has_param() && !param_ty.has_param() {
+                    let cause = traits::ObligationCause::new(
+                        tcx.def_span(param.def_id),
+                        wfcx.body_def_id,
+                        ObligationCauseCode::WellFormed(None),
+                    );
+                    wfcx.register_obligation(Obligation::new(
+                        tcx,
+                        cause,
+                        wfcx.param_env,
+                        ty::ClauseKind::ConstArgHasType(ct, param_ty),
+                    ));
+                }
             }
         }
     }
diff --git a/tests/ui/const-generics/defaults/concrete-const-param-type.rs b/tests/ui/const-generics/defaults/concrete-const-param-type.rs
new file mode 100644
index 0000000000000..c411f81192bd6
--- /dev/null
+++ b/tests/ui/const-generics/defaults/concrete-const-param-type.rs
@@ -0,0 +1,13 @@
+#![feature(generic_const_parameter_types, unsized_const_params, adt_const_params)]
+//~^ WARN the feature `generic_const_parameter_types` is incomplete
+//~| WARN the feature `unsized_const_params` is incomplete
+// Make sure that we test the const param type of default const parameters
+// if both the type of the default and the type of the parameter are concrete.
+
+use std::marker::ConstParamTy_;
+
+struct Foo<const N: u32, const M: u64 = N>; //~ ERROR the constant `N` is not of type `u64`
+struct Bar<T: ConstParamTy_, const N: T, const M: u64 = N>(T); // ok
+struct Baz<T: ConstParamTy_, const N: u32, const M: T = N>(T); // ok
+
+fn main() {}
diff --git a/tests/ui/const-generics/defaults/concrete-const-param-type.stderr b/tests/ui/const-generics/defaults/concrete-const-param-type.stderr
new file mode 100644
index 0000000000000..ad077f87e5dfb
--- /dev/null
+++ b/tests/ui/const-generics/defaults/concrete-const-param-type.stderr
@@ -0,0 +1,25 @@
+warning: the feature `generic_const_parameter_types` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/concrete-const-param-type.rs:1:12
+   |
+LL | #![feature(generic_const_parameter_types, unsized_const_params, adt_const_params)]
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #137626 <https://github.com/rust-lang/rust/issues/137626> for more information
+   = note: `#[warn(incomplete_features)]` on by default
+
+warning: the feature `unsized_const_params` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/concrete-const-param-type.rs:1:43
+   |
+LL | #![feature(generic_const_parameter_types, unsized_const_params, adt_const_params)]
+   |                                           ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: see issue #95174 <https://github.com/rust-lang/rust/issues/95174> for more information
+
+error: the constant `N` is not of type `u64`
+  --> $DIR/concrete-const-param-type.rs:9:26
+   |
+LL | struct Foo<const N: u32, const M: u64 = N>;
+   |                          ^^^^^^^^^^^^^^^^ expected `u64`, found `u32`
+
+error: aborting due to 1 previous error; 2 warnings emitted
+