From 47f78a2487010d884bd40af173fddd4ac9d9aba8 Mon Sep 17 00:00:00 2001 From: b-naber Date: Thu, 17 Mar 2022 11:44:57 +0100 Subject: [PATCH 01/12] try to evaluate in try_unify --- compiler/rustc_infer/src/infer/mod.rs | 3 +- compiler/rustc_middle/src/query/mod.rs | 8 +- compiler/rustc_middle/src/ty/relate.rs | 2 +- .../src/traits/const_evaluatable.rs | 88 +++++++++++++------ 4 files changed, 66 insertions(+), 35 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 5697e73f73a70..e1d0da803fce2 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -691,11 +691,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { &self, a: ty::Unevaluated<'tcx, ()>, b: ty::Unevaluated<'tcx, ()>, + param_env: ty::ParamEnv<'tcx>, ) -> bool { let canonical = self.canonicalize_query((a, b), &mut OriginalQueryValues::default()); debug!("canonical consts: {:?}", &canonical.value); - self.tcx.try_unify_abstract_consts(canonical.value) + self.tcx.try_unify_abstract_consts(param_env.and(canonical.value)) } pub fn is_in_snapshot(&self) -> bool { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 44b622c1e3d86..d39ae43fe8c1a 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -329,12 +329,12 @@ rustc_queries! { } } - query try_unify_abstract_consts(key: ( - ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()> - )) -> bool { + query try_unify_abstract_consts(key: + ty::ParamEnvAnd<'tcx, (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()> + )>) -> bool { desc { |tcx| "trying to unify the generic constants {} and {}", - tcx.def_path_str(key.0.def.did), tcx.def_path_str(key.1.def.did) + tcx.def_path_str(key.value.0.def.did), tcx.def_path_str(key.value.1.def.did) } } diff --git a/compiler/rustc_middle/src/ty/relate.rs b/compiler/rustc_middle/src/ty/relate.rs index 81ee7942c4d34..5d6cbcf690706 100644 --- a/compiler/rustc_middle/src/ty/relate.rs +++ b/compiler/rustc_middle/src/ty/relate.rs @@ -585,7 +585,7 @@ pub fn super_relate_consts<'tcx, R: TypeRelation<'tcx>>( (ty::ConstKind::Unevaluated(au), ty::ConstKind::Unevaluated(bu)) if tcx.features().generic_const_exprs => { - tcx.try_unify_abstract_consts((au.shrink(), bu.shrink())) + tcx.try_unify_abstract_consts(relation.param_env().and((au.shrink(), bu.shrink()))) } // While this is slightly incorrect, it shouldn't matter for `min_const_generics` diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index f880b28b3c8dc..0c33ea858fd0d 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -28,13 +28,13 @@ use std::iter; use std::ops::ControlFlow; /// Check if a given constant can be evaluated. +#[instrument(skip(infcx), level = "debug")] pub fn is_const_evaluatable<'cx, 'tcx>( infcx: &InferCtxt<'cx, 'tcx>, uv: ty::Unevaluated<'tcx, ()>, param_env: ty::ParamEnv<'tcx>, span: Span, ) -> Result<(), NotConstEvaluatable> { - debug!("is_const_evaluatable({:?})", uv); let tcx = infcx.tcx; if tcx.features().generic_const_exprs { @@ -185,6 +185,7 @@ pub fn is_const_evaluatable<'cx, 'tcx>( } } +#[instrument(skip(tcx), level = "debug")] fn satisfied_from_param_env<'tcx>( tcx: TyCtxt<'tcx>, ct: AbstractConst<'tcx>, @@ -197,11 +198,12 @@ fn satisfied_from_param_env<'tcx>( // Try to unify with each subtree in the AbstractConst to allow for // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable` // predicate for `(N + 1) * 2` - let result = - walk_abstract_const(tcx, b_ct, |b_ct| match try_unify(tcx, ct, b_ct) { + let result = walk_abstract_const(tcx, b_ct, |b_ct| { + match try_unify(tcx, ct, b_ct, param_env) { true => ControlFlow::BREAK, false => ControlFlow::CONTINUE, - }); + } + }); if let ControlFlow::Break(()) = result { debug!("is_const_evaluatable: abstract_const ~~> ok"); @@ -570,11 +572,12 @@ pub(super) fn thir_abstract_const<'tcx>( pub(super) fn try_unify_abstract_consts<'tcx>( tcx: TyCtxt<'tcx>, (a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>), + param_env: ty::ParamEnv<'tcx>, ) -> bool { (|| { if let Some(a) = AbstractConst::new(tcx, a)? { if let Some(b) = AbstractConst::new(tcx, b)? { - return Ok(try_unify(tcx, a, b)); + return Ok(try_unify(tcx, a, b, param_env)); } } @@ -619,32 +622,59 @@ where recurse(tcx, ct, &mut f) } +// Substitutes generics repeatedly to allow AbstractConsts to unify where a +// ConstKind::Unevalated could be turned into an AbstractConst that would unify e.g. +// Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])] +#[inline] +#[instrument(skip(tcx), level = "debug")] +fn try_replace_substs_in_root<'tcx>( + tcx: TyCtxt<'tcx>, + mut abstr_const: AbstractConst<'tcx>, +) -> Option> { + while let Node::Leaf(ct) = abstr_const.root(tcx) { + match AbstractConst::from_const(tcx, ct) { + Ok(Some(act)) => abstr_const = act, + Ok(None) => break, + Err(_) => return None, + } + } + + Some(abstr_const) +} + /// Tries to unify two abstract constants using structural equality. +#[instrument(skip(tcx), level = "debug")] pub(super) fn try_unify<'tcx>( tcx: TyCtxt<'tcx>, - mut a: AbstractConst<'tcx>, - mut b: AbstractConst<'tcx>, + a: AbstractConst<'tcx>, + b: AbstractConst<'tcx>, + param_env: ty::ParamEnv<'tcx>, ) -> bool { - // We substitute generics repeatedly to allow AbstractConsts to unify where a - // ConstKind::Unevalated could be turned into an AbstractConst that would unify e.g. - // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])] - while let Node::Leaf(a_ct) = a.root(tcx) { - match AbstractConst::from_const(tcx, a_ct) { - Ok(Some(a_act)) => a = a_act, - Ok(None) => break, - Err(_) => return true, + let a = match try_replace_substs_in_root(tcx, a) { + Some(a) => a, + None => { + return true; } - } - while let Node::Leaf(b_ct) = b.root(tcx) { - match AbstractConst::from_const(tcx, b_ct) { - Ok(Some(b_act)) => b = b_act, - Ok(None) => break, - Err(_) => return true, + }; + + let b = match try_replace_substs_in_root(tcx, b) { + Some(b) => b, + None => { + return true; } - } + }; - match (a.root(tcx), b.root(tcx)) { + let a_root = a.root(tcx); + let b_root = b.root(tcx); + debug!(?a_root, ?b_root); + + match (a_root, b_root) { (Node::Leaf(a_ct), Node::Leaf(b_ct)) => { + let a_ct = a_ct.eval(tcx, param_env); + debug!("a_ct evaluated: {:?}", a_ct); + let b_ct = b_ct.eval(tcx, param_env); + debug!("b_ct evaluated: {:?}", b_ct); + if a_ct.ty() != b_ct.ty() { return false; } @@ -678,23 +708,23 @@ pub(super) fn try_unify<'tcx>( } } (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => { - try_unify(tcx, a.subtree(al), b.subtree(bl)) - && try_unify(tcx, a.subtree(ar), b.subtree(br)) + try_unify(tcx, a.subtree(al), b.subtree(bl), param_env) + && try_unify(tcx, a.subtree(ar), b.subtree(br), param_env) } (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => { - try_unify(tcx, a.subtree(av), b.subtree(bv)) + try_unify(tcx, a.subtree(av), b.subtree(bv), param_env) } (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args)) if a_args.len() == b_args.len() => { - try_unify(tcx, a.subtree(a_f), b.subtree(b_f)) + try_unify(tcx, a.subtree(a_f), b.subtree(b_f), param_env) && iter::zip(a_args, b_args) - .all(|(&an, &bn)| try_unify(tcx, a.subtree(an), b.subtree(bn))) + .all(|(&an, &bn)| try_unify(tcx, a.subtree(an), b.subtree(bn), param_env)) } (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty)) if (a_ty == b_ty) && (a_kind == b_kind) => { - try_unify(tcx, a.subtree(a_operand), b.subtree(b_operand)) + try_unify(tcx, a.subtree(a_operand), b.subtree(b_operand), param_env) } // use this over `_ => false` to make adding variants to `Node` less error prone (Node::Cast(..), _) From ac0458a597674a4afc01a11b97b6b6385727050d Mon Sep 17 00:00:00 2001 From: b-naber Date: Mon, 21 Mar 2022 18:26:02 +0100 Subject: [PATCH 02/12] stall on on constants that contain infer vars in const_eval_resolve --- compiler/rustc_middle/src/mir/interpret/queries.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 8b0f92d23d75f..155ee210ebe38 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -1,6 +1,7 @@ use super::{ErrorHandled, EvalToConstValueResult, GlobalId}; use crate::mir; +use crate::ty::fold::TypeFoldable; use crate::ty::subst::InternalSubsts; use crate::ty::{self, TyCtxt}; use rustc_hir::def_id::DefId; @@ -38,6 +39,13 @@ impl<'tcx> TyCtxt<'tcx> { ct: ty::Unevaluated<'tcx>, span: Option, ) -> EvalToConstValueResult<'tcx> { + // Cannot resolve `Unevaluated` constants that contain inference + // variables. We reject those here since `resolve_opt_const_arg` + // would fail otherwise + if ct.substs.has_infer_types_or_consts() { + return Err(ErrorHandled::TooGeneric); + } + match ty::Instance::resolve_opt_const_arg(self, param_env, ct.def, ct.substs) { Ok(Some(instance)) => { let cid = GlobalId { instance, promoted: ct.promoted }; From 3b9de6b087ffddff6b4e32e070a01799878bc05d Mon Sep 17 00:00:00 2001 From: b-naber Date: Mon, 21 Mar 2022 18:26:53 +0100 Subject: [PATCH 03/12] dont try to unify unevaluated constants that contain infer vars --- compiler/rustc_infer/src/infer/mod.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index e1d0da803fce2..e7fd7d2928e53 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -687,14 +687,23 @@ pub struct CombinedSnapshot<'a, 'tcx> { impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// calls `tcx.try_unify_abstract_consts` after /// canonicalizing the consts. + #[instrument(skip(self), level = "debug")] pub fn try_unify_abstract_consts( &self, a: ty::Unevaluated<'tcx, ()>, b: ty::Unevaluated<'tcx, ()>, param_env: ty::ParamEnv<'tcx>, ) -> bool { + // Reject any attempt to unify two unevaluated constants that contain inference + // variables. + // FIXME `TyCtxt::const_eval_resolve` already rejects the resolution of those + // constants early, but the canonicalization below messes with that mechanism. + if a.substs.has_infer_types_or_consts() || b.substs.has_infer_types_or_consts() { + debug!("a or b contain infer vars in its substs -> cannot unify"); + return false; + } + let canonical = self.canonicalize_query((a, b), &mut OriginalQueryValues::default()); - debug!("canonical consts: {:?}", &canonical.value); self.tcx.try_unify_abstract_consts(param_env.and(canonical.value)) } @@ -1599,6 +1608,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { /// /// This handles inferences variables within both `param_env` and `substs` by /// performing the operation on their respective canonical forms. + #[instrument(skip(self), level = "debug")] pub fn const_eval_resolve( &self, param_env: ty::ParamEnv<'tcx>, @@ -1606,15 +1616,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { span: Option, ) -> EvalToConstValueResult<'tcx> { let substs = self.resolve_vars_if_possible(unevaluated.substs); + debug!(?substs); // Postpone the evaluation of constants whose substs depend on inference // variables if substs.has_infer_types_or_consts() { + debug!("has infer types or consts"); return Err(ErrorHandled::TooGeneric); } let param_env_erased = self.tcx.erase_regions(param_env); let substs_erased = self.tcx.erase_regions(substs); + debug!(?param_env_erased); + debug!(?substs_erased); let unevaluated = ty::Unevaluated { def: unevaluated.def, From 6cf3409e16845bf52e72f4ffdac9db139a24a8d3 Mon Sep 17 00:00:00 2001 From: b-naber Date: Mon, 21 Mar 2022 18:46:20 +0100 Subject: [PATCH 04/12] add/update tests --- .../generic_const_exprs/eval-try-unify.rs | 26 ++++ .../generic_const_exprs/eval-try-unify.stderr | 11 ++ .../ui/const-generics/issues/issue-83765.rs | 13 +- .../const-generics/issues/issue-83765.stderr | 137 ++---------------- 4 files changed, 50 insertions(+), 137 deletions(-) create mode 100644 src/test/ui/const-generics/generic_const_exprs/eval-try-unify.rs create mode 100644 src/test/ui/const-generics/generic_const_exprs/eval-try-unify.stderr diff --git a/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.rs b/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.rs new file mode 100644 index 0000000000000..c59d62e576d9b --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.rs @@ -0,0 +1,26 @@ +// build-pass + +#![feature(generic_const_exprs)] +//~^ WARNING the feature `generic_const_exprs` is incomplete + +trait Generic { + const ASSOC: usize; +} + +impl Generic for u8 { + const ASSOC: usize = 17; +} +impl Generic for u16 { + const ASSOC: usize = 13; +} + + +fn uses_assoc_type() -> [u8; N + T::ASSOC] { + [0; N + T::ASSOC] +} + +fn only_generic_n() -> [u8; N + 13] { + uses_assoc_type::() +} + +fn main() {} diff --git a/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.stderr b/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.stderr new file mode 100644 index 0000000000000..b5719b3fe1dc2 --- /dev/null +++ b/src/test/ui/const-generics/generic_const_exprs/eval-try-unify.stderr @@ -0,0 +1,11 @@ +warning: the feature `generic_const_exprs` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/eval-try-unify.rs:3:12 + | +LL | #![feature(generic_const_exprs)] + | ^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(incomplete_features)]` on by default + = note: see issue #76560 for more information + +warning: 1 warning emitted + diff --git a/src/test/ui/const-generics/issues/issue-83765.rs b/src/test/ui/const-generics/issues/issue-83765.rs index 68536348d3884..67d813819e7a3 100644 --- a/src/test/ui/const-generics/issues/issue-83765.rs +++ b/src/test/ui/const-generics/issues/issue-83765.rs @@ -3,6 +3,7 @@ trait TensorDimension { const DIM : usize; + //~^ ERROR cycle detected when resolving instance ` const ISSCALAR : bool = Self::DIM == 0; fn is_scalar(&self) -> bool {Self::ISSCALAR} } @@ -42,22 +43,16 @@ impl<'a,T : Broadcastable,const DIM : usize> TensorDimension for LazyUpdim<'a,T, impl<'a,T : Broadcastable,const DIM : usize> TensorSize for LazyUpdim<'a,T,{T::DIM},DIM> { fn size(&self) -> [usize;DIM] {self.size} - //~^ ERROR method not compatible with trait } impl<'a,T : Broadcastable,const DIM : usize> Broadcastable for LazyUpdim<'a,T,{T::DIM},DIM> { type Element = T::Element; fn bget(&self,index:[usize;DIM]) -> Option { - //~^ ERROR method not compatible with trait assert!(DIM >= T::DIM); if !self.inbounds(index) {return None} - //~^ ERROR unconstrained generic constant - //~| ERROR mismatched types let size = self.size(); - //~^ ERROR unconstrained generic constant let newindex : [usize;T::DIM] = Default::default(); - //~^ ERROR the trait bound `[usize; _]: Default` is not satisfied self.reference.bget(newindex) } } @@ -76,9 +71,6 @@ impl<'a,R, T : Broadcastable, F : Fn(T::Element) -> R , const DIM: usize> TensorSize for BMap<'a,R,T,F,DIM> { fn size(&self) -> [usize;DIM] {self.reference.size()} - //~^ ERROR unconstrained generic constant - //~| ERROR mismatched types - //~| ERROR method not compatible with trait } impl<'a,R, T : Broadcastable, F : Fn(T::Element) -> R , @@ -86,10 +78,7 @@ impl<'a,R, T : Broadcastable, F : Fn(T::Element) -> R , type Element = R; fn bget(&self,index:[usize;DIM]) -> Option { - //~^ ERROR method not compatible with trait self.reference.bget(index).map(&self.closure) - //~^ ERROR unconstrained generic constant - //~| ERROR mismatched types } } diff --git a/src/test/ui/const-generics/issues/issue-83765.stderr b/src/test/ui/const-generics/issues/issue-83765.stderr index a49f850717f8a..0003969f7ac2d 100644 --- a/src/test/ui/const-generics/issues/issue-83765.stderr +++ b/src/test/ui/const-generics/issues/issue-83765.stderr @@ -1,130 +1,17 @@ -error[E0308]: method not compatible with trait - --> $DIR/issue-83765.rs:44:5 +error[E0391]: cycle detected when resolving instance ` as TensorDimension>::DIM` + --> $DIR/issue-83765.rs:5:5 | -LL | fn size(&self) -> [usize;DIM] {self.size} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` +LL | const DIM : usize; + | ^^^^^^^^^^^^^^^^^^ | - = note: expected type `Self::DIM` - found type `DIM` - -error[E0308]: method not compatible with trait - --> $DIR/issue-83765.rs:51:5 - | -LL | fn bget(&self,index:[usize;DIM]) -> Option { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` - | - = note: expected type `Self::DIM` - found type `DIM` - -error[E0308]: method not compatible with trait - --> $DIR/issue-83765.rs:78:5 - | -LL | fn size(&self) -> [usize;DIM] {self.reference.size()} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` - | - = note: expected type `Self::DIM` - found type `DIM` - -error[E0308]: method not compatible with trait - --> $DIR/issue-83765.rs:88:5 - | -LL | fn bget(&self,index:[usize;DIM]) -> Option { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Self::DIM`, found `DIM` - | - = note: expected type `Self::DIM` - found type `DIM` - -error: unconstrained generic constant - --> $DIR/issue-83765.rs:54:18 - | -LL | if !self.inbounds(index) {return None} - | ^^^^^^^^ - | - = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:` -note: required by a bound in `TensorSize::inbounds` - --> $DIR/issue-83765.rs:12:38 - | -LL | fn inbounds(&self,index : [usize;Self::DIM]) -> bool { - | ^^^^^^^^^ required by this bound in `TensorSize::inbounds` - -error[E0308]: mismatched types - --> $DIR/issue-83765.rs:54:27 - | -LL | if !self.inbounds(index) {return None} - | ^^^^^ expected `Self::DIM`, found `DIM` - | - = note: expected type `Self::DIM` - found type `DIM` - -error: unconstrained generic constant - --> $DIR/issue-83765.rs:57:25 - | -LL | let size = self.size(); - | ^^^^ - | - = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:` -note: required by a bound in `TensorSize::size` - --> $DIR/issue-83765.rs:11:30 - | -LL | fn size(&self) -> [usize;Self::DIM]; - | ^^^^^^^^^ required by this bound in `TensorSize::size` - -error[E0277]: the trait bound `[usize; _]: Default` is not satisfied - --> $DIR/issue-83765.rs:59:41 - | -LL | let newindex : [usize;T::DIM] = Default::default(); - | ^^^^^^^^^^^^^^^^ the trait `Default` is not implemented for `[usize; _]` - | -help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement - | -LL | impl<'a,T : Broadcastable,const DIM : usize> Broadcastable for LazyUpdim<'a,T,{T::DIM},DIM> where [usize; _]: Default - | +++++++++++++++++++++++++ - -error: unconstrained generic constant - --> $DIR/issue-83765.rs:78:51 - | -LL | fn size(&self) -> [usize;DIM] {self.reference.size()} - | ^^^^ - | - = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:` -note: required by a bound in `TensorSize::size` - --> $DIR/issue-83765.rs:11:30 - | -LL | fn size(&self) -> [usize;Self::DIM]; - | ^^^^^^^^^ required by this bound in `TensorSize::size` - -error[E0308]: mismatched types - --> $DIR/issue-83765.rs:78:36 - | -LL | fn size(&self) -> [usize;DIM] {self.reference.size()} - | ^^^^^^^^^^^^^^^^^^^^^ expected `DIM`, found `Self::DIM` - | - = note: expected type `DIM` - found type `Self::DIM` - -error: unconstrained generic constant - --> $DIR/issue-83765.rs:90:24 - | -LL | self.reference.bget(index).map(&self.closure) - | ^^^^ - | - = help: try adding a `where` bound using this expression: `where [(); Self::DIM]:` -note: required by a bound in `Broadcastable::bget` - --> $DIR/issue-83765.rs:20:33 - | -LL | fn bget(&self, index:[usize;Self::DIM]) -> Option; - | ^^^^^^^^^ required by this bound in `Broadcastable::bget` - -error[E0308]: mismatched types - --> $DIR/issue-83765.rs:90:29 - | -LL | self.reference.bget(index).map(&self.closure) - | ^^^^^ expected `Self::DIM`, found `DIM` +note: ...which requires checking if `TensorDimension` fulfills its obligations... + --> $DIR/issue-83765.rs:4:1 | - = note: expected type `Self::DIM` - found type `DIM` +LL | trait TensorDimension { + | ^^^^^^^^^^^^^^^^^^^^^ + = note: ...which again requires resolving instance ` as TensorDimension>::DIM`, completing the cycle + = note: cycle used when normalizing ` as TensorDimension>::DIM` -error: aborting due to 12 previous errors +error: aborting due to previous error -Some errors have detailed explanations: E0277, E0308. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0391`. From 8ff1edbe5e38cede450d926d2761191f07bb0af1 Mon Sep 17 00:00:00 2001 From: b-naber Date: Tue, 22 Mar 2022 10:38:46 +0100 Subject: [PATCH 05/12] fix previous failures and address review --- compiler/rustc_infer/src/infer/mod.rs | 13 +- .../rustc_middle/src/mir/interpret/queries.rs | 2 +- .../src/traits/const_evaluatable.rs | 208 ++++++++++-------- .../src/traits/fulfill.rs | 6 +- .../rustc_trait_selection/src/traits/mod.rs | 5 +- .../src/traits/select/mod.rs | 6 +- compiler/rustc_typeck/src/check/dropck.rs | 2 +- .../ui/const-generics/issues/issue-83765.rs | 118 +++++----- .../const-generics/issues/issue-83765.stderr | 10 +- 9 files changed, 204 insertions(+), 166 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index e7fd7d2928e53..45502c720ae79 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -20,8 +20,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::mir::interpret::{ErrorHandled, EvalToConstValueResult}; use rustc_middle::traits::select; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; @@ -695,9 +694,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { param_env: ty::ParamEnv<'tcx>, ) -> bool { // Reject any attempt to unify two unevaluated constants that contain inference - // variables. - // FIXME `TyCtxt::const_eval_resolve` already rejects the resolution of those - // constants early, but the canonicalization below messes with that mechanism. + // variables, since inference variables in queries lead to ICEs. if a.substs.has_infer_types_or_consts() || b.substs.has_infer_types_or_consts() { debug!("a or b contain infer vars in its substs -> cannot unify"); return false; @@ -1621,8 +1618,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // Postpone the evaluation of constants whose substs depend on inference // variables if substs.has_infer_types_or_consts() { - debug!("has infer types or consts"); - return Err(ErrorHandled::TooGeneric); + debug!("substs have infer types or consts: {:?}", substs); + if substs.has_infer_types_or_consts() { + return Err(ErrorHandled::TooGeneric); + } } let param_env_erased = self.tcx.erase_regions(param_env); diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 155ee210ebe38..639a9bc600ac4 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -43,7 +43,7 @@ impl<'tcx> TyCtxt<'tcx> { // variables. We reject those here since `resolve_opt_const_arg` // would fail otherwise if ct.substs.has_infer_types_or_consts() { - return Err(ErrorHandled::TooGeneric); + bug!("did not expect inference variables here"); } match ty::Instance::resolve_opt_const_arg(self, param_env, ct.def, ct.substs) { diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 0c33ea858fd0d..c44e961363048 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -569,6 +569,18 @@ pub(super) fn thir_abstract_const<'tcx>( } } +/// Tries to unify two abstract constants using structural equality. +#[instrument(skip(tcx), level = "debug")] +pub(super) fn try_unify<'tcx>( + tcx: TyCtxt<'tcx>, + a: AbstractConst<'tcx>, + b: AbstractConst<'tcx>, + param_env: ty::ParamEnv<'tcx>, +) -> bool { + let const_unify_ctxt = ConstUnifyCtxt::new(tcx, param_env); + const_unify_ctxt.try_unify_inner(a, b) +} + pub(super) fn try_unify_abstract_consts<'tcx>( tcx: TyCtxt<'tcx>, (a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>), @@ -622,115 +634,119 @@ where recurse(tcx, ct, &mut f) } -// Substitutes generics repeatedly to allow AbstractConsts to unify where a -// ConstKind::Unevalated could be turned into an AbstractConst that would unify e.g. -// Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])] -#[inline] -#[instrument(skip(tcx), level = "debug")] -fn try_replace_substs_in_root<'tcx>( +pub(super) struct ConstUnifyCtxt<'tcx> { tcx: TyCtxt<'tcx>, - mut abstr_const: AbstractConst<'tcx>, -) -> Option> { - while let Node::Leaf(ct) = abstr_const.root(tcx) { - match AbstractConst::from_const(tcx, ct) { - Ok(Some(act)) => abstr_const = act, - Ok(None) => break, - Err(_) => return None, - } - } - - Some(abstr_const) + param_env: ty::ParamEnv<'tcx>, } -/// Tries to unify two abstract constants using structural equality. -#[instrument(skip(tcx), level = "debug")] -pub(super) fn try_unify<'tcx>( - tcx: TyCtxt<'tcx>, - a: AbstractConst<'tcx>, - b: AbstractConst<'tcx>, - param_env: ty::ParamEnv<'tcx>, -) -> bool { - let a = match try_replace_substs_in_root(tcx, a) { - Some(a) => a, - None => { - return true; +impl<'tcx> ConstUnifyCtxt<'tcx> { + pub(super) fn new(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self { + ConstUnifyCtxt { tcx, param_env } + } + + // Substitutes generics repeatedly to allow AbstractConsts to unify where a + // ConstKind::Unevalated could be turned into an AbstractConst that would unify e.g. + // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])] + #[inline] + #[instrument(skip(self), level = "debug")] + pub(super) fn try_replace_substs_in_root( + &self, + mut abstr_const: AbstractConst<'tcx>, + ) -> Option> { + while let Node::Leaf(ct) = abstr_const.root(self.tcx) { + match AbstractConst::from_const(self.tcx, ct) { + Ok(Some(act)) => abstr_const = act, + Ok(None) => break, + Err(_) => return None, + } } - }; - let b = match try_replace_substs_in_root(tcx, b) { - Some(b) => b, - None => { + Some(abstr_const) + } + + /// Tries to unify two abstract constants using structural equality. + #[instrument(skip(self), level = "debug")] + fn try_unify_inner(&self, a: AbstractConst<'tcx>, b: AbstractConst<'tcx>) -> bool { + let a = if let Some(a) = self.try_replace_substs_in_root(a) { + a + } else { return true; - } - }; + }; - let a_root = a.root(tcx); - let b_root = b.root(tcx); - debug!(?a_root, ?b_root); + let b = if let Some(b) = self.try_replace_substs_in_root(b) { + b + } else { + return true; + }; - match (a_root, b_root) { - (Node::Leaf(a_ct), Node::Leaf(b_ct)) => { - let a_ct = a_ct.eval(tcx, param_env); - debug!("a_ct evaluated: {:?}", a_ct); - let b_ct = b_ct.eval(tcx, param_env); - debug!("b_ct evaluated: {:?}", b_ct); + let a_root = a.root(self.tcx); + let b_root = b.root(self.tcx); + debug!(?a_root, ?b_root); - if a_ct.ty() != b_ct.ty() { - return false; - } + match (a_root, b_root) { + (Node::Leaf(a_ct), Node::Leaf(b_ct)) => { + let a_ct = a_ct.eval(self.tcx, self.param_env); + debug!("a_ct evaluated: {:?}", a_ct); + let b_ct = b_ct.eval(self.tcx, self.param_env); + debug!("b_ct evaluated: {:?}", b_ct); - match (a_ct.val(), b_ct.val()) { - // We can just unify errors with everything to reduce the amount of - // emitted errors here. - (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true, - (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => { - a_param == b_param + if a_ct.ty() != b_ct.ty() { + return false; } - (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, - // If we have `fn a() -> [u8; N + 1]` and `fn b() -> [u8; 1 + M]` - // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This - // means that we only allow inference variables if they are equal. - (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val, - // We expand generic anonymous constants at the start of this function, so this - // branch should only be taking when dealing with associated constants, at - // which point directly comparing them seems like the desired behavior. - // - // FIXME(generic_const_exprs): This isn't actually the case. - // We also take this branch for concrete anonymous constants and - // expand generic anonymous constants with concrete substs. - (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => { - a_uv == b_uv + + match (a_ct.val(), b_ct.val()) { + // We can just unify errors with everything to reduce the amount of + // emitted errors here. + (ty::ConstKind::Error(_), _) | (_, ty::ConstKind::Error(_)) => true, + (ty::ConstKind::Param(a_param), ty::ConstKind::Param(b_param)) => { + a_param == b_param + } + (ty::ConstKind::Value(a_val), ty::ConstKind::Value(b_val)) => a_val == b_val, + // If we have `fn a() -> [u8; N + 1]` and `fn b() -> [u8; 1 + M]` + // we do not want to use `assert_eq!(a(), b())` to infer that `N` and `M` have to be `1`. This + // means that we only allow inference variables if they are equal. + (ty::ConstKind::Infer(a_val), ty::ConstKind::Infer(b_val)) => a_val == b_val, + // We expand generic anonymous constants at the start of this function, so this + // branch should only be taking when dealing with associated constants, at + // which point directly comparing them seems like the desired behavior. + // + // FIXME(generic_const_exprs): This isn't actually the case. + // We also take this branch for concrete anonymous constants and + // expand generic anonymous constants with concrete substs. + (ty::ConstKind::Unevaluated(a_uv), ty::ConstKind::Unevaluated(b_uv)) => { + a_uv == b_uv + } + // FIXME(generic_const_exprs): We may want to either actually try + // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like + // this, for now we just return false here. + _ => false, } - // FIXME(generic_const_exprs): We may want to either actually try - // to evaluate `a_ct` and `b_ct` if they are are fully concrete or something like - // this, for now we just return false here. - _ => false, } + (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => { + self.try_unify_inner(a.subtree(al), b.subtree(bl)) + && self.try_unify_inner(a.subtree(ar), b.subtree(br)) + } + (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => { + self.try_unify_inner(a.subtree(av), b.subtree(bv)) + } + (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args)) + if a_args.len() == b_args.len() => + { + self.try_unify_inner(a.subtree(a_f), b.subtree(b_f)) + && iter::zip(a_args, b_args) + .all(|(&an, &bn)| self.try_unify_inner(a.subtree(an), b.subtree(bn))) + } + (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty)) + if (a_ty == b_ty) && (a_kind == b_kind) => + { + self.try_unify_inner(a.subtree(a_operand), b.subtree(b_operand)) + } + // use this over `_ => false` to make adding variants to `Node` less error prone + (Node::Cast(..), _) + | (Node::FunctionCall(..), _) + | (Node::UnaryOp(..), _) + | (Node::Binop(..), _) + | (Node::Leaf(..), _) => false, } - (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => { - try_unify(tcx, a.subtree(al), b.subtree(bl), param_env) - && try_unify(tcx, a.subtree(ar), b.subtree(br), param_env) - } - (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => { - try_unify(tcx, a.subtree(av), b.subtree(bv), param_env) - } - (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args)) - if a_args.len() == b_args.len() => - { - try_unify(tcx, a.subtree(a_f), b.subtree(b_f), param_env) - && iter::zip(a_args, b_args) - .all(|(&an, &bn)| try_unify(tcx, a.subtree(an), b.subtree(bn), param_env)) - } - (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty)) - if (a_ty == b_ty) && (a_kind == b_kind) => - { - try_unify(tcx, a.subtree(a_operand), b.subtree(b_operand), param_env) - } - // use this over `_ => false` to make adding variants to `Node` less error prone - (Node::Cast(..), _) - | (Node::FunctionCall(..), _) - | (Node::UnaryOp(..), _) - | (Node::Binop(..), _) - | (Node::Leaf(..), _) => false, } } diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index 62c6c8454797a..df1e8cdcde00d 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -581,7 +581,11 @@ impl<'a, 'b, 'tcx> FulfillProcessor<'a, 'b, 'tcx> { if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = (c1.val(), c2.val()) { - if infcx.try_unify_abstract_consts(a.shrink(), b.shrink()) { + if infcx.try_unify_abstract_consts( + a.shrink(), + b.shrink(), + obligation.param_env, + ) { return ProcessResult::Changed(vec![]); } } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index bf94c61e6c2ce..e06ecc528aaa6 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -860,7 +860,10 @@ pub fn provide(providers: &mut ty::query::Providers) { ty::WithOptConstParam { did, const_param_did: Some(param_did) }, ) }, - try_unify_abstract_consts: const_evaluatable::try_unify_abstract_consts, + try_unify_abstract_consts: |tcx, param_env_and| { + let (param_env, (a, b)) = param_env_and.into_parts(); + const_evaluatable::try_unify_abstract_consts(tcx, (a, b), param_env) + }, ..*providers }; } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 8af4606db8520..fe369ca5c461c 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -643,7 +643,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { if let (ty::ConstKind::Unevaluated(a), ty::ConstKind::Unevaluated(b)) = (c1.val(), c2.val()) { - if self.infcx.try_unify_abstract_consts(a.shrink(), b.shrink()) { + if self.infcx.try_unify_abstract_consts( + a.shrink(), + b.shrink(), + obligation.param_env, + ) { return Ok(EvaluatedToOk); } } diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs index 1849ece9f76d9..57b642dc38fab 100644 --- a/compiler/rustc_typeck/src/check/dropck.rs +++ b/compiler/rustc_typeck/src/check/dropck.rs @@ -243,7 +243,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( ( ty::PredicateKind::ConstEvaluatable(a), ty::PredicateKind::ConstEvaluatable(b), - ) => tcx.try_unify_abstract_consts((a, b)), + ) => tcx.try_unify_abstract_consts(self_param_env.and((a, b))), ( ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, lt_a)), ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_b, lt_b)), diff --git a/src/test/ui/const-generics/issues/issue-83765.rs b/src/test/ui/const-generics/issues/issue-83765.rs index 67d813819e7a3..71c164ab0a5bf 100644 --- a/src/test/ui/const-generics/issues/issue-83765.rs +++ b/src/test/ui/const-generics/issues/issue-83765.rs @@ -2,103 +2,115 @@ #![allow(incomplete_features)] trait TensorDimension { - const DIM : usize; - //~^ ERROR cycle detected when resolving instance ` - const ISSCALAR : bool = Self::DIM == 0; - fn is_scalar(&self) -> bool {Self::ISSCALAR} + const DIM: usize; + //~^ ERROR cycle detected when resolving instance + // FIXME Given the current state of the compiler its expected that we cycle here, + // but the cycle is still wrong. + const ISSCALAR: bool = Self::DIM == 0; + fn is_scalar(&self) -> bool { + Self::ISSCALAR + } } -trait TensorSize : TensorDimension { - fn size(&self) -> [usize;Self::DIM]; - fn inbounds(&self,index : [usize;Self::DIM]) -> bool { - index.iter().zip(self.size().iter()).all(|(i,s)| i < s) +trait TensorSize: TensorDimension { + fn size(&self) -> [usize; Self::DIM]; + fn inbounds(&self, index: [usize; Self::DIM]) -> bool { + index.iter().zip(self.size().iter()).all(|(i, s)| i < s) } } - trait Broadcastable: TensorSize + Sized { type Element; - fn bget(&self, index:[usize;Self::DIM]) -> Option; - fn lazy_updim(&self, size : [usize;NEWDIM] ) -> - LazyUpdim - { - assert!(NEWDIM >= Self::DIM, - "Updimmed tensor cannot have fewer indices than the initial one."); - LazyUpdim {size,reference:&self} + fn bget(&self, index: [usize; Self::DIM]) -> Option; + fn lazy_updim( + &self, + size: [usize; NEWDIM], + ) -> LazyUpdim { + assert!( + NEWDIM >= Self::DIM, + "Updimmed tensor cannot have fewer indices than the initial one." + ); + LazyUpdim { size, reference: &self } } - fn bmap T>(&self,foo : F) -> BMap{ - BMap {reference:self,closure : foo} + fn bmap T>(&self, foo: F) -> BMap { + BMap { reference: self, closure: foo } } } - -struct LazyUpdim<'a,T : Broadcastable,const OLDDIM : usize, const DIM : usize> { - size : [usize;DIM], - reference : &'a T +struct LazyUpdim<'a, T: Broadcastable, const OLDDIM: usize, const DIM: usize> { + size: [usize; DIM], + reference: &'a T, } -impl<'a,T : Broadcastable,const DIM : usize> TensorDimension for LazyUpdim<'a,T,{T::DIM},DIM> { - const DIM : usize = DIM; +impl<'a, T: Broadcastable, const DIM: usize> TensorDimension for LazyUpdim<'a, T, { T::DIM }, DIM> { + const DIM: usize = DIM; } -impl<'a,T : Broadcastable,const DIM : usize> TensorSize for LazyUpdim<'a,T,{T::DIM},DIM> { - fn size(&self) -> [usize;DIM] {self.size} +impl<'a, T: Broadcastable, const DIM: usize> TensorSize for LazyUpdim<'a, T, { T::DIM }, DIM> { + fn size(&self) -> [usize; DIM] { + self.size + } } -impl<'a,T : Broadcastable,const DIM : usize> Broadcastable for LazyUpdim<'a,T,{T::DIM},DIM> -{ +impl<'a, T: Broadcastable, const DIM: usize> Broadcastable for LazyUpdim<'a, T, { T::DIM }, DIM> { type Element = T::Element; - fn bget(&self,index:[usize;DIM]) -> Option { + fn bget(&self, index: [usize; DIM]) -> Option { assert!(DIM >= T::DIM); - if !self.inbounds(index) {return None} + if !self.inbounds(index) { + return None; + } let size = self.size(); - let newindex : [usize;T::DIM] = Default::default(); + let newindex: [usize; T::DIM] = Default::default(); self.reference.bget(newindex) } } -struct BMap<'a,R, T : Broadcastable, F : Fn(T::Element) -> R , const DIM: usize> { - reference : &'a T, - closure : F +struct BMap<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> { + reference: &'a T, + closure: F, } -impl<'a,R, T : Broadcastable, F : Fn(T::Element) -> R, - const DIM: usize> TensorDimension for BMap<'a,R,T,F,DIM> { - - const DIM : usize = DIM; +impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> TensorDimension + for BMap<'a, R, T, F, DIM> +{ + const DIM: usize = DIM; } -impl<'a,R, T : Broadcastable, F : Fn(T::Element) -> R , - const DIM: usize> TensorSize for BMap<'a,R,T,F,DIM> { - - fn size(&self) -> [usize;DIM] {self.reference.size()} +impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> TensorSize + for BMap<'a, R, T, F, DIM> +{ + fn size(&self) -> [usize; DIM] { + self.reference.size() + } } -impl<'a,R, T : Broadcastable, F : Fn(T::Element) -> R , - const DIM: usize> Broadcastable for BMap<'a,R,T,F,DIM> { - +impl<'a, R, T: Broadcastable, F: Fn(T::Element) -> R, const DIM: usize> Broadcastable + for BMap<'a, R, T, F, DIM> +{ type Element = R; - fn bget(&self,index:[usize;DIM]) -> Option { + fn bget(&self, index: [usize; DIM]) -> Option { self.reference.bget(index).map(&self.closure) } } impl TensorDimension for Vec { - const DIM : usize = 1; + const DIM: usize = 1; } impl TensorSize for Vec { - fn size(&self) -> [usize;1] {[self.len()]} + fn size(&self) -> [usize; 1] { + [self.len()] + } } impl Broadcastable for Vec { type Element = T; - fn bget(& self,index : [usize;1]) -> Option { + fn bget(&self, index: [usize; 1]) -> Option { self.get(index[0]).cloned() } } fn main() { - let v = vec![1,2,3]; - let bv = v.lazy_updim([3,4]); - let bbv = bv.bmap(|x| x*x); + let v = vec![1, 2, 3]; + let bv = v.lazy_updim([3, 4]); + let bbv = bv.bmap(|x| x * x); - println!("The size of v is {:?}",bbv.bget([0,2]).expect("Out of bounds.")); + println!("The size of v is {:?}", bbv.bget([0, 2]).expect("Out of bounds.")); } diff --git a/src/test/ui/const-generics/issues/issue-83765.stderr b/src/test/ui/const-generics/issues/issue-83765.stderr index 0003969f7ac2d..8705a39fa4bcd 100644 --- a/src/test/ui/const-generics/issues/issue-83765.stderr +++ b/src/test/ui/const-generics/issues/issue-83765.stderr @@ -1,16 +1,16 @@ -error[E0391]: cycle detected when resolving instance ` as TensorDimension>::DIM` +error[E0391]: cycle detected when resolving instance ` as TensorDimension>::DIM` --> $DIR/issue-83765.rs:5:5 | -LL | const DIM : usize; - | ^^^^^^^^^^^^^^^^^^ +LL | const DIM: usize; + | ^^^^^^^^^^^^^^^^^ | note: ...which requires checking if `TensorDimension` fulfills its obligations... --> $DIR/issue-83765.rs:4:1 | LL | trait TensorDimension { | ^^^^^^^^^^^^^^^^^^^^^ - = note: ...which again requires resolving instance ` as TensorDimension>::DIM`, completing the cycle - = note: cycle used when normalizing ` as TensorDimension>::DIM` + = note: ...which again requires resolving instance ` as TensorDimension>::DIM`, completing the cycle + = note: cycle used when normalizing ` as TensorDimension>::DIM` error: aborting due to previous error From fe69a5cf0cba9943311b350af0bc800f52c6adbd Mon Sep 17 00:00:00 2001 From: b-naber Date: Tue, 22 Mar 2022 15:27:20 +0100 Subject: [PATCH 06/12] dont canonicalize in try_unify_abstract_consts and erase regions instead --- compiler/rustc_infer/src/infer/mod.rs | 18 +++++----- .../src/traits/const_evaluatable.rs | 33 +++++++------------ 2 files changed, 22 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 45502c720ae79..a779e38a16989 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -70,7 +70,6 @@ mod sub; pub mod type_variable; mod undo_log; -use crate::infer::canonical::OriginalQueryValues; pub use rustc_middle::infer::unify_key; #[must_use] @@ -695,14 +694,19 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { ) -> bool { // Reject any attempt to unify two unevaluated constants that contain inference // variables, since inference variables in queries lead to ICEs. - if a.substs.has_infer_types_or_consts() || b.substs.has_infer_types_or_consts() { - debug!("a or b contain infer vars in its substs -> cannot unify"); + if a.substs.has_infer_types_or_consts() + || b.substs.has_infer_types_or_consts() + || param_env.has_infer_types_or_consts() + { + debug!("a or b or param_env contain infer vars in its substs -> cannot unify"); return false; } - let canonical = self.canonicalize_query((a, b), &mut OriginalQueryValues::default()); + let erased_args = self.tcx.erase_regions((a, b)); + let erased_param_env = self.tcx.erase_regions(param_env); + debug!("after erase_regions args: {:?}, param_env: {:?}", erased_args, param_env); - self.tcx.try_unify_abstract_consts(param_env.and(canonical.value)) + self.tcx.try_unify_abstract_consts(erased_param_env.and(erased_args)) } pub fn is_in_snapshot(&self) -> bool { @@ -1619,9 +1623,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { // variables if substs.has_infer_types_or_consts() { debug!("substs have infer types or consts: {:?}", substs); - if substs.has_infer_types_or_consts() { - return Err(ErrorHandled::TooGeneric); - } + return Err(ErrorHandled::TooGeneric); } let param_env_erased = self.tcx.erase_regions(param_env); diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index c44e961363048..93810cb00cfdd 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -195,11 +195,13 @@ fn satisfied_from_param_env<'tcx>( match pred.kind().skip_binder() { ty::PredicateKind::ConstEvaluatable(uv) => { if let Some(b_ct) = AbstractConst::new(tcx, uv)? { + let const_unify_ctxt = ConstUnifyCtxt::new(tcx, param_env); + // Try to unify with each subtree in the AbstractConst to allow for // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable` // predicate for `(N + 1) * 2` let result = walk_abstract_const(tcx, b_ct, |b_ct| { - match try_unify(tcx, ct, b_ct, param_env) { + match const_unify_ctxt.try_unify(ct, b_ct) { true => ControlFlow::BREAK, false => ControlFlow::CONTINUE, } @@ -569,18 +571,6 @@ pub(super) fn thir_abstract_const<'tcx>( } } -/// Tries to unify two abstract constants using structural equality. -#[instrument(skip(tcx), level = "debug")] -pub(super) fn try_unify<'tcx>( - tcx: TyCtxt<'tcx>, - a: AbstractConst<'tcx>, - b: AbstractConst<'tcx>, - param_env: ty::ParamEnv<'tcx>, -) -> bool { - let const_unify_ctxt = ConstUnifyCtxt::new(tcx, param_env); - const_unify_ctxt.try_unify_inner(a, b) -} - pub(super) fn try_unify_abstract_consts<'tcx>( tcx: TyCtxt<'tcx>, (a, b): (ty::Unevaluated<'tcx, ()>, ty::Unevaluated<'tcx, ()>), @@ -589,7 +579,8 @@ pub(super) fn try_unify_abstract_consts<'tcx>( (|| { if let Some(a) = AbstractConst::new(tcx, a)? { if let Some(b) = AbstractConst::new(tcx, b)? { - return Ok(try_unify(tcx, a, b, param_env)); + let const_unify_ctxt = ConstUnifyCtxt::new(tcx, param_env); + return Ok(const_unify_ctxt.try_unify(a, b)); } } @@ -666,7 +657,7 @@ impl<'tcx> ConstUnifyCtxt<'tcx> { /// Tries to unify two abstract constants using structural equality. #[instrument(skip(self), level = "debug")] - fn try_unify_inner(&self, a: AbstractConst<'tcx>, b: AbstractConst<'tcx>) -> bool { + fn try_unify(&self, a: AbstractConst<'tcx>, b: AbstractConst<'tcx>) -> bool { let a = if let Some(a) = self.try_replace_substs_in_root(a) { a } else { @@ -723,23 +714,23 @@ impl<'tcx> ConstUnifyCtxt<'tcx> { } } (Node::Binop(a_op, al, ar), Node::Binop(b_op, bl, br)) if a_op == b_op => { - self.try_unify_inner(a.subtree(al), b.subtree(bl)) - && self.try_unify_inner(a.subtree(ar), b.subtree(br)) + self.try_unify(a.subtree(al), b.subtree(bl)) + && self.try_unify(a.subtree(ar), b.subtree(br)) } (Node::UnaryOp(a_op, av), Node::UnaryOp(b_op, bv)) if a_op == b_op => { - self.try_unify_inner(a.subtree(av), b.subtree(bv)) + self.try_unify(a.subtree(av), b.subtree(bv)) } (Node::FunctionCall(a_f, a_args), Node::FunctionCall(b_f, b_args)) if a_args.len() == b_args.len() => { - self.try_unify_inner(a.subtree(a_f), b.subtree(b_f)) + self.try_unify(a.subtree(a_f), b.subtree(b_f)) && iter::zip(a_args, b_args) - .all(|(&an, &bn)| self.try_unify_inner(a.subtree(an), b.subtree(bn))) + .all(|(&an, &bn)| self.try_unify(a.subtree(an), b.subtree(bn))) } (Node::Cast(a_kind, a_operand, a_ty), Node::Cast(b_kind, b_operand, b_ty)) if (a_ty == b_ty) && (a_kind == b_kind) => { - self.try_unify_inner(a.subtree(a_operand), b.subtree(b_operand)) + self.try_unify(a.subtree(a_operand), b.subtree(b_operand)) } // use this over `_ => false` to make adding variants to `Node` less error prone (Node::Cast(..), _) From 11a70dbc8ac49416598b0ff4525096845a0dc498 Mon Sep 17 00:00:00 2001 From: b-naber Date: Tue, 22 Mar 2022 16:13:28 +0100 Subject: [PATCH 07/12] erase region in ParamEnvAnd and make ConstUnifyCtxt private --- compiler/rustc_infer/src/infer/mod.rs | 8 ++++---- compiler/rustc_middle/src/mir/interpret/queries.rs | 5 ++++- .../src/traits/const_evaluatable.rs | 12 ++++-------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index a779e38a16989..2886d921c705d 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -702,11 +702,11 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { return false; } - let erased_args = self.tcx.erase_regions((a, b)); - let erased_param_env = self.tcx.erase_regions(param_env); - debug!("after erase_regions args: {:?}, param_env: {:?}", erased_args, param_env); + let param_env_and = param_env.and((a, b)); + let erased = self.tcx.erase_regions(param_env_and); + debug!("after erase_regions: {:?}", erased); - self.tcx.try_unify_abstract_consts(erased_param_env.and(erased_args)) + self.tcx.try_unify_abstract_consts(erased) } pub fn is_in_snapshot(&self) -> bool { diff --git a/compiler/rustc_middle/src/mir/interpret/queries.rs b/compiler/rustc_middle/src/mir/interpret/queries.rs index 639a9bc600ac4..7e5989b4112cf 100644 --- a/compiler/rustc_middle/src/mir/interpret/queries.rs +++ b/compiler/rustc_middle/src/mir/interpret/queries.rs @@ -41,7 +41,10 @@ impl<'tcx> TyCtxt<'tcx> { ) -> EvalToConstValueResult<'tcx> { // Cannot resolve `Unevaluated` constants that contain inference // variables. We reject those here since `resolve_opt_const_arg` - // would fail otherwise + // would fail otherwise. + // + // When trying to evaluate constants containing inference variables, + // use `Infcx::const_eval_resolve` instead. if ct.substs.has_infer_types_or_consts() { bug!("did not expect inference variables here"); } diff --git a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs index 93810cb00cfdd..4fb186ab0efe8 100644 --- a/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs +++ b/compiler/rustc_trait_selection/src/traits/const_evaluatable.rs @@ -195,7 +195,7 @@ fn satisfied_from_param_env<'tcx>( match pred.kind().skip_binder() { ty::PredicateKind::ConstEvaluatable(uv) => { if let Some(b_ct) = AbstractConst::new(tcx, uv)? { - let const_unify_ctxt = ConstUnifyCtxt::new(tcx, param_env); + let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env }; // Try to unify with each subtree in the AbstractConst to allow for // `N + 1` being const evaluatable even if theres only a `ConstEvaluatable` @@ -579,7 +579,7 @@ pub(super) fn try_unify_abstract_consts<'tcx>( (|| { if let Some(a) = AbstractConst::new(tcx, a)? { if let Some(b) = AbstractConst::new(tcx, b)? { - let const_unify_ctxt = ConstUnifyCtxt::new(tcx, param_env); + let const_unify_ctxt = ConstUnifyCtxt { tcx, param_env }; return Ok(const_unify_ctxt.try_unify(a, b)); } } @@ -625,22 +625,18 @@ where recurse(tcx, ct, &mut f) } -pub(super) struct ConstUnifyCtxt<'tcx> { +struct ConstUnifyCtxt<'tcx> { tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, } impl<'tcx> ConstUnifyCtxt<'tcx> { - pub(super) fn new(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Self { - ConstUnifyCtxt { tcx, param_env } - } - // Substitutes generics repeatedly to allow AbstractConsts to unify where a // ConstKind::Unevalated could be turned into an AbstractConst that would unify e.g. // Param(N) should unify with Param(T), substs: [Unevaluated("T2", [Unevaluated("T3", [Param(N)])])] #[inline] #[instrument(skip(self), level = "debug")] - pub(super) fn try_replace_substs_in_root( + fn try_replace_substs_in_root( &self, mut abstr_const: AbstractConst<'tcx>, ) -> Option> { From fe40240e4d4154c349e9c76514badbfed7a0a0cf Mon Sep 17 00:00:00 2001 From: Jakob Degen Date: Sat, 5 Mar 2022 20:37:04 -0500 Subject: [PATCH 08/12] Clarify which kinds of MIR are allowed during which phases. This enhances documentation with these details and extends the validator to check these requirements more thoroughly. As a part of this, we add a new `Deaggregated` phase, and rename other phases so that their names more naturally correspond to what they represent. --- .../src/transform/promote_consts.rs | 2 +- .../src/transform/validate.rs | 74 ++++++++++++------- compiler/rustc_middle/src/mir/mod.rs | 56 +++++++++----- .../rustc_mir_transform/src/deaggregator.rs | 4 + .../src/elaborate_drops.rs | 2 +- compiler/rustc_mir_transform/src/generator.rs | 2 +- compiler/rustc_mir_transform/src/lib.rs | 8 +- .../rustc_mir_transform/src/pass_manager.rs | 2 +- 8 files changed, 98 insertions(+), 52 deletions(-) diff --git a/compiler/rustc_const_eval/src/transform/promote_consts.rs b/compiler/rustc_const_eval/src/transform/promote_consts.rs index 30764f689c95c..faea2111d9210 100644 --- a/compiler/rustc_const_eval/src/transform/promote_consts.rs +++ b/compiler/rustc_const_eval/src/transform/promote_consts.rs @@ -42,7 +42,7 @@ pub struct PromoteTemps<'tcx> { impl<'tcx> MirPass<'tcx> for PromoteTemps<'tcx> { fn phase_change(&self) -> Option { - Some(MirPhase::ConstPromotion) + Some(MirPhase::ConstsPromoted) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index cf15fc4ddc3a5..deeca78b75d99 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -266,22 +266,15 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ); } } - // The deaggregator currently does not deaggreagate arrays. - // So for now, we ignore them here. - Rvalue::Aggregate(box AggregateKind::Array { .. }, _) => {} - // All other aggregates must be gone after some phases. - Rvalue::Aggregate(box kind, _) => { - if self.mir_phase > MirPhase::DropLowering - && !matches!(kind, AggregateKind::Generator(..)) - { - // Generators persist until the state machine transformation, but all - // other aggregates must have been lowered. - self.fail( - location, - format!("{:?} have been lowered to field assignments", rvalue), - ) - } else if self.mir_phase > MirPhase::GeneratorLowering { - // No more aggregates after drop and generator lowering. + Rvalue::Aggregate(agg_kind, _) => { + let disallowed = match **agg_kind { + AggregateKind::Array(..) => false, + AggregateKind::Generator(..) => { + self.mir_phase >= MirPhase::GeneratorsLowered + } + _ => self.mir_phase >= MirPhase::Deaggregated, + }; + if disallowed { self.fail( location, format!("{:?} have been lowered to field assignments", rvalue), @@ -289,7 +282,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } Rvalue::Ref(_, BorrowKind::Shallow, _) => { - if self.mir_phase > MirPhase::DropLowering { + if self.mir_phase >= MirPhase::DropsLowered { self.fail( location, "`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase", @@ -300,7 +293,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } StatementKind::AscribeUserType(..) => { - if self.mir_phase > MirPhase::DropLowering { + if self.mir_phase >= MirPhase::DropsLowered { self.fail( location, "`AscribeUserType` should have been removed after drop lowering phase", @@ -308,7 +301,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } StatementKind::FakeRead(..) => { - if self.mir_phase > MirPhase::DropLowering { + if self.mir_phase >= MirPhase::DropsLowered { self.fail( location, "`FakeRead` should have been removed after drop lowering phase", @@ -351,10 +344,18 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.fail(location, format!("bad arg ({:?} != usize)", op_cnt_ty)) } } - StatementKind::SetDiscriminant { .. } - | StatementKind::StorageLive(..) + StatementKind::SetDiscriminant { .. } => { + if self.mir_phase < MirPhase::DropsLowered { + self.fail(location, "`SetDiscriminant` is not allowed until drop elaboration"); + } + } + StatementKind::Retag(_, _) => { + // FIXME(JakobDegen) The validator should check that `self.mir_phase < + // DropsLowered`. However, this causes ICEs with generation of drop shims, which + // seem to fail to set their `MirPhase` correctly. + } + StatementKind::StorageLive(..) | StatementKind::StorageDead(..) - | StatementKind::Retag(_, _) | StatementKind::Coverage(_) | StatementKind::Nop => {} } @@ -424,10 +425,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } TerminatorKind::DropAndReplace { target, unwind, .. } => { - if self.mir_phase > MirPhase::DropLowering { + if self.mir_phase >= MirPhase::DropsLowered { self.fail( location, - "`DropAndReplace` is not permitted to exist after drop elaboration", + "`DropAndReplace` should have been removed during drop elaboration", ); } self.check_edge(location, *target, EdgeKind::Normal); @@ -494,7 +495,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } TerminatorKind::Yield { resume, drop, .. } => { - if self.mir_phase > MirPhase::GeneratorLowering { + if self.mir_phase >= MirPhase::GeneratorsLowered { self.fail(location, "`Yield` should have been replaced by generator lowering"); } self.check_edge(location, *resume, EdgeKind::Normal); @@ -503,10 +504,22 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } TerminatorKind::FalseEdge { real_target, imaginary_target } => { + if self.mir_phase >= MirPhase::DropsLowered { + self.fail( + location, + "`FalseEdge` should have been removed after drop elaboration", + ); + } self.check_edge(location, *real_target, EdgeKind::Normal); self.check_edge(location, *imaginary_target, EdgeKind::Normal); } TerminatorKind::FalseUnwind { real_target, unwind } => { + if self.mir_phase >= MirPhase::DropsLowered { + self.fail( + location, + "`FalseUnwind` should have been removed after drop elaboration", + ); + } self.check_edge(location, *real_target, EdgeKind::Normal); if let Some(unwind) = unwind { self.check_edge(location, *unwind, EdgeKind::Unwind); @@ -520,12 +533,19 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { self.check_edge(location, *cleanup, EdgeKind::Unwind); } } + TerminatorKind::GeneratorDrop => { + if self.mir_phase >= MirPhase::GeneratorsLowered { + self.fail( + location, + "`GeneratorDrop` should have been replaced by generator lowering", + ); + } + } // Nothing to validate for these. TerminatorKind::Resume | TerminatorKind::Abort | TerminatorKind::Return - | TerminatorKind::Unreachable - | TerminatorKind::GeneratorDrop => {} + | TerminatorKind::Unreachable => {} } self.super_terminator(terminator, location); diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index a5468b3f4f202..59cd7d7dda7f3 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -127,14 +127,11 @@ pub trait MirPass<'tcx> { /// These phases all describe dialects of MIR. Since all MIR uses the same datastructures, the /// dialects forbid certain variants or values in certain phases. /// -/// Note: Each phase's validation checks all invariants of the *previous* phases' dialects. A phase -/// that changes the dialect documents what invariants must be upheld *after* that phase finishes. -/// /// Warning: ordering of variants is significant. #[derive(Copy, Clone, TyEncodable, TyDecodable, Debug, PartialEq, Eq, PartialOrd, Ord)] #[derive(HashStable)] pub enum MirPhase { - Build = 0, + Built = 0, // FIXME(oli-obk): it's unclear whether we still need this phase (and its corresponding query). // We used to have this for pre-miri MIR based const eval. Const = 1, @@ -142,17 +139,32 @@ pub enum MirPhase { /// by creating a new MIR body per promoted element. After this phase (and thus the termination /// of the `mir_promoted` query), these promoted elements are available in the `promoted_mir` /// query. - ConstPromotion = 2, - /// After this phase - /// * the only `AggregateKind`s allowed are `Array` and `Generator`, - /// * `DropAndReplace` is gone for good - /// * `Drop` now uses explicit drop flags visible in the MIR and reaching a `Drop` terminator - /// means that the auto-generated drop glue will be invoked. - DropLowering = 3, - /// After this phase, generators are explicit state machines (no more `Yield`). - /// `AggregateKind::Generator` is gone for good. - GeneratorLowering = 4, - Optimization = 5, + ConstsPromoted = 2, + /// Beginning with this phase, the following variants are disallowed: + /// * [`TerminatorKind::DropAndReplace`](terminator::TerminatorKind::DropAndReplace) + /// * [`TerminatorKind::FalseUnwind`](terminator::TerminatorKind::FalseUnwind) + /// * [`TerminatorKind::FalseEdge`](terminator::TerminatorKind::FalseEdge) + /// * [`StatementKind::FakeRead`] + /// * [`StatementKind::AscribeUserType`] + /// * [`Rvalue::Ref`] with `BorrowKind::Shallow` + /// + /// And the following variant is allowed: + /// * [`StatementKind::Retag`] + /// + /// Furthermore, `Drop` now uses explicit drop flags visible in the MIR and reaching a `Drop` + /// terminator means that the auto-generated drop glue will be invoked. + DropsLowered = 3, + /// Beginning with this phase, the following variant is disallowed: + /// * [`Rvalue::Aggregate`] for any `AggregateKind` except `Array` + /// + /// And the following variant is allowed: + /// * [`StatementKind::SetDiscriminant`] + Deaggregated = 4, + /// Beginning with this phase, the following variants are disallowed: + /// * [`TerminatorKind::Yield`](terminator::TerminatorKind::Yield) + /// * [`TerminatorKind::GeneratorDrop](terminator::TerminatorKind::GeneratorDrop) + GeneratorsLowered = 5, + Optimized = 6, } impl MirPhase { @@ -311,7 +323,7 @@ impl<'tcx> Body<'tcx> { ); let mut body = Body { - phase: MirPhase::Build, + phase: MirPhase::Built, source, basic_blocks, source_scopes, @@ -346,7 +358,7 @@ impl<'tcx> Body<'tcx> { /// crate. pub fn new_cfg_only(basic_blocks: IndexVec>) -> Self { let mut body = Body { - phase: MirPhase::Build, + phase: MirPhase::Built, source: MirSource::item(DefId::local(CRATE_DEF_INDEX)), basic_blocks, source_scopes: IndexVec::new(), @@ -1541,6 +1553,11 @@ impl Statement<'_> { } } +/// The various kinds of statements that can appear in MIR. +/// +/// Not all of these are allowed at every [`MirPhase`]. Check the documentation there to see which +/// ones you do not have to worry about. The MIR validator will generally enforce such restrictions, +/// causing an ICE if they are violated. #[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)] pub enum StatementKind<'tcx> { /// Write the RHS Rvalue to the LHS Place. @@ -2223,6 +2240,11 @@ impl<'tcx> Operand<'tcx> { /// Rvalues #[derive(Clone, TyEncodable, TyDecodable, Hash, HashStable, PartialEq)] +/// The various kinds of rvalues that can appear in MIR. +/// +/// Not all of these are allowed at every [`MirPhase`]. Check the documentation there to see which +/// ones you do not have to worry about. The MIR validator will generally enforce such restrictions, +/// causing an ICE if they are violated. pub enum Rvalue<'tcx> { /// x (either a move or copy, depending on type of x) Use(Operand<'tcx>), diff --git a/compiler/rustc_mir_transform/src/deaggregator.rs b/compiler/rustc_mir_transform/src/deaggregator.rs index 44753c5f631ca..616ba819982ec 100644 --- a/compiler/rustc_mir_transform/src/deaggregator.rs +++ b/compiler/rustc_mir_transform/src/deaggregator.rs @@ -6,6 +6,10 @@ use rustc_middle::ty::TyCtxt; pub struct Deaggregator; impl<'tcx> MirPass<'tcx> for Deaggregator { + fn phase_change(&self) -> Option { + Some(MirPhase::Deaggregated) + } + fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut(); let local_decls = &*local_decls; diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index a4b1d86ff610e..f78c7a084d89a 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -20,7 +20,7 @@ pub struct ElaborateDrops; impl<'tcx> MirPass<'tcx> for ElaborateDrops { fn phase_change(&self) -> Option { - Some(MirPhase::DropLowering) + Some(MirPhase::DropsLowered) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/generator.rs b/compiler/rustc_mir_transform/src/generator.rs index 8bc7f5e9ce2e0..ad96bf544cbd3 100644 --- a/compiler/rustc_mir_transform/src/generator.rs +++ b/compiler/rustc_mir_transform/src/generator.rs @@ -1235,7 +1235,7 @@ fn create_cases<'tcx>( impl<'tcx> MirPass<'tcx> for StateTransform { fn phase_change(&self) -> Option { - Some(MirPhase::GeneratorLowering) + Some(MirPhase::GeneratorsLowered) } fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 3b2332a6e3142..a6436ae515451 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -341,7 +341,7 @@ fn inner_mir_for_ctfe(tcx: TyCtxt<'_>, def: ty::WithOptConstParam) - pm::run_passes( tcx, &mut body, - &[&const_prop::ConstProp, &marker::PhaseChange(MirPhase::Optimization)], + &[&const_prop::ConstProp, &marker::PhaseChange(MirPhase::Optimized)], ); } } @@ -398,7 +398,7 @@ fn mir_drops_elaborated_and_const_checked<'tcx>( } run_post_borrowck_cleanup_passes(tcx, &mut body); - assert!(body.phase == MirPhase::DropLowering); + assert!(body.phase == MirPhase::Deaggregated); tcx.alloc_steal_mir(body) } @@ -458,7 +458,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { ], ); - assert!(body.phase == MirPhase::GeneratorLowering); + assert!(body.phase == MirPhase::GeneratorsLowered); // The main optimizations that we do on MIR. pm::run_passes( @@ -495,7 +495,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { &deduplicate_blocks::DeduplicateBlocks, // Some cleanup necessary at least for LLVM and potentially other codegen backends. &add_call_guards::CriticalCallEdges, - &marker::PhaseChange(MirPhase::Optimization), + &marker::PhaseChange(MirPhase::Optimized), // Dump the end result for testing and debugging purposes. &dump_mir::Marker("PreCodegen"), ], diff --git a/compiler/rustc_mir_transform/src/pass_manager.rs b/compiler/rustc_mir_transform/src/pass_manager.rs index 8725eae870917..740a2168b41cc 100644 --- a/compiler/rustc_mir_transform/src/pass_manager.rs +++ b/compiler/rustc_mir_transform/src/pass_manager.rs @@ -114,7 +114,7 @@ pub fn run_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, passes: &[&dyn } } - if validate || body.phase == MirPhase::Optimization { + if validate || body.phase == MirPhase::Optimized { validate_body(tcx, body, format!("end of phase transition to {:?}", body.phase)); } } From 26d7b8ddb2d323e2d6cb77c04caf7c58ce5af267 Mon Sep 17 00:00:00 2001 From: Jakob Degen Date: Sun, 6 Mar 2022 15:53:23 -0500 Subject: [PATCH 09/12] Clarify more MIR docs --- compiler/rustc_middle/src/mir/mod.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index 59cd7d7dda7f3..ea71cff161612 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -1561,6 +1561,8 @@ impl Statement<'_> { #[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable)] pub enum StatementKind<'tcx> { /// Write the RHS Rvalue to the LHS Place. + /// + /// The LHS place may not overlap with any memory accessed on the RHS. Assign(Box<(Place<'tcx>, Rvalue<'tcx>)>), /// This represents all the reading that a pattern match may do @@ -1778,6 +1780,19 @@ static_assert_size!(Place<'_>, 16); pub enum ProjectionElem { Deref, Field(Field, T), + /// Index into a slice/array. + /// + /// Note that this does not also dereference, and so it does not exactly correspond to slice + /// indexing in Rust. In other words, in the below Rust code: + /// + /// ```rust + /// let x = &[1, 2, 3, 4]; + /// let i = 2; + /// x[i]; + /// ``` + /// + /// The `x[i]` is turned into a `Deref` followed by an `Index`, not just an `Index`. The same + /// thing is true of the `ConstantIndex` and `Subslice` projections below. Index(V), /// These indices are generated by slice patterns. Easiest to explain From e169261a6fc4410cd1ff9cdc37da6ef3cc5e0600 Mon Sep 17 00:00:00 2001 From: Michael Woerister Date: Thu, 24 Mar 2022 10:59:28 +0100 Subject: [PATCH 10/12] debuginfo: Fix debuginfo for Box where T is unsized. Before this fix, the debuginfo for the fields was generated from the struct defintion of Box, but (at least at the moment) the compiler pretends that Box is just a (fat) pointer, so the fields need to be `pointer` and `vtable` instead of `__0: Unique` and `__1: Allocator`. This is meant as a temporary mitigation until we can make sure that simply treating Box as a regular struct in debuginfo does not cause too much breakage in the ecosystem. --- .../src/debuginfo/metadata.rs | 19 ++++++++++++++++++- src/test/debuginfo/unsized.rs | 19 +++++++++++++++---- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs index f02c7b2d2e11a..74e194750fadd 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs @@ -166,6 +166,13 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( pointee_type: Ty<'tcx>, unique_type_id: UniqueTypeId<'tcx>, ) -> DINodeCreationResult<'ll> { + // The debuginfo generated by this function is only valid if `ptr_type` is really just + // a (fat) pointer. Make sure it is not called for e.g. `Box`. + debug_assert_eq!( + cx.size_and_align_of(ptr_type), + cx.size_and_align_of(cx.tcx.mk_mut_ptr(pointee_type)) + ); + let pointee_type_di_node = type_di_node(cx, pointee_type); return_if_di_node_created_in_meantime!(cx, unique_type_id); @@ -212,7 +219,17 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( DIFlags::FlagZero, ), |cx, owner| { - let layout = cx.layout_of(ptr_type); + // FIXME: If this fat pointer is a `Box` then we don't want to use its + // type layout and instead use the layout of the raw pointer inside + // of it. + // The proper way to handle this is to not treat Box as a pointer + // at all and instead emit regular struct debuginfo for it. We just + // need to make sure that we don't break existing debuginfo consumers + // by doing that (at least not without a warning period). + let layout_type = + if ptr_type.is_box() { cx.tcx.mk_mut_ptr(pointee_type) } else { ptr_type }; + + let layout = cx.layout_of(layout_type); let addr_field = layout.field(cx, abi::FAT_PTR_ADDR); let extra_field = layout.field(cx, abi::FAT_PTR_EXTRA); diff --git a/src/test/debuginfo/unsized.rs b/src/test/debuginfo/unsized.rs index 7ccc88ef940dc..7cb0002ca5125 100644 --- a/src/test/debuginfo/unsized.rs +++ b/src/test/debuginfo/unsized.rs @@ -16,13 +16,17 @@ // gdbg-check:$3 = {pointer = [...], vtable = [...]} // gdbr-check:$3 = &unsized::Foo {pointer: [...], vtable: [...]} +// gdb-command:print _box +// gdbg-check:$4 = {pointer = [...], vtable = [...]} +// gdbr-check:$4 = alloc::boxed::Box, alloc::alloc::Global> {pointer: [...], vtable: [...]} + // gdb-command:print tuple_slice -// gdbg-check:$4 = {data_ptr = [...], length = 2} -// gdbr-check:$4 = &(i32, i32, [i32]) {data_ptr: [...], length: 2} +// gdbg-check:$5 = {data_ptr = [...], length = 2} +// gdbr-check:$5 = &(i32, i32, [i32]) {data_ptr: [...], length: 2} // gdb-command:print tuple_dyn -// gdbg-check:$5 = {pointer = [...], vtable = [...]} -// gdbr-check:$5 = &(i32, i32, dyn core::fmt::Debug) {pointer: [...], vtable: [...]} +// gdbg-check:$6 = {pointer = [...], vtable = [...]} +// gdbr-check:$6 = &(i32, i32, dyn core::fmt::Debug) {pointer: [...], vtable: [...]} // === CDB TESTS =================================================================================== @@ -42,6 +46,12 @@ // cdb-check: [+0x000] pointer : 0x[...] [Type: unsized::Foo > *] // cdb-check: [...] vtable : 0x[...] [Type: unsigned [...]int[...] (*)[3]] +// cdb-command:dx _box +// cdb-check: +// cdb-check:_box [Type: alloc::boxed::Box >,alloc::alloc::Global>] +// cdb-check:[+0x000] pointer : 0x[...] [Type: unsized::Foo > *] +// cdb-check:[...] vtable : 0x[...] [Type: unsigned [...]int[...] (*)[3]] + // cdb-command:dx tuple_slice // cdb-check:tuple_slice [Type: ref$ > >] // cdb-check: [+0x000] data_ptr : 0x[...] [Type: tuple$ > *] @@ -69,6 +79,7 @@ fn main() { let a: &Foo<[u8]> = &foo.value; let b: &Foo> = &foo; let c: &Foo = &Foo { value: 7i32 }; + let _box: Box> = Box::new(Foo { value: 8i32 }); // Also check unsized tuples let tuple_slice: &(i32, i32, [i32]) = &(0, 1, [2, 3]); From 64ad96dd9a9a1725ee00ba1033abc70f96d2c5b1 Mon Sep 17 00:00:00 2001 From: Max Baumann Date: Thu, 24 Mar 2022 17:05:47 +0100 Subject: [PATCH 11/12] add diagnostic items for clippy's --- compiler/rustc_span/src/symbol.rs | 4 ++++ library/core/src/str/mod.rs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 48ca321b73746..5cf362bfa7e98 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1347,6 +1347,10 @@ symbols! { store, str, str_alloc, + str_split_whitespace, + str_trim, + str_trim_end, + str_trim_start, stringify, stringify_macro, struct_field_attributes, diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index b1d36f2710737..c603420f0f862 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -904,6 +904,7 @@ impl str { #[must_use = "this returns the split string as an iterator, \ without modifying the original"] #[stable(feature = "split_whitespace", since = "1.1.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "str_split_whitespace")] #[inline] pub fn split_whitespace(&self) -> SplitWhitespace<'_> { SplitWhitespace { inner: self.split(IsWhitespace).filter(IsNotEmpty) } @@ -1846,6 +1847,7 @@ impl str { #[must_use = "this returns the trimmed string as a slice, \ without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "str_trim")] pub fn trim(&self) -> &str { self.trim_matches(|c: char| c.is_whitespace()) } @@ -1884,6 +1886,7 @@ impl str { #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] #[stable(feature = "trim_direction", since = "1.30.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "str_trim_start")] pub fn trim_start(&self) -> &str { self.trim_start_matches(|c: char| c.is_whitespace()) } @@ -1922,6 +1925,7 @@ impl str { #[must_use = "this returns the trimmed string as a new slice, \ without modifying the original"] #[stable(feature = "trim_direction", since = "1.30.0")] + #[cfg_attr(not(test), rustc_diagnostic_item = "str_trim_end")] pub fn trim_end(&self) -> &str { self.trim_end_matches(|c: char| c.is_whitespace()) } From 85e67b9a5917bc82bdba0aaa3a18595904cee629 Mon Sep 17 00:00:00 2001 From: lightning1141 Date: Sat, 26 Feb 2022 11:55:07 +0800 Subject: [PATCH 12/12] Fix ice when error reporting recursion errors Fixes: #90319, #92148, #93955 --- compiler/rustc_middle/src/traits/mod.rs | 2 +- compiler/rustc_middle/src/traits/select.rs | 15 ++++++++++++++- .../src/traits/error_reporting/mod.rs | 9 +++++++-- .../src/traits/project.rs | 5 ++++- .../src/traits/query/evaluate_obligation.rs | 2 ++ .../src/traits/select/candidate_assembly.rs | 3 ++- .../src/traits/select/mod.rs | 18 ++++++++++-------- src/test/ui/typeck/issue-90319.rs | 17 +++++++++++++++++ src/test/ui/typeck/issue-90319.stderr | 9 +++++++++ 9 files changed, 66 insertions(+), 14 deletions(-) create mode 100644 src/test/ui/typeck/issue-90319.rs create mode 100644 src/test/ui/typeck/issue-90319.stderr diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 36bbccf0a8038..0707fa2efea70 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -478,7 +478,7 @@ pub enum SelectionError<'tcx> { /// A given constant couldn't be evaluated. NotConstEvaluatable(NotConstEvaluatable), /// Exceeded the recursion depth during type projection. - Overflow, + Overflow(OverflowError), /// Signaling that an error has already been emitted, to avoid /// multiple errors being shown. ErrorReporting, diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 56d42706f6778..5297825a92fcc 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -5,6 +5,7 @@ use self::EvaluationResult::*; use super::{SelectionError, SelectionResult}; +use rustc_errors::ErrorGuaranteed; use crate::ty; @@ -264,14 +265,26 @@ impl EvaluationResult { /// Indicates that trait evaluation caused overflow and in which pass. #[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable)] pub enum OverflowError { + Error(ErrorGuaranteed), Canonical, ErrorReporting, } +impl From for OverflowError { + fn from(e: ErrorGuaranteed) -> OverflowError { + OverflowError::Error(e) + } +} + +TrivialTypeFoldableAndLiftImpls! { + OverflowError, +} + impl<'tcx> From for SelectionError<'tcx> { fn from(overflow_error: OverflowError) -> SelectionError<'tcx> { match overflow_error { - OverflowError::Canonical => SelectionError::Overflow, + OverflowError::Error(e) => SelectionError::Overflow(OverflowError::Error(e)), + OverflowError::Canonical => SelectionError::Overflow(OverflowError::Canonical), OverflowError::ErrorReporting => SelectionError::ErrorReporting, } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 229e108d5d640..5e220173caeea 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -22,6 +22,7 @@ use rustc_hir::GenericParam; use rustc_hir::Item; use rustc_hir::Node; use rustc_middle::thir::abstract_const::NotConstEvaluatable; +use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::error::ExpectedFound; use rustc_middle::ty::fold::TypeFolder; use rustc_middle::ty::{ @@ -928,8 +929,12 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { self.tcx.sess.delay_span_bug(span, "`ErrorGuaranteed` without an error"); return; } - - Overflow => { + // Already reported. + Overflow(OverflowError::Error(_)) => { + self.tcx.sess.delay_span_bug(span, "`OverflowError` has been reported"); + return; + } + Overflow(_) => { bug!("overflow should be handled before the `report_selection_error` path"); } SelectionError::ErrorReporting => { diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 390381752f95d..b61e68735712b 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -27,6 +27,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_infer::infer::resolve::OpportunisticRegionResolver; +use rustc_middle::traits::select::OverflowError; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, Term, ToPredicate, Ty, TyCtxt}; @@ -1139,7 +1140,9 @@ fn project<'cx, 'tcx>( if !selcx.tcx().recursion_limit().value_within_limit(obligation.recursion_depth) { // This should really be an immediate error, but some existing code // relies on being able to recover from this. - return Err(ProjectionError::TraitSelectionError(SelectionError::Overflow)); + return Err(ProjectionError::TraitSelectionError(SelectionError::Overflow( + OverflowError::Canonical, + ))); } if obligation.predicate.references_error() { diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs index 4874ba6f58cac..db45ee3fed7db 100644 --- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs +++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs @@ -108,9 +108,11 @@ impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> { ) } OverflowError::ErrorReporting => EvaluationResult::EvaluatedToErr, + OverflowError::Error(_) => EvaluationResult::EvaluatedToErr, }) } Err(OverflowError::ErrorReporting) => EvaluationResult::EvaluatedToErr, + Err(OverflowError::Error(_)) => EvaluationResult::EvaluatedToErr, } } } diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index c0a283d2edae4..3e7a2252318be 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -164,8 +164,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(Some(EvaluatedCandidate { candidate: c, evaluation: eval })) } Ok(_) => Ok(None), - Err(OverflowError::Canonical) => Err(Overflow), + Err(OverflowError::Canonical) => Err(Overflow(OverflowError::Canonical)), Err(OverflowError::ErrorReporting) => Err(ErrorReporting), + Err(OverflowError::Error(e)) => Err(Overflow(OverflowError::Error(e))), }) .flat_map(Result::transpose) .collect::, _>>()?; diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 4135fbca06099..a988a51c83964 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -29,7 +29,7 @@ use crate::traits::project::ProjectionCacheKeyExt; use crate::traits::ProjectionCacheKey; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_errors::Diagnostic; +use rustc_errors::{Diagnostic, ErrorGuaranteed}; use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_infer::infer::LateBoundRegionConversionTime; @@ -320,11 +320,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation: &TraitObligation<'tcx>, ) -> SelectionResult<'tcx, Selection<'tcx>> { let candidate = match self.select_from_obligation(obligation) { - Err(SelectionError::Overflow) => { + Err(SelectionError::Overflow(OverflowError::Canonical)) => { // In standard mode, overflow must have been caught and reported // earlier. assert!(self.query_mode == TraitQueryMode::Canonical); - return Err(SelectionError::Overflow); + return Err(SelectionError::Overflow(OverflowError::Canonical)); } Err(SelectionError::Ambiguous(_)) => { return Ok(None); @@ -339,9 +339,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; match self.confirm_candidate(obligation, candidate) { - Err(SelectionError::Overflow) => { + Err(SelectionError::Overflow(OverflowError::Canonical)) => { assert!(self.query_mode == TraitQueryMode::Canonical); - Err(SelectionError::Overflow) + Err(SelectionError::Overflow(OverflowError::Canonical)) } Err(e) => Err(e), Ok(candidate) => { @@ -958,7 +958,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(Some(c)) => self.evaluate_candidate(stack, &c), Err(SelectionError::Ambiguous(_)) => Ok(EvaluatedToAmbig), Ok(None) => Ok(EvaluatedToAmbig), - Err(Overflow) => Err(OverflowError::Canonical), + Err(Overflow(OverflowError::Canonical)) => Err(OverflowError::Canonical), Err(ErrorReporting) => Err(OverflowError::ErrorReporting), Err(..) => Ok(EvaluatedToErr), } @@ -1117,7 +1117,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match self.query_mode { TraitQueryMode::Standard => { if self.infcx.is_tainted_by_errors() { - return Err(OverflowError::ErrorReporting); + return Err(OverflowError::Error( + ErrorGuaranteed::unchecked_claim_error_was_emitted(), + )); } self.infcx.report_overflow_error(error_obligation, true); } @@ -1353,7 +1355,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } if self.can_use_global_caches(param_env) { - if let Err(Overflow) = candidate { + if let Err(Overflow(OverflowError::Canonical)) = candidate { // Don't cache overflow globally; we only produce this in certain modes. } else if !pred.needs_infer() { if !candidate.needs_infer() { diff --git a/src/test/ui/typeck/issue-90319.rs b/src/test/ui/typeck/issue-90319.rs new file mode 100644 index 0000000000000..57e6ac7cf34f5 --- /dev/null +++ b/src/test/ui/typeck/issue-90319.rs @@ -0,0 +1,17 @@ +struct Wrapper(T); + +trait Trait { + fn method(&self) {} +} + +impl<'a, T> Trait for Wrapper<&'a T> where Wrapper: Trait {} + +fn get() -> T { + unimplemented!() +} + +fn main() { + let thing = get::();//~ERROR cannot find type `Thing` in this scope [E0412] + let wrapper = Wrapper(thing); + Trait::method(&wrapper); +} diff --git a/src/test/ui/typeck/issue-90319.stderr b/src/test/ui/typeck/issue-90319.stderr new file mode 100644 index 0000000000000..61549dd701e74 --- /dev/null +++ b/src/test/ui/typeck/issue-90319.stderr @@ -0,0 +1,9 @@ +error[E0412]: cannot find type `Thing` in this scope + --> $DIR/issue-90319.rs:14:23 + | +LL | let thing = get::(); + | ^^^^^ not found in this scope + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0412`.