improve wf check for const param defaults

This commit is contained in:
lcnr 2021-04-18 15:14:17 +02:00
parent 7cb1dcd488
commit 312b4fdfd2
8 changed files with 94 additions and 18 deletions

View File

@ -728,20 +728,36 @@ fn check_where_clauses<'tcx, 'fcx>(
//
// Here, the default `Vec<[u32]>` is not WF because `[u32]: Sized` does not hold.
for param in &generics.params {
if let GenericParamDefKind::Type { .. } = param.kind {
if is_our_default(&param) {
let ty = fcx.tcx.type_of(param.def_id);
// Ignore dependent defaults -- that is, where the default of one type
// parameter includes another (e.g., `<T, U = T>`). In those cases, we can't
// be sure if it will error or not as user might always specify the other.
if !ty.needs_subst() {
match param.kind {
GenericParamDefKind::Type { .. } => {
if is_our_default(&param) {
let ty = fcx.tcx.type_of(param.def_id);
// Ignore dependent defaults -- that is, where the default of one type
// parameter includes another (e.g., `<T, U = T>`). In those cases, we can't
// be sure if it will error or not as user might always specify the other.
if !ty.needs_subst() {
fcx.register_wf_obligation(
ty.into(),
fcx.tcx.def_span(param.def_id),
ObligationCauseCode::MiscObligation,
);
}
}
}
GenericParamDefKind::Const { .. } => {
// FIXME(const_generics_defaults): Figure out if this
// is the behavior we want, see the comment further below.
if is_our_default(&param) {
let default_ct = tcx.const_param_default(param.def_id);
fcx.register_wf_obligation(
ty.into(),
default_ct.into(),
fcx.tcx.def_span(param.def_id),
ObligationCauseCode::MiscObligation,
);
}
}
// Doesn't have defaults.
GenericParamDefKind::Lifetime => {}
}
}
@ -774,14 +790,25 @@ fn check_where_clauses<'tcx, 'fcx>(
fcx.tcx.mk_param_from_def(param)
}
GenericParamDefKind::Const { .. } => {
// FIXME(const_generics_defaults): I(@lcnr) feel like always
// using the const parameter is the right choice here, even
// if it needs substs.
//
// Before stabilizing this we probably want to get some tests
// where this makes a difference and figure out what's the exact
// behavior we want here.
// If the param has a default, ...
if is_our_default(param) {
let default_ct = tcx.const_param_default(param.def_id);
// Const params currently have to be concrete.
assert!(!default_ct.needs_subst());
default_ct.into()
} else {
fcx.tcx.mk_param_from_def(param)
// ... and it's not a dependent default, ...
if !default_ct.needs_subst() {
// ... then substitute it with the default.
return default_ct.into();
}
}
fcx.tcx.mk_param_from_def(param)
}
}
});

View File

@ -1,5 +1,5 @@
error: generic parameters may not be used in const operations
--> $DIR/complex-generic-default-expr.rs:6:47
--> $DIR/complex-generic-default-expr.rs:10:47
|
LL | struct Foo<const N: usize, const M: usize = { N + 1 }>;
| ^ cannot perform const operation using `N`
@ -8,7 +8,7 @@ LL | struct Foo<const N: usize, const M: usize = { N + 1 }>;
= help: use `#![feature(const_generics)]` and `#![feature(const_evaluatable_checked)]` to allow generic const expressions
error: generic parameters may not be used in const operations
--> $DIR/complex-generic-default-expr.rs:9:62
--> $DIR/complex-generic-default-expr.rs:13:62
|
LL | struct Bar<T, const TYPE_SIZE: usize = { std::mem::size_of::<T>() }>(T);
| ^ cannot perform const operation using `T`

View File

@ -1,4 +1,8 @@
// revisions: full min
// revisions: min
// FIXME(const_generics): add the `full` revision,
// currently causes an ICE as we don't supply substs to
// anon consts in the parameter listing, as that would
// cause that anon const to reference itself.
#![cfg_attr(full, feature(const_generics))]
#![feature(const_generics_defaults)]
#![allow(incomplete_features)]
@ -8,6 +12,6 @@
struct Bar<T, const TYPE_SIZE: usize = { std::mem::size_of::<T>() }>(T);
//[min]~^ ERROR generic parameters may not be used in const operations
//[full]~^^ ERROR the size for values of type `T` cannot be known at compilation time
//[full]~^^ ERROR the size for values of type `T` cannot be known at compilation time
fn main() {}

View File

@ -0,0 +1,14 @@
// run-pass
#![feature(const_generics_defaults)]
#![allow(incomplete_features)]
struct Foo<const N: usize, const M: usize = N>([u8; N], [u8; M]);
fn foo<const N: usize>() -> Foo<N> {
let x = [0; N];
Foo(x, x)
}
fn main() {
let val = foo::<13>();
assert_eq!(val.0, val.1);
}

View File

@ -0,0 +1,18 @@
// run-pass
#![feature(const_generics_defaults)]
#![allow(incomplete_features)]
// FIXME(const_generics_defaults): while we can allow this,
// we probably won't easily allow this with more complex const operations.
//
// So we have to make a conscious decision here when stabilizing a relaxed parameter ordering.
struct Foo<const N: usize, T = [u8; N]>(T);
impl<const N: usize> Foo<N> {
fn new() -> Self {
Foo([0; N])
}
}
fn main() {
assert_eq!(Foo::new().0, [0; 10]);
}

View File

@ -0,0 +1,5 @@
#![feature(const_generics_defaults)]
#![allow(incomplete_features)]
struct Foo<const N: u8 = { 255 + 1 }>;
//~^ ERROR evaluation of constant value failed
fn main() {}

View File

@ -0,0 +1,9 @@
error[E0080]: evaluation of constant value failed
--> $DIR/default-param-wf-concrete.rs:3:28
|
LL | struct Foo<const N: u8 = { 255 + 1 }>;
| ^^^^^^^ attempt to compute `u8::MAX + 1_u8`, which would overflow
error: aborting due to previous error
For more information about this error, try `rustc --explain E0080`.

View File

@ -11,4 +11,3 @@ trait Foo<const KIND: bool = true> {}
fn foo<const SIZE: usize = 5>() {}
struct Range<const FROM: usize = 0, const LEN: usize = 0, const TO: usize = FROM>;