Auto merge of #86580 - BoxyUwU:cgd-subst-ice, r=nikomatsakis
dont provide fwd declared params to cg defaults Fixes #83938 ```rust #![feature(const_evaluatable_checked, const_generics, const_generics_defaults)] #![allow(incomplete_features)] pub struct Bar<const N: usize, const M: usize = { N + 1 }>; pub fn foo<const N1: usize>() -> Bar<N1> { loop {} } fn main() {} ``` This PR makes this code no longer ICE, it was ICE'ing previously because when building substs for `Bar<N1>` we would subst the anon ct: `ConstKind::Unevaluated({N + 1}, substs: [N, M])` with substs of `[N1]`. the anon const has forward declared params supplied though so we end up trying to substitute the provided `M` param which causes the ICE. This PR doesn't handle the predicates of the const so ```rust trait Foo<const N: usize> { const Assoc: usize; } pub struct Bar<const N: usize = { <()>::Assoc }> where (): Foo<N>; ``` Resolves to `<() as Foo<N>>::Assoc` which can allow for using fwd declared params indirectly. ```rust trait Foo<const N: usize> {} struct Bar<const N: usize = { 2 + 3 }> where (): Foo<N>; ``` This code also ICEs under this PR because instantiating the default's predicates causes an ICE as predicates_of contains predicates with fwd declared params PR was briefly discussed [in this zulip thread](https://rust-lang.zulipchat.com/#narrow/stream/260443-project-const-generics/topic/evil.20preds.20in.20param.20env.20.2386580)
This commit is contained in:
commit
d9aa287672
@ -1422,6 +1422,9 @@ pub type Lit = Spanned<LitKind>;
|
||||
/// These are usually found nested inside types (e.g., array lengths)
|
||||
/// or expressions (e.g., repeat counts), and also used to define
|
||||
/// explicit discriminant values for enum variants.
|
||||
///
|
||||
/// You can check if this anon const is a default in a const param
|
||||
/// `const N: usize = { ... }` with `tcx.hir().opt_const_param_default_param_hir_id(..)`
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Encodable, Debug, HashStable_Generic)]
|
||||
pub struct AnonConst {
|
||||
pub hir_id: HirId,
|
||||
|
@ -919,6 +919,19 @@ impl<'hir> Map<'hir> {
|
||||
pub fn node_to_string(&self, id: HirId) -> String {
|
||||
hir_id_to_string(self, id)
|
||||
}
|
||||
|
||||
/// Returns the HirId of `N` in `struct Foo<const N: usize = { ... }>` when
|
||||
/// called with the HirId for the `{ ... }` anon const
|
||||
pub fn opt_const_param_default_param_hir_id(&self, anon_const: HirId) -> Option<HirId> {
|
||||
match self.get(self.get_parent_node(anon_const)) {
|
||||
Node::GenericParam(GenericParam {
|
||||
hir_id: param_id,
|
||||
kind: GenericParamKind::Const { .. },
|
||||
..
|
||||
}) => Some(*param_id),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'hir> intravisit::Map<'hir> for Map<'hir> {
|
||||
|
@ -1437,6 +1437,52 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
|
||||
// of a const parameter type, e.g. `struct Foo<const N: usize, const M: [u8; N]>` is not allowed.
|
||||
None
|
||||
} else if tcx.lazy_normalization() {
|
||||
if let Some(param_id) = tcx.hir().opt_const_param_default_param_hir_id(hir_id) {
|
||||
// If the def_id we are calling generics_of on is an anon ct default i.e:
|
||||
//
|
||||
// struct Foo<const N: usize = { .. }>;
|
||||
// ^^^ ^ ^^^^^^ def id of this anon const
|
||||
// ^ ^ param_id
|
||||
// ^ parent_def_id
|
||||
//
|
||||
// then we only want to return generics for params to the left of `N`. If we don't do that we
|
||||
// end up with that const looking like: `ty::ConstKind::Unevaluated(def_id, substs: [N#0])`.
|
||||
//
|
||||
// This causes ICEs (#86580) when building the substs for Foo in `fn foo() -> Foo { .. }` as
|
||||
// we substitute the defaults with the partially built substs when we build the substs. Subst'ing
|
||||
// the `N#0` on the unevaluated const indexes into the empty substs we're in the process of building.
|
||||
//
|
||||
// We fix this by having this function return the parent's generics ourselves and truncating the
|
||||
// generics to only include non-forward declared params (with the exception of the `Self` ty)
|
||||
//
|
||||
// For the above code example that means we want `substs: []`
|
||||
// For the following struct def we want `substs: [N#0]` when generics_of is called on
|
||||
// the def id of the `{ N + 1 }` anon const
|
||||
// struct Foo<const N: usize, const M: usize = { N + 1 }>;
|
||||
//
|
||||
// This has some implications for how we get the predicates available to the anon const
|
||||
// see `explicit_predicates_of` for more information on this
|
||||
let generics = tcx.generics_of(parent_def_id.to_def_id());
|
||||
let param_def = tcx.hir().local_def_id(param_id).to_def_id();
|
||||
let param_def_idx = generics.param_def_id_to_index[¶m_def];
|
||||
// In the above example this would be .params[..N#0]
|
||||
let params = generics.params[..param_def_idx as usize].to_owned();
|
||||
let param_def_id_to_index =
|
||||
params.iter().map(|param| (param.def_id, param.index)).collect();
|
||||
|
||||
return ty::Generics {
|
||||
// we set the parent of these generics to be our parent's parent so that we
|
||||
// dont end up with substs: [N, M, N] for the const default on a struct like this:
|
||||
// struct Foo<const N: usize, const M: usize = { ... }>;
|
||||
parent: generics.parent,
|
||||
parent_count: generics.parent_count,
|
||||
params,
|
||||
param_def_id_to_index,
|
||||
has_self: generics.has_self,
|
||||
has_late_bound_regions: generics.has_late_bound_regions,
|
||||
};
|
||||
}
|
||||
|
||||
// HACK(eddyb) this provides the correct generics when
|
||||
// `feature(const_generics)` is enabled, so that const expressions
|
||||
// used with const generics, e.g. `Foo<{N+1}>`, can work at all.
|
||||
@ -2355,7 +2401,8 @@ fn trait_explicit_predicates_and_bounds(
|
||||
}
|
||||
|
||||
fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> {
|
||||
if let DefKind::Trait = tcx.def_kind(def_id) {
|
||||
let def_kind = tcx.def_kind(def_id);
|
||||
if let DefKind::Trait = def_kind {
|
||||
// Remove bounds on associated types from the predicates, they will be
|
||||
// returned by `explicit_item_bounds`.
|
||||
let predicates_and_bounds = tcx.trait_explicit_predicates_and_bounds(def_id.expect_local());
|
||||
@ -2400,6 +2447,26 @@ fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicat
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if matches!(def_kind, DefKind::AnonConst) && tcx.lazy_normalization() {
|
||||
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
|
||||
if let Some(_) = tcx.hir().opt_const_param_default_param_hir_id(hir_id) {
|
||||
// In `generics_of` we set the generics' parent to be our parent's parent which means that
|
||||
// we lose out on the predicates of our actual parent if we dont return those predicates here.
|
||||
// (See comment in `generics_of` for more information on why the parent shenanigans is necessary)
|
||||
//
|
||||
// struct Foo<T, const N: usize = { <T as Trait>::ASSOC }>(T) where T: Trait;
|
||||
// ^^^ ^^^^^^^^^^^^^^^^^^^^^^^ the def id we are calling
|
||||
// ^^^ explicit_predicates_of on
|
||||
// parent item we dont have set as the
|
||||
// parent of generics returned by `generics_of`
|
||||
//
|
||||
// In the above code we want the anon const to have predicates in its param env for `T: Trait`
|
||||
let item_id = tcx.hir().get_parent_item(hir_id);
|
||||
let item_def_id = tcx.hir().local_def_id(item_id).to_def_id();
|
||||
// In the above code example we would be calling `explicit_predicates_of(Foo)` here
|
||||
return tcx.explicit_predicates_of(item_def_id);
|
||||
}
|
||||
}
|
||||
gather_explicit_predicates_of(tcx, def_id)
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,27 @@ pub fn provide(providers: &mut Providers) {
|
||||
fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[(ty::Predicate<'_>, Span)] {
|
||||
let id = tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local());
|
||||
|
||||
if matches!(tcx.def_kind(item_def_id), hir::def::DefKind::AnonConst) && tcx.lazy_normalization()
|
||||
{
|
||||
if let Some(_) = tcx.hir().opt_const_param_default_param_hir_id(id) {
|
||||
// In `generics_of` we set the generics' parent to be our parent's parent which means that
|
||||
// we lose out on the predicates of our actual parent if we dont return those predicates here.
|
||||
// (See comment in `generics_of` for more information on why the parent shenanigans is necessary)
|
||||
//
|
||||
// struct Foo<'a, 'b, const N: usize = { ... }>(&'a &'b ());
|
||||
// ^^^ ^^^^^^^ the def id we are calling
|
||||
// ^^^ inferred_outlives_of on
|
||||
// parent item we dont have set as the
|
||||
// parent of generics returned by `generics_of`
|
||||
//
|
||||
// In the above code we want the anon const to have predicates in its param env for `'b: 'a`
|
||||
let item_id = tcx.hir().get_parent_item(id);
|
||||
let item_def_id = tcx.hir().local_def_id(item_id).to_def_id();
|
||||
// In the above code example we would be calling `inferred_outlives_of(Foo)` here
|
||||
return tcx.inferred_outlives_of(item_def_id);
|
||||
}
|
||||
}
|
||||
|
||||
match tcx.hir().get(id) {
|
||||
Node::Item(item) => match item.kind {
|
||||
hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) | hir::ItemKind::Union(..) => {
|
||||
|
14
src/test/ui/const-generics/defaults/cec-concrete-default.rs
Normal file
14
src/test/ui/const-generics/defaults/cec-concrete-default.rs
Normal file
@ -0,0 +1,14 @@
|
||||
#![feature(const_generics, const_evaluatable_checked, const_generics_defaults)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
struct Foo<const N: usize, const M: usize = { N + 1 }>;
|
||||
fn no_constraining() -> Foo<10> {
|
||||
Foo::<10, 11>
|
||||
}
|
||||
|
||||
pub fn different_than_default() -> Foo<10> {
|
||||
Foo::<10, 12>
|
||||
//~^ error: mismatched types
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,12 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/cec-concrete-default.rs:10:5
|
||||
|
|
||||
LL | Foo::<10, 12>
|
||||
| ^^^^^^^^^^^^^ expected `11_usize`, found `12_usize`
|
||||
|
|
||||
= note: expected type `11_usize`
|
||||
found type `12_usize`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
@ -0,0 +1,16 @@
|
||||
#![feature(const_generics, const_evaluatable_checked, const_generics_defaults)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
struct Foo<const N: usize, const M: usize = { N + 1 }>;
|
||||
fn should_unify<const N: usize>() -> Foo<N> where [(); { N + 1 }]: {
|
||||
Foo::<N, { N + 1 }>
|
||||
}
|
||||
pub fn shouldnt_unify<const N: usize>() -> Foo<N>
|
||||
where
|
||||
[(); { N + 1 }]:,
|
||||
[(); { N + 2 }]:, {
|
||||
Foo::<N, { N + 2 }>
|
||||
//~^ error: mismatched types
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,12 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/cec-generic-default-mismatched-types.rs:12:5
|
||||
|
|
||||
LL | Foo::<N, { N + 2 }>
|
||||
| ^^^^^^^^^^^^^^^^^^^ expected `{ N + 1 }`, found `{ N + 2 }`
|
||||
|
|
||||
= note: expected type `{ N + 1 }`
|
||||
found type `{ N + 2 }`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
24
src/test/ui/const-generics/defaults/cec-generic-default.rs
Normal file
24
src/test/ui/const-generics/defaults/cec-generic-default.rs
Normal file
@ -0,0 +1,24 @@
|
||||
#![feature(const_evaluatable_checked, const_generics, const_generics_defaults)]
|
||||
#![allow(incomplete_features)]
|
||||
|
||||
pub struct Foo<const N: usize, const M: usize = { N + 1 }>;
|
||||
pub fn needs_evaluatable_bound<const N1: usize>() -> Foo<N1> {
|
||||
//~^ error: unconstrained generic constant
|
||||
loop {}
|
||||
}
|
||||
pub fn has_evaluatable_bound<const N1: usize>() -> Foo<N1> where [(); N1 + 1]: {
|
||||
loop {}
|
||||
}
|
||||
|
||||
type FooAlias<const N: usize, const NP: usize = { N + 1 }> = [(); NP];
|
||||
fn needs_evaluatable_bound_alias<T, const N: usize>() -> FooAlias<N>
|
||||
{
|
||||
//~^^ error: unconstrained generic constant
|
||||
todo!()
|
||||
}
|
||||
fn has_evaluatable_bound_alias<const N: usize>() -> FooAlias<N>
|
||||
where [(); N + 1]: {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,18 @@
|
||||
error: unconstrained generic constant
|
||||
--> $DIR/cec-generic-default.rs:5:54
|
||||
|
|
||||
LL | pub fn needs_evaluatable_bound<const N1: usize>() -> Foo<N1> {
|
||||
| ^^^^^^^
|
||||
|
|
||||
= help: try adding a `where` bound using this expression: `where [(); { N + 1 }]:`
|
||||
|
||||
error: unconstrained generic constant
|
||||
--> $DIR/cec-generic-default.rs:14:58
|
||||
|
|
||||
LL | fn needs_evaluatable_bound_alias<T, const N: usize>() -> FooAlias<N>
|
||||
| ^^^^^^^^^^^
|
||||
|
|
||||
= help: try adding a `where` bound using this expression: `where [(); { N + 1 }]:`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
Loading…
x
Reference in New Issue
Block a user