Always check ConstArgHasType even when otherwise ignoring

This commit is contained in:
khyperia
2026-03-18 11:48:09 +01:00
parent a3903b1e6f
commit e9c273e377
8 changed files with 524 additions and 5 deletions
+26 -2
View File
@@ -952,10 +952,10 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(),
tcx.ensure_ok().type_of(def_id);
tcx.ensure_ok().predicates_of(def_id);
check_type_alias_type_params_are_used(tcx, def_id);
let ty = tcx.type_of(def_id).instantiate_identity();
let span = tcx.def_span(def_id);
if tcx.type_alias_is_lazy(def_id) {
res = res.and(enter_wf_checking_ctxt(tcx, def_id, |wfcx| {
let ty = tcx.type_of(def_id).instantiate_identity();
let span = tcx.def_span(def_id);
let item_ty = wfcx.deeply_normalize(span, Some(WellFormedLoc::Ty(def_id)), ty);
wfcx.register_wf_obligation(
span,
@@ -966,6 +966,30 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(),
Ok(())
}));
check_variances_for_type_defn(tcx, def_id);
} else {
res = res.and(enter_wf_checking_ctxt(tcx, def_id, |wfcx| {
// HACK: We sometimes incidentally check that const arguments have the correct
// type as a side effect of the anon const desugaring. To make this "consistent"
// for users we explicitly check `ConstArgHasType` clauses so that const args
// that don't go through an anon const still have their types checked.
//
// We use the unnormalized type as this mirrors the behaviour that we previously
// would have had when all const arguments were anon consts.
//
// Changing this to normalized obligations is a breaking change:
// `type Bar = [(); panic!()];` would become an error
if let Some(unnormalized_obligations) = wfcx.unnormalized_obligations(span, ty)
{
let filtered_obligations =
unnormalized_obligations.into_iter().filter(|o| {
matches!(o.predicate.kind().skip_binder(),
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, _))
if matches!(ct.kind(), ty::ConstKind::Param(..)))
});
wfcx.ocx.register_obligations(filtered_obligations)
}
Ok(())
}));
}
// Only `Node::Item` and `Node::ForeignItem` still have HIR based
@@ -15,6 +15,7 @@
use rustc_hir::{AmbigArg, ItemKind, find_attr};
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
use rustc_infer::infer::{self, InferCtxt, SubregionOrigin, TyCtxtInferExt};
use rustc_infer::traits::PredicateObligations;
use rustc_lint_defs::builtin::SHADOWING_SUPERTRAIT_ITEMS;
use rustc_macros::Diagnostic;
use rustc_middle::mir::interpret::ErrorHandled;
@@ -124,6 +125,20 @@ pub(super) fn register_wf_obligation(
ty::ClauseKind::WellFormed(term),
));
}
pub(super) fn unnormalized_obligations(
&self,
span: Span,
ty: Ty<'tcx>,
) -> Option<PredicateObligations<'tcx>> {
traits::wf::unnormalized_obligations(
self.ocx.infcx,
self.param_env,
ty.into(),
span,
self.body_def_id,
)
}
}
pub(super) fn enter_wf_checking_ctxt<'tcx, F>(
@@ -140,7 +155,12 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>(
let mut wfcx = WfCheckingCtxt { ocx, body_def_id, param_env };
if !tcx.features().trivial_bounds() {
// As of now, bounds are only checked on lazy type aliases, they're ignored for most type
// aliases. So, only check for false global bounds if we're not ignoring bounds altogether.
let ignore_bounds =
tcx.def_kind(body_def_id) == DefKind::TyAlias && !tcx.type_alias_is_lazy(body_def_id);
if !ignore_bounds && !tcx.features().trivial_bounds() {
wfcx.check_false_global_bounds()
}
f(&mut wfcx)?;
@@ -942,14 +942,53 @@ fn visit_ty(&mut self, t: Ty<'tcx>) -> Self::Result {
// FIXME(#27579) RFC also considers adding trait
// obligations that don't refer to Self and
// checking those
if let Some(principal) = data.principal_def_id() {
if let Some(principal) = data.principal() {
let principal_def_id = principal.skip_binder().def_id;
self.out.push(traits::Obligation::with_depth(
tcx,
self.cause(ObligationCauseCode::WellFormed(None)),
self.recursion_depth,
self.param_env,
ty::Binder::dummy(ty::PredicateKind::DynCompatible(principal)),
ty::Binder::dummy(ty::PredicateKind::DynCompatible(principal_def_id)),
));
// For the most part we don't add wf predicates corresponding to
// the trait ref's generic arguments which allows code like this
// to compile:
// ```rust
// trait Trait<T: Sized> {}
// fn foo(_: &dyn Trait<[u32]>) {}
// ```
//
// However, we sometimes incidentally check that const arguments
// have the correct type as a side effect of the anon const
// desugaring. To make this "consistent" for users we explicitly
// check `ConstArgHasType` clauses so that const args that don't
// go through an anon const still have their types checked.
//
// See also: https://rustc-dev-guide.rust-lang.org/const-generics.html
let args = principal.skip_binder().with_self_ty(self.tcx(), t).args;
let obligations =
self.nominal_obligations(principal_def_id, args).into_iter().filter(|o| {
let kind = o.predicate.kind().skip_binder();
match kind {
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(
ct,
_,
)) if matches!(ct.kind(), ty::ConstKind::Param(..)) => {
// ConstArgHasType clauses are not higher kinded. Assert as
// such so we can fix this up if that ever changes.
assert!(o.predicate.kind().bound_vars().is_empty());
// In stable rust, variables from the trait object binder
// cannot be referenced by a ConstArgHasType clause. However,
// under `generic_const_parameter_types`, it can. Ignore those
// predicates for now, to not have HKT-ConstArgHasTypes.
!kind.has_escaping_bound_vars()
}
_ => false,
}
});
self.out.extend(obligations);
}
if !t.has_escaping_bound_vars() {
@@ -0,0 +1,133 @@
error: the constant `B` is not of type `usize`
--> $DIR/check_const_arg_type_in_free_alias.rs:15:1
|
LL | type ArrLen<const B: bool> = [(); B];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool`
|
= note: the length of array `[(); B]` must be type `usize`
error: the constant `B` is not of type `usize`
--> $DIR/check_const_arg_type_in_free_alias.rs:19:1
|
LL | type ConstArg<const B: bool> = Foo<B>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool`
|
note: required by a const generic parameter in `Foo`
--> $DIR/check_const_arg_type_in_free_alias.rs:13:12
|
LL | struct Foo<const N: usize>;
| ^^^^^^^^^^^^^^ required by this const generic parameter in `Foo`
error: the constant `B` is not of type `usize`
--> $DIR/check_const_arg_type_in_free_alias.rs:32:1
|
LL | type Alias<const B: bool> = <() as IdentityWithUnused<B>>::This;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool`
|
note: required by a const generic parameter in `IdentityWithUnused`
--> $DIR/check_const_arg_type_in_free_alias.rs:24:26
|
LL | trait IdentityWithUnused<const N: usize> {
| ^^^^^^^^^^^^^^ required by this const generic parameter in `IdentityWithUnused`
error: the constant `B` is not of type `usize`
--> $DIR/check_const_arg_type_in_free_alias.rs:38:1
|
LL | type UseFree<const B: bool> = Free<B>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool`
|
= note: the length of array `[(); B]` must be type `usize`
error: the constant `B` is not of type `usize`
--> $DIR/check_const_arg_type_in_free_alias.rs:58:1
|
LL | type IndirectArr<const B: bool> = Wrap<Wrap<[(); B]>>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool`
|
= note: the length of array `[(); B]` must be type `usize`
error: the constant `B` is not of type `usize`
--> $DIR/check_const_arg_type_in_free_alias.rs:62:1
|
LL | type IndirectConstArg<const B: bool> = Wrap<Wrap<Foo<B>>>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool`
|
note: required by a const generic parameter in `Foo`
--> $DIR/check_const_arg_type_in_free_alias.rs:13:12
|
LL | struct Foo<const N: usize>;
| ^^^^^^^^^^^^^^ required by this const generic parameter in `Foo`
error[E0308]: mismatched types
--> $DIR/check_const_arg_type_in_free_alias.rs:17:24
|
LL | type AnonArrLen = [(); true];
| ^^^^ expected `usize`, found `bool`
|
= note: array length can only be `usize`
error[E0308]: mismatched types
--> $DIR/check_const_arg_type_in_free_alias.rs:21:25
|
LL | type AnonConstArg = Foo<true>;
| ^^^^ expected `usize`, found `bool`
|
note: expected because of the type of the const parameter
--> $DIR/check_const_arg_type_in_free_alias.rs:13:12
|
LL | struct Foo<const N: usize>;
| ^^^^^^^^^^^^^^
error[E0308]: mismatched types
--> $DIR/check_const_arg_type_in_free_alias.rs:34:44
|
LL | type AnonAlias = <() as IdentityWithUnused<true>>::This;
| ^^^^ expected `usize`, found `bool`
error[E0308]: mismatched types
--> $DIR/check_const_arg_type_in_free_alias.rs:40:25
|
LL | type AnonUseFree = Free<true>;
| ^^^^ expected `usize`, found `bool`
|
note: expected because of the type of the const parameter
--> $DIR/check_const_arg_type_in_free_alias.rs:37:11
|
LL | type Free<const N: usize> = [(); N];
| ^^^^^^^^^^^^^^
error[E0308]: mismatched types
--> $DIR/check_const_arg_type_in_free_alias.rs:53:45
|
LL | type AnonUseFreeIndirectlyCorrect = UseFree<1_usize>;
| ^^^^^^^ expected `bool`, found `usize`
|
note: expected because of the type of the const parameter
--> $DIR/check_const_arg_type_in_free_alias.rs:38:14
|
LL | type UseFree<const B: bool> = Free<B>;
| ^^^^^^^^^^^^^
error[E0308]: mismatched types
--> $DIR/check_const_arg_type_in_free_alias.rs:60:39
|
LL | type AnonIndirectArr = Wrap<Wrap<[(); true]>>;
| ^^^^ expected `usize`, found `bool`
|
= note: array length can only be `usize`
error[E0308]: mismatched types
--> $DIR/check_const_arg_type_in_free_alias.rs:64:43
|
LL | type AnonIndirectConstArg = Wrap<Wrap<Foo<true>>>;
| ^^^^ expected `usize`, found `bool`
|
note: expected because of the type of the const parameter
--> $DIR/check_const_arg_type_in_free_alias.rs:13:12
|
LL | struct Foo<const N: usize>;
| ^^^^^^^^^^^^^^
error: aborting due to 13 previous errors
For more information about this error, try `rustc --explain E0308`.
@@ -0,0 +1,151 @@
error: the constant `B` is not of type `usize`
--> $DIR/check_const_arg_type_in_free_alias.rs:15:1
|
LL | type ArrLen<const B: bool> = [(); B];
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool`
|
= note: the length of array `[(); B]` must be type `usize`
error[E0308]: mismatched types
--> $DIR/check_const_arg_type_in_free_alias.rs:17:24
|
LL | type AnonArrLen = [(); true];
| ^^^^ expected `usize`, found `bool`
|
= note: array length can only be `usize`
error: the constant `B` is not of type `usize`
--> $DIR/check_const_arg_type_in_free_alias.rs:19:1
|
LL | type ConstArg<const B: bool> = Foo<B>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool`
|
note: required by a const generic parameter in `Foo`
--> $DIR/check_const_arg_type_in_free_alias.rs:13:12
|
LL | struct Foo<const N: usize>;
| ^^^^^^^^^^^^^^ required by this const generic parameter in `Foo`
error[E0308]: mismatched types
--> $DIR/check_const_arg_type_in_free_alias.rs:21:25
|
LL | type AnonConstArg = Foo<true>;
| ^^^^ expected `usize`, found `bool`
|
note: expected because of the type of the const parameter
--> $DIR/check_const_arg_type_in_free_alias.rs:13:12
|
LL | struct Foo<const N: usize>;
| ^^^^^^^^^^^^^^
error: the constant `B` is not of type `usize`
--> $DIR/check_const_arg_type_in_free_alias.rs:32:1
|
LL | type Alias<const B: bool> = <() as IdentityWithUnused<B>>::This;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool`
|
note: required for `()` to implement `IdentityWithUnused<B>`
--> $DIR/check_const_arg_type_in_free_alias.rs:28:25
|
LL | impl<T, const N: usize> IdentityWithUnused<N> for T {
| -------------- ^^^^^^^^^^^^^^^^^^^^^ ^
| |
| unsatisfied trait bound introduced here
error[E0308]: mismatched types
--> $DIR/check_const_arg_type_in_free_alias.rs:34:44
|
LL | type AnonAlias = <() as IdentityWithUnused<true>>::This;
| ^^^^ expected `usize`, found `bool`
error: the constant `B` is not of type `usize`
--> $DIR/check_const_arg_type_in_free_alias.rs:38:1
|
LL | type UseFree<const B: bool> = Free<B>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool`
|
note: required by a bound on the type alias `Free`
--> $DIR/check_const_arg_type_in_free_alias.rs:37:11
|
LL | type Free<const N: usize> = [(); N];
| ^^^^^^^^^^^^^^ required by this bound
error[E0308]: mismatched types
--> $DIR/check_const_arg_type_in_free_alias.rs:40:25
|
LL | type AnonUseFree = Free<true>;
| ^^^^ expected `usize`, found `bool`
|
note: expected because of the type of the const parameter
--> $DIR/check_const_arg_type_in_free_alias.rs:37:11
|
LL | type Free<const N: usize> = [(); N];
| ^^^^^^^^^^^^^^
error: the constant `N` is not of type `bool`
--> $DIR/check_const_arg_type_in_free_alias.rs:51:1
|
LL | type UseFreeIndirectlyCorrect<const N: usize> = UseFree<N>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `usize`
|
note: required by a bound on the type alias `UseFree`
--> $DIR/check_const_arg_type_in_free_alias.rs:38:14
|
LL | type UseFree<const B: bool> = Free<B>;
| ^^^^^^^^^^^^^ required by this bound
error[E0308]: mismatched types
--> $DIR/check_const_arg_type_in_free_alias.rs:53:45
|
LL | type AnonUseFreeIndirectlyCorrect = UseFree<1_usize>;
| ^^^^^^^ expected `bool`, found `usize`
|
note: expected because of the type of the const parameter
--> $DIR/check_const_arg_type_in_free_alias.rs:38:14
|
LL | type UseFree<const B: bool> = Free<B>;
| ^^^^^^^^^^^^^
error: the constant `B` is not of type `usize`
--> $DIR/check_const_arg_type_in_free_alias.rs:58:1
|
LL | type IndirectArr<const B: bool> = Wrap<Wrap<[(); B]>>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool`
|
= note: the length of array `[(); B]` must be type `usize`
error[E0308]: mismatched types
--> $DIR/check_const_arg_type_in_free_alias.rs:60:39
|
LL | type AnonIndirectArr = Wrap<Wrap<[(); true]>>;
| ^^^^ expected `usize`, found `bool`
|
= note: array length can only be `usize`
error: the constant `B` is not of type `usize`
--> $DIR/check_const_arg_type_in_free_alias.rs:62:1
|
LL | type IndirectConstArg<const B: bool> = Wrap<Wrap<Foo<B>>>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool`
|
note: required by a const generic parameter in `Foo`
--> $DIR/check_const_arg_type_in_free_alias.rs:13:12
|
LL | struct Foo<const N: usize>;
| ^^^^^^^^^^^^^^ required by this const generic parameter in `Foo`
error[E0308]: mismatched types
--> $DIR/check_const_arg_type_in_free_alias.rs:64:43
|
LL | type AnonIndirectConstArg = Wrap<Wrap<Foo<true>>>;
| ^^^^ expected `usize`, found `bool`
|
note: expected because of the type of the const parameter
--> $DIR/check_const_arg_type_in_free_alias.rs:13:12
|
LL | struct Foo<const N: usize>;
| ^^^^^^^^^^^^^^
error: aborting due to 14 previous errors
For more information about this error, try `rustc --explain E0308`.
@@ -0,0 +1,67 @@
//@ revisions: eager lazy
#![cfg_attr(lazy, feature(lazy_type_alias))]
#![cfg_attr(lazy, expect(incomplete_features))]
// We currently do not check free type aliases for well formedness.
// However, previously we desugared all const arguments to anon
// consts which resulted in us happening to check that they were
// of the correct type. Nowadays we don't necessarily lower to a
// const argument, but to continue erroring on such code we special
// case `ConstArgHasType` clauses to be checked for free type aliases
// even though we ignore the rest of the wf requirements.
struct Foo<const N: usize>;
type ArrLen<const B: bool> = [(); B];
//~^ ERROR: the constant `B` is not of type
type AnonArrLen = [(); true];
//~^ ERROR: mismatched types
type ConstArg<const B: bool> = Foo<B>;
//~^ ERROR: the constant `B` is not of type
type AnonConstArg = Foo<true>;
//~^ ERROR: mismatched types
trait IdentityWithUnused<const N: usize> {
type This;
}
impl<T, const N: usize> IdentityWithUnused<N> for T {
type This = T;
}
type Alias<const B: bool> = <() as IdentityWithUnused<B>>::This;
//~^ ERROR: the constant `B` is not of type
type AnonAlias = <() as IdentityWithUnused<true>>::This;
//~^ ERROR: mismatched types
type Free<const N: usize> = [(); N];
type UseFree<const B: bool> = Free<B>;
//~^ ERROR: the constant `B` is not of type
type AnonUseFree = Free<true>;
//~^ ERROR: mismatched types
// This previously emitted an error before we stopped using
// anon consts. Now, as free aliases don't exist after ty
// lowering, we don't emit an error because we only see `N`
// being used as an argument to an array length.
//
// Free type aliases are not allowed to have unused generic
// parameters so this shouldn't be able to cause code to
// pass that should error.
type UseFreeIndirectlyCorrect<const N: usize> = UseFree<N>;
//[lazy]~^ ERROR: the constant `N` is not of type
type AnonUseFreeIndirectlyCorrect = UseFree<1_usize>;
//~^ ERROR: mismatched types
struct Wrap<T>(T);
type IndirectArr<const B: bool> = Wrap<Wrap<[(); B]>>;
//~^ ERROR: the constant `B` is not of type
type AnonIndirectArr = Wrap<Wrap<[(); true]>>;
//~^ ERROR: mismatched types
type IndirectConstArg<const B: bool> = Wrap<Wrap<Foo<B>>>;
//~^ ERROR: the constant `B` is not of type
type AnonIndirectConstArg = Wrap<Wrap<Foo<true>>>;
//~^ ERROR: mismatched types
fn main() {}
@@ -0,0 +1,36 @@
// We currently do not check trait objects for well formedness.
// However, previously we desugared all const arguments to anon
// consts which resulted in us happening to check that they were
// of the correct type. Nowadays we don't necessarily lower to a
// const argument, but to continue erroring on such code we special
// case `ConstArgHasType` clauses to be checked for trait objects
// even though we ignore the rest of the wf requirements.
trait Object<const N: usize> {}
trait Object2<T> {}
trait Object3<'a: 'static, const N: usize> {}
struct Wrap<T>(T);
fn arg<const B: bool, const N: usize>(
param: &dyn Object<B>,
//~^ ERROR: the constant `B` is not of type
anon: &dyn Object<true>,
//~^ ERROR: mismatched types
) {
}
fn indirect<const B: bool, const N: usize>(
param: &dyn Object2<Wrap<[(); B]>>,
//~^ ERROR: the constant `B` is not of type
anon: &dyn Object2<Wrap<[(); true]>>,
//~^ ERROR: mismatched types
) {
}
// the 'a: 'static bound should *not* have its error reported (at least, until we implement checking
// all wf things for dyn objects)
fn binder<const B: bool>(param: &dyn for<'a> Object3<'a, B>) {}
//~^ ERROR: the constant `B` is not of type
fn main() {}
@@ -0,0 +1,49 @@
error[E0308]: mismatched types
--> $DIR/check_const_arg_type_in_trait_object.rs:18:23
|
LL | anon: &dyn Object<true>,
| ^^^^ expected `usize`, found `bool`
error: the constant `B` is not of type `usize`
--> $DIR/check_const_arg_type_in_trait_object.rs:16:12
|
LL | param: &dyn Object<B>,
| ^^^^^^^^^^^^^^ expected `usize`, found `bool`
|
note: required by a const generic parameter in `Object`
--> $DIR/check_const_arg_type_in_trait_object.rs:9:14
|
LL | trait Object<const N: usize> {}
| ^^^^^^^^^^^^^^ required by this const generic parameter in `Object`
error[E0308]: mismatched types
--> $DIR/check_const_arg_type_in_trait_object.rs:26:34
|
LL | anon: &dyn Object2<Wrap<[(); true]>>,
| ^^^^ expected `usize`, found `bool`
|
= note: array length can only be `usize`
error: the constant `B` is not of type `usize`
--> $DIR/check_const_arg_type_in_trait_object.rs:24:12
|
LL | param: &dyn Object2<Wrap<[(); B]>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool`
|
= note: the length of array `[(); B]` must be type `usize`
error: the constant `B` is not of type `usize`
--> $DIR/check_const_arg_type_in_trait_object.rs:33:33
|
LL | fn binder<const B: bool>(param: &dyn for<'a> Object3<'a, B>) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found `bool`
|
note: required by a const generic parameter in `Object3`
--> $DIR/check_const_arg_type_in_trait_object.rs:11:28
|
LL | trait Object3<'a: 'static, const N: usize> {}
| ^^^^^^^^^^^^^^ required by this const generic parameter in `Object3`
error: aborting due to 5 previous errors
For more information about this error, try `rustc --explain E0308`.