From 20bd0c377163f384ae66943674ed2e397dd1c73a Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 21 Jun 2022 12:31:42 +0200 Subject: [PATCH 01/56] Re-enable atomic loads and stores for all RISC-V targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This roughly reverts PR https://github.com/rust-lang/rust/pull/66548 Atomic "CAS" are still disabled for targets without the *“A” Standard Extension for Atomic Instructions*. However this extension only adds instructions for operations more complex than simple loads and stores, which are always atomic when aligned. In the [Unprivileged Spec v. 20191213](https://riscv.org/technical/specifications/) section 2.6 *Load and Store Instructions* of chapter 2 *RV32I Base Integer Instruction Set* (emphasis mine): > Even when misaligned loads and stores complete successfully, > these accesses might run extremely slowly depending on the implementation > (e.g., when implemented via an invisible trap). Further-more, whereas > **naturally aligned loads and stores are guaranteed to execute atomically**, > misaligned loads and stores might not, and hence require > additional synchronization to ensure atomicity. Unfortunately PR https://github.com/rust-lang/rust/pull/66548 did not provide much details on the bug that motivated it, but https://github.com/rust-lang/rust/issues/66240 and https://github.com/rust-lang/rust/issues/85736 appear related and happen with targets that do have the A extension. --- compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs | 2 +- compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs | 2 +- compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs index 7124e2df9b3..c07029a4be7 100644 --- a/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv32i_unknown_none_elf.rs @@ -12,7 +12,7 @@ pub fn target() -> Target { linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".into()), cpu: "generic-rv32".into(), - max_atomic_width: Some(0), + max_atomic_width: Some(32), atomic_cas: false, executables: true, panic_strategy: PanicStrategy::Abort, diff --git a/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs index 508982eed68..849acf29b9b 100644 --- a/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv32im_unknown_none_elf.rs @@ -12,7 +12,7 @@ pub fn target() -> Target { linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".into()), cpu: "generic-rv32".into(), - max_atomic_width: Some(0), + max_atomic_width: Some(32), atomic_cas: false, features: "+m".into(), executables: true, diff --git a/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs b/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs index 4216968cb77..c88b8592d45 100644 --- a/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs +++ b/compiler/rustc_target/src/spec/riscv32imc_unknown_none_elf.rs @@ -12,7 +12,7 @@ pub fn target() -> Target { linker_flavor: LinkerFlavor::Lld(LldFlavor::Ld), linker: Some("rust-lld".into()), cpu: "generic-rv32".into(), - max_atomic_width: Some(0), + max_atomic_width: Some(32), atomic_cas: false, features: "+m,+c".into(), executables: true, From 1d456583293f351507bc5b8c292d408300e3ad7c Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 24 Jul 2023 14:07:23 +0000 Subject: [PATCH 02/56] Some tracing changes --- compiler/rustc_borrowck/src/type_check/mod.rs | 6 +- .../src/mem_categorization.rs | 75 ++++++++----------- compiler/rustc_hir_typeck/src/upvar.rs | 10 +-- 3 files changed, 35 insertions(+), 56 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 78aa513033c..0b2f39c1eb3 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -504,14 +504,13 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { /// Checks that the types internal to the `place` match up with /// what would be expected. + #[instrument(level = "debug", skip(self, location), ret)] fn sanitize_place( &mut self, place: &Place<'tcx>, location: Location, context: PlaceContext, ) -> PlaceTy<'tcx> { - debug!("sanitize_place: {:?}", place); - let mut place_ty = PlaceTy::from_ty(self.body().local_decls[place.local].ty); for elem in place.projection.iter() { @@ -614,7 +613,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { } } - #[instrument(skip(self), level = "debug")] + #[instrument(skip(self, location), ret, level = "debug")] fn sanitize_projection( &mut self, base: PlaceTy<'tcx>, @@ -623,7 +622,6 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { location: Location, context: PlaceContext, ) -> PlaceTy<'tcx> { - debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, place); let tcx = self.tcx(); let base_ty = base.ty; match pi { diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index 0700e2e0554..9de4f82a06b 100644 --- a/compiler/rustc_hir_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -198,13 +198,14 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } /// Like `pat_ty`, but ignores implicit `&` patterns. + #[instrument(level = "debug", skip(self), ret)] fn pat_ty_unadjusted(&self, pat: &hir::Pat<'_>) -> McResult> { let base_ty = self.node_ty(pat.hir_id)?; - debug!("pat_ty(pat={:?}) base_ty={:?}", pat, base_ty); + trace!(?base_ty); // This code detects whether we are looking at a `ref x`, // and if so, figures out what the type *being borrowed* is. - let ret_ty = match pat.kind { + match pat.kind { PatKind::Binding(..) => { let bm = *self .typeck_results @@ -217,21 +218,18 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { // but what we want here is the type of the underlying value being borrowed. // So peel off one-level, turning the &T into T. match base_ty.builtin_deref(false) { - Some(t) => t.ty, + Some(t) => Ok(t.ty), None => { - debug!("By-ref binding of non-derefable type {:?}", base_ty); - return Err(()); + debug!("By-ref binding of non-derefable type"); + Err(()) } } } else { - base_ty + Ok(base_ty) } } - _ => base_ty, - }; - debug!("pat_ty(pat={:?}) ret_ty={:?}", pat, ret_ty); - - Ok(ret_ty) + _ => Ok(base_ty), + } } pub(crate) fn cat_expr(&self, expr: &hir::Expr<'_>) -> McResult> { @@ -299,13 +297,11 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } } - #[instrument(level = "debug", skip(self))] + #[instrument(level = "debug", skip(self), ret)] pub(crate) fn cat_expr_unadjusted( &self, expr: &hir::Expr<'_>, ) -> McResult> { - debug!("cat_expr: id={} expr={:?}", expr.hir_id, expr); - let expr_ty = self.expr_ty(expr)?; match expr.kind { hir::ExprKind::Unary(hir::UnOp::Deref, ref e_base) => { @@ -319,7 +315,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { hir::ExprKind::Field(ref base, _) => { let base = self.cat_expr(base)?; - debug!("cat_expr(cat_field): id={} expr={:?} base={:?}", expr.hir_id, expr, base); + debug!(?base); let field_idx = self .typeck_results @@ -389,7 +385,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } } - #[instrument(level = "debug", skip(self, span))] + #[instrument(level = "debug", skip(self, span), ret)] pub(crate) fn cat_res( &self, hir_id: hir::HirId, @@ -430,6 +426,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { /// Note: the actual upvar access contains invisible derefs of closure /// environment and upvar reference as appropriate. Only regionck cares /// about these dereferences, so we let it compute them as needed. + #[instrument(level = "debug", skip(self), ret)] fn cat_upvar(&self, hir_id: hir::HirId, var_id: hir::HirId) -> McResult> { let closure_expr_def_id = self.body_owner; @@ -439,24 +436,20 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { }; let var_ty = self.node_ty(var_id)?; - let ret = PlaceWithHirId::new(hir_id, var_ty, PlaceBase::Upvar(upvar_id), Vec::new()); - - debug!("cat_upvar ret={:?}", ret); - Ok(ret) + Ok(PlaceWithHirId::new(hir_id, var_ty, PlaceBase::Upvar(upvar_id), Vec::new())) } + #[instrument(level = "debug", skip(self), ret)] pub(crate) fn cat_rvalue( &self, hir_id: hir::HirId, span: Span, expr_ty: Ty<'tcx>, ) -> PlaceWithHirId<'tcx> { - debug!("cat_rvalue hir_id={:?}, expr_ty={:?}, span={:?}", hir_id, expr_ty, span); - let ret = PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Rvalue, Vec::new()); - debug!("cat_rvalue ret={:?}", ret); - ret + PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Rvalue, Vec::new()) } + #[instrument(level = "debug", skip(self, node), ret)] pub(crate) fn cat_projection( &self, node: &N, @@ -466,14 +459,12 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { ) -> PlaceWithHirId<'tcx> { let mut projections = base_place.place.projections; projections.push(Projection { kind, ty }); - let ret = PlaceWithHirId::new( + PlaceWithHirId::new( node.hir_id(), base_place.place.base_ty, base_place.place.base, projections, - ); - debug!("cat_field ret {:?}", ret); - ret + ) } #[instrument(level = "debug", skip(self))] @@ -497,7 +488,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { self.cat_deref(expr, base) } - #[instrument(level = "debug", skip(self, node))] + #[instrument(level = "debug", skip(self, node), ret)] fn cat_deref( &self, node: &impl HirNode, @@ -514,14 +505,12 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { let mut projections = base_place.place.projections; projections.push(Projection { kind: ProjectionKind::Deref, ty: deref_ty }); - let ret = PlaceWithHirId::new( + Ok(PlaceWithHirId::new( node.hir_id(), base_place.place.base_ty, base_place.place.base, projections, - ); - debug!("cat_deref ret {:?}", ret); - Ok(ret) + )) } pub(crate) fn cat_pattern( @@ -603,6 +592,13 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { } } + /// Here, `place` is the `PlaceWithHirId` being matched and pat is the pattern it + /// is being matched against. + /// + /// In general, the way that this works is that we walk down the pattern, + /// constructing a `PlaceWithHirId` that represents the path that will be taken + /// to reach the value being matched. + #[instrument(skip(self, op), ret, level = "debug")] fn cat_pattern_( &self, mut place_with_id: PlaceWithHirId<'tcx>, @@ -612,15 +608,6 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { where F: FnMut(&PlaceWithHirId<'tcx>, &hir::Pat<'_>), { - // Here, `place` is the `PlaceWithHirId` being matched and pat is the pattern it - // is being matched against. - // - // In general, the way that this works is that we walk down the pattern, - // constructing a `PlaceWithHirId` that represents the path that will be taken - // to reach the value being matched. - - debug!("cat_pattern(pat={:?}, place_with_id={:?})", pat, place_with_id); - // If (pattern) adjustments are active for this pattern, adjust the `PlaceWithHirId` correspondingly. // `PlaceWithHirId`s are constructed differently from patterns. For example, in // @@ -654,11 +641,11 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { // `deref { deref { place_foo }}` instead of `place_foo` since the pattern is now `Some(x,)` // and not `&&Some(x,)`, even though its assigned type is that of `&&Some(x,)`. for _ in 0..self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(0, |v| v.len()) { - debug!("cat_pattern: applying adjustment to place_with_id={:?}", place_with_id); + debug!("applying adjustment to place_with_id={:?}", place_with_id); place_with_id = self.cat_deref(pat, place_with_id)?; } let place_with_id = place_with_id; // lose mutability - debug!("cat_pattern: applied adjustment derefs to get place_with_id={:?}", place_with_id); + debug!("applied adjustment derefs to get place_with_id={:?}", place_with_id); // Invoke the callback, but only now, after the `place_with_id` has adjusted. // diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index fb81a8395d7..08c05a67b6b 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -294,10 +294,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Equate the type variables for the upvars with the actual types. let final_upvar_tys = self.final_upvar_tys(closure_def_id); - debug!( - "analyze_closure: id={:?} args={:?} final_upvar_tys={:?}", - closure_hir_id, args, final_upvar_tys - ); + debug!(?closure_hir_id, ?args, ?final_upvar_tys); // Build a tuple (U0..Un) of the final upvar types U0..Un // and unify the upvar tuple type in the closure with it: @@ -338,10 +335,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let upvar_ty = captured_place.place.ty(); let capture = captured_place.info.capture_kind; - debug!( - "final_upvar_tys: place={:?} upvar_ty={:?} capture={:?}, mutability={:?}", - captured_place.place, upvar_ty, capture, captured_place.mutability, - ); + debug!(?captured_place.place, ?upvar_ty, ?capture, ?captured_place.mutability); apply_capture_kind_on_capture_ty(self.tcx, upvar_ty, capture, captured_place.region) }) From d14569bd64e468bb08472f4885b4796ba8354474 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 24 Jul 2023 14:46:04 +0000 Subject: [PATCH 03/56] Simplify a shadowing replacement (that sometimes identity replaces) with mutation --- compiler/rustc_hir_typeck/src/upvar.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 08c05a67b6b..62eff4bab86 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -264,12 +264,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.demand_eqtype(span, closure_kind.to_ty(self.tcx), closure_kind_ty); // If we have an origin, store it. - if let Some(origin) = origin { - let origin = if enable_precise_capture(span) { - (origin.0, origin.1) - } else { - (origin.0, Place { projections: vec![], ..origin.1 }) - }; + if let Some(mut origin) = origin { + if !enable_precise_capture(span) { + // Without precise captures, we just capture the base and ignore + // the projections. + origin.1.projections.clear() + } self.typeck_results .borrow_mut() From e390dc9c36448ca033aad48a6e7fd5ecc3f038d2 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 24 Jul 2023 14:47:32 +0000 Subject: [PATCH 04/56] Perform OpaqueCast field projection on HIR, too. This is necessary for closure captures in 2021 edition, as they capture individual fields, not the full mentioned variables. So it may try to capture a field of an opaque (because the hidden type is known to be something with a field). --- compiler/rustc_hir_typeck/src/mem_categorization.rs | 9 +++++++++ compiler/rustc_hir_typeck/src/upvar.rs | 5 +++++ compiler/rustc_middle/src/hir/place.rs | 4 ++++ compiler/rustc_middle/src/ty/closure.rs | 2 ++ compiler/rustc_mir_build/src/build/expr/as_place.rs | 3 +++ compiler/rustc_mir_build/src/thir/cx/expr.rs | 3 +++ src/tools/clippy/clippy_utils/src/sugg.rs | 2 ++ .../type-alias-impl-trait/issue-96572-unconstrained.rs | 2 ++ 8 files changed, 30 insertions(+) diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index 9de4f82a06b..e91103f2130 100644 --- a/compiler/rustc_hir_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -457,7 +457,16 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { ty: Ty<'tcx>, kind: ProjectionKind, ) -> PlaceWithHirId<'tcx> { + let place_ty = base_place.place.ty(); let mut projections = base_place.place.projections; + + let node_ty = self.typeck_results.node_type(node.hir_id()); + // Opaque types can't have field projections, but we can instead convert + // the current place in-place (heh) to the hidden type, and then apply all + // follow up projections on that. + if node_ty != place_ty && place_ty.has_opaque_types() { + projections.push(Projection { kind: ProjectionKind::OpaqueCast, ty: node_ty }); + } projections.push(Projection { kind, ty }); PlaceWithHirId::new( node.hir_id(), diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 62eff4bab86..facbb4b3cf8 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -673,6 +673,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match (p1.kind, p2.kind) { // Paths are the same, continue to next loop. (ProjectionKind::Deref, ProjectionKind::Deref) => {} + (ProjectionKind::OpaqueCast, ProjectionKind::OpaqueCast) => {} (ProjectionKind::Field(i1, _), ProjectionKind::Field(i2, _)) if i1 == i2 => {} @@ -695,10 +696,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { l @ (ProjectionKind::Index | ProjectionKind::Subslice | ProjectionKind::Deref + | ProjectionKind::OpaqueCast | ProjectionKind::Field(..)), r @ (ProjectionKind::Index | ProjectionKind::Subslice | ProjectionKind::Deref + | ProjectionKind::OpaqueCast | ProjectionKind::Field(..)), ) => bug!( "ProjectionKinds Index or Subslice were unexpected: ({:?}, {:?})", @@ -1885,6 +1888,7 @@ fn restrict_capture_precision( return (place, curr_mode); } ProjectionKind::Deref => {} + ProjectionKind::OpaqueCast => {} ProjectionKind::Field(..) => {} // ignore } } @@ -1941,6 +1945,7 @@ fn construct_place_string<'tcx>(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String ProjectionKind::Deref => String::from("Deref"), ProjectionKind::Index => String::from("Index"), ProjectionKind::Subslice => String::from("Subslice"), + ProjectionKind::OpaqueCast => String::from("OpaqueCast"), }; if i != 0 { projections_str.push(','); diff --git a/compiler/rustc_middle/src/hir/place.rs b/compiler/rustc_middle/src/hir/place.rs index 8a22de931c3..32f3a177508 100644 --- a/compiler/rustc_middle/src/hir/place.rs +++ b/compiler/rustc_middle/src/hir/place.rs @@ -36,6 +36,10 @@ pub enum ProjectionKind { /// A subslice covering a range of values like `B[x..y]`. Subslice, + + /// A conversion from an opaque type to its hidden type so we can + /// do further projections on it. + OpaqueCast, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TyEncodable, TyDecodable, HashStable)] diff --git a/compiler/rustc_middle/src/ty/closure.rs b/compiler/rustc_middle/src/ty/closure.rs index 91eefa2c125..6cf4fef1d04 100644 --- a/compiler/rustc_middle/src/ty/closure.rs +++ b/compiler/rustc_middle/src/ty/closure.rs @@ -174,6 +174,8 @@ impl<'tcx> CapturedPlace<'tcx> { // Ignore derefs for now, as they are likely caused by // autoderefs that don't appear in the original code. HirProjectionKind::Deref => {} + // Just change the type to the hidden type, so we can actually project. + HirProjectionKind::OpaqueCast => {} proj => bug!("Unexpected projection {:?} in captured place", proj), } ty = proj.ty; diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs index 7756d5d4879..2e7ef265a93 100644 --- a/compiler/rustc_mir_build/src/build/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs @@ -236,6 +236,9 @@ fn strip_prefix<'a, 'tcx>( } assert_matches!(iter.next(), Some(ProjectionElem::Field(..))); } + HirProjectionKind::OpaqueCast => { + assert_matches!(iter.next(), Some(ProjectionElem::OpaqueCast(..))); + } HirProjectionKind::Index | HirProjectionKind::Subslice => { bug!("unexpected projection kind: {:?}", projection); } diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 711a9126c04..2f0f71a8dc6 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -1074,6 +1074,9 @@ impl<'tcx> Cx<'tcx> { variant_index, name: field, }, + HirProjectionKind::OpaqueCast => { + ExprKind::Use { source: self.thir.exprs.push(captured_place_expr) } + } HirProjectionKind::Index | HirProjectionKind::Subslice => { // We don't capture these projections, so we can ignore them here continue; diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index 5d7e1494fcf..a43a81bc63a 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -1011,6 +1011,8 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { }, // note: unable to trigger `Subslice` kind in tests ProjectionKind::Subslice => (), + // Doesn't have surface syntax. Only occurs in patterns. + ProjectionKind::OpaqueCast => (), ProjectionKind::Deref => { // Explicit derefs are typically handled later on, but // some items do not need explicit deref, such as array accesses, diff --git a/tests/ui/type-alias-impl-trait/issue-96572-unconstrained.rs b/tests/ui/type-alias-impl-trait/issue-96572-unconstrained.rs index 2c740ccc1ae..fdd8fa65bd0 100644 --- a/tests/ui/type-alias-impl-trait/issue-96572-unconstrained.rs +++ b/tests/ui/type-alias-impl-trait/issue-96572-unconstrained.rs @@ -1,5 +1,7 @@ #![feature(type_alias_impl_trait)] // check-pass +// revisions: default edition2021 +//[edition2021] compile-flags: --edition 2021 fn main() { type T = impl Copy; From 1d7f728901090e3343e9c284886f9983a052edef Mon Sep 17 00:00:00 2001 From: Erik Desjardins Date: Sat, 29 Jul 2023 16:12:27 -0400 Subject: [PATCH 05/56] cg_llvm: stop identifying ADTs in LLVM IR Now that we use opaque pointers, ADTs can no longer be recursive, so we do not need to name them. Previously, this would be necessary if you had a struct like ```rs struct Foo(Box, u64, u64); ``` which would be represented with something like ```ll %Foo = type { %Foo*, i64, i64 } ``` which is now just ```ll { ptr, i64, i64 } ``` --- compiler/rustc_codegen_llvm/src/type_of.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index ac18fdfe07f..b6cf22b4cca 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -61,9 +61,6 @@ fn uncached_llvm_type<'a, 'tcx>( } Some(name) } - // Use identified structure types for ADT. Due to pointee types in LLVM IR their definition - // might be recursive. Other cases are non-recursive and we can use literal structure types. - ty::Adt(..) => Some(String::new()), _ => None, }; From 29de70da1be1c71b89b006f9955fedc70e084067 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 25 Jul 2023 13:09:53 +0000 Subject: [PATCH 06/56] Replace the many arguments of `EmitterWriter::stderr` with builder methods --- Cargo.lock | 54 +++++++++++++++++++++++++++ compiler/rustc_driver_impl/src/lib.rs | 12 +----- compiler/rustc_errors/Cargo.toml | 1 + compiler/rustc_errors/src/emitter.rs | 38 +++++++------------ compiler/rustc_errors/src/lib.rs | 13 +------ compiler/rustc_session/src/session.rs | 34 +++++------------ src/librustdoc/core.rs | 20 ++++------ src/librustdoc/doctest.rs | 16 ++------ src/tools/tidy/src/deps.rs | 7 ++++ 9 files changed, 97 insertions(+), 98 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 45959c039e7..dd8ce11ac12 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -830,6 +830,41 @@ dependencies = [ "winapi", ] +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.27", +] + +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.27", +] + [[package]] name = "datafrog" version = "2.0.1" @@ -869,6 +904,18 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_setters" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e8ef033054e131169b8f0f9a7af8f5533a9436fadf3c500ed547f730f07090d" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.27", +] + [[package]] name = "diff" version = "0.1.13" @@ -1740,6 +1787,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + [[package]] name = "idna" version = "0.4.0" @@ -3523,6 +3576,7 @@ name = "rustc_errors" version = "0.0.0" dependencies = [ "annotate-snippets", + "derive_setters", "rustc_ast", "rustc_ast_pretty", "rustc_data_structures", diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 8f8b9eaa274..d4532873854 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -27,9 +27,7 @@ use rustc_data_structures::profiling::{ use rustc_data_structures::sync::SeqCst; use rustc_errors::registry::{InvalidErrorCode, Registry}; use rustc_errors::{markdown, ColorConfig}; -use rustc_errors::{ - DiagnosticMessage, ErrorGuaranteed, Handler, PResult, SubdiagnosticMessage, TerminalUrl, -}; +use rustc_errors::{DiagnosticMessage, ErrorGuaranteed, Handler, PResult, SubdiagnosticMessage}; use rustc_feature::find_gated_cfg; use rustc_fluent_macro::fluent_messages; use rustc_interface::util::{self, collect_crate_types, get_codegen_backend}; @@ -1405,15 +1403,7 @@ pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str, extra_info: rustc_errors::fallback_fluent_bundle(crate::DEFAULT_LOCALE_RESOURCES.to_vec(), false); let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr( rustc_errors::ColorConfig::Auto, - None, - None, fallback_bundle, - false, - false, - None, - false, - false, - TerminalUrl::No, )); let handler = rustc_errors::Handler::with_emitter(emitter); diff --git a/compiler/rustc_errors/Cargo.toml b/compiler/rustc_errors/Cargo.toml index e8bcd7c1184..faab9f09da8 100644 --- a/compiler/rustc_errors/Cargo.toml +++ b/compiler/rustc_errors/Cargo.toml @@ -25,6 +25,7 @@ annotate-snippets = "0.9" termize = "0.1.1" serde = { version = "1.0.125", features = [ "derive" ] } serde_json = "1.0.59" +derive_setters = "0.1.6" [target.'cfg(windows)'.dependencies.windows] version = "0.48.0" diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 44654571d43..70ca972ddb4 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -24,6 +24,7 @@ use crate::{ }; use rustc_lint_defs::pluralize; +use derive_setters::Setters; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_data_structures::sync::Lrc; use rustc_error_messages::{FluentArgs, SpanLabel}; @@ -639,10 +640,13 @@ impl ColorConfig { } /// Handles the writing of `HumanReadableErrorType::Default` and `HumanReadableErrorType::Short` +#[derive(Setters)] pub struct EmitterWriter { + #[setters(skip)] dst: Destination, sm: Option>, fluent_bundle: Option>, + #[setters(skip)] fallback_bundle: LazyFallbackBundle, short_message: bool, teach: bool, @@ -662,31 +666,20 @@ pub struct FileWithAnnotatedLines { } impl EmitterWriter { - pub fn stderr( - color_config: ColorConfig, - source_map: Option>, - fluent_bundle: Option>, - fallback_bundle: LazyFallbackBundle, - short_message: bool, - teach: bool, - diagnostic_width: Option, - macro_backtrace: bool, - track_diagnostics: bool, - terminal_url: TerminalUrl, - ) -> EmitterWriter { + pub fn stderr(color_config: ColorConfig, fallback_bundle: LazyFallbackBundle) -> EmitterWriter { let dst = Destination::from_stderr(color_config); EmitterWriter { dst, - sm: source_map, - fluent_bundle, + sm: None, + fluent_bundle: None, fallback_bundle, - short_message, - teach, + short_message: false, + teach: false, ui_testing: false, - diagnostic_width, - macro_backtrace, - track_diagnostics, - terminal_url, + diagnostic_width: None, + macro_backtrace: false, + track_diagnostics: false, + terminal_url: TerminalUrl::No, } } @@ -718,11 +711,6 @@ impl EmitterWriter { } } - pub fn ui_testing(mut self, ui_testing: bool) -> Self { - self.ui_testing = ui_testing; - self - } - fn maybe_anonymized(&self, line_num: usize) -> Cow<'static, str> { if self.ui_testing { Cow::Borrowed(ANONYMIZED_LINE_NUM) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 3d1639db4af..02b77cecbe2 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -556,18 +556,7 @@ impl Handler { sm: Option>, fallback_bundle: LazyFallbackBundle, ) -> Self { - let emitter = Box::new(EmitterWriter::stderr( - ColorConfig::Auto, - sm, - None, - fallback_bundle, - false, - false, - None, - false, - false, - TerminalUrl::No, - )); + let emitter = Box::new(EmitterWriter::stderr(ColorConfig::Auto, fallback_bundle).sm(sm)); Self::with_emitter(emitter) } pub fn disable_warnings(mut self) -> Self { diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index ad22e7c703d..ac745d16161 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -1350,18 +1350,15 @@ fn default_emitter( ); Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing)) } else { - let emitter = EmitterWriter::stderr( - color_config, - Some(source_map), - bundle, - fallback_bundle, - short, - sopts.unstable_opts.teach, - sopts.diagnostic_width, - macro_backtrace, - track_diagnostics, - terminal_url, - ); + let emitter = EmitterWriter::stderr(color_config, fallback_bundle) + .fluent_bundle(bundle) + .sm(Some(source_map)) + .short_message(short) + .teach(sopts.unstable_opts.teach) + .diagnostic_width(sopts.diagnostic_width) + .macro_backtrace(macro_backtrace) + .track_diagnostics(track_diagnostics) + .terminal_url(terminal_url); Box::new(emitter.ui_testing(sopts.unstable_opts.ui_testing)) } } @@ -1794,18 +1791,7 @@ fn mk_emitter(output: ErrorOutputType) -> Box = match output { config::ErrorOutputType::HumanReadable(kind) => { let (short, color_config) = kind.unzip(); - Box::new(EmitterWriter::stderr( - color_config, - None, - None, - fallback_bundle, - short, - false, - None, - false, - false, - TerminalUrl::No, - )) + Box::new(EmitterWriter::stderr(color_config, fallback_bundle).short_message(short)) } config::ErrorOutputType::Json { pretty, json_rendered } => Box::new(JsonEmitter::basic( pretty, diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 6f791aec3f2..12d620b5b18 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -136,19 +136,13 @@ pub(crate) fn new_handler( ErrorOutputType::HumanReadable(kind) => { let (short, color_config) = kind.unzip(); Box::new( - EmitterWriter::stderr( - color_config, - source_map.map(|sm| sm as _), - None, - fallback_bundle, - short, - unstable_opts.teach, - diagnostic_width, - false, - unstable_opts.track_diagnostics, - TerminalUrl::No, - ) - .ui_testing(unstable_opts.ui_testing), + EmitterWriter::stderr(color_config, fallback_bundle) + .sm(source_map.map(|sm| sm as _)) + .short_message(short) + .teach(unstable_opts.teach) + .diagnostic_width(diagnostic_width) + .track_diagnostics(unstable_opts.track_diagnostics) + .ui_testing(unstable_opts.ui_testing), ) } ErrorOutputType::Json { pretty, json_rendered } => { diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index a48273a5c73..8f25f765390 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -558,19 +558,9 @@ pub(crate) fn make_test( rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false, ); - supports_color = EmitterWriter::stderr( - ColorConfig::Auto, - None, - None, - fallback_bundle.clone(), - false, - false, - Some(80), - false, - false, - TerminalUrl::No, - ) - .supports_color(); + supports_color = EmitterWriter::stderr(ColorConfig::Auto, fallback_bundle.clone()) + .diagnostic_width(Some(80)) + .supports_color(); let emitter = EmitterWriter::new( Box::new(io::sink()), diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 57cbfe68be4..15151e645fa 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -138,8 +138,12 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "crossbeam-utils", "crypto-common", "cstr", + "darling", + "darling_core", + "darling_macro", "datafrog", "derive_more", + "derive_setters", "digest", "displaydoc", "dissimilar", @@ -158,6 +162,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "fluent-bundle", "fluent-langneg", "fluent-syntax", + "fnv", "fortanix-sgx-abi", "generic-array", "getopts", @@ -171,6 +176,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "icu_provider", "icu_provider_adapters", "icu_provider_macros", + "ident_case", "indexmap", "instant", "intl-memoizer", @@ -245,6 +251,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ "stable_deref_trait", "stacker", "static_assertions", + "strsim", "syn", "synstructure", "tempfile", From 0e7ec9683dae0a0bc66797b1351059ed642f4e2d Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 25 Jul 2023 13:25:38 +0000 Subject: [PATCH 07/56] Use builder pattern instead of lots of arguments for `EmitterWriter::new` --- compiler/rustc_errors/src/emitter.rs | 47 +++--------------------- compiler/rustc_errors/src/json.rs | 18 ++++----- compiler/rustc_expand/src/tests.rs | 19 +++------- src/librustdoc/doctest.rs | 30 ++------------- src/tools/clippy/clippy_lints/src/doc.rs | 10 +---- src/tools/rustfmt/src/parse/session.rs | 15 +------- 6 files changed, 24 insertions(+), 115 deletions(-) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 70ca972ddb4..41fb1cafe25 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -62,30 +62,11 @@ impl HumanReadableErrorType { pub fn new_emitter( self, dst: Box, - source_map: Option>, - bundle: Option>, fallback_bundle: LazyFallbackBundle, - teach: bool, - diagnostic_width: Option, - macro_backtrace: bool, - track_diagnostics: bool, - terminal_url: TerminalUrl, ) -> EmitterWriter { let (short, color_config) = self.unzip(); let color = color_config.suggests_using_colors(); - EmitterWriter::new( - dst, - source_map, - bundle, - fallback_bundle, - short, - teach, - color, - diagnostic_width, - macro_backtrace, - track_diagnostics, - terminal_url, - ) + EmitterWriter::new(dst, fallback_bundle, color).short_message(short) } } @@ -668,6 +649,10 @@ pub struct FileWithAnnotatedLines { impl EmitterWriter { pub fn stderr(color_config: ColorConfig, fallback_bundle: LazyFallbackBundle) -> EmitterWriter { let dst = Destination::from_stderr(color_config); + Self::create(dst, fallback_bundle) + } + + fn create(dst: Destination, fallback_bundle: LazyFallbackBundle) -> EmitterWriter { EmitterWriter { dst, sm: None, @@ -685,30 +670,10 @@ impl EmitterWriter { pub fn new( dst: Box, - source_map: Option>, - fluent_bundle: Option>, fallback_bundle: LazyFallbackBundle, - short_message: bool, - teach: bool, colored: bool, - diagnostic_width: Option, - macro_backtrace: bool, - track_diagnostics: bool, - terminal_url: TerminalUrl, ) -> EmitterWriter { - EmitterWriter { - dst: Raw(dst, colored), - sm: source_map, - fluent_bundle, - fallback_bundle, - short_message, - teach, - ui_testing: false, - diagnostic_width, - macro_backtrace, - track_diagnostics, - terminal_url, - } + Self::create(Raw(dst, colored), fallback_bundle) } fn maybe_anonymized(&self, line_num: usize) -> Cow<'static, str> { diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index 55f7c485024..e199244b1e4 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -359,17 +359,13 @@ impl Diagnostic { let buf = BufWriter::default(); let output = buf.clone(); je.json_rendered - .new_emitter( - Box::new(buf), - Some(je.sm.clone()), - je.fluent_bundle.clone(), - je.fallback_bundle.clone(), - false, - je.diagnostic_width, - je.macro_backtrace, - je.track_diagnostics, - je.terminal_url, - ) + .new_emitter(Box::new(buf), je.fallback_bundle.clone()) + .sm(Some(je.sm.clone())) + .fluent_bundle(je.fluent_bundle.clone()) + .diagnostic_width(je.diagnostic_width) + .macro_backtrace(je.macro_backtrace) + .track_diagnostics(je.track_diagnostics) + .terminal_url(je.terminal_url) .ui_testing(je.ui_testing) .emit_diagnostic(diag); let output = Arc::try_unwrap(output.0).unwrap().into_inner().unwrap(); diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs index 30fa5fea407..4d1d834ed2b 100644 --- a/compiler/rustc_expand/src/tests.rs +++ b/compiler/rustc_expand/src/tests.rs @@ -8,7 +8,7 @@ use rustc_span::{BytePos, Span}; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::EmitterWriter; -use rustc_errors::{Handler, MultiSpan, PResult, TerminalUrl}; +use rustc_errors::{Handler, MultiSpan, PResult}; use std::io; use std::io::prelude::*; @@ -29,19 +29,10 @@ fn create_test_handler() -> (Handler, Lrc, Arc>>) { vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE], false, ); - let emitter = EmitterWriter::new( - Box::new(Shared { data: output.clone() }), - Some(source_map.clone()), - None, - fallback_bundle, - false, - false, - false, - Some(140), - false, - false, - TerminalUrl::No, - ); + let emitter = + EmitterWriter::new(Box::new(Shared { data: output.clone() }), fallback_bundle, false) + .sm(Some(source_map.clone())) + .diagnostic_width(Some(140)); let handler = Handler::with_emitter(Box::new(emitter)); (handler, source_map, output) } diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 8f25f765390..1afac214da3 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -1,7 +1,7 @@ use rustc_ast as ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; -use rustc_errors::{ColorConfig, ErrorGuaranteed, FatalError, TerminalUrl}; +use rustc_errors::{ColorConfig, ErrorGuaranteed, FatalError}; use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; use rustc_hir::{self as hir, intravisit, CRATE_HIR_ID}; use rustc_interface::interface; @@ -562,19 +562,7 @@ pub(crate) fn make_test( .diagnostic_width(Some(80)) .supports_color(); - let emitter = EmitterWriter::new( - Box::new(io::sink()), - None, - None, - fallback_bundle, - false, - false, - false, - None, - false, - false, - TerminalUrl::No, - ); + let emitter = EmitterWriter::new(Box::new(io::sink()), fallback_bundle, false); // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings(); @@ -750,19 +738,7 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { false, ); - let emitter = EmitterWriter::new( - Box::new(io::sink()), - None, - None, - fallback_bundle, - false, - false, - false, - None, - false, - false, - TerminalUrl::No, - ); + let emitter = EmitterWriter::new(Box::new(io::sink()), fallback_bundle, false); let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings(); let sess = ParseSess::with_span_handler(handler, sm); diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index 00f70e586f4..573838ce63e 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -16,7 +16,7 @@ use rustc_ast::token::CommentKind; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::EmitterWriter; -use rustc_errors::{Applicability, Handler, SuggestionStyle, TerminalUrl}; +use rustc_errors::{Applicability, Handler, SuggestionStyle}; use rustc_hir as hir; use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::{AnonConst, Expr}; @@ -718,16 +718,8 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false); let emitter = EmitterWriter::new( Box::new(io::sink()), - None, - None, fallback_bundle, false, - false, - false, - None, - false, - false, - TerminalUrl::No, ); let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings(); let sess = ParseSess::with_span_handler(handler, sm); diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs index aa75b477473..945e3e42fdd 100644 --- a/src/tools/rustfmt/src/parse/session.rs +++ b/src/tools/rustfmt/src/parse/session.rs @@ -4,7 +4,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use rustc_data_structures::sync::{Lrc, Send}; use rustc_errors::emitter::{Emitter, EmitterWriter}; use rustc_errors::translation::Translate; -use rustc_errors::{ColorConfig, Diagnostic, Handler, Level as DiagnosticLevel, TerminalUrl}; +use rustc_errors::{ColorConfig, Diagnostic, Handler, Level as DiagnosticLevel}; use rustc_session::parse::ParseSess as RawParseSess; use rustc_span::{ source_map::{FilePathMapping, SourceMap}, @@ -139,18 +139,7 @@ fn default_handler( rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false, ); - Box::new(EmitterWriter::stderr( - emit_color, - Some(source_map.clone()), - None, - fallback_bundle, - false, - false, - None, - false, - false, - TerminalUrl::No, - )) + Box::new(EmitterWriter::stderr(emit_color, fallback_bundle).sm(Some(source_map.clone()))) }; Handler::with_emitter(Box::new(SilentOnIgnoredFilesEmitter { has_non_ignorable_parser_errors: false, From 51c22154f56d54d512b0b14aebf9eb18278963a1 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 26 Jul 2023 13:58:50 +0000 Subject: [PATCH 08/56] Remove a `bool` for color in favor of the `WriteColor` trait wrapping colored and uncolored printing --- Cargo.lock | 1 + compiler/rustc_errors/src/emitter.rs | 31 ++++++++++-------------- compiler/rustc_errors/src/json.rs | 14 +++++++++++ compiler/rustc_expand/Cargo.toml | 1 + compiler/rustc_expand/src/tests.rs | 22 ++++++++++++++--- src/librustdoc/doctest.rs | 4 +-- src/tools/clippy/clippy_lints/src/doc.rs | 1 - 7 files changed, 49 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dd8ce11ac12..7b7c0fd60d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3619,6 +3619,7 @@ dependencies = [ "rustc_session", "rustc_span", "smallvec", + "termcolor", "thin-vec", "tracing", ] diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 41fb1cafe25..87b3a25ed44 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -61,12 +61,15 @@ impl HumanReadableErrorType { } pub fn new_emitter( self, - dst: Box, + mut dst: Box, fallback_bundle: LazyFallbackBundle, ) -> EmitterWriter { let (short, color_config) = self.unzip(); let color = color_config.suggests_using_colors(); - EmitterWriter::new(dst, fallback_bundle, color).short_message(short) + if !dst.supports_color() && color { + dst = Box::new(Ansi::new(dst)); + } + EmitterWriter::new(dst, fallback_bundle).short_message(short) } } @@ -669,11 +672,10 @@ impl EmitterWriter { } pub fn new( - dst: Box, + dst: Box, fallback_bundle: LazyFallbackBundle, - colored: bool, ) -> EmitterWriter { - Self::create(Raw(dst, colored), fallback_bundle) + Self::create(Raw(dst), fallback_bundle) } fn maybe_anonymized(&self, line_num: usize) -> Cow<'static, str> { @@ -2603,15 +2605,13 @@ fn emit_to_destination( pub enum Destination { Terminal(StandardStream), Buffered(BufferWriter), - // The bool denotes whether we should be emitting ansi color codes or not - Raw(Box<(dyn Write + Send)>, bool), + Raw(Box<(dyn WriteColor + Send)>), } pub enum WritableDst<'a> { Terminal(&'a mut StandardStream), Buffered(&'a mut BufferWriter, Buffer), - Raw(&'a mut (dyn Write + Send)), - ColoredRaw(Ansi<&'a mut (dyn Write + Send)>), + Raw(&'a mut (dyn WriteColor + Send)), } impl Destination { @@ -2637,8 +2637,7 @@ impl Destination { let buf = t.buffer(); WritableDst::Buffered(t, buf) } - Destination::Raw(ref mut t, false) => WritableDst::Raw(t), - Destination::Raw(ref mut t, true) => WritableDst::ColoredRaw(Ansi::new(t)), + Destination::Raw(ref mut t) => WritableDst::Raw(t), } } @@ -2646,7 +2645,7 @@ impl Destination { match *self { Self::Terminal(ref stream) => stream.supports_color(), Self::Buffered(ref buffer) => buffer.buffer().supports_color(), - Self::Raw(_, supports_color) => supports_color, + Self::Raw(ref writer) => writer.supports_color(), } } } @@ -2706,8 +2705,7 @@ impl<'a> WritableDst<'a> { match *self { WritableDst::Terminal(ref mut t) => t.set_color(color), WritableDst::Buffered(_, ref mut t) => t.set_color(color), - WritableDst::ColoredRaw(ref mut t) => t.set_color(color), - WritableDst::Raw(_) => Ok(()), + WritableDst::Raw(ref mut t) => t.set_color(color), } } @@ -2715,8 +2713,7 @@ impl<'a> WritableDst<'a> { match *self { WritableDst::Terminal(ref mut t) => t.reset(), WritableDst::Buffered(_, ref mut t) => t.reset(), - WritableDst::ColoredRaw(ref mut t) => t.reset(), - WritableDst::Raw(_) => Ok(()), + WritableDst::Raw(ref mut t) => t.reset(), } } } @@ -2727,7 +2724,6 @@ impl<'a> Write for WritableDst<'a> { WritableDst::Terminal(ref mut t) => t.write(bytes), WritableDst::Buffered(_, ref mut buf) => buf.write(bytes), WritableDst::Raw(ref mut w) => w.write(bytes), - WritableDst::ColoredRaw(ref mut t) => t.write(bytes), } } @@ -2736,7 +2732,6 @@ impl<'a> Write for WritableDst<'a> { WritableDst::Terminal(ref mut t) => t.flush(), WritableDst::Buffered(_, ref mut buf) => buf.flush(), WritableDst::Raw(ref mut w) => w.flush(), - WritableDst::ColoredRaw(ref mut w) => w.flush(), } } } diff --git a/compiler/rustc_errors/src/json.rs b/compiler/rustc_errors/src/json.rs index e199244b1e4..b8f58e3057c 100644 --- a/compiler/rustc_errors/src/json.rs +++ b/compiler/rustc_errors/src/json.rs @@ -10,6 +10,7 @@ // FIXME: spec the JSON output properly. use rustc_span::source_map::{FilePathMapping, SourceMap}; +use termcolor::{ColorSpec, WriteColor}; use crate::emitter::{Emitter, HumanReadableErrorType}; use crate::registry::Registry; @@ -356,6 +357,19 @@ impl Diagnostic { self.0.lock().unwrap().flush() } } + impl WriteColor for BufWriter { + fn supports_color(&self) -> bool { + false + } + + fn set_color(&mut self, _spec: &ColorSpec) -> io::Result<()> { + Ok(()) + } + + fn reset(&mut self) -> io::Result<()> { + Ok(()) + } + } let buf = BufWriter::default(); let output = buf.clone(); je.json_rendered diff --git a/compiler/rustc_expand/Cargo.toml b/compiler/rustc_expand/Cargo.toml index 2dae0e3f53c..02da5b5dc53 100644 --- a/compiler/rustc_expand/Cargo.toml +++ b/compiler/rustc_expand/Cargo.toml @@ -27,3 +27,4 @@ rustc_span = { path = "../rustc_span" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } thin-vec = "0.2.12" tracing = "0.1" +termcolor = "1.2" diff --git a/compiler/rustc_expand/src/tests.rs b/compiler/rustc_expand/src/tests.rs index 4d1d834ed2b..8e3219c138c 100644 --- a/compiler/rustc_expand/src/tests.rs +++ b/compiler/rustc_expand/src/tests.rs @@ -9,6 +9,7 @@ use rustc_span::{BytePos, Span}; use rustc_data_structures::sync::Lrc; use rustc_errors::emitter::EmitterWriter; use rustc_errors::{Handler, MultiSpan, PResult}; +use termcolor::WriteColor; use std::io; use std::io::prelude::*; @@ -29,10 +30,9 @@ fn create_test_handler() -> (Handler, Lrc, Arc>>) { vec![crate::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE], false, ); - let emitter = - EmitterWriter::new(Box::new(Shared { data: output.clone() }), fallback_bundle, false) - .sm(Some(source_map.clone())) - .diagnostic_width(Some(140)); + let emitter = EmitterWriter::new(Box::new(Shared { data: output.clone() }), fallback_bundle) + .sm(Some(source_map.clone())) + .diagnostic_width(Some(140)); let handler = Handler::with_emitter(Box::new(emitter)); (handler, source_map, output) } @@ -156,6 +156,20 @@ pub(crate) struct Shared { pub data: Arc>, } +impl WriteColor for Shared { + fn supports_color(&self) -> bool { + false + } + + fn set_color(&mut self, _spec: &termcolor::ColorSpec) -> io::Result<()> { + Ok(()) + } + + fn reset(&mut self) -> io::Result<()> { + Ok(()) + } +} + impl Write for Shared { fn write(&mut self, buf: &[u8]) -> io::Result { self.data.lock().unwrap().write(buf) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 1afac214da3..3315ccad4d3 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -562,7 +562,7 @@ pub(crate) fn make_test( .diagnostic_width(Some(80)) .supports_color(); - let emitter = EmitterWriter::new(Box::new(io::sink()), fallback_bundle, false); + let emitter = EmitterWriter::new(Box::new(io::sink()), fallback_bundle); // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings(); @@ -738,7 +738,7 @@ fn check_if_attr_is_complete(source: &str, edition: Edition) -> bool { false, ); - let emitter = EmitterWriter::new(Box::new(io::sink()), fallback_bundle, false); + let emitter = EmitterWriter::new(Box::new(io::sink()), fallback_bundle); let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings(); let sess = ParseSess::with_span_handler(handler, sm); diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index 573838ce63e..2c4d93e33ba 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -719,7 +719,6 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { let emitter = EmitterWriter::new( Box::new(io::sink()), fallback_bundle, - false, ); let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings(); let sess = ParseSess::with_span_handler(handler, sm); From 339890e18618ff6efbe999e079a242c083dd7060 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 26 Jul 2023 14:43:39 +0000 Subject: [PATCH 09/56] Remove an enum variant that can be covered by another --- compiler/rustc_errors/src/emitter.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 87b3a25ed44..a54bc1b3c49 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -2609,7 +2609,6 @@ pub enum Destination { } pub enum WritableDst<'a> { - Terminal(&'a mut StandardStream), Buffered(&'a mut BufferWriter, Buffer), Raw(&'a mut (dyn WriteColor + Send)), } @@ -2632,7 +2631,7 @@ impl Destination { fn writable(&mut self) -> WritableDst<'_> { match *self { - Destination::Terminal(ref mut t) => WritableDst::Terminal(t), + Destination::Terminal(ref mut t) => WritableDst::Raw(t), Destination::Buffered(ref mut t) => { let buf = t.buffer(); WritableDst::Buffered(t, buf) @@ -2703,7 +2702,6 @@ impl<'a> WritableDst<'a> { fn set_color(&mut self, color: &ColorSpec) -> io::Result<()> { match *self { - WritableDst::Terminal(ref mut t) => t.set_color(color), WritableDst::Buffered(_, ref mut t) => t.set_color(color), WritableDst::Raw(ref mut t) => t.set_color(color), } @@ -2711,7 +2709,6 @@ impl<'a> WritableDst<'a> { fn reset(&mut self) -> io::Result<()> { match *self { - WritableDst::Terminal(ref mut t) => t.reset(), WritableDst::Buffered(_, ref mut t) => t.reset(), WritableDst::Raw(ref mut t) => t.reset(), } @@ -2721,7 +2718,6 @@ impl<'a> WritableDst<'a> { impl<'a> Write for WritableDst<'a> { fn write(&mut self, bytes: &[u8]) -> io::Result { match *self { - WritableDst::Terminal(ref mut t) => t.write(bytes), WritableDst::Buffered(_, ref mut buf) => buf.write(bytes), WritableDst::Raw(ref mut w) => w.write(bytes), } @@ -2729,7 +2725,6 @@ impl<'a> Write for WritableDst<'a> { fn flush(&mut self) -> io::Result<()> { match *self { - WritableDst::Terminal(ref mut t) => t.flush(), WritableDst::Buffered(_, ref mut buf) => buf.flush(), WritableDst::Raw(ref mut w) => w.flush(), } From 00074698a7addd9bc726e3742c6462d2ee9627bf Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 26 Jul 2023 14:56:34 +0000 Subject: [PATCH 10/56] Merge buffered destination into raw destination --- compiler/rustc_errors/src/emitter.rs | 54 +++++++++++++++++++--------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index a54bc1b3c49..d60d4c7c8fb 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -36,8 +36,8 @@ use std::io::prelude::*; use std::io::{self, IsTerminal}; use std::iter; use std::path::Path; -use termcolor::{Ansi, BufferWriter, ColorChoice, ColorSpec, StandardStream}; -use termcolor::{Buffer, Color, WriteColor}; +use termcolor::{Ansi, Buffer, BufferWriter, ColorChoice, ColorSpec, StandardStream}; +use termcolor::{Color, WriteColor}; /// Default column width, used in tests and when terminal dimensions cannot be determined. const DEFAULT_COLUMN_WIDTH: usize = 140; @@ -2604,15 +2604,44 @@ fn emit_to_destination( pub enum Destination { Terminal(StandardStream), - Buffered(BufferWriter), Raw(Box<(dyn WriteColor + Send)>), } pub enum WritableDst<'a> { - Buffered(&'a mut BufferWriter, Buffer), Raw(&'a mut (dyn WriteColor + Send)), } +struct Buffy { + buffer_writer: BufferWriter, + buffer: Buffer, +} + +impl Write for Buffy { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.buffer.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.buffer_writer.print(&self.buffer)?; + self.buffer.clear(); + Ok(()) + } +} + +impl WriteColor for Buffy { + fn supports_color(&self) -> bool { + self.buffer.supports_color() + } + + fn set_color(&mut self, spec: &ColorSpec) -> io::Result<()> { + self.buffer.set_color(spec) + } + + fn reset(&mut self) -> io::Result<()> { + self.buffer.reset() + } +} + impl Destination { fn from_stderr(color: ColorConfig) -> Destination { let choice = color.to_color_choice(); @@ -2625,17 +2654,15 @@ impl Destination { if cfg!(windows) { Terminal(StandardStream::stderr(choice)) } else { - Buffered(BufferWriter::stderr(choice)) + let buffer_writer = BufferWriter::stderr(choice); + let buffer = buffer_writer.buffer(); + Raw(Box::new(Buffy { buffer_writer, buffer })) } } fn writable(&mut self) -> WritableDst<'_> { match *self { Destination::Terminal(ref mut t) => WritableDst::Raw(t), - Destination::Buffered(ref mut t) => { - let buf = t.buffer(); - WritableDst::Buffered(t, buf) - } Destination::Raw(ref mut t) => WritableDst::Raw(t), } } @@ -2643,7 +2670,6 @@ impl Destination { fn supports_color(&self) -> bool { match *self { Self::Terminal(ref stream) => stream.supports_color(), - Self::Buffered(ref buffer) => buffer.buffer().supports_color(), Self::Raw(ref writer) => writer.supports_color(), } } @@ -2702,14 +2728,12 @@ impl<'a> WritableDst<'a> { fn set_color(&mut self, color: &ColorSpec) -> io::Result<()> { match *self { - WritableDst::Buffered(_, ref mut t) => t.set_color(color), WritableDst::Raw(ref mut t) => t.set_color(color), } } fn reset(&mut self) -> io::Result<()> { match *self { - WritableDst::Buffered(_, ref mut t) => t.reset(), WritableDst::Raw(ref mut t) => t.reset(), } } @@ -2718,14 +2742,12 @@ impl<'a> WritableDst<'a> { impl<'a> Write for WritableDst<'a> { fn write(&mut self, bytes: &[u8]) -> io::Result { match *self { - WritableDst::Buffered(_, ref mut buf) => buf.write(bytes), WritableDst::Raw(ref mut w) => w.write(bytes), } } fn flush(&mut self) -> io::Result<()> { match *self { - WritableDst::Buffered(_, ref mut buf) => buf.flush(), WritableDst::Raw(ref mut w) => w.flush(), } } @@ -2733,9 +2755,7 @@ impl<'a> Write for WritableDst<'a> { impl<'a> Drop for WritableDst<'a> { fn drop(&mut self) { - if let WritableDst::Buffered(ref mut dst, ref mut buf) = self { - drop(dst.print(buf)); - } + self.flush().unwrap() } } From 1e2167f5b56b443c70546bcd332c87266c1def29 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 26 Jul 2023 14:58:04 +0000 Subject: [PATCH 11/56] Move `WritableDst` method onto `Style` directly --- compiler/rustc_errors/src/emitter.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index d60d4c7c8fb..13d405c41bc 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -2590,7 +2590,8 @@ fn emit_to_destination( let _buffer_lock = lock::acquire_global_lock("rustc_errors"); for (pos, line) in rendered_buffer.iter().enumerate() { for part in line { - dst.apply_style(*lvl, part.style)?; + let style = part.style.color_spec(*lvl); + dst.set_color(&style)?; write!(dst, "{}", part.text)?; dst.reset()?; } @@ -2675,10 +2676,10 @@ impl Destination { } } -impl<'a> WritableDst<'a> { - fn apply_style(&mut self, lvl: Level, style: Style) -> io::Result<()> { +impl Style { + fn color_spec(&self, lvl: Level) -> ColorSpec { let mut spec = ColorSpec::new(); - match style { + match self { Style::Addition => { spec.set_fg(Some(Color::Green)).set_intense(true); } @@ -2723,9 +2724,11 @@ impl<'a> WritableDst<'a> { spec.set_bold(true); } } - self.set_color(&spec) + spec } +} +impl<'a> WritableDst<'a> { fn set_color(&mut self, color: &ColorSpec) -> io::Result<()> { match *self { WritableDst::Raw(ref mut t) => t.set_color(color), From 826a8ef52ed7809953e5a702d5ea88de2f1fd065 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 26 Jul 2023 18:24:14 +0000 Subject: [PATCH 12/56] Remove a now-redundant single-variant enum --- compiler/rustc_errors/src/emitter.rs | 48 +++------------------------- 1 file changed, 5 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 13d405c41bc..cf456ebc8d5 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -2158,7 +2158,7 @@ impl EmitterWriter { Err(e) => panic!("failed to emit error: {e}"), } - let mut dst = self.dst.writable(); + let dst = self.dst.writable(); match writeln!(dst) { Err(e) => panic!("failed to emit error: {e}"), _ => { @@ -2573,7 +2573,7 @@ fn emit_to_destination( ) -> io::Result<()> { use crate::lock; - let mut dst = dst.writable(); + let dst = dst.writable(); // In order to prevent error message interleaving, where multiple error lines get intermixed // when multiple compiler processes error simultaneously, we emit errors with additional @@ -2608,10 +2608,6 @@ pub enum Destination { Raw(Box<(dyn WriteColor + Send)>), } -pub enum WritableDst<'a> { - Raw(&'a mut (dyn WriteColor + Send)), -} - struct Buffy { buffer_writer: BufferWriter, buffer: Buffer, @@ -2661,10 +2657,10 @@ impl Destination { } } - fn writable(&mut self) -> WritableDst<'_> { + fn writable(&mut self) -> &mut dyn WriteColor { match *self { - Destination::Terminal(ref mut t) => WritableDst::Raw(t), - Destination::Raw(ref mut t) => WritableDst::Raw(t), + Destination::Terminal(ref mut t) => t, + Destination::Raw(ref mut t) => t, } } @@ -2728,40 +2724,6 @@ impl Style { } } -impl<'a> WritableDst<'a> { - fn set_color(&mut self, color: &ColorSpec) -> io::Result<()> { - match *self { - WritableDst::Raw(ref mut t) => t.set_color(color), - } - } - - fn reset(&mut self) -> io::Result<()> { - match *self { - WritableDst::Raw(ref mut t) => t.reset(), - } - } -} - -impl<'a> Write for WritableDst<'a> { - fn write(&mut self, bytes: &[u8]) -> io::Result { - match *self { - WritableDst::Raw(ref mut w) => w.write(bytes), - } - } - - fn flush(&mut self) -> io::Result<()> { - match *self { - WritableDst::Raw(ref mut w) => w.flush(), - } - } -} - -impl<'a> Drop for WritableDst<'a> { - fn drop(&mut self) { - self.flush().unwrap() - } -} - /// Whether the original and suggested code are visually similar enough to warrant extra wording. pub fn is_case_difference(sm: &SourceMap, suggested: &str, sp: Span) -> bool { // FIXME: this should probably be extended to also account for `FO0` → `FOO` and unicode. From 10da30f540f76c10c7f029da1eec27439496f703 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 26 Jul 2023 18:26:24 +0000 Subject: [PATCH 13/56] Merge all `Destination` variants --- compiler/rustc_errors/src/emitter.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index cf456ebc8d5..b51ec3a8fcd 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -2604,7 +2604,6 @@ fn emit_to_destination( } pub enum Destination { - Terminal(StandardStream), Raw(Box<(dyn WriteColor + Send)>), } @@ -2649,7 +2648,7 @@ impl Destination { // On non-Windows we rely on the atomicity of `write` to ensure errors // don't get all jumbled up. if cfg!(windows) { - Terminal(StandardStream::stderr(choice)) + Raw(Box::new(StandardStream::stderr(choice))) } else { let buffer_writer = BufferWriter::stderr(choice); let buffer = buffer_writer.buffer(); @@ -2659,14 +2658,12 @@ impl Destination { fn writable(&mut self) -> &mut dyn WriteColor { match *self { - Destination::Terminal(ref mut t) => t, Destination::Raw(ref mut t) => t, } } fn supports_color(&self) -> bool { match *self { - Self::Terminal(ref stream) => stream.supports_color(), Self::Raw(ref writer) => writer.supports_color(), } } From 2131eee179019bb0572b17943e4913ff77108123 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 26 Jul 2023 18:39:10 +0000 Subject: [PATCH 14/56] Turn a single-variant enum into a struct --- compiler/rustc_errors/src/emitter.rs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index b51ec3a8fcd..2d1b954690e 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -7,8 +7,6 @@ //! //! The output types are defined in `rustc_session::config::ErrorOutputType`. -use Destination::*; - use rustc_span::source_map::SourceMap; use rustc_span::{FileLines, SourceFile, Span}; @@ -675,7 +673,7 @@ impl EmitterWriter { dst: Box, fallback_bundle: LazyFallbackBundle, ) -> EmitterWriter { - Self::create(Raw(dst), fallback_bundle) + Self::create(Destination(dst), fallback_bundle) } fn maybe_anonymized(&self, line_num: usize) -> Cow<'static, str> { @@ -2603,9 +2601,7 @@ fn emit_to_destination( Ok(()) } -pub enum Destination { - Raw(Box<(dyn WriteColor + Send)>), -} +pub struct Destination(pub(crate) Box<(dyn WriteColor + Send)>); struct Buffy { buffer_writer: BufferWriter, @@ -2648,24 +2644,20 @@ impl Destination { // On non-Windows we rely on the atomicity of `write` to ensure errors // don't get all jumbled up. if cfg!(windows) { - Raw(Box::new(StandardStream::stderr(choice))) + Destination(Box::new(StandardStream::stderr(choice))) } else { let buffer_writer = BufferWriter::stderr(choice); let buffer = buffer_writer.buffer(); - Raw(Box::new(Buffy { buffer_writer, buffer })) + Destination(Box::new(Buffy { buffer_writer, buffer })) } } fn writable(&mut self) -> &mut dyn WriteColor { - match *self { - Destination::Raw(ref mut t) => t, - } + &mut self.0 } fn supports_color(&self) -> bool { - match *self { - Self::Raw(ref writer) => writer.supports_color(), - } + self.0.supports_color() } } From d9deaf4b8af7efc4b980e00e2f15622c20d9cdc3 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 26 Jul 2023 18:47:38 +0000 Subject: [PATCH 15/56] Get rid of the thin wrapper type that is `Destination` and just write to the `Writer` trait object directly --- compiler/rustc_errors/src/emitter.rs | 51 +++++++++++----------------- 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 2d1b954690e..0d73073395b 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -649,7 +649,7 @@ pub struct FileWithAnnotatedLines { impl EmitterWriter { pub fn stderr(color_config: ColorConfig, fallback_bundle: LazyFallbackBundle) -> EmitterWriter { - let dst = Destination::from_stderr(color_config); + let dst = from_stderr(color_config); Self::create(dst, fallback_bundle) } @@ -673,7 +673,7 @@ impl EmitterWriter { dst: Box, fallback_bundle: LazyFallbackBundle, ) -> EmitterWriter { - Self::create(Destination(dst), fallback_bundle) + Self::create(dst, fallback_bundle) } fn maybe_anonymized(&self, line_num: usize) -> Cow<'static, str> { @@ -2156,11 +2156,10 @@ impl EmitterWriter { Err(e) => panic!("failed to emit error: {e}"), } - let dst = self.dst.writable(); - match writeln!(dst) { + match writeln!(self.dst) { Err(e) => panic!("failed to emit error: {e}"), _ => { - if let Err(e) = dst.flush() { + if let Err(e) = self.dst.flush() { panic!("failed to emit error: {e}") } } @@ -2571,8 +2570,6 @@ fn emit_to_destination( ) -> io::Result<()> { use crate::lock; - let dst = dst.writable(); - // In order to prevent error message interleaving, where multiple error lines get intermixed // when multiple compiler processes error simultaneously, we emit errors with additional // steps. @@ -2601,7 +2598,7 @@ fn emit_to_destination( Ok(()) } -pub struct Destination(pub(crate) Box<(dyn WriteColor + Send)>); +pub type Destination = Box<(dyn WriteColor + Send)>; struct Buffy { buffer_writer: BufferWriter, @@ -2634,30 +2631,20 @@ impl WriteColor for Buffy { } } -impl Destination { - fn from_stderr(color: ColorConfig) -> Destination { - let choice = color.to_color_choice(); - // On Windows we'll be performing global synchronization on the entire - // system for emitting rustc errors, so there's no need to buffer - // anything. - // - // On non-Windows we rely on the atomicity of `write` to ensure errors - // don't get all jumbled up. - if cfg!(windows) { - Destination(Box::new(StandardStream::stderr(choice))) - } else { - let buffer_writer = BufferWriter::stderr(choice); - let buffer = buffer_writer.buffer(); - Destination(Box::new(Buffy { buffer_writer, buffer })) - } - } - - fn writable(&mut self) -> &mut dyn WriteColor { - &mut self.0 - } - - fn supports_color(&self) -> bool { - self.0.supports_color() +fn from_stderr(color: ColorConfig) -> Destination { + let choice = color.to_color_choice(); + // On Windows we'll be performing global synchronization on the entire + // system for emitting rustc errors, so there's no need to buffer + // anything. + // + // On non-Windows we rely on the atomicity of `write` to ensure errors + // don't get all jumbled up. + if cfg!(windows) { + Box::new(StandardStream::stderr(choice)) + } else { + let buffer_writer = BufferWriter::stderr(choice); + let buffer = buffer_writer.buffer(); + Box::new(Buffy { buffer_writer, buffer }) } } From 375d8f1b25b12d252e6aff030c0bcf64b77f886b Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Wed, 26 Jul 2023 18:52:08 +0000 Subject: [PATCH 16/56] Sanity check that we actually flush all buffers --- compiler/rustc_errors/src/emitter.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index 0d73073395b..0cae06881b1 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -2617,6 +2617,15 @@ impl Write for Buffy { } } +impl Drop for Buffy { + fn drop(&mut self) { + if !self.buffer.is_empty() { + self.flush().unwrap(); + panic!("buffers need to be flushed in order to print their contents"); + } + } +} + impl WriteColor for Buffy { fn supports_color(&self) -> bool { self.buffer.supports_color() From f74eee2fa6620ecb88070ed2eb2dfd7ba12e0b63 Mon Sep 17 00:00:00 2001 From: Taras Tsugrii Date: Tue, 1 Aug 2023 16:57:43 -0700 Subject: [PATCH 17/56] [rustc_span][perf] Remove unnecessary string joins and allocs. Comparing vectors of string parts yields the same result but avoids unnecessary `join` and potential allocation for resulting `String`. This code is cold so it's unlikely to have any measurable impact, but considering but since it's also simpler, why not? :) --- compiler/rustc_span/src/edit_distance.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_span/src/edit_distance.rs b/compiler/rustc_span/src/edit_distance.rs index 259f4238654..0ce099387f4 100644 --- a/compiler/rustc_span/src/edit_distance.rs +++ b/compiler/rustc_span/src/edit_distance.rs @@ -247,9 +247,9 @@ fn find_match_by_sorted_words(iter_names: &[Symbol], lookup: &str) -> Option String { +fn sort_by_words(name: &str) -> Vec<&str> { let mut split_words: Vec<&str> = name.split('_').collect(); // We are sorting primitive &strs and can use unstable sort here. split_words.sort_unstable(); - split_words.join("_") + split_words } From 673ab17c7f2b23ead8190df2514be52f81973573 Mon Sep 17 00:00:00 2001 From: Bryan Garza <1396101+bryangarza@users.noreply.github.com> Date: Tue, 30 May 2023 14:32:29 -0700 Subject: [PATCH 18/56] Add separate feature gate for async fn track caller This patch adds a feature gate `async_fn_track_caller` that is separate from `closure_track_caller`. This is to allow enabling `async_fn_track_caller` separately. Fixes #110009 --- compiler/rustc_ast_lowering/src/expr.rs | 4 +- compiler/rustc_ast_lowering/src/item.rs | 7 ++- .../rustc_codegen_ssa/src/codegen_attrs.rs | 9 ++- compiler/rustc_feature/src/active.rs | 2 + compiler/rustc_lint/src/builtin.rs | 12 ++-- compiler/rustc_lint/src/lints.rs | 2 +- compiler/rustc_span/src/symbol.rs | 1 + .../track-caller/async-block.afn.stderr | 30 ++++++++++ .../track-caller/async-block.nofeat.stderr | 30 ++++++++++ .../async-await/track-caller/async-block.rs | 18 ++++++ .../track-caller/async-block.stderr | 12 ---- .../async-closure-gate.afn.stderr | 57 +++++++++++++++++++ .../async-closure-gate.nofeat.stderr | 57 +++++++++++++++++++ .../track-caller/async-closure-gate.rs | 41 +++++++++++++ .../track-caller/async-closure-gate.stderr | 12 ---- .../panic-track-caller.cls.stderr | 31 ++++++++++ .../panic-track-caller.nofeat.stderr | 14 +++-- .../track-caller/panic-track-caller.rs | 46 ++++++++------- tests/ui/proc-macro/meta-macro-hygiene.stdout | 2 +- .../nonterminal-token-hygiene.stdout | 2 +- 20 files changed, 326 insertions(+), 63 deletions(-) create mode 100644 tests/ui/async-await/track-caller/async-block.afn.stderr create mode 100644 tests/ui/async-await/track-caller/async-block.nofeat.stderr delete mode 100644 tests/ui/async-await/track-caller/async-block.stderr create mode 100644 tests/ui/async-await/track-caller/async-closure-gate.afn.stderr create mode 100644 tests/ui/async-await/track-caller/async-closure-gate.nofeat.stderr delete mode 100644 tests/ui/async-await/track-caller/async-closure-gate.stderr create mode 100644 tests/ui/async-await/track-caller/panic-track-caller.cls.stderr diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 0954cf03da9..6bbf59b7fd3 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -657,14 +657,14 @@ impl<'hir> LoweringContext<'_, 'hir> { } /// Forwards a possible `#[track_caller]` annotation from `outer_hir_id` to - /// `inner_hir_id` in case the `closure_track_caller` feature is enabled. + /// `inner_hir_id` in case the `async_fn_track_caller` feature is enabled. pub(super) fn maybe_forward_track_caller( &mut self, span: Span, outer_hir_id: hir::HirId, inner_hir_id: hir::HirId, ) { - if self.tcx.features().closure_track_caller + if self.tcx.features().async_fn_track_caller && let Some(attrs) = self.attrs.get(&outer_hir_id.local_id) && attrs.into_iter().any(|attr| attr.has_name(sym::track_caller)) { diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 5a0474dbc01..df73c721ade 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -56,6 +56,11 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> { owner: NodeId, f: impl FnOnce(&mut LoweringContext<'_, 'hir>) -> hir::OwnerNode<'hir>, ) { + let allow_gen_future = Some(if self.tcx.features().async_fn_track_caller { + [sym::gen_future, sym::closure_track_caller][..].into() + } else { + [sym::gen_future][..].into() + }); let mut lctx = LoweringContext { // Pseudo-globals. tcx: self.tcx, @@ -83,7 +88,7 @@ impl<'a, 'hir> ItemLowerer<'a, 'hir> { impl_trait_defs: Vec::new(), impl_trait_bounds: Vec::new(), allow_try_trait: Some([sym::try_trait_v2, sym::yeet_desugar_details][..].into()), - allow_gen_future: Some([sym::gen_future, sym::closure_track_caller][..].into()), + allow_gen_future, generics_def_id_map: Default::default(), }; lctx.with_hir_id_owner(owner, |lctx| f(lctx)); diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 92792ab6477..f6936c80b77 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -215,14 +215,19 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs { } sym::thread_local => codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL, sym::track_caller => { - if !tcx.is_closure(did.to_def_id()) + let is_closure = tcx.is_closure(did.to_def_id()); + + if !is_closure && let Some(fn_sig) = fn_sig() && fn_sig.skip_binder().abi() != abi::Abi::Rust { struct_span_err!(tcx.sess, attr.span, E0737, "`#[track_caller]` requires Rust ABI") .emit(); } - if tcx.is_closure(did.to_def_id()) && !tcx.features().closure_track_caller { + if is_closure + && !tcx.features().closure_track_caller + && !attr.span.allows_unstable(sym::closure_track_caller) + { feature_err( &tcx.sess.parse_sess, sym::closure_track_caller, diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index bbc3d291e20..92085b4ef9a 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -313,6 +313,8 @@ declare_features! ( (active, async_closure, "1.37.0", Some(62290), None), /// Allows async functions to be declared, implemented, and used in traits. (active, async_fn_in_trait, "1.66.0", Some(91611), None), + /// Allows `#[track_caller]` on async functions. + (active, async_fn_track_caller, "CURRENT_RUSTC_VERSION", Some(110011), None), /// Allows builtin # foo() syntax (active, builtin_syntax, "1.71.0", Some(110680), None), /// Allows `c"foo"` literals. diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index e6917f4b2d3..70621eba6af 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1259,8 +1259,8 @@ impl<'tcx> LateLintPass<'tcx> for UnstableFeatures { declare_lint! { /// The `ungated_async_fn_track_caller` lint warns when the - /// `#[track_caller]` attribute is used on an async function, method, or - /// closure, without enabling the corresponding unstable feature flag. + /// `#[track_caller]` attribute is used on an async function + /// without enabling the corresponding unstable feature flag. /// /// ### Example /// @@ -1274,13 +1274,13 @@ declare_lint! { /// ### Explanation /// /// The attribute must be used in conjunction with the - /// [`closure_track_caller` feature flag]. Otherwise, the `#[track_caller]` + /// [`async_fn_track_caller` feature flag]. Otherwise, the `#[track_caller]` /// annotation will function as a no-op. /// - /// [`closure_track_caller` feature flag]: https://doc.rust-lang.org/beta/unstable-book/language-features/closure-track-caller.html + /// [`async_fn_track_caller` feature flag]: https://doc.rust-lang.org/beta/unstable-book/language-features/async-fn-track-caller.html UNGATED_ASYNC_FN_TRACK_CALLER, Warn, - "enabling track_caller on an async fn is a no-op unless the closure_track_caller feature is enabled" + "enabling track_caller on an async fn is a no-op unless the async_fn_track_caller feature is enabled" } declare_lint_pass!( @@ -1300,7 +1300,7 @@ impl<'tcx> LateLintPass<'tcx> for UngatedAsyncFnTrackCaller { def_id: LocalDefId, ) { if fn_kind.asyncness() == IsAsync::Async - && !cx.tcx.features().closure_track_caller + && !cx.tcx.features().async_fn_track_caller // Now, check if the function has the `#[track_caller]` attribute && let Some(attr) = cx.tcx.get_attr(def_id, sym::track_caller) { diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index a6a48bf4ffa..bf955086393 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -250,7 +250,7 @@ impl<'a> DecorateLint<'a, ()> for BuiltinUngatedAsyncFnTrackCaller<'_> { rustc_session::parse::add_feature_diagnostics( diag, &self.parse_sess, - sym::closure_track_caller, + sym::async_fn_track_caller, ); diag } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index d3739733c1d..0d6d425a174 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -400,6 +400,7 @@ symbols! { async_await, async_closure, async_fn_in_trait, + async_fn_track_caller, atomic, atomic_mod, atomics, diff --git a/tests/ui/async-await/track-caller/async-block.afn.stderr b/tests/ui/async-await/track-caller/async-block.afn.stderr new file mode 100644 index 00000000000..2302722eecc --- /dev/null +++ b/tests/ui/async-await/track-caller/async-block.afn.stderr @@ -0,0 +1,30 @@ +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-block.rs:8:13 + | +LL | let _ = #[track_caller] async { + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-block.rs:15:13 + | +LL | let _ = #[track_caller] async { + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-block.rs:23:17 + | +LL | let _ = #[track_caller] async { + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/async-await/track-caller/async-block.nofeat.stderr b/tests/ui/async-await/track-caller/async-block.nofeat.stderr new file mode 100644 index 00000000000..2302722eecc --- /dev/null +++ b/tests/ui/async-await/track-caller/async-block.nofeat.stderr @@ -0,0 +1,30 @@ +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-block.rs:8:13 + | +LL | let _ = #[track_caller] async { + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-block.rs:15:13 + | +LL | let _ = #[track_caller] async { + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-block.rs:23:17 + | +LL | let _ = #[track_caller] async { + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/async-await/track-caller/async-block.rs b/tests/ui/async-await/track-caller/async-block.rs index 8ddd4ab1186..24711b966b5 100644 --- a/tests/ui/async-await/track-caller/async-block.rs +++ b/tests/ui/async-await/track-caller/async-block.rs @@ -1,9 +1,27 @@ // edition:2021 +// revisions: afn nofeat #![feature(stmt_expr_attributes)] +#![cfg_attr(afn, feature(async_fn_track_caller))] fn main() { let _ = #[track_caller] async { //~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] }; } + +#[track_caller] +async fn foo() { + let _ = #[track_caller] async { + //~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] + }; +} + +#[track_caller] +async fn foo2() { + let _ = async { + let _ = #[track_caller] async { + //~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] + }; + }; +} diff --git a/tests/ui/async-await/track-caller/async-block.stderr b/tests/ui/async-await/track-caller/async-block.stderr deleted file mode 100644 index 21d1054d220..00000000000 --- a/tests/ui/async-await/track-caller/async-block.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-block.rs:6:13 - | -LL | let _ = #[track_caller] async { - | ^^^^^^^^^^^^^^^ - | - = note: see issue #87417 for more information - = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/async-await/track-caller/async-closure-gate.afn.stderr b/tests/ui/async-await/track-caller/async-closure-gate.afn.stderr new file mode 100644 index 00000000000..739c04a7673 --- /dev/null +++ b/tests/ui/async-await/track-caller/async-closure-gate.afn.stderr @@ -0,0 +1,57 @@ +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-closure-gate.rs:8:13 + | +LL | let _ = #[track_caller] async || { + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-closure-gate.rs:15:13 + | +LL | let _ = #[track_caller] async || { + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-closure-gate.rs:21:13 + | +LL | let _ = #[track_caller] || { + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-closure-gate.rs:28:17 + | +LL | let _ = #[track_caller] || { + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-closure-gate.rs:36:9 + | +LL | #[track_caller] || { + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-closure-gate.rs:45:13 + | +LL | #[track_caller] || { + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/async-await/track-caller/async-closure-gate.nofeat.stderr b/tests/ui/async-await/track-caller/async-closure-gate.nofeat.stderr new file mode 100644 index 00000000000..739c04a7673 --- /dev/null +++ b/tests/ui/async-await/track-caller/async-closure-gate.nofeat.stderr @@ -0,0 +1,57 @@ +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-closure-gate.rs:8:13 + | +LL | let _ = #[track_caller] async || { + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-closure-gate.rs:15:13 + | +LL | let _ = #[track_caller] async || { + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-closure-gate.rs:21:13 + | +LL | let _ = #[track_caller] || { + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-closure-gate.rs:28:17 + | +LL | let _ = #[track_caller] || { + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-closure-gate.rs:36:9 + | +LL | #[track_caller] || { + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + +error[E0658]: `#[track_caller]` on closures is currently unstable + --> $DIR/async-closure-gate.rs:45:13 + | +LL | #[track_caller] || { + | ^^^^^^^^^^^^^^^ + | + = note: see issue #87417 for more information + = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/async-await/track-caller/async-closure-gate.rs b/tests/ui/async-await/track-caller/async-closure-gate.rs index d9d55685599..8d8d081aa90 100644 --- a/tests/ui/async-await/track-caller/async-closure-gate.rs +++ b/tests/ui/async-await/track-caller/async-closure-gate.rs @@ -1,9 +1,50 @@ // edition:2021 +// revisions: afn nofeat #![feature(async_closure, stmt_expr_attributes)] +#![cfg_attr(afn, feature(async_fn_track_caller))] fn main() { let _ = #[track_caller] async || { //~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] }; } + +#[track_caller] +async fn foo() { + let _ = #[track_caller] async || { + //~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] + }; +} + +async fn foo2() { + let _ = #[track_caller] || { + //~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] + }; +} + +fn foo3() { + async { + let _ = #[track_caller] || { + //~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] + }; + } +} + +async fn foo4() { + let _ = || { + #[track_caller] || { + //~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] + }; + }; +} + +fn foo5() { + async { + let _ = || { + #[track_caller] || { + //~^ ERROR `#[track_caller]` on closures is currently unstable [E0658] + }; + }; + } +} diff --git a/tests/ui/async-await/track-caller/async-closure-gate.stderr b/tests/ui/async-await/track-caller/async-closure-gate.stderr deleted file mode 100644 index 498f1b43b9b..00000000000 --- a/tests/ui/async-await/track-caller/async-closure-gate.stderr +++ /dev/null @@ -1,12 +0,0 @@ -error[E0658]: `#[track_caller]` on closures is currently unstable - --> $DIR/async-closure-gate.rs:6:13 - | -LL | let _ = #[track_caller] async || { - | ^^^^^^^^^^^^^^^ - | - = note: see issue #87417 for more information - = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr b/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr new file mode 100644 index 00000000000..f3090e3b9a6 --- /dev/null +++ b/tests/ui/async-await/track-caller/panic-track-caller.cls.stderr @@ -0,0 +1,31 @@ +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:53:1 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +... +LL | / async fn bar_track_caller() { +LL | | panic!() +LL | | } + | |_- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + = note: `#[warn(ungated_async_fn_track_caller)]` on by default + +warning: `#[track_caller]` on async functions is a no-op + --> $DIR/panic-track-caller.rs:67:5 + | +LL | #[track_caller] + | ^^^^^^^^^^^^^^^ +... +LL | / async fn bar_assoc() { +LL | | panic!(); +LL | | } + | |_____- this function will not propagate the caller location + | + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable + +warning: 2 warnings emitted + diff --git a/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr b/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr index 51ea225f4cb..f3090e3b9a6 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr +++ b/tests/ui/async-await/track-caller/panic-track-caller.nofeat.stderr @@ -1,29 +1,31 @@ warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:50:1 + --> $DIR/panic-track-caller.rs:53:1 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ +... LL | / async fn bar_track_caller() { LL | | panic!() LL | | } | |_- this function will not propagate the caller location | - = note: see issue #87417 for more information - = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable = note: `#[warn(ungated_async_fn_track_caller)]` on by default warning: `#[track_caller]` on async functions is a no-op - --> $DIR/panic-track-caller.rs:62:5 + --> $DIR/panic-track-caller.rs:67:5 | LL | #[track_caller] | ^^^^^^^^^^^^^^^ +... LL | / async fn bar_assoc() { LL | | panic!(); LL | | } | |_____- this function will not propagate the caller location | - = note: see issue #87417 for more information - = help: add `#![feature(closure_track_caller)]` to the crate attributes to enable + = note: see issue #110011 for more information + = help: add `#![feature(async_fn_track_caller)]` to the crate attributes to enable warning: 2 warnings emitted diff --git a/tests/ui/async-await/track-caller/panic-track-caller.rs b/tests/ui/async-await/track-caller/panic-track-caller.rs index 65bb23e0b4b..df8290e5fff 100644 --- a/tests/ui/async-await/track-caller/panic-track-caller.rs +++ b/tests/ui/async-await/track-caller/panic-track-caller.rs @@ -1,9 +1,12 @@ // run-pass // edition:2021 -// revisions: feat nofeat +// revisions: afn cls nofeat // needs-unwind +// gate-test-async_fn_track_caller #![feature(async_closure, stmt_expr_attributes)] -#![cfg_attr(feat, feature(closure_track_caller))] +#![cfg_attr(afn, feature(async_fn_track_caller))] +#![cfg_attr(cls, feature(closure_track_caller))] +#![allow(unused)] use std::future::Future; use std::panic; @@ -47,7 +50,9 @@ async fn foo() { bar().await } -#[track_caller] //[nofeat]~ WARN `#[track_caller]` on async functions is a no-op +#[track_caller] +//[cls]~^ WARN `#[track_caller]` on async functions is a no-op +//[nofeat]~^^ WARN `#[track_caller]` on async functions is a no-op async fn bar_track_caller() { panic!() } @@ -59,7 +64,9 @@ async fn foo_track_caller() { struct Foo; impl Foo { - #[track_caller] //[nofeat]~ WARN `#[track_caller]` on async functions is a no-op + #[track_caller] + //[cls]~^ WARN `#[track_caller]` on async functions is a no-op + //[nofeat]~^^ WARN `#[track_caller]` on async functions is a no-op async fn bar_assoc() { panic!(); } @@ -71,7 +78,7 @@ async fn foo_assoc() { // Since compilation is expected to fail for this fn when using // `nofeat`, we test that separately in `async-closure-gate.rs` -#[cfg(feat)] +#[cfg(cls)] async fn foo_closure() { let c = #[track_caller] async || { panic!(); @@ -81,7 +88,7 @@ async fn foo_closure() { // Since compilation is expected to fail for this fn when using // `nofeat`, we test that separately in `async-block.rs` -#[cfg(feat)] +#[cfg(cls)] async fn foo_block() { let a = #[track_caller] async { panic!(); @@ -106,21 +113,22 @@ fn panicked_at(f: impl FnOnce() + panic::UnwindSafe) -> u32 { } fn main() { - assert_eq!(panicked_at(|| block_on(foo())), 43); + assert_eq!(panicked_at(|| block_on(foo())), 46 +); - #[cfg(feat)] - assert_eq!(panicked_at(|| block_on(foo_track_caller())), 56); - #[cfg(nofeat)] - assert_eq!(panicked_at(|| block_on(foo_track_caller())), 52); + #[cfg(afn)] + assert_eq!(panicked_at(|| block_on(foo_track_caller())), 61); + #[cfg(any(cls, nofeat))] + assert_eq!(panicked_at(|| block_on(foo_track_caller())), 57); - #[cfg(feat)] - assert_eq!(panicked_at(|| block_on(foo_assoc())), 69); - #[cfg(nofeat)] - assert_eq!(panicked_at(|| block_on(foo_assoc())), 64); + #[cfg(afn)] + assert_eq!(panicked_at(|| block_on(foo_assoc())), 76); + #[cfg(any(cls, nofeat))] + assert_eq!(panicked_at(|| block_on(foo_assoc())), 71); - #[cfg(feat)] - assert_eq!(panicked_at(|| block_on(foo_closure())), 79); + #[cfg(cls)] + assert_eq!(panicked_at(|| block_on(foo_closure())), 84); - #[cfg(feat)] - assert_eq!(panicked_at(|| block_on(foo_block())), 89); + #[cfg(cls)] + assert_eq!(panicked_at(|| block_on(foo_block())), 96); } diff --git a/tests/ui/proc-macro/meta-macro-hygiene.stdout b/tests/ui/proc-macro/meta-macro-hygiene.stdout index 4a2200091b2..e476a8024fd 100644 --- a/tests/ui/proc-macro/meta-macro-hygiene.stdout +++ b/tests/ui/proc-macro/meta-macro-hygiene.stdout @@ -18,7 +18,7 @@ Respanned: TokenStream [Ident { ident: "$crate", span: $DIR/auxiliary/make-macro use core /* 0#1 */::prelude /* 0#1 */::rust_2018 /* 0#1 */::*; #[macro_use /* 0#1 */] extern crate core /* 0#1 */; -extern crate compiler_builtins /* 443 */ as _ /* 0#1 */; +extern crate compiler_builtins /* 444 */ as _ /* 0#1 */; // Don't load unnecessary hygiene information from std extern crate std /* 0#0 */; diff --git a/tests/ui/proc-macro/nonterminal-token-hygiene.stdout b/tests/ui/proc-macro/nonterminal-token-hygiene.stdout index 077a728a7a6..23a21004238 100644 --- a/tests/ui/proc-macro/nonterminal-token-hygiene.stdout +++ b/tests/ui/proc-macro/nonterminal-token-hygiene.stdout @@ -39,7 +39,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ use ::core /* 0#1 */::prelude /* 0#1 */::rust_2015 /* 0#1 */::*; #[macro_use /* 0#1 */] extern crate core /* 0#2 */; -extern crate compiler_builtins /* 443 */ as _ /* 0#2 */; +extern crate compiler_builtins /* 444 */ as _ /* 0#2 */; // Don't load unnecessary hygiene information from std extern crate std /* 0#0 */; From 263a0dec60c8cf1917b65abc83f7dae29086e986 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 3 Aug 2023 01:03:31 +0200 Subject: [PATCH 19/56] Compute variances for lazy type aliases --- .../rustc_hir_analysis/src/check/wfcheck.rs | 26 +++++++++++-- .../src/variance/constraints.rs | 28 ++++++++++++-- .../rustc_hir_analysis/src/variance/mod.rs | 10 ++++- .../rustc_hir_analysis/src/variance/terms.rs | 8 +++- compiler/rustc_metadata/src/rmeta/encoder.rs | 10 +++-- compiler/rustc_middle/src/query/mod.rs | 2 +- tests/ui/lazy-type-alias/variance.rs | 38 +++++++++++++++++++ 7 files changed, 109 insertions(+), 13 deletions(-) create mode 100644 tests/ui/lazy-type-alias/variance.rs diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index e64848da86b..16d4d099c7e 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -245,13 +245,14 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) { } // `ForeignItem`s are handled separately. hir::ItemKind::ForeignMod { .. } => {} - hir::ItemKind::TyAlias(hir_ty, ..) => { + hir::ItemKind::TyAlias(hir_ty, ast_generics) => { if tcx.features().lazy_type_alias || tcx.type_of(item.owner_id).skip_binder().has_opaque_types() { // Bounds of lazy type aliases and of eager ones that contain opaque types are respected. // E.g: `type X = impl Trait;`, `type X = (impl Trait, Y);`. check_item_type(tcx, def_id, hir_ty.span, UnsizedHandling::Allow); + check_variances_for_type_defn(tcx, item, ast_generics); } } _ => {} @@ -1700,10 +1701,27 @@ fn check_variances_for_type_defn<'tcx>( hir_generics: &hir::Generics<'_>, ) { let identity_args = ty::GenericArgs::identity_for_item(tcx, item.owner_id); - for field in tcx.adt_def(item.owner_id).all_fields() { - if field.ty(tcx, identity_args).references_error() { - return; + + match item.kind { + ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) => { + for field in tcx.adt_def(item.owner_id).all_fields() { + if field.ty(tcx, identity_args).references_error() { + return; + } + } } + ItemKind::TyAlias(..) => { + let ty = tcx.type_of(item.owner_id).instantiate_identity(); + + if tcx.features().lazy_type_alias || ty.has_opaque_types() { + if ty.references_error() { + return; + } + } else { + bug!(); + } + } + _ => bug!(), } let ty_predicates = tcx.predicates_of(item.owner_id); diff --git a/compiler/rustc_hir_analysis/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs index ec8889781f4..4a3d522e488 100644 --- a/compiler/rustc_hir_analysis/src/variance/constraints.rs +++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs @@ -6,7 +6,7 @@ use hir::def_id::{DefId, LocalDefId}; use rustc_hir as hir; use rustc_hir::def::DefKind; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; use rustc_middle::ty::{GenericArgKind, GenericArgsRef}; use super::terms::VarianceTerm::*; @@ -78,6 +78,12 @@ pub fn add_constraints_from_crate<'a, 'tcx>( } } DefKind::Fn | DefKind::AssocFn => constraint_cx.build_constraints_for_item(def_id), + DefKind::TyAlias + if tcx.features().lazy_type_alias + || tcx.type_of(def_id).instantiate_identity().has_opaque_types() => + { + constraint_cx.build_constraints_for_item(def_id) + } _ => {} } } @@ -101,7 +107,18 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { let inferred_start = self.terms_cx.inferred_starts[&def_id]; let current_item = &CurrentItem { inferred_start }; - match tcx.type_of(def_id).instantiate_identity().kind() { + let ty = tcx.type_of(def_id).instantiate_identity(); + + // The type as returned by `type_of` is the underlying type and generally not a weak projection. + // Therefore we need to check the `DefKind` first. + if let DefKind::TyAlias = tcx.def_kind(def_id) + && (tcx.features().lazy_type_alias || ty.has_opaque_types()) + { + self.add_constraints_from_ty(current_item, ty, self.covariant); + return; + } + + match ty.kind() { ty::Adt(def, _) => { // Not entirely obvious: constraints on structs/enums do not // affect the variance of their type parameters. See discussion @@ -127,6 +144,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { } ty::Error(_) => {} + _ => { span_bug!( tcx.def_span(def_id), @@ -252,10 +270,14 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { self.add_constraints_from_args(current, def.did(), args, variance); } - ty::Alias(_, ref data) => { + ty::Alias(ty::Projection | ty::Inherent | ty::Opaque, ref data) => { self.add_constraints_from_invariant_args(current, data.args, variance); } + ty::Alias(ty::Weak, ref data) => { + self.add_constraints_from_args(current, data.def_id, data.args, variance); + } + ty::Dynamic(data, r, _) => { // The type `dyn Trait +'a` is covariant w/r/t `'a`: self.add_constraints_from_region(current, r, variance); diff --git a/compiler/rustc_hir_analysis/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs index 6952a3fa66f..2ef294c6793 100644 --- a/compiler/rustc_hir_analysis/src/variance/mod.rs +++ b/compiler/rustc_hir_analysis/src/variance/mod.rs @@ -8,7 +8,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::query::Providers; use rustc_middle::ty::{self, CrateVariancesMap, GenericArgsRef, Ty, TyCtxt}; -use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable}; +use rustc_middle::ty::{TypeSuperVisitable, TypeVisitable, TypeVisitableExt}; use std::ops::ControlFlow; /// Defines the `TermsContext` basically houses an arena where we can @@ -56,6 +56,14 @@ fn variances_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[ty::Variance] { let crate_map = tcx.crate_variances(()); return crate_map.variances.get(&item_def_id.to_def_id()).copied().unwrap_or(&[]); } + DefKind::TyAlias + if tcx.features().lazy_type_alias + || tcx.type_of(item_def_id).instantiate_identity().has_opaque_types() => + { + // These are inferred. + let crate_map = tcx.crate_variances(()); + return crate_map.variances.get(&item_def_id.to_def_id()).copied().unwrap_or(&[]); + } DefKind::OpaqueTy => { return variance_of_opaque(tcx, item_def_id); } diff --git a/compiler/rustc_hir_analysis/src/variance/terms.rs b/compiler/rustc_hir_analysis/src/variance/terms.rs index ed03c5da26f..1ef3d383bd8 100644 --- a/compiler/rustc_hir_analysis/src/variance/terms.rs +++ b/compiler/rustc_hir_analysis/src/variance/terms.rs @@ -12,7 +12,7 @@ use rustc_arena::DroplessArena; use rustc_hir::def::DefKind; use rustc_hir::def_id::{LocalDefId, LocalDefIdMap}; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use std::fmt; use self::VarianceTerm::*; @@ -97,6 +97,12 @@ pub fn determine_parameters_to_be_inferred<'a, 'tcx>( } } DefKind::Fn | DefKind::AssocFn => terms_cx.add_inferreds_for_item(def_id), + DefKind::TyAlias + if tcx.features().lazy_type_alias + || tcx.type_of(def_id).instantiate_identity().has_opaque_types() => + { + terms_cx.add_inferreds_for_item(def_id) + } _ => {} } } diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index d72053ca985..f12094a271f 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -30,6 +30,7 @@ use rustc_middle::query::Providers; use rustc_middle::traits::specialization_graph; use rustc_middle::ty::codec::TyEncoder; use rustc_middle::ty::fast_reject::{self, SimplifiedType, TreatParams}; +use rustc_middle::ty::TypeVisitableExt; use rustc_middle::ty::{self, AssocItemContainer, SymbolName, Ty, TyCtxt}; use rustc_middle::util::common::to_readable_str; use rustc_serialize::{opaque, Decodable, Decoder, Encodable, Encoder}; @@ -1034,7 +1035,7 @@ fn should_encode_mir(tcx: TyCtxt<'_>, def_id: LocalDefId) -> (bool, bool) { } } -fn should_encode_variances(def_kind: DefKind) -> bool { +fn should_encode_variances<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, def_kind: DefKind) -> bool { match def_kind { DefKind::Struct | DefKind::Union @@ -1053,7 +1054,6 @@ fn should_encode_variances(def_kind: DefKind) -> bool { | DefKind::Static(..) | DefKind::Const | DefKind::ForeignMod - | DefKind::TyAlias | DefKind::Impl { .. } | DefKind::Trait | DefKind::TraitAlias @@ -1067,6 +1067,10 @@ fn should_encode_variances(def_kind: DefKind) -> bool { | DefKind::Closure | DefKind::Generator | DefKind::ExternCrate => false, + DefKind::TyAlias => { + tcx.features().lazy_type_alias + || tcx.type_of(def_id).instantiate_identity().has_opaque_types() + } } } @@ -1349,7 +1353,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { self.encode_default_body_stability(def_id); self.encode_deprecation(def_id); } - if should_encode_variances(def_kind) { + if should_encode_variances(tcx, def_id, def_kind) { let v = self.tcx.variances_of(def_id); record_array!(self.tables.variances_of[def_id] <- v); } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 1189dcdfcbb..a02f9a9f796 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -749,7 +749,7 @@ rustc_queries! { separate_provide_extern } - /// Gets a map with the variance of every item; use `item_variance` instead. + /// Gets a map with the variance of every item; use `variances_of` instead. query crate_variances(_: ()) -> &'tcx ty::CrateVariancesMap<'tcx> { arena_cache desc { "computing the variances for items in this crate" } diff --git a/tests/ui/lazy-type-alias/variance.rs b/tests/ui/lazy-type-alias/variance.rs new file mode 100644 index 00000000000..f83215856b8 --- /dev/null +++ b/tests/ui/lazy-type-alias/variance.rs @@ -0,0 +1,38 @@ +// This is a regression test for issue #114221. +// Check that we compute variances for lazy type aliases. + +// check-pass + +#![feature(lazy_type_alias)] +#![allow(incomplete_features)] + +// [+] `A` is covariant over `'a`. +struct A<'a>(Co<'a>); + +// [+] `Co` is covariant over `'a`. +type Co<'a> = &'a (); + +fn co<'a>(x: A<'static>) { + let _: A<'a> = x; +} + +// [-] `B` is contravariant over `'a`. +struct B<'a>(Contra<'a>); + +// [-] `Contra` is contravariant over `'a`. +type Contra<'a> = fn(&'a ()); + +fn contra<'a>(x: B<'a>) { + let _: B<'static> = x; +} + +struct C(CoContra); + +// [+, -] `CoContra` is covariant over `T` and contravariant over `U`. +type CoContra = Option<(T, fn(U))>; + +fn co_contra<'a>(x: C<&'static (), &'a ()>) -> C<&'a (), &'static ()> { + x +} + +fn main() {} From 6f5d8556ca79473df091466306f69633877bdcd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Le=C3=B3n=20Orell=20Valerian=20Liehr?= Date: Thu, 3 Aug 2023 01:04:17 +0200 Subject: [PATCH 20/56] Handle inherent associated types fallout --- .../type/inherent_associated_type_bound.rs | 17 +++--- .../inherent_associated_type_projections.rs | 12 ++-- tests/rustdoc/inherent-projections.rs | 10 ++-- .../issue-111879-0.rs | 4 +- .../issue-111879-0.stderr | 6 +- .../late-bound-regions.rs | 4 +- .../late-bound-regions.stderr | 2 +- .../not-found-self-type-differs.alias.stderr | 16 ------ .../not-found-self-type-differs.local.stderr | 16 ------ .../not-found-self-type-differs.rs | 9 +-- .../not-found-self-type-differs.stderr | 29 ++++++++++ .../substitute-params-bad.rs | 2 +- .../substitute-params.rs | 3 +- .../type-alias-bounds-are-enforced.rs | 4 +- .../type-alias-bounds-are-enforced.stderr | 55 +++++++++++++++++++ 15 files changed, 117 insertions(+), 72 deletions(-) delete mode 100644 tests/ui/associated-inherent-types/not-found-self-type-differs.alias.stderr delete mode 100644 tests/ui/associated-inherent-types/not-found-self-type-differs.local.stderr create mode 100644 tests/ui/associated-inherent-types/not-found-self-type-differs.stderr create mode 100644 tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.stderr diff --git a/tests/rustdoc-json/type/inherent_associated_type_bound.rs b/tests/rustdoc-json/type/inherent_associated_type_bound.rs index 2e9b13d0cac..8e39f471824 100644 --- a/tests/rustdoc-json/type/inherent_associated_type_bound.rs +++ b/tests/rustdoc-json/type/inherent_associated_type_bound.rs @@ -5,14 +5,15 @@ // @set Carrier = '$.index[*][?(@.name=="Carrier")].id' pub struct Carrier<'a>(&'a ()); -// @is '$.index[*][?(@.name=="User")].inner.typedef.type.function_pointer.generic_params[*].name' \""'b"\" -// @is '$.index[*][?(@.name=="User")].inner.typedef.type.function_pointer.decl.inputs[0][1].qualified_path.self_type.resolved_path.id' $Carrier -// @is '$.index[*][?(@.name=="User")].inner.typedef.type.function_pointer.decl.inputs[0][1].qualified_path.self_type.resolved_path.args.angle_bracketed.args[0].lifetime' \""'b"\" -// @is '$.index[*][?(@.name=="User")].inner.typedef.type.function_pointer.decl.inputs[0][1].qualified_path.name' '"Focus"' -// @is '$.index[*][?(@.name=="User")].inner.typedef.type.function_pointer.decl.inputs[0][1].qualified_path.trait' null -// @is '$.index[*][?(@.name=="User")].inner.typedef.type.function_pointer.decl.inputs[0][1].qualified_path.args.angle_bracketed.args[0].type.primitive' '"i32"' - -pub type User = for<'b> fn(Carrier<'b>::Focus); +// @count "$.index[*][?(@.name=='user')].inner.function.decl.inputs[*]" 1 +// @is "$.index[*][?(@.name=='user')].inner.function.decl.inputs[0][0]" '"_"' +// @is '$.index[*][?(@.name=="user")].inner.function.decl.inputs[0][1].function_pointer.generic_params[*].name' \""'b"\" +// @is '$.index[*][?(@.name=="user")].inner.function.decl.inputs[0][1].function_pointer.decl.inputs[0][1].qualified_path.self_type.resolved_path.id' $Carrier +// @is '$.index[*][?(@.name=="user")].inner.function.decl.inputs[0][1].function_pointer.decl.inputs[0][1].qualified_path.self_type.resolved_path.args.angle_bracketed.args[0].lifetime' \""'b"\" +// @is '$.index[*][?(@.name=="user")].inner.function.decl.inputs[0][1].function_pointer.decl.inputs[0][1].qualified_path.name' '"Focus"' +// @is '$.index[*][?(@.name=="user")].inner.function.decl.inputs[0][1].function_pointer.decl.inputs[0][1].qualified_path.trait' null +// @is '$.index[*][?(@.name=="user")].inner.function.decl.inputs[0][1].function_pointer.decl.inputs[0][1].qualified_path.args.angle_bracketed.args[0].type.primitive' '"i32"' +pub fn user(_: for<'b> fn(Carrier<'b>::Focus)) {} impl<'a> Carrier<'a> { pub type Focus = &'a mut T; diff --git a/tests/rustdoc-json/type/inherent_associated_type_projections.rs b/tests/rustdoc-json/type/inherent_associated_type_projections.rs index 942e323efca..d12e5f4a784 100644 --- a/tests/rustdoc-json/type/inherent_associated_type_projections.rs +++ b/tests/rustdoc-json/type/inherent_associated_type_projections.rs @@ -5,11 +5,13 @@ // @set Parametrized = '$.index[*][?(@.name=="Parametrized")].id' pub struct Parametrized(T); -// @is '$.index[*][?(@.name=="Test")].inner.typedef.type.qualified_path.self_type.resolved_path.id' $Parametrized -// @is '$.index[*][?(@.name=="Test")].inner.typedef.type.qualified_path.self_type.resolved_path.args.angle_bracketed.args[0].type.primitive' \"i32\" -// @is '$.index[*][?(@.name=="Test")].inner.typedef.type.qualified_path.name' '"Proj"' -// @is '$.index[*][?(@.name=="Test")].inner.typedef.type.qualified_path.trait' null -pub type Test = Parametrized::Proj; +// @count "$.index[*][?(@.name=='test')].inner.function.decl.inputs[*]" 1 +// @is "$.index[*][?(@.name=='test')].inner.function.decl.inputs[0][0]" '"_"' +// @is '$.index[*][?(@.name=="test")].inner.function.decl.inputs[0][1].qualified_path.self_type.resolved_path.id' $Parametrized +// @is '$.index[*][?(@.name=="test")].inner.function.decl.inputs[0][1].qualified_path.self_type.resolved_path.args.angle_bracketed.args[0].type.primitive' \"i32\" +// @is '$.index[*][?(@.name=="test")].inner.function.decl.inputs[0][1].qualified_path.name' '"Proj"' +// @is '$.index[*][?(@.name=="test")].inner.function.decl.inputs[0][1].qualified_path.trait' null +pub fn test(_: Parametrized::Proj) {} /// param_bool impl Parametrized { diff --git a/tests/rustdoc/inherent-projections.rs b/tests/rustdoc/inherent-projections.rs index 9bda0acaf83..25f51282617 100644 --- a/tests/rustdoc/inherent-projections.rs +++ b/tests/rustdoc/inherent-projections.rs @@ -13,8 +13,8 @@ impl Owner { } // Make sure we handle bound vars correctly. -// @has 'inherent_projections/type.User.html' '//pre[@class="rust item-decl"]' "for<'a> fn(_: Carrier<'a>::Focus)" -pub type User = for<'a> fn(Carrier<'a>::Focus); +// @has 'inherent_projections/fn.user.html' '//pre[@class="rust item-decl"]' "user(_: for<'a> fn(_: Carrier<'a>::Focus))" +pub fn user(_: for<'a> fn(Carrier<'a>::Focus)) {} pub struct Carrier<'a>(&'a ()); @@ -27,11 +27,11 @@ impl<'a> Carrier<'a> { // FIXME(inherent_associated_types): Below we link to `Proj` but we should link to `Proj-1`. // The current test checks for the buggy behavior for demonstration purposes. -// @has 'inherent_projections/type.Test.html' -// @has - '//pre[@class="rust item-decl"]' "Parametrized" +// @has 'inherent_projections/fn.test.html' +// @has - '//pre[@class="rust item-decl"]' "test(_: Parametrized::Proj)" // @has - '//pre[@class="rust item-decl"]//a[@class="associatedtype"]/@href' 'struct.Parametrized.html#associatedtype.Proj' // @!has - '//pre[@class="rust item-decl"]//a[@class="associatedtype"]/@href' 'struct.Parametrized.html#associatedtype.Proj-1' -pub type Test = Parametrized::Proj; +pub fn test(_: Parametrized::Proj) {} pub struct Parametrized(T); diff --git a/tests/ui/associated-inherent-types/issue-111879-0.rs b/tests/ui/associated-inherent-types/issue-111879-0.rs index e37f7d34ab5..d01354465df 100644 --- a/tests/ui/associated-inherent-types/issue-111879-0.rs +++ b/tests/ui/associated-inherent-types/issue-111879-0.rs @@ -5,10 +5,8 @@ pub struct Carrier<'a>(&'a ()); -pub type User = for<'b> fn(Carrier<'b>::Focus); - impl<'a> Carrier<'a> { - pub type Focus = &'a mut User; //~ ERROR overflow evaluating associated type + pub type Focus = &'a mut for<'b> fn(Carrier<'b>::Focus); //~ ERROR overflow evaluating associated type } fn main() {} diff --git a/tests/ui/associated-inherent-types/issue-111879-0.stderr b/tests/ui/associated-inherent-types/issue-111879-0.stderr index 7bdbad44017..f6367c88aea 100644 --- a/tests/ui/associated-inherent-types/issue-111879-0.stderr +++ b/tests/ui/associated-inherent-types/issue-111879-0.stderr @@ -1,8 +1,8 @@ error: overflow evaluating associated type `Carrier<'b>::Focus` - --> $DIR/issue-111879-0.rs:11:25 + --> $DIR/issue-111879-0.rs:9:25 | -LL | pub type Focus = &'a mut User; - | ^^^^^^^^^^^^ +LL | pub type Focus = &'a mut for<'b> fn(Carrier<'b>::Focus); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/tests/ui/associated-inherent-types/late-bound-regions.rs b/tests/ui/associated-inherent-types/late-bound-regions.rs index 488a2cda649..1dbeb00d495 100644 --- a/tests/ui/associated-inherent-types/late-bound-regions.rs +++ b/tests/ui/associated-inherent-types/late-bound-regions.rs @@ -3,8 +3,6 @@ // Test if we correctly normalize `S<'a>::P` with respect to late-bound regions. -type Function = for<'a> fn(&'a i32) -> S<'a>::P; - struct S<'a>(&'a ()); trait Inter { @@ -16,7 +14,7 @@ impl<'a> S<'a> { } fn ret_ref_local<'e>() -> &'e i32 { - let f: Function = |x| x; + let f: for<'a> fn(&'a i32) -> S<'a>::P = |x| x; let local = 0; f(&local) //~ ERROR cannot return value referencing local variable `local` diff --git a/tests/ui/associated-inherent-types/late-bound-regions.stderr b/tests/ui/associated-inherent-types/late-bound-regions.stderr index 4706fcca91d..0dd17b05cd0 100644 --- a/tests/ui/associated-inherent-types/late-bound-regions.stderr +++ b/tests/ui/associated-inherent-types/late-bound-regions.stderr @@ -1,5 +1,5 @@ error[E0515]: cannot return value referencing local variable `local` - --> $DIR/late-bound-regions.rs:22:5 + --> $DIR/late-bound-regions.rs:20:5 | LL | f(&local) | ^^------^ diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs.alias.stderr b/tests/ui/associated-inherent-types/not-found-self-type-differs.alias.stderr deleted file mode 100644 index 4396435a6dd..00000000000 --- a/tests/ui/associated-inherent-types/not-found-self-type-differs.alias.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error[E0220]: associated type `Proj` not found for `Family>` in the current scope - --> $DIR/not-found-self-type-differs.rs:17:34 - | -LL | struct Family(T); - | ---------------- associated item `Proj` not found for this struct -... -LL | type Alias = Family>::Proj; - | ^^^^ associated item not found in `Family>` - | - = note: the associated type was found for - - `Family<()>` - - `Family>` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs.local.stderr b/tests/ui/associated-inherent-types/not-found-self-type-differs.local.stderr deleted file mode 100644 index d527db02217..00000000000 --- a/tests/ui/associated-inherent-types/not-found-self-type-differs.local.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error[E0220]: associated type `Proj` not found for `Family` in the current scope - --> $DIR/not-found-self-type-differs.rs:21:40 - | -LL | struct Family(T); - | ---------------- associated item `Proj` not found for this struct -... -LL | let _: Family::Proj = (); - | ^^^^ associated item not found in `Family` - | - = note: the associated type was found for - - `Family<()>` - - `Family>` - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs.rs b/tests/ui/associated-inherent-types/not-found-self-type-differs.rs index 93f58dcb6e6..76c5d4e7f69 100644 --- a/tests/ui/associated-inherent-types/not-found-self-type-differs.rs +++ b/tests/ui/associated-inherent-types/not-found-self-type-differs.rs @@ -1,5 +1,3 @@ -// revisions: local alias - #![feature(inherent_associated_types)] #![allow(incomplete_features)] @@ -13,10 +11,7 @@ impl Family> { type Proj = Self; } -#[cfg(alias)] -type Alias = Family>::Proj; //[alias]~ ERROR associated type `Proj` not found for `Family>` - fn main() { - #[cfg(local)] - let _: Family::Proj = (); //[local]~ ERROR associated type `Proj` not found for `Family` + let _: Family>::Proj; //~ ERROR associated type `Proj` not found for `Family>` + let _: Family::Proj = (); //~ ERROR associated type `Proj` not found for `Family` } diff --git a/tests/ui/associated-inherent-types/not-found-self-type-differs.stderr b/tests/ui/associated-inherent-types/not-found-self-type-differs.stderr new file mode 100644 index 00000000000..1871407c64f --- /dev/null +++ b/tests/ui/associated-inherent-types/not-found-self-type-differs.stderr @@ -0,0 +1,29 @@ +error[E0220]: associated type `Proj` not found for `Family>` in the current scope + --> $DIR/not-found-self-type-differs.rs:15:32 + | +LL | struct Family(T); + | ---------------- associated item `Proj` not found for this struct +... +LL | let _: Family>::Proj; + | ^^^^ associated item not found in `Family>` + | + = note: the associated type was found for + - `Family<()>` + - `Family>` + +error[E0220]: associated type `Proj` not found for `Family` in the current scope + --> $DIR/not-found-self-type-differs.rs:16:40 + | +LL | struct Family(T); + | ---------------- associated item `Proj` not found for this struct +... +LL | let _: Family::Proj = (); + | ^^^^ associated item not found in `Family` + | + = note: the associated type was found for + - `Family<()>` + - `Family>` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0220`. diff --git a/tests/ui/associated-inherent-types/substitute-params-bad.rs b/tests/ui/associated-inherent-types/substitute-params-bad.rs index 00eb1a14da4..a5d73c7b49f 100644 --- a/tests/ui/associated-inherent-types/substitute-params-bad.rs +++ b/tests/ui/associated-inherent-types/substitute-params-bad.rs @@ -17,7 +17,7 @@ impl Subj<(T, S)> { } fn main() { - type A = S<()>::P; + let _: S<()>::P; let _: Subj<(i32, i32)>::Un = 0i32; //~ ERROR mismatched types } diff --git a/tests/ui/associated-inherent-types/substitute-params.rs b/tests/ui/associated-inherent-types/substitute-params.rs index e94d6833159..631340b2b4d 100644 --- a/tests/ui/associated-inherent-types/substitute-params.rs +++ b/tests/ui/associated-inherent-types/substitute-params.rs @@ -15,8 +15,7 @@ impl S<(T,)> { fn main() { // Regression test for issue #104240. - type A = S<()>::P; - let _: A = (); + let _: S<()>::P = (); // Regression test for issue #107468. let _: S<(i32,)>::Un = 0i32; diff --git a/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs b/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs index b32b4288ac9..5c59f217be6 100644 --- a/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs +++ b/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.rs @@ -1,4 +1,5 @@ -// check-pass +// FIXME(inherent_associated_types): This should be `check-pass` +// known-bug: #108491 // compile-flags: --crate-type=lib #![feature(inherent_associated_types)] @@ -17,7 +18,6 @@ pub type Alias = (Source::Assoc,); - pub struct Source(T); pub trait Bound {} diff --git a/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.stderr b/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.stderr new file mode 100644 index 00000000000..5e18543fc90 --- /dev/null +++ b/tests/ui/associated-inherent-types/type-alias-bounds-are-enforced.stderr @@ -0,0 +1,55 @@ +error[E0391]: cycle detected when expanding type alias `Alias` + --> $DIR/type-alias-bounds-are-enforced.rs:19:1 + | +LL | pub type Alias = (Source::Assoc,); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: ...which requires computing the variances of `Source`... + --> $DIR/type-alias-bounds-are-enforced.rs:21:1 + | +LL | pub struct Source(T); + | ^^^^^^^^^^^^^^^^^^^^ + = note: ...which requires computing the variances for items in this crate... + = note: ...which again requires expanding type alias `Alias`, completing the cycle +note: cycle used when collecting item types in top-level module + --> $DIR/type-alias-bounds-are-enforced.rs:5:1 + | +LL | / #![feature(inherent_associated_types)] +LL | | #![allow(incomplete_features)] +LL | | +LL | | // Bounds on the self type play a major role in the resolution of inherent associated types (*). +... | +LL | | pub type Assoc = (); +LL | | } + | |_^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error[E0391]: cycle detected when expanding type alias `Alias` + --> $DIR/type-alias-bounds-are-enforced.rs:19:1 + | +LL | pub type Alias = (Source::Assoc,); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: ...which requires computing the variances of `Source`... + --> $DIR/type-alias-bounds-are-enforced.rs:21:1 + | +LL | pub struct Source(T); + | ^^^^^^^^^^^^^^^^^^^^ + = note: ...which requires computing the variances for items in this crate... + = note: ...which again requires expanding type alias `Alias`, completing the cycle +note: cycle used when collecting item types in top-level module + --> $DIR/type-alias-bounds-are-enforced.rs:5:1 + | +LL | / #![feature(inherent_associated_types)] +LL | | #![allow(incomplete_features)] +LL | | +LL | | // Bounds on the self type play a major role in the resolution of inherent associated types (*). +... | +LL | | pub type Assoc = (); +LL | | } + | |_^ + = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0391`. From a090b4548d9956c4ac4c826a8fea6f3891707629 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 3 Aug 2023 14:16:26 +0200 Subject: [PATCH 21/56] avoid more `ty::Binder:dummy` --- compiler/rustc_middle/src/ty/mod.rs | 13 +++++++++++++ .../src/solve/alias_relate.rs | 2 +- .../src/solve/assembly/mod.rs | 5 +---- .../src/solve/eval_ctxt.rs | 4 ++-- .../src/solve/eval_ctxt/select.rs | 2 +- .../src/solve/normalize.rs | 9 +++------ .../src/solve/project_goals.rs | 18 ++++++------------ .../src/solve/trait_goals.rs | 12 ++---------- 8 files changed, 29 insertions(+), 36 deletions(-) diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 6b0f0132062..48aa25dba6d 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1338,12 +1338,25 @@ impl<'tcx> ToPredicate<'tcx> for PolyTypeOutlivesPredicate<'tcx> { } } +impl<'tcx> ToPredicate<'tcx> for ProjectionPredicate<'tcx> { + fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { + ty::Binder::dummy(PredicateKind::Clause(ClauseKind::Projection(self))).to_predicate(tcx) + } +} + impl<'tcx> ToPredicate<'tcx> for PolyProjectionPredicate<'tcx> { fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Predicate<'tcx> { self.map_bound(|p| PredicateKind::Clause(ClauseKind::Projection(p))).to_predicate(tcx) } } +impl<'tcx> ToPredicate<'tcx, Clause<'tcx>> for ProjectionPredicate<'tcx> { + fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Clause<'tcx> { + let p: Predicate<'tcx> = self.to_predicate(tcx); + p.expect_clause() + } +} + impl<'tcx> ToPredicate<'tcx, Clause<'tcx>> for PolyProjectionPredicate<'tcx> { fn to_predicate(self, tcx: TyCtxt<'tcx>) -> Clause<'tcx> { let p: Predicate<'tcx> = self.to_predicate(tcx); diff --git a/compiler/rustc_trait_selection/src/solve/alias_relate.rs b/compiler/rustc_trait_selection/src/solve/alias_relate.rs index 1b4af95cb8a..6b839d64b87 100644 --- a/compiler/rustc_trait_selection/src/solve/alias_relate.rs +++ b/compiler/rustc_trait_selection/src/solve/alias_relate.rs @@ -162,7 +162,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.add_goal(Goal::new( self.tcx(), param_env, - ty::Binder::dummy(ty::ProjectionPredicate { projection_ty: alias, term: other }), + ty::ProjectionPredicate { projection_ty: alias, term: other }, )); Ok(()) diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 8bf003f863e..e57188edc3d 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -413,10 +413,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let normalized_ty = ecx.next_ty_infer(); let normalizes_to_goal = goal.with( tcx, - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty, - term: normalized_ty.into(), - }), + ty::ProjectionPredicate { projection_ty, term: normalized_ty.into() }, ); ecx.add_goal(normalizes_to_goal); let _ = ecx.try_evaluate_added_goals().inspect_err(|_| { diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index 485bc912c44..c31559b14d7 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -487,10 +487,10 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { let unconstrained_rhs = this.next_term_infer_of_kind(goal.predicate.term); let unconstrained_goal = goal.with( this.tcx(), - ty::Binder::dummy(ty::ProjectionPredicate { + ty::ProjectionPredicate { projection_ty: goal.predicate.projection_ty, term: unconstrained_rhs, - }), + }, ); let (_, certainty, instantiate_goals) = diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs index 8a3c7b22e32..2b46e5b8b01 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -269,7 +269,7 @@ fn rematch_unsize<'tcx>( infcx.tcx, ObligationCause::dummy(), goal.param_env, - ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region)), + ty::OutlivesPredicate(a_ty, region), )); Ok(Some(ImplSource::Builtin(source, nested))) diff --git a/compiler/rustc_trait_selection/src/solve/normalize.rs b/compiler/rustc_trait_selection/src/solve/normalize.rs index 091b7f33834..872f0c87916 100644 --- a/compiler/rustc_trait_selection/src/solve/normalize.rs +++ b/compiler/rustc_trait_selection/src/solve/normalize.rs @@ -75,10 +75,7 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> { tcx, self.at.cause.clone(), self.at.param_env, - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty: alias, - term: new_infer_ty.into(), - }), + ty::ProjectionPredicate { projection_ty: alias, term: new_infer_ty.into() }, ); // Do not emit an error if normalization is known to fail but instead @@ -131,10 +128,10 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> { tcx, self.at.cause.clone(), self.at.param_env, - ty::Binder::dummy(ty::ProjectionPredicate { + ty::ProjectionPredicate { projection_ty: tcx.mk_alias_ty(uv.def, uv.args), term: new_infer_ct.into(), - }), + }, ); let result = if infcx.predicate_may_hold(&obligation) { diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index 2ce57751740..75cf33d8194 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -393,10 +393,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { None => tcx.types.unit, Some(field_def) => { let self_ty = field_def.ty(tcx, args); - ecx.add_goal(goal.with( - tcx, - ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)), - )); + ecx.add_goal(goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty))); return ecx .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); } @@ -406,10 +403,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { ty::Tuple(elements) => match elements.last() { None => tcx.types.unit, Some(&self_ty) => { - ecx.add_goal(goal.with( - tcx, - ty::Binder::dummy(goal.predicate.with_self_ty(tcx, self_ty)), - )); + ecx.add_goal(goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty))); return ecx .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); } @@ -450,10 +444,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { Self::consider_implied_clause( ecx, goal, - ty::Binder::dummy(ty::ProjectionPredicate { + ty::ProjectionPredicate { projection_ty: ecx.tcx().mk_alias_ty(goal.predicate.def_id(), [self_ty]), term, - }) + } .to_predicate(tcx), // Technically, we need to check that the future type is Sized, // but that's already proven by the generator being WF. @@ -490,12 +484,12 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> { Self::consider_implied_clause( ecx, goal, - ty::Binder::dummy(ty::ProjectionPredicate { + ty::ProjectionPredicate { projection_ty: ecx .tcx() .mk_alias_ty(goal.predicate.def_id(), [self_ty, generator.resume_ty()]), term, - }) + } .to_predicate(tcx), // Technically, we need to check that the future type is Sized, // but that's already proven by the generator being WF. diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 14a5b9656e5..6e552633246 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -857,12 +857,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ecx.add_goals( constituent_tys(ecx, goal.predicate.self_ty())? .into_iter() - .map(|ty| { - goal.with( - ecx.tcx(), - ty::Binder::dummy(goal.predicate.with_self_ty(ecx.tcx(), ty)), - ) - }) + .map(|ty| goal.with(ecx.tcx(), goal.predicate.with_self_ty(ecx.tcx(), ty))) .collect::>(), ); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) @@ -905,10 +900,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let normalizes_to_goal = Goal::new( ecx.tcx(), param_env, - ty::Binder::dummy(ty::ProjectionPredicate { - projection_ty, - term: normalized_ty.into(), - }), + ty::ProjectionPredicate { projection_ty, term: normalized_ty.into() }, ); ecx.add_goal(normalizes_to_goal); if let Err(err) = ecx.try_evaluate_added_goals() { From ae3c353067f294d34ec61e674c7f5a9d58868458 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 3 Aug 2023 14:30:13 +0200 Subject: [PATCH 22/56] fix `make_ambiguous_response_no_constraints` we previously had incorrect universes in the query response. --- .../src/solve/eval_ctxt.rs | 21 ++++++------- .../src/solve/eval_ctxt/canonical.rs | 30 ++++--------------- .../src/solve/eval_ctxt/probe.rs | 1 + .../rustc_trait_selection/src/solve/mod.rs | 20 +++++++------ .../src/solve/search_graph/mod.rs | 12 ++++++-- .../src/solve/search_graph/overflow.rs | 4 +-- 6 files changed, 41 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index c31559b14d7..5a2c06826bf 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -3,11 +3,11 @@ use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::canonical::CanonicalVarValues; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc_infer::infer::{ - DefineOpaqueTypes, InferCtxt, InferOk, LateBoundRegionConversionTime, RegionVariableOrigin, - TyCtxtInferExt, + DefineOpaqueTypes, InferCtxt, InferOk, LateBoundRegionConversionTime, TyCtxtInferExt, }; use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::ObligationCause; +use rustc_middle::infer::canonical::CanonicalVarInfos; use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc_middle::traits::solve::inspect; use rustc_middle::traits::solve::{ @@ -55,6 +55,9 @@ pub struct EvalCtxt<'a, 'tcx> { /// the job already. infcx: &'a InferCtxt<'tcx>, + /// The variable info for the `var_values`, only used to make an ambiguous response + /// with no constraints. + variables: CanonicalVarInfos<'tcx>, pub(super) var_values: CanonicalVarValues<'tcx>, predefined_opaques_in_body: PredefinedOpaques<'tcx>, @@ -184,18 +187,19 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { let mut ecx = EvalCtxt { search_graph: &mut search_graph, - infcx: infcx, + infcx, + nested_goals: NestedGoals::new(), + inspect: ProofTreeBuilder::new_maybe_root(infcx.tcx, generate_proof_tree), + // Only relevant when canonicalizing the response, // which we don't do within this evaluation context. predefined_opaques_in_body: infcx .tcx .mk_predefined_opaques_in_body(PredefinedOpaquesData::default()), - // Only relevant when canonicalizing the response. max_input_universe: ty::UniverseIndex::ROOT, + variables: ty::List::empty(), var_values: CanonicalVarValues::dummy(), - nested_goals: NestedGoals::new(), tainted: Ok(()), - inspect: ProofTreeBuilder::new_maybe_root(infcx.tcx, generate_proof_tree), }; let result = f(&mut ecx); @@ -245,6 +249,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { let mut ecx = EvalCtxt { infcx, + variables: canonical_input.variables, var_values, predefined_opaques_in_body: input.predefined_opaques_in_body, max_input_universe: canonical_input.max_universe, @@ -593,10 +598,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { }) } - pub(super) fn next_region_infer(&self) -> ty::Region<'tcx> { - self.infcx.next_region_var(RegionVariableOrigin::MiscVariable(DUMMY_SP)) - } - pub(super) fn next_const_infer(&self, ty: Ty<'tcx>) -> ty::Const<'tcx> { self.infcx.next_const_var( ty, diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index 7323b98b8ce..5e93e58b35c 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -10,7 +10,7 @@ //! [c]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html use super::{CanonicalInput, Certainty, EvalCtxt, Goal}; use crate::solve::canonicalize::{CanonicalizeMode, Canonicalizer}; -use crate::solve::{CanonicalResponse, QueryResult, Response}; +use crate::solve::{response_no_constraints_raw, CanonicalResponse, QueryResult, Response}; use rustc_data_structures::fx::FxHashSet; use rustc_index::IndexVec; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; @@ -109,29 +109,11 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { &self, maybe_cause: MaybeCause, ) -> CanonicalResponse<'tcx> { - let unconstrained_response = Response { - var_values: CanonicalVarValues { - var_values: self.tcx().mk_args_from_iter(self.var_values.var_values.iter().map( - |arg| -> ty::GenericArg<'tcx> { - match arg.unpack() { - GenericArgKind::Lifetime(_) => self.next_region_infer().into(), - GenericArgKind::Type(_) => self.next_ty_infer().into(), - GenericArgKind::Const(ct) => self.next_const_infer(ct.ty()).into(), - } - }, - )), - }, - external_constraints: self - .tcx() - .mk_external_constraints(ExternalConstraintsData::default()), - certainty: Certainty::Maybe(maybe_cause), - }; - - Canonicalizer::canonicalize( - self.infcx, - CanonicalizeMode::Response { max_input_universe: self.max_input_universe }, - &mut Default::default(), - unconstrained_response, + response_no_constraints_raw( + self.tcx(), + self.max_input_universe, + self.variables, + Certainty::Maybe(maybe_cause), ) } diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs index 4477ea7d501..317c43baf8f 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/probe.rs @@ -17,6 +17,7 @@ where let mut nested_ecx = EvalCtxt { infcx: outer_ecx.infcx, + variables: outer_ecx.variables, var_values: outer_ecx.var_values, predefined_opaques_in_body: outer_ecx.predefined_opaques_in_body, max_input_universe: outer_ecx.max_input_universe, diff --git a/compiler/rustc_trait_selection/src/solve/mod.rs b/compiler/rustc_trait_selection/src/solve/mod.rs index 63e48c94a86..63d4a38119f 100644 --- a/compiler/rustc_trait_selection/src/solve/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/mod.rs @@ -17,10 +17,11 @@ use rustc_hir::def_id::DefId; use rustc_infer::infer::canonical::{Canonical, CanonicalVarValues}; use rustc_infer::traits::query::NoSolution; +use rustc_middle::infer::canonical::CanonicalVarInfos; use rustc_middle::traits::solve::{ CanonicalResponse, Certainty, ExternalConstraintsData, Goal, QueryResult, Response, }; -use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{self, Ty, TyCtxt, UniverseIndex}; use rustc_middle::ty::{ CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, TypeOutlivesPredicate, }; @@ -284,20 +285,21 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } } -pub(super) fn response_no_constraints<'tcx>( +fn response_no_constraints_raw<'tcx>( tcx: TyCtxt<'tcx>, - goal: Canonical<'tcx, impl Sized>, + max_universe: UniverseIndex, + variables: CanonicalVarInfos<'tcx>, certainty: Certainty, -) -> QueryResult<'tcx> { - Ok(Canonical { - max_universe: goal.max_universe, - variables: goal.variables, +) -> CanonicalResponse<'tcx> { + Canonical { + max_universe, + variables, value: Response { - var_values: CanonicalVarValues::make_identity(tcx, goal.variables), + var_values: CanonicalVarValues::make_identity(tcx, variables), // FIXME: maybe we should store the "no response" version in tcx, like // we do for tcx.types and stuff. external_constraints: tcx.mk_external_constraints(ExternalConstraintsData::default()), certainty, }, - }) + } } diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs index c25d6eca918..b70ec48ee2f 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs @@ -107,7 +107,7 @@ impl<'tcx> SearchGraph<'tcx> { } let depth = self.stack.push(StackElem { input, has_been_used: false }); - let response = super::response_no_constraints(tcx, input, Certainty::Yes); + let response = Self::response_no_constraints(tcx, input, Certainty::Yes); let entry_index = cache.entries.push(ProvisionalEntry { response, depth, input }); v.insert(entry_index); Ok(()) @@ -144,7 +144,7 @@ impl<'tcx> SearchGraph<'tcx> { { Err(cache.provisional_result(entry_index)) } else { - Err(super::response_no_constraints(tcx, input, Certainty::OVERFLOW)) + Err(Self::response_no_constraints(tcx, input, Certainty::OVERFLOW)) } } } @@ -283,4 +283,12 @@ impl<'tcx> SearchGraph<'tcx> { result } + + fn response_no_constraints( + tcx: TyCtxt<'tcx>, + goal: CanonicalInput<'tcx>, + certainty: Certainty, + ) -> QueryResult<'tcx> { + Ok(super::response_no_constraints_raw(tcx, goal.max_universe, goal.variables, certainty)) + } } diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs b/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs index 83a051eeb1a..f54563557d7 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs @@ -5,7 +5,7 @@ use rustc_middle::ty::TyCtxt; use rustc_session::Limit; use super::SearchGraph; -use crate::solve::{response_no_constraints, EvalCtxt}; +use crate::solve::{response_no_constraints_raw, EvalCtxt}; /// When detecting a solver overflow, we return ambiguity. Overflow can be /// *hidden* by either a fatal error in an **AND** or a trivial success in an **OR**. @@ -115,6 +115,6 @@ impl<'tcx> SearchGraph<'tcx> { goal: Canonical<'tcx, impl Sized>, ) -> QueryResult<'tcx> { self.overflow_data.deal_with_overflow(); - response_no_constraints(tcx, goal, Certainty::OVERFLOW) + Ok(response_no_constraints_raw(tcx, goal.max_universe, goal.variables, Certainty::OVERFLOW)) } } From c0468313cb38492e3f7a2323980b6e7c622f9111 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 3 Aug 2023 14:32:56 +0200 Subject: [PATCH 23/56] add `ensure_sufficient_stack` to the new solver --- .../src/solve/eval_ctxt.rs | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index 5a2c06826bf..7283334aab2 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -1,3 +1,4 @@ +use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_infer::infer::at::ToTrace; use rustc_infer::infer::canonical::CanonicalVarValues; @@ -305,24 +306,26 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { // Deal with overflow, caching, and coinduction. // // The actual solver logic happens in `ecx.compute_goal`. - search_graph.with_new_goal( - tcx, - canonical_input, - goal_evaluation, - |search_graph, goal_evaluation| { - EvalCtxt::enter_canonical( - tcx, - search_graph, - canonical_input, - goal_evaluation, - |ecx, goal| { - let result = ecx.compute_goal(goal); - ecx.inspect.query_result(result); - result - }, - ) - }, - ) + ensure_sufficient_stack(|| { + search_graph.with_new_goal( + tcx, + canonical_input, + goal_evaluation, + |search_graph, goal_evaluation| { + EvalCtxt::enter_canonical( + tcx, + search_graph, + canonical_input, + goal_evaluation, + |ecx, goal| { + let result = ecx.compute_goal(goal); + ecx.inspect.query_result(result); + result + }, + ) + }, + ) + }) } /// Recursively evaluates `goal`, returning whether any inference vars have From a745cbb042c07115199e5ba725067f745157fc4a Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 3 Aug 2023 14:41:34 +0200 Subject: [PATCH 24/56] handle overflow in the `EvalCtxt` separately --- .../src/solve/assembly/mod.rs | 74 +++---- .../src/solve/eval_ctxt.rs | 193 +++++++++--------- .../src/solve/eval_ctxt/select.rs | 3 +- .../src/solve/search_graph/mod.rs | 6 + .../src/solve/search_graph/overflow.rs | 29 +-- .../src/solve/trait_goals.rs | 40 ++-- 6 files changed, 156 insertions(+), 189 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index e57188edc3d..267f345c062 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -1,6 +1,5 @@ //! Code shared by trait and projection goals for candidate assembly. -use super::search_graph::OverflowHandler; use super::{EvalCtxt, SolverMode}; use crate::traits::coherence; use rustc_hir::def_id::DefId; @@ -315,7 +314,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { return ambig; } - let mut candidates = self.assemble_candidates_via_self_ty(goal); + let mut candidates = self.assemble_candidates_via_self_ty(goal, 0); self.assemble_blanket_impl_candidates(goal, &mut candidates); @@ -351,6 +350,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { fn assemble_candidates_via_self_ty>( &mut self, goal: Goal<'tcx, G>, + num_steps: usize, ) -> Vec> { debug_assert_eq!(goal, self.resolve_vars_if_possible(goal)); if let Some(ambig) = self.assemble_self_ty_infer_ambiguity_response(goal) { @@ -369,7 +369,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.assemble_coherence_unknowable_candidates(goal, &mut candidates); - self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates); + self.assemble_candidates_after_normalizing_self_ty(goal, &mut candidates, num_steps); candidates } @@ -393,46 +393,40 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { &mut self, goal: Goal<'tcx, G>, candidates: &mut Vec>, + num_steps: usize, ) { let tcx = self.tcx(); let &ty::Alias(_, projection_ty) = goal.predicate.self_ty().kind() else { return }; - let normalized_self_candidates: Result<_, NoSolution> = - self.probe(|_| CandidateKind::NormalizedSelfTyAssembly).enter(|ecx| { - ecx.with_incremented_depth( - |ecx| { - let result = ecx.evaluate_added_goals_and_make_canonical_response( - Certainty::OVERFLOW, - )?; - Ok(vec![Candidate { - source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), - result, - }]) - }, - |ecx| { - let normalized_ty = ecx.next_ty_infer(); - let normalizes_to_goal = goal.with( - tcx, - ty::ProjectionPredicate { projection_ty, term: normalized_ty.into() }, - ); - ecx.add_goal(normalizes_to_goal); - let _ = ecx.try_evaluate_added_goals().inspect_err(|_| { - debug!("self type normalization failed"); - })?; - let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty); - debug!(?normalized_ty, "self type normalized"); - // NOTE: Alternatively we could call `evaluate_goal` here and only - // have a `Normalized` candidate. This doesn't work as long as we - // use `CandidateSource` in winnowing. - let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty)); - Ok(ecx.assemble_candidates_via_self_ty(goal)) - }, - ) - }); - - if let Ok(normalized_self_candidates) = normalized_self_candidates { - candidates.extend(normalized_self_candidates); - } + candidates.extend(self.probe(|_| CandidateKind::NormalizedSelfTyAssembly).enter(|ecx| { + if num_steps < ecx.local_overflow_limit() { + let normalized_ty = ecx.next_ty_infer(); + let normalizes_to_goal = goal.with( + tcx, + ty::ProjectionPredicate { projection_ty, term: normalized_ty.into() }, + ); + ecx.add_goal(normalizes_to_goal); + if let Err(NoSolution) = ecx.try_evaluate_added_goals() { + debug!("self type normalization failed"); + return vec![]; + } + let normalized_ty = ecx.resolve_vars_if_possible(normalized_ty); + debug!(?normalized_ty, "self type normalized"); + // NOTE: Alternatively we could call `evaluate_goal` here and only + // have a `Normalized` candidate. This doesn't work as long as we + // use `CandidateSource` in winnowing. + let goal = goal.with(tcx, goal.predicate.with_self_ty(tcx, normalized_ty)); + ecx.assemble_candidates_via_self_ty(goal, num_steps + 1) + } else { + match ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW) { + Ok(result) => vec![Candidate { + source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc), + result, + }], + Err(NoSolution) => vec![], + } + } + })); } #[instrument(level = "debug", skip_all)] @@ -530,7 +524,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { ty::Alias(_, _) | ty::Placeholder(..) | ty::Error(_) => (), // FIXME: These should ideally not exist as a self type. It would be nice for - // the builtin auto trait impls of generators should instead directly recurse + // the builtin auto trait impls of generators to instead directly recurse // into the witness. ty::GeneratorWitness(_) | ty::GeneratorWitnessMIR(_, _) => (), diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index 7283334aab2..8a20552d339 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -15,7 +15,7 @@ use rustc_middle::traits::solve::{ CanonicalInput, CanonicalResponse, Certainty, IsNormalizesToHack, PredefinedOpaques, PredefinedOpaquesData, QueryResult, }; -use rustc_middle::traits::DefiningAnchor; +use rustc_middle::traits::{specialization_graph, DefiningAnchor}; use rustc_middle::ty::{ self, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, @@ -25,11 +25,10 @@ use rustc_span::DUMMY_SP; use std::io::Write; use std::ops::ControlFlow; -use crate::traits::specialization_graph; use crate::traits::vtable::{count_own_vtable_entries, prepare_vtable_segments, VtblSegment}; use super::inspect::ProofTreeBuilder; -use super::search_graph::{self, OverflowHandler}; +use super::search_graph; use super::SolverMode; use super::{search_graph::SearchGraph, Goal}; pub use select::InferCtxtSelectExt; @@ -175,6 +174,10 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { self.search_graph.solver_mode() } + pub(super) fn local_overflow_limit(&self) -> usize { + self.search_graph.local_overflow_limit() + } + /// Creates a root evaluation context and search graph. This should only be /// used from outside of any evaluation, and other methods should be preferred /// over using this manually (such as [`InferCtxtEvalExt::evaluate_root_goal`]). @@ -479,101 +482,22 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { let inspect = self.inspect.new_evaluate_added_goals(); let inspect = core::mem::replace(&mut self.inspect, inspect); - let mut goals = core::mem::replace(&mut self.nested_goals, NestedGoals::new()); - let mut new_goals = NestedGoals::new(); - - let response = self.repeat_while_none( - |_| Ok(Certainty::OVERFLOW), - |this| { - this.inspect.evaluate_added_goals_loop_start(); - - let mut has_changed = Err(Certainty::Yes); - - if let Some(goal) = goals.normalizes_to_hack_goal.take() { - // Replace the goal with an unconstrained infer var, so the - // RHS does not affect projection candidate assembly. - let unconstrained_rhs = this.next_term_infer_of_kind(goal.predicate.term); - let unconstrained_goal = goal.with( - this.tcx(), - ty::ProjectionPredicate { - projection_ty: goal.predicate.projection_ty, - term: unconstrained_rhs, - }, - ); - - let (_, certainty, instantiate_goals) = - match this.evaluate_goal(IsNormalizesToHack::Yes, unconstrained_goal) { - Ok(r) => r, - Err(NoSolution) => return Some(Err(NoSolution)), - }; - new_goals.goals.extend(instantiate_goals); - - // Finally, equate the goal's RHS with the unconstrained var. - // We put the nested goals from this into goals instead of - // next_goals to avoid needing to process the loop one extra - // time if this goal returns something -- I don't think this - // matters in practice, though. - match this.eq_and_get_goals( - goal.param_env, - goal.predicate.term, - unconstrained_rhs, - ) { - Ok(eq_goals) => { - goals.goals.extend(eq_goals); - } - Err(NoSolution) => return Some(Err(NoSolution)), - }; - - // We only look at the `projection_ty` part here rather than - // looking at the "has changed" return from evaluate_goal, - // because we expect the `unconstrained_rhs` part of the predicate - // to have changed -- that means we actually normalized successfully! - if goal.predicate.projection_ty - != this.resolve_vars_if_possible(goal.predicate.projection_ty) - { - has_changed = Ok(()) - } - - match certainty { - Certainty::Yes => {} - Certainty::Maybe(_) => { - // We need to resolve vars here so that we correctly - // deal with `has_changed` in the next iteration. - new_goals.normalizes_to_hack_goal = - Some(this.resolve_vars_if_possible(goal)); - has_changed = has_changed.map_err(|c| c.unify_with(certainty)); - } - } + let mut response = Ok(Certainty::OVERFLOW); + for _ in 0..self.local_overflow_limit() { + // FIXME: This match is a bit ugly, it might be nice to change the inspect + // stuff to use a closure instead. which should hopefully simplify this a bit. + match self.evaluate_added_goals_step() { + Ok(Some(cert)) => { + response = Ok(cert); + break; } - - for goal in goals.goals.drain(..) { - let (changed, certainty, instantiate_goals) = - match this.evaluate_goal(IsNormalizesToHack::No, goal) { - Ok(result) => result, - Err(NoSolution) => return Some(Err(NoSolution)), - }; - new_goals.goals.extend(instantiate_goals); - - if changed { - has_changed = Ok(()); - } - - match certainty { - Certainty::Yes => {} - Certainty::Maybe(_) => { - new_goals.goals.push(goal); - has_changed = has_changed.map_err(|c| c.unify_with(certainty)); - } - } + Ok(None) => {} + Err(NoSolution) => { + response = Err(NoSolution); + break; } - - core::mem::swap(&mut new_goals, &mut goals); - match has_changed { - Ok(()) => None, - Err(certainty) => Some(Ok(certainty)), - } - }, - ); + } + } self.inspect.eval_added_goals_result(response); @@ -584,9 +508,84 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { let goal_evaluations = std::mem::replace(&mut self.inspect, inspect); self.inspect.added_goals_evaluation(goal_evaluations); - self.nested_goals = goals; response } + + /// Iterate over all added goals: returning `Ok(Some(_))` in case we can stop rerunning. + /// + /// Goals for the next step get directly added the the nested goals of the `EvalCtxt`. + fn evaluate_added_goals_step(&mut self) -> Result, NoSolution> { + let tcx = self.tcx(); + let mut goals = core::mem::replace(&mut self.nested_goals, NestedGoals::new()); + + self.inspect.evaluate_added_goals_loop_start(); + // If this loop did not result in any progress, what's our final certainty. + let mut unchanged_certainty = Some(Certainty::Yes); + if let Some(goal) = goals.normalizes_to_hack_goal.take() { + // Replace the goal with an unconstrained infer var, so the + // RHS does not affect projection candidate assembly. + let unconstrained_rhs = self.next_term_infer_of_kind(goal.predicate.term); + let unconstrained_goal = goal.with( + tcx, + ty::ProjectionPredicate { + projection_ty: goal.predicate.projection_ty, + term: unconstrained_rhs, + }, + ); + + let (_, certainty, instantiate_goals) = + self.evaluate_goal(IsNormalizesToHack::Yes, unconstrained_goal)?; + self.add_goals(instantiate_goals); + + // Finally, equate the goal's RHS with the unconstrained var. + // We put the nested goals from this into goals instead of + // next_goals to avoid needing to process the loop one extra + // time if this goal returns something -- I don't think this + // matters in practice, though. + let eq_goals = + self.eq_and_get_goals(goal.param_env, goal.predicate.term, unconstrained_rhs)?; + goals.goals.extend(eq_goals); + + // We only look at the `projection_ty` part here rather than + // looking at the "has changed" return from evaluate_goal, + // because we expect the `unconstrained_rhs` part of the predicate + // to have changed -- that means we actually normalized successfully! + if goal.predicate.projection_ty + != self.resolve_vars_if_possible(goal.predicate.projection_ty) + { + unchanged_certainty = None; + } + + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => { + // We need to resolve vars here so that we correctly + // deal with `has_changed` in the next iteration. + self.set_normalizes_to_hack_goal(self.resolve_vars_if_possible(goal)); + unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty)); + } + } + } + + for goal in goals.goals.drain(..) { + let (has_changed, certainty, instantiate_goals) = + self.evaluate_goal(IsNormalizesToHack::No, goal)?; + self.add_goals(instantiate_goals); + if has_changed { + unchanged_certainty = None; + } + + match certainty { + Certainty::Yes => {} + Certainty::Maybe(_) => { + self.add_goal(goal); + unchanged_certainty = unchanged_certainty.map(|c| c.unify_with(certainty)); + } + } + } + + Ok(unchanged_certainty) + } } impl<'tcx> EvalCtxt<'_, 'tcx> { diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs index 2b46e5b8b01..ca4a4c9510c 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/select.rs @@ -14,7 +14,6 @@ use rustc_span::DUMMY_SP; use crate::solve::assembly::{Candidate, CandidateSource}; use crate::solve::eval_ctxt::{EvalCtxt, GenerateProofTree}; use crate::solve::inspect::ProofTreeBuilder; -use crate::solve::search_graph::OverflowHandler; use crate::traits::StructurallyNormalizeExt; use crate::traits::TraitEngineExt; @@ -143,7 +142,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // the cycle anyways one step later. EvalCtxt::enter_canonical( self.tcx(), - self.search_graph(), + self.search_graph, canonical_input, // FIXME: This is wrong, idk if we even want to track stuff here. &mut ProofTreeBuilder::new_noop(), diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs index b70ec48ee2f..f84445e8820 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs @@ -27,6 +27,7 @@ struct StackElem<'tcx> { pub(super) struct SearchGraph<'tcx> { mode: SolverMode, + local_overflow_limit: usize, /// The stack of goals currently being computed. /// /// An element is *deeper* in the stack if its index is *lower*. @@ -39,6 +40,7 @@ impl<'tcx> SearchGraph<'tcx> { pub(super) fn new(tcx: TyCtxt<'tcx>, mode: SolverMode) -> SearchGraph<'tcx> { Self { mode, + local_overflow_limit: tcx.recursion_limit().0.ilog2() as usize, stack: Default::default(), overflow_data: OverflowData::new(tcx), provisional_cache: ProvisionalCache::empty(), @@ -49,6 +51,10 @@ impl<'tcx> SearchGraph<'tcx> { self.mode } + pub(super) fn local_overflow_limit(&self) -> usize { + self.local_overflow_limit + } + /// We do not use the global cache during coherence. /// /// The trait solver behavior is different for coherence diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs b/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs index f54563557d7..4abde2c96d4 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs @@ -5,7 +5,7 @@ use rustc_middle::ty::TyCtxt; use rustc_session::Limit; use super::SearchGraph; -use crate::solve::{response_no_constraints_raw, EvalCtxt}; +use crate::solve::response_no_constraints_raw; /// When detecting a solver overflow, we return ambiguity. Overflow can be /// *hidden* by either a fatal error in an **AND** or a trivial success in an **OR**. @@ -73,33 +73,6 @@ pub(in crate::solve) trait OverflowHandler<'tcx> { self.search_graph().overflow_data.deal_with_overflow(); on_overflow(self) } - - // Increment the `additional_depth` by one and evaluate `body`, or `on_overflow` - // if the depth is overflown. - fn with_incremented_depth( - &mut self, - on_overflow: impl FnOnce(&mut Self) -> T, - body: impl FnOnce(&mut Self) -> T, - ) -> T { - let depth = self.search_graph().stack.len(); - self.search_graph().overflow_data.additional_depth += 1; - - let result = if self.search_graph().overflow_data.has_overflow(depth) { - self.search_graph().overflow_data.deal_with_overflow(); - on_overflow(self) - } else { - body(self) - }; - - self.search_graph().overflow_data.additional_depth -= 1; - result - } -} - -impl<'tcx> OverflowHandler<'tcx> for EvalCtxt<'_, 'tcx> { - fn search_graph(&mut self) -> &mut SearchGraph<'tcx> { - &mut self.search_graph - } } impl<'tcx> OverflowHandler<'tcx> for SearchGraph<'tcx> { diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 6e552633246..6a96ea38555 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -1,7 +1,6 @@ //! Dealing with trait goals, i.e. `T: Trait<'a, U>`. use super::assembly::{self, structural_traits}; -use super::search_graph::OverflowHandler; use super::{EvalCtxt, SolverMode}; use rustc_hir::def_id::DefId; use rustc_hir::{LangItem, Movability}; @@ -874,7 +873,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { } /// Normalize a non-self type when it is structually matched on when solving - /// a built-in goal. This is handled already through `assemble_candidates_after_normalizing_self_ty` + /// a built-in goal. + /// + /// This is handled already through `assemble_candidates_after_normalizing_self_ty` /// for the self type, but for other goals, additional normalization of other /// arguments may be needed to completely implement the semantics of the trait. /// @@ -889,27 +890,22 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { return Ok(Some(ty)); } - self.repeat_while_none( - |_| Ok(None), - |ecx| { - let ty::Alias(_, projection_ty) = *ty.kind() else { - return Some(Ok(Some(ty))); - }; + for _ in 0..self.local_overflow_limit() { + let ty::Alias(_, projection_ty) = *ty.kind() else { + return Ok(Some(ty)); + }; - let normalized_ty = ecx.next_ty_infer(); - let normalizes_to_goal = Goal::new( - ecx.tcx(), - param_env, - ty::ProjectionPredicate { projection_ty, term: normalized_ty.into() }, - ); - ecx.add_goal(normalizes_to_goal); - if let Err(err) = ecx.try_evaluate_added_goals() { - return Some(Err(err)); - } + let normalized_ty = self.next_ty_infer(); + let normalizes_to_goal = Goal::new( + self.tcx(), + param_env, + ty::ProjectionPredicate { projection_ty, term: normalized_ty.into() }, + ); + self.add_goal(normalizes_to_goal); + self.try_evaluate_added_goals()?; + ty = self.resolve_vars_if_possible(normalized_ty); + } - ty = ecx.resolve_vars_if_possible(normalized_ty); - None - }, - ) + Ok(None) } } From 383b715163da1aedcf170b7de17a0b6ddc527acd Mon Sep 17 00:00:00 2001 From: klensy Date: Thu, 3 Aug 2023 16:05:26 +0300 Subject: [PATCH 25/56] bump parking_lot 0.11 to 0.12 --- Cargo.lock | 4 ++-- compiler/rustc_data_structures/Cargo.toml | 2 +- compiler/rustc_query_system/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9f6df6e14c6..e6a3f252393 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3416,7 +3416,7 @@ dependencies = [ "libc", "measureme", "memmap2", - "parking_lot 0.11.2", + "parking_lot 0.12.1", "rustc-hash", "rustc-rayon", "rustc-rayon-core", @@ -4135,7 +4135,7 @@ dependencies = [ name = "rustc_query_system" version = "0.0.0" dependencies = [ - "parking_lot 0.11.2", + "parking_lot 0.12.1", "rustc-rayon-core", "rustc_ast", "rustc_data_structures", diff --git a/compiler/rustc_data_structures/Cargo.toml b/compiler/rustc_data_structures/Cargo.toml index a5c3cb3f857..f77bd53e76c 100644 --- a/compiler/rustc_data_structures/Cargo.toml +++ b/compiler/rustc_data_structures/Cargo.toml @@ -35,7 +35,7 @@ elsa = "=1.7.1" itertools = "0.10.1" [dependencies.parking_lot] -version = "0.11" +version = "0.12" [target.'cfg(windows)'.dependencies.windows] version = "0.48.0" diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml index e02cf38b671..584355df802 100644 --- a/compiler/rustc_query_system/Cargo.toml +++ b/compiler/rustc_query_system/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [lib] [dependencies] -parking_lot = "0.11" +parking_lot = "0.12" rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } From 2a0a1f918dabe3a1206d40ab9e40a3dafe44c1f0 Mon Sep 17 00:00:00 2001 From: Camille GILLOT Date: Thu, 3 Aug 2023 12:43:42 +0000 Subject: [PATCH 26/56] Make test harness lint about unnnameable tests. --- compiler/rustc_builtin_macros/messages.ftl | 2 + .../rustc_builtin_macros/src/test_harness.rs | 22 ++++++ compiler/rustc_lint/messages.ftl | 2 - compiler/rustc_lint/src/builtin.rs | 78 +------------------ compiler/rustc_lint/src/lib.rs | 2 - compiler/rustc_lint/src/lints.rs | 4 - compiler/rustc_lint_defs/src/builtin.rs | 40 ++++++++++ 7 files changed, 65 insertions(+), 85 deletions(-) diff --git a/compiler/rustc_builtin_macros/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 79fae7f92f1..8d8db4c13fa 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -227,3 +227,5 @@ builtin_macros_unexpected_lit = expected path to a trait, found literal .label = not a trait .str_lit = try using `#[derive({$sym})]` .other = for example, write `#[derive(Debug)]` for `Debug` + +builtin_macros_unnameable_test_items = cannot test inner items diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs index 81b618548da..507b74c2437 100644 --- a/compiler/rustc_builtin_macros/src/test_harness.rs +++ b/compiler/rustc_builtin_macros/src/test_harness.rs @@ -4,10 +4,12 @@ use rustc_ast as ast; use rustc_ast::entry::EntryPointType; use rustc_ast::mut_visit::{ExpectOne, *}; use rustc_ast::ptr::P; +use rustc_ast::visit::{walk_item, Visitor}; use rustc_ast::{attr, ModKind}; use rustc_expand::base::{ExtCtxt, ResolverExpand}; use rustc_expand::expand::{AstFragment, ExpansionConfig}; use rustc_feature::Features; +use rustc_session::lint::builtin::UNNAMEABLE_TEST_ITEMS; use rustc_session::Session; use rustc_span::hygiene::{AstPass, SyntaxContext, Transparency}; use rustc_span::symbol::{sym, Ident, Symbol}; @@ -137,11 +139,31 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> { let prev_tests = mem::take(&mut self.tests); noop_visit_item_kind(&mut item.kind, self); self.add_test_cases(item.id, span, prev_tests); + } else { + // But in those cases, we emit a lint to warn the user of these missing tests. + walk_item(&mut InnerItemLinter { sess: self.cx.ext_cx.sess }, &item); } smallvec![P(item)] } } +struct InnerItemLinter<'a> { + sess: &'a Session, +} + +impl<'a> Visitor<'a> for InnerItemLinter<'_> { + fn visit_item(&mut self, i: &'a ast::Item) { + if let Some(attr) = attr::find_by_name(&i.attrs, sym::rustc_test_marker) { + self.sess.parse_sess.buffer_lint( + UNNAMEABLE_TEST_ITEMS, + attr.span, + i.id, + crate::fluent_generated::builtin_macros_unnameable_test_items, + ); + } + } +} + // Beware, this is duplicated in librustc_passes/entry.rs (with // `rustc_hir::Item`), so make sure to keep them in sync. fn entry_point_type(item: &ast::Item, depth: usize) -> EntryPointType { diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index f482e3d7c12..2ad91cdb444 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -127,8 +127,6 @@ lint_builtin_unexpected_cli_config_name = unexpected `{$name}` as condition name lint_builtin_unexpected_cli_config_value = unexpected condition value `{$value}` for condition name `{$name}` .help = was set with `--cfg` but isn't in the `--check-cfg` expected values -lint_builtin_unnameable_test_items = cannot test inner items - lint_builtin_unpermitted_type_init_label = this code causes undefined behavior when executed lint_builtin_unpermitted_type_init_label_suggestion = help: use `MaybeUninit` instead, and only call `assume_init` after initialization is done diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index e6917f4b2d3..cabc6bd96e9 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -35,7 +35,7 @@ use crate::{ BuiltinSpecialModuleNameUsed, BuiltinTrivialBounds, BuiltinTypeAliasGenericBounds, BuiltinTypeAliasGenericBoundsSuggestion, BuiltinTypeAliasWhereClause, BuiltinUnexpectedCliConfigName, BuiltinUnexpectedCliConfigValue, - BuiltinUngatedAsyncFnTrackCaller, BuiltinUnnameableTestItems, BuiltinUnpermittedTypeInit, + BuiltinUngatedAsyncFnTrackCaller, BuiltinUnpermittedTypeInit, BuiltinUnpermittedTypeInitSub, BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub, BuiltinWhileTrue, SuggestChangingAssocTypes, @@ -1770,82 +1770,6 @@ impl EarlyLintPass for EllipsisInclusiveRangePatterns { } } -declare_lint! { - /// The `unnameable_test_items` lint detects [`#[test]`][test] functions - /// that are not able to be run by the test harness because they are in a - /// position where they are not nameable. - /// - /// [test]: https://doc.rust-lang.org/reference/attributes/testing.html#the-test-attribute - /// - /// ### Example - /// - /// ```rust,test - /// fn main() { - /// #[test] - /// fn foo() { - /// // This test will not fail because it does not run. - /// assert_eq!(1, 2); - /// } - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// In order for the test harness to run a test, the test function must be - /// located in a position where it can be accessed from the crate root. - /// This generally means it must be defined in a module, and not anywhere - /// else such as inside another function. The compiler previously allowed - /// this without an error, so a lint was added as an alert that a test is - /// not being used. Whether or not this should be allowed has not yet been - /// decided, see [RFC 2471] and [issue #36629]. - /// - /// [RFC 2471]: https://github.com/rust-lang/rfcs/pull/2471#issuecomment-397414443 - /// [issue #36629]: https://github.com/rust-lang/rust/issues/36629 - UNNAMEABLE_TEST_ITEMS, - Warn, - "detects an item that cannot be named being marked as `#[test_case]`", - report_in_external_macro -} - -pub struct UnnameableTestItems { - boundary: Option, // Id of the item under which things are not nameable - items_nameable: bool, -} - -impl_lint_pass!(UnnameableTestItems => [UNNAMEABLE_TEST_ITEMS]); - -impl UnnameableTestItems { - pub fn new() -> Self { - Self { boundary: None, items_nameable: true } - } -} - -impl<'tcx> LateLintPass<'tcx> for UnnameableTestItems { - fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) { - if self.items_nameable { - if let hir::ItemKind::Mod(..) = it.kind { - } else { - self.items_nameable = false; - self.boundary = Some(it.owner_id); - } - return; - } - - let attrs = cx.tcx.hir().attrs(it.hir_id()); - if let Some(attr) = attr::find_by_name(attrs, sym::rustc_test_marker) { - cx.emit_spanned_lint(UNNAMEABLE_TEST_ITEMS, attr.span, BuiltinUnnameableTestItems); - } - } - - fn check_item_post(&mut self, _cx: &LateContext<'_>, it: &hir::Item<'_>) { - if !self.items_nameable && self.boundary == Some(it.owner_id) { - self.items_nameable = true; - } - } -} - declare_lint! { /// The `keyword_idents` lint detects edition keywords being used as an /// identifier. diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 96fd3ccf774..d86c90efe86 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -188,8 +188,6 @@ late_lint_methods!( [ pub BuiltinCombinedLateLintPass, [ - // Tracks state across modules - UnnameableTestItems: UnnameableTestItems::new(), // Tracks attributes of parents MissingDoc: MissingDoc::new(), // Builds a global list of all impls of `Debug`. diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index a6a48bf4ffa..1c08c0e44f8 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -370,10 +370,6 @@ pub enum BuiltinEllipsisInclusiveRangePatternsLint { }, } -#[derive(LintDiagnostic)] -#[diag(lint_builtin_unnameable_test_items)] -pub struct BuiltinUnnameableTestItems; - #[derive(LintDiagnostic)] #[diag(lint_builtin_keyword_idents)] pub struct BuiltinKeywordIdents { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 7e3b6e9e218..1920a11ced4 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -2846,6 +2846,45 @@ declare_lint! { }; } +declare_lint! { + /// The `unnameable_test_items` lint detects [`#[test]`][test] functions + /// that are not able to be run by the test harness because they are in a + /// position where they are not nameable. + /// + /// [test]: https://doc.rust-lang.org/reference/attributes/testing.html#the-test-attribute + /// + /// ### Example + /// + /// ```rust,test + /// fn main() { + /// #[test] + /// fn foo() { + /// // This test will not fail because it does not run. + /// assert_eq!(1, 2); + /// } + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// In order for the test harness to run a test, the test function must be + /// located in a position where it can be accessed from the crate root. + /// This generally means it must be defined in a module, and not anywhere + /// else such as inside another function. The compiler previously allowed + /// this without an error, so a lint was added as an alert that a test is + /// not being used. Whether or not this should be allowed has not yet been + /// decided, see [RFC 2471] and [issue #36629]. + /// + /// [RFC 2471]: https://github.com/rust-lang/rfcs/pull/2471#issuecomment-397414443 + /// [issue #36629]: https://github.com/rust-lang/rust/issues/36629 + pub UNNAMEABLE_TEST_ITEMS, + Warn, + "detects an item that cannot be named being marked as `#[test_case]`", + report_in_external_macro +} + declare_lint! { /// The `useless_deprecated` lint detects deprecation attributes with no effect. /// @@ -3403,6 +3442,7 @@ declare_lint_pass! { UNKNOWN_CRATE_TYPES, UNKNOWN_DIAGNOSTIC_ATTRIBUTES, UNKNOWN_LINTS, + UNNAMEABLE_TEST_ITEMS, UNNAMEABLE_TYPES, UNREACHABLE_CODE, UNREACHABLE_PATTERNS, From 8aca388af8450d7e89b2c670e6ab5cfa1e82de53 Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 3 Aug 2023 14:43:26 +0200 Subject: [PATCH 27/56] rewrite stack dependent overflow handling --- compiler/rustc_middle/src/traits/solve.rs | 4 +- .../rustc_middle/src/traits/solve/cache.rs | 100 +++++++ .../src/solve/eval_ctxt.rs | 2 + .../src/solve/search_graph/mod.rs | 266 ++++++++++++------ .../src/solve/search_graph/overflow.rs | 93 ------ 5 files changed, 286 insertions(+), 179 deletions(-) create mode 100644 compiler/rustc_middle/src/traits/solve/cache.rs delete mode 100644 compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index b21a00e4122..475b575ca05 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -1,7 +1,6 @@ use std::ops::ControlFlow; use rustc_data_structures::intern::Interned; -use rustc_query_system::cache::Cache; use crate::infer::canonical::{CanonicalVarValues, QueryRegionConstraints}; use crate::traits::query::NoSolution; @@ -11,9 +10,10 @@ use crate::ty::{ TypeVisitor, }; +mod cache; pub mod inspect; -pub type EvaluationCache<'tcx> = Cache, QueryResult<'tcx>>; +pub use cache::{CacheData, EvaluationCache}; /// A goal is a statement, i.e. `predicate`, we want to prove /// given some assumptions, i.e. `param_env`. diff --git a/compiler/rustc_middle/src/traits/solve/cache.rs b/compiler/rustc_middle/src/traits/solve/cache.rs new file mode 100644 index 00000000000..9898b0019bb --- /dev/null +++ b/compiler/rustc_middle/src/traits/solve/cache.rs @@ -0,0 +1,100 @@ +use super::{CanonicalInput, QueryResult}; +use crate::ty::TyCtxt; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::sync::Lock; +use rustc_query_system::cache::WithDepNode; +use rustc_query_system::dep_graph::DepNodeIndex; +use rustc_session::Limit; +/// The trait solver cache used by `-Ztrait-solver=next`. +/// +/// FIXME(@lcnr): link to some official documentation of how +/// this works. +#[derive(Default)] +pub struct EvaluationCache<'tcx> { + map: Lock, CacheEntry<'tcx>>>, +} + +pub struct CacheData<'tcx> { + pub result: QueryResult<'tcx>, + pub reached_depth: usize, + pub encountered_overflow: bool, +} + +impl<'tcx> EvaluationCache<'tcx> { + /// Insert a final result into the global cache. + pub fn insert( + &self, + key: CanonicalInput<'tcx>, + reached_depth: usize, + did_overflow: bool, + cycle_participants: FxHashSet>, + dep_node: DepNodeIndex, + result: QueryResult<'tcx>, + ) { + let mut map = self.map.borrow_mut(); + let entry = map.entry(key).or_default(); + let data = WithDepNode::new(dep_node, result); + entry.cycle_participants.extend(cycle_participants); + if did_overflow { + entry.with_overflow.insert(reached_depth, data); + } else { + entry.success = Some(Success { data, reached_depth }); + } + } + + /// Try to fetch a cached result, checking the recursion limit + /// and handling root goals of coinductive cycles. + /// + /// If this returns `Some` the cache result can be used. + pub fn get( + &self, + tcx: TyCtxt<'tcx>, + key: CanonicalInput<'tcx>, + cycle_participant_in_stack: impl FnOnce(&FxHashSet>) -> bool, + available_depth: Limit, + ) -> Option> { + let map = self.map.borrow(); + let entry = map.get(&key)?; + + if cycle_participant_in_stack(&entry.cycle_participants) { + return None; + } + + if let Some(ref success) = entry.success { + if available_depth.value_within_limit(success.reached_depth) { + return Some(CacheData { + result: success.data.get(tcx), + reached_depth: success.reached_depth, + encountered_overflow: false, + }); + } + } + + entry.with_overflow.get(&available_depth.0).map(|e| CacheData { + result: e.get(tcx), + reached_depth: available_depth.0, + encountered_overflow: true, + }) + } +} + +struct Success<'tcx> { + data: WithDepNode>, + reached_depth: usize, +} + +/// The cache entry for a goal `CanonicalInput`. +/// +/// This contains results whose computation never hit the +/// recursion limit in `success`, and all results which hit +/// the recursion limit in `with_overflow`. +#[derive(Default)] +struct CacheEntry<'tcx> { + success: Option>, + /// We have to be careful when caching roots of cycles. + /// + /// See the doc comment of `StackEntry::cycle_participants` for more + /// details. + cycle_participants: FxHashSet>, + with_overflow: FxHashMap>>, +} diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs index 8a20552d339..5ec9ddfe64a 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt.rs @@ -340,6 +340,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { ) -> Result<(bool, Certainty, Vec>>), NoSolution> { let (orig_values, canonical_goal) = self.canonicalize_goal(goal); let mut goal_evaluation = self.inspect.new_goal_evaluation(goal, is_normalizes_to_hack); + let encountered_overflow = self.search_graph.encountered_overflow(); let canonical_response = EvalCtxt::evaluate_canonical_goal( self.tcx(), self.search_graph, @@ -388,6 +389,7 @@ impl<'a, 'tcx> EvalCtxt<'a, 'tcx> { && !self.search_graph.in_cycle() { debug!("rerunning goal to check result is stable"); + self.search_graph.reset_encountered_overflow(encountered_overflow); let (_orig_values, canonical_goal) = self.canonicalize_goal(goal); let new_canonical_response = EvalCtxt::evaluate_canonical_goal( self.tcx(), diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs index f84445e8820..36b141f0ce8 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs @@ -1,28 +1,42 @@ mod cache; -mod overflow; - -pub(super) use overflow::OverflowHandler; -use rustc_middle::traits::solve::inspect::CacheHit; use self::cache::ProvisionalEntry; -use cache::ProvisionalCache; -use overflow::OverflowData; -use rustc_index::IndexVec; -use rustc_middle::dep_graph::DepKind; -use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, QueryResult}; -use rustc_middle::ty::TyCtxt; -use std::{collections::hash_map::Entry, mem}; - use super::inspect::ProofTreeBuilder; use super::SolverMode; +use cache::ProvisionalCache; +use rustc_data_structures::fx::FxHashSet; +use rustc_index::Idx; +use rustc_index::IndexVec; +use rustc_middle::dep_graph::DepKind; +use rustc_middle::traits::solve::inspect::CacheHit; +use rustc_middle::traits::solve::CacheData; +use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, QueryResult}; +use rustc_middle::ty::TyCtxt; +use rustc_session::Limit; +use std::{collections::hash_map::Entry, mem}; rustc_index::newtype_index! { pub struct StackDepth {} } -struct StackElem<'tcx> { +#[derive(Debug)] +struct StackEntry<'tcx> { input: CanonicalInput<'tcx>, + available_depth: Limit, + // The maximum depth reached by this stack entry, only up-to date + // for the top of the stack and lazily updated for the rest. + reached_depth: StackDepth, + encountered_overflow: bool, has_been_used: bool, + + /// We put only the root goal of a coinductive cycle into the global cache. + /// + /// If we were to use that result when later trying to prove another cycle + /// participant, we can end up with unstable query results. + /// + /// See tests/ui/new-solver/coinduction/incompleteness-unstable-result.rs for + /// an example of where this is needed. + cycle_participants: FxHashSet>, } pub(super) struct SearchGraph<'tcx> { @@ -31,8 +45,7 @@ pub(super) struct SearchGraph<'tcx> { /// The stack of goals currently being computed. /// /// An element is *deeper* in the stack if its index is *lower*. - stack: IndexVec>, - overflow_data: OverflowData, + stack: IndexVec>, provisional_cache: ProvisionalCache<'tcx>, } @@ -42,7 +55,6 @@ impl<'tcx> SearchGraph<'tcx> { mode, local_overflow_limit: tcx.recursion_limit().0.ilog2() as usize, stack: Default::default(), - overflow_data: OverflowData::new(tcx), provisional_cache: ProvisionalCache::empty(), } } @@ -55,15 +67,34 @@ impl<'tcx> SearchGraph<'tcx> { self.local_overflow_limit } - /// We do not use the global cache during coherence. + /// Update the stack and reached depths on cache hits. + #[instrument(level = "debug", skip(self))] + fn on_cache_hit(&mut self, additional_depth: usize, encountered_overflow: bool) { + let reached_depth = self.stack.next_index().plus(additional_depth); + if let Some(last) = self.stack.raw.last_mut() { + last.reached_depth = last.reached_depth.max(reached_depth); + last.encountered_overflow |= encountered_overflow; + } + } + + /// Pops the highest goal from the stack, lazily updating the + /// the next goal in the stack. /// + /// Directly popping from the stack instead of using this method + /// would cause us to not track overflow and recursion depth correctly. + fn pop_stack(&mut self) -> StackEntry<'tcx> { + let elem = self.stack.pop().unwrap(); + if let Some(last) = self.stack.raw.last_mut() { + last.reached_depth = last.reached_depth.max(elem.reached_depth); + last.encountered_overflow |= elem.encountered_overflow; + } + elem + } + /// The trait solver behavior is different for coherence - /// so we would have to add the solver mode to the cache key. - /// This is probably not worth it as trait solving during - /// coherence tends to already be incredibly fast. - /// - /// We could add another global cache for coherence instead, - /// but that's effort so let's only do it if necessary. + /// so we use a separate cache. Alternatively we could use + /// a single cache and share it between coherence and ordinary + /// trait solving. pub(super) fn global_cache(&self, tcx: TyCtxt<'tcx>) -> &'tcx EvaluationCache<'tcx> { match self.mode { SolverMode::Normal => &tcx.new_solver_evaluation_cache, @@ -93,6 +124,47 @@ impl<'tcx> SearchGraph<'tcx> { } } + /// Fetches whether the current goal encountered overflow. + /// + /// This should only be used for the check in `evaluate_goal`. + pub(super) fn encountered_overflow(&self) -> bool { + if let Some(last) = self.stack.raw.last() { last.encountered_overflow } else { false } + } + + /// Resets `encountered_overflow` of the current goal. + /// + /// This should only be used for the check in `evaluate_goal`. + pub(super) fn reset_encountered_overflow(&mut self, encountered_overflow: bool) { + if encountered_overflow { + self.stack.raw.last_mut().unwrap().encountered_overflow = true; + } + } + + /// Returns the remaining depth allowed for nested goals. + /// + /// This is generally simply one less than the current depth. + /// However, if we encountered overflow, we significantly reduce + /// the remaining depth of all nested goals to prevent hangs + /// in case there is exponential blowup. + fn allowed_depth_for_nested( + tcx: TyCtxt<'tcx>, + stack: &IndexVec>, + ) -> Option { + if let Some(last) = stack.raw.last() { + if last.available_depth.0 == 0 { + return None; + } + + Some(if last.encountered_overflow { + Limit(last.available_depth.0 / 4) + } else { + Limit(last.available_depth.0 - 1) + }) + } else { + Some(tcx.recursion_limit()) + } + } + /// Tries putting the new goal on the stack, returning an error if it is already cached. /// /// This correctly updates the provisional cache if there is a cycle. @@ -101,18 +173,24 @@ impl<'tcx> SearchGraph<'tcx> { &mut self, tcx: TyCtxt<'tcx>, input: CanonicalInput<'tcx>, + available_depth: Limit, inspect: &mut ProofTreeBuilder<'tcx>, ) -> Result<(), QueryResult<'tcx>> { // Look at the provisional cache to check for cycles. let cache = &mut self.provisional_cache; match cache.lookup_table.entry(input) { - // No entry, simply push this goal on the stack after dealing with overflow. + // No entry, simply push this goal on the stack. Entry::Vacant(v) => { - if self.overflow_data.has_overflow(self.stack.len()) { - return Err(self.deal_with_overflow(tcx, input)); - } - - let depth = self.stack.push(StackElem { input, has_been_used: false }); + let depth = self.stack.next_index(); + let entry = StackEntry { + input, + available_depth, + reached_depth: depth, + encountered_overflow: false, + has_been_used: false, + cycle_participants: Default::default(), + }; + assert_eq!(self.stack.push(entry), depth); let response = Self::response_no_constraints(tcx, input, Certainty::Yes); let entry_index = cache.entries.push(ProvisionalEntry { response, depth, input }); v.insert(entry_index); @@ -136,8 +214,12 @@ impl<'tcx> SearchGraph<'tcx> { debug!("encountered cycle with depth {stack_depth:?}"); cache.add_dependency_of_leaf_on(entry_index); + let mut iter = self.stack.iter_mut(); + let root = iter.nth(stack_depth.as_usize()).unwrap(); + for e in iter { + root.cycle_participants.insert(e.input); + } - self.stack[stack_depth].has_been_used = true; // NOTE: The goals on the stack aren't the only goals involved in this cycle. // We can also depend on goals which aren't part of the stack but coinductively // depend on the stack themselves. We already checked whether all the goals @@ -148,6 +230,9 @@ impl<'tcx> SearchGraph<'tcx> { .iter() .all(|g| g.input.value.goal.predicate.is_coinductive(tcx)) { + // If we're in a coinductive cycle, we have to retry proving the current goal + // until we reach a fixpoint. + self.stack[stack_depth].has_been_used = true; Err(cache.provisional_result(entry_index)) } else { Err(Self::response_no_constraints(tcx, input, Certainty::OVERFLOW)) @@ -173,13 +258,12 @@ impl<'tcx> SearchGraph<'tcx> { &mut self, actual_input: CanonicalInput<'tcx>, response: QueryResult<'tcx>, - ) -> bool { - let stack_elem = self.stack.pop().unwrap(); - let StackElem { input, has_been_used } = stack_elem; - assert_eq!(input, actual_input); + ) -> Result, ()> { + let stack_entry = self.pop_stack(); + assert_eq!(stack_entry.input, actual_input); let cache = &mut self.provisional_cache; - let provisional_entry_index = *cache.lookup_table.get(&input).unwrap(); + let provisional_entry_index = *cache.lookup_table.get(&stack_entry.input).unwrap(); let provisional_entry = &mut cache.entries[provisional_entry_index]; // We eagerly update the response in the cache here. If we have to reevaluate // this goal we use the new response when hitting a cycle, and we definitely @@ -188,7 +272,7 @@ impl<'tcx> SearchGraph<'tcx> { // Was the current goal the root of a cycle and was the provisional response // different from the final one. - if has_been_used && prev_response != response { + if stack_entry.has_been_used && prev_response != response { // If so, remove all entries whose result depends on this goal // from the provisional cache... // @@ -201,29 +285,44 @@ impl<'tcx> SearchGraph<'tcx> { cache.entries.truncate(provisional_entry_index.index() + 1); // ...and finally push our goal back on the stack and reevaluate it. - self.stack.push(StackElem { input, has_been_used: false }); - false + self.stack.push(StackEntry { has_been_used: false, ..stack_entry }); + Err(()) } else { - true + Ok(stack_entry) } } pub(super) fn with_new_goal( &mut self, tcx: TyCtxt<'tcx>, - canonical_input: CanonicalInput<'tcx>, + input: CanonicalInput<'tcx>, inspect: &mut ProofTreeBuilder<'tcx>, mut loop_body: impl FnMut(&mut Self, &mut ProofTreeBuilder<'tcx>) -> QueryResult<'tcx>, ) -> QueryResult<'tcx> { + let Some(available_depth) = Self::allowed_depth_for_nested(tcx, &self.stack) else { + if let Some(last) = self.stack.raw.last_mut() { + last.encountered_overflow = true; + } + return Self::response_no_constraints(tcx, input, Certainty::OVERFLOW); + }; + if inspect.use_global_cache() { - if let Some(result) = self.global_cache(tcx).get(&canonical_input, tcx) { - debug!(?canonical_input, ?result, "cache hit"); - inspect.cache_hit(CacheHit::Global); + if let Some(CacheData { result, reached_depth, encountered_overflow }) = + self.global_cache(tcx).get( + tcx, + input, + |cycle_participants| { + self.stack.iter().any(|entry| cycle_participants.contains(&entry.input)) + }, + available_depth, + ) + { + self.on_cache_hit(reached_depth, encountered_overflow); return result; } } - match self.try_push_stack(tcx, canonical_input, inspect) { + match self.try_push_stack(tcx, input, available_depth, inspect) { Ok(()) => {} // Our goal is already on the stack, eager return. Err(response) => return response, @@ -232,59 +331,58 @@ impl<'tcx> SearchGraph<'tcx> { // This is for global caching, so we properly track query dependencies. // Everything that affects the `Result` should be performed within this // `with_anon_task` closure. - let (result, dep_node) = tcx.dep_graph.with_anon_task(tcx, DepKind::TraitSelect, || { - self.repeat_while_none( - |this| { - let result = this.deal_with_overflow(tcx, canonical_input); - let _ = this.stack.pop().unwrap(); - result - }, - |this| { - let result = loop_body(this, inspect); - this.try_finalize_goal(canonical_input, result).then(|| result) - }, - ) - }); + let ((final_entry, result), dep_node) = + tcx.dep_graph.with_anon_task(tcx, DepKind::TraitSelect, || { + // We run our goal in a loop to handle coinductive cycles. If we fail to reach a + // fipoint we return overflow. + for _ in 0..self.local_overflow_limit() { + let result = loop_body(self, inspect); + if let Ok(final_entry) = self.try_finalize_goal(input, result) { + return (final_entry, result); + } + } + + debug!("canonical cycle overflow"); + let current_entry = self.pop_stack(); + let result = Self::response_no_constraints(tcx, input, Certainty::OVERFLOW); + (current_entry, result) + }); let cache = &mut self.provisional_cache; - let provisional_entry_index = *cache.lookup_table.get(&canonical_input).unwrap(); + let provisional_entry_index = *cache.lookup_table.get(&input).unwrap(); let provisional_entry = &mut cache.entries[provisional_entry_index]; let depth = provisional_entry.depth; - // If not, we're done with this goal. + // We're now done with this goal. In case this goal is involved in a cycle + // do not remove it from the provisional cache and do not add it to the global + // cache. // - // Check whether that this goal doesn't depend on a goal deeper on the stack - // and if so, move it to the global cache. - // - // Note that if any nested goal were to depend on something deeper on the stack, - // this would have also updated the depth of the current goal. + // It is not possible for any nested goal to depend on something deeper on the + // stack, as this would have also updated the depth of the current goal. if depth == self.stack.next_index() { - // If the current goal is the head of a cycle, we drop all other - // cycle participants without moving them to the global cache. - let other_cycle_participants = provisional_entry_index.index() + 1; - for (i, entry) in cache.entries.drain_enumerated(other_cycle_participants..) { + for (i, entry) in cache.entries.drain_enumerated(provisional_entry_index.index()..) { let actual_index = cache.lookup_table.remove(&entry.input); debug_assert_eq!(Some(i), actual_index); debug_assert!(entry.depth == depth); } - let current_goal = cache.entries.pop().unwrap(); - let actual_index = cache.lookup_table.remove(¤t_goal.input); - debug_assert_eq!(Some(provisional_entry_index), actual_index); - debug_assert!(current_goal.depth == depth); - - // We move the root goal to the global cache if we either did not hit an overflow or if it's - // the root goal as that will now always hit the same overflow limit. + // When encountering a cycle, both inductive and coinductive, we only + // move the root into the global cache. We also store all other cycle + // participants involved. // - // NOTE: We cannot move any non-root goals to the global cache. When replaying the root goal's - // dependencies, our non-root goal may no longer appear as child of the root goal. - // - // See https://github.com/rust-lang/rust/pull/108071 for some additional context. - let can_cache = inspect.use_global_cache() - && (!self.overflow_data.did_overflow() || self.stack.is_empty()); - if can_cache { - self.global_cache(tcx).insert(current_goal.input, dep_node, current_goal.response) - } + // We disable the global cache entry of the root goal if a cycle + // participant is on the stack. This is necessary to prevent unstable + // results. See the comment of `StackEntry::cycle_participants` for + // more details. + let reached_depth = final_entry.reached_depth.as_usize() - self.stack.len(); + self.global_cache(tcx).insert( + input, + reached_depth, + final_entry.encountered_overflow, + final_entry.cycle_participants, + dep_node, + result, + ) } result diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs b/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs deleted file mode 100644 index 4abde2c96d4..00000000000 --- a/compiler/rustc_trait_selection/src/solve/search_graph/overflow.rs +++ /dev/null @@ -1,93 +0,0 @@ -use rustc_infer::infer::canonical::Canonical; -use rustc_infer::traits::query::NoSolution; -use rustc_middle::traits::solve::{Certainty, QueryResult}; -use rustc_middle::ty::TyCtxt; -use rustc_session::Limit; - -use super::SearchGraph; -use crate::solve::response_no_constraints_raw; - -/// When detecting a solver overflow, we return ambiguity. Overflow can be -/// *hidden* by either a fatal error in an **AND** or a trivial success in an **OR**. -/// -/// This is in issue in case of exponential blowup, e.g. if each goal on the stack -/// has multiple nested (overflowing) candidates. To deal with this, we reduce the limit -/// used by the solver when hitting the default limit for the first time. -/// -/// FIXME: Get tests where always using the `default_limit` results in a hang and refer -/// to them here. We can also improve the overflow strategy if necessary. -pub(super) struct OverflowData { - default_limit: Limit, - current_limit: Limit, - /// When proving an **AND** we have to repeatedly iterate over the yet unproven goals. - /// - /// Because of this each iteration also increases the depth in addition to the stack - /// depth. - additional_depth: usize, -} - -impl OverflowData { - pub(super) fn new(tcx: TyCtxt<'_>) -> OverflowData { - let default_limit = tcx.recursion_limit(); - OverflowData { default_limit, current_limit: default_limit, additional_depth: 0 } - } - - #[inline] - pub(super) fn did_overflow(&self) -> bool { - self.default_limit.0 != self.current_limit.0 - } - - #[inline] - pub(super) fn has_overflow(&self, depth: usize) -> bool { - !self.current_limit.value_within_limit(depth + self.additional_depth) - } - - /// Updating the current limit when hitting overflow. - fn deal_with_overflow(&mut self) { - // When first hitting overflow we reduce the overflow limit - // for all future goals to prevent hangs if there's an exponential - // blowup. - self.current_limit.0 = self.default_limit.0 / 8; - } -} - -pub(in crate::solve) trait OverflowHandler<'tcx> { - fn search_graph(&mut self) -> &mut SearchGraph<'tcx>; - - fn repeat_while_none( - &mut self, - on_overflow: impl FnOnce(&mut Self) -> Result, - mut loop_body: impl FnMut(&mut Self) -> Option>, - ) -> Result { - let start_depth = self.search_graph().overflow_data.additional_depth; - let depth = self.search_graph().stack.len(); - while !self.search_graph().overflow_data.has_overflow(depth) { - if let Some(result) = loop_body(self) { - self.search_graph().overflow_data.additional_depth = start_depth; - return result; - } - - self.search_graph().overflow_data.additional_depth += 1; - } - self.search_graph().overflow_data.additional_depth = start_depth; - self.search_graph().overflow_data.deal_with_overflow(); - on_overflow(self) - } -} - -impl<'tcx> OverflowHandler<'tcx> for SearchGraph<'tcx> { - fn search_graph(&mut self) -> &mut SearchGraph<'tcx> { - self - } -} - -impl<'tcx> SearchGraph<'tcx> { - pub fn deal_with_overflow( - &mut self, - tcx: TyCtxt<'tcx>, - goal: Canonical<'tcx, impl Sized>, - ) -> QueryResult<'tcx> { - self.overflow_data.deal_with_overflow(); - Ok(response_no_constraints_raw(tcx, goal.max_universe, goal.variables, Certainty::OVERFLOW)) - } -} From b1d9cb9a2aea50bd6d201dd756f6e13b587fa94b Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 3 Aug 2023 14:43:36 +0200 Subject: [PATCH 28/56] add tests --- .../param-env-region-infer.current.stderr | 2 +- .../param-env-region-infer.next.stderr | 30 -------- tests/ui/dyn-star/param-env-region-infer.rs | 9 +-- .../fixpoint-exponential-growth.rs | 32 +++++++++ .../fixpoint-exponential-growth.stderr | 23 +++++++ .../incompleteness-unstable-result.rs | 69 +++++++++++++++++++ .../incompleteness-unstable-result.stderr | 16 +++++ .../new-solver/overflow/global-cache.rs | 23 +++++++ .../new-solver/overflow/global-cache.stderr | 16 +++++ 9 files changed, 185 insertions(+), 35 deletions(-) delete mode 100644 tests/ui/dyn-star/param-env-region-infer.next.stderr create mode 100644 tests/ui/traits/new-solver/coinduction/fixpoint-exponential-growth.rs create mode 100644 tests/ui/traits/new-solver/coinduction/fixpoint-exponential-growth.stderr create mode 100644 tests/ui/traits/new-solver/coinduction/incompleteness-unstable-result.rs create mode 100644 tests/ui/traits/new-solver/coinduction/incompleteness-unstable-result.stderr create mode 100644 tests/ui/traits/new-solver/overflow/global-cache.rs create mode 100644 tests/ui/traits/new-solver/overflow/global-cache.stderr diff --git a/tests/ui/dyn-star/param-env-region-infer.current.stderr b/tests/ui/dyn-star/param-env-region-infer.current.stderr index 902053ecfef..b982be45196 100644 --- a/tests/ui/dyn-star/param-env-region-infer.current.stderr +++ b/tests/ui/dyn-star/param-env-region-infer.current.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed - --> $DIR/param-env-region-infer.rs:18:10 + --> $DIR/param-env-region-infer.rs:19:10 | LL | t as _ | ^ cannot infer type diff --git a/tests/ui/dyn-star/param-env-region-infer.next.stderr b/tests/ui/dyn-star/param-env-region-infer.next.stderr deleted file mode 100644 index 28aec533a00..00000000000 --- a/tests/ui/dyn-star/param-env-region-infer.next.stderr +++ /dev/null @@ -1,30 +0,0 @@ -error[E0391]: cycle detected when computing type of `make_dyn_star::{opaque#0}` - --> $DIR/param-env-region-infer.rs:16:60 - | -LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: ...which requires type-checking `make_dyn_star`... - --> $DIR/param-env-region-infer.rs:16:1 - | -LL | fn make_dyn_star<'a, T: PointerLike + Debug + 'a>(t: T) -> impl PointerLike + Debug + 'a { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: ...which requires computing layout of `make_dyn_star::{opaque#0}`... - = note: ...which requires normalizing `make_dyn_star::{opaque#0}`... - = note: ...which again requires computing type of `make_dyn_star::{opaque#0}`, completing the cycle -note: cycle used when checking item types in top-level module - --> $DIR/param-env-region-infer.rs:10:1 - | -LL | / #![feature(dyn_star, pointer_like_trait)] -LL | | #![allow(incomplete_features)] -LL | | -LL | | use std::fmt::Debug; -... | -LL | | -LL | | fn main() {} - | |____________^ - = note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0391`. diff --git a/tests/ui/dyn-star/param-env-region-infer.rs b/tests/ui/dyn-star/param-env-region-infer.rs index 537473abc3a..50dec94d25b 100644 --- a/tests/ui/dyn-star/param-env-region-infer.rs +++ b/tests/ui/dyn-star/param-env-region-infer.rs @@ -1,9 +1,10 @@ -// revisions: current next -// Need `-Zdeduplicate-diagnostics=yes` because the number of cycle errors -// emitted is for some horrible reason platform-specific. -//[next] compile-flags: -Ztrait-solver=next -Zdeduplicate-diagnostics=yes +// revisions: current // incremental +// FIXME(-Ztrait-solver=next): THis currently results in unstable query results: +// `normalizes-to(opaque, opaque)` changes from `Maybe(Ambiguous)` to `Maybe(Overflow)` +// once the hidden type of the opaque is already defined to be itself. + // checks that we don't ICE if there are region inference variables in the environment // when computing `PointerLike` builtin candidates. diff --git a/tests/ui/traits/new-solver/coinduction/fixpoint-exponential-growth.rs b/tests/ui/traits/new-solver/coinduction/fixpoint-exponential-growth.rs new file mode 100644 index 00000000000..fcafdcf637a --- /dev/null +++ b/tests/ui/traits/new-solver/coinduction/fixpoint-exponential-growth.rs @@ -0,0 +1,32 @@ +// compile-flags: -Ztrait-solver=next + +// Proving `W: Trait` instantiates `?0` with `(W, W)` and then +// proves `W: Trait` and `W: Trait`, resulting in a coinductive cycle. +// +// Proving coinductive cycles runs until we reach a fixpoint. This fixpoint is +// never reached here and each step doubles the amount of nested obligations. +// +// This previously caused a hang in the trait solver, see +// https://github.com/rust-lang/trait-system-refactor-initiative/issues/13. + +#![feature(rustc_attrs)] + +#[rustc_coinductive] +trait Trait {} + +struct W(T); + +impl Trait for W<(W, W)> +where + W: Trait, + W: Trait, +{ +} + +fn impls() {} + +fn main() { + impls::>(); + //~^ ERROR type annotations needed + //~| ERROR overflow evaluating the requirement +} diff --git a/tests/ui/traits/new-solver/coinduction/fixpoint-exponential-growth.stderr b/tests/ui/traits/new-solver/coinduction/fixpoint-exponential-growth.stderr new file mode 100644 index 00000000000..7d3535e1f01 --- /dev/null +++ b/tests/ui/traits/new-solver/coinduction/fixpoint-exponential-growth.stderr @@ -0,0 +1,23 @@ +error[E0282]: type annotations needed + --> $DIR/fixpoint-exponential-growth.rs:29:5 + | +LL | impls::>(); + | ^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `impls` + +error[E0275]: overflow evaluating the requirement `W<_>: Trait` + --> $DIR/fixpoint-exponential-growth.rs:29:5 + | +LL | impls::>(); + | ^^^^^^^^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`fixpoint_exponential_growth`) +note: required by a bound in `impls` + --> $DIR/fixpoint-exponential-growth.rs:26:13 + | +LL | fn impls() {} + | ^^^^^ required by this bound in `impls` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0275, E0282. +For more information about an error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/new-solver/coinduction/incompleteness-unstable-result.rs b/tests/ui/traits/new-solver/coinduction/incompleteness-unstable-result.rs new file mode 100644 index 00000000000..0cd14f05c8d --- /dev/null +++ b/tests/ui/traits/new-solver/coinduction/incompleteness-unstable-result.rs @@ -0,0 +1,69 @@ +// compile-flags: -Ztrait-solver=next +#![feature(rustc_attrs)] + +// This test is incredibly subtle. At its core the goal is to get a coinductive cycle, +// which, depending on its root goal, either holds or errors. We achieve this by getting +// incomplete inference via a `ParamEnv` candidate in the `A` impl and required +// inference from an `Impl` candidate in the `B` impl. +// +// To make global cache accesses stronger than the guidance from the where-bounds, we add +// another coinductive cycle from `A: Trait` to `A: Trait` and only +// constrain `D` directly. This means that any candidates which rely on `V` only make +// progress in the second iteration, allowing a cache access in the first iteration to take +// precedence. +// +// tl;dr: our caching of coinductive cycles was broken and this is a regression +// test for that. + +#[rustc_coinductive] +trait Trait {} +struct A(*const T); +struct B(*const T); + +trait IncompleteGuidance {} +impl IncompleteGuidance for T {} +impl IncompleteGuidance for T {} +impl IncompleteGuidance for T {} + +trait ImplGuidance {} +impl ImplGuidance for T {} +impl ImplGuidance for T {} + +impl Trait for A +where + T: IncompleteGuidance, + A: Trait, + B: Trait, + (): ToU8, +{ +} + +trait ToU8 {} +impl ToU8 for () {} + +impl Trait for B +where + T: ImplGuidance, + A: Trait, +{ +} + +fn impls_trait, U: ?Sized, V: ?Sized, D: ?Sized>() {} + +fn with_bound() +where + X: IncompleteGuidance, + X: IncompleteGuidance, + X: IncompleteGuidance, +{ + impls_trait::, _, _, _>(); // entering the cycle from `B` works + + // entering the cycle from `A` fails, but would work if we were to use the cache + // result of `B`. + impls_trait::, _, _, _>(); + //~^ ERROR the trait bound `A: Trait<_, _, _>` is not satisfied +} + +fn main() { + with_bound::(); +} diff --git a/tests/ui/traits/new-solver/coinduction/incompleteness-unstable-result.stderr b/tests/ui/traits/new-solver/coinduction/incompleteness-unstable-result.stderr new file mode 100644 index 00000000000..f1871ff0564 --- /dev/null +++ b/tests/ui/traits/new-solver/coinduction/incompleteness-unstable-result.stderr @@ -0,0 +1,16 @@ +error[E0277]: the trait bound `A: Trait<_, _, _>` is not satisfied + --> $DIR/incompleteness-unstable-result.rs:63:19 + | +LL | impls_trait::, _, _, _>(); + | ^^^^ the trait `Trait<_, _, _>` is not implemented for `A` + | + = help: the trait `Trait` is implemented for `A` +note: required by a bound in `impls_trait` + --> $DIR/incompleteness-unstable-result.rs:51:28 + | +LL | fn impls_trait, U: ?Sized, V: ?Sized, D: ?Sized>() {} + | ^^^^^^^^^^^^^^ required by this bound in `impls_trait` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/new-solver/overflow/global-cache.rs b/tests/ui/traits/new-solver/overflow/global-cache.rs new file mode 100644 index 00000000000..adc03da04a8 --- /dev/null +++ b/tests/ui/traits/new-solver/overflow/global-cache.rs @@ -0,0 +1,23 @@ +// compile-flags: -Ztrait-solver=next + +// Check that we consider the reached depth of global cache +// entries when detecting overflow. We would otherwise be unstable +// wrt to incremental compilation. +#![recursion_limit = "9"] + +trait Trait {} + +struct Inc(T); + +impl Trait for Inc {} +impl Trait for () {} + +fn impls_trait() {} + +type Four = Inc>>>; + +fn main() { + impls_trait::>>(); + impls_trait::>>>>(); + //~^ ERROR overflow evaluating the requirement +} diff --git a/tests/ui/traits/new-solver/overflow/global-cache.stderr b/tests/ui/traits/new-solver/overflow/global-cache.stderr new file mode 100644 index 00000000000..f3b86a083ad --- /dev/null +++ b/tests/ui/traits/new-solver/overflow/global-cache.stderr @@ -0,0 +1,16 @@ +error[E0275]: overflow evaluating the requirement `Inc>>>>>>: Trait` + --> $DIR/global-cache.rs:21:5 + | +LL | impls_trait::>>>>(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "18"]` attribute to your crate (`global_cache`) +note: required by a bound in `impls_trait` + --> $DIR/global-cache.rs:15:19 + | +LL | fn impls_trait() {} + | ^^^^^ required by this bound in `impls_trait` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0275`. From baf076825c338806ee7e385dce971484be4f1cee Mon Sep 17 00:00:00 2001 From: lcnr Date: Thu, 3 Aug 2023 15:12:34 +0200 Subject: [PATCH 29/56] inline helper methods into `with_new_goal` --- .../src/solve/search_graph/mod.rs | 195 +++++++----------- 1 file changed, 80 insertions(+), 115 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs index 36b141f0ce8..21c8d476902 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs @@ -165,21 +165,46 @@ impl<'tcx> SearchGraph<'tcx> { } } - /// Tries putting the new goal on the stack, returning an error if it is already cached. + /// Probably the most involved method of the whole solver. /// - /// This correctly updates the provisional cache if there is a cycle. - #[instrument(level = "debug", skip(self, tcx, inspect), ret)] - fn try_push_stack( + /// Given some goal which is proven via the `prove_goal` closure, this + /// handles caching, overflow, and coinductive cycles. + pub(super) fn with_new_goal( &mut self, tcx: TyCtxt<'tcx>, input: CanonicalInput<'tcx>, - available_depth: Limit, inspect: &mut ProofTreeBuilder<'tcx>, - ) -> Result<(), QueryResult<'tcx>> { - // Look at the provisional cache to check for cycles. + mut prove_goal: impl FnMut(&mut Self, &mut ProofTreeBuilder<'tcx>) -> QueryResult<'tcx>, + ) -> QueryResult<'tcx> { + // Check for overflow. + let Some(available_depth) = Self::allowed_depth_for_nested(tcx, &self.stack) else { + if let Some(last) = self.stack.raw.last_mut() { + last.encountered_overflow = true; + } + return Self::response_no_constraints(tcx, input, Certainty::OVERFLOW); + }; + + // Try to fetch the goal from the global cache. + if inspect.use_global_cache() { + if let Some(CacheData { result, reached_depth, encountered_overflow }) = + self.global_cache(tcx).get( + tcx, + input, + |cycle_participants| { + self.stack.iter().any(|entry| cycle_participants.contains(&entry.input)) + }, + available_depth, + ) + { + self.on_cache_hit(reached_depth, encountered_overflow); + return result; + } + } + + // Look at the provisional cache to detect cycles. let cache = &mut self.provisional_cache; match cache.lookup_table.entry(input) { - // No entry, simply push this goal on the stack. + // No entry, we push this goal on the stack and try to prove it. Entry::Vacant(v) => { let depth = self.stack.next_index(); let entry = StackEntry { @@ -194,13 +219,12 @@ impl<'tcx> SearchGraph<'tcx> { let response = Self::response_no_constraints(tcx, input, Certainty::Yes); let entry_index = cache.entries.push(ProvisionalEntry { response, depth, input }); v.insert(entry_index); - Ok(()) } // We have a nested goal which relies on a goal `root` deeper in the stack. // - // We first store that we may have to rerun `evaluate_goal` for `root` in case the - // provisional response is not equal to the final response. We also update the depth - // of all goals which recursively depend on our current goal to depend on `root` + // We first store that we may have to reprove `root` in case the provisional + // response is not equal to the final response. We also update the depth of all + // goals which recursively depend on our current goal to depend on `root` // instead. // // Finally we can return either the provisional response for that goal if we have a @@ -209,7 +233,6 @@ impl<'tcx> SearchGraph<'tcx> { inspect.cache_hit(CacheHit::Provisional); let entry_index = *entry_index.get(); - let stack_depth = cache.depth(entry_index); debug!("encountered cycle with depth {stack_depth:?}"); @@ -233,112 +256,55 @@ impl<'tcx> SearchGraph<'tcx> { // If we're in a coinductive cycle, we have to retry proving the current goal // until we reach a fixpoint. self.stack[stack_depth].has_been_used = true; - Err(cache.provisional_result(entry_index)) + return cache.provisional_result(entry_index); } else { - Err(Self::response_no_constraints(tcx, input, Certainty::OVERFLOW)) + return Self::response_no_constraints(tcx, input, Certainty::OVERFLOW); } } } - } - - /// We cannot simply store the result of [super::EvalCtxt::compute_goal] as we have to deal with - /// coinductive cycles. - /// - /// When we encounter a coinductive cycle, we have to prove the final result of that cycle - /// while we are still computing that result. Because of this we continuously recompute the - /// cycle until the result of the previous iteration is equal to the final result, at which - /// point we are done. - /// - /// This function returns `true` if we were able to finalize the goal and `false` if it has - /// updated the provisional cache and we have to recompute the current goal. - /// - /// FIXME: Refer to the rustc-dev-guide entry once it exists. - #[instrument(level = "debug", skip(self, actual_input), ret)] - fn try_finalize_goal( - &mut self, - actual_input: CanonicalInput<'tcx>, - response: QueryResult<'tcx>, - ) -> Result, ()> { - let stack_entry = self.pop_stack(); - assert_eq!(stack_entry.input, actual_input); - - let cache = &mut self.provisional_cache; - let provisional_entry_index = *cache.lookup_table.get(&stack_entry.input).unwrap(); - let provisional_entry = &mut cache.entries[provisional_entry_index]; - // We eagerly update the response in the cache here. If we have to reevaluate - // this goal we use the new response when hitting a cycle, and we definitely - // want to access the final response whenever we look at the cache. - let prev_response = mem::replace(&mut provisional_entry.response, response); - - // Was the current goal the root of a cycle and was the provisional response - // different from the final one. - if stack_entry.has_been_used && prev_response != response { - // If so, remove all entries whose result depends on this goal - // from the provisional cache... - // - // That's not completely correct, as a nested goal can also - // depend on a goal which is lower in the stack so it doesn't - // actually depend on the current goal. This should be fairly - // rare and is hopefully not relevant for performance. - #[allow(rustc::potential_query_instability)] - cache.lookup_table.retain(|_key, index| *index <= provisional_entry_index); - cache.entries.truncate(provisional_entry_index.index() + 1); - - // ...and finally push our goal back on the stack and reevaluate it. - self.stack.push(StackEntry { has_been_used: false, ..stack_entry }); - Err(()) - } else { - Ok(stack_entry) - } - } - - pub(super) fn with_new_goal( - &mut self, - tcx: TyCtxt<'tcx>, - input: CanonicalInput<'tcx>, - inspect: &mut ProofTreeBuilder<'tcx>, - mut loop_body: impl FnMut(&mut Self, &mut ProofTreeBuilder<'tcx>) -> QueryResult<'tcx>, - ) -> QueryResult<'tcx> { - let Some(available_depth) = Self::allowed_depth_for_nested(tcx, &self.stack) else { - if let Some(last) = self.stack.raw.last_mut() { - last.encountered_overflow = true; - } - return Self::response_no_constraints(tcx, input, Certainty::OVERFLOW); - }; - - if inspect.use_global_cache() { - if let Some(CacheData { result, reached_depth, encountered_overflow }) = - self.global_cache(tcx).get( - tcx, - input, - |cycle_participants| { - self.stack.iter().any(|entry| cycle_participants.contains(&entry.input)) - }, - available_depth, - ) - { - self.on_cache_hit(reached_depth, encountered_overflow); - return result; - } - } - - match self.try_push_stack(tcx, input, available_depth, inspect) { - Ok(()) => {} - // Our goal is already on the stack, eager return. - Err(response) => return response, - } // This is for global caching, so we properly track query dependencies. - // Everything that affects the `Result` should be performed within this + // Everything that affects the `result` should be performed within this // `with_anon_task` closure. let ((final_entry, result), dep_node) = tcx.dep_graph.with_anon_task(tcx, DepKind::TraitSelect, || { - // We run our goal in a loop to handle coinductive cycles. If we fail to reach a - // fipoint we return overflow. + // When we encounter a coinductive cycle, we have to fetch the + // result of that cycle while we are still computing it. Because + // of this we continuously recompute the cycle until the result + // of the previous iteration is equal to the final result, at which + // point we are done. for _ in 0..self.local_overflow_limit() { - let result = loop_body(self, inspect); - if let Ok(final_entry) = self.try_finalize_goal(input, result) { - return (final_entry, result); + let response = prove_goal(self, inspect); + + // Check whether the current goal is the root of a cycle and whether + // we have to rerun because its provisional result differed from the + // final result. + // + // Also update the response for this goal stored in the provisional + // cache. + let stack_entry = self.pop_stack(); + debug_assert_eq!(stack_entry.input, input); + let cache = &mut self.provisional_cache; + let provisional_entry_index = + *cache.lookup_table.get(&stack_entry.input).unwrap(); + let provisional_entry = &mut cache.entries[provisional_entry_index]; + let prev_response = mem::replace(&mut provisional_entry.response, response); + if stack_entry.has_been_used && prev_response != response { + // If so, remove all entries whose result depends on this goal + // from the provisional cache... + // + // That's not completely correct, as a nested goal can also + // depend on a goal which is lower in the stack so it doesn't + // actually depend on the current goal. This should be fairly + // rare and is hopefully not relevant for performance. + #[allow(rustc::potential_query_instability)] + cache.lookup_table.retain(|_key, index| *index <= provisional_entry_index); + cache.entries.truncate(provisional_entry_index.index() + 1); + + // ...and finally push our goal back on the stack and reevaluate it. + self.stack.push(StackEntry { has_been_used: false, ..stack_entry }); + } else { + return (stack_entry, response); } } @@ -348,17 +314,16 @@ impl<'tcx> SearchGraph<'tcx> { (current_entry, result) }); - let cache = &mut self.provisional_cache; - let provisional_entry_index = *cache.lookup_table.get(&input).unwrap(); - let provisional_entry = &mut cache.entries[provisional_entry_index]; - let depth = provisional_entry.depth; - - // We're now done with this goal. In case this goal is involved in a cycle + // We're now done with this goal. In case this goal is involved in a larger cycle // do not remove it from the provisional cache and do not add it to the global // cache. // // It is not possible for any nested goal to depend on something deeper on the // stack, as this would have also updated the depth of the current goal. + let cache = &mut self.provisional_cache; + let provisional_entry_index = *cache.lookup_table.get(&input).unwrap(); + let provisional_entry = &mut cache.entries[provisional_entry_index]; + let depth = provisional_entry.depth; if depth == self.stack.next_index() { for (i, entry) in cache.entries.drain_enumerated(provisional_entry_index.index()..) { let actual_index = cache.lookup_table.remove(&entry.input); From 3635b48973ee17951c81769e4a62b0a4891a2501 Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 22 Jul 2023 10:13:49 +0800 Subject: [PATCH 30/56] Fix wrong span for trait selection failure error reporting --- .../src/traits/error_reporting/mod.rs | 8 ++++++ tests/ui/dst/issue-113447.fixed | 25 +++++++++++++++++++ tests/ui/dst/issue-113447.rs | 25 +++++++++++++++++++ tests/ui/dst/issue-113447.stderr | 25 +++++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 tests/ui/dst/issue-113447.fixed create mode 100644 tests/ui/dst/issue-113447.rs create mode 100644 tests/ui/dst/issue-113447.stderr 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 eae13eb6302..4d85e2b6089 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -2987,6 +2987,14 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> { unsatisfied_const: bool, ) { let body_def_id = obligation.cause.body_id; + let span = if let ObligationCauseCode::BinOp { rhs_span: Some(rhs_span), .. } = + obligation.cause.code() + { + *rhs_span + } else { + span + }; + // Try to report a help message if is_fn_trait && let Ok((implemented_kind, params)) = self.type_implements_fn_trait( diff --git a/tests/ui/dst/issue-113447.fixed b/tests/ui/dst/issue-113447.fixed new file mode 100644 index 00000000000..536f680f697 --- /dev/null +++ b/tests/ui/dst/issue-113447.fixed @@ -0,0 +1,25 @@ +// run-rustfix + +pub struct Bytes; + +impl Bytes { + pub fn as_slice(&self) -> &[u8] { + todo!() + } +} + +impl PartialEq<[u8]> for Bytes { + fn eq(&self, other: &[u8]) -> bool { + self.as_slice() == other + } +} + +impl PartialEq for &[u8] { + fn eq(&self, other: &Bytes) -> bool { + *other == **self + } +} + +fn main() { + let _ = &[0u8] == &[0xAA][..]; //~ ERROR can't compare `&[u8; 1]` with `[{integer}; 1]` +} diff --git a/tests/ui/dst/issue-113447.rs b/tests/ui/dst/issue-113447.rs new file mode 100644 index 00000000000..c10a4f2ff8e --- /dev/null +++ b/tests/ui/dst/issue-113447.rs @@ -0,0 +1,25 @@ +// run-rustfix + +pub struct Bytes; + +impl Bytes { + pub fn as_slice(&self) -> &[u8] { + todo!() + } +} + +impl PartialEq<[u8]> for Bytes { + fn eq(&self, other: &[u8]) -> bool { + self.as_slice() == other + } +} + +impl PartialEq for &[u8] { + fn eq(&self, other: &Bytes) -> bool { + *other == **self + } +} + +fn main() { + let _ = &[0u8] == [0xAA]; //~ ERROR can't compare `&[u8; 1]` with `[{integer}; 1]` +} diff --git a/tests/ui/dst/issue-113447.stderr b/tests/ui/dst/issue-113447.stderr new file mode 100644 index 00000000000..240553a675b --- /dev/null +++ b/tests/ui/dst/issue-113447.stderr @@ -0,0 +1,25 @@ +error[E0277]: can't compare `&[u8; 1]` with `[{integer}; 1]` + --> $DIR/issue-113447.rs:24:20 + | +LL | let _ = &[0u8] == [0xAA]; + | ^^ no implementation for `&[u8; 1] == [{integer}; 1]` + | + = help: the trait `PartialEq<[{integer}; 1]>` is not implemented for `&[u8; 1]` + = help: the following other types implement trait `PartialEq`: + <[A; N] as PartialEq<[B; N]>> + <[A; N] as PartialEq<[B]>> + <[A; N] as PartialEq<&[B]>> + <[A; N] as PartialEq<&mut [B]>> + <[T] as PartialEq>> + <[A] as PartialEq<[B]>> + <[B] as PartialEq<[A; N]>> + <&[u8] as PartialEq> + and 4 others +help: convert the array to a `&[u8]` slice instead + | +LL | let _ = &[0u8] == &[0xAA][..]; + | + ++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. From cde8f06503d06d254aa4191438588ba9b2a36b77 Mon Sep 17 00:00:00 2001 From: yukang Date: Fri, 4 Aug 2023 00:14:13 +0800 Subject: [PATCH 31/56] enable suggest convert to slice for binary operation --- .../src/traits/error_reporting/suggestions.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 46c31c77613..bde72c691dc 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -3953,9 +3953,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { candidate_impls: &[ImplCandidate<'tcx>], span: Span, ) { - // We can only suggest the slice coersion for function arguments since the suggestion - // would make no sense in turbofish or call - let ObligationCauseCode::FunctionArgumentObligation { .. } = obligation.cause.code() else { + // We can only suggest the slice coersion for function and binary operation arguments, + // since the suggestion would make no sense in turbofish or call + let (ObligationCauseCode::BinOp { .. } + | ObligationCauseCode::FunctionArgumentObligation { .. }) = obligation.cause.code() + else { return; }; From ea3b49f2f1ec0a11a4d17572d5a9160eb2425851 Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Thu, 3 Aug 2023 18:40:35 +0200 Subject: [PATCH 32/56] Handle non-utf8 rpaths (fix FIXME) --- compiler/rustc_codegen_ssa/src/back/rpath.rs | 36 +++++++++++-------- .../rustc_codegen_ssa/src/back/rpath/tests.rs | 35 +++++++++--------- 2 files changed, 39 insertions(+), 32 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/rpath.rs b/compiler/rustc_codegen_ssa/src/back/rpath.rs index 18268622341..ebf04e7a399 100644 --- a/compiler/rustc_codegen_ssa/src/back/rpath.rs +++ b/compiler/rustc_codegen_ssa/src/back/rpath.rs @@ -1,6 +1,7 @@ use pathdiff::diff_paths; use rustc_data_structures::fx::FxHashSet; use std::env; +use std::ffi::OsString; use std::fs; use std::path::{Path, PathBuf}; @@ -12,7 +13,7 @@ pub struct RPathConfig<'a> { pub linker_is_gnu: bool, } -pub fn get_rpath_flags(config: &mut RPathConfig<'_>) -> Vec { +pub fn get_rpath_flags(config: &mut RPathConfig<'_>) -> Vec { // No rpath on windows if !config.has_rpath { return Vec::new(); @@ -21,36 +22,38 @@ pub fn get_rpath_flags(config: &mut RPathConfig<'_>) -> Vec { debug!("preparing the RPATH!"); let rpaths = get_rpaths(config); - let mut flags = rpaths_to_flags(&rpaths); + let mut flags = rpaths_to_flags(rpaths); if config.linker_is_gnu { // Use DT_RUNPATH instead of DT_RPATH if available - flags.push("-Wl,--enable-new-dtags".to_owned()); + flags.push("-Wl,--enable-new-dtags".into()); // Set DF_ORIGIN for substitute $ORIGIN - flags.push("-Wl,-z,origin".to_owned()); + flags.push("-Wl,-z,origin".into()); } flags } -fn rpaths_to_flags(rpaths: &[String]) -> Vec { +fn rpaths_to_flags(rpaths: Vec) -> Vec { let mut ret = Vec::with_capacity(rpaths.len()); // the minimum needed capacity for rpath in rpaths { - if rpath.contains(',') { + if rpath.to_string_lossy().contains(',') { ret.push("-Wl,-rpath".into()); ret.push("-Xlinker".into()); - ret.push(rpath.clone()); + ret.push(rpath); } else { - ret.push(format!("-Wl,-rpath,{}", &(*rpath))); + let mut single_arg = OsString::from("-Wl,-rpath,"); + single_arg.push(rpath); + ret.push(single_arg); } } ret } -fn get_rpaths(config: &mut RPathConfig<'_>) -> Vec { +fn get_rpaths(config: &mut RPathConfig<'_>) -> Vec { debug!("output: {:?}", config.out_filename.display()); debug!("libs:"); for libpath in config.libs { @@ -64,18 +67,18 @@ fn get_rpaths(config: &mut RPathConfig<'_>) -> Vec { debug!("rpaths:"); for rpath in &rpaths { - debug!(" {}", rpath); + debug!(" {:?}", rpath); } // Remove duplicates minimize_rpaths(&rpaths) } -fn get_rpaths_relative_to_output(config: &mut RPathConfig<'_>) -> Vec { +fn get_rpaths_relative_to_output(config: &mut RPathConfig<'_>) -> Vec { config.libs.iter().map(|a| get_rpath_relative_to_output(config, a)).collect() } -fn get_rpath_relative_to_output(config: &mut RPathConfig<'_>, lib: &Path) -> String { +fn get_rpath_relative_to_output(config: &mut RPathConfig<'_>, lib: &Path) -> OsString { // Mac doesn't appear to support $ORIGIN let prefix = if config.is_like_osx { "@loader_path" } else { "$ORIGIN" }; @@ -87,8 +90,11 @@ fn get_rpath_relative_to_output(config: &mut RPathConfig<'_>, lib: &Path) -> Str let output = fs::canonicalize(&output).unwrap_or(output); let relative = path_relative_from(&lib, &output) .unwrap_or_else(|| panic!("couldn't create relative path from {output:?} to {lib:?}")); - // FIXME (#9639): This needs to handle non-utf8 paths - format!("{}/{}", prefix, relative.to_str().expect("non-utf8 component in path")) + + let mut rpath = OsString::from(prefix); + rpath.push("/"); + rpath.push(relative); + rpath } // This routine is adapted from the *old* Path's `path_relative_from` @@ -99,7 +105,7 @@ fn path_relative_from(path: &Path, base: &Path) -> Option { diff_paths(path, base) } -fn minimize_rpaths(rpaths: &[String]) -> Vec { +fn minimize_rpaths(rpaths: &[OsString]) -> Vec { let mut set = FxHashSet::default(); let mut minimized = Vec::new(); for rpath in rpaths { diff --git a/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs b/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs index 604f19144a6..ac2e54072c4 100644 --- a/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs +++ b/compiler/rustc_codegen_ssa/src/back/rpath/tests.rs @@ -1,32 +1,33 @@ use super::RPathConfig; use super::{get_rpath_relative_to_output, minimize_rpaths, rpaths_to_flags}; +use std::ffi::OsString; use std::path::{Path, PathBuf}; #[test] fn test_rpaths_to_flags() { - let flags = rpaths_to_flags(&["path1".to_string(), "path2".to_string()]); + let flags = rpaths_to_flags(vec!["path1".into(), "path2".into()]); assert_eq!(flags, ["-Wl,-rpath,path1", "-Wl,-rpath,path2"]); } #[test] fn test_minimize1() { - let res = minimize_rpaths(&["rpath1".to_string(), "rpath2".to_string(), "rpath1".to_string()]); + let res = minimize_rpaths(&["rpath1".into(), "rpath2".into(), "rpath1".into()]); assert!(res == ["rpath1", "rpath2",]); } #[test] fn test_minimize2() { let res = minimize_rpaths(&[ - "1a".to_string(), - "2".to_string(), - "2".to_string(), - "1a".to_string(), - "4a".to_string(), - "1a".to_string(), - "2".to_string(), - "3".to_string(), - "4a".to_string(), - "3".to_string(), + "1a".into(), + "2".into(), + "2".into(), + "1a".into(), + "4a".into(), + "1a".into(), + "2".into(), + "3".into(), + "4a".into(), + "3".into(), ]); assert!(res == ["1a", "2", "4a", "3",]); } @@ -58,15 +59,15 @@ fn test_rpath_relative() { #[test] fn test_xlinker() { - let args = rpaths_to_flags(&["a/normal/path".to_string(), "a,comma,path".to_string()]); + let args = rpaths_to_flags(vec!["a/normal/path".into(), "a,comma,path".into()]); assert_eq!( args, vec![ - "-Wl,-rpath,a/normal/path".to_string(), - "-Wl,-rpath".to_string(), - "-Xlinker".to_string(), - "a,comma,path".to_string() + OsString::from("-Wl,-rpath,a/normal/path"), + OsString::from("-Wl,-rpath"), + OsString::from("-Xlinker"), + OsString::from("a,comma,path") ] ); } From 1bb6ae5874730933188fe4be56b3a2f3d5a66962 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 26 Jul 2023 23:54:55 +0000 Subject: [PATCH 33/56] Rework upcasting --- compiler/rustc_infer/src/infer/at.rs | 28 ++++ .../src/infer/error_reporting/mod.rs | 8 + compiler/rustc_infer/src/infer/mod.rs | 2 + .../rustc_middle/src/traits/solve/inspect.rs | 13 +- .../src/traits/solve/inspect/format.rs | 7 +- compiler/rustc_middle/src/ty/print/pretty.rs | 3 +- .../src/solve/trait_goals.rs | 125 +++++++++++----- .../src/traits/select/confirmation.rs | 139 ++++++++++-------- .../trait-upcasting/fewer-associated.rs | 25 ++++ .../illegal-upcast-from-impl.current.stderr | 14 ++ .../illegal-upcast-from-impl.next.stderr | 14 ++ .../illegal-upcast-from-impl.rs | 23 +++ 12 files changed, 297 insertions(+), 104 deletions(-) create mode 100644 tests/ui/traits/trait-upcasting/fewer-associated.rs create mode 100644 tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.current.stderr create mode 100644 tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.next.stderr create mode 100644 tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.rs diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs index 433735e827b..6d5db3336cf 100644 --- a/compiler/rustc_infer/src/infer/at.rs +++ b/compiler/rustc_infer/src/infer/at.rs @@ -481,3 +481,31 @@ impl<'tcx> ToTrace<'tcx> for ty::FnSig<'tcx> { TypeTrace { cause: cause.clone(), values: Sigs(ExpectedFound::new(a_is_expected, a, b)) } } } + +impl<'tcx> ToTrace<'tcx> for ty::PolyExistentialTraitRef<'tcx> { + fn to_trace( + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: Self, + b: Self, + ) -> TypeTrace<'tcx> { + TypeTrace { + cause: cause.clone(), + values: ExistentialTraitRef(ExpectedFound::new(a_is_expected, a, b)), + } + } +} + +impl<'tcx> ToTrace<'tcx> for ty::PolyExistentialProjection<'tcx> { + fn to_trace( + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: Self, + b: Self, + ) -> TypeTrace<'tcx> { + TypeTrace { + cause: cause.clone(), + values: ExistentialProjection(ExpectedFound::new(a_is_expected, a, b)), + } + } +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 25077f9bb97..2b420bd869b 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1635,6 +1635,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { (false, Mismatch::Fixed(self.tcx.def_descr(expected.def_id))) } ValuePairs::Regions(_) => (false, Mismatch::Fixed("lifetime")), + ValuePairs::ExistentialTraitRef(_) => { + (false, Mismatch::Fixed("existential trait ref")) + } + ValuePairs::ExistentialProjection(_) => { + (false, Mismatch::Fixed("existential projection")) + } }; let Some(vals) = self.values_str(values) else { // Derived error. Cancel the emitter. @@ -2139,6 +2145,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { infer::Regions(exp_found) => self.expected_found_str(exp_found), infer::Terms(exp_found) => self.expected_found_str_term(exp_found), infer::Aliases(exp_found) => self.expected_found_str(exp_found), + infer::ExistentialTraitRef(exp_found) => self.expected_found_str(exp_found), + infer::ExistentialProjection(exp_found) => self.expected_found_str(exp_found), infer::TraitRefs(exp_found) => { let pretty_exp_found = ty::error::ExpectedFound { expected: exp_found.expected.print_only_trait_path(), diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index a73e84f94c4..aaabf1482e2 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -374,6 +374,8 @@ pub enum ValuePairs<'tcx> { TraitRefs(ExpectedFound>), PolyTraitRefs(ExpectedFound>), Sigs(ExpectedFound>), + ExistentialTraitRef(ExpectedFound>), + ExistentialProjection(ExpectedFound>), } impl<'tcx> ValuePairs<'tcx> { diff --git a/compiler/rustc_middle/src/traits/solve/inspect.rs b/compiler/rustc_middle/src/traits/solve/inspect.rs index e793f481995..4e2af3816ac 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect.rs @@ -73,12 +73,15 @@ pub struct GoalCandidate<'tcx> { pub enum CandidateKind<'tcx> { /// Probe entered when normalizing the self ty during candidate assembly NormalizedSelfTyAssembly, - DynUpcastingAssembly, /// A normal candidate for proving a goal - Candidate { - name: String, - result: QueryResult<'tcx>, - }, + Candidate { name: String, result: QueryResult<'tcx> }, + /// Used in the probe that wraps normalizing the non-self type for the unsize + /// trait, which is also structurally matched on. + UnsizeAssembly, + /// During upcasting from some source object to target object type, used to + /// do a probe to find out what projection type(s) may be used to prove that + /// the source type upholds all of the target type's object bounds. + UpcastProbe, } impl Debug for GoalCandidate<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/compiler/rustc_middle/src/traits/solve/inspect/format.rs b/compiler/rustc_middle/src/traits/solve/inspect/format.rs index f1e567e9bf8..8759fecb05a 100644 --- a/compiler/rustc_middle/src/traits/solve/inspect/format.rs +++ b/compiler/rustc_middle/src/traits/solve/inspect/format.rs @@ -100,8 +100,11 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> { CandidateKind::NormalizedSelfTyAssembly => { writeln!(self.f, "NORMALIZING SELF TY FOR ASSEMBLY:") } - CandidateKind::DynUpcastingAssembly => { - writeln!(self.f, "ASSEMBLING CANDIDATES FOR DYN UPCASTING:") + CandidateKind::UnsizeAssembly => { + writeln!(self.f, "ASSEMBLING CANDIDATES FOR UNSIZING:") + } + CandidateKind::UpcastProbe => { + writeln!(self.f, "PROBING FOR PROJECTION COMPATIBILITY FOR UPCASTING:") } CandidateKind::Candidate { name, result } => { writeln!(self.f, "CANDIDATE {name}: {result:?}") diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 6e68022031a..d3fd49150ba 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2734,8 +2734,9 @@ forward_display_to_print! { // HACK(eddyb) these are exhaustive instead of generic, // because `for<'tcx>` isn't possible yet. ty::PolyExistentialPredicate<'tcx>, + ty::PolyExistentialProjection<'tcx>, + ty::PolyExistentialTraitRef<'tcx>, ty::Binder<'tcx, ty::TraitRef<'tcx>>, - ty::Binder<'tcx, ty::ExistentialTraitRef<'tcx>>, ty::Binder<'tcx, TraitRefPrintOnlyTraitPath<'tcx>>, ty::Binder<'tcx, TraitRefPrintOnlyTraitName<'tcx>>, ty::Binder<'tcx, ty::FnSig<'tcx>>, diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 14a5b9656e5..41565fe5dd9 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -444,7 +444,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> { Err(NoSolution) => vec![], }; - ecx.probe(|_| CandidateKind::DynUpcastingAssembly).enter(|ecx| { + ecx.probe(|_| CandidateKind::UnsizeAssembly).enter(|ecx| { let a_ty = goal.predicate.self_ty(); // We need to normalize the b_ty since it's matched structurally // in the other functions below. @@ -526,7 +526,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { b_region: ty::Region<'tcx>, ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> { let tcx = self.tcx(); - let Goal { predicate: (a_ty, b_ty), .. } = goal; + let Goal { predicate: (a_ty, _b_ty), .. } = goal; // All of a's auto traits need to be in b's auto traits. let auto_traits_compatible = @@ -535,51 +535,30 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { return vec![]; } - // Try to match `a_ty` against `b_ty`, replacing `a_ty`'s principal trait ref with - // the supertrait principal and subtyping the types. - let unsize_dyn_to_principal = - |ecx: &mut Self, principal: Option>| { - ecx.probe_candidate("upcast dyn to principle").enter( - |ecx| -> Result<_, NoSolution> { - // Require that all of the trait predicates from A match B, except for - // the auto traits. We do this by constructing a new A type with B's - // auto traits, and equating these types. - let new_a_data = principal - .into_iter() - .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait)) - .chain(a_data.iter().filter(|a| { - matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_)) - })) - .chain( - b_data - .auto_traits() - .map(ty::ExistentialPredicate::AutoTrait) - .map(ty::Binder::dummy), - ); - let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data); - let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn); - - // We also require that A's lifetime outlives B's lifetime. - ecx.eq(goal.param_env, new_a_ty, b_ty)?; - ecx.add_goal(goal.with(tcx, ty::OutlivesPredicate(a_region, b_region))); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }, - ) - }; - let mut responses = vec![]; // If the principal def ids match (or are both none), then we're not doing // trait upcasting. We're just removing auto traits (or shortening the lifetime). if a_data.principal_def_id() == b_data.principal_def_id() { - if let Ok(resp) = unsize_dyn_to_principal(self, a_data.principal()) { + if let Ok(resp) = self.consider_builtin_upcast_to_principal( + goal, + a_data, + a_region, + b_data, + b_region, + a_data.principal(), + ) { responses.push((resp, BuiltinImplSource::Misc)); } } else if let Some(a_principal) = a_data.principal() { self.walk_vtable( a_principal.with_self_ty(tcx, a_ty), |ecx, new_a_principal, _, vtable_vptr_slot| { - if let Ok(resp) = unsize_dyn_to_principal( - ecx, + if let Ok(resp) = ecx.consider_builtin_upcast_to_principal( + goal, + a_data, + a_region, + b_data, + b_region, Some(new_a_principal.map_bound(|trait_ref| { ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) })), @@ -631,6 +610,78 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } + fn consider_builtin_upcast_to_principal( + &mut self, + goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, + a_data: &'tcx ty::List>, + a_region: ty::Region<'tcx>, + b_data: &'tcx ty::List>, + b_region: ty::Region<'tcx>, + upcast_principal: Option>, + ) -> QueryResult<'tcx> { + let param_env = goal.param_env; + + // More than one projection in a_ty's bounds may match the projection + // in b_ty's bound. Use this to first determine *which* apply without + // having any inference side-effects. We process obligations because + // unification may initially succeed due to deferred projection equality. + let projection_may_match = |ecx: &mut Self, source_projection, target_projection| { + ecx.probe(|_| CandidateKind::UpcastProbe) + .enter(|ecx| -> Result<(), NoSolution> { + ecx.eq(param_env, source_projection, target_projection)?; + let _ = ecx.try_evaluate_added_goals()?; + Ok(()) + }) + .is_ok() + }; + + for bound in b_data { + match bound.skip_binder() { + // Check that a's supertrait (upcast_principal) is compatible + // with the target (b_ty). + ty::ExistentialPredicate::Trait(target_principal) => { + self.eq(param_env, upcast_principal.unwrap(), bound.rebind(target_principal))?; + } + // Check that b_ty's projection is satisfied by exactly one of + // a_ty's projections. First, we look through the list to see if + // any match. If not, error. Then, if *more* than one matches, we + // return ambiguity. Otherwise, if exactly one matches, equate + // it with b_ty's projection. + ty::ExistentialPredicate::Projection(target_projection) => { + let target_projection = bound.rebind(target_projection); + let mut matching_projections = + a_data.projection_bounds().filter(|source_projection| { + projection_may_match(self, *source_projection, target_projection) + }); + let Some(source_projection) = matching_projections.next() else { + return Err(NoSolution); + }; + if matching_projections.next().is_some() { + return self.evaluate_added_goals_and_make_canonical_response( + Certainty::AMBIGUOUS, + ); + } + self.eq(param_env, source_projection, target_projection)?; + } + // Check that b_ty's auto traits are present in a_ty's bounds. + ty::ExistentialPredicate::AutoTrait(def_id) => { + if !a_data.auto_traits().any(|source_def_id| source_def_id == def_id) { + return Err(NoSolution); + } + } + } + } + + // Also require that a_ty's lifetime outlives b_ty's lifetime. + self.add_goal(Goal::new( + self.tcx(), + param_env, + ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), + )); + + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + /// We have the following builtin impls for arrays: /// ```ignore (builtin impl example) /// impl Unsize<[T]> for [T; N] {} diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index bf09681c66d..77304984402 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -879,68 +879,89 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // `assemble_candidates_for_unsizing` should ensure there are no late-bound // regions here. See the comment there for more details. - let source = self.infcx.shallow_resolve(obligation.self_ty().no_bound_vars().unwrap()); - let target = obligation.predicate.skip_binder().trait_ref.args.type_at(1); - let target = self.infcx.shallow_resolve(target); + let predicate = obligation.predicate.no_bound_vars().unwrap(); + let a_ty = self.infcx.shallow_resolve(predicate.self_ty()); + let b_ty = self.infcx.shallow_resolve(predicate.trait_ref.args.type_at(1)); - debug!(?source, ?target, "confirm_trait_upcasting_unsize_candidate"); + let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { bug!() }; + let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { bug!() }; + + let source_principal = a_data.principal().unwrap().with_self_ty(tcx, a_ty); + let upcast_principal = util::supertraits(tcx, source_principal).nth(idx).unwrap(); let mut nested = vec![]; - let source_trait_ref; - let upcast_trait_ref; - match (source.kind(), target.kind()) { - // TraitA+Kx+'a -> TraitB+Ky+'b (trait upcasting coercion). - ( - &ty::Dynamic(ref data_a, r_a, repr_a @ ty::Dyn), - &ty::Dynamic(ref data_b, r_b, ty::Dyn), - ) => { - // See `assemble_candidates_for_unsizing` for more info. - // We already checked the compatibility of auto traits within `assemble_candidates_for_unsizing`. - let principal_a = data_a.principal().unwrap(); - source_trait_ref = principal_a.with_self_ty(tcx, source); - upcast_trait_ref = util::supertraits(tcx, source_trait_ref).nth(idx).unwrap(); - assert_eq!(data_b.principal_def_id(), Some(upcast_trait_ref.def_id())); - let existential_predicate = upcast_trait_ref.map_bound(|trait_ref| { - ty::ExistentialPredicate::Trait(ty::ExistentialTraitRef::erase_self_ty( - tcx, trait_ref, - )) - }); - let iter = Some(existential_predicate) - .into_iter() - .chain( - data_a - .projection_bounds() - .map(|b| b.map_bound(ty::ExistentialPredicate::Projection)), - ) - .chain( - data_b - .auto_traits() - .map(ty::ExistentialPredicate::AutoTrait) - .map(ty::Binder::dummy), + for bound in b_data { + match bound.skip_binder() { + // Check that a's supertrait (upcast_principal) is compatible + // with the target (b_ty). + ty::ExistentialPredicate::Trait(target_principal) => { + nested.extend( + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup( + DefineOpaqueTypes::No, + upcast_principal.map_bound(|trait_ref| { + ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) + }), + bound.rebind(target_principal), + ) + .map_err(|_| SelectionError::Unimplemented)? + .into_obligations(), ); - let existential_predicates = tcx.mk_poly_existential_predicates_from_iter(iter); - let source_trait = Ty::new_dynamic(tcx, existential_predicates, r_b, repr_a); - - // Require that the traits involved in this upcast are **equal**; - // only the **lifetime bound** is changed. - let InferOk { obligations, .. } = self - .infcx - .at(&obligation.cause, obligation.param_env) - .sup(DefineOpaqueTypes::No, target, source_trait) - .map_err(|_| Unimplemented)?; - nested.extend(obligations); - - let outlives = ty::OutlivesPredicate(r_a, r_b); - nested.push(Obligation::with_depth( - tcx, - obligation.cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - obligation.predicate.rebind(outlives), - )); + } + // Check that b_ty's projection is satisfied by exactly one of + // a_ty's projections. First, we look through the list to see if + // any match. If not, error. Then, if *more* than one matches, we + // return ambiguity. Otherwise, if exactly one matches, equate + // it with b_ty's projection. + ty::ExistentialPredicate::Projection(target_projection) => { + let target_projection = bound.rebind(target_projection); + let mut matching_projections = + a_data.projection_bounds().filter(|source_projection| { + // Eager normalization means that we can just use can_eq + // here instead of equating and processing obligations. + self.infcx.can_eq( + obligation.param_env, + *source_projection, + target_projection, + ) + }); + let Some(source_projection) = matching_projections.next() else { + return Err(SelectionError::Unimplemented); + }; + if matching_projections.next().is_some() { + // This is incomplete but I don't care. We should never + // have more than one projection that ever applies with + // eager norm and actually implementable traits, since + // you can't have two supertraits like: + // `trait A: B + B` + return Err(SelectionError::Unimplemented); + } + nested.extend( + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup(DefineOpaqueTypes::No, source_projection, target_projection) + .map_err(|_| SelectionError::Unimplemented)? + .into_obligations(), + ); + } + // Check that b_ty's auto trait is present in a_ty's bounds. + ty::ExistentialPredicate::AutoTrait(def_id) => { + if !a_data.auto_traits().any(|source_def_id| source_def_id == def_id) { + return Err(SelectionError::Unimplemented); + } + } } - _ => bug!(), - }; + } + + // Also require that a_ty's lifetime outlives b_ty's lifetime. + nested.push(Obligation::with_depth( + tcx, + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), + )); let vtable_segment_callback = { let mut vptr_offset = 0; @@ -951,7 +972,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { vptr_offset += count_own_vtable_entries(tcx, trait_ref); - if trait_ref == upcast_trait_ref { + if trait_ref == upcast_principal { if emit_vptr { return ControlFlow::Break(Some(vptr_offset)); } else { @@ -969,7 +990,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; let vtable_vptr_slot = - prepare_vtable_segments(tcx, source_trait_ref, vtable_segment_callback).unwrap(); + prepare_vtable_segments(tcx, source_principal, vtable_segment_callback).unwrap(); Ok(ImplSource::Builtin(BuiltinImplSource::TraitUpcasting { vtable_vptr_slot }, nested)) } diff --git a/tests/ui/traits/trait-upcasting/fewer-associated.rs b/tests/ui/traits/trait-upcasting/fewer-associated.rs new file mode 100644 index 00000000000..8228eea2681 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/fewer-associated.rs @@ -0,0 +1,25 @@ +// check-pass +// issue: 114035 +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next + +#![feature(trait_upcasting)] + +trait A: B { + type Assoc; +} + +trait B {} + +fn upcast(a: &dyn A) -> &dyn B { + a +} + +// Make sure that we can drop the existential projection `A::Assoc = i32` +// when upcasting `dyn A` to `dyn B`. Before, we used some +// complicated algorithm which required rebuilding a new object type with +// different bounds in order to test that an upcast was valid, but this +// didn't allow upcasting to t that have fewer associated types +// than the source type. + +fn main() {} diff --git a/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.current.stderr b/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.current.stderr new file mode 100644 index 00000000000..59c9d573705 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.current.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/illegal-upcast-from-impl.rs:16:66 + | +LL | fn illegal(x: &dyn Sub) -> &dyn Super { x } + | ----------------------- ^ expected trait `Super`, found trait `Sub` + | | + | expected `&dyn Super` because of return type + | + = note: expected reference `&dyn Super` + found reference `&dyn Sub` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.next.stderr b/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.next.stderr new file mode 100644 index 00000000000..59c9d573705 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.next.stderr @@ -0,0 +1,14 @@ +error[E0308]: mismatched types + --> $DIR/illegal-upcast-from-impl.rs:16:66 + | +LL | fn illegal(x: &dyn Sub) -> &dyn Super { x } + | ----------------------- ^ expected trait `Super`, found trait `Sub` + | | + | expected `&dyn Super` because of return type + | + = note: expected reference `&dyn Super` + found reference `&dyn Sub` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.rs b/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.rs new file mode 100644 index 00000000000..774474281ea --- /dev/null +++ b/tests/ui/traits/trait-upcasting/illegal-upcast-from-impl.rs @@ -0,0 +1,23 @@ +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next + +#![feature(trait_upcasting)] + +trait Super { + type Assoc; +} + +trait Sub: Super {} + +impl Super for T { + type Assoc = i32; +} + +fn illegal(x: &dyn Sub) -> &dyn Super { x } +//~^ ERROR mismatched types + +// Want to make sure that we can't "upcast" to a supertrait that has a different +// associated type that is instead provided by a blanket impl (and doesn't come +// from the object bounds). + +fn main() {} From 4cc659eb3fc99cd44457616b17d390488e80fcb0 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 27 Jul 2023 00:48:51 +0000 Subject: [PATCH 34/56] short-circuit when proj def ids differ --- .../src/solve/trait_goals.rs | 23 +++++++++++-------- .../src/traits/select/confirmation.rs | 11 +++++---- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 41565fe5dd9..6a23895e1be 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -625,15 +625,20 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { // in b_ty's bound. Use this to first determine *which* apply without // having any inference side-effects. We process obligations because // unification may initially succeed due to deferred projection equality. - let projection_may_match = |ecx: &mut Self, source_projection, target_projection| { - ecx.probe(|_| CandidateKind::UpcastProbe) - .enter(|ecx| -> Result<(), NoSolution> { - ecx.eq(param_env, source_projection, target_projection)?; - let _ = ecx.try_evaluate_added_goals()?; - Ok(()) - }) - .is_ok() - }; + let projection_may_match = + |ecx: &mut Self, + source_projection: ty::PolyExistentialProjection<'tcx>, + target_projection: ty::PolyExistentialProjection<'tcx>| { + source_projection.item_def_id() == target_projection.item_def_id() + && ecx + .probe(|_| CandidateKind::UpcastProbe) + .enter(|ecx| -> Result<(), NoSolution> { + ecx.eq(param_env, source_projection, target_projection)?; + let _ = ecx.try_evaluate_added_goals()?; + Ok(()) + }) + .is_ok() + }; for bound in b_data { match bound.skip_binder() { diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 77304984402..3cf2735b46b 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -920,11 +920,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { a_data.projection_bounds().filter(|source_projection| { // Eager normalization means that we can just use can_eq // here instead of equating and processing obligations. - self.infcx.can_eq( - obligation.param_env, - *source_projection, - target_projection, - ) + source_projection.item_def_id() == target_projection.item_def_id() + && self.infcx.can_eq( + obligation.param_env, + *source_projection, + target_projection, + ) }); let Some(source_projection) = matching_projections.next() else { return Err(SelectionError::Unimplemented); From 238beae5e588fb3dc4550d970085b5bf29f40776 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 27 Jul 2023 01:00:01 +0000 Subject: [PATCH 35/56] Fix upcasting with normalization in old solver, add a test --- .../src/traits/select/confirmation.rs | 14 +++++++++++-- .../traits/trait-upcasting/normalization.rs | 20 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 tests/ui/traits/trait-upcasting/normalization.rs diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 3cf2735b46b..3d6df08f6db 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -887,9 +887,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else { bug!() }; let source_principal = a_data.principal().unwrap().with_self_ty(tcx, a_ty); - let upcast_principal = util::supertraits(tcx, source_principal).nth(idx).unwrap(); + let unnormalized_upcast_principal = + util::supertraits(tcx, source_principal).nth(idx).unwrap(); let mut nested = vec![]; + let upcast_principal = normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + unnormalized_upcast_principal, + &mut nested, + ); + for bound in b_data { match bound.skip_binder() { // Check that a's supertrait (upcast_principal) is compatible @@ -973,7 +983,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } VtblSegment::TraitOwnEntries { trait_ref, emit_vptr } => { vptr_offset += count_own_vtable_entries(tcx, trait_ref); - if trait_ref == upcast_principal { + if trait_ref == unnormalized_upcast_principal { if emit_vptr { return ControlFlow::Break(Some(vptr_offset)); } else { diff --git a/tests/ui/traits/trait-upcasting/normalization.rs b/tests/ui/traits/trait-upcasting/normalization.rs new file mode 100644 index 00000000000..c78338b0da9 --- /dev/null +++ b/tests/ui/traits/trait-upcasting/normalization.rs @@ -0,0 +1,20 @@ +// check-pass +// issue: 114113 +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next + +#![feature(trait_upcasting)] + +trait Mirror { + type Assoc; +} +impl Mirror for T { + type Assoc = T; +} + +trait Bar {} +trait Foo: Bar<::Assoc> {} + +fn upcast(x: &dyn Foo) -> &dyn Bar { x } + +fn main() {} From 7c942ccb0c8b404ff804b2678f011f42729e81f2 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 1 Aug 2023 23:51:37 +0000 Subject: [PATCH 36/56] Don't be incomplete --- .../src/traits/select/candidate_assembly.rs | 35 +++++-- .../src/traits/select/confirmation.rs | 93 ++----------------- .../src/traits/select/mod.rs | 92 ++++++++++++++++++ 3 files changed, 128 insertions(+), 92 deletions(-) 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 8f2f02a5e41..e3da87a2210 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -745,7 +745,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { match (source.kind(), target.kind()) { // Trait+Kx+'a -> Trait+Ky+'b (upcasts). - (&ty::Dynamic(ref data_a, _, ty::Dyn), &ty::Dynamic(ref data_b, _, ty::Dyn)) => { + ( + &ty::Dynamic(ref a_data, a_region, ty::Dyn), + &ty::Dynamic(ref b_data, b_region, ty::Dyn), + ) => { // Upcast coercions permit several things: // // 1. Dropping auto traits, e.g., `Foo + Send` to `Foo` @@ -757,19 +760,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // // We always perform upcasting coercions when we can because of reason // #2 (region bounds). - let auto_traits_compatible = data_b + let auto_traits_compatible = b_data .auto_traits() // All of a's auto traits need to be in b's auto traits. - .all(|b| data_a.auto_traits().any(|a| a == b)); + .all(|b| a_data.auto_traits().any(|a| a == b)); if auto_traits_compatible { - let principal_def_id_a = data_a.principal_def_id(); - let principal_def_id_b = data_b.principal_def_id(); + let principal_def_id_a = a_data.principal_def_id(); + let principal_def_id_b = b_data.principal_def_id(); if principal_def_id_a == principal_def_id_b { // no cyclic candidates.vec.push(BuiltinUnsizeCandidate); } else if principal_def_id_a.is_some() && principal_def_id_b.is_some() { // not casual unsizing, now check whether this is trait upcasting coercion. - let principal_a = data_a.principal().unwrap(); + let principal_a = a_data.principal().unwrap(); let target_trait_did = principal_def_id_b.unwrap(); let source_trait_ref = principal_a.with_self_ty(self.tcx(), source); if let Some(deref_trait_ref) = self.need_migrate_deref_output_trait_object( @@ -785,9 +788,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { for (idx, upcast_trait_ref) in util::supertraits(self.tcx(), source_trait_ref).enumerate() { - if upcast_trait_ref.def_id() == target_trait_did { - candidates.vec.push(TraitUpcastingUnsizeCandidate(idx)); - } + self.infcx.probe(|_| { + if upcast_trait_ref.def_id() == target_trait_did + && let Ok(nested) = self.match_upcast_principal( + obligation, + upcast_trait_ref, + a_data, + b_data, + a_region, + b_region, + ) + { + if nested.is_none() { + candidates.ambiguous = true; + } + candidates.vec.push(TraitUpcastingUnsizeCandidate(idx)); + } + }) } } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index 3d6df08f6db..65d43b55b90 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -890,89 +890,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let unnormalized_upcast_principal = util::supertraits(tcx, source_principal).nth(idx).unwrap(); - let mut nested = vec![]; - let upcast_principal = normalize_with_depth_to( - self, - obligation.param_env, - obligation.cause.clone(), - obligation.recursion_depth + 1, - unnormalized_upcast_principal, - &mut nested, - ); - - for bound in b_data { - match bound.skip_binder() { - // Check that a's supertrait (upcast_principal) is compatible - // with the target (b_ty). - ty::ExistentialPredicate::Trait(target_principal) => { - nested.extend( - self.infcx - .at(&obligation.cause, obligation.param_env) - .sup( - DefineOpaqueTypes::No, - upcast_principal.map_bound(|trait_ref| { - ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) - }), - bound.rebind(target_principal), - ) - .map_err(|_| SelectionError::Unimplemented)? - .into_obligations(), - ); - } - // Check that b_ty's projection is satisfied by exactly one of - // a_ty's projections. First, we look through the list to see if - // any match. If not, error. Then, if *more* than one matches, we - // return ambiguity. Otherwise, if exactly one matches, equate - // it with b_ty's projection. - ty::ExistentialPredicate::Projection(target_projection) => { - let target_projection = bound.rebind(target_projection); - let mut matching_projections = - a_data.projection_bounds().filter(|source_projection| { - // Eager normalization means that we can just use can_eq - // here instead of equating and processing obligations. - source_projection.item_def_id() == target_projection.item_def_id() - && self.infcx.can_eq( - obligation.param_env, - *source_projection, - target_projection, - ) - }); - let Some(source_projection) = matching_projections.next() else { - return Err(SelectionError::Unimplemented); - }; - if matching_projections.next().is_some() { - // This is incomplete but I don't care. We should never - // have more than one projection that ever applies with - // eager norm and actually implementable traits, since - // you can't have two supertraits like: - // `trait A: B + B` - return Err(SelectionError::Unimplemented); - } - nested.extend( - self.infcx - .at(&obligation.cause, obligation.param_env) - .sup(DefineOpaqueTypes::No, source_projection, target_projection) - .map_err(|_| SelectionError::Unimplemented)? - .into_obligations(), - ); - } - // Check that b_ty's auto trait is present in a_ty's bounds. - ty::ExistentialPredicate::AutoTrait(def_id) => { - if !a_data.auto_traits().any(|source_def_id| source_def_id == def_id) { - return Err(SelectionError::Unimplemented); - } - } - } - } - - // Also require that a_ty's lifetime outlives b_ty's lifetime. - nested.push(Obligation::with_depth( - tcx, - obligation.cause.clone(), - obligation.recursion_depth + 1, - obligation.param_env, - ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), - )); + let nested = self + .match_upcast_principal( + obligation, + unnormalized_upcast_principal, + a_data, + b_data, + a_region, + b_region, + )? + .expect("did not expect ambiguity during confirmation"); let vtable_segment_callback = { let mut vptr_offset = 0; diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 5da8d838db2..f1bd9f8b71a 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2477,6 +2477,98 @@ impl<'tcx> SelectionContext<'_, 'tcx> { Ok(Normalized { value: impl_args, obligations: nested_obligations }) } + fn match_upcast_principal( + &mut self, + obligation: &PolyTraitObligation<'tcx>, + unnormalized_upcast_principal: ty::PolyTraitRef<'tcx>, + a_data: &'tcx ty::List>, + b_data: &'tcx ty::List>, + a_region: ty::Region<'tcx>, + b_region: ty::Region<'tcx>, + ) -> SelectionResult<'tcx, Vec>> { + let tcx = self.tcx(); + let mut nested = vec![]; + + let upcast_principal = normalize_with_depth_to( + self, + obligation.param_env, + obligation.cause.clone(), + obligation.recursion_depth + 1, + unnormalized_upcast_principal, + &mut nested, + ); + + for bound in b_data { + match bound.skip_binder() { + // Check that a_ty's supertrait (upcast_principal) is compatible + // with the target (b_ty). + ty::ExistentialPredicate::Trait(target_principal) => { + nested.extend( + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup( + DefineOpaqueTypes::No, + upcast_principal.map_bound(|trait_ref| { + ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) + }), + bound.rebind(target_principal), + ) + .map_err(|_| SelectionError::Unimplemented)? + .into_obligations(), + ); + } + // Check that b_ty's projection is satisfied by exactly one of + // a_ty's projections. First, we look through the list to see if + // any match. If not, error. Then, if *more* than one matches, we + // return ambiguity. Otherwise, if exactly one matches, equate + // it with b_ty's projection. + ty::ExistentialPredicate::Projection(target_projection) => { + let target_projection = bound.rebind(target_projection); + let mut matching_projections = + a_data.projection_bounds().filter(|source_projection| { + // Eager normalization means that we can just use can_eq + // here instead of equating and processing obligations. + source_projection.item_def_id() == target_projection.item_def_id() + && self.infcx.can_eq( + obligation.param_env, + *source_projection, + target_projection, + ) + }); + let Some(source_projection) = matching_projections.next() else { + return Err(SelectionError::Unimplemented); + }; + if matching_projections.next().is_some() { + return Ok(None); + } + nested.extend( + self.infcx + .at(&obligation.cause, obligation.param_env) + .sup(DefineOpaqueTypes::No, source_projection, target_projection) + .map_err(|_| SelectionError::Unimplemented)? + .into_obligations(), + ); + } + // Check that b_ty's auto traits are present in a_ty's bounds. + ty::ExistentialPredicate::AutoTrait(def_id) => { + if !a_data.auto_traits().any(|source_def_id| source_def_id == def_id) { + return Err(SelectionError::Unimplemented); + } + } + } + } + + nested.push(Obligation::with_depth( + tcx, + obligation.cause.clone(), + obligation.recursion_depth + 1, + obligation.param_env, + ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)), + )); + + Ok(Some(nested)) + } + /// Normalize `where_clause_trait_ref` and try to match it against /// `obligation`. If successful, return any predicates that /// result from the normalization. From 5bea48ba18b71c0e4a80fd08f6ec33330c770648 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 2 Aug 2023 00:43:35 +0000 Subject: [PATCH 37/56] separate calculation and interning of external query constraints --- .../src/solve/eval_ctxt/canonical.rs | 55 ++++++++++--------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index 7323b98b8ce..749568cd80d 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -18,7 +18,7 @@ use rustc_infer::infer::canonical::CanonicalVarValues; use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints}; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::{ - ExternalConstraints, ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput, + ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput, }; use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable}; use rustc_span::DUMMY_SP; @@ -69,35 +69,36 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { previous call to `try_evaluate_added_goals!`" ); - let certainty = certainty.unify_with(goals_certainty); + if let Certainty::OVERFLOW = certainty { + // If we have overflow, it's probable that we're substituting a type + // into itself infinitely and any partial substitutions in the query + // response are probably not useful anyways, so just return an empty + // query response. + // + // This may prevent us from potentially useful inference, e.g. + // 2 candidates, one ambiguous and one overflow, which both + // have the same inference constraints. + // + // Changing this to retain some constraints in the future + // won't be a breaking change, so this is good enough for now. + return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow)); + } - let response = match certainty { - Certainty::Yes | Certainty::Maybe(MaybeCause::Ambiguity) => { - let external_constraints = self.compute_external_query_constraints()?; - Response { var_values: self.var_values, external_constraints, certainty } - } - Certainty::Maybe(MaybeCause::Overflow) => { - // If we have overflow, it's probable that we're substituting a type - // into itself infinitely and any partial substitutions in the query - // response are probably not useful anyways, so just return an empty - // query response. - // - // This may prevent us from potentially useful inference, e.g. - // 2 candidates, one ambiguous and one overflow, which both - // have the same inference constraints. - // - // Changing this to retain some constraints in the future - // won't be a breaking change, so this is good enough for now. - return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow)); - } - }; + let certainty = certainty.unify_with(goals_certainty); + let var_values = self.var_values; + let external_constraints = self.compute_external_query_constraints()?; let canonical = Canonicalizer::canonicalize( self.infcx, CanonicalizeMode::Response { max_input_universe: self.max_input_universe }, &mut Default::default(), - response, + Response { + var_values, + certainty, + external_constraints: self.tcx().mk_external_constraints(external_constraints), + }, ); + Ok(canonical) } @@ -143,7 +144,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { /// further constrained by inference, that will be passed back in the var /// values. #[instrument(level = "debug", skip(self), ret)] - fn compute_external_query_constraints(&self) -> Result, NoSolution> { + fn compute_external_query_constraints( + &self, + ) -> Result, NoSolution> { // We only check for leaks from universes which were entered inside // of the query. self.infcx.leak_check(self.max_input_universe, None).map_err(|e| { @@ -173,9 +176,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { self.predefined_opaques_in_body.opaque_types.iter().all(|(pa, _)| pa != a) }); - Ok(self - .tcx() - .mk_external_constraints(ExternalConstraintsData { region_constraints, opaque_types })) + Ok(ExternalConstraintsData { region_constraints, opaque_types }) } /// After calling a canonical query, we apply the constraints returned From eaf8af5de8ade694784ca621fa0886aca2e46eaa Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 2 Aug 2023 01:31:31 +0000 Subject: [PATCH 38/56] resolve before canonicalization, ICE if unresolved --- compiler/rustc_middle/src/traits/solve.rs | 2 +- .../src/solve/canonicalize.rs | 94 +++++++------------ .../src/solve/eval_ctxt/canonical.rs | 87 +++++++++++++++-- 3 files changed, 115 insertions(+), 68 deletions(-) diff --git a/compiler/rustc_middle/src/traits/solve.rs b/compiler/rustc_middle/src/traits/solve.rs index b21a00e4122..90139f925ed 100644 --- a/compiler/rustc_middle/src/traits/solve.rs +++ b/compiler/rustc_middle/src/traits/solve.rs @@ -147,7 +147,7 @@ impl<'tcx> std::ops::Deref for ExternalConstraints<'tcx> { } /// Additional constraints returned on success. -#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default)] +#[derive(Debug, PartialEq, Eq, Clone, Hash, HashStable, Default, TypeVisitable, TypeFoldable)] pub struct ExternalConstraintsData<'tcx> { // FIXME: implement this. pub region_constraints: QueryRegionConstraints<'tcx>, diff --git a/compiler/rustc_trait_selection/src/solve/canonicalize.rs b/compiler/rustc_trait_selection/src/solve/canonicalize.rs index 88771f90756..a9d182abfb7 100644 --- a/compiler/rustc_trait_selection/src/solve/canonicalize.rs +++ b/compiler/rustc_trait_selection/src/solve/canonicalize.rs @@ -207,23 +207,18 @@ impl<'tcx> TypeFolder> for Canonicalizer<'_, 'tcx> { t } - fn fold_region(&mut self, mut r: ty::Region<'tcx>) -> ty::Region<'tcx> { - match self.canonicalize_mode { - CanonicalizeMode::Input => { - // Don't resolve infer vars in input, since it affects - // caching and may cause trait selection bugs which rely - // on regions to be equal. - } - CanonicalizeMode::Response { .. } => { - if let ty::ReVar(vid) = *r { - r = self - .infcx - .inner - .borrow_mut() - .unwrap_region_constraints() - .opportunistic_resolve_var(self.infcx.tcx, vid); - } - } + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + if let ty::ReVar(vid) = *r { + let resolved_region = self + .infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(self.infcx.tcx, vid); + assert_eq!( + r, resolved_region, + "region var should have been resolved, {r} -> {resolved_region}" + ); } let kind = match *r { @@ -278,38 +273,22 @@ impl<'tcx> TypeFolder> for Canonicalizer<'_, 'tcx> { ty::Region::new_late_bound(self.interner(), self.binder_index, br) } - fn fold_ty(&mut self, mut t: Ty<'tcx>) -> Ty<'tcx> { + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { let kind = match *t.kind() { - ty::Infer(ty::TyVar(mut vid)) => { - // We need to canonicalize the *root* of our ty var. - // This is so that our canonical response correctly reflects - // any equated inference vars correctly! - let root_vid = self.infcx.root_var(vid); - if root_vid != vid { - t = Ty::new_var(self.infcx.tcx, root_vid); - vid = root_vid; - } - - match self.infcx.probe_ty_var(vid) { - Ok(t) => return self.fold_ty(t), - Err(ui) => CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)), - } + ty::Infer(ty::TyVar(vid)) => { + assert_eq!(self.infcx.root_var(vid), vid, "ty vid should have been resolved"); + let Err(ui) = self.infcx.probe_ty_var(vid) else { + bug!("ty var should have been resolved: {t}"); + }; + CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)) } ty::Infer(ty::IntVar(vid)) => { - let nt = self.infcx.opportunistic_resolve_int_var(vid); - if nt != t { - return self.fold_ty(nt); - } else { - CanonicalVarKind::Ty(CanonicalTyVarKind::Int) - } + assert_eq!(self.infcx.opportunistic_resolve_int_var(vid), t); + CanonicalVarKind::Ty(CanonicalTyVarKind::Int) } ty::Infer(ty::FloatVar(vid)) => { - let nt = self.infcx.opportunistic_resolve_float_var(vid); - if nt != t { - return self.fold_ty(nt); - } else { - CanonicalVarKind::Ty(CanonicalTyVarKind::Float) - } + assert_eq!(self.infcx.opportunistic_resolve_float_var(vid), t); + CanonicalVarKind::Ty(CanonicalTyVarKind::Float) } ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { bug!("fresh var during canonicalization: {t:?}") @@ -372,22 +351,19 @@ impl<'tcx> TypeFolder> for Canonicalizer<'_, 'tcx> { Ty::new_bound(self.infcx.tcx, self.binder_index, bt) } - fn fold_const(&mut self, mut c: ty::Const<'tcx>) -> ty::Const<'tcx> { + fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { let kind = match c.kind() { - ty::ConstKind::Infer(ty::InferConst::Var(mut vid)) => { - // We need to canonicalize the *root* of our const var. - // This is so that our canonical response correctly reflects - // any equated inference vars correctly! - let root_vid = self.infcx.root_const_var(vid); - if root_vid != vid { - c = ty::Const::new_var(self.infcx.tcx, root_vid, c.ty()); - vid = root_vid; - } - - match self.infcx.probe_const_var(vid) { - Ok(c) => return self.fold_const(c), - Err(universe) => CanonicalVarKind::Const(universe, c.ty()), - } + ty::ConstKind::Infer(ty::InferConst::Var(vid)) => { + assert_eq!( + self.infcx.root_const_var(vid), + vid, + "const var should have been resolved" + ); + let Err(ui) = self.infcx.probe_const_var(vid) else { + bug!("const var should have been resolved"); + }; + // FIXME: we should fold this ty eventually + CanonicalVarKind::Const(ui, c.ty()) } ty::ConstKind::Infer(ty::InferConst::Fresh(_)) => { bug!("fresh var during canonicalization: {c:?}") diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index 749568cd80d..84ca555a5de 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -16,11 +16,15 @@ use rustc_index::IndexVec; use rustc_infer::infer::canonical::query_response::make_query_region_constraints; use rustc_infer::infer::canonical::CanonicalVarValues; use rustc_infer::infer::canonical::{CanonicalExt, QueryRegionConstraints}; +use rustc_infer::infer::InferCtxt; use rustc_middle::traits::query::NoSolution; use rustc_middle::traits::solve::{ - ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput, + ExternalConstraintsData, MaybeCause, PredefinedOpaquesData, QueryInput, +}; +use rustc_middle::ty::{ + self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + TypeVisitableExt, }; -use rustc_middle::ty::{self, BoundVar, GenericArgKind, Ty, TyCtxt, TypeFoldable}; use rustc_span::DUMMY_SP; use std::iter; use std::ops::Deref; @@ -32,6 +36,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { &self, goal: Goal<'tcx, T>, ) -> (Vec>, CanonicalInput<'tcx, T>) { + let opaque_types = self.infcx.clone_opaque_types_for_query_response(); + let (goal, opaque_types) = + (goal, opaque_types).fold_with(&mut EagerResolver { infcx: self.infcx }); + let mut orig_values = Default::default(); let canonical_goal = Canonicalizer::canonicalize( self.infcx, @@ -40,11 +48,9 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { QueryInput { goal, anchor: self.infcx.defining_use_anchor, - predefined_opaques_in_body: self.tcx().mk_predefined_opaques_in_body( - PredefinedOpaquesData { - opaque_types: self.infcx.clone_opaque_types_for_query_response(), - }, - ), + predefined_opaques_in_body: self + .tcx() + .mk_predefined_opaques_in_body(PredefinedOpaquesData { opaque_types }), }, ); (orig_values, canonical_goal) @@ -69,6 +75,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { previous call to `try_evaluate_added_goals!`" ); + let certainty = certainty.unify_with(goals_certainty); if let Certainty::OVERFLOW = certainty { // If we have overflow, it's probable that we're substituting a type // into itself infinitely and any partial substitutions in the query @@ -84,10 +91,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { return Ok(self.make_ambiguous_response_no_constraints(MaybeCause::Overflow)); } - let certainty = certainty.unify_with(goals_certainty); let var_values = self.var_values; let external_constraints = self.compute_external_query_constraints()?; + let (var_values, external_constraints) = + (var_values, external_constraints).fold_with(&mut EagerResolver { infcx: self.infcx }); + let canonical = Canonicalizer::canonicalize( self.infcx, CanonicalizeMode::Response { max_input_universe: self.max_input_universe }, @@ -334,3 +343,65 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { Ok(()) } } + +/// Resolves ty, region, and const vars to their inferred values or their root vars. +struct EagerResolver<'a, 'tcx> { + infcx: &'a InferCtxt<'tcx>, +} + +impl<'tcx> TypeFolder> for EagerResolver<'_, 'tcx> { + fn interner(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match *t.kind() { + ty::Infer(ty::TyVar(vid)) => match self.infcx.probe_ty_var(vid) { + Ok(t) => t.fold_with(self), + Err(_) => Ty::new_var(self.infcx.tcx, self.infcx.root_var(vid)), + }, + ty::Infer(ty::IntVar(vid)) => self.infcx.opportunistic_resolve_int_var(vid), + ty::Infer(ty::FloatVar(vid)) => self.infcx.opportunistic_resolve_float_var(vid), + _ => { + if t.has_infer() { + t.super_fold_with(self) + } else { + t + } + } + } + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + match *r { + ty::ReVar(vid) => self + .infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(self.infcx.tcx, vid), + _ => r, + } + } + + fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { + match c.kind() { + ty::ConstKind::Infer(ty::InferConst::Var(vid)) => { + // FIXME: we need to fold the ty too, I think. + match self.infcx.probe_const_var(vid) { + Ok(c) => c.fold_with(self), + Err(_) => { + ty::Const::new_var(self.infcx.tcx, self.infcx.root_const_var(vid), c.ty()) + } + } + } + _ => { + if c.has_infer() { + c.super_fold_with(self) + } else { + c + } + } + } + } +} From f848fd3ae3ffcd10628b95dbc1c1aad435e4c590 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 2 Aug 2023 01:31:48 +0000 Subject: [PATCH 39/56] Remove trivial region constraints --- .../rustc_trait_selection/src/solve/eval_ctxt/canonical.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs index 84ca555a5de..0a54b1c6433 100644 --- a/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_trait_selection/src/solve/eval_ctxt/canonical.rs @@ -94,8 +94,13 @@ impl<'tcx> EvalCtxt<'_, 'tcx> { let var_values = self.var_values; let external_constraints = self.compute_external_query_constraints()?; - let (var_values, external_constraints) = + let (var_values, mut external_constraints) = (var_values, external_constraints).fold_with(&mut EagerResolver { infcx: self.infcx }); + // Remove any trivial region constraints once we've resolved regions + external_constraints + .region_constraints + .outlives + .retain(|(outlives, _)| outlives.0.as_region().map_or(true, |re| re != outlives.1)); let canonical = Canonicalizer::canonicalize( self.infcx, From d87f4a6dc1e1f4f37ad3f6d530483e735e195b5e Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 2 Aug 2023 09:26:31 -0700 Subject: [PATCH 40/56] Placeholder nit --- .../rustc_infer/src/infer/canonical/canonicalizer.rs | 10 ++-------- compiler/rustc_middle/src/ty/visit.rs | 8 ++------ compiler/rustc_type_ir/src/lib.rs | 5 +++++ 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs index 54d901f20da..9d7a9fefd08 100644 --- a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -562,15 +562,9 @@ impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { V: TypeFoldable>, { let needs_canonical_flags = if canonicalize_region_mode.any() { - TypeFlags::HAS_INFER | - TypeFlags::HAS_FREE_REGIONS | // `HAS_RE_PLACEHOLDER` implies `HAS_FREE_REGIONS` - TypeFlags::HAS_TY_PLACEHOLDER | - TypeFlags::HAS_CT_PLACEHOLDER + TypeFlags::HAS_INFER | TypeFlags::HAS_PLACEHOLDER | TypeFlags::HAS_FREE_REGIONS } else { - TypeFlags::HAS_INFER - | TypeFlags::HAS_RE_PLACEHOLDER - | TypeFlags::HAS_TY_PLACEHOLDER - | TypeFlags::HAS_CT_PLACEHOLDER + TypeFlags::HAS_INFER | TypeFlags::HAS_PLACEHOLDER }; // Fast path: nothing that needs to be canonicalized. diff --git a/compiler/rustc_middle/src/ty/visit.rs b/compiler/rustc_middle/src/ty/visit.rs index 520bb55e031..156eda477ad 100644 --- a/compiler/rustc_middle/src/ty/visit.rs +++ b/compiler/rustc_middle/src/ty/visit.rs @@ -88,14 +88,10 @@ pub trait TypeVisitableExt<'tcx>: TypeVisitable> { self.has_type_flags(TypeFlags::HAS_INFER) } fn has_placeholders(&self) -> bool { - self.has_type_flags( - TypeFlags::HAS_RE_PLACEHOLDER - | TypeFlags::HAS_TY_PLACEHOLDER - | TypeFlags::HAS_CT_PLACEHOLDER, - ) + self.has_type_flags(TypeFlags::HAS_PLACEHOLDER) } fn has_non_region_placeholders(&self) -> bool { - self.has_type_flags(TypeFlags::HAS_TY_PLACEHOLDER | TypeFlags::HAS_CT_PLACEHOLDER) + self.has_type_flags(TypeFlags::HAS_PLACEHOLDER - TypeFlags::HAS_RE_PLACEHOLDER) } fn has_param(&self) -> bool { self.has_type_flags(TypeFlags::HAS_PARAM) diff --git a/compiler/rustc_type_ir/src/lib.rs b/compiler/rustc_type_ir/src/lib.rs index c2655d68b79..3e6b80352cd 100644 --- a/compiler/rustc_type_ir/src/lib.rs +++ b/compiler/rustc_type_ir/src/lib.rs @@ -215,6 +215,11 @@ bitflags! { /// Does this have `ConstKind::Placeholder`? const HAS_CT_PLACEHOLDER = 1 << 8; + /// Does this have placeholders? + const HAS_PLACEHOLDER = TypeFlags::HAS_TY_PLACEHOLDER.bits + | TypeFlags::HAS_RE_PLACEHOLDER.bits + | TypeFlags::HAS_CT_PLACEHOLDER.bits; + /// `true` if there are "names" of regions and so forth /// that are local to a particular fn/inferctxt const HAS_FREE_LOCAL_REGIONS = 1 << 9; From 31a81a08786826cc6e832bd0b49fb8b934e29648 Mon Sep 17 00:00:00 2001 From: Ariadne Conill Date: Thu, 3 Aug 2023 15:05:40 -0700 Subject: [PATCH 41/56] bootstrap: config: fix version comparison bug Rust requires a previous version of Rust to build, such as the current version, or the previous version. However, the version comparison logic did not take patch releases into consideration when doing the version comparison for the current branch, e.g. Rust 1.71.1 could not be built by Rust 1.71.0 because it is neither an exact version match, or the previous version. Adjust the version comparison logic to tolerate mismatches in the patch version. Signed-off-by: Ariadne Conill --- src/bootstrap/config.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 45bea9608fc..f283b2eb7e9 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -2004,7 +2004,8 @@ impl Config { .unwrap(); if !(source_version == rustc_version || (source_version.major == rustc_version.major - && source_version.minor == rustc_version.minor + 1)) + && (source_version.minor == rustc_version.minor + || source_version.minor == rustc_version.minor + 1))) { let prev_version = format!("{}.{}.x", source_version.major, source_version.minor - 1); eprintln!( From 5706be1854db74d0aafcc4658423884689f139e9 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Thu, 3 Aug 2023 21:43:17 +0200 Subject: [PATCH 42/56] Improve spans for indexing expressions Indexing is similar to method calls in having an arbitrary left-hand-side and then something on the right, which is the main part of the expression. Method calls already have a span for that right part, but indexing does not. This means that long method chains that use indexing have really bad spans, especially when the indexing panics and that span in coverted into a panic location. This does the same thing as method calls for the AST and HIR, storing an extra span which is then put into the `fn_span` field in THIR. --- compiler/rustc_ast/src/ast.rs | 3 +- compiler/rustc_ast/src/mut_visit.rs | 9 ++++-- compiler/rustc_ast/src/util/parser.rs | 2 +- compiler/rustc_ast/src/visit.rs | 2 +- compiler/rustc_ast_lowering/src/expr.rs | 4 +-- .../rustc_ast_pretty/src/pprust/state/expr.rs | 2 +- .../src/diagnostics/explain_borrow.rs | 2 +- .../src/diagnostics/mutability_errors.rs | 4 +-- .../src/assert/context.rs | 2 +- compiler/rustc_hir/src/hir.rs | 8 ++++-- compiler/rustc_hir/src/intravisit.rs | 2 +- compiler/rustc_hir_pretty/src/lib.rs | 4 +-- compiler/rustc_hir_typeck/src/expr.rs | 25 +++++++++-------- .../rustc_hir_typeck/src/expr_use_visitor.rs | 2 +- .../src/mem_categorization.rs | 2 +- compiler/rustc_hir_typeck/src/place_op.rs | 2 +- .../rustc_hir_typeck/src/rvalue_scopes.rs | 2 +- compiler/rustc_hir_typeck/src/writeback.rs | 2 +- compiler/rustc_lint/src/unused.rs | 4 +-- compiler/rustc_mir_build/src/thir/cx/expr.rs | 10 +++++-- compiler/rustc_parse/src/parser/expr.rs | 11 +++++--- compiler/rustc_passes/src/liveness.rs | 2 +- compiler/rustc_resolve/src/late.rs | 2 +- .../src/traits/error_reporting/suggestions.rs | 2 +- .../clippy/clippy_lints/src/dereference.rs | 2 +- .../clippy_lints/src/functions/must_use.rs | 2 +- .../clippy_lints/src/index_refutable_slice.rs | 2 +- .../clippy_lints/src/indexing_slicing.rs | 2 +- .../clippy_lints/src/loops/manual_memcpy.rs | 4 +-- .../src/loops/needless_range_loop.rs | 2 +- .../clippy_lints/src/loops/never_loop.rs | 2 +- .../src/loops/while_let_on_iterator.rs | 2 +- .../clippy/clippy_lints/src/manual_strip.rs | 2 +- .../src/matches/match_on_vec_items.rs | 4 +-- .../clippy_lints/src/methods/filter_next.rs | 2 +- .../src/methods/iter_next_slice.rs | 2 +- .../src/mixed_read_write_in_expression.rs | 2 +- .../clippy/clippy_lints/src/needless_bool.rs | 2 +- .../clippy/clippy_lints/src/no_effect.rs | 4 +-- .../clippy/clippy_lints/src/non_copy_const.rs | 2 +- src/tools/clippy/clippy_lints/src/ptr.rs | 2 +- .../clippy_lints/src/redundant_slicing.rs | 2 +- src/tools/clippy/clippy_lints/src/strings.rs | 4 +-- .../src/suspicious_operation_groupings.rs | 2 +- src/tools/clippy/clippy_lints/src/swap.rs | 4 +-- .../clippy_lints/src/temporary_assignment.rs | 2 +- .../src/tuple_array_conversions.rs | 4 +-- .../clippy/clippy_lints/src/utils/author.rs | 2 +- .../clippy_lints/src/vec_init_then_push.rs | 2 +- .../clippy/clippy_utils/src/ast_utils.rs | 2 +- .../clippy_utils/src/check_proc_macro.rs | 2 +- src/tools/clippy/clippy_utils/src/consts.rs | 2 +- .../clippy/clippy_utils/src/eager_or_lazy.rs | 2 +- .../clippy/clippy_utils/src/hir_utils.rs | 4 +-- src/tools/clippy/clippy_utils/src/lib.rs | 4 +-- .../clippy_utils/src/ty/type_certainty/mod.rs | 2 +- src/tools/clippy/clippy_utils/src/visitors.rs | 4 +-- src/tools/rustfmt/src/expr.rs | 4 +-- src/tools/rustfmt/src/matches.rs | 2 +- src/tools/rustfmt/src/utils.rs | 4 +-- src/tools/tidy/src/ui_tests.rs | 2 +- tests/ui/array-slice-vec/slice-2.stderr | 16 +++++------ .../suggest-local-var-for-vector.stderr | 10 +++---- ...uggest-storing-local-var-for-vector.stderr | 10 +++---- .../two-phase-nonrecv-autoref.base.stderr | 28 +++++++++---------- tests/ui/consts/issue-94675.stderr | 4 +-- tests/ui/error-codes/E0608.stderr | 4 +-- tests/ui/{ => indexing}/index-bot.rs | 0 tests/ui/{ => indexing}/index-bot.stderr | 4 +-- tests/ui/{ => indexing}/index-help.rs | 0 tests/ui/{ => indexing}/index-help.stderr | 0 tests/ui/{ => indexing}/index_message.rs | 0 tests/ui/{ => indexing}/index_message.stderr | 4 +-- .../indexing-requires-a-uint.rs | 0 .../indexing-requires-a-uint.stderr | 0 .../indexing-spans-caller-location.rs | 27 ++++++++++++++++++ ...const-eval-select-backtrace-std.run.stderr | 2 +- tests/ui/issues/issue-27842.stderr | 12 ++++---- tests/ui/issues/issue-40861.stderr | 4 +-- .../lint/lint-unconditional-recursion.stderr | 2 +- tests/ui/span/suggestion-non-ascii.stderr | 4 +-- .../issue-112252-ptr-arithmetics-help.stderr | 4 +-- 82 files changed, 192 insertions(+), 149 deletions(-) rename tests/ui/{ => indexing}/index-bot.rs (100%) rename tests/ui/{ => indexing}/index-bot.stderr (78%) rename tests/ui/{ => indexing}/index-help.rs (100%) rename tests/ui/{ => indexing}/index-help.stderr (100%) rename tests/ui/{ => indexing}/index_message.rs (100%) rename tests/ui/{ => indexing}/index_message.stderr (67%) rename tests/ui/{ => indexing}/indexing-requires-a-uint.rs (100%) rename tests/ui/{ => indexing}/indexing-requires-a-uint.stderr (100%) create mode 100644 tests/ui/indexing/indexing-spans-caller-location.rs diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 2a268c2da85..f2e90fd8eed 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1462,7 +1462,8 @@ pub enum ExprKind { /// Access of a named (e.g., `obj.foo`) or unnamed (e.g., `obj.0`) struct field. Field(P, Ident), /// An indexing operation (e.g., `foo[2]`). - Index(P, P), + /// The span represents the span of the `[2]`, including brackets. + Index(P, P, Span), /// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`; and `..` in destructuring assignment). Range(Option>, Option>, RangeLimits), /// An underscore, used in destructuring assignment to ignore a value. diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 84b56efd325..bae3979fbf9 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1400,7 +1400,7 @@ pub fn noop_visit_expr( fn_decl, body, fn_decl_span, - fn_arg_span: _, + fn_arg_span, }) => { vis.visit_closure_binder(binder); visit_constness(constness, vis); @@ -1408,6 +1408,7 @@ pub fn noop_visit_expr( vis.visit_fn_decl(fn_decl); vis.visit_expr(body); vis.visit_span(fn_decl_span); + vis.visit_span(fn_arg_span); } ExprKind::Block(blk, label) => { vis.visit_block(blk); @@ -1420,9 +1421,10 @@ pub fn noop_visit_expr( vis.visit_expr(expr); vis.visit_span(await_kw_span); } - ExprKind::Assign(el, er, _) => { + ExprKind::Assign(el, er, span) => { vis.visit_expr(el); vis.visit_expr(er); + vis.visit_span(span); } ExprKind::AssignOp(_op, el, er) => { vis.visit_expr(el); @@ -1432,9 +1434,10 @@ pub fn noop_visit_expr( vis.visit_expr(el); vis.visit_ident(ident); } - ExprKind::Index(el, er) => { + ExprKind::Index(el, er, brackets_span) => { vis.visit_expr(el); vis.visit_expr(er); + vis.visit_span(brackets_span); } ExprKind::Range(e1, e2, _lim) => { visit_opt(e1, |e1| vis.visit_expr(e1)); diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs index 096077e09bf..d3e43e20235 100644 --- a/compiler/rustc_ast/src/util/parser.rs +++ b/compiler/rustc_ast/src/util/parser.rs @@ -390,7 +390,7 @@ pub fn contains_exterior_struct_lit(value: &ast::Expr) -> bool { | ast::ExprKind::Cast(x, _) | ast::ExprKind::Type(x, _) | ast::ExprKind::Field(x, _) - | ast::ExprKind::Index(x, _) => { + | ast::ExprKind::Index(x, _, _) => { // &X { y: 1 }, X { y: 1 }.y contains_exterior_struct_lit(x) } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index aed24e11c4e..6d474de2d15 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -885,7 +885,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { visitor.visit_expr(subexpression); visitor.visit_ident(*ident); } - ExprKind::Index(main_expression, index_expression) => { + ExprKind::Index(main_expression, index_expression, _) => { visitor.visit_expr(main_expression); visitor.visit_expr(index_expression) } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 0954cf03da9..8c35266110e 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -240,8 +240,8 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::Field(el, ident) => { hir::ExprKind::Field(self.lower_expr(el), self.lower_ident(*ident)) } - ExprKind::Index(el, er) => { - hir::ExprKind::Index(self.lower_expr(el), self.lower_expr(er)) + ExprKind::Index(el, er, brackets_span) => { + hir::ExprKind::Index(self.lower_expr(el), self.lower_expr(er), *brackets_span) } ExprKind::Range(Some(e1), Some(e2), RangeLimits::Closed) => { self.lower_expr_range_closed(e.span, e1, e2) diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 8767bbcb210..39741a03930 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -477,7 +477,7 @@ impl<'a> State<'a> { self.word("."); self.print_ident(*ident); } - ast::ExprKind::Index(expr, index) => { + ast::ExprKind::Index(expr, index, _) => { self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX); self.word("["); self.print_expr(index); diff --git a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs index c92f32071f4..c66a2447356 100644 --- a/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs +++ b/compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs @@ -79,7 +79,7 @@ impl<'tcx> BorrowExplanation<'tcx> { | hir::ExprKind::Unary(hir::UnOp::Deref, inner) | hir::ExprKind::Field(inner, _) | hir::ExprKind::MethodCall(_, inner, _, _) - | hir::ExprKind::Index(inner, _) = &expr.kind + | hir::ExprKind::Index(inner, _, _) = &expr.kind { expr = inner; } diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs index 3c32121a51a..d62541daf07 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs @@ -567,7 +567,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { } }; if let hir::ExprKind::Assign(place, rv, _sp) = expr.kind - && let hir::ExprKind::Index(val, index) = place.kind + && let hir::ExprKind::Index(val, index, _) = place.kind && (expr.span == self.assign_span || place.span == self.assign_span) { // val[index] = rv; @@ -620,7 +620,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { ); self.suggested = true; } else if let hir::ExprKind::MethodCall(_path, receiver, _, sp) = expr.kind - && let hir::ExprKind::Index(val, index) = receiver.kind + && let hir::ExprKind::Index(val, index, _) = receiver.kind && expr.span == self.assign_span { // val[index].path(args..); diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index cbf115127cf..bda473120ed 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -237,7 +237,7 @@ impl<'cx, 'a> Context<'cx, 'a> { ExprKind::If(local_expr, _, _) => { self.manage_cond_expr(local_expr); } - ExprKind::Index(prefix, suffix) => { + ExprKind::Index(prefix, suffix, _) => { self.manage_cond_expr(prefix); self.manage_cond_expr(suffix); } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 6b76e16825f..bc05565fed4 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1754,7 +1754,7 @@ impl Expr<'_> { ExprKind::Unary(UnOp::Deref, _) => true, - ExprKind::Field(ref base, _) | ExprKind::Index(ref base, _) => { + ExprKind::Field(ref base, _) | ExprKind::Index(ref base, _, _) => { allow_projections_from(base) || base.is_place_expr(allow_projections_from) } @@ -1831,7 +1831,7 @@ impl Expr<'_> { ExprKind::Type(base, _) | ExprKind::Unary(_, base) | ExprKind::Field(base, _) - | ExprKind::Index(base, _) + | ExprKind::Index(base, _, _) | ExprKind::AddrOf(.., base) | ExprKind::Cast(base, _) => { // This isn't exactly true for `Index` and all `Unary`, but we are using this @@ -2015,7 +2015,9 @@ pub enum ExprKind<'hir> { /// Access of a named (e.g., `obj.foo`) or unnamed (e.g., `obj.0`) struct or tuple field. Field(&'hir Expr<'hir>, Ident), /// An indexing operation (`foo[2]`). - Index(&'hir Expr<'hir>, &'hir Expr<'hir>), + /// Similar to [`ExprKind::MethodCall`], the final `Span` represents the span of the brackets + /// and index. + Index(&'hir Expr<'hir>, &'hir Expr<'hir>, Span), /// Path to a definition, possibly containing lifetime or type parameters. Path(QPath<'hir>), diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index a8a94e6a476..9d1d899eef0 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -780,7 +780,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) visitor.visit_expr(subexpression); visitor.visit_ident(ident); } - ExprKind::Index(ref main_expression, ref index_expression) => { + ExprKind::Index(ref main_expression, ref index_expression, _) => { visitor.visit_expr(main_expression); visitor.visit_expr(index_expression) } diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 2d8b956771b..fda46798708 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1526,7 +1526,7 @@ impl<'a> State<'a> { self.word("."); self.print_ident(ident); } - hir::ExprKind::Index(expr, index) => { + hir::ExprKind::Index(expr, index, _) => { self.print_expr_maybe_paren(expr, parser::PREC_POSTFIX); self.word("["); self.print_expr(index); @@ -2419,7 +2419,7 @@ fn contains_exterior_struct_lit(value: &hir::Expr<'_>) -> bool { | hir::ExprKind::Cast(x, _) | hir::ExprKind::Type(x, _) | hir::ExprKind::Field(x, _) - | hir::ExprKind::Index(x, _) => { + | hir::ExprKind::Index(x, _, _) => { // `&X { y: 1 }, X { y: 1 }.y` contains_exterior_struct_lit(x) } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 80d7cc57edb..2af8648455b 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -345,7 +345,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_expr_struct(expr, expected, qpath, fields, base_expr) } ExprKind::Field(base, field) => self.check_field(expr, &base, field, expected), - ExprKind::Index(base, idx) => self.check_expr_index(base, idx, expr), + ExprKind::Index(base, idx, brackets_span) => { + self.check_expr_index(base, idx, expr, brackets_span) + } ExprKind::Yield(value, ref src) => self.check_expr_yield(value, expr, src), hir::ExprKind::Err(guar) => Ty::new_error(tcx, guar), } @@ -2840,6 +2842,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { base: &'tcx hir::Expr<'tcx>, idx: &'tcx hir::Expr<'tcx>, expr: &'tcx hir::Expr<'tcx>, + brackets_span: Span, ) -> Ty<'tcx> { let base_t = self.check_expr(&base); let idx_t = self.check_expr(&idx); @@ -2873,7 +2876,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut err = type_error_struct!( self.tcx.sess, - expr.span, + brackets_span, base_t, E0608, "cannot index into a value of type `{base_t}`", @@ -2887,16 +2890,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { && let ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) = lit.node && i < types.len().try_into().expect("expected tuple index to be < usize length") { - let snip = self.tcx.sess.source_map().span_to_snippet(base.span); - if let Ok(snip) = snip { - err.span_suggestion( - expr.span, - "to access tuple elements, use", - format!("{snip}.{i}"), - Applicability::MachineApplicable, - ); - needs_note = false; - } + + err.span_suggestion( + brackets_span, + "to access tuple elements, use", + format!(".{i}"), + Applicability::MachineApplicable, + ); + needs_note = false; } else if let ExprKind::Path(..) = idx.peel_borrows().kind { err.span_label(idx.span, "cannot access tuple elements at a variable index"); } diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index a59061cbf67..840910732d8 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -211,7 +211,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { self.select_from_expr(base); } - hir::ExprKind::Index(lhs, rhs) => { + hir::ExprKind::Index(lhs, rhs, _) => { // lhs[rhs] self.select_from_expr(lhs); self.consume_expr(rhs); diff --git a/compiler/rustc_hir_typeck/src/mem_categorization.rs b/compiler/rustc_hir_typeck/src/mem_categorization.rs index 0700e2e0554..20b3e470f73 100644 --- a/compiler/rustc_hir_typeck/src/mem_categorization.rs +++ b/compiler/rustc_hir_typeck/src/mem_categorization.rs @@ -336,7 +336,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { )) } - hir::ExprKind::Index(ref base, _) => { + hir::ExprKind::Index(ref base, _, _) => { if self.typeck_results.is_method_call(expr) { // If this is an index implemented by a method call, then it // will include an implicit deref of the result. diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index 1eb84eb1637..406434e09e6 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -284,7 +284,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut exprs = vec![expr]; while let hir::ExprKind::Field(ref expr, _) - | hir::ExprKind::Index(ref expr, _) + | hir::ExprKind::Index(ref expr, _, _) | hir::ExprKind::Unary(hir::UnOp::Deref, ref expr) = exprs.last().unwrap().kind { exprs.push(expr); diff --git a/compiler/rustc_hir_typeck/src/rvalue_scopes.rs b/compiler/rustc_hir_typeck/src/rvalue_scopes.rs index 091e88abe97..04d84102336 100644 --- a/compiler/rustc_hir_typeck/src/rvalue_scopes.rs +++ b/compiler/rustc_hir_typeck/src/rvalue_scopes.rs @@ -40,7 +40,7 @@ fn record_rvalue_scope_rec( hir::ExprKind::AddrOf(_, _, subexpr) | hir::ExprKind::Unary(hir::UnOp::Deref, subexpr) | hir::ExprKind::Field(subexpr, _) - | hir::ExprKind::Index(subexpr, _) => { + | hir::ExprKind::Index(subexpr, _, _) => { expr = subexpr; } _ => { diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 6f47623ec43..603681bbc99 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -210,7 +210,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { // to use builtin indexing because the index type is known to be // usize-ish fn fix_index_builtin_expr(&mut self, e: &hir::Expr<'_>) { - if let hir::ExprKind::Index(ref base, ref index) = e.kind { + if let hir::ExprKind::Index(ref base, ref index, _) = e.kind { // All valid indexing looks like this; might encounter non-valid indexes at this point. let base_ty = self.typeck_results.expr_ty_adjusted_opt(base); if base_ty.is_none() { diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index a3c5ca4cda1..3db6b302790 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -653,7 +653,7 @@ trait UnusedDelimLint { ExprKind::Call(fn_, _params) => fn_, ExprKind::Cast(expr, _ty) => expr, ExprKind::Type(expr, _ty) => expr, - ExprKind::Index(base, _subscript) => base, + ExprKind::Index(base, _subscript, _) => base, _ => break, }; if !classify::expr_requires_semi_to_be_stmt(innermost) { @@ -830,7 +830,7 @@ trait UnusedDelimLint { (value, UnusedDelimsCtx::ReturnValue, false, Some(left), None, true) } - Index(_, ref value) => (value, UnusedDelimsCtx::IndexExpr, false, None, None, false), + Index(_, ref value, _) => (value, UnusedDelimsCtx::IndexExpr, false, None, None, false), Assign(_, ref value, _) | AssignOp(.., ref value) => { (value, UnusedDelimsCtx::AssignedValue, false, None, None, false) diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index ff4620948fa..4701b64d22a 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -469,11 +469,17 @@ impl<'tcx> Cx<'tcx> { } } - hir::ExprKind::Index(ref lhs, ref index) => { + hir::ExprKind::Index(ref lhs, ref index, brackets_span) => { if self.typeck_results().is_method_call(expr) { let lhs = self.mirror_expr(lhs); let index = self.mirror_expr(index); - self.overloaded_place(expr, expr_ty, None, Box::new([lhs, index]), expr.span) + self.overloaded_place( + expr, + expr_ty, + None, + Box::new([lhs, index]), + brackets_span, + ) } else { ExprKind::Index { lhs: self.mirror_expr(lhs), index: self.mirror_expr(index) } } diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index dc3b131e7f2..1b45a01f8b3 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -857,7 +857,7 @@ impl<'a> Parser<'a> { let msg = format!( "cast cannot be followed by {}", match with_postfix.kind { - ExprKind::Index(_, _) => "indexing", + ExprKind::Index(..) => "indexing", ExprKind::Try(_) => "`?`", ExprKind::Field(_, _) => "a field access", ExprKind::MethodCall(_) => "a method call", @@ -1304,7 +1304,10 @@ impl<'a> Parser<'a> { let index = self.parse_expr()?; self.suggest_missing_semicolon_before_array(prev_span, open_delim_span)?; self.expect(&token::CloseDelim(Delimiter::Bracket))?; - Ok(self.mk_expr(lo.to(self.prev_token.span), self.mk_index(base, index))) + Ok(self.mk_expr( + lo.to(self.prev_token.span), + self.mk_index(base, index, open_delim_span.to(self.prev_token.span)), + )) } /// Assuming we have just parsed `.`, continue parsing into an expression. @@ -3366,8 +3369,8 @@ impl<'a> Parser<'a> { ExprKind::Binary(binop, lhs, rhs) } - fn mk_index(&self, expr: P, idx: P) -> ExprKind { - ExprKind::Index(expr, idx) + fn mk_index(&self, expr: P, idx: P, brackets_span: Span) -> ExprKind { + ExprKind::Index(expr, idx, brackets_span) } fn mk_call(&self, f: P, args: ThinVec>) -> ExprKind { diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs index 15757a0f1ad..0c0b8b6d094 100644 --- a/compiler/rustc_passes/src/liveness.rs +++ b/compiler/rustc_passes/src/liveness.rs @@ -1061,7 +1061,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> { self.propagate_through_expr(&l, ln) } - hir::ExprKind::Index(ref l, ref r) | hir::ExprKind::Binary(_, ref l, ref r) => { + hir::ExprKind::Index(ref l, ref r, _) | hir::ExprKind::Binary(_, ref l, ref r) => { let r_succ = self.propagate_through_expr(&r, succ); self.propagate_through_expr(&l, r_succ) } diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 0e9d74480a9..7b590d16d8c 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -4269,7 +4269,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { ExprKind::ConstBlock(ref ct) => { self.resolve_anon_const(ct, AnonConstKind::InlineConst); } - ExprKind::Index(ref elem, ref idx) => { + ExprKind::Index(ref elem, ref idx, _) => { self.resolve_expr(elem, Some(expr)); self.visit_expr(idx); } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 46c31c77613..85bbd495dd8 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -2854,7 +2854,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.note("all local variables must have a statically known size"); } Some(Node::Local(hir::Local { - init: Some(hir::Expr { kind: hir::ExprKind::Index(_, _), span, .. }), + init: Some(hir::Expr { kind: hir::ExprKind::Index(..), span, .. }), .. })) => { // When encountering an assignment of an unsized trait, like diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index a5ec979ccd9..137bd9c9451 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -800,7 +800,7 @@ fn in_postfix_position<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> boo && parent.span.ctxt() == e.span.ctxt() { match parent.kind { - ExprKind::Call(child, _) | ExprKind::MethodCall(_, child, _, _) | ExprKind::Index(child, _) + ExprKind::Call(child, _) | ExprKind::MethodCall(_, child, _, _) | ExprKind::Index(child, _, _) if child.hir_id == e.hir_id => true, ExprKind::Field(_, _) | ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar) => true, _ => false, diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs index 83445c7a0ca..57df5683c9d 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -221,7 +221,7 @@ fn is_mutated_static(e: &hir::Expr<'_>) -> bool { match e.kind { Path(QPath::Resolved(_, path)) => !matches!(path.res, Res::Local(_)), Path(_) => true, - Field(inner, _) | Index(inner, _) => is_mutated_static(inner), + Field(inner, _) | Index(inner, _, _) => is_mutated_static(inner), _ => false, } } diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs index 01a7c497cbe..f507f45d5bb 100644 --- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs +++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs @@ -254,7 +254,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> { // Checking for slice indexing let parent_id = map.parent_id(expr.hir_id); if let Some(hir::Node::Expr(parent_expr)) = map.find(parent_id); - if let hir::ExprKind::Index(_, index_expr) = parent_expr.kind; + if let hir::ExprKind::Index(_, index_expr, _) = parent_expr.kind; if let Some(Constant::Int(index_value)) = constant(cx, cx.typeck_results(), index_expr); if let Ok(index_value) = index_value.try_into(); if index_value < max_suggested_slice; diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs index 22c14d9b04d..4f4f571773f 100644 --- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs @@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing { return; } - if let ExprKind::Index(array, index) = &expr.kind { + if let ExprKind::Index(array, index, _) = &expr.kind { let note = "the suggestion might not be applicable in constant blocks"; let ty = cx.typeck_results().expr_ty(array).peel_refs(); if let Some(range) = higher::Range::hir(index) { diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs index 7d1f8ef29c8..d3fd0e8639e 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs @@ -60,8 +60,8 @@ pub(super) fn check<'tcx>( o.and_then(|(lhs, rhs)| { let rhs = fetch_cloned_expr(rhs); if_chain! { - if let ExprKind::Index(base_left, idx_left) = lhs.kind; - if let ExprKind::Index(base_right, idx_right) = rhs.kind; + if let ExprKind::Index(base_left, idx_left, _) = lhs.kind; + if let ExprKind::Index(base_right, idx_right, _) = rhs.kind; if let Some(ty) = get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_left)); if get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_right)).is_some(); if let Some((start_left, offset_left)) = get_details_from_idx(cx, idx_left, &starts); diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs index 2c20e9e8693..c4af46b8fd3 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs @@ -319,7 +319,7 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { if_chain! { // an index op - if let ExprKind::Index(seqexpr, idx) = expr.kind; + if let ExprKind::Index(seqexpr, idx, _) = expr.kind; if !self.check(idx, seqexpr, expr); then { return; diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs index 7da9121fbb3..dd77c69ef88 100644 --- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs @@ -162,7 +162,7 @@ fn never_loop_expr<'tcx>( ExprKind::Binary(_, e1, e2) | ExprKind::Assign(e1, e2, _) | ExprKind::AssignOp(_, e1, e2) - | ExprKind::Index(e1, e2) => never_loop_expr_all(cx, &mut [e1, e2].iter().copied(), ignore_ids, main_loop_id), + | ExprKind::Index(e1, e2, _) => never_loop_expr_all(cx, &mut [e1, e2].iter().copied(), ignore_ids, main_loop_id), ExprKind::Loop(b, _, _, _) => { // Break can come from the inner loop so remove them. absorb_break(never_loop_block(cx, b, ignore_ids, main_loop_id)) diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs index 8019f906aca..5153070cfe6 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs @@ -113,7 +113,7 @@ fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option { + ExprKind::Index(base, idx, _) if !idx.can_have_side_effects() => { can_move = false; fields.clear(); e = base; diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs index 2b9def1a688..201bb56efcd 100644 --- a/src/tools/clippy/clippy_lints/src/manual_strip.rs +++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs @@ -204,7 +204,7 @@ fn find_stripping<'tcx>( if_chain! { if is_ref_str(self.cx, ex); let unref = peel_ref(ex); - if let ExprKind::Index(indexed, index) = &unref.kind; + if let ExprKind::Index(indexed, index, _) = &unref.kind; if let Some(higher::Range { start, end, .. }) = higher::Range::hir(index); if let ExprKind::Path(path) = &indexed.kind; if self.cx.qpath_res(path, ex.hir_id) == self.target; diff --git a/src/tools/clippy/clippy_lints/src/matches/match_on_vec_items.rs b/src/tools/clippy/clippy_lints/src/matches/match_on_vec_items.rs index 89dcac4d849..bd53ebd48c8 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_on_vec_items.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_on_vec_items.rs @@ -12,7 +12,7 @@ use super::MATCH_ON_VEC_ITEMS; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>) { if_chain! { if let Some(idx_expr) = is_vec_indexing(cx, scrutinee); - if let ExprKind::Index(vec, idx) = idx_expr.kind; + if let ExprKind::Index(vec, idx, _) = idx_expr.kind; then { // FIXME: could be improved to suggest surrounding every pattern with Some(_), @@ -36,7 +36,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>) { fn is_vec_indexing<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> { if_chain! { - if let ExprKind::Index(array, index) = expr.kind; + if let ExprKind::Index(array, index, _) = expr.kind; if is_vector(cx, array); if !is_full_range(cx, index); diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_next.rs b/src/tools/clippy/clippy_lints/src/methods/filter_next.rs index ce7f9997b5a..ac7bc9bcca4 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_next.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_next.rs @@ -12,7 +12,7 @@ use super::FILTER_NEXT; fn path_to_local(expr: &hir::Expr<'_>) -> Option { match expr.kind { hir::ExprKind::Field(f, _) => path_to_local(f), - hir::ExprKind::Index(recv, _) => path_to_local(recv), + hir::ExprKind::Index(recv, _, _) => path_to_local(recv), hir::ExprKind::Path(hir::QPath::Resolved( _, hir::Path { diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs b/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs index e2029da8081..8f885e9f729 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs @@ -27,7 +27,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal if derefs_to_slice(cx, caller_expr, cx.typeck_results().expr_ty(caller_expr)).is_some() { // caller is a Slice if_chain! { - if let hir::ExprKind::Index(caller_var, index_expr) = &caller_expr.kind; + if let hir::ExprKind::Index(caller_var, index_expr, _) = &caller_expr.kind; if let Some(higher::Range { start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen }) = higher::Range::hir(index_expr); if let hir::ExprKind::Lit(start_lit) = &start_expr.kind; diff --git a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs index 57ec3a1f1e6..367cd6bd413 100644 --- a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs @@ -239,7 +239,7 @@ fn check_expr<'tcx>(vis: &mut ReadVisitor<'_, 'tcx>, expr: &'tcx Expr<'_>) -> St | ExprKind::MethodCall(..) | ExprKind::Call(_, _) | ExprKind::Assign(..) - | ExprKind::Index(_, _) + | ExprKind::Index(..) | ExprKind::Repeat(_, _) | ExprKind::Struct(_, _, _) => { walk_expr(vis, expr); diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs index 46457400abc..f6b87b071b9 100644 --- a/src/tools/clippy/clippy_lints/src/needless_bool.rs +++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs @@ -119,7 +119,7 @@ fn condition_needs_parentheses(e: &Expr<'_>) -> bool { | ExprKind::Call(i, _) | ExprKind::Cast(i, _) | ExprKind::Type(i, _) - | ExprKind::Index(i, _) = inner.kind + | ExprKind::Index(i, _, _) = inner.kind { if matches!( i.kind, diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index e6d3e72d1e6..5f2a324b05f 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -160,7 +160,7 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { match peel_blocks(expr).kind { ExprKind::Lit(..) | ExprKind::Closure { .. } => true, ExprKind::Path(..) => !has_drop(cx, cx.typeck_results().expr_ty(expr)), - ExprKind::Index(a, b) | ExprKind::Binary(_, a, b) => has_no_effect(cx, a) && has_no_effect(cx, b), + ExprKind::Index(a, b, _) | ExprKind::Binary(_, a, b) => has_no_effect(cx, a) && has_no_effect(cx, b), ExprKind::Array(v) | ExprKind::Tup(v) => v.iter().all(|val| has_no_effect(cx, val)), ExprKind::Repeat(inner, _) | ExprKind::Cast(inner, _) @@ -263,7 +263,7 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option Some(vec![a, b]), + ExprKind::Index(a, b, _) => Some(vec![a, b]), ExprKind::Binary(ref binop, a, b) if binop.node != BinOpKind::And && binop.node != BinOpKind::Or => { Some(vec![a, b]) }, diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index a70692d8ff8..243192385c2 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -438,7 +438,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst { dereferenced_expr = parent_expr; }, - ExprKind::Index(e, _) if ptr::eq(&**e, cur_expr) => { + ExprKind::Index(e, _, _) if ptr::eq(&**e, cur_expr) => { // `e[i]` => desugared to `*Index::index(&e, i)`, // meaning `e` must be referenced. // no need to go further up since a method call is involved now. diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index 42299d8d42f..8009b00b4b9 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -695,7 +695,7 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args: } }, // Indexing is fine for currently supported types. - ExprKind::Index(e, _) if e.hir_id == child_id => (), + ExprKind::Index(e, _, _) if e.hir_id == child_id => (), _ => set_skip_flag(), }, _ => set_skip_flag(), diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs index 8277ce724d4..4abfa0fc35c 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs @@ -81,7 +81,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { if_chain! { if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind; if addressee.span.ctxt() == ctxt; - if let ExprKind::Index(indexed, range) = addressee.kind; + if let ExprKind::Index(indexed, range, _) = addressee.kind; if is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull); then { let (expr_ty, expr_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(expr)); diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index 4d45091f4b5..76f463fff7d 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -190,7 +190,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd { ); } }, - ExprKind::Index(target, _idx) => { + ExprKind::Index(target, _idx, _) => { let e_ty = cx.typeck_results().expr_ty(target).peel_refs(); if e_ty.is_str() || is_type_lang_item(cx, e_ty, LangItem::String) { span_lint( @@ -262,7 +262,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes { // Find string::as_bytes if let ExprKind::AddrOf(BorrowKind::Ref, _, args) = args[0].kind; - if let ExprKind::Index(left, right) = args.kind; + if let ExprKind::Index(left, right, _) = args.kind; let (method_names, expressions, _) = method_calls(left, 1); if method_names.len() == 1; if expressions.len() == 1; diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs index e2cdc48b583..23d6e2a845f 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs @@ -572,7 +572,7 @@ fn ident_difference_expr_with_base_location( | (AddrOf(_, _, _), AddrOf(_, _, _)) | (Path(_, _), Path(_, _)) | (Range(_, _, _), Range(_, _, _)) - | (Index(_, _), Index(_, _)) + | (Index(_, _, _), Index(_, _, _)) | (Field(_, _), Field(_, _)) | (AssignOp(_, _, _), AssignOp(_, _, _)) | (Assign(_, _, _), Assign(_, _, _)) diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs index 98158ed0aee..548fabb8b73 100644 --- a/src/tools/clippy/clippy_lints/src/swap.rs +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -86,8 +86,8 @@ fn generate_swap_warning(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>, spa let mut applicability = Applicability::MachineApplicable; if !can_mut_borrow_both(cx, e1, e2) { - if let ExprKind::Index(lhs1, idx1) = e1.kind - && let ExprKind::Index(lhs2, idx2) = e2.kind + if let ExprKind::Index(lhs1, idx1, _) = e1.kind + && let ExprKind::Index(lhs2, idx2, _) = e2.kind && eq_expr_value(cx, lhs1, lhs2) && e1.span.ctxt() == ctxt && e2.span.ctxt() == ctxt diff --git a/src/tools/clippy/clippy_lints/src/temporary_assignment.rs b/src/tools/clippy/clippy_lints/src/temporary_assignment.rs index 3766b8f8ed1..b6b653f6610 100644 --- a/src/tools/clippy/clippy_lints/src/temporary_assignment.rs +++ b/src/tools/clippy/clippy_lints/src/temporary_assignment.rs @@ -33,7 +33,7 @@ impl<'tcx> LateLintPass<'tcx> for TemporaryAssignment { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Assign(target, ..) = &expr.kind { let mut base = target; - while let ExprKind::Field(f, _) | ExprKind::Index(f, _) = &base.kind { + while let ExprKind::Field(f, _) | ExprKind::Index(f, _, _) = &base.kind { base = f; } if is_temporary(base) && !is_adjusted(cx, base) { diff --git a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs index 7eec6982092..78ad52d8a87 100644 --- a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs @@ -103,11 +103,11 @@ fn check_tuple<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, elements: & // Fix #11100 && tys.iter().all_equal() && let Some(locals) = (match first.kind { - ExprKind::Index(_, _) => elements + ExprKind::Index(..) => elements .iter() .enumerate() .map(|(i, i_expr)| -> Option<&'tcx Expr<'tcx>> { - if let ExprKind::Index(lhs, index) = i_expr.kind + if let ExprKind::Index(lhs, index, _) = i_expr.kind && let ExprKind::Lit(lit) = index.kind && let LitKind::Int(val, _) = lit.node { diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 6b51974d739..f02c33cc674 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -526,7 +526,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { self.ident(field_name); self.expr(object); }, - ExprKind::Index(object, index) => { + ExprKind::Index(object, index, _) => { bind!(self, object, index); kind!("Index({object}, {index})"); self.expr(object); diff --git a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs index 1d4fc24eb6c..3fa51216c77 100644 --- a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs +++ b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs @@ -88,7 +88,7 @@ impl VecPushSearcher { let mut last_place = parent; while let Some(parent) = get_parent_expr(cx, last_place) { if matches!(parent.kind, ExprKind::Unary(UnOp::Deref, _) | ExprKind::Field(..)) - || matches!(parent.kind, ExprKind::Index(e, _) if e.hir_id == last_place.hir_id) + || matches!(parent.kind, ExprKind::Index(e, _, _) if e.hir_id == last_place.hir_id) { last_place = parent; } else { diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index 7e42924603a..2d0d6f559ad 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -178,7 +178,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l, r), (Break(ll, le), Break(rl, re)) => eq_label(ll, rl) && eq_expr_opt(le, re), (Continue(ll), Continue(rl)) => eq_label(ll, rl), - (Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2), Index(r1, r2)) => eq_expr(l1, r1) && eq_expr(l2, r2), + (Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2, _), Index(r1, r2, _)) => eq_expr(l1, r1) && eq_expr(l2, r2), (AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv), (Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp), (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, eq_arm), diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs index 89f8a65c5ae..60fab1ec41a 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -163,7 +163,7 @@ fn expr_search_pat(tcx: TyCtxt<'_>, e: &Expr<'_>) -> (Pat, Pat) { ) => (Pat::Str("unsafe"), Pat::Str("}")), ExprKind::Block(_, None) => (Pat::Str("{"), Pat::Str("}")), ExprKind::Field(e, name) => (expr_search_pat(tcx, e).0, Pat::Sym(name.name)), - ExprKind::Index(e, _) => (expr_search_pat(tcx, e).0, Pat::Str("]")), + ExprKind::Index(e, _, _) => (expr_search_pat(tcx, e).0, Pat::Str("]")), ExprKind::Path(ref path) => qpath_search_pat(path), ExprKind::AddrOf(_, _, e) => (Pat::Str("&"), expr_search_pat(tcx, e).1), ExprKind::Break(Destination { label: None, .. }, None) => (Pat::Str("break"), Pat::Str("break")), diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index f19e09a18ec..d38e3f1ae76 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -394,7 +394,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { } } }, - ExprKind::Index(arr, index) => self.index(arr, index), + ExprKind::Index(arr, index, _) => self.index(arr, index), ExprKind::AddrOf(_, _, inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))), ExprKind::Field(local_expr, ref field) => { let result = self.expr(local_expr); diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs index 3c969572b8e..0bcefba75a7 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -185,7 +185,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS .type_dependent_def_id(e.hir_id) .map_or(Lazy, |id| fn_eagerness(self.cx, id, name.ident.name, true)); }, - ExprKind::Index(_, e) => { + ExprKind::Index(_, e, _) => { let ty = self.cx.typeck_results().expr_ty_adjusted(e); if is_copy(self.cx, ty) && !ty.is_ref() { self.eagerness |= NoChange; diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 85b3b005f93..fdc35cd4ddf 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -299,7 +299,7 @@ impl HirEqInterExpr<'_, '_, '_> { (&ExprKind::Field(l_f_exp, ref l_f_ident), &ExprKind::Field(r_f_exp, ref r_f_ident)) => { l_f_ident.name == r_f_ident.name && self.eq_expr(l_f_exp, r_f_exp) }, - (&ExprKind::Index(la, li), &ExprKind::Index(ra, ri)) => self.eq_expr(la, ra) && self.eq_expr(li, ri), + (&ExprKind::Index(la, li, _), &ExprKind::Index(ra, ri, _)) => self.eq_expr(la, ra) && self.eq_expr(li, ri), (&ExprKind::If(lc, lt, ref le), &ExprKind::If(rc, rt, ref re)) => { self.eq_expr(lc, rc) && self.eq_expr(lt, rt) && both(le, re, |l, r| self.eq_expr(l, r)) }, @@ -730,7 +730,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { self.hash_expr(e); self.hash_name(f.name); }, - ExprKind::Index(a, i) => { + ExprKind::Index(a, i, _) => { self.hash_expr(a); self.hash_expr(i); }, diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index cf30930b76e..4cd8a8c3325 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -735,7 +735,7 @@ fn projection_stack<'a, 'hir>(mut e: &'a Expr<'hir>) -> (Vec<&'a Expr<'hir>>, &' let mut result = vec![]; let root = loop { match e.kind { - ExprKind::Index(ep, _) | ExprKind::Field(ep, _) => { + ExprKind::Index(ep, _, _) | ExprKind::Field(ep, _) => { result.push(e); e = ep; }, @@ -782,7 +782,7 @@ pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) - return true; } }, - (ExprKind::Index(_, i1), ExprKind::Index(_, i2)) => { + (ExprKind::Index(_, i1, _), ExprKind::Index(_, i2, _)) => { if !eq_expr_value(cx, i1, i2) { return false; } diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs index 45b5483e323..0669ea72eb3 100644 --- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs @@ -31,7 +31,7 @@ fn expr_type_certainty(cx: &LateContext<'_>, expr: &Expr<'_>) -> Certainty { let certainty = match &expr.kind { ExprKind::Unary(_, expr) | ExprKind::Field(expr, _) - | ExprKind::Index(expr, _) + | ExprKind::Index(expr, _, _) | ExprKind::AddrOf(_, _, expr) => expr_type_certainty(cx, expr), ExprKind::Array(exprs) => join(exprs.iter().map(|expr| expr_type_certainty(cx, expr))), diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index 09f447b27eb..f83988a6e32 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -329,7 +329,7 @@ pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {}, ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_ref() => (), ExprKind::Unary(_, e) if self.cx.typeck_results().expr_ty(e).peel_refs().is_primitive_ty() => (), - ExprKind::Index(base, _) + ExprKind::Index(base, _, _) if matches!( self.cx.typeck_results().expr_ty(base).peel_refs().kind(), ty::Slice(_) | ty::Array(..) @@ -629,7 +629,7 @@ pub fn for_each_unconsumed_temporary<'tcx, B>( helper(typeck, true, arg, f)?; } }, - ExprKind::Index(borrowed, consumed) + ExprKind::Index(borrowed, consumed, _) | ExprKind::Assign(borrowed, consumed, _) | ExprKind::AssignOp(_, borrowed, consumed) => { helper(typeck, false, borrowed, f)?; diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index 739afb4e0ac..c3c07f310bf 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -256,7 +256,7 @@ pub(crate) fn format_expr( shape, SeparatorPlace::Back, ), - ast::ExprKind::Index(ref expr, ref index) => { + ast::ExprKind::Index(ref expr, ref index, _) => { rewrite_index(&**expr, &**index, context, shape) } ast::ExprKind::Repeat(ref expr, ref repeats) => rewrite_pair( @@ -1342,7 +1342,7 @@ pub(crate) fn is_simple_expr(expr: &ast::Expr) -> bool { | ast::ExprKind::Field(ref expr, _) | ast::ExprKind::Try(ref expr) | ast::ExprKind::Unary(_, ref expr) => is_simple_expr(expr), - ast::ExprKind::Index(ref lhs, ref rhs) => is_simple_expr(lhs) && is_simple_expr(rhs), + ast::ExprKind::Index(ref lhs, ref rhs, _) => is_simple_expr(lhs) && is_simple_expr(rhs), ast::ExprKind::Repeat(ref lhs, ref rhs) => { is_simple_expr(lhs) && is_simple_expr(&*rhs.value) } diff --git a/src/tools/rustfmt/src/matches.rs b/src/tools/rustfmt/src/matches.rs index aac5e59b860..4c37116f120 100644 --- a/src/tools/rustfmt/src/matches.rs +++ b/src/tools/rustfmt/src/matches.rs @@ -594,7 +594,7 @@ fn can_flatten_block_around_this(body: &ast::Expr) -> bool { ast::ExprKind::AddrOf(_, _, ref expr) | ast::ExprKind::Try(ref expr) | ast::ExprKind::Unary(_, ref expr) - | ast::ExprKind::Index(ref expr, _) + | ast::ExprKind::Index(ref expr, _, _) | ast::ExprKind::Cast(ref expr, _) => can_flatten_block_around_this(expr), _ => false, } diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index 890a05b8c82..4fc5a9b6896 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -441,7 +441,7 @@ pub(crate) fn left_most_sub_expr(e: &ast::Expr) -> &ast::Expr { | ast::ExprKind::Assign(ref e, _, _) | ast::ExprKind::AssignOp(_, ref e, _) | ast::ExprKind::Field(ref e, _) - | ast::ExprKind::Index(ref e, _) + | ast::ExprKind::Index(ref e, _, _) | ast::ExprKind::Range(Some(ref e), _, _) | ast::ExprKind::Try(ref e) => left_most_sub_expr(e), _ => e, @@ -479,7 +479,7 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr | ast::ExprKind::Match(..) => repr.contains('\n'), ast::ExprKind::Paren(ref expr) | ast::ExprKind::Binary(_, _, ref expr) - | ast::ExprKind::Index(_, ref expr) + | ast::ExprKind::Index(_, ref expr, _) | ast::ExprKind::Unary(_, ref expr) | ast::ExprKind::Try(ref expr) | ast::ExprKind::Yield(Some(ref expr)) => is_block_expr(context, expr, repr), diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 44c7c07d3a0..930b7408390 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -11,7 +11,7 @@ use std::path::{Path, PathBuf}; const ENTRY_LIMIT: usize = 900; // FIXME: The following limits should be reduced eventually. const ISSUES_ENTRY_LIMIT: usize = 1893; -const ROOT_ENTRY_LIMIT: usize = 873; +const ROOT_ENTRY_LIMIT: usize = 866; const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[ "rs", // test source files diff --git a/tests/ui/array-slice-vec/slice-2.stderr b/tests/ui/array-slice-vec/slice-2.stderr index 561feb90f0a..b122b46914c 100644 --- a/tests/ui/array-slice-vec/slice-2.stderr +++ b/tests/ui/array-slice-vec/slice-2.stderr @@ -1,26 +1,26 @@ error[E0608]: cannot index into a value of type `Foo` - --> $DIR/slice-2.rs:7:6 + --> $DIR/slice-2.rs:7:7 | LL | &x[..]; - | ^^^^^ + | ^^^^ error[E0608]: cannot index into a value of type `Foo` - --> $DIR/slice-2.rs:8:6 + --> $DIR/slice-2.rs:8:7 | LL | &x[Foo..]; - | ^^^^^^^^ + | ^^^^^^^ error[E0608]: cannot index into a value of type `Foo` - --> $DIR/slice-2.rs:9:6 + --> $DIR/slice-2.rs:9:7 | LL | &x[..Foo]; - | ^^^^^^^^ + | ^^^^^^^ error[E0608]: cannot index into a value of type `Foo` - --> $DIR/slice-2.rs:10:6 + --> $DIR/slice-2.rs:10:7 | LL | &x[Foo..Foo]; - | ^^^^^^^^^^^ + | ^^^^^^^^^^ error: aborting due to 4 previous errors diff --git a/tests/ui/borrowck/suggest-local-var-for-vector.stderr b/tests/ui/borrowck/suggest-local-var-for-vector.stderr index ea92d76b4ec..c8d00f7b222 100644 --- a/tests/ui/borrowck/suggest-local-var-for-vector.stderr +++ b/tests/ui/borrowck/suggest-local-var-for-vector.stderr @@ -3,10 +3,10 @@ error[E0502]: cannot borrow `vec` as immutable because it is also borrowed as mu | LL | vec[vec.len() - 1] = 123; | ----^^^----------- - | | | - | | immutable borrow occurs here + | | || + | | |immutable borrow occurs here + | | mutable borrow later used here | mutable borrow occurs here - | mutable borrow later used here | help: try adding a local storing this... --> $DIR/suggest-local-var-for-vector.rs:3:9 @@ -14,10 +14,10 @@ help: try adding a local storing this... LL | vec[vec.len() - 1] = 123; | ^^^^^^^^^ help: ...and then using that local here - --> $DIR/suggest-local-var-for-vector.rs:3:5 + --> $DIR/suggest-local-var-for-vector.rs:3:8 | LL | vec[vec.len() - 1] = 123; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/tests/ui/borrowck/suggest-storing-local-var-for-vector.stderr b/tests/ui/borrowck/suggest-storing-local-var-for-vector.stderr index 6007beb7753..368d728101c 100644 --- a/tests/ui/borrowck/suggest-storing-local-var-for-vector.stderr +++ b/tests/ui/borrowck/suggest-storing-local-var-for-vector.stderr @@ -3,10 +3,10 @@ error[E0502]: cannot borrow `vec` as immutable because it is also borrowed as mu | LL | vec[vec.len() - 1] = 123; | ----^^^----------- - | | | - | | immutable borrow occurs here + | | || + | | |immutable borrow occurs here + | | mutable borrow later used here | mutable borrow occurs here - | mutable borrow later used here | help: try adding a local storing this... --> $DIR/suggest-storing-local-var-for-vector.rs:3:9 @@ -14,10 +14,10 @@ help: try adding a local storing this... LL | vec[vec.len() - 1] = 123; | ^^^^^^^^^ help: ...and then using that local here - --> $DIR/suggest-storing-local-var-for-vector.rs:3:5 + --> $DIR/suggest-storing-local-var-for-vector.rs:3:8 | LL | vec[vec.len() - 1] = 123; - | ^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^ error: aborting due to previous error diff --git a/tests/ui/borrowck/two-phase-nonrecv-autoref.base.stderr b/tests/ui/borrowck/two-phase-nonrecv-autoref.base.stderr index efd63a08aae..e122977b9f2 100644 --- a/tests/ui/borrowck/two-phase-nonrecv-autoref.base.stderr +++ b/tests/ui/borrowck/two-phase-nonrecv-autoref.base.stderr @@ -50,42 +50,42 @@ error[E0502]: cannot borrow `i` as immutable because it is also borrowed as muta | LL | i[i[3]] = 4; | --^---- - | | | - | | immutable borrow occurs here + | ||| + | ||immutable borrow occurs here + | |mutable borrow later used here | mutable borrow occurs here - | mutable borrow later used here | help: try adding a local storing this... - --> $DIR/two-phase-nonrecv-autoref.rs:132:7 + --> $DIR/two-phase-nonrecv-autoref.rs:132:8 | LL | i[i[3]] = 4; - | ^^^^ + | ^^^ help: ...and then using that local here - --> $DIR/two-phase-nonrecv-autoref.rs:132:5 + --> $DIR/two-phase-nonrecv-autoref.rs:132:6 | LL | i[i[3]] = 4; - | ^^^^^^^ + | ^^^^^^ error[E0502]: cannot borrow `i` as immutable because it is also borrowed as mutable --> $DIR/two-phase-nonrecv-autoref.rs:138:7 | LL | i[i[3]] = i[4]; | --^---- - | | | - | | immutable borrow occurs here + | ||| + | ||immutable borrow occurs here + | |mutable borrow later used here | mutable borrow occurs here - | mutable borrow later used here | help: try adding a local storing this... - --> $DIR/two-phase-nonrecv-autoref.rs:138:7 + --> $DIR/two-phase-nonrecv-autoref.rs:138:8 | LL | i[i[3]] = i[4]; - | ^^^^ + | ^^^ help: ...and then using that local here - --> $DIR/two-phase-nonrecv-autoref.rs:138:5 + --> $DIR/two-phase-nonrecv-autoref.rs:138:6 | LL | i[i[3]] = i[4]; - | ^^^^^^^ + | ^^^^^^ error: aborting due to 7 previous errors diff --git a/tests/ui/consts/issue-94675.stderr b/tests/ui/consts/issue-94675.stderr index cee4dfda2c9..f51f305ac38 100644 --- a/tests/ui/consts/issue-94675.stderr +++ b/tests/ui/consts/issue-94675.stderr @@ -7,10 +7,10 @@ LL | self.bar[0] = baz.len(); = note: calls in constant functions are limited to constant functions, tuple structs and tuple variants error[E0015]: cannot call non-const operator in constant functions - --> $DIR/issue-94675.rs:11:9 + --> $DIR/issue-94675.rs:11:17 | LL | self.bar[0] = baz.len(); - | ^^^^^^^^^^^ + | ^^^ | note: impl defined here, but it is not `const` --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL diff --git a/tests/ui/error-codes/E0608.stderr b/tests/ui/error-codes/E0608.stderr index 3aec509934b..f23f9977ba0 100644 --- a/tests/ui/error-codes/E0608.stderr +++ b/tests/ui/error-codes/E0608.stderr @@ -1,8 +1,8 @@ error[E0608]: cannot index into a value of type `u8` - --> $DIR/E0608.rs:2:5 + --> $DIR/E0608.rs:2:8 | LL | 0u8[2]; - | ^^^^^^ + | ^^^ error: aborting due to previous error diff --git a/tests/ui/index-bot.rs b/tests/ui/indexing/index-bot.rs similarity index 100% rename from tests/ui/index-bot.rs rename to tests/ui/indexing/index-bot.rs diff --git a/tests/ui/index-bot.stderr b/tests/ui/indexing/index-bot.stderr similarity index 78% rename from tests/ui/index-bot.stderr rename to tests/ui/indexing/index-bot.stderr index b5d78297505..bf231c92cad 100644 --- a/tests/ui/index-bot.stderr +++ b/tests/ui/indexing/index-bot.stderr @@ -1,8 +1,8 @@ error[E0608]: cannot index into a value of type `!` - --> $DIR/index-bot.rs:2:5 + --> $DIR/index-bot.rs:2:13 | LL | (return)[0]; - | ^^^^^^^^^^^ + | ^^^ error: aborting due to previous error diff --git a/tests/ui/index-help.rs b/tests/ui/indexing/index-help.rs similarity index 100% rename from tests/ui/index-help.rs rename to tests/ui/indexing/index-help.rs diff --git a/tests/ui/index-help.stderr b/tests/ui/indexing/index-help.stderr similarity index 100% rename from tests/ui/index-help.stderr rename to tests/ui/indexing/index-help.stderr diff --git a/tests/ui/index_message.rs b/tests/ui/indexing/index_message.rs similarity index 100% rename from tests/ui/index_message.rs rename to tests/ui/indexing/index_message.rs diff --git a/tests/ui/index_message.stderr b/tests/ui/indexing/index_message.stderr similarity index 67% rename from tests/ui/index_message.stderr rename to tests/ui/indexing/index_message.stderr index 56d1d70809d..80f2bd52314 100644 --- a/tests/ui/index_message.stderr +++ b/tests/ui/indexing/index_message.stderr @@ -1,8 +1,8 @@ error[E0608]: cannot index into a value of type `({integer},)` - --> $DIR/index_message.rs:3:13 + --> $DIR/index_message.rs:3:14 | LL | let _ = z[0]; - | ^^^^ help: to access tuple elements, use: `z.0` + | ^^^ help: to access tuple elements, use: `.0` error: aborting due to previous error diff --git a/tests/ui/indexing-requires-a-uint.rs b/tests/ui/indexing/indexing-requires-a-uint.rs similarity index 100% rename from tests/ui/indexing-requires-a-uint.rs rename to tests/ui/indexing/indexing-requires-a-uint.rs diff --git a/tests/ui/indexing-requires-a-uint.stderr b/tests/ui/indexing/indexing-requires-a-uint.stderr similarity index 100% rename from tests/ui/indexing-requires-a-uint.stderr rename to tests/ui/indexing/indexing-requires-a-uint.stderr diff --git a/tests/ui/indexing/indexing-spans-caller-location.rs b/tests/ui/indexing/indexing-spans-caller-location.rs new file mode 100644 index 00000000000..2652f00211d --- /dev/null +++ b/tests/ui/indexing/indexing-spans-caller-location.rs @@ -0,0 +1,27 @@ +// run-pass + +// Regression test for https://github.com/rust-lang/rust/issues/114388 + +#[track_caller] +fn caller_line() -> u32 { + std::panic::Location::caller().line() +} + +fn main() { + let prev_line = caller_line(); // first line + (A { prev_line }) // second line + [0]; // third line +} + +struct A { + prev_line: u32, +} +impl std::ops::Index for A { + type Output = (); + + fn index(&self, _idx: usize) -> &() { + // Use the relative number to make it resistent to header changes. + assert_eq!(caller_line(), self.prev_line + 2); + &() + } +} diff --git a/tests/ui/intrinsics/const-eval-select-backtrace-std.run.stderr b/tests/ui/intrinsics/const-eval-select-backtrace-std.run.stderr index 69c7491b2af..a0024c0920f 100644 --- a/tests/ui/intrinsics/const-eval-select-backtrace-std.run.stderr +++ b/tests/ui/intrinsics/const-eval-select-backtrace-std.run.stderr @@ -1,3 +1,3 @@ -thread 'main' panicked at $DIR/const-eval-select-backtrace-std.rs:6:6: +thread 'main' panicked at $DIR/const-eval-select-backtrace-std.rs:6:8: byte index 1 is out of bounds of `` note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace diff --git a/tests/ui/issues/issue-27842.stderr b/tests/ui/issues/issue-27842.stderr index 83333aa0c47..b18fe1512b5 100644 --- a/tests/ui/issues/issue-27842.stderr +++ b/tests/ui/issues/issue-27842.stderr @@ -1,24 +1,24 @@ error[E0608]: cannot index into a value of type `({integer}, {integer}, {integer})` - --> $DIR/issue-27842.rs:4:13 + --> $DIR/issue-27842.rs:4:16 | LL | let _ = tup[0]; - | ^^^^^^ help: to access tuple elements, use: `tup.0` + | ^^^ help: to access tuple elements, use: `.0` error[E0608]: cannot index into a value of type `({integer}, {integer}, {integer})` - --> $DIR/issue-27842.rs:9:13 + --> $DIR/issue-27842.rs:9:16 | LL | let _ = tup[i]; - | ^^^^-^ + | ^-^ | | | cannot access tuple elements at a variable index | = help: to access tuple elements, use tuple indexing syntax (e.g., `tuple.0`) error[E0608]: cannot index into a value of type `({integer},)` - --> $DIR/issue-27842.rs:14:13 + --> $DIR/issue-27842.rs:14:16 | LL | let _ = tup[3]; - | ^^^^^^ + | ^^^ | = help: to access tuple elements, use tuple indexing syntax (e.g., `tuple.0`) diff --git a/tests/ui/issues/issue-40861.stderr b/tests/ui/issues/issue-40861.stderr index 84e38b9bb05..9b6469d05e9 100644 --- a/tests/ui/issues/issue-40861.stderr +++ b/tests/ui/issues/issue-40861.stderr @@ -1,8 +1,8 @@ error[E0608]: cannot index into a value of type `()` - --> $DIR/issue-40861.rs:4:5 + --> $DIR/issue-40861.rs:4:7 | LL | ()[f(&[1.0])]; - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^ | = help: to access tuple elements, use tuple indexing syntax (e.g., `tuple.0`) diff --git a/tests/ui/lint/lint-unconditional-recursion.stderr b/tests/ui/lint/lint-unconditional-recursion.stderr index 9d200a7898e..d75754bf9f9 100644 --- a/tests/ui/lint/lint-unconditional-recursion.stderr +++ b/tests/ui/lint/lint-unconditional-recursion.stderr @@ -139,7 +139,7 @@ error: function cannot return without recursing LL | fn index(&self, x: usize) -> &Baz { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing LL | &self[x] - | ------- recursive call site + | --- recursive call site | = help: a `loop` may express intention better if this is on purpose diff --git a/tests/ui/span/suggestion-non-ascii.stderr b/tests/ui/span/suggestion-non-ascii.stderr index b14632d4e1b..21f8bb62a0c 100644 --- a/tests/ui/span/suggestion-non-ascii.stderr +++ b/tests/ui/span/suggestion-non-ascii.stderr @@ -1,8 +1,8 @@ error[E0608]: cannot index into a value of type `({integer},)` - --> $DIR/suggestion-non-ascii.rs:3:21 + --> $DIR/suggestion-non-ascii.rs:3:24 | LL | println!("☃{}", tup[0]); - | ^^^^^^ help: to access tuple elements, use: `tup.0` + | ^^^ help: to access tuple elements, use: `.0` error: aborting due to previous error diff --git a/tests/ui/typeck/issue-112252-ptr-arithmetics-help.stderr b/tests/ui/typeck/issue-112252-ptr-arithmetics-help.stderr index c55930da225..f81736245f3 100644 --- a/tests/ui/typeck/issue-112252-ptr-arithmetics-help.stderr +++ b/tests/ui/typeck/issue-112252-ptr-arithmetics-help.stderr @@ -38,10 +38,10 @@ LL | let _c = unsafe { _ptr2.offset_from(_ptr1) }; | ++++++++ ~~~~~~~~~~~~~ +++ error[E0608]: cannot index into a value of type `*const u32` - --> $DIR/issue-112252-ptr-arithmetics-help.rs:9:14 + --> $DIR/issue-112252-ptr-arithmetics-help.rs:9:19 | LL | let _d = _ptr1[5]; - | ^^^^^^^^ + | ^^^ | help: consider using `wrapping_add` or `add` for indexing into raw pointer | From 97aa4ba171bc78ccbb96b72bd2eef056ad2ece73 Mon Sep 17 00:00:00 2001 From: Sebastian Toh Date: Fri, 4 Aug 2023 20:12:20 +0800 Subject: [PATCH 43/56] Fix unwrap on None --- .../src/fn_ctxt/suggestions.rs | 3 +- tests/ui/typeck/issue-114423.rs | 15 ++++++ tests/ui/typeck/issue-114423.stderr | 52 +++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 tests/ui/typeck/issue-114423.rs create mode 100644 tests/ui/typeck/issue-114423.stderr diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index d4edd08d302..89bbb4c2203 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -1620,8 +1620,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter() .enumerate() .filter(|x| x.1.hir_id == *hir_id) - .map(|(i, _)| init_tup.get(i).unwrap()) - .next() + .find_map(|(i, _)| init_tup.get(i)) { self.note_type_is_not_clone_inner_expr(init) } else { diff --git a/tests/ui/typeck/issue-114423.rs b/tests/ui/typeck/issue-114423.rs new file mode 100644 index 00000000000..da2dae1c46b --- /dev/null +++ b/tests/ui/typeck/issue-114423.rs @@ -0,0 +1,15 @@ +struct RGB { + g: f64, + b: f64, +} + +fn main() { + let (r, alone_in_path, b): (f32, f32, f32) = (e.clone(), e.clone()); + //~^ ERROR cannot find value `e` in this scope + //~| ERROR cannot find value `e` in this scope + //~| ERROR mismatched types + let _ = RGB { r, g, b }; + //~^ ERROR cannot find value `g` in this scope + //~| ERROR struct `RGB` has no field named `r` + //~| ERROR mismatched types +} diff --git a/tests/ui/typeck/issue-114423.stderr b/tests/ui/typeck/issue-114423.stderr new file mode 100644 index 00000000000..c20a4391297 --- /dev/null +++ b/tests/ui/typeck/issue-114423.stderr @@ -0,0 +1,52 @@ +error[E0425]: cannot find value `e` in this scope + --> $DIR/issue-114423.rs:7:51 + | +LL | let (r, alone_in_path, b): (f32, f32, f32) = (e.clone(), e.clone()); + | ^ not found in this scope + +error[E0425]: cannot find value `e` in this scope + --> $DIR/issue-114423.rs:7:62 + | +LL | let (r, alone_in_path, b): (f32, f32, f32) = (e.clone(), e.clone()); + | ^ not found in this scope + +error[E0425]: cannot find value `g` in this scope + --> $DIR/issue-114423.rs:11:22 + | +LL | let _ = RGB { r, g, b }; + | ^ help: a local variable with a similar name exists: `b` + +error[E0308]: mismatched types + --> $DIR/issue-114423.rs:7:50 + | +LL | let (r, alone_in_path, b): (f32, f32, f32) = (e.clone(), e.clone()); + | --------------- ^^^^^^^^^^^^^^^^^^^^^^ expected a tuple with 3 elements, found one with 2 elements + | | + | expected due to this + | + = note: expected tuple `(f32, f32, f32)` + found tuple `(f32, f32)` + +error[E0560]: struct `RGB` has no field named `r` + --> $DIR/issue-114423.rs:11:19 + | +LL | let _ = RGB { r, g, b }; + | ^ `RGB` does not have this field + | + = note: all struct fields are already assigned + +error[E0308]: mismatched types + --> $DIR/issue-114423.rs:11:25 + | +LL | let _ = RGB { r, g, b }; + | ^ expected `f64`, found `f32` + | +help: you can convert an `f32` to an `f64` + | +LL | let _ = RGB { r, g, b: b.into() }; + | ++ +++++++ + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0308, E0425, E0560. +For more information about an error, try `rustc --explain E0308`. From 3345077b42ec15a4c85b9f5a4c364ccb599a1d0f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 4 Aug 2023 15:00:57 +0200 Subject: [PATCH 44/56] interpret: add mplace_to_ref helper method --- compiler/rustc_const_eval/src/interpret/place.rs | 15 +++++++++++++-- .../rustc_const_eval/src/interpret/terminator.rs | 5 +---- .../src/borrow_tracker/stacked_borrows/mod.rs | 5 ++--- .../miri/src/borrow_tracker/tree_borrows/mod.rs | 5 ++--- 4 files changed, 18 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index 5f4f5434b18..2dc856528f5 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -157,7 +157,6 @@ impl MemPlace { } /// Turn a mplace into a (thin or wide) pointer, as a reference, pointing to the same space. - /// This is the inverse of `ref_to_mplace`. #[inline(always)] pub fn to_ref(self, cx: &impl HasDataLayout) -> Immediate { match self.meta { @@ -415,7 +414,7 @@ where } /// Take a value, which represents a (thin or wide) reference, and make it a place. - /// Alignment is just based on the type. This is the inverse of `MemPlace::to_ref()`. + /// Alignment is just based on the type. This is the inverse of `mplace_to_ref()`. /// /// Only call this if you are sure the place is "valid" (aligned and inbounds), or do not /// want to ever use the place for memory access! @@ -438,6 +437,18 @@ where Ok(MPlaceTy::from_aligned_ptr_with_meta(ptr.to_pointer(self)?, layout, meta)) } + /// Turn a mplace into a (thin or wide) mutable raw pointer, pointing to the same space. + /// `align` information is lost! + /// This is the inverse of `ref_to_mplace`. + pub fn mplace_to_ref( + &self, + mplace: &MPlaceTy<'tcx, M::Provenance>, + ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { + let imm = mplace.to_ref(self); + let layout = self.layout_of(Ty::new_mut_ptr(self.tcx.tcx, mplace.layout.ty))?; + Ok(ImmTy::from_immediate(imm, layout)) + } + /// Take an operand, representing a pointer, and dereference it to a place. /// Corresponds to the `*` operator in Rust. #[instrument(skip(self), level = "debug")] diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs index bf33c5cca10..1bd21473182 100644 --- a/compiler/rustc_const_eval/src/interpret/terminator.rs +++ b/compiler/rustc_const_eval/src/interpret/terminator.rs @@ -852,10 +852,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let instance = ty::Instance::resolve_drop_in_place(*self.tcx, place.layout.ty); let fn_abi = self.fn_abi_of_instance(instance, ty::List::empty())?; - let arg = ImmTy::from_immediate( - place.to_ref(self), - self.layout_of(Ty::new_mut_ptr(self.tcx.tcx, place.layout.ty))?, - ); + let arg = self.mplace_to_ref(&place)?; let ret = MPlaceTy::fake_alloc_zst(self.layout_of(self.tcx.types.unit)?); self.eval_fn_call( diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index e929091b396..75e4b5f8466 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -15,7 +15,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir::{Mutability, RetagKind}; use rustc_middle::ty::{ self, - layout::{HasParamEnv, LayoutOf}, + layout::HasParamEnv, Ty, }; use rustc_target::abi::{Abi, Align, Size}; @@ -993,8 +993,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // We have to turn the place into a pointer to use the usual retagging logic. // (The pointer type does not matter, so we use a raw pointer.) - let ptr_layout = this.layout_of(Ty::new_mut_ptr(this.tcx.tcx, place.layout.ty))?; - let ptr = ImmTy::from_immediate(place.to_ref(this), ptr_layout); + let ptr = this.mplace_to_ref(place)?; // Reborrow it. With protection! That is the entire point. let new_perm = NewPermission::Uniform { perm: Permission::Unique, diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index b2dbe8a70f0..d30745b3b61 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -7,7 +7,7 @@ use rustc_middle::{ mir::{Mutability, RetagKind}, ty::{ self, - layout::{HasParamEnv, LayoutOf}, + layout::HasParamEnv, Ty, }, }; @@ -488,8 +488,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // We have to turn the place into a pointer to use the usual retagging logic. // (The pointer type does not matter, so we use a raw pointer.) - let ptr_layout = this.layout_of(Ty::new_mut_ptr(this.tcx.tcx, place.layout.ty))?; - let ptr = ImmTy::from_immediate(place.to_ref(this), ptr_layout); + let ptr = this.mplace_to_ref(place)?; // Reborrow it. With protection! That is the entire point. let new_perm = NewPermission { initial_state: Permission::new_active(), From d55522aad87c5605d7edd5dd4b37926e8b446117 Mon Sep 17 00:00:00 2001 From: Ali MJ Al-Nasrawy Date: Tue, 11 Jul 2023 14:24:08 +0000 Subject: [PATCH 45/56] don't ICE on higher ranked hidden types --- .../rustc_borrowck/src/region_infer/mod.rs | 25 +++++++------ .../src/region_infer/opaque_types.rs | 15 ++++++++ .../src/infer/error_reporting/mod.rs | 9 +++++ tests/ui/impl-trait/nested-rpit-hrtb-2.rs | 9 +++++ tests/ui/impl-trait/nested-rpit-hrtb-2.stderr | 12 +++++++ .../type-alias-impl-trait/nested-tait-hrtb.rs | 15 ++++++++ .../nested-tait-hrtb.stderr | 35 +++++++++++++++++++ 7 files changed, 109 insertions(+), 11 deletions(-) create mode 100644 tests/ui/impl-trait/nested-rpit-hrtb-2.rs create mode 100644 tests/ui/impl-trait/nested-rpit-hrtb-2.stderr create mode 100644 tests/ui/type-alias-impl-trait/nested-tait-hrtb.rs create mode 100644 tests/ui/type-alias-impl-trait/nested-tait-hrtb.stderr diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 30dfb2d919a..b8cd94e5422 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -784,13 +784,20 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// is considered a *lower bound*. If possible, we will modify /// the constraint to set it equal to one of the option regions. /// If we make any changes, returns true, else false. + /// + /// This function only adds the member constraints to the region graph, + /// it does not check them. They are later checked in + /// `check_member_constraints` after the region graph has been computed. #[instrument(skip(self, member_constraint_index), level = "debug")] fn apply_member_constraint( &mut self, scc: ConstraintSccIndex, member_constraint_index: NllMemberConstraintIndex, choice_regions: &[ty::RegionVid], - ) -> bool { + ) { + // Lazily compute the reverse graph, we'll need it later. + self.compute_reverse_scc_graph(); + // Create a mutable vector of the options. We'll try to winnow // them down. let mut choice_regions: Vec = choice_regions.to_vec(); @@ -805,10 +812,11 @@ impl<'tcx> RegionInferenceContext<'tcx> { *c_r = self.scc_representatives[scc]; } - // The 'member region' in a member constraint is part of the - // hidden type, which must be in the root universe. Therefore, - // it cannot have any placeholders in its value. - assert!(self.scc_universes[scc] == ty::UniverseIndex::ROOT); + // If the member region lives in a higher universe, we currently choose + // the most conservative option by leaving it unchanged. + if self.scc_universes[scc] != ty::UniverseIndex::ROOT { + return; + } debug_assert!( self.scc_values.placeholders_contained_in(scc).next().is_none(), "scc {:?} in a member constraint has placeholder value: {:?}", @@ -832,7 +840,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { // free region that must outlive the member region `R0` (`UB: // R0`). Therefore, we need only keep an option `O` if `UB: O` // for all UB. - self.compute_reverse_scc_graph(); let universal_region_relations = &self.universal_region_relations; for ub in self.rev_scc_graph.as_ref().unwrap().upper_bounds(scc) { debug!(?ub); @@ -867,7 +874,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } }) else { debug!("no unique minimum choice"); - return false; + return; }; let min_choice_scc = self.constraint_sccs.scc(min_choice); @@ -878,10 +885,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { min_choice, member_constraint_index, }); - - true - } else { - false } } diff --git a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs index 90bde88f792..68dddd65acb 100644 --- a/compiler/rustc_borrowck/src/region_infer/opaque_types.rs +++ b/compiler/rustc_borrowck/src/region_infer/opaque_types.rs @@ -185,6 +185,21 @@ impl<'tcx> RegionInferenceContext<'tcx> { { tcx.fold_regions(ty, |region, _| match *region { ty::ReVar(vid) => { + let scc = self.constraint_sccs.scc(vid); + + // Special handling of higher-ranked regions. + if self.scc_universes[scc] != ty::UniverseIndex::ROOT { + match self.scc_values.placeholders_contained_in(scc).enumerate().last() { + // If the region contains a single placeholder then they're equal. + Some((0, placeholder)) => { + return ty::Region::new_placeholder(tcx, placeholder); + } + + // Fallback: this will produce a cryptic error message. + _ => return region, + } + } + // Find something that we can name let upper_bound = self.approx_universal_upper_bound(vid); let upper_bound = &self.definitions[upper_bound]; diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 69e5bda975e..75cca973306 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -351,6 +351,15 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>( ) } } + ty::RePlaceholder(_) => { + explain_free_region( + tcx, + &mut err, + &format!("hidden type `{}` captures ", hidden_ty), + hidden_region, + "", + ); + } ty::ReError(_) => { err.delay_as_bug(); } diff --git a/tests/ui/impl-trait/nested-rpit-hrtb-2.rs b/tests/ui/impl-trait/nested-rpit-hrtb-2.rs new file mode 100644 index 00000000000..4d72962157b --- /dev/null +++ b/tests/ui/impl-trait/nested-rpit-hrtb-2.rs @@ -0,0 +1,9 @@ +// The nested impl Trait references a higher-ranked region + +trait Trait<'a> { type Assoc; } +impl<'a> Trait<'a> for () { type Assoc = &'a str; } + +fn test() -> impl for<'a> Trait<'a, Assoc = impl Sized> {} +//~^ ERROR captures lifetime that does not appear in bounds + +fn main() {} diff --git a/tests/ui/impl-trait/nested-rpit-hrtb-2.stderr b/tests/ui/impl-trait/nested-rpit-hrtb-2.stderr new file mode 100644 index 00000000000..71d1d45f48b --- /dev/null +++ b/tests/ui/impl-trait/nested-rpit-hrtb-2.stderr @@ -0,0 +1,12 @@ +error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds + --> $DIR/nested-rpit-hrtb-2.rs:6:57 + | +LL | fn test() -> impl for<'a> Trait<'a, Assoc = impl Sized> {} + | -- ---------- ^^ + | | | + | | opaque type defined here + | hidden type `&'a str` captures the lifetime `'a` as defined here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0700`. diff --git a/tests/ui/type-alias-impl-trait/nested-tait-hrtb.rs b/tests/ui/type-alias-impl-trait/nested-tait-hrtb.rs new file mode 100644 index 00000000000..4a9631a7208 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/nested-tait-hrtb.rs @@ -0,0 +1,15 @@ +#![feature(type_alias_impl_trait)] + +trait Trait<'a> { type Assoc; } +impl<'a> Trait<'a> for () { type Assoc = &'a str; } + +type WithoutLt = impl Sized; +fn without_lt() -> impl for<'a> Trait<'a, Assoc = WithoutLt> {} +//~^ ERROR captures lifetime that does not appear in bounds + +type WithLt<'a> = impl Sized + 'a; +//~^ ERROR concrete type differs from previous defining opaque type use +fn with_lt() -> impl for<'a> Trait<'a, Assoc = WithLt<'a>> {} +//~^ ERROR expected generic lifetime parameter, found `'a` + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/nested-tait-hrtb.stderr b/tests/ui/type-alias-impl-trait/nested-tait-hrtb.stderr new file mode 100644 index 00000000000..9a783a6d92a --- /dev/null +++ b/tests/ui/type-alias-impl-trait/nested-tait-hrtb.stderr @@ -0,0 +1,35 @@ +error[E0700]: hidden type for `WithoutLt` captures lifetime that does not appear in bounds + --> $DIR/nested-tait-hrtb.rs:7:62 + | +LL | type WithoutLt = impl Sized; + | ---------- opaque type defined here +LL | fn without_lt() -> impl for<'a> Trait<'a, Assoc = WithoutLt> {} + | -- ^^ + | | + | hidden type `&'a str` captures the lifetime `'a` as defined here + +error[E0792]: expected generic lifetime parameter, found `'a` + --> $DIR/nested-tait-hrtb.rs:12:60 + | +LL | type WithLt<'a> = impl Sized + 'a; + | -- this generic parameter must be used with a generic lifetime parameter +LL | +LL | fn with_lt() -> impl for<'a> Trait<'a, Assoc = WithLt<'a>> {} + | ^^ + +error: concrete type differs from previous defining opaque type use + --> $DIR/nested-tait-hrtb.rs:10:19 + | +LL | type WithLt<'a> = impl Sized + 'a; + | ^^^^^^^^^^^^^^^ expected `&'a str`, got `{type error}` + | +note: previous use here + --> $DIR/nested-tait-hrtb.rs:12:17 + | +LL | fn with_lt() -> impl for<'a> Trait<'a, Assoc = WithLt<'a>> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0700, E0792. +For more information about an error, try `rustc --explain E0700`. From e3700953c14bb941e465a2a5b37fe553cd4adb7e Mon Sep 17 00:00:00 2001 From: klensy Date: Thu, 3 Aug 2023 16:54:16 +0300 Subject: [PATCH 46/56] replace few explicit use of parking_lot with rustc_data_structures::sync onces --- .../rustc_query_system/src/dep_graph/graph.rs | 3 +-- compiler/rustc_query_system/src/query/job.rs | 23 +++++++++---------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index b87757a3e1a..3803f7eca0a 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -1,4 +1,3 @@ -use parking_lot::Mutex; use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::profiling::{EventId, QueryInvocationId, SelfProfilerRef}; @@ -88,7 +87,7 @@ pub struct DepGraphData { colors: DepNodeColorMap, - processed_side_effects: Mutex>, + processed_side_effects: Lock>, /// When we load, there may be `.o` files, cached MIR, or other such /// things available to us. If we find that they are not dirty, we diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index d2140161f1d..bfc51da170d 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -21,12 +21,11 @@ use { parking_lot::{Condvar, Mutex}, rayon_core, rustc_data_structures::fx::FxHashSet, - rustc_data_structures::sync::Lock, - rustc_data_structures::sync::Lrc, rustc_data_structures::{defer, jobserver}, rustc_span::DUMMY_SP, std::iter, std::process, + std::sync::Arc, }; /// Represents a span and a query key. @@ -191,7 +190,7 @@ struct QueryWaiter { query: Option, condvar: Condvar, span: Span, - cycle: Lock>>, + cycle: Mutex>>, } #[cfg(parallel_compiler)] @@ -205,20 +204,20 @@ impl QueryWaiter { #[cfg(parallel_compiler)] struct QueryLatchInfo { complete: bool, - waiters: Vec>>, + waiters: Vec>>, } #[cfg(parallel_compiler)] #[derive(Clone)] pub(super) struct QueryLatch { - info: Lrc>>, + info: Arc>>, } #[cfg(parallel_compiler)] impl QueryLatch { fn new() -> Self { QueryLatch { - info: Lrc::new(Mutex::new(QueryLatchInfo { complete: false, waiters: Vec::new() })), + info: Arc::new(Mutex::new(QueryLatchInfo { complete: false, waiters: Vec::new() })), } } @@ -229,11 +228,11 @@ impl QueryLatch { span: Span, ) -> Result<(), CycleError> { let waiter = - Lrc::new(QueryWaiter { query, span, cycle: Lock::new(None), condvar: Condvar::new() }); + Arc::new(QueryWaiter { query, span, cycle: Mutex::new(None), condvar: Condvar::new() }); self.wait_on_inner(&waiter); // FIXME: Get rid of this lock. We have ownership of the QueryWaiter - // although another thread may still have a Lrc reference so we cannot - // use Lrc::get_mut + // although another thread may still have a Arc reference so we cannot + // use Arc::get_mut let mut cycle = waiter.cycle.lock(); match cycle.take() { None => Ok(()), @@ -242,7 +241,7 @@ impl QueryLatch { } /// Awaits the caller on this latch by blocking the current thread. - fn wait_on_inner(&self, waiter: &Lrc>) { + fn wait_on_inner(&self, waiter: &Arc>) { let mut info = self.info.lock(); if !info.complete { // We push the waiter on to the `waiters` list. It can be accessed inside @@ -276,7 +275,7 @@ impl QueryLatch { /// Removes a single waiter from the list of waiters. /// This is used to break query cycles. - fn extract_waiter(&self, waiter: usize) -> Lrc> { + fn extract_waiter(&self, waiter: usize) -> Arc> { let mut info = self.info.lock(); debug_assert!(!info.complete); // Remove the waiter from the list of waiters @@ -428,7 +427,7 @@ where fn remove_cycle( query_map: &QueryMap, jobs: &mut Vec, - wakelist: &mut Vec>>, + wakelist: &mut Vec>>, ) -> bool { let mut visited = FxHashSet::default(); let mut stack = Vec::new(); From 553508c22e85b1d5deb90da751e13fa6ff207a36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 4 Aug 2023 16:46:08 +0000 Subject: [PATCH 47/56] Reword confusable idents lint Fix #76140. --- compiler/rustc_lint/messages.ftl | 5 +++-- compiler/rustc_lint/src/lints.rs | 4 +++- compiler/rustc_lint/src/non_ascii_idents.rs | 1 + .../lint-confusable-idents.rs | 4 ++-- .../lint-confusable-idents.stderr | 12 ++++++------ 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 16e17fc9d6a..0afd7713dea 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -166,8 +166,9 @@ lint_check_name_warning = {$msg} lint_command_line_source = `forbid` lint level was set on command line -lint_confusable_identifier_pair = identifier pair considered confusable between `{$existing_sym}` and `{$sym}` - .label = this is where the previous identifier occurred +lint_confusable_identifier_pair = found both `{$existing_sym}` and `{$sym}` as identifiers, which look alike + .current_use = this identifier can be confused with `{$existing_sym}` + .other_use = other identifier used here lint_cstring_ptr = getting the inner pointer of a temporary `CString` .as_ptr_label = this pointer will be invalid diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 968172693a9..3c1059debc4 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1056,8 +1056,10 @@ pub struct IdentifierUncommonCodepoints; pub struct ConfusableIdentifierPair { pub existing_sym: Symbol, pub sym: Symbol, - #[label] + #[label(lint_other_use)] pub label: Span, + #[label(lint_current_use)] + pub main_label: Span, } #[derive(LintDiagnostic)] diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs index 4af879b4e91..62bb8c2c67d 100644 --- a/compiler/rustc_lint/src/non_ascii_idents.rs +++ b/compiler/rustc_lint/src/non_ascii_idents.rs @@ -222,6 +222,7 @@ impl EarlyLintPass for NonAsciiIdents { existing_sym: *existing_symbol, sym: symbol, label: *existing_span, + main_label: sp, }, ); } diff --git a/tests/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.rs b/tests/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.rs index e7da825ae36..b2d8a28d3c4 100644 --- a/tests/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.rs +++ b/tests/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.rs @@ -5,8 +5,8 @@ const s: usize = 42; const s_s: usize = 42; fn main() { - let s = "rust"; //~ ERROR identifier pair considered confusable - let s_s = "rust2"; //~ ERROR identifier pair considered confusable + let s = "rust"; //~ ERROR found both + let s_s = "rust2"; //~ ERROR found both not_affected(); } diff --git a/tests/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.stderr b/tests/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.stderr index e9906c83d12..d1920f215e2 100644 --- a/tests/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.stderr +++ b/tests/ui/lint/rfc-2457-non-ascii-idents/lint-confusable-idents.stderr @@ -1,11 +1,11 @@ -error: identifier pair considered confusable between `s` and `s` +error: found both `s` and `s` as identifiers, which look alike --> $DIR/lint-confusable-idents.rs:8:9 | LL | const s: usize = 42; - | -- this is where the previous identifier occurred + | -- other identifier used here ... LL | let s = "rust"; - | ^ + | ^ this identifier can be confused with `s` | note: the lint level is defined here --> $DIR/lint-confusable-idents.rs:1:9 @@ -13,14 +13,14 @@ note: the lint level is defined here LL | #![deny(confusable_idents)] | ^^^^^^^^^^^^^^^^^ -error: identifier pair considered confusable between `s_s` and `s_s` +error: found both `s_s` and `s_s` as identifiers, which look alike --> $DIR/lint-confusable-idents.rs:9:9 | LL | const s_s: usize = 42; - | --- this is where the previous identifier occurred + | --- other identifier used here ... LL | let s_s = "rust2"; - | ^^^^^ + | ^^^^^ this identifier can be confused with `s_s` error: aborting due to 2 previous errors From 3d25b5c7e8653a4ce0513cf8d7bcbd796ed81c4b Mon Sep 17 00:00:00 2001 From: yukang Date: Fri, 4 Aug 2023 13:28:04 +0800 Subject: [PATCH 48/56] Fix ICE failed to get layout for ReferencesError --- .../rustc_codegen_cranelift/src/common.rs | 2 +- compiler/rustc_codegen_gcc/src/context.rs | 2 +- compiler/rustc_codegen_llvm/src/context.rs | 2 +- .../invalid/issue-114435-layout-type-err.rs | 44 +++++++++++++++++++ .../issue-114435-layout-type-err.stderr | 8 ++++ 5 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 tests/ui/invalid/issue-114435-layout-type-err.rs create mode 100644 tests/ui/invalid/issue-114435-layout-type-err.stderr diff --git a/compiler/rustc_codegen_cranelift/src/common.rs b/compiler/rustc_codegen_cranelift/src/common.rs index 67ea20112fe..3081dcfa2b7 100644 --- a/compiler/rustc_codegen_cranelift/src/common.rs +++ b/compiler/rustc_codegen_cranelift/src/common.rs @@ -477,7 +477,7 @@ impl<'tcx> LayoutOfHelpers<'tcx> for RevealAllLayoutCx<'tcx> { #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { - if let layout::LayoutError::SizeOverflow(_) = err { + if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err { self.0.sess.span_fatal(span, err.to_string()) } else { span_bug!(span, "failed to get layout for `{}`: {}", ty, err) diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index 08507e19652..88dcafa7370 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -476,7 +476,7 @@ impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> { #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { - if let LayoutError::SizeOverflow(_) = err { + if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err { self.sess().emit_fatal(respan(span, err.into_diagnostic())) } else { span_bug!(span, "failed to get layout for `{}`: {}", ty, err) diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index cb093996d1d..3577fb2d951 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -985,7 +985,7 @@ impl<'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'_, 'tcx> { #[inline] fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! { - if let LayoutError::SizeOverflow(_) = err { + if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err { self.sess().emit_fatal(Spanned { span, node: err.into_diagnostic() }) } else { span_bug!(span, "failed to get layout for `{ty}`: {err:?}") diff --git a/tests/ui/invalid/issue-114435-layout-type-err.rs b/tests/ui/invalid/issue-114435-layout-type-err.rs new file mode 100644 index 00000000000..a2d40593687 --- /dev/null +++ b/tests/ui/invalid/issue-114435-layout-type-err.rs @@ -0,0 +1,44 @@ +// build-fail +// compile-flags: --crate-type lib -Cdebuginfo=2 +// error-pattern: the type has an unknown layout + +#![recursion_limit = "10"] +macro_rules! link { + ($outer:ident, $inner:ident) => { + struct $outer($inner); + impl $outer { + fn new() -> $outer { + $outer($inner::new()) + } + } + impl std::ops::Deref for $outer { + type Target = $inner; + fn deref(&self) -> &$inner { + &self.0 + } + } + }; +} + +struct Bottom; + +impl Bottom { + fn new() -> Bottom { + Bottom + } +} + + +link!(A, B); +link!(B, C); +link!(C, D); +link!(D, E); +link!(E, F); +link!(F, G); +link!(G, H); +link!(H, I); +link!(I, J); +link!(J, K); +link!(K, Bottom); + +fn main() { } diff --git a/tests/ui/invalid/issue-114435-layout-type-err.stderr b/tests/ui/invalid/issue-114435-layout-type-err.stderr new file mode 100644 index 00000000000..a2db74ff8bd --- /dev/null +++ b/tests/ui/invalid/issue-114435-layout-type-err.stderr @@ -0,0 +1,8 @@ +error: reached the recursion limit finding the struct tail for `Bottom` + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "20"]` + +error: the type has an unknown layout + +error: aborting due to 2 previous errors + From b2a6b99aa3dce16de9f0cdf50e4578b70e96fd06 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 4 Aug 2023 19:42:47 +0200 Subject: [PATCH 49/56] Use similar color scheme to make it obvious they are linked --- src/librustdoc/html/static/css/themes/dark.css | 2 +- src/librustdoc/html/static/css/themes/light.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/html/static/css/themes/dark.css b/src/librustdoc/html/static/css/themes/dark.css index b653f61d536..2b302988734 100644 --- a/src/librustdoc/html/static/css/themes/dark.css +++ b/src/librustdoc/html/static/css/themes/dark.css @@ -68,7 +68,7 @@ --test-arrow-color: #dedede; --test-arrow-background-color: rgba(78, 139, 202, 0.2); --test-arrow-hover-color: #dedede; - --test-arrow-hover-background-color: #4e8bca; + --test-arrow-hover-background-color: rgb(78, 139, 202); --target-background-color: #494a3d; --target-border-color: #bb7410; --kbd-color: #000; diff --git a/src/librustdoc/html/static/css/themes/light.css b/src/librustdoc/html/static/css/themes/light.css index 6be25fc0544..56fd8cbef12 100644 --- a/src/librustdoc/html/static/css/themes/light.css +++ b/src/librustdoc/html/static/css/themes/light.css @@ -68,7 +68,7 @@ --test-arrow-color: #f5f5f5; --test-arrow-background-color: rgba(78, 139, 202, 0.2); --test-arrow-hover-color: #f5f5f5; - --test-arrow-hover-background-color: #4e8bca; + --test-arrow-hover-background-color: rgb(78, 139, 202); --target-background-color: #fdffd3; --target-border-color: #ad7c37; --kbd-color: #000; From 85ed2feb67556c3cece64aacc5abaf35ecfed93a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 4 Aug 2023 19:42:56 +0200 Subject: [PATCH 50/56] Migrate GUI colors test to original CSS color format --- tests/rustdoc-gui/run-on-hover.goml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/rustdoc-gui/run-on-hover.goml b/tests/rustdoc-gui/run-on-hover.goml index b5fc49eacac..1f87febcec6 100644 --- a/tests/rustdoc-gui/run-on-hover.goml +++ b/tests/rustdoc-gui/run-on-hover.goml @@ -33,22 +33,22 @@ define-function: ( call-function: ("check-run-button", { "theme": "ayu", - "color": "rgb(120, 135, 151)", + "color": "#788797", "background": "rgba(57, 175, 215, 0.09)", - "hover_color": "rgb(197, 197, 197)", + "hover_color": "#c5c5c5", "hover_background": "rgba(57, 175, 215, 0.37)", }) call-function: ("check-run-button", { "theme": "dark", - "color": "rgb(222, 222, 222)", + "color": "#dedede", "background": "rgba(78, 139, 202, 0.2)", - "hover_color": "rgb(222, 222, 222)", + "hover_color": "#dedede", "hover_background": "rgb(78, 139, 202)", }) call-function: ("check-run-button", { "theme": "light", - "color": "rgb(245, 245, 245)", + "color": "#f5f5f5", "background": "rgba(78, 139, 202, 0.2)", - "hover_color": "rgb(245, 245, 245)", + "hover_color": "#f5f5f5", "hover_background": "rgb(78, 139, 202)", }) From edc3e2677335f930450f3e9f006e87869fe31d0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Fri, 4 Aug 2023 17:50:12 +0000 Subject: [PATCH 51/56] Account for `Rc` and `Arc` when suggesting to clone When suggesting to clone a reference-counted value, be less uncertain. --- .../src/diagnostics/conflict_errors.rs | 12 +++++++++++- .../use_of_moved_value_clone_suggestions.stderr | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 03b90f4ab18..4cb6b028e55 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -751,9 +751,19 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { ) .must_apply_modulo_regions() { + let msg = if let ty::Adt(def, _) = ty.kind() + && [ + tcx.get_diagnostic_item(sym::Arc), + tcx.get_diagnostic_item(sym::Rc), + ].contains(&Some(def.did())) + { + "clone the value to increment its reference count" + } else { + "consider cloning the value if the performance cost is acceptable" + }; err.span_suggestion_verbose( span.shrink_to_hi(), - "consider cloning the value if the performance cost is acceptable", + msg, suggestion, Applicability::MachineApplicable, ); diff --git a/tests/ui/moves/use_of_moved_value_clone_suggestions.stderr b/tests/ui/moves/use_of_moved_value_clone_suggestions.stderr index 22e7951dbe3..0bb486a8893 100644 --- a/tests/ui/moves/use_of_moved_value_clone_suggestions.stderr +++ b/tests/ui/moves/use_of_moved_value_clone_suggestions.stderr @@ -8,7 +8,7 @@ LL | (t, t) | | | value moved here | -help: consider cloning the value if the performance cost is acceptable +help: clone the value to increment its reference count | LL | (t.clone(), t) | ++++++++ From f78485a97ecea3327e212275a32bd987267cbdc8 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 4 Aug 2023 19:32:54 +0000 Subject: [PATCH 52/56] Fix ui-fulldeps missing the internal_features lint on stage 0 --- src/tools/compiletest/src/runtest.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 07a54cb26d3..c1987420417 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -2466,8 +2466,13 @@ impl<'test> TestCx<'test> { rustc.args(&["-A", "unused"]); } - // Allow tests to use internal features. - rustc.args(&["-A", "internal_features"]); + // #[cfg(not(bootstrap)] unconditionally pass flag after beta bump + // since `ui-fulldeps --stage=1` builds using the stage 0 compiler, + // which doesn't have this lint. + if !(self.config.stage_id.starts_with("stage1-") && self.config.suite == "ui-fulldeps") { + // Allow tests to use internal features. + rustc.args(&["-A", "internal_features"]); + } if self.props.force_host { self.maybe_add_external_args(&mut rustc, &self.config.host_rustcflags); From d64968ba304a1a959cf3d857168ea3e135c95358 Mon Sep 17 00:00:00 2001 From: kernelmethod <17100608+kernelmethod@users.noreply.github.com> Date: Sat, 5 Aug 2023 00:38:52 +0000 Subject: [PATCH 53/56] Fix a typo in the error reporting for sealed traits. --- .../src/traits/error_reporting/suggestions.rs | 2 +- tests/ui/privacy/sealed-traits/sealed-trait-local.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 32b4b4eb264..1c59f3ff6c7 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -2789,7 +2789,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // implement this trait and list them. err.note(format!( "`{short_item_name}` is a \"sealed trait\", because to implement \ - it you also need to implelement `{}`, which is not accessible; \ + it you also need to implement `{}`, which is not accessible; \ this is usually done to force you to use one of the provided \ types that already implement it", with_no_trimmed_paths!(tcx.def_path_str(def_id)), diff --git a/tests/ui/privacy/sealed-traits/sealed-trait-local.stderr b/tests/ui/privacy/sealed-traits/sealed-trait-local.stderr index d1052ce3508..5f8076fc84d 100644 --- a/tests/ui/privacy/sealed-traits/sealed-trait-local.stderr +++ b/tests/ui/privacy/sealed-traits/sealed-trait-local.stderr @@ -9,7 +9,7 @@ note: required by a bound in `Sealed` | LL | pub trait Sealed: self::b::Hidden { | ^^^^^^^^^^^^^^^ required by this bound in `Sealed` - = note: `Sealed` is a "sealed trait", because to implement it you also need to implelement `a::b::Hidden`, which is not accessible; this is usually done to force you to use one of the provided types that already implement it + = note: `Sealed` is a "sealed trait", because to implement it you also need to implement `a::b::Hidden`, which is not accessible; this is usually done to force you to use one of the provided types that already implement it error: aborting due to previous error From 5b8f92122682a6d6b47709ea0d7e821eec4d9bc3 Mon Sep 17 00:00:00 2001 From: Sebastian Toh Date: Sat, 5 Aug 2023 08:55:38 +0800 Subject: [PATCH 54/56] Rename issue #114423 test files to include context --- ... => issue-114423-ice-regression-in-suggestion.rs} | 0 ...issue-114423-ice-regression-in-suggestion.stderr} | 12 ++++++------ 2 files changed, 6 insertions(+), 6 deletions(-) rename tests/ui/typeck/{issue-114423.rs => issue-114423-ice-regression-in-suggestion.rs} (100%) rename tests/ui/typeck/{issue-114423.stderr => issue-114423-ice-regression-in-suggestion.stderr} (82%) diff --git a/tests/ui/typeck/issue-114423.rs b/tests/ui/typeck/issue-114423-ice-regression-in-suggestion.rs similarity index 100% rename from tests/ui/typeck/issue-114423.rs rename to tests/ui/typeck/issue-114423-ice-regression-in-suggestion.rs diff --git a/tests/ui/typeck/issue-114423.stderr b/tests/ui/typeck/issue-114423-ice-regression-in-suggestion.stderr similarity index 82% rename from tests/ui/typeck/issue-114423.stderr rename to tests/ui/typeck/issue-114423-ice-regression-in-suggestion.stderr index c20a4391297..4ccfacfb005 100644 --- a/tests/ui/typeck/issue-114423.stderr +++ b/tests/ui/typeck/issue-114423-ice-regression-in-suggestion.stderr @@ -1,23 +1,23 @@ error[E0425]: cannot find value `e` in this scope - --> $DIR/issue-114423.rs:7:51 + --> $DIR/issue-114423-ice-regression-in-suggestion.rs:7:51 | LL | let (r, alone_in_path, b): (f32, f32, f32) = (e.clone(), e.clone()); | ^ not found in this scope error[E0425]: cannot find value `e` in this scope - --> $DIR/issue-114423.rs:7:62 + --> $DIR/issue-114423-ice-regression-in-suggestion.rs:7:62 | LL | let (r, alone_in_path, b): (f32, f32, f32) = (e.clone(), e.clone()); | ^ not found in this scope error[E0425]: cannot find value `g` in this scope - --> $DIR/issue-114423.rs:11:22 + --> $DIR/issue-114423-ice-regression-in-suggestion.rs:11:22 | LL | let _ = RGB { r, g, b }; | ^ help: a local variable with a similar name exists: `b` error[E0308]: mismatched types - --> $DIR/issue-114423.rs:7:50 + --> $DIR/issue-114423-ice-regression-in-suggestion.rs:7:50 | LL | let (r, alone_in_path, b): (f32, f32, f32) = (e.clone(), e.clone()); | --------------- ^^^^^^^^^^^^^^^^^^^^^^ expected a tuple with 3 elements, found one with 2 elements @@ -28,7 +28,7 @@ LL | let (r, alone_in_path, b): (f32, f32, f32) = (e.clone(), e.clone()); found tuple `(f32, f32)` error[E0560]: struct `RGB` has no field named `r` - --> $DIR/issue-114423.rs:11:19 + --> $DIR/issue-114423-ice-regression-in-suggestion.rs:11:19 | LL | let _ = RGB { r, g, b }; | ^ `RGB` does not have this field @@ -36,7 +36,7 @@ LL | let _ = RGB { r, g, b }; = note: all struct fields are already assigned error[E0308]: mismatched types - --> $DIR/issue-114423.rs:11:25 + --> $DIR/issue-114423-ice-regression-in-suggestion.rs:11:25 | LL | let _ = RGB { r, g, b }; | ^ expected `f64`, found `f32` From b3a8e8eaa1c698f2decfffbf2ea2209766190485 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 5 Aug 2023 08:21:34 +0200 Subject: [PATCH 55/56] Preparing for merge from rustc --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index f195bd5ac8a..716b690daaa 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -a7caaae9fbef81325887aea060fc551da4589c6f \ No newline at end of file +fca59ab5f0e7df7d816bed77a32abc0045ebe80b \ No newline at end of file From 621aeeb096d8e4f3eb656259fc286a1f32862ed3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 5 Aug 2023 08:22:15 +0200 Subject: [PATCH 56/56] fmt --- src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs | 6 +----- src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index 75e4b5f8466..ea7cba3f346 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -13,11 +13,7 @@ use log::trace; use rustc_data_structures::fx::FxHashSet; use rustc_middle::mir::{Mutability, RetagKind}; -use rustc_middle::ty::{ - self, - layout::HasParamEnv, - Ty, -}; +use rustc_middle::ty::{self, layout::HasParamEnv, Ty}; use rustc_target::abi::{Abi, Align, Size}; use crate::borrow_tracker::{ diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs index c584eae220e..283bc94d3cf 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs @@ -5,11 +5,7 @@ use rustc_target::abi::{Abi, Align, Size}; use crate::borrow_tracker::{AccessKind, GlobalStateInner, ProtectorKind, RetagFields}; use rustc_middle::{ mir::{Mutability, RetagKind}, - ty::{ - self, - layout::HasParamEnv, - Ty, - }, + ty::{self, layout::HasParamEnv, Ty}, }; use rustc_span::def_id::DefId;