From 7530c43b79b8054924430e4cb643003aa273d79f Mon Sep 17 00:00:00 2001 From: b-naber Date: Mon, 18 Oct 2021 17:36:45 +0200 Subject: [PATCH 1/4] postpone evaluation of constants whose substs depend on inference vars or regions --- compiler/rustc_infer/src/infer/mod.rs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 6b905f67e683f..4873d3a6421f5 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -21,6 +21,7 @@ use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues}; use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType}; +use rustc_middle::mir::interpret::ErrorHandled; use rustc_middle::mir::interpret::EvalToConstValueResult; use rustc_middle::traits::select; use rustc_middle::ty::error::{ExpectedFound, TypeError}; @@ -1584,10 +1585,23 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { unevaluated: ty::Unevaluated<'tcx>, span: Option, ) -> EvalToConstValueResult<'tcx> { - let mut original_values = OriginalQueryValues::default(); - let canonical = self.canonicalize_query((param_env, unevaluated), &mut original_values); + let param_env = self.tcx.erase_regions(param_env); + let mut substs = unevaluated.substs(self.tcx); + substs = self.tcx.erase_regions(substs); + substs = self.resolve_vars_if_possible(substs); + + // Postpone the evaluation of constants whose substs depend on inference + // variables + if substs.has_infer_types_or_consts() { + return Err(ErrorHandled::TooGeneric); + } + + let unevaluated = ty::Unevaluated { + def: unevaluated.def, + substs_: Some(substs), + promoted: unevaluated.promoted, + }; - let (param_env, unevaluated) = canonical.value; // The return value is the evaluated value which doesn't contain any reference to inference // variables, thus we don't need to substitute back the original values. self.tcx.const_eval_resolve(param_env, unevaluated, span) From 6000b4844a954879df51dafe95dc88239cb485a5 Mon Sep 17 00:00:00 2001 From: b-naber Date: Mon, 18 Oct 2021 18:25:45 +0200 Subject: [PATCH 2/4] add tests --- .../const_eval_resolve_canonical.rs | 7 +- .../const_eval_resolve_canonical.stderr | 9 +++ .../ui/const-generics/issues/issue-83249.rs | 23 +++++++ .../const-generics/issues/issue-83249.stderr | 22 ++++++ .../ui/const-generics/issues/issue-83288.rs | 69 +++++++++++++++++++ .../ui/const-generics/issues/issue-87470.rs | 24 +++++++ .../ui/const-generics/issues/issue-87964.rs | 29 ++++++++ .../ui/const-generics/issues/issue-89146.rs | 26 +++++++ .../ui/const-generics/issues/issue-89320.rs | 19 +++++ 9 files changed, 225 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/const-generics/generic_const_exprs/const_eval_resolve_canonical.stderr create mode 100644 src/test/ui/const-generics/issues/issue-83249.rs create mode 100644 src/test/ui/const-generics/issues/issue-83249.stderr create mode 100644 src/test/ui/const-generics/issues/issue-83288.rs create mode 100644 src/test/ui/const-generics/issues/issue-87470.rs create mode 100644 src/test/ui/const-generics/issues/issue-87964.rs create mode 100644 src/test/ui/const-generics/issues/issue-89146.rs create mode 100644 src/test/ui/const-generics/issues/issue-89320.rs diff --git a/src/test/ui/const-generics/generic_const_exprs/const_eval_resolve_canonical.rs b/src/test/ui/const-generics/generic_const_exprs/const_eval_resolve_canonical.rs index b79bc262d2bac..bcb3e83b73a5f 100644 --- a/src/test/ui/const-generics/generic_const_exprs/const_eval_resolve_canonical.rs +++ b/src/test/ui/const-generics/generic_const_exprs/const_eval_resolve_canonical.rs @@ -1,4 +1,3 @@ -// run-pass #![feature(generic_const_exprs)] #![allow(incomplete_features)] @@ -22,8 +21,10 @@ where } fn main() { - // Test that we can correctly infer `T` which requires evaluating - // `{ N + 1 }` which has substs containing an inference var + // FIXME(generic_const_exprs): We can't correctly infer `T` which requires + // evaluating `{ N + 1 }` which has substs containing an inference var let mut _q = Default::default(); + //~^ ERROR type annotations needed + _q = foo::<_, 2>(_q); } diff --git a/src/test/ui/const-generics/generic_const_exprs/const_eval_resolve_canonical.stderr b/src/test/ui/const-generics/generic_const_exprs/const_eval_resolve_canonical.stderr new file mode 100644 index 0000000000000..75bca058e7c5c --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/const_eval_resolve_canonical.stderr @@ -0,0 +1,9 @@ +error[E0282]: type annotations needed + --> $DIR/const_eval_resolve_canonical.rs:26:9 + | +LL | let mut _q = Default::default(); + | ^^^^^^ consider giving `_q` a type + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/src/test/ui/const-generics/issues/issue-83249.rs b/src/test/ui/const-generics/issues/issue-83249.rs new file mode 100644 index 0000000000000..65148c55ee541 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-83249.rs @@ -0,0 +1,23 @@ +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] + +trait Foo { + const N: usize; +} + +impl Foo for u8 { + const N: usize = 1; +} + +fn foo(_: [u8; T::N]) -> T { + todo!() +} + +pub fn bar() { + let _: u8 = foo([0; 1]); + + let _ = foo([0; 1]); + //~^ ERROR type annotations needed +} + +fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-83249.stderr b/src/test/ui/const-generics/issues/issue-83249.stderr new file mode 100644 index 0000000000000..13914134eb2de --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-83249.stderr @@ -0,0 +1,22 @@ +error[E0283]: type annotations needed + --> $DIR/issue-83249.rs:19:13 + | +LL | let _ = foo([0; 1]); + | - ^^^ cannot infer type for type parameter `T` declared on the function `foo` + | | + | consider giving this pattern a type + | + = note: cannot satisfy `_: Foo` +note: required by a bound in `foo` + --> $DIR/issue-83249.rs:12:11 + | +LL | fn foo(_: [u8; T::N]) -> T { + | ^^^ required by this bound in `foo` +help: consider specifying the type argument in the function call + | +LL | let _ = foo::([0; 1]); + | +++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0283`. diff --git a/src/test/ui/const-generics/issues/issue-83288.rs b/src/test/ui/const-generics/issues/issue-83288.rs new file mode 100644 index 0000000000000..a24596d242e0d --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-83288.rs @@ -0,0 +1,69 @@ +// build-pass + +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] + +use std::{marker::PhantomData, ops::Mul}; + +pub enum Nil {} +pub struct Cons { + _phantom: PhantomData<(T, L)>, +} + +pub trait Indices { + const RANK: usize; + const NUM_ELEMS: usize; +} + +impl Indices for Nil { + const RANK: usize = 0; + const NUM_ELEMS: usize = 1; +} + +impl, const N: usize> Indices for Cons { + const RANK: usize = I::RANK + 1; + const NUM_ELEMS: usize = I::NUM_ELEMS * N; +} + +pub trait Concat { + type Output; +} + +impl Concat for Nil { + type Output = J; +} + +impl Concat for Cons +where + I: Concat, +{ + type Output = Cons>::Output>; +} + +pub struct Tensor, const N: usize> +where + [u8; I::NUM_ELEMS]: Sized, +{ + pub data: [u8; I::NUM_ELEMS], + _phantom: PhantomData, +} + +impl, J: Indices, const N: usize> Mul> for Tensor +where + I: Concat, + >::Output: Indices, + [u8; I::NUM_ELEMS]: Sized, + [u8; J::NUM_ELEMS]: Sized, + [u8; >::Output::NUM_ELEMS]: Sized, +{ + type Output = Tensor<>::Output, N>; + + fn mul(self, _rhs: Tensor) -> Self::Output { + Tensor { + data: [0u8; >::Output::NUM_ELEMS], + _phantom: PhantomData, + } + } +} + +fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-87470.rs b/src/test/ui/const-generics/issues/issue-87470.rs new file mode 100644 index 0000000000000..d60181a418a15 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-87470.rs @@ -0,0 +1,24 @@ +// build-pass + +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +pub trait TraitWithConst { + const SOME_CONST: usize; +} + +pub trait OtherTrait: TraitWithConst { + fn some_fn(self) -> [u8 ; ::SOME_CONST]; +} + +impl TraitWithConst for f32 { + const SOME_CONST: usize = 32; +} + +impl OtherTrait for f32 { + fn some_fn(self) -> [u8 ; ::SOME_CONST] { + [0; 32] + } +} + +fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-87964.rs b/src/test/ui/const-generics/issues/issue-87964.rs new file mode 100644 index 0000000000000..116686abb9e3d --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-87964.rs @@ -0,0 +1,29 @@ +// build-pass + +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +pub trait Target { + const LENGTH: usize; +} + + +pub struct Container +where + [(); T::LENGTH]: Sized, +{ + _target: T, +} + +impl Container +where + [(); T::LENGTH]: Sized, +{ + pub fn start( + _target: T, + ) -> Container { + Container { _target } + } +} + +fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-89146.rs b/src/test/ui/const-generics/issues/issue-89146.rs new file mode 100644 index 0000000000000..e3540f46f1e81 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-89146.rs @@ -0,0 +1,26 @@ +// build-pass + +#![allow(incomplete_features)] +#![feature(generic_const_exprs)] + +pub trait Foo { + const SIZE: usize; + + fn to_bytes(&self) -> [u8; Self::SIZE]; +} + +pub fn bar(a: &G) -> u8 +where + [(); G::SIZE]: Sized, +{ + deeper_bar(a) +} + +fn deeper_bar(a: &G) -> u8 +where + [(); G::SIZE]: Sized, +{ + a.to_bytes()[0] +} + +fn main() {} diff --git a/src/test/ui/const-generics/issues/issue-89320.rs b/src/test/ui/const-generics/issues/issue-89320.rs new file mode 100644 index 0000000000000..afa5c8fab74e8 --- /dev/null +++ b/src/test/ui/const-generics/issues/issue-89320.rs @@ -0,0 +1,19 @@ +// build-pass + +#![feature(generic_const_exprs)] +#![allow(incomplete_features)] + +pub trait Enumerable { + const N: usize; +} + +#[derive(Clone)] +pub struct SymmetricGroup +where + S: Enumerable, + [(); S::N]: Sized, +{ + _phantom: std::marker::PhantomData, +} + +fn main() {} From dae24073687fca6682c351379d41d61397e7c329 Mon Sep 17 00:00:00 2001 From: b-naber Date: Fri, 5 Nov 2021 18:11:08 +0100 Subject: [PATCH 3/4] resolve variables before erasing lifetimes --- compiler/rustc_infer/src/infer/mod.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 4873d3a6421f5..5e687a7063ce4 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -1585,9 +1585,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { unevaluated: ty::Unevaluated<'tcx>, span: Option, ) -> EvalToConstValueResult<'tcx> { - let param_env = self.tcx.erase_regions(param_env); let mut substs = unevaluated.substs(self.tcx); - substs = self.tcx.erase_regions(substs); substs = self.resolve_vars_if_possible(substs); // Postpone the evaluation of constants whose substs depend on inference @@ -1596,15 +1594,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { return Err(ErrorHandled::TooGeneric); } + let param_env_erased = self.tcx.erase_regions(param_env); + let substs_erased = self.tcx.erase_regions(substs); + let unevaluated = ty::Unevaluated { def: unevaluated.def, - substs_: Some(substs), + substs_: Some(substs_erased), promoted: unevaluated.promoted, }; // The return value is the evaluated value which doesn't contain any reference to inference // variables, thus we don't need to substitute back the original values. - self.tcx.const_eval_resolve(param_env, unevaluated, span) + self.tcx.const_eval_resolve(param_env_erased, unevaluated, span) } /// If `typ` is a type variable of some kind, resolve it one level From 37ed2db1e04e5b9dcdf833374d364b1f5e374fc3 Mon Sep 17 00:00:00 2001 From: b-naber Date: Sun, 7 Nov 2021 22:38:33 +0100 Subject: [PATCH 4/4] consider unevaluated consts in extract_inference_diagnostics_data --- .../infer/error_reporting/need_type_info.rs | 92 +++++++++++++------ .../const_eval_resolve_canonical.rs | 1 + .../const_eval_resolve_canonical.stderr | 28 +++++- .../const-generics/issues/issue-83249.stderr | 15 +-- 4 files changed, 95 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index e00003face9ce..5b39897bc1d20 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -1,5 +1,6 @@ use crate::infer::type_variable::TypeVariableOriginKind; use crate::infer::InferCtxt; +use crate::rustc_middle::ty::TypeFoldable; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Namespace}; @@ -390,36 +391,75 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { } } GenericArgKind::Const(ct) => { - if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.val { - let origin = - self.inner.borrow_mut().const_unification_table().probe_value(vid).origin; - if let ConstVariableOriginKind::ConstParameterDefinition(name, def_id) = - origin.kind - { - return InferenceDiagnosticsData { - name: name.to_string(), + match ct.val { + ty::ConstKind::Infer(InferConst::Var(vid)) => { + let origin = self + .inner + .borrow_mut() + .const_unification_table() + .probe_value(vid) + .origin; + if let ConstVariableOriginKind::ConstParameterDefinition(name, def_id) = + origin.kind + { + return InferenceDiagnosticsData { + name: name.to_string(), + span: Some(origin.span), + kind: UnderspecifiedArgKind::Const { is_parameter: true }, + parent: InferenceDiagnosticsParentData::for_def_id( + self.tcx, def_id, + ), + }; + } + + debug_assert!(!origin.span.is_dummy()); + let mut s = String::new(); + let mut printer = + ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::ValueNS); + if let Some(highlight) = highlight { + printer.region_highlight_mode = highlight; + } + let _ = ct.print(printer); + InferenceDiagnosticsData { + name: s, span: Some(origin.span), - kind: UnderspecifiedArgKind::Const { is_parameter: true }, - parent: InferenceDiagnosticsParentData::for_def_id(self.tcx, def_id), - }; + kind: UnderspecifiedArgKind::Const { is_parameter: false }, + parent: None, + } } - - debug_assert!(!origin.span.is_dummy()); - let mut s = String::new(); - let mut printer = - ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::ValueNS); - if let Some(highlight) = highlight { - printer.region_highlight_mode = highlight; + ty::ConstKind::Unevaluated(ty::Unevaluated { + substs_: Some(substs), .. + }) => { + assert!(substs.has_infer_types_or_consts()); + + // FIXME: We only use the first inference variable we encounter in + // `substs` here, this gives insufficiently informative diagnostics + // in case there are multiple inference variables + for s in substs.iter() { + match s.unpack() { + GenericArgKind::Type(t) => match t.kind() { + ty::Infer(_) => { + return self.extract_inference_diagnostics_data(s, None); + } + _ => {} + }, + GenericArgKind::Const(c) => match c.val { + ty::ConstKind::Infer(InferConst::Var(_)) => { + return self.extract_inference_diagnostics_data(s, None); + } + _ => {} + }, + _ => {} + } + } + bug!( + "expected an inference variable in substs of unevaluated const {:?}", + ct + ); } - let _ = ct.print(printer); - InferenceDiagnosticsData { - name: s, - span: Some(origin.span), - kind: UnderspecifiedArgKind::Const { is_parameter: false }, - parent: None, + _ => { + bug!("unexpect const: {:?}", ct); } - } else { - bug!("unexpect const: {:?}", ct); } } GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"), diff --git a/src/test/ui/const-generics/generic_const_exprs/const_eval_resolve_canonical.rs b/src/test/ui/const-generics/generic_const_exprs/const_eval_resolve_canonical.rs index bcb3e83b73a5f..18f33acaabbba 100644 --- a/src/test/ui/const-generics/generic_const_exprs/const_eval_resolve_canonical.rs +++ b/src/test/ui/const-generics/generic_const_exprs/const_eval_resolve_canonical.rs @@ -27,4 +27,5 @@ fn main() { //~^ ERROR type annotations needed _q = foo::<_, 2>(_q); + //~^ ERROR type annotations needed } diff --git a/src/test/ui/const-generics/generic_const_exprs/const_eval_resolve_canonical.stderr b/src/test/ui/const-generics/generic_const_exprs/const_eval_resolve_canonical.stderr index 75bca058e7c5c..e59f1ac8027de 100644 --- a/src/test/ui/const-generics/generic_const_exprs/const_eval_resolve_canonical.stderr +++ b/src/test/ui/const-generics/generic_const_exprs/const_eval_resolve_canonical.stderr @@ -4,6 +4,30 @@ error[E0282]: type annotations needed LL | let mut _q = Default::default(); | ^^^^^^ consider giving `_q` a type -error: aborting due to previous error +error[E0283]: type annotations needed + --> $DIR/const_eval_resolve_canonical.rs:29:10 + | +LL | _q = foo::<_, 2>(_q); + | ^^^^^^^^^^^ cannot infer type + | +note: multiple `impl`s satisfying `(): Foo<{ N + 1 }>` found + --> $DIR/const_eval_resolve_canonical.rs:8:1 + | +LL | impl Foo<0> for () { + | ^^^^^^^^^^^^^^^^^^ +... +LL | impl Foo<3> for () { + | ^^^^^^^^^^^^^^^^^^ +note: required by a bound in `foo` + --> $DIR/const_eval_resolve_canonical.rs:18:9 + | +LL | fn foo(_: T) -> <() as Foo<{ N + 1 }>>::Assoc + | --- required by a bound in this +LL | where +LL | (): Foo<{ N + 1 }>, + | ^^^^^^^^^^^^^^ required by this bound in `foo` + +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0282`. +Some errors have detailed explanations: E0282, E0283. +For more information about an error, try `rustc --explain E0282`. diff --git a/src/test/ui/const-generics/issues/issue-83249.stderr b/src/test/ui/const-generics/issues/issue-83249.stderr index 13914134eb2de..402b3aa2d61dc 100644 --- a/src/test/ui/const-generics/issues/issue-83249.stderr +++ b/src/test/ui/const-generics/issues/issue-83249.stderr @@ -1,22 +1,11 @@ -error[E0283]: type annotations needed +error[E0282]: type annotations needed --> $DIR/issue-83249.rs:19:13 | LL | let _ = foo([0; 1]); | - ^^^ cannot infer type for type parameter `T` declared on the function `foo` | | | consider giving this pattern a type - | - = note: cannot satisfy `_: Foo` -note: required by a bound in `foo` - --> $DIR/issue-83249.rs:12:11 - | -LL | fn foo(_: [u8; T::N]) -> T { - | ^^^ required by this bound in `foo` -help: consider specifying the type argument in the function call - | -LL | let _ = foo::([0; 1]); - | +++++ error: aborting due to previous error -For more information about this error, try `rustc --explain E0283`. +For more information about this error, try `rustc --explain E0282`.