diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index a7ce92ea57917..f3f2b83c821d5 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1422,6 +1422,9 @@ pub type Lit = Spanned; /// 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, diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index 07b39c97c492a..9d81407c330c4 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -901,6 +901,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` when + /// called with the HirId for the `{ ... }` anon const + pub fn opt_const_param_default_param_hir_id(&self, anon_const: HirId) -> Option { + 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> { diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 583ba9392f062..a2ac3e2555f50 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -1441,6 +1441,52 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics { // of a const parameter type, e.g. `struct Foo` 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; + // ^^^ ^ ^^^^^^ 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; + // + // 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; + 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. @@ -2359,7 +2405,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()); @@ -2404,6 +2451,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::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) } } diff --git a/compiler/rustc_typeck/src/outlives/mod.rs b/compiler/rustc_typeck/src/outlives/mod.rs index d7eb31c2abef5..70a2ba7fcd9d9 100644 --- a/compiler/rustc_typeck/src/outlives/mod.rs +++ b/compiler/rustc_typeck/src/outlives/mod.rs @@ -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(..) => { diff --git a/src/test/ui/const-generics/defaults/cec-concrete-default.rs b/src/test/ui/const-generics/defaults/cec-concrete-default.rs new file mode 100644 index 0000000000000..c2a41cf2ad7dd --- /dev/null +++ b/src/test/ui/const-generics/defaults/cec-concrete-default.rs @@ -0,0 +1,14 @@ +#![feature(const_generics, const_evaluatable_checked, const_generics_defaults)] +#![allow(incomplete_features)] + +struct Foo; +fn no_constraining() -> Foo<10> { + Foo::<10, 11> +} + +pub fn different_than_default() -> Foo<10> { + Foo::<10, 12> + //~^ error: mismatched types +} + +fn main() {} diff --git a/src/test/ui/const-generics/defaults/cec-concrete-default.stderr b/src/test/ui/const-generics/defaults/cec-concrete-default.stderr new file mode 100644 index 0000000000000..090e507b7f34e --- /dev/null +++ b/src/test/ui/const-generics/defaults/cec-concrete-default.stderr @@ -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`. diff --git a/src/test/ui/const-generics/defaults/cec-generic-default-mismatched-types.rs b/src/test/ui/const-generics/defaults/cec-generic-default-mismatched-types.rs new file mode 100644 index 0000000000000..15822dfac1c5a --- /dev/null +++ b/src/test/ui/const-generics/defaults/cec-generic-default-mismatched-types.rs @@ -0,0 +1,16 @@ +#![feature(const_generics, const_evaluatable_checked, const_generics_defaults)] +#![allow(incomplete_features)] + +struct Foo; +fn should_unify() -> Foo where [(); { N + 1 }]: { + Foo:: +} +pub fn shouldnt_unify() -> Foo +where + [(); { N + 1 }]:, + [(); { N + 2 }]:, { + Foo:: + //~^ error: mismatched types +} + +fn main() {} diff --git a/src/test/ui/const-generics/defaults/cec-generic-default-mismatched-types.stderr b/src/test/ui/const-generics/defaults/cec-generic-default-mismatched-types.stderr new file mode 100644 index 0000000000000..f97fc26a07321 --- /dev/null +++ b/src/test/ui/const-generics/defaults/cec-generic-default-mismatched-types.stderr @@ -0,0 +1,12 @@ +error[E0308]: mismatched types + --> $DIR/cec-generic-default-mismatched-types.rs:12:5 + | +LL | Foo:: + | ^^^^^^^^^^^^^^^^^^^ 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`. diff --git a/src/test/ui/const-generics/defaults/cec-generic-default.rs b/src/test/ui/const-generics/defaults/cec-generic-default.rs new file mode 100644 index 0000000000000..76ff7c7801b06 --- /dev/null +++ b/src/test/ui/const-generics/defaults/cec-generic-default.rs @@ -0,0 +1,24 @@ +#![feature(const_evaluatable_checked, const_generics, const_generics_defaults)] +#![allow(incomplete_features)] + +pub struct Foo; +pub fn needs_evaluatable_bound() -> Foo { + //~^ error: unconstrained generic constant + loop {} +} +pub fn has_evaluatable_bound() -> Foo where [(); N1 + 1]: { + loop {} +} + +type FooAlias = [(); NP]; +fn needs_evaluatable_bound_alias() -> FooAlias +{ + //~^^ error: unconstrained generic constant + todo!() +} +fn has_evaluatable_bound_alias() -> FooAlias +where [(); N + 1]: { + todo!() +} + +fn main() {} diff --git a/src/test/ui/const-generics/defaults/cec-generic-default.stderr b/src/test/ui/const-generics/defaults/cec-generic-default.stderr new file mode 100644 index 0000000000000..0234ea8b9a4a9 --- /dev/null +++ b/src/test/ui/const-generics/defaults/cec-generic-default.stderr @@ -0,0 +1,18 @@ +error: unconstrained generic constant + --> $DIR/cec-generic-default.rs:5:54 + | +LL | pub fn needs_evaluatable_bound() -> Foo { + | ^^^^^^^ + | + = 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() -> FooAlias + | ^^^^^^^^^^^ + | + = help: try adding a `where` bound using this expression: `where [(); { N + 1 }]:` + +error: aborting due to 2 previous errors +