Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dont provide fwd declared params to cg defaults #86580

Merged
merged 6 commits into from
Jul 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
13 changes: 13 additions & 0 deletions compiler/rustc_middle/src/hir/map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<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> {
Expand Down
69 changes: 68 additions & 1 deletion compiler/rustc_typeck/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1441,6 +1441,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[&param_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.
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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<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)
}
}
Expand Down
21 changes: 21 additions & 0 deletions compiler/rustc_typeck/src/outlives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(..) => {
Expand Down
14 changes: 14 additions & 0 deletions src/test/ui/const-generics/defaults/cec-concrete-default.rs
Original file line number Diff line number Diff line change
@@ -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() {}
12 changes: 12 additions & 0 deletions src/test/ui/const-generics/defaults/cec-concrete-default.stderr
Original file line number Diff line number Diff line change
@@ -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`.
Original file line number Diff line number Diff line change
@@ -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() {}
Original file line number Diff line number Diff line change
@@ -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 changes: 24 additions & 0 deletions src/test/ui/const-generics/defaults/cec-generic-default.rs
Original file line number Diff line number Diff line change
@@ -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() {}
18 changes: 18 additions & 0 deletions src/test/ui/const-generics/defaults/cec-generic-default.stderr
Original file line number Diff line number Diff line change
@@ -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