| author | bors <bors@rust-lang.org> 2026-06-12 20:35:50 UTC |
| committer | bors <bors@rust-lang.org> 2026-06-12 20:35:50 UTC |
| log | 65407954098ca3c19f0d46092cb374b5d3e9dc3c |
| tree | f1f7a80add3c963293a2970daff75f3ba7a0aa1a |
| parent | 3bdd7f86fcecc0ea65d79e62e39a70c8a2aabf15 |
| parent | 5e5789c936b707dd626677242471f04314525859 |
Rollup of 23 pull requests
Successful merges:
- rust-lang/rust#144220 (Add powerpc64-unknown-linux-gnuelfv2 target)
- rust-lang/rust#153238 (debuginfo: slices are DW_TAG_array_type's)
- rust-lang/rust#157112 (Update aarch64-unknown-freebsd target description)
- rust-lang/rust#157322 (test pre-stabilization items on CI)
- rust-lang/rust#157348 (Don't track cwd for `-Zremap-cwd-prefix` in incremental compilation)
- rust-lang/rust#157490 (Add field-wise CoerceShared reborrow tests)
- rust-lang/rust#157655 (Make Share::share final and improve docs)
- rust-lang/rust#157672 (Region inference: Simplify initialisation of region values)
- rust-lang/rust#157680 (Require `#[pin_v2]` for explicit pin-projection patterns)
- rust-lang/rust#157688 (Create experimental test job `aarch64-apple-macos-26` for evaluating `macos-26` runner images)
- rust-lang/rust#157796 (rustdoc: Some more lazy formatting)
- rust-lang/rust#157818 (miri subtree update)
- rust-lang/rust#157069 (Test that you can't implement Unpin for a compiler-generated future using TAIT)
- rust-lang/rust#157079 (Don't recover `&raw EXPR` as a missing comma)
- rust-lang/rust#157202 (add #[rustc_no_writable] to slice::get_unchecked_mut)
- rust-lang/rust#157622 (Disable retagging for variadic arguments in const-eval)
- rust-lang/rust#157684 (-Zassumptions-on-binders: insert empty assumptions when entering binders in the solver)
- rust-lang/rust#157695 (Extend capabilities of `TypeFoldable_Generic`)
- rust-lang/rust#157766 (interpret: avoid computing layout of sized raw pointee)
- rust-lang/rust#157785 (fuchsia: Support AddressSanitizer on riscv64gc-unknown-fuchsia)
- rust-lang/rust#157795 (revert 157013)
- rust-lang/rust#157798 (Prevent approving PRs that wait for Crater or formal decisions)
- rust-lang/rust#157803 (Rename `errors.rs` file to `diagnostics.rs` (7/N))
Failed merges:
- rust-lang/rust#157752 (Rename `errors.rs` file to `diagnostics.rs` (6/N))193 files changed, 4299 insertions(+), 1286 deletions(-)
Cargo.lock+1| ... | ... | @@ -4892,6 +4892,7 @@ dependencies = [ |
| 4892 | 4892 | name = "rustc_type_ir_macros" |
| 4893 | 4893 | version = "0.0.0" |
| 4894 | 4894 | dependencies = [ |
| 4895 | "indexmap", | |
| 4895 | 4896 | "proc-macro2", |
| 4896 | 4897 | "quote", |
| 4897 | 4898 | "syn", |
compiler/rustc_borrowck/src/diagnostics/find_use.rs+1-1| ... | ... | @@ -35,7 +35,7 @@ impl<'a, 'tcx> UseFinder<'a, 'tcx> { |
| 35 | 35 | |
| 36 | 36 | queue.push_back(self.start_point); |
| 37 | 37 | while let Some(p) = queue.pop_front() { |
| 38 | if !self.regioncx.region_contains(self.region_vid, p) { | |
| 38 | if !self.regioncx.region_contains_point(self.region_vid, p) { | |
| 39 | 39 | continue; |
| 40 | 40 | } |
| 41 | 41 |
compiler/rustc_borrowck/src/region_infer/mod.rs+41-92| ... | ... | @@ -30,7 +30,7 @@ use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo}; |
| 30 | 30 | use crate::handle_placeholders::{LoweredConstraints, RegionTracker}; |
| 31 | 31 | use crate::polonius::LiveLoans; |
| 32 | 32 | use crate::polonius::legacy::PoloniusOutput; |
| 33 | use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues, ToElementIndex}; | |
| 33 | use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues}; | |
| 34 | 34 | use crate::type_check::Locations; |
| 35 | 35 | use crate::type_check::free_region_relations::UniversalRegionRelations; |
| 36 | 36 | use crate::universal_regions::UniversalRegions; |
| ... | ... | @@ -345,7 +345,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { |
| 345 | 345 | outlives_constraints, |
| 346 | 346 | scc_annotations, |
| 347 | 347 | type_tests, |
| 348 | liveness_constraints, | |
| 348 | mut liveness_constraints, | |
| 349 | 349 | universe_causes, |
| 350 | 350 | placeholder_indices, |
| 351 | 351 | } = lowered_constraints; |
| ... | ... | @@ -364,106 +364,54 @@ impl<'tcx> RegionInferenceContext<'tcx> { |
| 364 | 364 | let mut scc_values = |
| 365 | 365 | RegionValues::new(location_map, universal_regions.len(), placeholder_indices); |
| 366 | 366 | |
| 367 | for region in liveness_constraints.regions() { | |
| 367 | // Initializes the region variables with their initial live points. | |
| 368 | for (region, definition) in definitions.iter_enumerated() { | |
| 368 | 369 | let scc = constraint_sccs.scc(region); |
| 369 | scc_values.merge_liveness(scc, region, &liveness_constraints); | |
| 370 | } | |
| 371 | 370 | |
| 372 | let mut result = Self { | |
| 373 | definitions, | |
| 374 | liveness_constraints, | |
| 375 | constraints: outlives_constraints, | |
| 376 | constraint_graph, | |
| 377 | constraint_sccs, | |
| 378 | scc_annotations, | |
| 379 | universe_causes, | |
| 380 | scc_values, | |
| 381 | type_tests, | |
| 382 | universal_region_relations, | |
| 383 | }; | |
| 384 | ||
| 385 | result.init_free_and_bound_regions(); | |
| 386 | ||
| 387 | result | |
| 388 | } | |
| 389 | ||
| 390 | /// Initializes the region variables for each universally | |
| 391 | /// quantified region (lifetime parameter). The first N variables | |
| 392 | /// always correspond to the regions appearing in the function | |
| 393 | /// signature (both named and anonymous) and where-clauses. This | |
| 394 | /// function iterates over those regions and initializes them with | |
| 395 | /// minimum values. | |
| 396 | /// | |
| 397 | /// For example: | |
| 398 | /// ```ignore (illustrative) | |
| 399 | /// fn foo<'a, 'b>( /* ... */ ) where 'a: 'b { /* ... */ } | |
| 400 | /// ``` | |
| 401 | /// would initialize two variables like so: | |
| 402 | /// ```ignore (illustrative) | |
| 403 | /// R0 = { CFG, R0 } // 'a | |
| 404 | /// R1 = { CFG, R0, R1 } // 'b | |
| 405 | /// ``` | |
| 406 | /// Here, R0 represents `'a`, and it contains (a) the entire CFG | |
| 407 | /// and (b) any universally quantified regions that it outlives, | |
| 408 | /// which in this case is just itself. R1 (`'b`) in contrast also | |
| 409 | /// outlives `'a` and hence contains R0 and R1. | |
| 410 | /// | |
| 411 | /// This bit of logic also handles invalid universe relations | |
| 412 | /// for higher-kinded types. | |
| 413 | /// | |
| 414 | /// We Walk each SCC `A` and `B` such that `A: B` | |
| 415 | /// and ensure that universe(A) can see universe(B). | |
| 416 | /// | |
| 417 | /// This serves to enforce the 'empty/placeholder' hierarchy | |
| 418 | /// (described in more detail on `RegionKind`): | |
| 419 | /// | |
| 420 | /// ```ignore (illustrative) | |
| 421 | /// static -----+ | |
| 422 | /// | | | |
| 423 | /// empty(U0) placeholder(U1) | |
| 424 | /// | / | |
| 425 | /// empty(U1) | |
| 426 | /// ``` | |
| 427 | /// | |
| 428 | /// In particular, imagine we have variables R0 in U0 and R1 | |
| 429 | /// created in U1, and constraints like this; | |
| 430 | /// | |
| 431 | /// ```ignore (illustrative) | |
| 432 | /// R1: !1 // R1 outlives the placeholder in U1 | |
| 433 | /// R1: R0 // R1 outlives R0 | |
| 434 | /// ``` | |
| 435 | /// | |
| 436 | /// Here, we wish for R1 to be `'static`, because it | |
| 437 | /// cannot outlive `placeholder(U1)` and `empty(U0)` any other way. | |
| 438 | /// | |
| 439 | /// Thanks to this loop, what happens is that the `R1: R0` | |
| 440 | /// constraint has lowered the universe of `R1` to `U0`, which in turn | |
| 441 | /// means that the `R1: !1` constraint here will cause | |
| 442 | /// `R1` to become `'static`. | |
| 443 | fn init_free_and_bound_regions(&mut self) { | |
| 444 | for variable in self.definitions.indices() { | |
| 445 | let scc = self.constraint_sccs.scc(variable); | |
| 446 | ||
| 447 | match self.definitions[variable].origin { | |
| 371 | // For each universally quantified region (lifetime parameter). The | |
| 372 | // first N variables always correspond to the regions appearing in the | |
| 373 | // function signature (both named and anonymous) and in where-clauses. | |
| 374 | match definition.origin { | |
| 375 | // For each free, universally quantified region X: | |
| 448 | 376 | NllRegionVariableOrigin::FreeRegion => { |
| 449 | // For each free, universally quantified region X: | |
| 450 | ||
| 451 | 377 | // Add all nodes in the CFG to liveness constraints |
| 452 | self.liveness_constraints.add_all_points(variable); | |
| 453 | self.scc_values.add_all_points(scc); | |
| 378 | liveness_constraints.add_all_points(region); | |
| 454 | 379 | |
| 455 | 380 | // Add `end(X)` into the set for X. |
| 456 | self.scc_values.add_element(scc, variable); | |
| 381 | scc_values.add_free_region(scc, region); | |
| 457 | 382 | } |
| 458 | 383 | |
| 459 | 384 | NllRegionVariableOrigin::Placeholder(placeholder) => { |
| 460 | self.scc_values.add_element(scc, placeholder); | |
| 385 | scc_values.add_placeholder(scc, placeholder); | |
| 461 | 386 | } |
| 462 | 387 | |
| 463 | 388 | NllRegionVariableOrigin::Existential { .. } => { |
| 464 | 389 | // For existential, regions, nothing to do. |
| 465 | 390 | } |
| 466 | 391 | } |
| 392 | ||
| 393 | // Initially copy the liveness constraints of any region that | |
| 394 | // has them, setting `scc_values[scc(region)] |= liveness_constraints[region]`. | |
| 395 | // | |
| 396 | // These values will later be propagated during [`Self::propagate_constraints()`]. | |
| 397 | // The values include any live-at-all-points constraints added above | |
| 398 | // for free regions. | |
| 399 | if let Some(liveness) = liveness_constraints.point_liveness(region) { | |
| 400 | scc_values.merge_liveness(scc, liveness) | |
| 401 | } | |
| 402 | } | |
| 403 | ||
| 404 | Self { | |
| 405 | definitions, | |
| 406 | liveness_constraints, | |
| 407 | constraints: outlives_constraints, | |
| 408 | constraint_graph, | |
| 409 | constraint_sccs, | |
| 410 | scc_annotations, | |
| 411 | universe_causes, | |
| 412 | scc_values, | |
| 413 | type_tests, | |
| 414 | universal_region_relations, | |
| 467 | 415 | } |
| 468 | 416 | } |
| 469 | 417 | |
| ... | ... | @@ -495,9 +443,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { |
| 495 | 443 | /// Returns `true` if the region `r` contains the point `p`. |
| 496 | 444 | /// |
| 497 | 445 | /// Panics if called before `solve()` executes, |
| 498 | pub(crate) fn region_contains(&self, r: RegionVid, p: impl ToElementIndex<'tcx>) -> bool { | |
| 446 | pub(crate) fn region_contains_point(&self, r: RegionVid, p: Location) -> bool { | |
| 499 | 447 | let scc = self.constraint_sccs.scc(r); |
| 500 | self.scc_values.contains(scc, p) | |
| 448 | self.scc_values.contains_point(scc, p) | |
| 501 | 449 | } |
| 502 | 450 | |
| 503 | 451 | /// Returns the lowest statement index in `start..=end` which is not contained by `r`. |
| ... | ... | @@ -608,7 +556,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { |
| 608 | 556 | // To propagate constraints, we walk the DAG induced by the |
| 609 | 557 | // SCC. For each SCC `A`, we visit its successors and compute |
| 610 | 558 | // their values, then we union all those values to get our |
| 611 | // own. | |
| 559 | // own. This one-shot approach works because iteration is in | |
| 560 | // dependency order. I.e. a chain A: B: C will visit C, B, A. | |
| 612 | 561 | for scc_a in self.constraint_sccs.all_sccs() { |
| 613 | 562 | // Walk each SCC `B` such that `A: B`... |
| 614 | 563 | for &scc_b in self.constraint_sccs.successors(scc_a) { |
| ... | ... | @@ -1643,10 +1592,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { |
| 1643 | 1592 | &self.definitions[r] |
| 1644 | 1593 | } |
| 1645 | 1594 | |
| 1646 | /// Check if the SCC of `r` contains `upper`. | |
| 1595 | /// Check if the SCC of `r` contains `upper`, a free region. | |
| 1647 | 1596 | pub(crate) fn upper_bound_in_region_scc(&self, r: RegionVid, upper: RegionVid) -> bool { |
| 1648 | 1597 | let r_scc = self.constraint_sccs.scc(r); |
| 1649 | self.scc_values.contains(r_scc, upper) | |
| 1598 | self.scc_values.contains_free_region(r_scc, upper) | |
| 1650 | 1599 | } |
| 1651 | 1600 | |
| 1652 | 1601 | pub(crate) fn universal_regions(&self) -> &UniversalRegions<'tcx> { |
compiler/rustc_borrowck/src/region_infer/opaque_types/region_ctxt.rs+3-3| ... | ... | @@ -78,11 +78,11 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { |
| 78 | 78 | let placeholder_indices = Default::default(); |
| 79 | 79 | let mut scc_values = |
| 80 | 80 | RegionValues::new(location_map, universal_regions.len(), placeholder_indices); |
| 81 | for variable in definitions.indices() { | |
| 81 | for (variable, definition) in definitions.iter_enumerated() { | |
| 82 | 82 | let scc = constraint_sccs.scc(variable); |
| 83 | match definitions[variable].origin { | |
| 83 | match definition.origin { | |
| 84 | 84 | NllRegionVariableOrigin::FreeRegion => { |
| 85 | scc_values.add_element(scc, variable); | |
| 85 | scc_values.add_free_region(scc, variable); | |
| 86 | 86 | } |
| 87 | 87 | _ => {} |
| 88 | 88 | } |
compiler/rustc_borrowck/src/region_infer/values.rs+22-64| ... | ... | @@ -95,9 +95,10 @@ impl LivenessValues { |
| 95 | 95 | } |
| 96 | 96 | } |
| 97 | 97 | |
| 98 | /// Iterate through each region that has a value in this set. | |
| 99 | pub(crate) fn regions(&self) -> impl Iterator<Item = RegionVid> { | |
| 100 | self.points().rows() | |
| 98 | /// Get the liveness status of a region `r`, if any. | |
| 99 | /// Panics if liveness data is not tracked for any region. | |
| 100 | pub(crate) fn point_liveness(&self, region: RegionVid) -> Option<&IntervalSet<PointIndex>> { | |
| 101 | self.points().row(region) | |
| 101 | 102 | } |
| 102 | 103 | |
| 103 | 104 | /// Iterate through each region that has a value in this set. |
| ... | ... | @@ -166,13 +167,12 @@ impl LivenessValues { |
| 166 | 167 | /// [`point`][rustc_mir_dataflow::points::PointIndex]. |
| 167 | 168 | #[inline] |
| 168 | 169 | pub(crate) fn is_live_at_point(&self, region: RegionVid, point: PointIndex) -> bool { |
| 169 | self.points().row(region).is_some_and(|r| r.contains(point)) | |
| 170 | self.point_liveness(region).is_some_and(|r| r.contains(point)) | |
| 170 | 171 | } |
| 171 | 172 | |
| 172 | 173 | /// Returns an iterator of all the points where `region` is live. |
| 173 | 174 | fn live_points(&self, region: RegionVid) -> impl Iterator<Item = PointIndex> { |
| 174 | self.points() | |
| 175 | .row(region) | |
| 175 | self.point_liveness(region) | |
| 176 | 176 | .into_iter() |
| 177 | 177 | .flat_map(|set| set.iter()) |
| 178 | 178 | .take_while(|&p| self.location_map.point_in_range(p)) |
| ... | ... | @@ -296,18 +296,6 @@ impl<'tcx, N: Idx> RegionValues<'tcx, N> { |
| 296 | 296 | } |
| 297 | 297 | } |
| 298 | 298 | |
| 299 | /// Adds the given element to the value for the given region. Returns whether | |
| 300 | /// the element is newly added (i.e., was not already present). | |
| 301 | pub(crate) fn add_element(&mut self, r: N, elem: impl ToElementIndex<'tcx>) -> bool { | |
| 302 | debug!("add(r={:?}, elem={:?})", r, elem); | |
| 303 | elem.add_to_row(self, r) | |
| 304 | } | |
| 305 | ||
| 306 | /// Adds all the control-flow points to the values for `r`. | |
| 307 | pub(crate) fn add_all_points(&mut self, r: N) { | |
| 308 | self.points.insert_all_into_row(r); | |
| 309 | } | |
| 310 | ||
| 311 | 299 | /// Adds all elements in `r_from` to `r_to` (because e.g., `r_to: |
| 312 | 300 | /// r_from`). |
| 313 | 301 | pub(crate) fn add_region(&mut self, r_to: N, r_from: N) -> bool { |
| ... | ... | @@ -316,11 +304,6 @@ impl<'tcx, N: Idx> RegionValues<'tcx, N> { |
| 316 | 304 | | self.placeholders.union_rows(r_from, r_to) |
| 317 | 305 | } |
| 318 | 306 | |
| 319 | /// Returns `true` if the region `r` contains the given element. | |
| 320 | pub(crate) fn contains(&self, r: N, elem: impl ToElementIndex<'tcx>) -> bool { | |
| 321 | elem.contained_in_row(self, r) | |
| 322 | } | |
| 323 | ||
| 324 | 307 | /// Returns the lowest statement index in `start..=end` which is not contained by `r`. |
| 325 | 308 | pub(crate) fn first_non_contained_inclusive( |
| 326 | 309 | &self, |
| ... | ... | @@ -337,13 +320,9 @@ impl<'tcx, N: Idx> RegionValues<'tcx, N> { |
| 337 | 320 | Some(first_unset.index() - block.index()) |
| 338 | 321 | } |
| 339 | 322 | |
| 340 | /// `self[to] |= values[from]`, essentially: that is, take all the | |
| 341 | /// elements for the region `from` from `values` and add them to | |
| 342 | /// the region `to` in `self`. | |
| 343 | pub(crate) fn merge_liveness(&mut self, to: N, from: RegionVid, values: &LivenessValues) { | |
| 344 | if let Some(set) = values.points().row(from) { | |
| 345 | self.points.union_row(to, set); | |
| 346 | } | |
| 323 | /// Merge a row of liveness into our points. | |
| 324 | pub(crate) fn merge_liveness(&mut self, to: N, liveness: &IntervalSet<PointIndex>) { | |
| 325 | self.points.union_row(to, liveness); | |
| 347 | 326 | } |
| 348 | 327 | |
| 349 | 328 | /// Returns `true` if `sup_region` contains all the CFG points that |
| ... | ... | @@ -405,47 +384,26 @@ impl<'tcx, N: Idx> RegionValues<'tcx, N> { |
| 405 | 384 | pub(crate) fn region_value_str(&self, r: N) -> String { |
| 406 | 385 | pretty_print_region_elements(self.elements_contained_in(r)) |
| 407 | 386 | } |
| 408 | } | |
| 409 | ||
| 410 | pub(crate) trait ToElementIndex<'tcx>: Debug + Copy { | |
| 411 | fn add_to_row<N: Idx>(self, values: &mut RegionValues<'tcx, N>, row: N) -> bool; | |
| 412 | ||
| 413 | fn contained_in_row<N: Idx>(self, values: &RegionValues<'tcx, N>, row: N) -> bool; | |
| 414 | } | |
| 415 | ||
| 416 | impl ToElementIndex<'_> for Location { | |
| 417 | fn add_to_row<N: Idx>(self, values: &mut RegionValues<'_, N>, row: N) -> bool { | |
| 418 | let index = values.location_map.point_from_location(self); | |
| 419 | values.points.insert(row, index) | |
| 420 | } | |
| 421 | 387 | |
| 422 | fn contained_in_row<N: Idx>(self, values: &RegionValues<'_, N>, row: N) -> bool { | |
| 423 | let index = values.location_map.point_from_location(self); | |
| 424 | values.points.contains(row, index) | |
| 425 | } | |
| 426 | } | |
| 427 | ||
| 428 | impl ToElementIndex<'_> for RegionVid { | |
| 429 | fn add_to_row<N: Idx>(self, values: &mut RegionValues<'_, N>, row: N) -> bool { | |
| 430 | values.free_regions.insert(row, self) | |
| 388 | /// Add a the free region with rvid `region` to SCC `scc` | |
| 389 | pub(crate) fn add_free_region(&mut self, scc: N, region: RegionVid) { | |
| 390 | self.free_regions.insert(scc, region); | |
| 431 | 391 | } |
| 432 | 392 | |
| 433 | fn contained_in_row<N: Idx>(self, values: &RegionValues<'_, N>, row: N) -> bool { | |
| 434 | values.free_regions.contains(row, self) | |
| 393 | pub(crate) fn add_placeholder(&mut self, scc: N, placeholder: ty::PlaceholderRegion<'tcx>) { | |
| 394 | let index = self.placeholder_indices.lookup_index(placeholder); | |
| 395 | self.placeholders.insert(scc, index); | |
| 435 | 396 | } |
| 436 | } | |
| 437 | 397 | |
| 438 | impl<'tcx> ToElementIndex<'tcx> for ty::PlaceholderRegion<'tcx> { | |
| 439 | fn add_to_row<N: Idx>(self, values: &mut RegionValues<'tcx, N>, row: N) -> bool { | |
| 440 | let placeholder: ty::PlaceholderRegion<'tcx> = self.into(); | |
| 441 | let index = values.placeholder_indices.lookup_index(placeholder); | |
| 442 | values.placeholders.insert(row, index) | |
| 398 | /// Determine if `scc` contains the CFG point `p`. | |
| 399 | pub(crate) fn contains_point(&self, scc: N, p: Location) -> bool { | |
| 400 | let index = self.location_map.point_from_location(p); | |
| 401 | self.points.contains(scc, index) | |
| 443 | 402 | } |
| 444 | 403 | |
| 445 | fn contained_in_row<N: Idx>(self, values: &RegionValues<'tcx, N>, row: N) -> bool { | |
| 446 | let placeholder: ty::PlaceholderRegion<'tcx> = self.into(); | |
| 447 | let index = values.placeholder_indices.lookup_index(placeholder); | |
| 448 | values.placeholders.contains(row, index) | |
| 404 | /// Determine if `scc` contains the free region `free_region`. | |
| 405 | pub(crate) fn contains_free_region(&self, scc: N, free_region: RegionVid) -> bool { | |
| 406 | self.free_regions.contains(scc, free_region) | |
| 449 | 407 | } |
| 450 | 408 | } |
| 451 | 409 |
compiler/rustc_codegen_llvm/src/context.rs+8| ... | ... | @@ -201,6 +201,14 @@ pub(crate) unsafe fn create_module<'ll>( |
| 201 | 201 | if sess.target.arch == Arch::PowerPC64 { |
| 202 | 202 | // LLVM 22 updated the ABI alignment for double on AIX: https://github.com/llvm/llvm-project/pull/144673 |
| 203 | 203 | target_data_layout = target_data_layout.replace("-f64:32:64", ""); |
| 204 | ||
| 205 | // LLVM 22 fixed the data layout calculation for targets that default to ELFv1 | |
| 206 | // when the ABI is set to ELFv2. With LLVM 21, the ELFv1 datalayout must be used, | |
| 207 | // which will overalign function entries. | |
| 208 | // https://github.com/llvm/llvm-project/pull/149725 | |
| 209 | if sess.target.llvm_target == "powerpc64-unknown-linux-gnu" { | |
| 210 | target_data_layout = target_data_layout.replace("-Fn32", "-Fi64"); | |
| 211 | } | |
| 204 | 212 | } |
| 205 | 213 | if sess.target.arch == Arch::AmdGpu { |
| 206 | 214 | // LLVM 22 specified ELF mangling in the amdgpu data layout: |
compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs+30-19| ... | ... | @@ -152,7 +152,20 @@ fn build_pointer_or_reference_di_node<'ll, 'tcx>( |
| 152 | 152 | cx.size_and_align_of(Ty::new_mut_ptr(cx.tcx, pointee_type)) |
| 153 | 153 | ); |
| 154 | 154 | |
| 155 | let pointee_type_di_node = type_di_node(cx, pointee_type); | |
| 155 | let pointee_type_di_node = match pointee_type.kind() { | |
| 156 | // `&[T]` will look like `{ data_ptr: *const T, length: usize }` | |
| 157 | ty::Slice(element_type) => type_di_node(cx, *element_type), | |
| 158 | // `&str` will look like `{ data_ptr: *const u8, length: usize }` | |
| 159 | ty::Str => type_di_node(cx, cx.tcx.types.u8), | |
| 160 | ||
| 161 | // `&dyn K` will look like `{ pointer: _, vtable: _}` | |
| 162 | // any Adt `Foo` containing an unsized type (eg `&[_]` or `&dyn _`) | |
| 163 | // will look like `{ data_ptr: *const Foo, length: usize }` | |
| 164 | // and thin pointers `&Foo` will just look like `*const Foo`. | |
| 165 | // | |
| 166 | // in all those cases, we just use the pointee_type | |
| 167 | _ => type_di_node(cx, pointee_type), | |
| 168 | }; | |
| 156 | 169 | |
| 157 | 170 | return_if_di_node_created_in_meantime!(cx, unique_type_id); |
| 158 | 171 | |
| ... | ... | @@ -389,26 +402,11 @@ fn build_dyn_type_di_node<'ll, 'tcx>( |
| 389 | 402 | } |
| 390 | 403 | |
| 391 | 404 | /// Create debuginfo for `[T]` and `str`. These are unsized. |
| 392 | /// | |
| 393 | /// NOTE: We currently emit just emit the debuginfo for the element type here | |
| 394 | /// (i.e. `T` for slices and `u8` for `str`), so that we end up with | |
| 395 | /// `*const T` for the `data_ptr` field of the corresponding wide-pointer | |
| 396 | /// debuginfo of `&[T]`. | |
| 397 | /// | |
| 398 | /// It would be preferable and more accurate if we emitted a DIArray of T | |
| 399 | /// without an upper bound instead. That is, LLVM already supports emitting | |
| 400 | /// debuginfo of arrays of unknown size. But GDB currently seems to end up | |
| 401 | /// in an infinite loop when confronted with such a type. | |
| 402 | /// | |
| 403 | /// As a side effect of the current encoding every instance of a type like | |
| 404 | /// `struct Foo { unsized_field: [u8] }` will look like | |
| 405 | /// `struct Foo { unsized_field: u8 }` in debuginfo. If the length of the | |
| 406 | /// slice is zero, then accessing `unsized_field` in the debugger would | |
| 407 | /// result in an out-of-bounds access. | |
| 408 | 405 | fn build_slice_type_di_node<'ll, 'tcx>( |
| 409 | 406 | cx: &CodegenCx<'ll, 'tcx>, |
| 410 | 407 | slice_type: Ty<'tcx>, |
| 411 | 408 | unique_type_id: UniqueTypeId<'tcx>, |
| 409 | span: Span, | |
| 412 | 410 | ) -> DINodeCreationResult<'ll> { |
| 413 | 411 | let element_type = match slice_type.kind() { |
| 414 | 412 | ty::Slice(element_type) => *element_type, |
| ... | ... | @@ -423,7 +421,20 @@ fn build_slice_type_di_node<'ll, 'tcx>( |
| 423 | 421 | |
| 424 | 422 | let element_type_di_node = type_di_node(cx, element_type); |
| 425 | 423 | return_if_di_node_created_in_meantime!(cx, unique_type_id); |
| 426 | DINodeCreationResult { di_node: element_type_di_node, already_stored_in_typemap: false } | |
| 424 | let (size, align) = cx.spanned_size_and_align_of(slice_type, span); | |
| 425 | let subrange = unsafe { llvm::LLVMDIBuilderGetOrCreateSubrange(DIB(cx), 0, -1) }; | |
| 426 | let subscripts = &[subrange]; | |
| 427 | let di_node = unsafe { | |
| 428 | llvm::LLVMDIBuilderCreateArrayType( | |
| 429 | DIB(cx), | |
| 430 | size.bits(), | |
| 431 | align.bits() as u32, | |
| 432 | element_type_di_node, | |
| 433 | subscripts.as_ptr(), | |
| 434 | subscripts.len() as c_uint, | |
| 435 | ) | |
| 436 | }; | |
| 437 | DINodeCreationResult { di_node, already_stored_in_typemap: false } | |
| 427 | 438 | } |
| 428 | 439 | |
| 429 | 440 | /// Get the debuginfo node for the given type. |
| ... | ... | @@ -454,7 +465,7 @@ pub(crate) fn spanned_type_di_node<'ll, 'tcx>( |
| 454 | 465 | } |
| 455 | 466 | ty::Tuple(elements) if elements.is_empty() => build_basic_type_di_node(cx, t), |
| 456 | 467 | ty::Array(..) => build_fixed_size_array_di_node(cx, unique_type_id, t, span), |
| 457 | ty::Slice(_) | ty::Str => build_slice_type_di_node(cx, t, unique_type_id), | |
| 468 | ty::Slice(_) | ty::Str => build_slice_type_di_node(cx, t, unique_type_id, span), | |
| 458 | 469 | ty::Dynamic(..) => build_dyn_type_di_node(cx, t, unique_type_id), |
| 459 | 470 | ty::Foreign(..) => build_foreign_type_di_node(cx, t, unique_type_id), |
| 460 | 471 | ty::RawPtr(pointee_type, _) | ty::Ref(_, pointee_type, _) => { |
compiler/rustc_const_eval/src/interpret/call.rs+6-2| ... | ... | @@ -502,8 +502,12 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { |
| 502 | 502 | let mplace = ecx.force_allocation(&place)?; |
| 503 | 503 | |
| 504 | 504 | // Consume the remaining arguments by putting them into the variable argument |
| 505 | // list. | |
| 506 | let varargs = ecx.allocate_varargs(&mut caller_args, &mut callee_args_abis)?; | |
| 505 | // list. We disable retagging to avoid creating protected tags. Protection should | |
| 506 | // only use callee-side information, and the varargs have no static callee-side type. | |
| 507 | let varargs = M::with_retag_mode(ecx, RetagMode::None, |ecx| { | |
| 508 | ecx.allocate_varargs(&mut caller_args, &mut callee_args_abis) | |
| 509 | })?; | |
| 510 | ||
| 507 | 511 | // When the frame is dropped, these variable arguments are deallocated. |
| 508 | 512 | ecx.frame_mut().va_list = varargs.clone(); |
| 509 | 513 | let key = ecx.va_list_ptr(varargs.into()); |
compiler/rustc_const_eval/src/interpret/validity.rs+7-3| ... | ... | @@ -925,7 +925,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { |
| 925 | 925 | } |
| 926 | 926 | interp_ok(true) |
| 927 | 927 | } |
| 928 | ty::RawPtr(..) => { | |
| 928 | ty::RawPtr(pointee, ..) => { | |
| 929 | 929 | let ptr = self.read_immediate(value, ExpectedKind::RawPtr)?; |
| 930 | 930 | if self.reset_provenance_and_padding { |
| 931 | 931 | self.reset_pointer_provenance(value, &ptr)?; |
| ... | ... | @@ -933,8 +933,12 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> { |
| 933 | 933 | self.add_data_range_place(value); |
| 934 | 934 | } |
| 935 | 935 | |
| 936 | let place = self.ecx.imm_ptr_to_mplace(&ptr)?; | |
| 937 | if place.layout.is_unsized() { | |
| 936 | if !pointee.is_sized(*self.ecx.tcx, self.ecx.typing_env) { | |
| 937 | // Raw pointers to unsized types need to have their metadata checked. | |
| 938 | // We avoid creating this place for sized types to match codegen: those types | |
| 939 | // might actually be invalid (i.e., too big)! | |
| 940 | let place = self.ecx.imm_ptr_to_mplace(&ptr)?; | |
| 941 | assert!(place.layout.is_unsized()); | |
| 938 | 942 | self.check_wide_ptr_meta(place.meta(), place.layout)?; |
| 939 | 943 | } |
| 940 | 944 | interp_ok(true) |
compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs-10| ... | ... | @@ -903,16 +903,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |
| 903 | 903 | |
| 904 | 904 | // Type check the pattern. Override if necessary to avoid knock-on errors. |
| 905 | 905 | self.check_pat_top(decl.pat, decl_ty, ty_span, origin_expr, Some(decl.origin)); |
| 906 | if decl.ty.is_none() | |
| 907 | && decl.init.is_none() | |
| 908 | && !matches!(decl.pat.kind, hir::PatKind::Binding(.., None) | hir::PatKind::Wild) | |
| 909 | { | |
| 910 | self.register_wf_obligation( | |
| 911 | decl_ty.into(), | |
| 912 | decl.pat.span, | |
| 913 | ObligationCauseCode::WellFormed(None), | |
| 914 | ); | |
| 915 | } | |
| 916 | 906 | let pat_ty = self.node_ty(decl.pat.hir_id); |
| 917 | 907 | self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, pat_ty); |
| 918 | 908 |
compiler/rustc_hir_typeck/src/pat.rs+37-15| ... | ... | @@ -544,21 +544,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |
| 544 | 544 | { |
| 545 | 545 | debug!("scrutinee ty {expected:?} is a pinned reference, inserting pin deref"); |
| 546 | 546 | |
| 547 | // if the inner_ty is an ADT, make sure that it can be structurally pinned | |
| 548 | // (i.e., it is `#[pin_v2]`). | |
| 549 | if let Some(adt) = inner_ty.ty_adt_def() | |
| 550 | && !adt.is_pin_project() | |
| 551 | && !adt.is_pin() | |
| 552 | { | |
| 553 | let def_span: Option<Span> = self.tcx.hir_span_if_local(adt.did()); | |
| 554 | let sugg_span = def_span.map(|span| span.shrink_to_lo()); | |
| 555 | self.dcx().emit_err(crate::errors::ProjectOnNonPinProjectType { | |
| 556 | span: pat.span, | |
| 557 | def_span, | |
| 558 | sugg_span, | |
| 559 | }); | |
| 560 | } | |
| 561 | ||
| 562 | 547 | // Use the old pat info to keep `current_depth` to its old value. |
| 563 | 548 | let new_pat_info = |
| 564 | 549 | self.adjust_pat_info(Pinnedness::Pinned, inner_mutability, old_pat_info); |
| ... | ... | @@ -1523,6 +1508,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |
| 1523 | 1508 | Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::Struct { variant } }) |
| 1524 | 1509 | } |
| 1525 | 1510 | |
| 1511 | /// Reject pin-projection through a type that isn't structurally pinnable. | |
| 1512 | /// | |
| 1513 | /// Destructuring an ADT underneath a `&pin` reference projects its fields as pinned references. | |
| 1514 | /// This is only sound if the type opted into structural pinning with `#[pin_v2]`; otherwise it | |
| 1515 | /// would let safe code form a `Pin<&mut Field>` for a type that should never be pinned, breaking | |
| 1516 | /// the `Pin` guarantee (see #157634). | |
| 1517 | /// | |
| 1518 | /// This covers both explicit (`&pin mut`/`&pin const`) and implicit (match-ergonomics) | |
| 1519 | /// projection. `max_pinnedness` is only set for `&pin mut`, so the implicit shared (`&pin | |
| 1520 | /// const`) case is instead recognized through its pinned binding mode, hence both are checked. | |
| 1521 | fn check_pin_projection( | |
| 1522 | &self, | |
| 1523 | pat: &'tcx Pat<'tcx>, | |
| 1524 | pat_ty: Ty<'tcx>, | |
| 1525 | pat_info: PatInfo<'tcx>, | |
| 1526 | ) { | |
| 1527 | let through_pin = pat_info.max_pinnedness == PinnednessCap::Pinned | |
| 1528 | || matches!(pat_info.binding_mode, ByRef::Yes(Pinnedness::Pinned, _)); | |
| 1529 | if through_pin | |
| 1530 | && let Some(adt) = pat_ty.ty_adt_def() | |
| 1531 | && !adt.is_pin_project() | |
| 1532 | && !adt.is_pin() | |
| 1533 | { | |
| 1534 | let def_span: Option<Span> = self.tcx.hir_span_if_local(adt.did()); | |
| 1535 | let sugg_span = def_span.map(|span| span.shrink_to_lo()); | |
| 1536 | self.dcx().emit_err(crate::errors::ProjectOnNonPinProjectType { | |
| 1537 | span: pat.span, | |
| 1538 | def_span, | |
| 1539 | sugg_span, | |
| 1540 | }); | |
| 1541 | } | |
| 1542 | } | |
| 1543 | ||
| 1526 | 1544 | fn check_pat_struct( |
| 1527 | 1545 | &self, |
| 1528 | 1546 | pat: &'tcx Pat<'tcx>, |
| ... | ... | @@ -1533,6 +1551,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |
| 1533 | 1551 | expected: Ty<'tcx>, |
| 1534 | 1552 | pat_info: PatInfo<'tcx>, |
| 1535 | 1553 | ) -> Ty<'tcx> { |
| 1554 | self.check_pin_projection(pat, pat_ty, pat_info); | |
| 1555 | ||
| 1536 | 1556 | // Type-check the path. |
| 1537 | 1557 | let had_err = self.demand_eqtype_pat(pat.span, expected, pat_ty, &pat_info.top_info); |
| 1538 | 1558 | |
| ... | ... | @@ -1791,6 +1811,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |
| 1791 | 1811 | expected: Ty<'tcx>, |
| 1792 | 1812 | pat_info: PatInfo<'tcx>, |
| 1793 | 1813 | ) -> Ty<'tcx> { |
| 1814 | self.check_pin_projection(pat, pat_ty, pat_info); | |
| 1815 | ||
| 1794 | 1816 | let tcx = self.tcx; |
| 1795 | 1817 | let on_error = |e| { |
| 1796 | 1818 | for pat in subpats { |
compiler/rustc_incremental/src/assert_dep_graph.rs+9-8| ... | ... | @@ -52,7 +52,7 @@ use rustc_middle::ty::TyCtxt; |
| 52 | 52 | use rustc_span::{Span, Symbol, sym}; |
| 53 | 53 | use tracing::debug; |
| 54 | 54 | |
| 55 | use crate::errors; | |
| 55 | use crate::diagnostics; | |
| 56 | 56 | |
| 57 | 57 | #[allow(missing_docs)] |
| 58 | 58 | pub(crate) fn assert_dep_graph(tcx: TyCtxt<'_>) { |
| ... | ... | @@ -128,7 +128,7 @@ impl<'tcx> IfThisChanged<'tcx> { |
| 128 | 128 | Err(()) => self |
| 129 | 129 | .tcx |
| 130 | 130 | .dcx() |
| 131 | .emit_fatal(errors::UnrecognizedDepNode { span, name: n }), | |
| 131 | .emit_fatal(diagnostics::UnrecognizedDepNode { span, name: n }), | |
| 132 | 132 | } |
| 133 | 133 | } |
| 134 | 134 | }; |
| ... | ... | @@ -139,9 +139,10 @@ impl<'tcx> IfThisChanged<'tcx> { |
| 139 | 139 | let Ok(dep_node) = |
| 140 | 140 | DepNode::from_label_string(self.tcx, n.as_str(), def_path_hash) |
| 141 | 141 | else { |
| 142 | self.tcx | |
| 143 | .dcx() | |
| 144 | .emit_fatal(errors::UnrecognizedDepNode { span: n.span, name: n.name }); | |
| 142 | self.tcx.dcx().emit_fatal(diagnostics::UnrecognizedDepNode { | |
| 143 | span: n.span, | |
| 144 | name: n.name, | |
| 145 | }); | |
| 145 | 146 | }; |
| 146 | 147 | self.then_this_would_need.push((n.span, n.name, hir_id, dep_node)); |
| 147 | 148 | } |
| ... | ... | @@ -186,7 +187,7 @@ fn check_paths<'tcx>( |
| 186 | 187 | ) { |
| 187 | 188 | if if_this_changed.is_empty() { |
| 188 | 189 | for &(target_span, _, _, _) in then_this_would_need { |
| 189 | tcx.dcx().emit_err(errors::MissingIfThisChanged { span: target_span }); | |
| 190 | tcx.dcx().emit_err(diagnostics::MissingIfThisChanged { span: target_span }); | |
| 190 | 191 | } |
| 191 | 192 | return; |
| 192 | 193 | } |
| ... | ... | @@ -195,13 +196,13 @@ fn check_paths<'tcx>( |
| 195 | 196 | let dependents = query.transitive_predecessors(source_dep_node); |
| 196 | 197 | for &(target_span, ref target_pass, _, ref target_dep_node) in then_this_would_need { |
| 197 | 198 | if !dependents.contains(&target_dep_node) { |
| 198 | tcx.dcx().emit_err(errors::NoPath { | |
| 199 | tcx.dcx().emit_err(diagnostics::NoPath { | |
| 199 | 200 | span: target_span, |
| 200 | 201 | source: tcx.def_path_str(source_def_id), |
| 201 | 202 | target: *target_pass, |
| 202 | 203 | }); |
| 203 | 204 | } else { |
| 204 | tcx.dcx().emit_err(errors::Ok { span: target_span }); | |
| 205 | tcx.dcx().emit_err(diagnostics::Ok { span: target_span }); | |
| 205 | 206 | } |
| 206 | 207 | } |
| 207 | 208 | } |
compiler/rustc_incremental/src/diagnostics.rs created+289| ... | ... | @@ -0,0 +1,289 @@ |
| 1 | use std::path::{Path, PathBuf}; | |
| 2 | ||
| 3 | use rustc_macros::Diagnostic; | |
| 4 | use rustc_span::{Span, Symbol}; | |
| 5 | ||
| 6 | #[derive(Diagnostic)] | |
| 7 | #[diag("unrecognized `DepNode` variant: {$name}")] | |
| 8 | pub(crate) struct UnrecognizedDepNode { | |
| 9 | #[primary_span] | |
| 10 | pub span: Span, | |
| 11 | pub name: Symbol, | |
| 12 | } | |
| 13 | ||
| 14 | #[derive(Diagnostic)] | |
| 15 | #[diag("no `#[rustc_if_this_changed]` annotation detected")] | |
| 16 | pub(crate) struct MissingIfThisChanged { | |
| 17 | #[primary_span] | |
| 18 | pub span: Span, | |
| 19 | } | |
| 20 | ||
| 21 | #[derive(Diagnostic)] | |
| 22 | #[diag("OK")] | |
| 23 | pub(crate) struct Ok { | |
| 24 | #[primary_span] | |
| 25 | pub span: Span, | |
| 26 | } | |
| 27 | ||
| 28 | #[derive(Diagnostic)] | |
| 29 | #[diag("no path from `{$source}` to `{$target}`")] | |
| 30 | pub(crate) struct NoPath { | |
| 31 | #[primary_span] | |
| 32 | pub span: Span, | |
| 33 | pub target: Symbol, | |
| 34 | pub source: String, | |
| 35 | } | |
| 36 | ||
| 37 | #[derive(Diagnostic)] | |
| 38 | #[diag("`except` specified DepNodes that can not be affected for \"{$name}\": \"{$e}\"")] | |
| 39 | pub(crate) struct AssertionAuto<'a> { | |
| 40 | #[primary_span] | |
| 41 | pub span: Span, | |
| 42 | pub name: &'a str, | |
| 43 | pub e: &'a str, | |
| 44 | } | |
| 45 | ||
| 46 | #[derive(Diagnostic)] | |
| 47 | #[diag("clean/dirty auto-assertions not yet defined for Node::Item.node={$kind}")] | |
| 48 | pub(crate) struct UndefinedCleanDirtyItem { | |
| 49 | #[primary_span] | |
| 50 | pub span: Span, | |
| 51 | pub kind: String, | |
| 52 | } | |
| 53 | ||
| 54 | #[derive(Diagnostic)] | |
| 55 | #[diag("clean/dirty auto-assertions not yet defined for {$kind}")] | |
| 56 | pub(crate) struct UndefinedCleanDirty { | |
| 57 | #[primary_span] | |
| 58 | pub span: Span, | |
| 59 | pub kind: String, | |
| 60 | } | |
| 61 | ||
| 62 | #[derive(Diagnostic)] | |
| 63 | #[diag("dep-node label `{$label}` is repeated")] | |
| 64 | pub(crate) struct RepeatedDepNodeLabel<'a> { | |
| 65 | #[primary_span] | |
| 66 | pub span: Span, | |
| 67 | pub label: &'a str, | |
| 68 | } | |
| 69 | ||
| 70 | #[derive(Diagnostic)] | |
| 71 | #[diag("dep-node label `{$label}` not recognized")] | |
| 72 | pub(crate) struct UnrecognizedDepNodeLabel<'a> { | |
| 73 | #[primary_span] | |
| 74 | pub span: Span, | |
| 75 | pub label: &'a str, | |
| 76 | } | |
| 77 | ||
| 78 | #[derive(Diagnostic)] | |
| 79 | #[diag("`{$dep_node_str}` should be dirty but is not")] | |
| 80 | pub(crate) struct NotDirty<'a> { | |
| 81 | #[primary_span] | |
| 82 | pub span: Span, | |
| 83 | pub dep_node_str: &'a str, | |
| 84 | } | |
| 85 | ||
| 86 | #[derive(Diagnostic)] | |
| 87 | #[diag("`{$dep_node_str}` should be clean but is not")] | |
| 88 | pub(crate) struct NotClean<'a> { | |
| 89 | #[primary_span] | |
| 90 | pub span: Span, | |
| 91 | pub dep_node_str: &'a str, | |
| 92 | } | |
| 93 | ||
| 94 | #[derive(Diagnostic)] | |
| 95 | #[diag("`{$dep_node_str}` should have been loaded from disk but it was not")] | |
| 96 | pub(crate) struct NotLoaded<'a> { | |
| 97 | #[primary_span] | |
| 98 | pub span: Span, | |
| 99 | pub dep_node_str: &'a str, | |
| 100 | } | |
| 101 | ||
| 102 | #[derive(Diagnostic)] | |
| 103 | #[diag("found unchecked `#[rustc_clean]` attribute")] | |
| 104 | pub(crate) struct UncheckedClean { | |
| 105 | #[primary_span] | |
| 106 | pub span: Span, | |
| 107 | } | |
| 108 | #[derive(Diagnostic)] | |
| 109 | #[diag("unable to delete old {$name} at `{$path}`: {$err}")] | |
| 110 | pub(crate) struct DeleteOld<'a> { | |
| 111 | pub name: &'a str, | |
| 112 | pub path: PathBuf, | |
| 113 | pub err: std::io::Error, | |
| 114 | } | |
| 115 | ||
| 116 | #[derive(Diagnostic)] | |
| 117 | #[diag("failed to create {$name} at `{$path}`: {$err}")] | |
| 118 | pub(crate) struct CreateNew<'a> { | |
| 119 | pub name: &'a str, | |
| 120 | pub path: PathBuf, | |
| 121 | pub err: std::io::Error, | |
| 122 | } | |
| 123 | ||
| 124 | #[derive(Diagnostic)] | |
| 125 | #[diag("failed to write {$name} to `{$path}`: {$err}")] | |
| 126 | pub(crate) struct WriteNew<'a> { | |
| 127 | pub name: &'a str, | |
| 128 | pub path: PathBuf, | |
| 129 | pub err: std::io::Error, | |
| 130 | } | |
| 131 | ||
| 132 | #[derive(Diagnostic)] | |
| 133 | #[diag("incremental compilation: error canonicalizing path `{$path}`: {$err}")] | |
| 134 | pub(crate) struct CanonicalizePath { | |
| 135 | pub path: PathBuf, | |
| 136 | pub err: std::io::Error, | |
| 137 | } | |
| 138 | ||
| 139 | #[derive(Diagnostic)] | |
| 140 | #[diag("could not create incremental compilation {$tag} directory `{$path}`: {$err}")] | |
| 141 | pub(crate) struct CreateIncrCompDir<'a> { | |
| 142 | pub tag: &'a str, | |
| 143 | pub path: &'a Path, | |
| 144 | pub err: std::io::Error, | |
| 145 | } | |
| 146 | ||
| 147 | #[derive(Diagnostic)] | |
| 148 | #[diag("incremental compilation: could not create session directory lock file: {$lock_err}")] | |
| 149 | pub(crate) struct CreateLock<'a> { | |
| 150 | pub lock_err: std::io::Error, | |
| 151 | pub session_dir: &'a Path, | |
| 152 | #[note( | |
| 153 | "the filesystem for the incremental path at {$session_dir} does not appear to support locking, consider changing the incremental path to a filesystem that supports locking or disable incremental compilation" | |
| 154 | )] | |
| 155 | pub is_unsupported_lock: bool, | |
| 156 | #[help( | |
| 157 | "incremental compilation can be disabled by setting the environment variable CARGO_INCREMENTAL=0 (see https://doc.rust-lang.org/cargo/reference/profiles.html#incremental)" | |
| 158 | )] | |
| 159 | #[help( | |
| 160 | "the entire build directory can be changed to a different filesystem by setting the environment variable CARGO_TARGET_DIR to a different path (see https://doc.rust-lang.org/cargo/reference/config.html#buildtarget-dir)" | |
| 161 | )] | |
| 162 | pub is_cargo: bool, | |
| 163 | } | |
| 164 | ||
| 165 | #[derive(Diagnostic)] | |
| 166 | #[diag("error deleting lock file for incremental compilation session directory `{$path}`: {$err}")] | |
| 167 | pub(crate) struct DeleteLock<'a> { | |
| 168 | pub path: &'a Path, | |
| 169 | pub err: std::io::Error, | |
| 170 | } | |
| 171 | ||
| 172 | #[derive(Diagnostic)] | |
| 173 | #[diag( | |
| 174 | "hard linking files in the incremental compilation cache failed. copying files instead. consider moving the cache directory to a file system which supports hard linking in session dir `{$path}`" | |
| 175 | )] | |
| 176 | pub(crate) struct HardLinkFailed<'a> { | |
| 177 | pub path: &'a Path, | |
| 178 | } | |
| 179 | ||
| 180 | #[derive(Diagnostic)] | |
| 181 | #[diag("failed to delete partly initialized session dir `{$path}`: {$err}")] | |
| 182 | pub(crate) struct DeletePartial<'a> { | |
| 183 | pub path: &'a Path, | |
| 184 | pub err: std::io::Error, | |
| 185 | } | |
| 186 | ||
| 187 | #[derive(Diagnostic)] | |
| 188 | #[diag("error deleting incremental compilation session directory `{$path}`: {$err}")] | |
| 189 | pub(crate) struct DeleteFull<'a> { | |
| 190 | pub path: &'a Path, | |
| 191 | pub err: std::io::Error, | |
| 192 | } | |
| 193 | ||
| 194 | #[derive(Diagnostic)] | |
| 195 | #[diag("did not finalize incremental compilation session directory `{$path}`: {$err}")] | |
| 196 | #[help("the next build will not be able to reuse work from this compilation")] | |
| 197 | pub(crate) struct Finalize<'a> { | |
| 198 | pub path: &'a Path, | |
| 199 | pub err: std::io::Error, | |
| 200 | } | |
| 201 | ||
| 202 | #[derive(Diagnostic)] | |
| 203 | #[diag( | |
| 204 | "failed to garbage collect invalid incremental compilation session directory `{$path}`: {$err}" | |
| 205 | )] | |
| 206 | pub(crate) struct InvalidGcFailed<'a> { | |
| 207 | pub path: &'a Path, | |
| 208 | pub err: std::io::Error, | |
| 209 | } | |
| 210 | ||
| 211 | #[derive(Diagnostic)] | |
| 212 | #[diag( | |
| 213 | "failed to garbage collect finalized incremental compilation session directory `{$path}`: {$err}" | |
| 214 | )] | |
| 215 | pub(crate) struct FinalizedGcFailed<'a> { | |
| 216 | pub path: &'a Path, | |
| 217 | pub err: std::io::Error, | |
| 218 | } | |
| 219 | ||
| 220 | #[derive(Diagnostic)] | |
| 221 | #[diag("failed to garbage collect incremental compilation session directory `{$path}`: {$err}")] | |
| 222 | pub(crate) struct SessionGcFailed<'a> { | |
| 223 | pub path: &'a Path, | |
| 224 | pub err: std::io::Error, | |
| 225 | } | |
| 226 | ||
| 227 | #[derive(Diagnostic)] | |
| 228 | #[diag("we asserted that the incremental cache should not be loaded, but it was loaded")] | |
| 229 | pub(crate) struct AssertNotLoaded; | |
| 230 | ||
| 231 | #[derive(Diagnostic)] | |
| 232 | #[diag( | |
| 233 | "we asserted that an existing incremental cache directory should be successfully loaded, but it was not" | |
| 234 | )] | |
| 235 | pub(crate) struct AssertLoaded; | |
| 236 | ||
| 237 | #[derive(Diagnostic)] | |
| 238 | #[diag( | |
| 239 | "failed to delete invalidated or incompatible incremental compilation session directory contents `{$path}`: {$err}" | |
| 240 | )] | |
| 241 | pub(crate) struct DeleteIncompatible { | |
| 242 | pub path: PathBuf, | |
| 243 | pub err: std::io::Error, | |
| 244 | } | |
| 245 | ||
| 246 | #[derive(Diagnostic)] | |
| 247 | #[diag("could not load dep-graph from `{$path}`: {$err}")] | |
| 248 | pub(crate) struct LoadDepGraph { | |
| 249 | pub path: PathBuf, | |
| 250 | pub err: std::io::Error, | |
| 251 | } | |
| 252 | ||
| 253 | #[derive(Diagnostic)] | |
| 254 | #[diag("failed to move dependency graph from `{$from}` to `{$to}`: {$err}")] | |
| 255 | pub(crate) struct MoveDepGraph<'a> { | |
| 256 | pub from: &'a Path, | |
| 257 | pub to: &'a Path, | |
| 258 | pub err: std::io::Error, | |
| 259 | } | |
| 260 | ||
| 261 | #[derive(Diagnostic)] | |
| 262 | #[diag("failed to create dependency graph at `{$path}`: {$err}")] | |
| 263 | pub(crate) struct CreateDepGraph<'a> { | |
| 264 | pub path: &'a Path, | |
| 265 | pub err: std::io::Error, | |
| 266 | } | |
| 267 | ||
| 268 | #[derive(Diagnostic)] | |
| 269 | #[diag("error copying object file `{$from}` to incremental directory as `{$to}`: {$err}")] | |
| 270 | pub(crate) struct CopyWorkProductToCache<'a> { | |
| 271 | pub from: &'a Path, | |
| 272 | pub to: &'a Path, | |
| 273 | pub err: std::io::Error, | |
| 274 | } | |
| 275 | ||
| 276 | #[derive(Diagnostic)] | |
| 277 | #[diag("file-system error deleting outdated file `{$path}`: {$err}")] | |
| 278 | pub(crate) struct DeleteWorkProduct<'a> { | |
| 279 | pub path: &'a Path, | |
| 280 | pub err: std::io::Error, | |
| 281 | } | |
| 282 | ||
| 283 | #[derive(Diagnostic)] | |
| 284 | #[diag( | |
| 285 | "corrupt incremental compilation artifact found at `{$path}`. This file will automatically be ignored and deleted. If you see this message repeatedly or can provoke it without manually manipulating the compiler's artifacts, please file an issue. The incremental compilation system relies on hardlinks and filesystem locks behaving correctly, and may not deal well with OS crashes, so whatever information you can provide about your filesystem or other state may be very relevant" | |
| 286 | )] | |
| 287 | pub(crate) struct CorruptFile<'a> { | |
| 288 | pub path: &'a Path, | |
| 289 | } |
compiler/rustc_incremental/src/errors.rs deleted-289| ... | ... | @@ -1,289 +0,0 @@ |
| 1 | use std::path::{Path, PathBuf}; | |
| 2 | ||
| 3 | use rustc_macros::Diagnostic; | |
| 4 | use rustc_span::{Span, Symbol}; | |
| 5 | ||
| 6 | #[derive(Diagnostic)] | |
| 7 | #[diag("unrecognized `DepNode` variant: {$name}")] | |
| 8 | pub(crate) struct UnrecognizedDepNode { | |
| 9 | #[primary_span] | |
| 10 | pub span: Span, | |
| 11 | pub name: Symbol, | |
| 12 | } | |
| 13 | ||
| 14 | #[derive(Diagnostic)] | |
| 15 | #[diag("no `#[rustc_if_this_changed]` annotation detected")] | |
| 16 | pub(crate) struct MissingIfThisChanged { | |
| 17 | #[primary_span] | |
| 18 | pub span: Span, | |
| 19 | } | |
| 20 | ||
| 21 | #[derive(Diagnostic)] | |
| 22 | #[diag("OK")] | |
| 23 | pub(crate) struct Ok { | |
| 24 | #[primary_span] | |
| 25 | pub span: Span, | |
| 26 | } | |
| 27 | ||
| 28 | #[derive(Diagnostic)] | |
| 29 | #[diag("no path from `{$source}` to `{$target}`")] | |
| 30 | pub(crate) struct NoPath { | |
| 31 | #[primary_span] | |
| 32 | pub span: Span, | |
| 33 | pub target: Symbol, | |
| 34 | pub source: String, | |
| 35 | } | |
| 36 | ||
| 37 | #[derive(Diagnostic)] | |
| 38 | #[diag("`except` specified DepNodes that can not be affected for \"{$name}\": \"{$e}\"")] | |
| 39 | pub(crate) struct AssertionAuto<'a> { | |
| 40 | #[primary_span] | |
| 41 | pub span: Span, | |
| 42 | pub name: &'a str, | |
| 43 | pub e: &'a str, | |
| 44 | } | |
| 45 | ||
| 46 | #[derive(Diagnostic)] | |
| 47 | #[diag("clean/dirty auto-assertions not yet defined for Node::Item.node={$kind}")] | |
| 48 | pub(crate) struct UndefinedCleanDirtyItem { | |
| 49 | #[primary_span] | |
| 50 | pub span: Span, | |
| 51 | pub kind: String, | |
| 52 | } | |
| 53 | ||
| 54 | #[derive(Diagnostic)] | |
| 55 | #[diag("clean/dirty auto-assertions not yet defined for {$kind}")] | |
| 56 | pub(crate) struct UndefinedCleanDirty { | |
| 57 | #[primary_span] | |
| 58 | pub span: Span, | |
| 59 | pub kind: String, | |
| 60 | } | |
| 61 | ||
| 62 | #[derive(Diagnostic)] | |
| 63 | #[diag("dep-node label `{$label}` is repeated")] | |
| 64 | pub(crate) struct RepeatedDepNodeLabel<'a> { | |
| 65 | #[primary_span] | |
| 66 | pub span: Span, | |
| 67 | pub label: &'a str, | |
| 68 | } | |
| 69 | ||
| 70 | #[derive(Diagnostic)] | |
| 71 | #[diag("dep-node label `{$label}` not recognized")] | |
| 72 | pub(crate) struct UnrecognizedDepNodeLabel<'a> { | |
| 73 | #[primary_span] | |
| 74 | pub span: Span, | |
| 75 | pub label: &'a str, | |
| 76 | } | |
| 77 | ||
| 78 | #[derive(Diagnostic)] | |
| 79 | #[diag("`{$dep_node_str}` should be dirty but is not")] | |
| 80 | pub(crate) struct NotDirty<'a> { | |
| 81 | #[primary_span] | |
| 82 | pub span: Span, | |
| 83 | pub dep_node_str: &'a str, | |
| 84 | } | |
| 85 | ||
| 86 | #[derive(Diagnostic)] | |
| 87 | #[diag("`{$dep_node_str}` should be clean but is not")] | |
| 88 | pub(crate) struct NotClean<'a> { | |
| 89 | #[primary_span] | |
| 90 | pub span: Span, | |
| 91 | pub dep_node_str: &'a str, | |
| 92 | } | |
| 93 | ||
| 94 | #[derive(Diagnostic)] | |
| 95 | #[diag("`{$dep_node_str}` should have been loaded from disk but it was not")] | |
| 96 | pub(crate) struct NotLoaded<'a> { | |
| 97 | #[primary_span] | |
| 98 | pub span: Span, | |
| 99 | pub dep_node_str: &'a str, | |
| 100 | } | |
| 101 | ||
| 102 | #[derive(Diagnostic)] | |
| 103 | #[diag("found unchecked `#[rustc_clean]` attribute")] | |
| 104 | pub(crate) struct UncheckedClean { | |
| 105 | #[primary_span] | |
| 106 | pub span: Span, | |
| 107 | } | |
| 108 | #[derive(Diagnostic)] | |
| 109 | #[diag("unable to delete old {$name} at `{$path}`: {$err}")] | |
| 110 | pub(crate) struct DeleteOld<'a> { | |
| 111 | pub name: &'a str, | |
| 112 | pub path: PathBuf, | |
| 113 | pub err: std::io::Error, | |
| 114 | } | |
| 115 | ||
| 116 | #[derive(Diagnostic)] | |
| 117 | #[diag("failed to create {$name} at `{$path}`: {$err}")] | |
| 118 | pub(crate) struct CreateNew<'a> { | |
| 119 | pub name: &'a str, | |
| 120 | pub path: PathBuf, | |
| 121 | pub err: std::io::Error, | |
| 122 | } | |
| 123 | ||
| 124 | #[derive(Diagnostic)] | |
| 125 | #[diag("failed to write {$name} to `{$path}`: {$err}")] | |
| 126 | pub(crate) struct WriteNew<'a> { | |
| 127 | pub name: &'a str, | |
| 128 | pub path: PathBuf, | |
| 129 | pub err: std::io::Error, | |
| 130 | } | |
| 131 | ||
| 132 | #[derive(Diagnostic)] | |
| 133 | #[diag("incremental compilation: error canonicalizing path `{$path}`: {$err}")] | |
| 134 | pub(crate) struct CanonicalizePath { | |
| 135 | pub path: PathBuf, | |
| 136 | pub err: std::io::Error, | |
| 137 | } | |
| 138 | ||
| 139 | #[derive(Diagnostic)] | |
| 140 | #[diag("could not create incremental compilation {$tag} directory `{$path}`: {$err}")] | |
| 141 | pub(crate) struct CreateIncrCompDir<'a> { | |
| 142 | pub tag: &'a str, | |
| 143 | pub path: &'a Path, | |
| 144 | pub err: std::io::Error, | |
| 145 | } | |
| 146 | ||
| 147 | #[derive(Diagnostic)] | |
| 148 | #[diag("incremental compilation: could not create session directory lock file: {$lock_err}")] | |
| 149 | pub(crate) struct CreateLock<'a> { | |
| 150 | pub lock_err: std::io::Error, | |
| 151 | pub session_dir: &'a Path, | |
| 152 | #[note( | |
| 153 | "the filesystem for the incremental path at {$session_dir} does not appear to support locking, consider changing the incremental path to a filesystem that supports locking or disable incremental compilation" | |
| 154 | )] | |
| 155 | pub is_unsupported_lock: bool, | |
| 156 | #[help( | |
| 157 | "incremental compilation can be disabled by setting the environment variable CARGO_INCREMENTAL=0 (see https://doc.rust-lang.org/cargo/reference/profiles.html#incremental)" | |
| 158 | )] | |
| 159 | #[help( | |
| 160 | "the entire build directory can be changed to a different filesystem by setting the environment variable CARGO_TARGET_DIR to a different path (see https://doc.rust-lang.org/cargo/reference/config.html#buildtarget-dir)" | |
| 161 | )] | |
| 162 | pub is_cargo: bool, | |
| 163 | } | |
| 164 | ||
| 165 | #[derive(Diagnostic)] | |
| 166 | #[diag("error deleting lock file for incremental compilation session directory `{$path}`: {$err}")] | |
| 167 | pub(crate) struct DeleteLock<'a> { | |
| 168 | pub path: &'a Path, | |
| 169 | pub err: std::io::Error, | |
| 170 | } | |
| 171 | ||
| 172 | #[derive(Diagnostic)] | |
| 173 | #[diag( | |
| 174 | "hard linking files in the incremental compilation cache failed. copying files instead. consider moving the cache directory to a file system which supports hard linking in session dir `{$path}`" | |
| 175 | )] | |
| 176 | pub(crate) struct HardLinkFailed<'a> { | |
| 177 | pub path: &'a Path, | |
| 178 | } | |
| 179 | ||
| 180 | #[derive(Diagnostic)] | |
| 181 | #[diag("failed to delete partly initialized session dir `{$path}`: {$err}")] | |
| 182 | pub(crate) struct DeletePartial<'a> { | |
| 183 | pub path: &'a Path, | |
| 184 | pub err: std::io::Error, | |
| 185 | } | |
| 186 | ||
| 187 | #[derive(Diagnostic)] | |
| 188 | #[diag("error deleting incremental compilation session directory `{$path}`: {$err}")] | |
| 189 | pub(crate) struct DeleteFull<'a> { | |
| 190 | pub path: &'a Path, | |
| 191 | pub err: std::io::Error, | |
| 192 | } | |
| 193 | ||
| 194 | #[derive(Diagnostic)] | |
| 195 | #[diag("did not finalize incremental compilation session directory `{$path}`: {$err}")] | |
| 196 | #[help("the next build will not be able to reuse work from this compilation")] | |
| 197 | pub(crate) struct Finalize<'a> { | |
| 198 | pub path: &'a Path, | |
| 199 | pub err: std::io::Error, | |
| 200 | } | |
| 201 | ||
| 202 | #[derive(Diagnostic)] | |
| 203 | #[diag( | |
| 204 | "failed to garbage collect invalid incremental compilation session directory `{$path}`: {$err}" | |
| 205 | )] | |
| 206 | pub(crate) struct InvalidGcFailed<'a> { | |
| 207 | pub path: &'a Path, | |
| 208 | pub err: std::io::Error, | |
| 209 | } | |
| 210 | ||
| 211 | #[derive(Diagnostic)] | |
| 212 | #[diag( | |
| 213 | "failed to garbage collect finalized incremental compilation session directory `{$path}`: {$err}" | |
| 214 | )] | |
| 215 | pub(crate) struct FinalizedGcFailed<'a> { | |
| 216 | pub path: &'a Path, | |
| 217 | pub err: std::io::Error, | |
| 218 | } | |
| 219 | ||
| 220 | #[derive(Diagnostic)] | |
| 221 | #[diag("failed to garbage collect incremental compilation session directory `{$path}`: {$err}")] | |
| 222 | pub(crate) struct SessionGcFailed<'a> { | |
| 223 | pub path: &'a Path, | |
| 224 | pub err: std::io::Error, | |
| 225 | } | |
| 226 | ||
| 227 | #[derive(Diagnostic)] | |
| 228 | #[diag("we asserted that the incremental cache should not be loaded, but it was loaded")] | |
| 229 | pub(crate) struct AssertNotLoaded; | |
| 230 | ||
| 231 | #[derive(Diagnostic)] | |
| 232 | #[diag( | |
| 233 | "we asserted that an existing incremental cache directory should be successfully loaded, but it was not" | |
| 234 | )] | |
| 235 | pub(crate) struct AssertLoaded; | |
| 236 | ||
| 237 | #[derive(Diagnostic)] | |
| 238 | #[diag( | |
| 239 | "failed to delete invalidated or incompatible incremental compilation session directory contents `{$path}`: {$err}" | |
| 240 | )] | |
| 241 | pub(crate) struct DeleteIncompatible { | |
| 242 | pub path: PathBuf, | |
| 243 | pub err: std::io::Error, | |
| 244 | } | |
| 245 | ||
| 246 | #[derive(Diagnostic)] | |
| 247 | #[diag("could not load dep-graph from `{$path}`: {$err}")] | |
| 248 | pub(crate) struct LoadDepGraph { | |
| 249 | pub path: PathBuf, | |
| 250 | pub err: std::io::Error, | |
| 251 | } | |
| 252 | ||
| 253 | #[derive(Diagnostic)] | |
| 254 | #[diag("failed to move dependency graph from `{$from}` to `{$to}`: {$err}")] | |
| 255 | pub(crate) struct MoveDepGraph<'a> { | |
| 256 | pub from: &'a Path, | |
| 257 | pub to: &'a Path, | |
| 258 | pub err: std::io::Error, | |
| 259 | } | |
| 260 | ||
| 261 | #[derive(Diagnostic)] | |
| 262 | #[diag("failed to create dependency graph at `{$path}`: {$err}")] | |
| 263 | pub(crate) struct CreateDepGraph<'a> { | |
| 264 | pub path: &'a Path, | |
| 265 | pub err: std::io::Error, | |
| 266 | } | |
| 267 | ||
| 268 | #[derive(Diagnostic)] | |
| 269 | #[diag("error copying object file `{$from}` to incremental directory as `{$to}`: {$err}")] | |
| 270 | pub(crate) struct CopyWorkProductToCache<'a> { | |
| 271 | pub from: &'a Path, | |
| 272 | pub to: &'a Path, | |
| 273 | pub err: std::io::Error, | |
| 274 | } | |
| 275 | ||
| 276 | #[derive(Diagnostic)] | |
| 277 | #[diag("file-system error deleting outdated file `{$path}`: {$err}")] | |
| 278 | pub(crate) struct DeleteWorkProduct<'a> { | |
| 279 | pub path: &'a Path, | |
| 280 | pub err: std::io::Error, | |
| 281 | } | |
| 282 | ||
| 283 | #[derive(Diagnostic)] | |
| 284 | #[diag( | |
| 285 | "corrupt incremental compilation artifact found at `{$path}`. This file will automatically be ignored and deleted. If you see this message repeatedly or can provoke it without manually manipulating the compiler's artifacts, please file an issue. The incremental compilation system relies on hardlinks and filesystem locks behaving correctly, and may not deal well with OS crashes, so whatever information you can provide about your filesystem or other state may be very relevant" | |
| 286 | )] | |
| 287 | pub(crate) struct CorruptFile<'a> { | |
| 288 | pub path: &'a Path, | |
| 289 | } |
compiler/rustc_incremental/src/lib.rs+1-1| ... | ... | @@ -6,7 +6,7 @@ |
| 6 | 6 | // tidy-alphabetical-end |
| 7 | 7 | |
| 8 | 8 | mod assert_dep_graph; |
| 9 | mod errors; | |
| 9 | mod diagnostics; | |
| 10 | 10 | mod persist; |
| 11 | 11 | |
| 12 | 12 | pub use persist::{ |
compiler/rustc_incremental/src/persist/clean.rs+11-11| ... | ... | @@ -33,7 +33,7 @@ use rustc_middle::ty::TyCtxt; |
| 33 | 33 | use rustc_span::{Span, Symbol}; |
| 34 | 34 | use tracing::debug; |
| 35 | 35 | |
| 36 | use crate::errors; | |
| 36 | use crate::diagnostics; | |
| 37 | 37 | |
| 38 | 38 | // Base and Extra labels to build up the labels |
| 39 | 39 | |
| ... | ... | @@ -193,7 +193,7 @@ impl<'tcx> CleanVisitor<'tcx> { |
| 193 | 193 | let loaded_from_disk = self.loaded_from_disk(attr); |
| 194 | 194 | for e in except.items().into_sorted_stable_ord() { |
| 195 | 195 | if !auto.remove(e) { |
| 196 | self.tcx.dcx().emit_fatal(errors::AssertionAuto { span: attr.span, name, e }); | |
| 196 | self.tcx.dcx().emit_fatal(diagnostics::AssertionAuto { span: attr.span, name, e }); | |
| 197 | 197 | } |
| 198 | 198 | } |
| 199 | 199 | Assertion { clean: auto, dirty: except, loaded_from_disk } |
| ... | ... | @@ -267,7 +267,7 @@ impl<'tcx> CleanVisitor<'tcx> { |
| 267 | 267 | // An implementation, eg `impl<A> Trait for Foo { .. }` |
| 268 | 268 | HirItem::Impl { .. } => ("ItemKind::Impl", LABELS_IMPL), |
| 269 | 269 | |
| 270 | _ => self.tcx.dcx().emit_fatal(errors::UndefinedCleanDirtyItem { | |
| 270 | _ => self.tcx.dcx().emit_fatal(diagnostics::UndefinedCleanDirtyItem { | |
| 271 | 271 | span, |
| 272 | 272 | kind: format!("{:?}", item.kind), |
| 273 | 273 | }), |
| ... | ... | @@ -286,7 +286,7 @@ impl<'tcx> CleanVisitor<'tcx> { |
| 286 | 286 | _ => self |
| 287 | 287 | .tcx |
| 288 | 288 | .dcx() |
| 289 | .emit_fatal(errors::UndefinedCleanDirty { span, kind: format!("{node:?}") }), | |
| 289 | .emit_fatal(diagnostics::UndefinedCleanDirty { span, kind: format!("{node:?}") }), | |
| 290 | 290 | }; |
| 291 | 291 | let labels = |
| 292 | 292 | Labels::from_iter(labels.iter().flat_map(|s| s.iter().map(|l| (*l).to_string()))); |
| ... | ... | @@ -301,13 +301,13 @@ impl<'tcx> CleanVisitor<'tcx> { |
| 301 | 301 | if out.contains(label_str) { |
| 302 | 302 | self.tcx |
| 303 | 303 | .dcx() |
| 304 | .emit_fatal(errors::RepeatedDepNodeLabel { span, label: label_str }); | |
| 304 | .emit_fatal(diagnostics::RepeatedDepNodeLabel { span, label: label_str }); | |
| 305 | 305 | } |
| 306 | 306 | out.insert(label_str.to_string()); |
| 307 | 307 | } else { |
| 308 | 308 | self.tcx |
| 309 | 309 | .dcx() |
| 310 | .emit_fatal(errors::UnrecognizedDepNodeLabel { span, label: label_str }); | |
| 310 | .emit_fatal(diagnostics::UnrecognizedDepNodeLabel { span, label: label_str }); | |
| 311 | 311 | } |
| 312 | 312 | } |
| 313 | 313 | out |
| ... | ... | @@ -328,7 +328,7 @@ impl<'tcx> CleanVisitor<'tcx> { |
| 328 | 328 | let dep_node_str = self.dep_node_str(&dep_node); |
| 329 | 329 | self.tcx |
| 330 | 330 | .dcx() |
| 331 | .emit_err(errors::NotDirty { span: item_span, dep_node_str: &dep_node_str }); | |
| 331 | .emit_err(diagnostics::NotDirty { span: item_span, dep_node_str: &dep_node_str }); | |
| 332 | 332 | } |
| 333 | 333 | } |
| 334 | 334 | |
| ... | ... | @@ -339,7 +339,7 @@ impl<'tcx> CleanVisitor<'tcx> { |
| 339 | 339 | let dep_node_str = self.dep_node_str(&dep_node); |
| 340 | 340 | self.tcx |
| 341 | 341 | .dcx() |
| 342 | .emit_err(errors::NotClean { span: item_span, dep_node_str: &dep_node_str }); | |
| 342 | .emit_err(diagnostics::NotClean { span: item_span, dep_node_str: &dep_node_str }); | |
| 343 | 343 | } |
| 344 | 344 | } |
| 345 | 345 | |
| ... | ... | @@ -369,7 +369,7 @@ impl<'tcx> CleanVisitor<'tcx> { |
| 369 | 369 | Ok(dep_node) => { |
| 370 | 370 | if !self.tcx.dep_graph.debug_was_loaded_from_disk(dep_node) { |
| 371 | 371 | let dep_node_str = self.dep_node_str(&dep_node); |
| 372 | self.tcx.dcx().emit_err(errors::NotLoaded { | |
| 372 | self.tcx.dcx().emit_err(diagnostics::NotLoaded { | |
| 373 | 373 | span: item_span, |
| 374 | 374 | dep_node_str: &dep_node_str, |
| 375 | 375 | }); |
| ... | ... | @@ -379,7 +379,7 @@ impl<'tcx> CleanVisitor<'tcx> { |
| 379 | 379 | Err(()) => { |
| 380 | 380 | let dep_kind = dep_kind_from_label(label); |
| 381 | 381 | if !self.tcx.dep_graph.debug_dep_kind_was_loaded_from_disk(dep_kind) { |
| 382 | self.tcx.dcx().emit_err(errors::NotLoaded { | |
| 382 | self.tcx.dcx().emit_err(diagnostics::NotLoaded { | |
| 383 | 383 | span: item_span, |
| 384 | 384 | dep_node_str: &label, |
| 385 | 385 | }); |
| ... | ... | @@ -407,7 +407,7 @@ impl<'tcx> FindAllAttrs<'tcx> { |
| 407 | 407 | fn report_unchecked_attrs(&self, mut checked_attrs: FxHashSet<Span>) { |
| 408 | 408 | for attr in &self.found_attrs { |
| 409 | 409 | if !checked_attrs.contains(&attr.span) { |
| 410 | self.tcx.dcx().emit_err(errors::UncheckedClean { span: attr.span }); | |
| 410 | self.tcx.dcx().emit_err(diagnostics::UncheckedClean { span: attr.span }); | |
| 411 | 411 | checked_attrs.insert(attr.span); |
| 412 | 412 | } |
| 413 | 413 | } |
compiler/rustc_incremental/src/persist/file_format.rs+4-4| ... | ... | @@ -20,7 +20,7 @@ use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; |
| 20 | 20 | use rustc_session::Session; |
| 21 | 21 | use tracing::debug; |
| 22 | 22 | |
| 23 | use crate::errors; | |
| 23 | use crate::diagnostics; | |
| 24 | 24 | |
| 25 | 25 | /// The first few bytes of files generated by incremental compilation. |
| 26 | 26 | const FILE_MAGIC: &[u8] = b"RSIC"; |
| ... | ... | @@ -57,12 +57,12 @@ where |
| 57 | 57 | debug!("save: remove old file"); |
| 58 | 58 | } |
| 59 | 59 | Err(err) if err.kind() == io::ErrorKind::NotFound => (), |
| 60 | Err(err) => sess.dcx().emit_fatal(errors::DeleteOld { name, path: path_buf, err }), | |
| 60 | Err(err) => sess.dcx().emit_fatal(diagnostics::DeleteOld { name, path: path_buf, err }), | |
| 61 | 61 | } |
| 62 | 62 | |
| 63 | 63 | let mut encoder = match FileEncoder::new(&path_buf) { |
| 64 | 64 | Ok(encoder) => encoder, |
| 65 | Err(err) => sess.dcx().emit_fatal(errors::CreateNew { name, path: path_buf, err }), | |
| 65 | Err(err) => sess.dcx().emit_fatal(diagnostics::CreateNew { name, path: path_buf, err }), | |
| 66 | 66 | }; |
| 67 | 67 | |
| 68 | 68 | write_file_header(&mut encoder, sess); |
| ... | ... | @@ -76,7 +76,7 @@ where |
| 76 | 76 | ); |
| 77 | 77 | debug!("save: data written to disk successfully"); |
| 78 | 78 | } |
| 79 | Err((path, err)) => sess.dcx().emit_fatal(errors::WriteNew { name, path, err }), | |
| 79 | Err((path, err)) => sess.dcx().emit_fatal(diagnostics::WriteNew { name, path, err }), | |
| 80 | 80 | } |
| 81 | 81 | } |
| 82 | 82 |
compiler/rustc_incremental/src/persist/fs.rs+14-12| ... | ... | @@ -120,7 +120,7 @@ use rustc_session::{Session, StableCrateId}; |
| 120 | 120 | use rustc_span::Symbol; |
| 121 | 121 | use tracing::debug; |
| 122 | 122 | |
| 123 | use crate::errors; | |
| 123 | use crate::diagnostics; | |
| 124 | 124 | |
| 125 | 125 | #[cfg(test)] |
| 126 | 126 | mod tests; |
| ... | ... | @@ -233,7 +233,7 @@ pub(crate) fn prepare_session_directory( |
| 233 | 233 | let crate_dir = match try_canonicalize(&crate_dir) { |
| 234 | 234 | Ok(v) => v, |
| 235 | 235 | Err(err) => { |
| 236 | sess.dcx().emit_fatal(errors::CanonicalizePath { path: crate_dir, err }); | |
| 236 | sess.dcx().emit_fatal(diagnostics::CanonicalizePath { path: crate_dir, err }); | |
| 237 | 237 | } |
| 238 | 238 | }; |
| 239 | 239 | |
| ... | ... | @@ -276,7 +276,7 @@ pub(crate) fn prepare_session_directory( |
| 276 | 276 | debug!("successfully copied data from: {}", source_directory.display()); |
| 277 | 277 | |
| 278 | 278 | if !allows_links { |
| 279 | sess.dcx().emit_warn(errors::HardLinkFailed { path: &session_dir }); | |
| 279 | sess.dcx().emit_warn(diagnostics::HardLinkFailed { path: &session_dir }); | |
| 280 | 280 | } |
| 281 | 281 | |
| 282 | 282 | sess.init_incr_comp_session(session_dir, directory_lock); |
| ... | ... | @@ -291,7 +291,7 @@ pub(crate) fn prepare_session_directory( |
| 291 | 291 | // Try to remove the session directory we just allocated. We don't |
| 292 | 292 | // know if there's any garbage in it from the failed copy action. |
| 293 | 293 | if let Err(err) = std_fs::remove_dir_all(&session_dir) { |
| 294 | sess.dcx().emit_warn(errors::DeletePartial { path: &session_dir, err }); | |
| 294 | sess.dcx().emit_warn(diagnostics::DeletePartial { path: &session_dir, err }); | |
| 295 | 295 | } |
| 296 | 296 | |
| 297 | 297 | delete_session_dir_lock_file(sess, &lock_file_path); |
| ... | ... | @@ -325,7 +325,7 @@ pub fn finalize_session_directory(sess: &Session, svh: Option<Svh>) { |
| 325 | 325 | ); |
| 326 | 326 | |
| 327 | 327 | if let Err(err) = std_fs::remove_dir_all(&*incr_comp_session_dir) { |
| 328 | sess.dcx().emit_warn(errors::DeleteFull { path: &incr_comp_session_dir, err }); | |
| 328 | sess.dcx().emit_warn(diagnostics::DeleteFull { path: &incr_comp_session_dir, err }); | |
| 329 | 329 | } |
| 330 | 330 | |
| 331 | 331 | let lock_file_path = lock_file_path(&*incr_comp_session_dir); |
| ... | ... | @@ -364,7 +364,7 @@ pub fn finalize_session_directory(sess: &Session, svh: Option<Svh>) { |
| 364 | 364 | } |
| 365 | 365 | Err(e) => { |
| 366 | 366 | // Warn about the error. However, no need to abort compilation now. |
| 367 | sess.dcx().emit_note(errors::Finalize { path: &incr_comp_session_dir, err: e }); | |
| 367 | sess.dcx().emit_note(diagnostics::Finalize { path: &incr_comp_session_dir, err: e }); | |
| 368 | 368 | |
| 369 | 369 | debug!("finalize_session_directory() - error, marking as invalid"); |
| 370 | 370 | // Drop the file lock, so we can garage collect |
| ... | ... | @@ -464,7 +464,9 @@ fn create_dir(sess: &Session, path: &Path, dir_tag: &str) { |
| 464 | 464 | Ok(()) => { |
| 465 | 465 | debug!("{} directory created successfully", dir_tag); |
| 466 | 466 | } |
| 467 | Err(err) => sess.dcx().emit_fatal(errors::CreateIncrCompDir { tag: dir_tag, path, err }), | |
| 467 | Err(err) => { | |
| 468 | sess.dcx().emit_fatal(diagnostics::CreateIncrCompDir { tag: dir_tag, path, err }) | |
| 469 | } | |
| 468 | 470 | } |
| 469 | 471 | } |
| 470 | 472 | |
| ... | ... | @@ -483,7 +485,7 @@ fn lock_directory(sess: &Session, session_dir: &Path) -> (flock::Lock, PathBuf) |
| 483 | 485 | Ok(lock) => (lock, lock_file_path), |
| 484 | 486 | Err(lock_err) => { |
| 485 | 487 | let is_unsupported_lock = flock::Lock::error_unsupported(&lock_err); |
| 486 | sess.dcx().emit_fatal(errors::CreateLock { | |
| 488 | sess.dcx().emit_fatal(diagnostics::CreateLock { | |
| 487 | 489 | lock_err, |
| 488 | 490 | session_dir, |
| 489 | 491 | is_unsupported_lock, |
| ... | ... | @@ -495,7 +497,7 @@ fn lock_directory(sess: &Session, session_dir: &Path) -> (flock::Lock, PathBuf) |
| 495 | 497 | |
| 496 | 498 | fn delete_session_dir_lock_file(sess: &Session, lock_file_path: &Path) { |
| 497 | 499 | if let Err(err) = safe_remove_file(lock_file_path) { |
| 498 | sess.dcx().emit_warn(errors::DeleteLock { path: lock_file_path, err }); | |
| 500 | sess.dcx().emit_warn(diagnostics::DeleteLock { path: lock_file_path, err }); | |
| 499 | 501 | } |
| 500 | 502 | } |
| 501 | 503 | |
| ... | ... | @@ -708,7 +710,7 @@ pub(crate) fn garbage_collect_session_directories(sess: &Session) -> io::Result< |
| 708 | 710 | if !lock_file_to_session_dir.items().any(|(_, dir)| *dir == directory_name) { |
| 709 | 711 | let path = crate_directory.join(directory_name); |
| 710 | 712 | if let Err(err) = std_fs::remove_dir_all(&path) { |
| 711 | sess.dcx().emit_warn(errors::InvalidGcFailed { path: &path, err }); | |
| 713 | sess.dcx().emit_warn(diagnostics::InvalidGcFailed { path: &path, err }); | |
| 712 | 714 | } |
| 713 | 715 | } |
| 714 | 716 | } |
| ... | ... | @@ -840,7 +842,7 @@ pub(crate) fn garbage_collect_session_directories(sess: &Session) -> io::Result< |
| 840 | 842 | debug!("garbage_collect_session_directories() - deleting `{}`", path.display()); |
| 841 | 843 | |
| 842 | 844 | if let Err(err) = std_fs::remove_dir_all(&path) { |
| 843 | sess.dcx().emit_warn(errors::FinalizedGcFailed { path: &path, err }); | |
| 845 | sess.dcx().emit_warn(diagnostics::FinalizedGcFailed { path: &path, err }); | |
| 844 | 846 | } else { |
| 845 | 847 | delete_session_dir_lock_file(sess, &lock_file_path(&path)); |
| 846 | 848 | } |
| ... | ... | @@ -858,7 +860,7 @@ fn delete_old(sess: &Session, path: &Path) { |
| 858 | 860 | debug!("garbage_collect_session_directories() - deleting `{}`", path.display()); |
| 859 | 861 | |
| 860 | 862 | if let Err(err) = std_fs::remove_dir_all(path) { |
| 861 | sess.dcx().emit_warn(errors::SessionGcFailed { path, err }); | |
| 863 | sess.dcx().emit_warn(diagnostics::SessionGcFailed { path, err }); | |
| 862 | 864 | } else { |
| 863 | 865 | delete_session_dir_lock_file(sess, &lock_file_path(path)); |
| 864 | 866 | } |
compiler/rustc_incremental/src/persist/load.rs+10-9| ... | ... | @@ -18,7 +18,7 @@ use tracing::{debug, warn}; |
| 18 | 18 | use super::data::*; |
| 19 | 19 | use super::fs::*; |
| 20 | 20 | use super::{file_format, work_product}; |
| 21 | use crate::errors; | |
| 21 | use crate::diagnostics; | |
| 22 | 22 | use crate::persist::file_format::{OpenFile, OpenFileError}; |
| 23 | 23 | |
| 24 | 24 | #[derive(Debug)] |
| ... | ... | @@ -60,7 +60,7 @@ fn load_dep_graph(sess: &Session) -> LoadResult { |
| 60 | 60 | { |
| 61 | 61 | // Decode the list of work_products |
| 62 | 62 | let Ok(mut work_product_decoder) = MemDecoder::new(&mmap[..], start_pos) else { |
| 63 | sess.dcx().emit_warn(errors::CorruptFile { path: &work_products_path }); | |
| 63 | sess.dcx().emit_warn(diagnostics::CorruptFile { path: &work_products_path }); | |
| 64 | 64 | return LoadResult::DataOutOfDate; |
| 65 | 65 | }; |
| 66 | 66 | let work_products: Vec<SerializedWorkProduct> = |
| ... | ... | @@ -93,7 +93,7 @@ fn load_dep_graph(sess: &Session) -> LoadResult { |
| 93 | 93 | Err(OpenFileError::IoError { err }) => LoadResult::IoError { path: path.to_owned(), err }, |
| 94 | 94 | Ok(OpenFile { mmap, start_pos }) => { |
| 95 | 95 | let Ok(mut decoder) = MemDecoder::new(&mmap, start_pos) else { |
| 96 | sess.dcx().emit_warn(errors::CorruptFile { path: &path }); | |
| 96 | sess.dcx().emit_warn(diagnostics::CorruptFile { path: &path }); | |
| 97 | 97 | return LoadResult::DataOutOfDate; |
| 98 | 98 | }; |
| 99 | 99 | let prev_commandline_args_hash = Hash64::decode(&mut decoder); |
| ... | ... | @@ -135,7 +135,7 @@ pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache> { |
| 135 | 135 | match file_format::open_incremental_file(sess, &path) { |
| 136 | 136 | Ok(OpenFile { mmap, start_pos }) => { |
| 137 | 137 | let cache = OnDiskCache::new(sess, mmap, start_pos).unwrap_or_else(|()| { |
| 138 | sess.dcx().emit_warn(errors::CorruptFile { path: &path }); | |
| 138 | sess.dcx().emit_warn(diagnostics::CorruptFile { path: &path }); | |
| 139 | 139 | OnDiskCache::new_empty() |
| 140 | 140 | }); |
| 141 | 141 | Some(cache) |
| ... | ... | @@ -161,12 +161,12 @@ fn maybe_assert_incr_state(sess: &Session, load_result: &LoadResult) { |
| 161 | 161 | match assertion { |
| 162 | 162 | IncrementalStateAssertion::Loaded => { |
| 163 | 163 | if !loaded { |
| 164 | sess.dcx().emit_fatal(errors::AssertLoaded); | |
| 164 | sess.dcx().emit_fatal(diagnostics::AssertLoaded); | |
| 165 | 165 | } |
| 166 | 166 | } |
| 167 | 167 | IncrementalStateAssertion::NotLoaded => { |
| 168 | 168 | if loaded { |
| 169 | sess.dcx().emit_fatal(errors::AssertNotLoaded) | |
| 169 | sess.dcx().emit_fatal(diagnostics::AssertNotLoaded) | |
| 170 | 170 | } |
| 171 | 171 | } |
| 172 | 172 | } |
| ... | ... | @@ -205,12 +205,13 @@ pub fn setup_dep_graph( |
| 205 | 205 | |
| 206 | 206 | let (prev_graph, prev_work_products) = match load_result { |
| 207 | 207 | LoadResult::IoError { path, err } => { |
| 208 | sess.dcx().emit_warn(errors::LoadDepGraph { path, err }); | |
| 208 | sess.dcx().emit_warn(diagnostics::LoadDepGraph { path, err }); | |
| 209 | 209 | Default::default() |
| 210 | 210 | } |
| 211 | 211 | LoadResult::DataOutOfDate => { |
| 212 | 212 | if let Err(err) = delete_all_session_dir_contents(sess) { |
| 213 | sess.dcx().emit_err(errors::DeleteIncompatible { path: dep_graph_path(sess), err }); | |
| 213 | sess.dcx() | |
| 214 | .emit_err(diagnostics::DeleteIncompatible { path: dep_graph_path(sess), err }); | |
| 214 | 215 | } |
| 215 | 216 | Default::default() |
| 216 | 217 | } |
| ... | ... | @@ -223,7 +224,7 @@ pub fn setup_dep_graph( |
| 223 | 224 | let mut encoder = FileEncoder::new(&path_buf).unwrap_or_else(|err| { |
| 224 | 225 | // We're in incremental mode but couldn't set up streaming output of the dep graph. |
| 225 | 226 | // Exit immediately instead of continuing in an inconsistent and untested state. |
| 226 | sess.dcx().emit_fatal(errors::CreateDepGraph { path: &path_buf, err }) | |
| 227 | sess.dcx().emit_fatal(diagnostics::CreateDepGraph { path: &path_buf, err }) | |
| 227 | 228 | }); |
| 228 | 229 | |
| 229 | 230 | file_format::write_file_header(&mut encoder, sess); |
compiler/rustc_incremental/src/persist/save.rs+2-2| ... | ... | @@ -13,7 +13,7 @@ use super::data::*; |
| 13 | 13 | use super::fs::*; |
| 14 | 14 | use super::{clean, file_format, work_product}; |
| 15 | 15 | use crate::assert_dep_graph::assert_dep_graph; |
| 16 | use crate::errors; | |
| 16 | use crate::diagnostics; | |
| 17 | 17 | |
| 18 | 18 | /// Saves and writes the [`DepGraph`] to the file system. |
| 19 | 19 | /// |
| ... | ... | @@ -45,7 +45,7 @@ pub(crate) fn save_dep_graph(tcx: TyCtxt<'_>) { |
| 45 | 45 | move || { |
| 46 | 46 | sess.time("incr_comp_persist_dep_graph", || { |
| 47 | 47 | if let Err(err) = fs::rename(&staging_dep_graph_path, &dep_graph_path) { |
| 48 | sess.dcx().emit_err(errors::MoveDepGraph { | |
| 48 | sess.dcx().emit_err(diagnostics::MoveDepGraph { | |
| 49 | 49 | from: &staging_dep_graph_path, |
| 50 | 50 | to: &dep_graph_path, |
| 51 | 51 | err, |
compiler/rustc_incremental/src/persist/work_product.rs+3-3| ... | ... | @@ -11,7 +11,7 @@ use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; |
| 11 | 11 | use rustc_session::Session; |
| 12 | 12 | use tracing::debug; |
| 13 | 13 | |
| 14 | use crate::errors; | |
| 14 | use crate::diagnostics; | |
| 15 | 15 | use crate::persist::fs::*; |
| 16 | 16 | |
| 17 | 17 | /// Copies a CGU work product to the incremental compilation directory, so next compilation can |
| ... | ... | @@ -40,7 +40,7 @@ pub fn copy_cgu_workproduct_to_incr_comp_cache_dir( |
| 40 | 40 | let _ = saved_files.insert(ext.to_string(), file_name); |
| 41 | 41 | } |
| 42 | 42 | Err(err) => { |
| 43 | sess.dcx().emit_warn(errors::CopyWorkProductToCache { | |
| 43 | sess.dcx().emit_warn(diagnostics::CopyWorkProductToCache { | |
| 44 | 44 | from: path, |
| 45 | 45 | to: &path_in_incr_dir, |
| 46 | 46 | err, |
| ... | ... | @@ -60,7 +60,7 @@ pub(crate) fn delete_workproduct_files(sess: &Session, work_product: &WorkProduc |
| 60 | 60 | for (_, path) in work_product.saved_files.items().into_sorted_stable_ord() { |
| 61 | 61 | let path = in_incr_comp_dir_sess(sess, path); |
| 62 | 62 | if let Err(err) = std_fs::remove_file(&path) { |
| 63 | sess.dcx().emit_warn(errors::DeleteWorkProduct { path: &path, err }); | |
| 63 | sess.dcx().emit_warn(diagnostics::DeleteWorkProduct { path: &path, err }); | |
| 64 | 64 | } |
| 65 | 65 | } |
| 66 | 66 | } |
compiler/rustc_index/src/interval.rs+4| ... | ... | @@ -370,6 +370,10 @@ impl<R: Idx, C: Step + Idx> SparseIntervalMatrix<R, C> { |
| 370 | 370 | self.rows.get(row) |
| 371 | 371 | } |
| 372 | 372 | |
| 373 | pub fn iter_enumerated(&self) -> impl Iterator<Item = (R, &IntervalSet<C>)> { | |
| 374 | self.rows.iter_enumerated() | |
| 375 | } | |
| 376 | ||
| 373 | 377 | fn ensure_row(&mut self, row: R) -> &mut IntervalSet<C> { |
| 374 | 378 | self.rows.ensure_contains_elem(row, || IntervalSet::new(self.column_size)) |
| 375 | 379 | } |
compiler/rustc_infer/src/infer/context.rs+15-1| ... | ... | @@ -209,7 +209,7 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { |
| 209 | 209 | ) |
| 210 | 210 | } |
| 211 | 211 | |
| 212 | fn enter_forall<T: TypeFoldable<TyCtxt<'tcx>>, U>( | |
| 212 | fn enter_forall_without_assumptions<T: TypeFoldable<TyCtxt<'tcx>>, U>( | |
| 213 | 213 | &self, |
| 214 | 214 | value: ty::Binder<'tcx, T>, |
| 215 | 215 | f: impl FnOnce(T) -> U, |
| ... | ... | @@ -217,6 +217,20 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> { |
| 217 | 217 | self.enter_forall(value, f) |
| 218 | 218 | } |
| 219 | 219 | |
| 220 | fn enter_forall_with_empty_assumptions<T: TypeFoldable<TyCtxt<'tcx>>, U>( | |
| 221 | &self, | |
| 222 | value: ty::Binder<'tcx, T>, | |
| 223 | f: impl FnOnce(T) -> U, | |
| 224 | ) -> U { | |
| 225 | self.enter_forall(value, |value| { | |
| 226 | let u = self.universe(); | |
| 227 | self.placeholder_assumptions_for_next_solver | |
| 228 | .borrow_mut() | |
| 229 | .insert(u, Some(rustc_type_ir::region_constraint::Assumptions::empty())); | |
| 230 | f(value) | |
| 231 | }) | |
| 232 | } | |
| 233 | ||
| 220 | 234 | fn equate_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid) { |
| 221 | 235 | self.inner.borrow_mut().type_variables().equate(a, b); |
| 222 | 236 | } |
compiler/rustc_interface/src/tests.rs+28-1| ... | ... | @@ -25,7 +25,7 @@ use rustc_session::utils::{CanonicalizedPath, NativeLib}; |
| 25 | 25 | use rustc_session::{CompilerIO, EarlyDiagCtxt, Session, build_session, getopts}; |
| 26 | 26 | use rustc_span::edition::{DEFAULT_EDITION, Edition}; |
| 27 | 27 | use rustc_span::source_map::{RealFileLoader, SourceMapInputs}; |
| 28 | use rustc_span::{FileName, SourceFileHashAlgorithm, sym}; | |
| 28 | use rustc_span::{FileName, RealFileName, RemapPathScopeComponents, SourceFileHashAlgorithm, sym}; | |
| 29 | 29 | use rustc_target::spec::{ |
| 30 | 30 | CodeModel, FramePointer, LinkerFlavorCli, MergeFunctions, OnBrokenPipe, PanicStrategy, |
| 31 | 31 | RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel, |
| ... | ... | @@ -175,6 +175,33 @@ fn test_can_print_warnings() { |
| 175 | 175 | }); |
| 176 | 176 | } |
| 177 | 177 | |
| 178 | // `-Zremap-cwd-prefix` must not be merged into the tracked `remap_path_prefix` option: | |
| 179 | // storing the absolute cwd there invalidates the incremental cache across build | |
| 180 | // directories (see #132132). It must instead be applied via `file_path_mapping`. | |
| 181 | #[test] | |
| 182 | fn test_remap_cwd_prefix_not_in_remap_path_prefix() { | |
| 183 | sess_and_cfg( | |
| 184 | &["--remap-path-prefix=/explicit=mapped", "-Zremap-cwd-prefix=cwd-mapped"], | |
| 185 | |sess, _cfg| { | |
| 186 | // The tracked option holds only the explicit `--remap-path-prefix` entry. | |
| 187 | assert_eq!( | |
| 188 | sess.opts.remap_path_prefix, | |
| 189 | vec![(PathBuf::from("/explicit"), PathBuf::from("mapped"))], | |
| 190 | ); | |
| 191 | ||
| 192 | // ... but the cwd remapping is still applied via `file_path_mapping`. | |
| 193 | let cwd = std::env::current_dir().unwrap(); | |
| 194 | let remapped = sess | |
| 195 | .opts | |
| 196 | .file_path_mapping() | |
| 197 | .to_real_filename(&RealFileName::empty(), cwd.join("foo.rs")) | |
| 198 | .path(RemapPathScopeComponents::DEBUGINFO) | |
| 199 | .to_path_buf(); | |
| 200 | assert_eq!(remapped, PathBuf::from("cwd-mapped/foo.rs")); | |
| 201 | }, | |
| 202 | ); | |
| 203 | } | |
| 204 | ||
| 178 | 205 | #[test] |
| 179 | 206 | fn test_output_types_tracking_hash_different_paths() { |
| 180 | 207 | let mut v1 = Options::default(); |
compiler/rustc_monomorphize/src/collector.rs+2-2| ... | ... | @@ -235,7 +235,7 @@ use rustc_session::config::{DebugInfo, EntryFnType}; |
| 235 | 235 | use rustc_span::{DUMMY_SP, Span, Spanned, dummy_spanned, respan}; |
| 236 | 236 | use tracing::{debug, instrument, trace}; |
| 237 | 237 | |
| 238 | use crate::errors::{ | |
| 238 | use crate::diagnostics::{ | |
| 239 | 239 | self, EncounteredErrorWhileInstantiating, EncounteredErrorWhileInstantiatingGlobalAsm, |
| 240 | 240 | NoOptimizedMir, RecursionLimit, |
| 241 | 241 | }; |
| ... | ... | @@ -1702,7 +1702,7 @@ impl<'v> RootCollector<'_, 'v> { |
| 1702 | 1702 | } |
| 1703 | 1703 | |
| 1704 | 1704 | let Some(start_def_id) = self.tcx.lang_items().start_fn() else { |
| 1705 | self.tcx.dcx().emit_fatal(errors::StartNotFound); | |
| 1705 | self.tcx.dcx().emit_fatal(diagnostics::StartNotFound); | |
| 1706 | 1706 | }; |
| 1707 | 1707 | let main_ret_ty = self.tcx.fn_sig(main_def_id).no_bound_vars().unwrap().output(); |
| 1708 | 1708 |
compiler/rustc_monomorphize/src/diagnostics.rs created+186| ... | ... | @@ -0,0 +1,186 @@ |
| 1 | use rustc_macros::Diagnostic; | |
| 2 | use rustc_middle::ty::{Instance, Ty}; | |
| 3 | use rustc_span::{Span, Symbol}; | |
| 4 | ||
| 5 | #[derive(Diagnostic)] | |
| 6 | #[diag("reached the recursion limit while instantiating `{$instance}`")] | |
| 7 | pub(crate) struct RecursionLimit<'tcx> { | |
| 8 | #[primary_span] | |
| 9 | pub span: Span, | |
| 10 | pub instance: Instance<'tcx>, | |
| 11 | #[note("`{$def_path_str}` defined here")] | |
| 12 | pub def_span: Span, | |
| 13 | pub def_path_str: String, | |
| 14 | } | |
| 15 | ||
| 16 | #[derive(Diagnostic)] | |
| 17 | #[diag("missing optimized MIR for `{$instance}` in the crate `{$crate_name}`")] | |
| 18 | pub(crate) struct NoOptimizedMir { | |
| 19 | #[note( | |
| 20 | "missing optimized MIR for this item (was the crate `{$crate_name}` compiled with `--emit=metadata`?)" | |
| 21 | )] | |
| 22 | pub span: Span, | |
| 23 | pub crate_name: Symbol, | |
| 24 | pub instance: String, | |
| 25 | } | |
| 26 | ||
| 27 | #[derive(Diagnostic)] | |
| 28 | #[diag("moving {$size} bytes")] | |
| 29 | #[note( | |
| 30 | "the current maximum size is {$limit}, but it can be customized with the move_size_limit attribute: `#![move_size_limit = \"...\"]`" | |
| 31 | )] | |
| 32 | pub(crate) struct LargeAssignmentsLint { | |
| 33 | #[label("value moved from here")] | |
| 34 | pub span: Span, | |
| 35 | pub size: u64, | |
| 36 | pub limit: u64, | |
| 37 | } | |
| 38 | ||
| 39 | #[derive(Diagnostic)] | |
| 40 | #[diag("symbol `{$symbol}` is already defined")] | |
| 41 | pub(crate) struct SymbolAlreadyDefined { | |
| 42 | #[primary_span] | |
| 43 | pub span: Option<Span>, | |
| 44 | pub symbol: String, | |
| 45 | } | |
| 46 | ||
| 47 | #[derive(Diagnostic)] | |
| 48 | #[diag("unexpected error occurred while dumping monomorphization stats: {$error}")] | |
| 49 | pub(crate) struct CouldntDumpMonoStats { | |
| 50 | pub error: String, | |
| 51 | } | |
| 52 | ||
| 53 | #[derive(Diagnostic)] | |
| 54 | #[diag("the above error was encountered while instantiating `{$kind} {$instance}`")] | |
| 55 | pub(crate) struct EncounteredErrorWhileInstantiating<'tcx> { | |
| 56 | #[primary_span] | |
| 57 | pub span: Span, | |
| 58 | pub kind: &'static str, | |
| 59 | pub instance: Instance<'tcx>, | |
| 60 | } | |
| 61 | ||
| 62 | #[derive(Diagnostic)] | |
| 63 | #[diag("the above error was encountered while instantiating `global_asm`")] | |
| 64 | pub(crate) struct EncounteredErrorWhileInstantiatingGlobalAsm { | |
| 65 | #[primary_span] | |
| 66 | pub span: Span, | |
| 67 | } | |
| 68 | ||
| 69 | #[derive(Diagnostic)] | |
| 70 | #[diag("using `fn main` requires the standard library")] | |
| 71 | #[help( | |
| 72 | "use `#![no_main]` to bypass the Rust generated entrypoint and declare a platform specific entrypoint yourself, usually with `#[no_mangle]`" | |
| 73 | )] | |
| 74 | pub(crate) struct StartNotFound; | |
| 75 | ||
| 76 | #[derive(Diagnostic)] | |
| 77 | #[diag("this function {$is_call -> | |
| 78 | [true] call | |
| 79 | *[false] definition | |
| 80 | } uses {$is_scalable -> | |
| 81 | [true] scalable | |
| 82 | *[false] SIMD | |
| 83 | } vector type `{$ty}` which (with the chosen ABI) requires the `{$required_feature}` target feature, which is not enabled{$is_call -> | |
| 84 | [true] {\" \"}in the caller | |
| 85 | *[false] {\"\"} | |
| 86 | }")] | |
| 87 | #[help( | |
| 88 | "consider enabling it globally (`-C target-feature=+{$required_feature}`) or locally (`#[target_feature(enable=\"{$required_feature}\")]`)" | |
| 89 | )] | |
| 90 | pub(crate) struct AbiErrorDisabledVectorType<'a> { | |
| 91 | #[primary_span] | |
| 92 | #[label( | |
| 93 | "function {$is_call -> | |
| 94 | [true] called | |
| 95 | *[false] defined | |
| 96 | } here" | |
| 97 | )] | |
| 98 | pub span: Span, | |
| 99 | pub required_feature: &'a str, | |
| 100 | pub ty: Ty<'a>, | |
| 101 | /// Whether this is a problem at a call site or at a declaration. | |
| 102 | pub is_call: bool, | |
| 103 | /// Whether this is a problem with a fixed length vector or a scalable vector | |
| 104 | pub is_scalable: bool, | |
| 105 | } | |
| 106 | ||
| 107 | #[derive(Diagnostic)] | |
| 108 | #[diag( | |
| 109 | "this function {$is_call -> | |
| 110 | [true] call | |
| 111 | *[false] definition | |
| 112 | } uses unsized type `{$ty}` which is not supported with the chosen ABI" | |
| 113 | )] | |
| 114 | #[help("only rustic ABIs support unsized parameters")] | |
| 115 | pub(crate) struct AbiErrorUnsupportedUnsizedParameter<'a> { | |
| 116 | #[primary_span] | |
| 117 | #[label( | |
| 118 | "function {$is_call -> | |
| 119 | [true] called | |
| 120 | *[false] defined | |
| 121 | } here" | |
| 122 | )] | |
| 123 | pub span: Span, | |
| 124 | pub ty: Ty<'a>, | |
| 125 | /// Whether this is a problem at a call site or at a declaration. | |
| 126 | pub is_call: bool, | |
| 127 | } | |
| 128 | ||
| 129 | #[derive(Diagnostic)] | |
| 130 | #[diag( | |
| 131 | "this function {$is_call -> | |
| 132 | [true] call | |
| 133 | *[false] definition | |
| 134 | } uses SIMD vector type `{$ty}` which is not currently supported with the chosen ABI" | |
| 135 | )] | |
| 136 | pub(crate) struct AbiErrorUnsupportedVectorType<'a> { | |
| 137 | #[primary_span] | |
| 138 | #[label( | |
| 139 | "function {$is_call -> | |
| 140 | [true] called | |
| 141 | *[false] defined | |
| 142 | } here" | |
| 143 | )] | |
| 144 | pub span: Span, | |
| 145 | pub ty: Ty<'a>, | |
| 146 | /// Whether this is a problem at a call site or at a declaration. | |
| 147 | pub is_call: bool, | |
| 148 | } | |
| 149 | ||
| 150 | #[derive(Diagnostic)] | |
| 151 | #[diag("this function {$is_call -> | |
| 152 | [true] call | |
| 153 | *[false] definition | |
| 154 | } uses ABI \"{$abi}\" which requires the `{$required_feature}` target feature, which is not enabled{$is_call -> | |
| 155 | [true] {\" \"}in the caller | |
| 156 | *[false] {\"\"} | |
| 157 | }")] | |
| 158 | #[help( | |
| 159 | "consider enabling it globally (`-C target-feature=+{$required_feature}`) or locally (`#[target_feature(enable=\"{$required_feature}\")]`)" | |
| 160 | )] | |
| 161 | pub(crate) struct AbiRequiredTargetFeature<'a> { | |
| 162 | #[primary_span] | |
| 163 | #[label( | |
| 164 | "function {$is_call -> | |
| 165 | [true] called | |
| 166 | *[false] defined | |
| 167 | } here" | |
| 168 | )] | |
| 169 | pub span: Span, | |
| 170 | pub required_feature: &'a str, | |
| 171 | pub abi: &'a str, | |
| 172 | /// Whether this is a problem at a call site or at a declaration. | |
| 173 | pub is_call: bool, | |
| 174 | } | |
| 175 | ||
| 176 | #[derive(Diagnostic)] | |
| 177 | #[diag("static initializer forms a cycle involving `{$head}`")] | |
| 178 | #[note("cyclic static initializers are not supported for target `{$target}`")] | |
| 179 | pub(crate) struct StaticInitializerCyclic<'a> { | |
| 180 | #[primary_span] | |
| 181 | pub span: Span, | |
| 182 | #[label("part of this cycle")] | |
| 183 | pub labels: Vec<Span>, | |
| 184 | pub head: &'a str, | |
| 185 | pub target: &'a str, | |
| 186 | } |
compiler/rustc_monomorphize/src/errors.rs deleted-186| ... | ... | @@ -1,186 +0,0 @@ |
| 1 | use rustc_macros::Diagnostic; | |
| 2 | use rustc_middle::ty::{Instance, Ty}; | |
| 3 | use rustc_span::{Span, Symbol}; | |
| 4 | ||
| 5 | #[derive(Diagnostic)] | |
| 6 | #[diag("reached the recursion limit while instantiating `{$instance}`")] | |
| 7 | pub(crate) struct RecursionLimit<'tcx> { | |
| 8 | #[primary_span] | |
| 9 | pub span: Span, | |
| 10 | pub instance: Instance<'tcx>, | |
| 11 | #[note("`{$def_path_str}` defined here")] | |
| 12 | pub def_span: Span, | |
| 13 | pub def_path_str: String, | |
| 14 | } | |
| 15 | ||
| 16 | #[derive(Diagnostic)] | |
| 17 | #[diag("missing optimized MIR for `{$instance}` in the crate `{$crate_name}`")] | |
| 18 | pub(crate) struct NoOptimizedMir { | |
| 19 | #[note( | |
| 20 | "missing optimized MIR for this item (was the crate `{$crate_name}` compiled with `--emit=metadata`?)" | |
| 21 | )] | |
| 22 | pub span: Span, | |
| 23 | pub crate_name: Symbol, | |
| 24 | pub instance: String, | |
| 25 | } | |
| 26 | ||
| 27 | #[derive(Diagnostic)] | |
| 28 | #[diag("moving {$size} bytes")] | |
| 29 | #[note( | |
| 30 | "the current maximum size is {$limit}, but it can be customized with the move_size_limit attribute: `#![move_size_limit = \"...\"]`" | |
| 31 | )] | |
| 32 | pub(crate) struct LargeAssignmentsLint { | |
| 33 | #[label("value moved from here")] | |
| 34 | pub span: Span, | |
| 35 | pub size: u64, | |
| 36 | pub limit: u64, | |
| 37 | } | |
| 38 | ||
| 39 | #[derive(Diagnostic)] | |
| 40 | #[diag("symbol `{$symbol}` is already defined")] | |
| 41 | pub(crate) struct SymbolAlreadyDefined { | |
| 42 | #[primary_span] | |
| 43 | pub span: Option<Span>, | |
| 44 | pub symbol: String, | |
| 45 | } | |
| 46 | ||
| 47 | #[derive(Diagnostic)] | |
| 48 | #[diag("unexpected error occurred while dumping monomorphization stats: {$error}")] | |
| 49 | pub(crate) struct CouldntDumpMonoStats { | |
| 50 | pub error: String, | |
| 51 | } | |
| 52 | ||
| 53 | #[derive(Diagnostic)] | |
| 54 | #[diag("the above error was encountered while instantiating `{$kind} {$instance}`")] | |
| 55 | pub(crate) struct EncounteredErrorWhileInstantiating<'tcx> { | |
| 56 | #[primary_span] | |
| 57 | pub span: Span, | |
| 58 | pub kind: &'static str, | |
| 59 | pub instance: Instance<'tcx>, | |
| 60 | } | |
| 61 | ||
| 62 | #[derive(Diagnostic)] | |
| 63 | #[diag("the above error was encountered while instantiating `global_asm`")] | |
| 64 | pub(crate) struct EncounteredErrorWhileInstantiatingGlobalAsm { | |
| 65 | #[primary_span] | |
| 66 | pub span: Span, | |
| 67 | } | |
| 68 | ||
| 69 | #[derive(Diagnostic)] | |
| 70 | #[diag("using `fn main` requires the standard library")] | |
| 71 | #[help( | |
| 72 | "use `#![no_main]` to bypass the Rust generated entrypoint and declare a platform specific entrypoint yourself, usually with `#[no_mangle]`" | |
| 73 | )] | |
| 74 | pub(crate) struct StartNotFound; | |
| 75 | ||
| 76 | #[derive(Diagnostic)] | |
| 77 | #[diag("this function {$is_call -> | |
| 78 | [true] call | |
| 79 | *[false] definition | |
| 80 | } uses {$is_scalable -> | |
| 81 | [true] scalable | |
| 82 | *[false] SIMD | |
| 83 | } vector type `{$ty}` which (with the chosen ABI) requires the `{$required_feature}` target feature, which is not enabled{$is_call -> | |
| 84 | [true] {\" \"}in the caller | |
| 85 | *[false] {\"\"} | |
| 86 | }")] | |
| 87 | #[help( | |
| 88 | "consider enabling it globally (`-C target-feature=+{$required_feature}`) or locally (`#[target_feature(enable=\"{$required_feature}\")]`)" | |
| 89 | )] | |
| 90 | pub(crate) struct AbiErrorDisabledVectorType<'a> { | |
| 91 | #[primary_span] | |
| 92 | #[label( | |
| 93 | "function {$is_call -> | |
| 94 | [true] called | |
| 95 | *[false] defined | |
| 96 | } here" | |
| 97 | )] | |
| 98 | pub span: Span, | |
| 99 | pub required_feature: &'a str, | |
| 100 | pub ty: Ty<'a>, | |
| 101 | /// Whether this is a problem at a call site or at a declaration. | |
| 102 | pub is_call: bool, | |
| 103 | /// Whether this is a problem with a fixed length vector or a scalable vector | |
| 104 | pub is_scalable: bool, | |
| 105 | } | |
| 106 | ||
| 107 | #[derive(Diagnostic)] | |
| 108 | #[diag( | |
| 109 | "this function {$is_call -> | |
| 110 | [true] call | |
| 111 | *[false] definition | |
| 112 | } uses unsized type `{$ty}` which is not supported with the chosen ABI" | |
| 113 | )] | |
| 114 | #[help("only rustic ABIs support unsized parameters")] | |
| 115 | pub(crate) struct AbiErrorUnsupportedUnsizedParameter<'a> { | |
| 116 | #[primary_span] | |
| 117 | #[label( | |
| 118 | "function {$is_call -> | |
| 119 | [true] called | |
| 120 | *[false] defined | |
| 121 | } here" | |
| 122 | )] | |
| 123 | pub span: Span, | |
| 124 | pub ty: Ty<'a>, | |
| 125 | /// Whether this is a problem at a call site or at a declaration. | |
| 126 | pub is_call: bool, | |
| 127 | } | |
| 128 | ||
| 129 | #[derive(Diagnostic)] | |
| 130 | #[diag( | |
| 131 | "this function {$is_call -> | |
| 132 | [true] call | |
| 133 | *[false] definition | |
| 134 | } uses SIMD vector type `{$ty}` which is not currently supported with the chosen ABI" | |
| 135 | )] | |
| 136 | pub(crate) struct AbiErrorUnsupportedVectorType<'a> { | |
| 137 | #[primary_span] | |
| 138 | #[label( | |
| 139 | "function {$is_call -> | |
| 140 | [true] called | |
| 141 | *[false] defined | |
| 142 | } here" | |
| 143 | )] | |
| 144 | pub span: Span, | |
| 145 | pub ty: Ty<'a>, | |
| 146 | /// Whether this is a problem at a call site or at a declaration. | |
| 147 | pub is_call: bool, | |
| 148 | } | |
| 149 | ||
| 150 | #[derive(Diagnostic)] | |
| 151 | #[diag("this function {$is_call -> | |
| 152 | [true] call | |
| 153 | *[false] definition | |
| 154 | } uses ABI \"{$abi}\" which requires the `{$required_feature}` target feature, which is not enabled{$is_call -> | |
| 155 | [true] {\" \"}in the caller | |
| 156 | *[false] {\"\"} | |
| 157 | }")] | |
| 158 | #[help( | |
| 159 | "consider enabling it globally (`-C target-feature=+{$required_feature}`) or locally (`#[target_feature(enable=\"{$required_feature}\")]`)" | |
| 160 | )] | |
| 161 | pub(crate) struct AbiRequiredTargetFeature<'a> { | |
| 162 | #[primary_span] | |
| 163 | #[label( | |
| 164 | "function {$is_call -> | |
| 165 | [true] called | |
| 166 | *[false] defined | |
| 167 | } here" | |
| 168 | )] | |
| 169 | pub span: Span, | |
| 170 | pub required_feature: &'a str, | |
| 171 | pub abi: &'a str, | |
| 172 | /// Whether this is a problem at a call site or at a declaration. | |
| 173 | pub is_call: bool, | |
| 174 | } | |
| 175 | ||
| 176 | #[derive(Diagnostic)] | |
| 177 | #[diag("static initializer forms a cycle involving `{$head}`")] | |
| 178 | #[note("cyclic static initializers are not supported for target `{$target}`")] | |
| 179 | pub(crate) struct StaticInitializerCyclic<'a> { | |
| 180 | #[primary_span] | |
| 181 | pub span: Span, | |
| 182 | #[label("part of this cycle")] | |
| 183 | pub labels: Vec<Span>, | |
| 184 | pub head: &'a str, | |
| 185 | pub target: &'a str, | |
| 186 | } |
compiler/rustc_monomorphize/src/graph_checks/statics.rs+2-2| ... | ... | @@ -8,7 +8,7 @@ use rustc_middle::mono::MonoItem; |
| 8 | 8 | use rustc_middle::ty::TyCtxt; |
| 9 | 9 | |
| 10 | 10 | use crate::collector::UsageMap; |
| 11 | use crate::errors; | |
| 11 | use crate::diagnostics; | |
| 12 | 12 | |
| 13 | 13 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] |
| 14 | 14 | struct StaticNodeIdx(usize); |
| ... | ... | @@ -105,7 +105,7 @@ pub(super) fn check_static_initializers_are_acyclic<'tcx, 'a, 'b>( |
| 105 | 105 | let head_def = statics[nodes[0].index()]; |
| 106 | 106 | let head_span = tcx.def_span(head_def); |
| 107 | 107 | |
| 108 | tcx.dcx().emit_err(errors::StaticInitializerCyclic { | |
| 108 | tcx.dcx().emit_err(diagnostics::StaticInitializerCyclic { | |
| 109 | 109 | span: head_span, |
| 110 | 110 | labels: nodes.iter().map(|&n| tcx.def_span(statics[n.index()])).collect(), |
| 111 | 111 | head: &tcx.def_path_str(head_def), |
compiler/rustc_monomorphize/src/lib.rs+1-1| ... | ... | @@ -13,7 +13,7 @@ use rustc_middle::{bug, traits}; |
| 13 | 13 | use rustc_span::ErrorGuaranteed; |
| 14 | 14 | |
| 15 | 15 | mod collector; |
| 16 | mod errors; | |
| 16 | mod diagnostics; | |
| 17 | 17 | mod graph_checks; |
| 18 | 18 | mod mono_checks; |
| 19 | 19 | mod partitioning; |
compiler/rustc_monomorphize/src/mono_checks/abi_check.rs+6-6| ... | ... | @@ -8,7 +8,7 @@ use rustc_span::def_id::DefId; |
| 8 | 8 | use rustc_span::{DUMMY_SP, Span, Symbol, sym}; |
| 9 | 9 | use rustc_target::callconv::{FnAbi, PassMode}; |
| 10 | 10 | |
| 11 | use crate::errors; | |
| 11 | use crate::diagnostics; | |
| 12 | 12 | |
| 13 | 13 | /// Are vector registers used? |
| 14 | 14 | enum UsesVectorRegisters { |
| ... | ... | @@ -71,7 +71,7 @@ fn do_check_simd_vector_abi<'tcx>( |
| 71 | 71 | Some((_, feature)) => feature, |
| 72 | 72 | None => { |
| 73 | 73 | let (span, _hir_id) = loc(); |
| 74 | tcx.dcx().emit_err(errors::AbiErrorUnsupportedVectorType { | |
| 74 | tcx.dcx().emit_err(diagnostics::AbiErrorUnsupportedVectorType { | |
| 75 | 75 | span, |
| 76 | 76 | ty: arg_abi.layout.ty, |
| 77 | 77 | is_call, |
| ... | ... | @@ -81,7 +81,7 @@ fn do_check_simd_vector_abi<'tcx>( |
| 81 | 81 | }; |
| 82 | 82 | if !feature.is_empty() && !have_feature(Symbol::intern(feature)) { |
| 83 | 83 | let (span, _hir_id) = loc(); |
| 84 | tcx.dcx().emit_err(errors::AbiErrorDisabledVectorType { | |
| 84 | tcx.dcx().emit_err(diagnostics::AbiErrorDisabledVectorType { | |
| 85 | 85 | span, |
| 86 | 86 | required_feature: feature, |
| 87 | 87 | ty: arg_abi.layout.ty, |
| ... | ... | @@ -98,7 +98,7 @@ fn do_check_simd_vector_abi<'tcx>( |
| 98 | 98 | }; |
| 99 | 99 | if !required_feature.is_empty() && !have_feature(Symbol::intern(required_feature)) { |
| 100 | 100 | let (span, _) = loc(); |
| 101 | tcx.dcx().emit_err(errors::AbiErrorDisabledVectorType { | |
| 101 | tcx.dcx().emit_err(diagnostics::AbiErrorDisabledVectorType { | |
| 102 | 102 | span, |
| 103 | 103 | required_feature, |
| 104 | 104 | ty: arg_abi.layout.ty, |
| ... | ... | @@ -115,7 +115,7 @@ fn do_check_simd_vector_abi<'tcx>( |
| 115 | 115 | // The `vectorcall` ABI is special in that it requires SSE2 no matter which types are being passed. |
| 116 | 116 | if abi.conv == CanonAbi::X86(X86Call::Vectorcall) && !have_feature(sym::sse2) { |
| 117 | 117 | let (span, _hir_id) = loc(); |
| 118 | tcx.dcx().emit_err(errors::AbiRequiredTargetFeature { | |
| 118 | tcx.dcx().emit_err(diagnostics::AbiRequiredTargetFeature { | |
| 119 | 119 | span, |
| 120 | 120 | required_feature: "sse2", |
| 121 | 121 | abi: "vectorcall", |
| ... | ... | @@ -142,7 +142,7 @@ fn do_check_unsized_params<'tcx>( |
| 142 | 142 | for arg_abi in fn_abi.args.iter() { |
| 143 | 143 | if !arg_abi.layout.layout.is_sized() { |
| 144 | 144 | let (span, _hir_id) = loc(); |
| 145 | tcx.dcx().emit_err(errors::AbiErrorUnsupportedUnsizedParameter { | |
| 145 | tcx.dcx().emit_err(diagnostics::AbiErrorUnsupportedUnsizedParameter { | |
| 146 | 146 | span, |
| 147 | 147 | ty: arg_abi.layout.ty, |
| 148 | 148 | is_call, |
compiler/rustc_monomorphize/src/mono_checks/move_check.rs+1-1| ... | ... | @@ -9,7 +9,7 @@ use rustc_session::lint::builtin::LARGE_ASSIGNMENTS; |
| 9 | 9 | use rustc_span::{Span, Spanned, sym}; |
| 10 | 10 | use tracing::{debug, trace}; |
| 11 | 11 | |
| 12 | use crate::errors::LargeAssignmentsLint; | |
| 12 | use crate::diagnostics::LargeAssignmentsLint; | |
| 13 | 13 | |
| 14 | 14 | struct MoveCheckVisitor<'tcx> { |
| 15 | 15 | tcx: TyCtxt<'tcx>, |
compiler/rustc_monomorphize/src/partitioning.rs+1-1| ... | ... | @@ -124,7 +124,7 @@ use rustc_target::spec::SymbolVisibility; |
| 124 | 124 | use tracing::debug; |
| 125 | 125 | |
| 126 | 126 | use crate::collector::{self, MonoItemCollectionStrategy, UsageMap}; |
| 127 | use crate::errors::{CouldntDumpMonoStats, SymbolAlreadyDefined}; | |
| 127 | use crate::diagnostics::{CouldntDumpMonoStats, SymbolAlreadyDefined}; | |
| 128 | 128 | use crate::graph_checks::target_specific_checks; |
| 129 | 129 | |
| 130 | 130 | struct PartitioningCx<'a, 'tcx> { |
compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs+3-3| ... | ... | @@ -1331,8 +1331,8 @@ where |
| 1331 | 1331 | self.delegate.instantiate_binder_with_infer(value) |
| 1332 | 1332 | } |
| 1333 | 1333 | |
| 1334 | /// `enter_forall`, but takes `&mut self` and passes it back through the | |
| 1335 | /// callback since it can't be aliased during the call. | |
| 1334 | /// `enter_forall_with_assumptions`, but takes `&mut self` and passes it back through | |
| 1335 | /// the callback since it can't be aliased during the call. | |
| 1336 | 1336 | /// |
| 1337 | 1337 | /// The `param_env` is used to *compute* the assumptions of the binder, not *as* the |
| 1338 | 1338 | /// assumptions associated with the binder. |
| ... | ... | @@ -1344,7 +1344,7 @@ where |
| 1344 | 1344 | param_env: I::ParamEnv, |
| 1345 | 1345 | f: impl FnOnce(&mut Self, T) -> U, |
| 1346 | 1346 | ) -> U { |
| 1347 | self.delegate.enter_forall(value, |value| { | |
| 1347 | self.delegate.enter_forall_without_assumptions(value, |value| { | |
| 1348 | 1348 | let u = self.delegate.universe(); |
| 1349 | 1349 | let assumptions = if self.cx().assumptions_on_binders() { |
| 1350 | 1350 | self.region_assumptions_for_placeholders_in_universe(value.clone(), u, param_env) |
compiler/rustc_parse/src/parser/diagnostics.rs+8-5| ... | ... | @@ -752,16 +752,19 @@ impl<'a> Parser<'a> { |
| 752 | 752 | Err(err) |
| 753 | 753 | } |
| 754 | 754 | |
| 755 | pub(super) fn is_expected_raw_ref_mut(&self) -> bool { | |
| 756 | self.prev_token.is_keyword(kw::Raw) | |
| 757 | && self.expected_token_types.contains(TokenType::KwMut) | |
| 758 | && self.expected_token_types.contains(TokenType::KwConst) | |
| 759 | && self.token.can_begin_expr() | |
| 760 | } | |
| 761 | ||
| 755 | 762 | /// Adds a label when `&raw EXPR` was written instead of `&raw const EXPR`/`&raw mut EXPR`. |
| 756 | 763 | /// |
| 757 | 764 | /// Given that not all parser diagnostics flow through `expected_one_of_not_found`, this |
| 758 | 765 | /// label may need added to other diagnostics emission paths as needed. |
| 759 | 766 | pub(super) fn label_expected_raw_ref(&mut self, err: &mut Diag<'_>) { |
| 760 | if self.prev_token.is_keyword(kw::Raw) | |
| 761 | && self.expected_token_types.contains(TokenType::KwMut) | |
| 762 | && self.expected_token_types.contains(TokenType::KwConst) | |
| 763 | && self.token.can_begin_expr() | |
| 764 | { | |
| 767 | if self.is_expected_raw_ref_mut() { | |
| 765 | 768 | err.span_suggestions( |
| 766 | 769 | self.prev_token.span.shrink_to_hi(), |
| 767 | 770 | "`&raw` must be followed by `const` or `mut` to be a raw reference expression", |
compiler/rustc_parse/src/parser/expr.rs+26-3| ... | ... | @@ -1279,15 +1279,38 @@ impl<'a> Parser<'a> { |
| 1279 | 1279 | }; |
| 1280 | 1280 | let open_paren = self.token.span; |
| 1281 | 1281 | |
| 1282 | let seq = self | |
| 1283 | .parse_expr_paren_seq() | |
| 1284 | .map(|args| self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args))); | |
| 1282 | let seq = match self.parse_expr_paren_seq() { | |
| 1283 | Ok(args) => Ok(self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args))), | |
| 1284 | Err(err) if self.is_expected_raw_ref_mut() => { | |
| 1285 | let guar = err.emit(); | |
| 1286 | // Preserve the call expression so later passes can still diagnose the callee, | |
| 1287 | // while treating the malformed `&raw <expr>` argument as an error expression. | |
| 1288 | let args = self.recover_raw_ref_call_args(guar); | |
| 1289 | return self.mk_expr(lo.to(self.prev_token.span), self.mk_call(fun, args)); | |
| 1290 | } | |
| 1291 | Err(err) => Err(err), | |
| 1292 | }; | |
| 1285 | 1293 | match self.maybe_recover_struct_lit_bad_delims(lo, open_paren, seq, snapshot) { |
| 1286 | 1294 | Ok(expr) => expr, |
| 1287 | 1295 | Err(err) => self.recover_seq_parse_error(exp!(OpenParen), exp!(CloseParen), lo, err), |
| 1288 | 1296 | } |
| 1289 | 1297 | } |
| 1290 | 1298 | |
| 1299 | fn recover_raw_ref_call_args(&mut self, guar: ErrorGuaranteed) -> ThinVec<Box<Expr>> { | |
| 1300 | let err_span = self.prev_token.span.to(self.token.span); | |
| 1301 | let mut args = thin_vec![self.mk_expr_err(err_span, guar)]; | |
| 1302 | while self.token != token::Eof && self.token != token::CloseParen { | |
| 1303 | if self.eat(exp!(Comma)) && self.token != token::Eof && self.token != token::CloseParen | |
| 1304 | { | |
| 1305 | args.push(self.mk_expr_err(self.prev_token.span.shrink_to_hi(), guar)); | |
| 1306 | } else { | |
| 1307 | self.parse_token_tree(); | |
| 1308 | } | |
| 1309 | } | |
| 1310 | let _ = self.eat(exp!(CloseParen)); | |
| 1311 | args | |
| 1312 | } | |
| 1313 | ||
| 1291 | 1314 | /// If we encounter a parser state that looks like the user has written a `struct` literal with |
| 1292 | 1315 | /// parentheses instead of braces, recover the parser state and provide suggestions. |
| 1293 | 1316 | #[instrument(skip(self, seq, snapshot), level = "trace")] |
compiler/rustc_parse/src/parser/mod.rs+7| ... | ... | @@ -917,6 +917,13 @@ impl<'a> Parser<'a> { |
| 917 | 917 | } |
| 918 | 918 | |
| 919 | 919 | // Attempt to keep parsing if it was an omitted separator. |
| 920 | // `&raw <expr>` already has a specific suggestion for missing | |
| 921 | // `const`/`mut`, so don't recover `<expr>` as the next element in | |
| 922 | // a comma-separated list. | |
| 923 | if exp.token_type == TokenType::Comma && self.is_expected_raw_ref_mut() | |
| 924 | { | |
| 925 | return Err(expect_err); | |
| 926 | } | |
| 920 | 927 | self.last_unexpected_token_span = None; |
| 921 | 928 | match f(self) { |
| 922 | 929 | Ok(t) => { |
compiler/rustc_session/src/config.rs+28-16| ... | ... | @@ -1384,9 +1384,21 @@ pub fn host_tuple() -> &'static str { |
| 1384 | 1384 | |
| 1385 | 1385 | fn file_path_mapping( |
| 1386 | 1386 | remap_path_prefix: Vec<(PathBuf, PathBuf)>, |
| 1387 | remap_cwd_prefix: Option<&Path>, | |
| 1387 | 1388 | remap_path_scope: RemapPathScopeComponents, |
| 1388 | 1389 | ) -> FilePathMapping { |
| 1389 | FilePathMapping::new(remap_path_prefix.clone(), remap_path_scope) | |
| 1390 | // Apply `-Zremap-cwd-prefix` here rather than in `parse_remap_path_prefix`, so the | |
| 1391 | // absolute cwd is never stored in the tracked `remap_path_prefix` option (#132132). | |
| 1392 | let cwd_remap = if let Some(to) = remap_cwd_prefix | |
| 1393 | && let Ok(cwd) = std::env::current_dir() | |
| 1394 | { | |
| 1395 | Some((cwd, to.to_path_buf())) | |
| 1396 | } else { | |
| 1397 | None | |
| 1398 | }; | |
| 1399 | // The cwd remapping is appended last: `map_prefix` tries entries in reverse order, so this | |
| 1400 | // keeps `-Zremap-cwd-prefix` taking precedence over `--remap-path-prefix`, as documented. | |
| 1401 | FilePathMapping::new(remap_path_prefix.into_iter().chain(cwd_remap).collect(), remap_path_scope) | |
| 1390 | 1402 | } |
| 1391 | 1403 | |
| 1392 | 1404 | impl Default for Options { |
| ... | ... | @@ -1398,7 +1410,8 @@ impl Default for Options { |
| 1398 | 1410 | // to create a default working directory. |
| 1399 | 1411 | let working_dir = { |
| 1400 | 1412 | let working_dir = std::env::current_dir().unwrap(); |
| 1401 | let file_mapping = file_path_mapping(Vec::new(), RemapPathScopeComponents::empty()); | |
| 1413 | let file_mapping = | |
| 1414 | file_path_mapping(Vec::new(), None, RemapPathScopeComponents::empty()); | |
| 1402 | 1415 | file_mapping.to_real_filename(&RealFileName::empty(), &working_dir) |
| 1403 | 1416 | }; |
| 1404 | 1417 | |
| ... | ... | @@ -1459,7 +1472,11 @@ impl Options { |
| 1459 | 1472 | } |
| 1460 | 1473 | |
| 1461 | 1474 | pub fn file_path_mapping(&self) -> FilePathMapping { |
| 1462 | file_path_mapping(self.remap_path_prefix.clone(), self.remap_path_scope) | |
| 1475 | file_path_mapping( | |
| 1476 | self.remap_path_prefix.clone(), | |
| 1477 | self.unstable_opts.remap_cwd_prefix.as_deref(), | |
| 1478 | self.remap_path_scope, | |
| 1479 | ) | |
| 1463 | 1480 | } |
| 1464 | 1481 | |
| 1465 | 1482 | /// Returns `true` if there will be an output file generated. |
| ... | ... | @@ -2393,9 +2410,8 @@ pub fn parse_externs( |
| 2393 | 2410 | fn parse_remap_path_prefix( |
| 2394 | 2411 | early_dcx: &EarlyDiagCtxt, |
| 2395 | 2412 | matches: &getopts::Matches, |
| 2396 | unstable_opts: &UnstableOptions, | |
| 2397 | 2413 | ) -> Vec<(PathBuf, PathBuf)> { |
| 2398 | let mut mapping: Vec<(PathBuf, PathBuf)> = matches | |
| 2414 | matches | |
| 2399 | 2415 | .opt_strs("remap-path-prefix") |
| 2400 | 2416 | .into_iter() |
| 2401 | 2417 | .map(|remap| match remap.rsplit_once('=') { |
| ... | ... | @@ -2404,15 +2420,7 @@ fn parse_remap_path_prefix( |
| 2404 | 2420 | } |
| 2405 | 2421 | Some((from, to)) => (PathBuf::from(from), PathBuf::from(to)), |
| 2406 | 2422 | }) |
| 2407 | .collect(); | |
| 2408 | match &unstable_opts.remap_cwd_prefix { | |
| 2409 | Some(to) => match std::env::current_dir() { | |
| 2410 | Ok(cwd) => mapping.push((cwd, to.clone())), | |
| 2411 | Err(_) => (), | |
| 2412 | }, | |
| 2413 | None => (), | |
| 2414 | }; | |
| 2415 | mapping | |
| 2423 | .collect() | |
| 2416 | 2424 | } |
| 2417 | 2425 | |
| 2418 | 2426 | fn parse_logical_env( |
| ... | ... | @@ -2678,7 +2686,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M |
| 2678 | 2686 | |
| 2679 | 2687 | let externs = parse_externs(early_dcx, matches, &unstable_opts); |
| 2680 | 2688 | |
| 2681 | let remap_path_prefix = parse_remap_path_prefix(early_dcx, matches, &unstable_opts); | |
| 2689 | let remap_path_prefix = parse_remap_path_prefix(early_dcx, matches); | |
| 2682 | 2690 | let remap_path_scope = parse_remap_path_scope(early_dcx, matches, &unstable_opts); |
| 2683 | 2691 | |
| 2684 | 2692 | let pretty = parse_pretty(early_dcx, &unstable_opts); |
| ... | ... | @@ -2746,7 +2754,11 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M |
| 2746 | 2754 | early_dcx.early_fatal(format!("Current directory is invalid: {e}")); |
| 2747 | 2755 | }); |
| 2748 | 2756 | |
| 2749 | let file_mapping = file_path_mapping(remap_path_prefix.clone(), remap_path_scope); | |
| 2757 | let file_mapping = file_path_mapping( | |
| 2758 | remap_path_prefix.clone(), | |
| 2759 | unstable_opts.remap_cwd_prefix.as_deref(), | |
| 2760 | remap_path_scope, | |
| 2761 | ); | |
| 2750 | 2762 | file_mapping.to_real_filename(&RealFileName::empty(), &working_dir) |
| 2751 | 2763 | }; |
| 2752 | 2764 |
compiler/rustc_target/src/spec/mod.rs+1| ... | ... | @@ -1462,6 +1462,7 @@ supported_targets! { |
| 1462 | 1462 | ("powerpc-unknown-linux-muslspe", powerpc_unknown_linux_muslspe), |
| 1463 | 1463 | ("powerpc64-ibm-aix", powerpc64_ibm_aix), |
| 1464 | 1464 | ("powerpc64-unknown-linux-gnu", powerpc64_unknown_linux_gnu), |
| 1465 | ("powerpc64-unknown-linux-gnuelfv2", powerpc64_unknown_linux_gnuelfv2), | |
| 1465 | 1466 | ("powerpc64-unknown-linux-musl", powerpc64_unknown_linux_musl), |
| 1466 | 1467 | ("powerpc64le-unknown-linux-gnu", powerpc64le_unknown_linux_gnu), |
| 1467 | 1468 | ("powerpc64le-unknown-linux-musl", powerpc64le_unknown_linux_musl), |
compiler/rustc_target/src/spec/targets/aarch64_unknown_freebsd.rs+1-1| ... | ... | @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { |
| 7 | 7 | llvm_target: "aarch64-unknown-freebsd".into(), |
| 8 | 8 | metadata: TargetMetadata { |
| 9 | 9 | description: Some("ARM64 FreeBSD".into()), |
| 10 | tier: Some(3), | |
| 10 | tier: Some(2), | |
| 11 | 11 | host_tools: Some(true), |
| 12 | 12 | std: Some(true), |
| 13 | 13 | }, |
compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnuelfv2.rs created+30| ... | ... | @@ -0,0 +1,30 @@ |
| 1 | use rustc_abi::Endian; | |
| 2 | ||
| 3 | use crate::spec::{ | |
| 4 | Arch, Cc, CfgAbi, LinkerFlavor, Lld, LlvmAbi, StackProbeType, Target, TargetMetadata, | |
| 5 | TargetOptions, base, | |
| 6 | }; | |
| 7 | ||
| 8 | pub(crate) fn target() -> Target { | |
| 9 | let mut base = base::linux_gnu::opts(); | |
| 10 | base.cpu = "ppc64".into(); | |
| 11 | base.add_pre_link_args(LinkerFlavor::Gnu(Cc::Yes, Lld::No), &["-m64"]); | |
| 12 | base.max_atomic_width = Some(64); | |
| 13 | base.stack_probes = StackProbeType::Inline; | |
| 14 | base.cfg_abi = CfgAbi::ElfV2; | |
| 15 | base.llvm_abiname = LlvmAbi::ElfV2; | |
| 16 | ||
| 17 | Target { | |
| 18 | llvm_target: "powerpc64-unknown-linux-gnu".into(), | |
| 19 | metadata: TargetMetadata { | |
| 20 | description: Some("PPC64 Linux (ELFv2 ABI, kernel 3.2, glibc 2.17)".into()), | |
| 21 | tier: Some(3), | |
| 22 | host_tools: Some(false), | |
| 23 | std: Some(true), | |
| 24 | }, | |
| 25 | pointer_width: 64, | |
| 26 | data_layout: "E-m:e-Fn32-i64:64-i128:128-n32:64-S128-v256:256:256-v512:512:512".into(), | |
| 27 | arch: Arch::PowerPC64, | |
| 28 | options: TargetOptions { endian: Endian::Big, mcount: "_mcount".into(), ..base }, | |
| 29 | } | |
| 30 | } |
compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs+1-1| ... | ... | @@ -10,7 +10,7 @@ pub(crate) fn target() -> Target { |
| 10 | 10 | base.llvm_abiname = LlvmAbi::Lp64d; |
| 11 | 11 | base.max_atomic_width = Some(64); |
| 12 | 12 | base.stack_probes = StackProbeType::Inline; |
| 13 | base.supported_sanitizers = SanitizerSet::SHADOWCALLSTACK; | |
| 13 | base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::SHADOWCALLSTACK; | |
| 14 | 14 | base.default_sanitizers = SanitizerSet::SHADOWCALLSTACK; |
| 15 | 15 | base.supports_xray = true; |
| 16 | 16 |
compiler/rustc_type_ir/src/infer_ctxt.rs+10-1| ... | ... | @@ -406,7 +406,16 @@ pub trait InferCtxtLike: Sized { |
| 406 | 406 | value: ty::Binder<Self::Interner, T>, |
| 407 | 407 | ) -> T; |
| 408 | 408 | |
| 409 | fn enter_forall<T: TypeFoldable<Self::Interner>, U>( | |
| 409 | fn enter_forall_without_assumptions<T: TypeFoldable<Self::Interner>, U>( | |
| 410 | &self, | |
| 411 | value: ty::Binder<Self::Interner, T>, | |
| 412 | f: impl FnOnce(T) -> U, | |
| 413 | ) -> U; | |
| 414 | ||
| 415 | /// FIXME(-Zassumptions-on-binders): Any usage of this method is likely wrong | |
| 416 | /// and should be replaced in the long term by actually taking assumptions into | |
| 417 | /// account. | |
| 418 | fn enter_forall_with_empty_assumptions<T: TypeFoldable<Self::Interner>, U>( | |
| 410 | 419 | &self, |
| 411 | 420 | value: ty::Binder<Self::Interner, T>, |
| 412 | 421 | f: impl FnOnce(T) -> U, |
compiler/rustc_type_ir/src/region_constraint.rs+3-7| ... | ... | @@ -1096,11 +1096,7 @@ fn alias_outlives_candidates_from_assumptions<Infcx: InferCtxtLike<Interner = I> |
| 1096 | 1096 | |
| 1097 | 1097 | let prev_universe = infcx.universe(); |
| 1098 | 1098 | |
| 1099 | // FIXME(-Zassumptions-on-binders): Handle the assumptions on this binder | |
| 1100 | infcx.enter_forall(bound_outlives, |(alias, r)| { | |
| 1101 | let u = infcx.universe(); | |
| 1102 | infcx.insert_placeholder_assumptions(u, Some(Assumptions::empty())); | |
| 1103 | ||
| 1099 | infcx.enter_forall_with_empty_assumptions(bound_outlives, |(alias, r)| { | |
| 1104 | 1100 | for bound_type_outlives in assumptions.type_outlives.iter() { |
| 1105 | 1101 | let OutlivesPredicate(alias2, r2) = |
| 1106 | 1102 | infcx.instantiate_binder_with_infer(*bound_type_outlives); |
| ... | ... | @@ -1187,14 +1183,14 @@ impl<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeRelation<I> |
| 1187 | 1183 | where |
| 1188 | 1184 | T: Relate<I>, |
| 1189 | 1185 | { |
| 1190 | self.infcx.enter_forall(a, |a| { | |
| 1186 | self.infcx.enter_forall_with_empty_assumptions(a, |a| { | |
| 1191 | 1187 | let u = self.infcx.universe(); |
| 1192 | 1188 | self.infcx.insert_placeholder_assumptions(u, Some(Assumptions::empty())); |
| 1193 | 1189 | let b = self.infcx.instantiate_binder_with_infer(b); |
| 1194 | 1190 | self.relate(a, b) |
| 1195 | 1191 | })?; |
| 1196 | 1192 | |
| 1197 | self.infcx.enter_forall(b, |b| { | |
| 1193 | self.infcx.enter_forall_with_empty_assumptions(b, |b| { | |
| 1198 | 1194 | let u = self.infcx.universe(); |
| 1199 | 1195 | self.infcx.insert_placeholder_assumptions(u, Some(Assumptions::empty())); |
| 1200 | 1196 | let a = self.infcx.instantiate_binder_with_infer(a); |
compiler/rustc_type_ir/src/relate/solver_relating.rs+4-4| ... | ... | @@ -311,13 +311,13 @@ where |
| 311 | 311 | // |
| 312 | 312 | // [rd]: https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/placeholders_and_universes.html |
| 313 | 313 | ty::Covariant => { |
| 314 | self.infcx.enter_forall(b, |b| { | |
| 314 | self.infcx.enter_forall_with_empty_assumptions(b, |b| { | |
| 315 | 315 | let a = self.infcx.instantiate_binder_with_infer(a); |
| 316 | 316 | self.relate(a, b) |
| 317 | 317 | })?; |
| 318 | 318 | } |
| 319 | 319 | ty::Contravariant => { |
| 320 | self.infcx.enter_forall(a, |a| { | |
| 320 | self.infcx.enter_forall_with_empty_assumptions(a, |a| { | |
| 321 | 321 | let b = self.infcx.instantiate_binder_with_infer(b); |
| 322 | 322 | self.relate(a, b) |
| 323 | 323 | })?; |
| ... | ... | @@ -334,13 +334,13 @@ where |
| 334 | 334 | // `exists<..> A == for<..> B` and `exists<..> B == for<..> A`. |
| 335 | 335 | // Check if `exists<..> A == for<..> B` |
| 336 | 336 | ty::Invariant => { |
| 337 | self.infcx.enter_forall(b, |b| { | |
| 337 | self.infcx.enter_forall_with_empty_assumptions(b, |b| { | |
| 338 | 338 | let a = self.infcx.instantiate_binder_with_infer(a); |
| 339 | 339 | self.relate(a, b) |
| 340 | 340 | })?; |
| 341 | 341 | |
| 342 | 342 | // Check if `exists<..> B == for<..> A`. |
| 343 | self.infcx.enter_forall(a, |a| { | |
| 343 | self.infcx.enter_forall_with_empty_assumptions(a, |a| { | |
| 344 | 344 | let b = self.infcx.instantiate_binder_with_infer(b); |
| 345 | 345 | self.relate(a, b) |
| 346 | 346 | })?; |
compiler/rustc_type_ir_macros/Cargo.toml+1| ... | ... | @@ -11,6 +11,7 @@ nightly = [] |
| 11 | 11 | |
| 12 | 12 | [dependencies] |
| 13 | 13 | # tidy-alphabetical-start |
| 14 | indexmap = "2.4.0" | |
| 14 | 15 | proc-macro2 = "1" |
| 15 | 16 | quote = "1" |
| 16 | 17 | syn = { version = "2.0.9", features = ["full", "visit-mut"] } |
compiler/rustc_type_ir_macros/src/lib.rs+91-20| ... | ... | @@ -1,3 +1,4 @@ |
| 1 | use indexmap::IndexSet; | |
| 1 | 2 | use quote::{ToTokens, quote}; |
| 2 | 3 | use syn::visit_mut::VisitMut; |
| 3 | 4 | use syn::{Attribute, parse_quote}; |
| ... | ... | @@ -17,11 +18,24 @@ decl_derive!( |
| 17 | 18 | [GenericTypeVisitable] => customizable_type_visitable_derive |
| 18 | 19 | ); |
| 19 | 20 | |
| 20 | struct LiftedTy { | |
| 21 | struct TransformedTy { | |
| 21 | 22 | ty: syn::Type, |
| 22 | generic_parameter_bounds: Vec<syn::Ident>, | |
| 23 | generic_parameter_bounds: IndexSet<syn::Ident>, | |
| 23 | 24 | } |
| 24 | 25 | |
| 26 | enum TypeParameterPath { | |
| 27 | Interner, | |
| 28 | GenericParameter(syn::Ident), | |
| 29 | } | |
| 30 | ||
| 31 | enum TypeParameterTransform { | |
| 32 | Continue, | |
| 33 | Stop, | |
| 34 | } | |
| 35 | ||
| 36 | type TypeParameterVisitor = | |
| 37 | fn(TypeParameterPath, &mut syn::TypePath, &mut IndexSet<syn::Ident>) -> TypeParameterTransform; | |
| 38 | ||
| 25 | 39 | fn has_ignore_attr(attrs: &[Attribute], name: &'static str, meta: &'static str) -> bool { |
| 26 | 40 | let mut ignored = false; |
| 27 | 41 | attrs.iter().for_each(|attr| { |
| ... | ... | @@ -91,6 +105,9 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke |
| 91 | 105 | |
| 92 | 106 | s.add_where_predicate(parse_quote! { I: Interner }); |
| 93 | 107 | s.add_bounds(synstructure::AddBounds::Fields); |
| 108 | let generic_parameters = | |
| 109 | s.ast().generics.type_params().map(|ty| ty.ident.clone()).collect::<Vec<_>>(); | |
| 110 | let mut generic_parameter_bounds = IndexSet::new(); | |
| 94 | 111 | s.bind_with(|_| synstructure::BindStyle::Move); |
| 95 | 112 | let body_try_fold = s.each_variant(|vi| { |
| 96 | 113 | let bindings = vi.bindings(); |
| ... | ... | @@ -101,6 +118,12 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke |
| 101 | 118 | if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") { |
| 102 | 119 | bind.to_token_stream() |
| 103 | 120 | } else { |
| 121 | for param in | |
| 122 | type_foldable_generic_parameters(bind.ast().ty.clone(), &generic_parameters) | |
| 123 | { | |
| 124 | generic_parameter_bounds.insert(param); | |
| 125 | } | |
| 126 | ||
| 104 | 127 | quote! { |
| 105 | 128 | ::rustc_type_ir::TypeFoldable::try_fold_with(#bind, __folder)? |
| 106 | 129 | } |
| ... | ... | @@ -129,6 +152,9 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke |
| 129 | 152 | // to generate code for them. |
| 130 | 153 | s.filter(|bi| !has_ignore_attr(&bi.ast().attrs, "type_foldable", "identity")); |
| 131 | 154 | s.add_bounds(synstructure::AddBounds::Fields); |
| 155 | for param in generic_parameter_bounds { | |
| 156 | s.add_where_predicate(parse_quote! { #param: ::rustc_type_ir::TypeFoldable<I> }); | |
| 157 | } | |
| 132 | 158 | s.bound_impl( |
| 133 | 159 | quote!(::rustc_type_ir::TypeFoldable<I>), |
| 134 | 160 | quote! { |
| ... | ... | @@ -149,6 +175,19 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke |
| 149 | 175 | ) |
| 150 | 176 | } |
| 151 | 177 | |
| 178 | fn type_foldable_generic_parameters( | |
| 179 | ty: syn::Type, | |
| 180 | generic_parameters: &[syn::Ident], | |
| 181 | ) -> IndexSet<syn::Ident> { | |
| 182 | transform_type_parameters(ty, generic_parameters, |path, _, generic_parameter_bounds| { | |
| 183 | if let TypeParameterPath::GenericParameter(param) = path { | |
| 184 | generic_parameter_bounds.insert(param); | |
| 185 | } | |
| 186 | TypeParameterTransform::Continue | |
| 187 | }) | |
| 188 | .generic_parameter_bounds | |
| 189 | } | |
| 190 | ||
| 152 | 191 | /// `Lift_Generic` is specialised for structs/enums parameterised by an interner |
| 153 | 192 | /// `I: Interner`. It derives `Lift<J>` by rewriting interner associated types |
| 154 | 193 | /// from `I::Assoc` to `J::Assoc`. The required associated type lift bounds are |
| ... | ... | @@ -251,40 +290,72 @@ fn is_type_phantom(ty: &syn::Type) -> bool { |
| 251 | 290 | get_first_path_segment(ty).is_some_and(|segment| segment.ident == "PhantomData") |
| 252 | 291 | } |
| 253 | 292 | |
| 254 | fn lift(mut ty: syn::Type, generic_parameters: &[syn::Ident]) -> LiftedTy { | |
| 255 | struct ItoJ<'a> { | |
| 293 | fn lift(ty: syn::Type, generic_parameters: &[syn::Ident]) -> TransformedTy { | |
| 294 | transform_type_parameters(ty, generic_parameters, |path, ty, generic_parameter_bounds| { | |
| 295 | match path { | |
| 296 | TypeParameterPath::Interner => { | |
| 297 | *ty.path.segments.first_mut().unwrap() = parse_quote! { J }; | |
| 298 | TypeParameterTransform::Continue | |
| 299 | } | |
| 300 | TypeParameterPath::GenericParameter(param) => { | |
| 301 | generic_parameter_bounds.insert(param.clone()); | |
| 302 | *ty = parse_quote! { <#param as ::rustc_type_ir::lift::Lift<J>>::Lifted }; | |
| 303 | TypeParameterTransform::Stop | |
| 304 | } | |
| 305 | } | |
| 306 | }) | |
| 307 | } | |
| 308 | ||
| 309 | fn transform_type_parameters( | |
| 310 | mut ty: syn::Type, | |
| 311 | generic_parameters: &[syn::Ident], | |
| 312 | visit: TypeParameterVisitor, | |
| 313 | ) -> TransformedTy { | |
| 314 | struct TypeParameterTransformer<'a> { | |
| 256 | 315 | generic_parameters: &'a [syn::Ident], |
| 257 | generic_parameter_bounds: Vec<syn::Ident>, | |
| 316 | generic_parameter_bounds: IndexSet<syn::Ident>, | |
| 317 | visit: TypeParameterVisitor, | |
| 258 | 318 | } |
| 259 | 319 | |
| 260 | impl VisitMut for ItoJ<'_> { | |
| 320 | impl VisitMut for TypeParameterTransformer<'_> { | |
| 261 | 321 | fn visit_type_path_mut(&mut self, i: &mut syn::TypePath) { |
| 262 | if i.qself.is_none() { | |
| 322 | let path = if i.qself.is_none() { | |
| 263 | 323 | let segments_len = i.path.segments.len(); |
| 264 | if let Some(first) = i.path.segments.first_mut() { | |
| 265 | // Turn paths from `I` into `J` | |
| 324 | i.path.segments.first().and_then(|first| { | |
| 266 | 325 | if first.ident == "I" { |
| 267 | *first = parse_quote! { J }; | |
| 326 | Some(TypeParameterPath::Interner) | |
| 268 | 327 | } else if segments_len == 1 |
| 269 | 328 | && matches!(first.arguments, syn::PathArguments::None) |
| 270 | && self.generic_parameters.iter().any(|param| first.ident == *param) | |
| 329 | && self.generic_parameters.contains(&first.ident) | |
| 271 | 330 | { |
| 272 | let ident = first.ident.clone(); | |
| 273 | if !self.generic_parameter_bounds.iter().any(|param| *param == ident) { | |
| 274 | self.generic_parameter_bounds.push(ident.clone()); | |
| 275 | } | |
| 276 | ||
| 277 | *i = parse_quote! { <#ident as ::rustc_type_ir::lift::Lift<J>>::Lifted }; | |
| 278 | return; | |
| 331 | Some(TypeParameterPath::GenericParameter(first.ident.clone())) | |
| 332 | } else { | |
| 333 | None | |
| 279 | 334 | } |
| 335 | }) | |
| 336 | } else { | |
| 337 | None | |
| 338 | }; | |
| 339 | ||
| 340 | if let Some(path) = path { | |
| 341 | if let TypeParameterTransform::Stop = | |
| 342 | (self.visit)(path, i, &mut self.generic_parameter_bounds) | |
| 343 | { | |
| 344 | return; | |
| 280 | 345 | } |
| 281 | 346 | } |
| 347 | ||
| 282 | 348 | syn::visit_mut::visit_type_path_mut(self, i); |
| 283 | 349 | } |
| 284 | 350 | } |
| 285 | let mut visitor = ItoJ { generic_parameters, generic_parameter_bounds: Vec::new() }; | |
| 351 | ||
| 352 | let mut visitor = TypeParameterTransformer { | |
| 353 | generic_parameters, | |
| 354 | generic_parameter_bounds: IndexSet::new(), | |
| 355 | visit, | |
| 356 | }; | |
| 286 | 357 | visitor.visit_type_mut(&mut ty); |
| 287 | LiftedTy { ty, generic_parameter_bounds: visitor.generic_parameter_bounds } | |
| 358 | TransformedTy { ty, generic_parameter_bounds: visitor.generic_parameter_bounds } | |
| 288 | 359 | } |
| 289 | 360 | |
| 290 | 361 | #[cfg(not(feature = "nightly"))] |
library/core/src/clone.rs+30-13| ... | ... | @@ -293,21 +293,36 @@ pub macro Clone($item:item) { |
| 293 | 293 | /// A trait for types whose [`Clone`] operation creates another alias to the same |
| 294 | 294 | /// logical resource or shared state. |
| 295 | 295 | /// |
| 296 | /// `Share` marks types where cloning creates another handle, reference, or alias | |
| 297 | /// to the same logical resource or shared state, rather than an independent owned | |
| 298 | /// value. The distinction is semantic, not cost-based: implementing `Share` does | |
| 299 | /// not merely mean that cloning is cheap, constant-time, allocation-free, or | |
| 300 | /// convenient. | |
| 296 | /// `Share` refines the meaning of [`Clone`] for types where cloning a value | |
| 297 | /// creates another handle, reference, or alias to the same logical resource or | |
| 298 | /// shared state, rather than an independent owned value. The distinction is | |
| 299 | /// semantic, not operational: `Share` does not mean merely that cloning is | |
| 300 | /// cheap, constant-time, allocation-free, or convenient. | |
| 301 | 301 | /// |
| 302 | /// Calling [`share`](Share::share) is equivalent to calling [`clone`](Clone::clone) | |
| 303 | /// for implementors, but communicates that the resulting value aliases the same | |
| 304 | /// underlying resource. | |
| 302 | /// `Share` is a third way to think about creating another usable value: | |
| 303 | /// | |
| 304 | /// * [`Copy`] may duplicate a value implicitly. | |
| 305 | /// * [`Clone`] explicitly creates another value. | |
| 306 | /// * `Share` explicitly creates another value that aliases the same underlying | |
| 307 | /// logical resource or shared state. | |
| 308 | /// | |
| 309 | /// `Share` is not a replacement for either [`Copy`] or [`Clone`], and neither | |
| 310 | /// trait implies it. For example, integers are [`Copy`] but not `Share`, because | |
| 311 | /// copying an integer creates an independent value. Likewise, not every cheap | |
| 312 | /// [`Clone`] implementation is `Share`. | |
| 305 | 313 | /// |
| 306 | 314 | /// Shared references, `Rc<T>`, `Arc<T>`, `Sender<T>`, and `SyncSender<T>` are |
| 307 | 315 | /// examples of types that can be shared this way. Types such as `Vec<T>`, |
| 308 | /// `String`, and `Box<T>` are not `Share` even though they implement `Clone`, | |
| 309 | /// because cloning them creates another owned value rather than another handle | |
| 310 | /// to the same logical resource. | |
| 316 | /// `String`, `Box<T>`, owned collections, and similar owned values are not | |
| 317 | /// `Share`, even though they implement [`Clone`], because cloning them creates | |
| 318 | /// independent owned storage or value ownership. Mutable references (`&mut T`) | |
| 319 | /// are neither `Clone` nor `Share`, because you cannot have two active at once. | |
| 320 | /// | |
| 321 | /// Calling [`share`](Share::share) is equivalent to calling [`clone`](Clone::clone) | |
| 322 | /// for implementors, but communicates that the resulting value aliases the same | |
| 323 | /// underlying resource. The `share` method is final, so implementors should | |
| 324 | /// define the operation through [`Clone::clone`] and implement `Share` only when | |
| 325 | /// those cloning semantics are clone-as-alias semantics. | |
| 311 | 326 | /// |
| 312 | 327 | /// # Examples |
| 313 | 328 | /// |
| ... | ... | @@ -367,9 +382,11 @@ pub macro Clone($item:item) { |
| 367 | 382 | pub trait Share: Clone { |
| 368 | 383 | /// Creates another alias to the same underlying resource or shared state. |
| 369 | 384 | /// |
| 370 | /// This is equivalent to calling [`Clone::clone`]. | |
| 385 | /// This is equivalent to calling [`Clone::clone`]. Use `share` at call | |
| 386 | /// sites to make aliasing intent explicit; implementors define this | |
| 387 | /// operation through [`Clone::clone`], not by overriding this method. | |
| 371 | 388 | #[unstable(feature = "share_trait", issue = "156756")] |
| 372 | fn share(&self) -> Self { | |
| 389 | final fn share(&self) -> Self { | |
| 373 | 390 | Clone::clone(self) |
| 374 | 391 | } |
| 375 | 392 | } |
library/core/src/lib.rs+1| ... | ... | @@ -138,6 +138,7 @@ |
| 138 | 138 | #![feature(f16)] |
| 139 | 139 | #![feature(f128)] |
| 140 | 140 | #![feature(field_projections)] |
| 141 | #![feature(final_associated_functions)] | |
| 141 | 142 | #![feature(freeze_impls)] |
| 142 | 143 | #![feature(fundamental)] |
| 143 | 144 | #![feature(funnel_shifts)] |
library/core/src/slice/mod.rs+2| ... | ... | @@ -596,6 +596,7 @@ impl<T> [T] { |
| 596 | 596 | #[inline] |
| 597 | 597 | #[must_use] |
| 598 | 598 | #[rustc_const_unstable(feature = "const_index", issue = "143775")] |
| 599 | #[rustc_no_writable] | |
| 599 | 600 | pub const fn get_mut<I>(&mut self, index: I) -> Option<&mut I::Output> |
| 600 | 601 | where |
| 601 | 602 | I: [const] SliceIndex<Self>, |
| ... | ... | @@ -681,6 +682,7 @@ impl<T> [T] { |
| 681 | 682 | #[must_use] |
| 682 | 683 | #[track_caller] |
| 683 | 684 | #[rustc_const_unstable(feature = "const_index", issue = "143775")] |
| 685 | #[rustc_no_writable] | |
| 684 | 686 | pub const unsafe fn get_unchecked_mut<I>(&mut self, index: I) -> &mut I::Output |
| 685 | 687 | where |
| 686 | 688 | I: [const] SliceIndex<Self>, |
rust-bors.toml+9| ... | ... | @@ -26,6 +26,15 @@ labels_blocking_approval = [ |
| 26 | 26 | "S-waiting-on-t-clippy", |
| 27 | 27 | # PR manually set to blocked |
| 28 | 28 | "S-blocked", |
| 29 | # Needs a Crater run before being approved | |
| 30 | "needs-crater", | |
| 31 | # Needs a formal decision to be made | |
| 32 | "needs-rfc", | |
| 33 | "needs-fcp", | |
| 34 | "needs-acp", | |
| 35 | "needs-mcp", | |
| 36 | # A PR in the Rust reference must be done first | |
| 37 | "needs-reference-pr" | |
| 29 | 38 | ] |
| 30 | 39 | |
| 31 | 40 | # If CI runs quicker than this duration, consider it to be a failure |
src/bootstrap/src/core/sanity.rs+1| ... | ... | @@ -37,6 +37,7 @@ pub struct Finder { |
| 37 | 37 | /// when the newly-bumped stage 0 compiler now knows about the formerly-missing targets. |
| 38 | 38 | const STAGE0_MISSING_TARGETS: &[&str] = &[ |
| 39 | 39 | // just a dummy comment so the list doesn't get onelined |
| 40 | "powerpc64-unknown-linux-gnuelfv2", | |
| 40 | 41 | ]; |
| 41 | 42 | |
| 42 | 43 | /// Minimum version threshold for libstdc++ required when using prebuilt LLVM |
src/ci/docker/host-x86_64/x86_64-gnu-pre-stabilization/Dockerfile created+29| ... | ... | @@ -0,0 +1,29 @@ |
| 1 | FROM ubuntu:22.04 | |
| 2 | ||
| 3 | ARG DEBIAN_FRONTEND=noninteractive | |
| 4 | RUN apt-get update && apt-get install -y --no-install-recommends \ | |
| 5 | g++ \ | |
| 6 | make \ | |
| 7 | ninja-build \ | |
| 8 | file \ | |
| 9 | curl \ | |
| 10 | ca-certificates \ | |
| 11 | python3 \ | |
| 12 | git \ | |
| 13 | cmake \ | |
| 14 | sudo \ | |
| 15 | gdb \ | |
| 16 | libssl-dev \ | |
| 17 | pkg-config \ | |
| 18 | xz-utils \ | |
| 19 | mingw-w64 \ | |
| 20 | zlib1g-dev \ | |
| 21 | && rm -rf /var/lib/apt/lists/* | |
| 22 | ||
| 23 | COPY scripts/sccache.sh /scripts/ | |
| 24 | RUN sh /scripts/sccache.sh | |
| 25 | ||
| 26 | ENV RUST_CONFIGURE_ARGS="--build=x86_64-unknown-linux-gnu" | |
| 27 | ||
| 28 | COPY scripts/x86_64-gnu-pre-stabilization.sh /scripts/ | |
| 29 | ENV SCRIPT="/scripts/x86_64-gnu-pre-stabilization.sh" |
src/ci/docker/scripts/x86_64-gnu-llvm.sh-5| ... | ... | @@ -15,8 +15,3 @@ set -ex |
| 15 | 15 | |
| 16 | 16 | # Run the UI test suite in `--pass=check` mode, to ensure it continues to work. |
| 17 | 17 | ../x.ps1 --stage 2 test tests/ui --pass=check --host='' --target=i686-unknown-linux-gnu |
| 18 | ||
| 19 | # Rebuild the stdlib using the new trait solver, to ensure it doesn't regress | |
| 20 | # until stabilization. | |
| 21 | RUSTFLAGS_NOT_BOOTSTRAP="-Znext-solver=globally" ../x --stage 1 build library \ | |
| 22 | --host='' --target=i686-unknown-linux-gnu |
src/ci/docker/scripts/x86_64-gnu-pre-stabilization.sh created+23| ... | ... | @@ -0,0 +1,23 @@ |
| 1 | #!/bin/bash | |
| 2 | ||
| 3 | set -ex | |
| 4 | ||
| 5 | # This script tests features intended to be stabilized in 2026. We want to | |
| 6 | # ensure they don't regress until then. | |
| 7 | ||
| 8 | # 1. For the new trait solver, we want to: | |
| 9 | # - ensure it can build the standard library | |
| 10 | # | |
| 11 | # FIXME: we also need to ensure it actually bootstraps. | |
| 12 | ||
| 13 | RUSTFLAGS_NOT_BOOTSTRAP="-Znext-solver=globally" ../x build library --stage 1 | |
| 14 | ||
| 15 | # 2. For the polonius alpha, we run the UI tests under the polonius | |
| 16 | # compare-mode. | |
| 17 | # | |
| 18 | # Note that we keep the same rustflags to avoid needing to rebuild any stage 1 | |
| 19 | # artifacts from the previous command. It also tests both features at the same | |
| 20 | # time. | |
| 21 | ||
| 22 | RUSTFLAGS_NOT_BOOTSTRAP="-Znext-solver=globally" ../x test tests/ui \ | |
| 23 | --compare-mode polonius --stage 1 |
src/ci/github-actions/jobs.yml+39-5| ... | ... | @@ -22,10 +22,14 @@ runners: |
| 22 | 22 | os: ubuntu-24.04-16core-64gb |
| 23 | 23 | <<: *base-job |
| 24 | 24 | |
| 25 | - &job-macos | |
| 25 | - &job-macos-15 | |
| 26 | 26 | os: macos-15 # macOS 15 Arm64 |
| 27 | 27 | <<: *base-job |
| 28 | 28 | |
| 29 | - &job-macos-26 | |
| 30 | os: macos-26 # macOS 26 Arm64 | |
| 31 | <<: *base-job | |
| 32 | ||
| 29 | 33 | - &job-windows |
| 30 | 34 | os: windows-2025 |
| 31 | 35 | <<: *base-job |
| ... | ... | @@ -152,6 +156,14 @@ pr: |
| 152 | 156 | env: |
| 153 | 157 | CODEGEN_BACKENDS: gcc |
| 154 | 158 | <<: *job-linux-4c |
| 159 | ||
| 160 | # This job tests features we want to stabilize soon, to ensure they don't | |
| 161 | # regress. | |
| 162 | - name: x86_64-gnu-pre-stabilization | |
| 163 | doc_url: https://rustc-dev-guide.rust-lang.org/tests/pre-stabilization-ci-job.html | |
| 164 | env: | |
| 165 | CODEGEN_BACKENDS: llvm | |
| 166 | <<: *job-linux-4c | |
| 155 | 167 | |
| 156 | 168 | # Jobs that run when you perform a try build (@bors try) |
| 157 | 169 | # These jobs automatically inherit envs.try, to avoid repeating |
| ... | ... | @@ -476,7 +488,7 @@ auto: |
| 476 | 488 | DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer |
| 477 | 489 | DIST_REQUIRE_ALL_TOOLS: 1 |
| 478 | 490 | CODEGEN_BACKENDS: llvm,cranelift |
| 479 | <<: *job-macos | |
| 491 | <<: *job-macos-15 | |
| 480 | 492 | |
| 481 | 493 | - name: dist-apple-various |
| 482 | 494 | env: |
| ... | ... | @@ -513,7 +525,7 @@ auto: |
| 513 | 525 | MACOSX_DEPLOYMENT_TARGET: 10.12 |
| 514 | 526 | MACOSX_STD_DEPLOYMENT_TARGET: 10.12 |
| 515 | 527 | DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer |
| 516 | <<: *job-macos | |
| 528 | <<: *job-macos-15 | |
| 517 | 529 | |
| 518 | 530 | - name: dist-aarch64-apple |
| 519 | 531 | env: |
| ... | ... | @@ -537,7 +549,7 @@ auto: |
| 537 | 549 | DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer |
| 538 | 550 | DIST_REQUIRE_ALL_TOOLS: 1 |
| 539 | 551 | CODEGEN_BACKENDS: llvm,cranelift |
| 540 | <<: *job-macos | |
| 552 | <<: *job-macos-15 | |
| 541 | 553 | |
| 542 | 554 | - name: aarch64-apple |
| 543 | 555 | env: |
| ... | ... | @@ -553,7 +565,29 @@ auto: |
| 553 | 565 | # supports the hardware, so only need to test it there. |
| 554 | 566 | MACOSX_DEPLOYMENT_TARGET: 11.0 |
| 555 | 567 | MACOSX_STD_DEPLOYMENT_TARGET: 11.0 |
| 556 | <<: *job-macos | |
| 568 | <<: *job-macos-15 | |
| 569 | ||
| 570 | # EXPERIMENT(#157687): we will try to run `aarch64-apple`-equivalent workload | |
| 571 | # on `macos-26` runner images in parallel to evaluate the running times, since | |
| 572 | # previous attempts have timed out multiple times. Remove/revert this job if | |
| 573 | # this hangs or times out, or if it becomes the slowest Merge CI job, and let | |
| 574 | # T-infra know. | |
| 575 | - name: aarch64-apple-macos-26 | |
| 576 | doc_url: https://github.com/rust-lang/rust/issues/157687 | |
| 577 | env: | |
| 578 | SCRIPT: > | |
| 579 | ./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin && | |
| 580 | ./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin src/tools/cargo | |
| 581 | RUST_CONFIGURE_ARGS: >- | |
| 582 | --enable-sanitizers | |
| 583 | --enable-profiler | |
| 584 | --set rust.jemalloc | |
| 585 | DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer | |
| 586 | # Aarch64 tooling only needs to support macOS 11.0 and up as nothing else | |
| 587 | # supports the hardware, so only need to test it there. | |
| 588 | MACOSX_DEPLOYMENT_TARGET: 11.0 | |
| 589 | MACOSX_STD_DEPLOYMENT_TARGET: 11.0 | |
| 590 | <<: *job-macos-26 | |
| 557 | 591 | |
| 558 | 592 | ###################### |
| 559 | 593 | # Windows Builders # |
src/doc/rustc/src/SUMMARY.md+1| ... | ... | @@ -107,6 +107,7 @@ |
| 107 | 107 | - [powerpc-unknown-linux-gnuspe](platform-support/powerpc-unknown-linux-gnuspe.md) |
| 108 | 108 | - [powerpc-unknown-linux-muslspe](platform-support/powerpc-unknown-linux-muslspe.md) |
| 109 | 109 | - [powerpc64-ibm-aix](platform-support/aix.md) |
| 110 | - [powerpc64-unknown-linux-gnuelfv2](platform-support/powerpc64-unknown-linux-gnuelfv2.md) | |
| 110 | 111 | - [powerpc64-unknown-linux-musl](platform-support/powerpc64-unknown-linux-musl.md) |
| 111 | 112 | - [powerpc64le-unknown-linux-gnu](platform-support/powerpc64le-unknown-linux-gnu.md) |
| 112 | 113 | - [powerpc64le-unknown-linux-musl](platform-support/powerpc64le-unknown-linux-musl.md) |
src/doc/rustc/src/platform-support.md+2-1| ... | ... | @@ -89,6 +89,7 @@ so Rustup may install the documentation for a similar tier 1 target instead. |
| 89 | 89 | target | notes |
| 90 | 90 | -------|------- |
| 91 | 91 | [`aarch64-pc-windows-gnullvm`](platform-support/windows-gnullvm.md) | ARM64 MinGW (Windows 10+), LLVM ABI |
| 92 | [`aarch64-unknown-freebsd`](platform-support/freebsd.md) | ARM64 FreeBSD | |
| 92 | 93 | [`aarch64-unknown-linux-musl`](platform-support/aarch64-unknown-linux-musl.md) | ARM64 Linux with musl 1.2.5 |
| 93 | 94 | [`aarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ARM64 OpenHarmony |
| 94 | 95 | `arm-unknown-linux-gnueabi` | Armv6 Linux (kernel 3.2+, glibc 2.17) |
| ... | ... | @@ -266,7 +267,6 @@ target | std | host | notes |
| 266 | 267 | -------|:---:|:----:|------- |
| 267 | 268 | [`aarch64-kmc-solid_asp3`](platform-support/kmc-solid.md) | ✓ | | ARM64 SOLID with TOPPERS/ASP3 |
| 268 | 269 | [`aarch64-nintendo-switch-freestanding`](platform-support/aarch64-nintendo-switch-freestanding.md) | * | | ARM64 Nintendo Switch, Horizon |
| 269 | [`aarch64-unknown-freebsd`](platform-support/freebsd.md) | ✓ | ✓ | ARM64 FreeBSD | |
| 270 | 270 | [`aarch64-unknown-helenos`](platform-support/helenos.md) | ✓ | | ARM64 HelenOS |
| 271 | 271 | [`aarch64-unknown-hermit`](platform-support/hermit.md) | ✓ | | ARM64 Hermit |
| 272 | 272 | [`aarch64-unknown-illumos`](platform-support/illumos.md) | ✓ | ✓ | ARM64 illumos |
| ... | ... | @@ -385,6 +385,7 @@ target | std | host | notes |
| 385 | 385 | [`powerpc-wrs-vxworks-spe`](platform-support/vxworks.md) | ✓ | | |
| 386 | 386 | [`powerpc64-ibm-aix`](platform-support/aix.md) | ? | | 64-bit AIX (7.2 and newer) |
| 387 | 387 | [`powerpc64-unknown-freebsd`](platform-support/freebsd.md) | ✓ | ✓ | PPC64 FreeBSD (ELFv2) |
| 388 | [`powerpc64-unknown-linux-gnuelfv2`](platform-support/powerpc64-unknown-linux-gnuelfv2.md) | ✓ | ✓ | PPC64 Linux (ELFv2 ABI, kernel 3.2, glibc 2.17) | |
| 388 | 389 | [`powerpc64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/powerpc64 |
| 389 | 390 | [`powerpc64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | |
| 390 | 391 | [`powerpc64le-unknown-freebsd`](platform-support/freebsd.md) | ✓ | ✓ | PPC64LE FreeBSD |
src/doc/rustc/src/platform-support/freebsd.md+7-5| ... | ... | @@ -11,8 +11,9 @@ |
| 11 | 11 | |
| 12 | 12 | ## Requirements |
| 13 | 13 | |
| 14 | The `x86_64-unknown-freebsd` target is Tier 2 with host tools. | |
| 15 | `i686-unknown-freebsd` is Tier 2 without host tools. Other targets are Tier 3. | |
| 14 | The `x86_64-unknown-freebsd` and `aarch64-unknown-freebsd` targets are Tier 2 | |
| 15 | with host tools. `i686-unknown-freebsd` is Tier 2 without host tools. | |
| 16 | Other targets are Tier 3. | |
| 16 | 17 | See [platform-support.md](../platform-support.md) for the full list. |
| 17 | 18 | |
| 18 | 19 | We commit that rustc will run on all currently supported releases of |
| ... | ... | @@ -34,9 +35,10 @@ FreeBSD OS binaries use the ELF file format. |
| 34 | 35 | |
| 35 | 36 | ## Building Rust programs |
| 36 | 37 | |
| 37 | The `x86_64-unknown-freebsd` and `i686-unknown-freebsd` artifacts are | |
| 38 | distributed by the rust project and may be installed with rustup. Other | |
| 39 | targets are built by the ports system and may be installed with | |
| 38 | The `x86_64-unknown-freebsd`, `aarch64-unknown-freebsd` and | |
| 39 | `i686-unknown-freebsd` artifacts are distributed by the rust | |
| 40 | project and may be installed with rustup. Other targets are | |
| 41 | built by the ports system and may be installed with | |
| 40 | 42 | [pkg(7)][pkg] or [ports(7)][ports]. |
| 41 | 43 | |
| 42 | 44 | By default the `i686-unknown-freebsd` target uses SSE2 instructions. To build |
src/doc/rustc/src/platform-support/powerpc64-unknown-linux-gnuelfv2.md created+50| ... | ... | @@ -0,0 +1,50 @@ |
| 1 | # powerpc64-unknown-linux-gnuelfv2 | |
| 2 | ||
| 3 | **Tier: 3** | |
| 4 | ||
| 5 | Target for 64-bit big endian PowerPC Linux programs using the ELFv2 ABI and | |
| 6 | the GNU C library. | |
| 7 | ||
| 8 | ## Target maintainers | |
| 9 | ||
| 10 | [@Gelbpunkt](https://github.com/Gelbpunkt) | |
| 11 | ||
| 12 | ## Requirements | |
| 13 | ||
| 14 | Building the target itself requires a 64-bit big endian PowerPC compiler that | |
| 15 | uses the ELFv2 ABI and is supported by `cc-rs`. | |
| 16 | ||
| 17 | ## Building the target | |
| 18 | ||
| 19 | The target can be built by enabling it for a `rustc` build. | |
| 20 | ||
| 21 | ```toml | |
| 22 | [build] | |
| 23 | target = ["powerpc64-unknown-linux-gnuelfv2"] | |
| 24 | ``` | |
| 25 | ||
| 26 | Make sure your C compiler is included in `$PATH`, then add it to the | |
| 27 | `bootstrap.toml`: | |
| 28 | ||
| 29 | ```toml | |
| 30 | [target.powerpc64-unknown-linux-gnuelfv2] | |
| 31 | cc = "powerpc64-linux-gnu-gcc" | |
| 32 | cxx = "powerpc64-linux-gnu-g++" | |
| 33 | ar = "powerpc64-linux-gnu-ar" | |
| 34 | linker = "powerpc64-linux-gnu-gcc" | |
| 35 | ``` | |
| 36 | ||
| 37 | ## Building Rust programs | |
| 38 | ||
| 39 | Rust does not yet ship pre-compiled artifacts for this target. To compile for | |
| 40 | this target, you will first need to build Rust with the target enabled (see | |
| 41 | "Building the target" above). | |
| 42 | ||
| 43 | ## Cross-compilation | |
| 44 | ||
| 45 | This target can be cross-compiled from any host. | |
| 46 | ||
| 47 | ## Testing | |
| 48 | ||
| 49 | This target can be tested as normal with `x.py` on a 64-bit big endian PowerPC | |
| 50 | host or via QEMU emulation. |
src/doc/unstable-book/src/compiler-flags/remap-cwd-prefix.md+6| ... | ... | @@ -16,6 +16,12 @@ directory from build output, while allowing the command line to be universally |
| 16 | 16 | reproducible, such that the same execution will work on all machines, regardless |
| 17 | 17 | of build environment. |
| 18 | 18 | |
| 19 | Unlike passing the equivalent mapping through `--remap-path-prefix`, the current | |
| 20 | working directory does not take part in incremental compilation's dependency | |
| 21 | tracking. Building the same sources from different directories (for example, a | |
| 22 | sandboxed or per-build checkout path) therefore reuses the incremental cache | |
| 23 | rather than invalidating it. | |
| 24 | ||
| 19 | 25 | ## Example |
| 20 | 26 | ```sh |
| 21 | 27 | # This would produce an absolute path to main.rs in build outputs of |
src/etc/gdb_lookup.py+1| ... | ... | @@ -103,6 +103,7 @@ if gdb_version[0] < 7 or (gdb_version[0] == 7 and gdb_version[1] < 12): |
| 103 | 103 | printer.add(RustType.StdString, StdStringProvider) |
| 104 | 104 | printer.add(RustType.StdOsString, StdOsStringProvider) |
| 105 | 105 | printer.add(RustType.StdStr, StdStrProvider) |
| 106 | printer.add(RustType.StdBoxStr, StdBoxStrProvider) | |
| 106 | 107 | printer.add(RustType.StdSlice, StdSliceProvider) |
| 107 | 108 | printer.add(RustType.StdVec, StdVecProvider) |
| 108 | 109 | printer.add(RustType.StdVecDeque, StdVecDequeProvider) |
src/etc/gdb_providers.py+20| ... | ... | @@ -142,6 +142,20 @@ class StdSliceProvider(printer_base): |
| 142 | 142 | return "array" |
| 143 | 143 | |
| 144 | 144 | |
| 145 | class StdBoxStrProvider(printer_base): | |
| 146 | def __init__(self, valobj): | |
| 147 | self._valobj = valobj | |
| 148 | self._length = int(valobj["length"]) | |
| 149 | self._data_ptr = valobj["data_ptr"] | |
| 150 | ||
| 151 | def to_string(self): | |
| 152 | return self._data_ptr.lazy_string(encoding="utf-8", length=self._length) | |
| 153 | ||
| 154 | @staticmethod | |
| 155 | def display_hint(): | |
| 156 | return "string" | |
| 157 | ||
| 158 | ||
| 145 | 159 | class StdVecProvider(printer_base): |
| 146 | 160 | def __init__(self, valobj): |
| 147 | 161 | self._valobj = valobj |
| ... | ... | @@ -209,6 +223,12 @@ class StdRcProvider(printer_base): |
| 209 | 223 | self._is_atomic = is_atomic |
| 210 | 224 | self._ptr = unwrap_unique_or_non_null(valobj["ptr"]) |
| 211 | 225 | self._value = self._ptr["data" if is_atomic else "value"] |
| 226 | # FIXME(shua): the debuginfo template type should be 'str' not 'u8' | |
| 227 | if self._ptr.type.target().name == "alloc::rc::RcInner<str>": | |
| 228 | length = self._valobj["ptr"]["pointer"]["length"] | |
| 229 | u8_ptr_ty = gdb.Type.pointer(gdb.lookup_type("u8")) | |
| 230 | ptr = self._value.address.reinterpret_cast(u8_ptr_ty) | |
| 231 | self._value = ptr.lazy_string(encoding="utf-8", length=length) | |
| 212 | 232 | self._strong = unwrap_scalar_wrappers(self._ptr["strong"]) |
| 213 | 233 | self._weak = unwrap_scalar_wrappers(self._ptr["weak"]) - 1 |
| 214 | 234 |
src/etc/rust_types.py+3| ... | ... | @@ -37,12 +37,14 @@ class RustType(Enum): |
| 37 | 37 | StdNonZeroNumber = 29 |
| 38 | 38 | StdPath = 30 |
| 39 | 39 | StdPathBuf = 31 |
| 40 | StdBoxStr = 32 | |
| 40 | 41 | |
| 41 | 42 | |
| 42 | 43 | STD_STRING_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)String$") |
| 43 | 44 | STD_STR_REGEX = re.compile(r"^&(mut )?str$") |
| 44 | 45 | STD_SLICE_REGEX = re.compile(r"^&(mut )?\[.+\]$") |
| 45 | 46 | STD_OS_STRING_REGEX = re.compile(r"^(std::ffi::([a-z_]+::)+)OsString$") |
| 47 | STD_BOX_STR_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)Box<str,.+>$") | |
| 46 | 48 | STD_VEC_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)Vec<.+>$") |
| 47 | 49 | STD_VEC_DEQUE_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)VecDeque<.+>$") |
| 48 | 50 | STD_BTREE_SET_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)BTreeSet<.+>$") |
| ... | ... | @@ -67,6 +69,7 @@ STD_TYPE_TO_REGEX = { |
| 67 | 69 | RustType.StdString: STD_STRING_REGEX, |
| 68 | 70 | RustType.StdOsString: STD_OS_STRING_REGEX, |
| 69 | 71 | RustType.StdStr: STD_STR_REGEX, |
| 72 | RustType.StdBoxStr: STD_BOX_STR_REGEX, | |
| 70 | 73 | RustType.StdSlice: STD_SLICE_REGEX, |
| 71 | 74 | RustType.StdVec: STD_VEC_REGEX, |
| 72 | 75 | RustType.StdVecDeque: STD_VEC_DEQUE_REGEX, |
src/librustdoc/html/render/mod.rs+9-5| ... | ... | @@ -2955,7 +2955,7 @@ fn render_call_locations<W: fmt::Write>( |
| 2955 | 2955 | fn render_attributes_in_code( |
| 2956 | 2956 | w: &mut impl fmt::Write, |
| 2957 | 2957 | item: &clean::Item, |
| 2958 | prefix: &str, | |
| 2958 | prefix: impl fmt::Display, | |
| 2959 | 2959 | cx: &Context<'_>, |
| 2960 | 2960 | ) -> fmt::Result { |
| 2961 | 2961 | render_attributes_in_code_with_options(w, item, prefix, cx, true, "") |
| ... | ... | @@ -2964,14 +2964,14 @@ fn render_attributes_in_code( |
| 2964 | 2964 | pub(super) fn render_attributes_in_code_with_options( |
| 2965 | 2965 | w: &mut impl fmt::Write, |
| 2966 | 2966 | item: &clean::Item, |
| 2967 | prefix: &str, | |
| 2967 | prefix: impl fmt::Display, | |
| 2968 | 2968 | cx: &Context<'_>, |
| 2969 | 2969 | render_doc_hidden: bool, |
| 2970 | 2970 | open_tag: &str, |
| 2971 | 2971 | ) -> fmt::Result { |
| 2972 | 2972 | w.write_str(open_tag)?; |
| 2973 | 2973 | if render_doc_hidden && item.is_doc_hidden() { |
| 2974 | render_code_attribute(prefix, "#[doc(hidden)]", w)?; | |
| 2974 | render_code_attribute(&prefix, "#[doc(hidden)]", w)?; | |
| 2975 | 2975 | } |
| 2976 | 2976 | for attr in &item.attrs.other_attrs { |
| 2977 | 2977 | let hir::Attribute::Parsed(kind) = attr else { continue }; |
| ... | ... | @@ -2986,7 +2986,7 @@ pub(super) fn render_attributes_in_code_with_options( |
| 2986 | 2986 | AttributeKind::NonExhaustive(..) => Cow::Borrowed("#[non_exhaustive]"), |
| 2987 | 2987 | _ => continue, |
| 2988 | 2988 | }; |
| 2989 | render_code_attribute(prefix, attr.as_ref(), w)?; | |
| 2989 | render_code_attribute(&prefix, attr.as_ref(), w)?; | |
| 2990 | 2990 | } |
| 2991 | 2991 | |
| 2992 | 2992 | if let Some(def_id) = item.def_id() |
| ... | ... | @@ -3008,7 +3008,11 @@ fn render_repr_attribute_in_code( |
| 3008 | 3008 | Ok(()) |
| 3009 | 3009 | } |
| 3010 | 3010 | |
| 3011 | fn render_code_attribute(prefix: &str, attr: &str, w: &mut impl fmt::Write) -> fmt::Result { | |
| 3011 | fn render_code_attribute( | |
| 3012 | prefix: impl fmt::Display, | |
| 3013 | attr: impl fmt::Display, | |
| 3014 | w: &mut impl fmt::Write, | |
| 3015 | ) -> fmt::Result { | |
| 3012 | 3016 | write!(w, "<div class=\"code-attribute\">{prefix}{attr}</div>") |
| 3013 | 3017 | } |
| 3014 | 3018 |
src/librustdoc/html/render/print_item.rs+22-28| ... | ... | @@ -1,3 +1,4 @@ |
| 1 | use std::borrow::Cow; | |
| 1 | 2 | use std::cmp::Ordering; |
| 2 | 3 | use std::fmt::{self, Display, Write as _}; |
| 3 | 4 | use std::iter; |
| ... | ... | @@ -395,25 +396,26 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i |
| 395 | 396 | let (stab_tags, deprecation) = match import.source.did { |
| 396 | 397 | Some(import_def_id) => { |
| 397 | 398 | let stab_tags = |
| 398 | print_extra_info_tags(tcx, myitem, item, Some(import_def_id)) | |
| 399 | .to_string(); | |
| 399 | print_extra_info_tags(tcx, myitem, item, Some(import_def_id)); | |
| 400 | 400 | let deprecation = tcx |
| 401 | 401 | .lookup_deprecation(import_def_id) |
| 402 | 402 | .is_some_and(|deprecation| deprecation.is_in_effect()); |
| 403 | (stab_tags, deprecation) | |
| 403 | (Some(stab_tags), deprecation) | |
| 404 | 404 | } |
| 405 | None => (String::new(), item.is_deprecated(tcx)), | |
| 405 | None => (None, item.is_deprecated(tcx)), | |
| 406 | 406 | }; |
| 407 | 407 | let visibility_and_hidden = visibility_and_hidden(myitem); |
| 408 | 408 | let id = match import.kind { |
| 409 | clean::ImportKind::Simple(s) => { | |
| 410 | format!(" id=\"{}\"", cx.derive_id(format!("reexport.{s}"))) | |
| 411 | } | |
| 412 | clean::ImportKind::Glob => String::new(), | |
| 409 | clean::ImportKind::Simple(s) => Some(format_args!( | |
| 410 | " id=\"{}\"", | |
| 411 | cx.derive_id(format!("reexport.{s}")) | |
| 412 | )), | |
| 413 | clean::ImportKind::Glob => None, | |
| 413 | 414 | }; |
| 414 | 415 | write!( |
| 415 | 416 | w, |
| 416 | 417 | "<dt{id}{deprecation_attr}><code>", |
| 418 | id = id.maybe_display(), | |
| 417 | 419 | deprecation_attr = deprecation_class_attr(deprecation) |
| 418 | 420 | )?; |
| 419 | 421 | write!( |
| ... | ... | @@ -423,6 +425,7 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i |
| 423 | 425 | vis = visibility_print_with_space(myitem, cx), |
| 424 | 426 | imp = print_import(import, cx), |
| 425 | 427 | visibility_and_hidden = visibility_and_hidden, |
| 428 | stab_tags = stab_tags.maybe_display(), | |
| 426 | 429 | )?; |
| 427 | 430 | } |
| 428 | 431 | _ => { |
| ... | ... | @@ -512,18 +515,14 @@ fn print_extra_info_tags( |
| 512 | 515 | write!(f, "{}", tag_html("unstable", "", "Experimental"))?; |
| 513 | 516 | } |
| 514 | 517 | |
| 518 | debug!(name = ?item.name, cfg = ?item.cfg, parent_cfg = ?parent.cfg, "Portability"); | |
| 519 | ||
| 515 | 520 | let cfg = match (&item.cfg, parent.cfg.as_ref()) { |
| 516 | (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg), | |
| 517 | (cfg, _) => cfg.as_deref().cloned(), | |
| 521 | (Some(cfg), Some(parent_cfg)) => cfg.simplify_with(parent_cfg).map(Cow::Owned), | |
| 522 | (cfg, _) => cfg.as_deref().map(Cow::Borrowed), | |
| 518 | 523 | }; |
| 519 | 524 | |
| 520 | debug!( | |
| 521 | "Portability name={name:?} {cfg:?} - {parent_cfg:?} = {cfg:?}", | |
| 522 | name = item.name, | |
| 523 | cfg = item.cfg, | |
| 524 | parent_cfg = parent.cfg | |
| 525 | ); | |
| 526 | if let Some(ref cfg) = cfg { | |
| 525 | if let Some(cfg) = cfg { | |
| 527 | 526 | write!( |
| 528 | 527 | f, |
| 529 | 528 | "{}", |
| ... | ... | @@ -976,7 +975,7 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt: |
| 976 | 975 | "Dyn Compatibility", |
| 977 | 976 | "dyn-compatibility", |
| 978 | 977 | None, |
| 979 | format!( | |
| 978 | format_args!( | |
| 980 | 979 | "<div class=\"dyn-compatibility-info\"><p>This trait {} \ |
| 981 | 980 | <a href=\"{base}/reference/items/traits.html#dyn-compatibility\">dyn compatible</a>.</p>\ |
| 982 | 981 | <p><i>In older versions of Rust, dyn compatibility was called \"object safety\".</i></p></div>", |
| ... | ... | @@ -1775,10 +1774,10 @@ fn item_variants( |
| 1775 | 1774 | w, |
| 1776 | 1775 | "{}", |
| 1777 | 1776 | write_section_heading( |
| 1778 | &format!("Variants{}", document_non_exhaustive_header(it)), | |
| 1777 | format_args!("Variants{}", document_non_exhaustive_header(it)), | |
| 1779 | 1778 | "variants", |
| 1780 | 1779 | Some("variants"), |
| 1781 | format!("{}<div class=\"variants\">", document_non_exhaustive(it)), | |
| 1780 | format_args!("{}<div class=\"variants\">", document_non_exhaustive(it)), | |
| 1782 | 1781 | ), |
| 1783 | 1782 | )?; |
| 1784 | 1783 | |
| ... | ... | @@ -2105,7 +2104,7 @@ fn item_fields( |
| 2105 | 2104 | if let None | Some(CtorKind::Fn) = ctor_kind |
| 2106 | 2105 | && fields.peek().is_some() |
| 2107 | 2106 | { |
| 2108 | let title = format!( | |
| 2107 | let title = format_args!( | |
| 2109 | 2108 | "{}{}", |
| 2110 | 2109 | if ctor_kind.is_none() { "Fields" } else { "Tuple Fields" }, |
| 2111 | 2110 | document_non_exhaustive_header(it), |
| ... | ... | @@ -2113,12 +2112,7 @@ fn item_fields( |
| 2113 | 2112 | write!( |
| 2114 | 2113 | w, |
| 2115 | 2114 | "{}", |
| 2116 | write_section_heading( | |
| 2117 | &title, | |
| 2118 | "fields", | |
| 2119 | Some("fields"), | |
| 2120 | document_non_exhaustive(it) | |
| 2121 | ) | |
| 2115 | write_section_heading(title, "fields", Some("fields"), document_non_exhaustive(it)) | |
| 2122 | 2116 | )?; |
| 2123 | 2117 | for (index, (field, ty)) in fields.enumerate() { |
| 2124 | 2118 | let field_name = |
| ... | ... | @@ -2554,7 +2548,7 @@ fn render_struct_fields( |
| 2554 | 2548 | } |
| 2555 | 2549 | for field in fields { |
| 2556 | 2550 | if let clean::StructFieldItem(ref ty) = field.kind { |
| 2557 | render_attributes_in_code(w, field, &format!("{tab} "), cx)?; | |
| 2551 | render_attributes_in_code(w, field, format_args!("{tab} "), cx)?; | |
| 2558 | 2552 | writeln!( |
| 2559 | 2553 | w, |
| 2560 | 2554 | "{tab} {vis}{name}: {ty},", |
src/librustdoc/html/render/write_shared.rs+1-1| ... | ... | @@ -413,7 +413,7 @@ impl CratesIndexPart { |
| 413 | 413 | let layout = &cx.shared.layout; |
| 414 | 414 | let style_files = &cx.shared.style_files; |
| 415 | 415 | const DELIMITER: &str = "\u{FFFC}"; // users are being naughty if they have this |
| 416 | let content = format!( | |
| 416 | let content = format_args!( | |
| 417 | 417 | "<div class=\"main-heading\">\ |
| 418 | 418 | <h1>List of all crates</h1>\ |
| 419 | 419 | <rustdoc-toolbar></rustdoc-toolbar>\ |
src/tools/miri/.github/workflows/ci.yml+11-4| ... | ... | @@ -6,7 +6,7 @@ on: |
| 6 | 6 | branches: |
| 7 | 7 | - 'master' |
| 8 | 8 | schedule: |
| 9 | - cron: '44 4 * * *' # At 4:44 UTC every day. | |
| 9 | - cron: '14 4 * * *' # At 4:14 UTC every day. | |
| 10 | 10 | |
| 11 | 11 | defaults: |
| 12 | 12 | run: |
| ... | ... | @@ -157,15 +157,22 @@ jobs: |
| 157 | 157 | |
| 158 | 158 | # This job is intentionally separate from `test` so that Priroda can be |
| 159 | 159 | # developed as a separate crate inside the Miri repository for now. |
| 160 | priroda-build: | |
| 160 | priroda: | |
| 161 | 161 | name: Priroda |
| 162 | 162 | runs-on: ubuntu-latest |
| 163 | 163 | steps: |
| 164 | 164 | - uses: actions/checkout@v6 |
| 165 | 165 | - uses: ./.github/workflows/setup |
| 166 | - name: install Miri driver | |
| 167 | run: ./miri install | |
| 166 | 168 | - name: build Priroda |
| 167 | 169 | working-directory: priroda |
| 168 | 170 | run: cargo build --locked |
| 171 | - name: test Priroda | |
| 172 | working-directory: priroda | |
| 173 | run: | | |
| 174 | sysroot="$(cargo miri setup --print-sysroot)" | |
| 175 | MIRI_SYSROOT="$sysroot" cargo test --locked | |
| 169 | 176 | |
| 170 | 177 | coverage: |
| 171 | 178 | name: coverage report |
| ... | ... | @@ -180,7 +187,7 @@ jobs: |
| 180 | 187 | # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB! |
| 181 | 188 | # And they should be added below in `cron-fail-notify` as well. |
| 182 | 189 | conclusion: |
| 183 | needs: [test, style, bootstrap, coverage, priroda-build] | |
| 190 | needs: [test, style, bootstrap, coverage, priroda] | |
| 184 | 191 | # We need to ensure this job does *not* get skipped if its dependencies fail, |
| 185 | 192 | # because a skipped job is considered a success by GitHub. So we have to |
| 186 | 193 | # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run |
| ... | ... | @@ -264,7 +271,7 @@ jobs: |
| 264 | 271 | cron-fail-notify: |
| 265 | 272 | name: cronjob failure notification |
| 266 | 273 | runs-on: ubuntu-latest |
| 267 | needs: [test, style, bootstrap, coverage, priroda-build] | |
| 274 | needs: [test, style, bootstrap, coverage, priroda] | |
| 268 | 275 | if: ${{ github.event_name == 'schedule' && failure() }} |
| 269 | 276 | steps: |
| 270 | 277 | # Send a Zulip notification |
src/tools/miri/Cargo.lock+2-2| ... | ... | @@ -19,9 +19,9 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" |
| 19 | 19 | |
| 20 | 20 | [[package]] |
| 21 | 21 | name = "aes" |
| 22 | version = "0.9.0" | |
| 22 | version = "0.9.1" | |
| 23 | 23 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 24 | checksum = "66bd29a732b644c0431c6140f370d097879203d79b80c94a6747ba0872adaef8" | |
| 24 | checksum = "f1fc76eaeac4c9164506c466d4ffdd8ec9d0c5bf57ee97177c4d8eceb3a0e138" | |
| 25 | 25 | dependencies = [ |
| 26 | 26 | "cipher", |
| 27 | 27 | "cpubits", |
src/tools/miri/README.md+4| ... | ... | @@ -1,5 +1,9 @@ |
| 1 | 1 | # Miri |
| 2 | 2 | |
| 3 | <img src="miri-sticker.png" align="right" width="200px" | |
| 4 | alt="Corro, the Unsafe Rusturchin, drinking from a juice bottle labeled 'Miri - 100% safe'" | |
| 5 | title="Art by Paige Losare-Lusby, Creative Commons Attribute-ShareAlike"> | |
| 6 | ||
| 3 | 7 | Miri is an [Undefined Behavior][reference-ub] detection tool for Rust. It can run binaries and test |
| 4 | 8 | suites of cargo projects and detect unsafe code that fails to uphold its safety requirements. For |
| 5 | 9 | instance: |
src/tools/miri/miri-sticker.png| Binary files /dev/null and b/src/tools/miri/miri-sticker.png differ |
src/tools/miri/priroda/Cargo.lock+452-2| ... | ... | @@ -2,6 +2,21 @@ |
| 2 | 2 | # It is not intended for manual editing. |
| 3 | 3 | version = 4 |
| 4 | 4 | |
| 5 | [[package]] | |
| 6 | name = "addr2line" | |
| 7 | version = "0.25.1" | |
| 8 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 9 | checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" | |
| 10 | dependencies = [ | |
| 11 | "gimli", | |
| 12 | ] | |
| 13 | ||
| 14 | [[package]] | |
| 15 | name = "adler2" | |
| 16 | version = "2.0.1" | |
| 17 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 18 | checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" | |
| 19 | ||
| 5 | 20 | [[package]] |
| 6 | 21 | name = "aes" |
| 7 | 22 | version = "0.9.0" |
| ... | ... | @@ -13,6 +28,31 @@ dependencies = [ |
| 13 | 28 | "cpufeatures", |
| 14 | 29 | ] |
| 15 | 30 | |
| 31 | [[package]] | |
| 32 | name = "aho-corasick" | |
| 33 | version = "1.1.4" | |
| 34 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 35 | checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" | |
| 36 | dependencies = [ | |
| 37 | "memchr", | |
| 38 | ] | |
| 39 | ||
| 40 | [[package]] | |
| 41 | name = "annotate-snippets" | |
| 42 | version = "0.11.5" | |
| 43 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 44 | checksum = "710e8eae58854cdc1790fcb56cca04d712a17be849eeb81da2a724bf4bae2bc4" | |
| 45 | dependencies = [ | |
| 46 | "anstyle", | |
| 47 | "unicode-width", | |
| 48 | ] | |
| 49 | ||
| 50 | [[package]] | |
| 51 | name = "anstyle" | |
| 52 | version = "1.0.14" | |
| 53 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 54 | checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" | |
| 55 | ||
| 16 | 56 | [[package]] |
| 17 | 57 | name = "anyhow" |
| 18 | 58 | version = "1.0.102" |
| ... | ... | @@ -25,6 +65,21 @@ version = "1.5.0" |
| 25 | 65 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 26 | 66 | checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" |
| 27 | 67 | |
| 68 | [[package]] | |
| 69 | name = "backtrace" | |
| 70 | version = "0.3.76" | |
| 71 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 72 | checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" | |
| 73 | dependencies = [ | |
| 74 | "addr2line", | |
| 75 | "cfg-if", | |
| 76 | "libc", | |
| 77 | "miniz_oxide", | |
| 78 | "object", | |
| 79 | "rustc-demangle", | |
| 80 | "windows-link 0.2.1", | |
| 81 | ] | |
| 82 | ||
| 28 | 83 | [[package]] |
| 29 | 84 | name = "bincode" |
| 30 | 85 | version = "1.3.3" |
| ... | ... | @@ -40,12 +95,32 @@ version = "2.11.1" |
| 40 | 95 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 41 | 96 | checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" |
| 42 | 97 | |
| 98 | [[package]] | |
| 99 | name = "bstr" | |
| 100 | version = "1.12.1" | |
| 101 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 102 | checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" | |
| 103 | dependencies = [ | |
| 104 | "memchr", | |
| 105 | "regex-automata", | |
| 106 | "serde", | |
| 107 | ] | |
| 108 | ||
| 43 | 109 | [[package]] |
| 44 | 110 | name = "bumpalo" |
| 45 | 111 | version = "3.20.2" |
| 46 | 112 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 47 | 113 | checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" |
| 48 | 114 | |
| 115 | [[package]] | |
| 116 | name = "camino" | |
| 117 | version = "1.2.2" | |
| 118 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 119 | checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" | |
| 120 | dependencies = [ | |
| 121 | "serde_core", | |
| 122 | ] | |
| 123 | ||
| 49 | 124 | [[package]] |
| 50 | 125 | name = "capstone" |
| 51 | 126 | version = "0.14.0" |
| ... | ... | @@ -65,6 +140,29 @@ dependencies = [ |
| 65 | 140 | "cc", |
| 66 | 141 | ] |
| 67 | 142 | |
| 143 | [[package]] | |
| 144 | name = "cargo-platform" | |
| 145 | version = "0.3.1" | |
| 146 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 147 | checksum = "122ec45a44b270afd1402f351b782c676b173e3c3fb28d86ff7ebfb4d86a4ee4" | |
| 148 | dependencies = [ | |
| 149 | "serde", | |
| 150 | ] | |
| 151 | ||
| 152 | [[package]] | |
| 153 | name = "cargo_metadata" | |
| 154 | version = "0.23.1" | |
| 155 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 156 | checksum = "ef987d17b0a113becdd19d3d0022d04d7ef41f9efe4f3fb63ac44ba61df3ade9" | |
| 157 | dependencies = [ | |
| 158 | "camino", | |
| 159 | "cargo-platform", | |
| 160 | "semver", | |
| 161 | "serde", | |
| 162 | "serde_json", | |
| 163 | "thiserror 2.0.18", | |
| 164 | ] | |
| 165 | ||
| 68 | 166 | [[package]] |
| 69 | 167 | name = "cc" |
| 70 | 168 | version = "1.2.62" |
| ... | ... | @@ -127,6 +225,60 @@ dependencies = [ |
| 127 | 225 | "inout", |
| 128 | 226 | ] |
| 129 | 227 | |
| 228 | [[package]] | |
| 229 | name = "color-eyre" | |
| 230 | version = "0.6.5" | |
| 231 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 232 | checksum = "e5920befb47832a6d61ee3a3a846565cfa39b331331e68a3b1d1116630f2f26d" | |
| 233 | dependencies = [ | |
| 234 | "backtrace", | |
| 235 | "color-spantrace", | |
| 236 | "eyre", | |
| 237 | "indenter", | |
| 238 | "once_cell", | |
| 239 | "owo-colors", | |
| 240 | "tracing-error", | |
| 241 | ] | |
| 242 | ||
| 243 | [[package]] | |
| 244 | name = "color-spantrace" | |
| 245 | version = "0.3.0" | |
| 246 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 247 | checksum = "b8b88ea9df13354b55bc7234ebcce36e6ef896aca2e42a15de9e10edce01b427" | |
| 248 | dependencies = [ | |
| 249 | "once_cell", | |
| 250 | "owo-colors", | |
| 251 | "tracing-core", | |
| 252 | "tracing-error", | |
| 253 | ] | |
| 254 | ||
| 255 | [[package]] | |
| 256 | name = "colored" | |
| 257 | version = "3.1.1" | |
| 258 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 259 | checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34" | |
| 260 | dependencies = [ | |
| 261 | "windows-sys", | |
| 262 | ] | |
| 263 | ||
| 264 | [[package]] | |
| 265 | name = "comma" | |
| 266 | version = "1.0.0" | |
| 267 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 268 | checksum = "55b672471b4e9f9e95499ea597ff64941a309b2cdbffcc46f2cc5e2d971fd335" | |
| 269 | ||
| 270 | [[package]] | |
| 271 | name = "console" | |
| 272 | version = "0.16.3" | |
| 273 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 274 | checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87" | |
| 275 | dependencies = [ | |
| 276 | "encode_unicode", | |
| 277 | "libc", | |
| 278 | "unicode-width", | |
| 279 | "windows-sys", | |
| 280 | ] | |
| 281 | ||
| 130 | 282 | [[package]] |
| 131 | 283 | name = "cpubits" |
| 132 | 284 | version = "0.1.1" |
| ... | ... | @@ -187,6 +339,12 @@ dependencies = [ |
| 187 | 339 | "windows-sys", |
| 188 | 340 | ] |
| 189 | 341 | |
| 342 | [[package]] | |
| 343 | name = "encode_unicode" | |
| 344 | version = "1.0.0" | |
| 345 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 346 | checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" | |
| 347 | ||
| 190 | 348 | [[package]] |
| 191 | 349 | name = "equivalent" |
| 192 | 350 | version = "1.0.2" |
| ... | ... | @@ -203,6 +361,16 @@ dependencies = [ |
| 203 | 361 | "windows-sys", |
| 204 | 362 | ] |
| 205 | 363 | |
| 364 | [[package]] | |
| 365 | name = "eyre" | |
| 366 | version = "0.6.12" | |
| 367 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 368 | checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" | |
| 369 | dependencies = [ | |
| 370 | "indenter", | |
| 371 | "once_cell", | |
| 372 | ] | |
| 373 | ||
| 206 | 374 | [[package]] |
| 207 | 375 | name = "fastrand" |
| 208 | 376 | version = "2.4.1" |
| ... | ... | @@ -288,6 +456,12 @@ dependencies = [ |
| 288 | 456 | "wasip3", |
| 289 | 457 | ] |
| 290 | 458 | |
| 459 | [[package]] | |
| 460 | name = "gimli" | |
| 461 | version = "0.32.3" | |
| 462 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 463 | checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" | |
| 464 | ||
| 291 | 465 | [[package]] |
| 292 | 466 | name = "hashbrown" |
| 293 | 467 | version = "0.15.5" |
| ... | ... | @@ -324,6 +498,12 @@ version = "2.3.0" |
| 324 | 498 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 325 | 499 | checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" |
| 326 | 500 | |
| 501 | [[package]] | |
| 502 | name = "indenter" | |
| 503 | version = "0.3.4" | |
| 504 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 505 | checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" | |
| 506 | ||
| 327 | 507 | [[package]] |
| 328 | 508 | name = "indexmap" |
| 329 | 509 | version = "2.14.0" |
| ... | ... | @@ -336,6 +516,19 @@ dependencies = [ |
| 336 | 516 | "serde_core", |
| 337 | 517 | ] |
| 338 | 518 | |
| 519 | [[package]] | |
| 520 | name = "indicatif" | |
| 521 | version = "0.18.4" | |
| 522 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 523 | checksum = "25470f23803092da7d239834776d653104d551bc4d7eacaf31e6837854b8e9eb" | |
| 524 | dependencies = [ | |
| 525 | "console", | |
| 526 | "portable-atomic", | |
| 527 | "unicode-width", | |
| 528 | "unit-prefix", | |
| 529 | "web-time", | |
| 530 | ] | |
| 531 | ||
| 339 | 532 | [[package]] |
| 340 | 533 | name = "inout" |
| 341 | 534 | version = "0.2.2" |
| ... | ... | @@ -381,12 +574,24 @@ dependencies = [ |
| 381 | 574 | "wasm-bindgen", |
| 382 | 575 | ] |
| 383 | 576 | |
| 577 | [[package]] | |
| 578 | name = "lazy_static" | |
| 579 | version = "1.5.0" | |
| 580 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 581 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" | |
| 582 | ||
| 384 | 583 | [[package]] |
| 385 | 584 | name = "leb128fmt" |
| 386 | 585 | version = "0.1.0" |
| 387 | 586 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 388 | 587 | checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" |
| 389 | 588 | |
| 589 | [[package]] | |
| 590 | name = "levenshtein" | |
| 591 | version = "1.0.5" | |
| 592 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 593 | checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" | |
| 594 | ||
| 390 | 595 | [[package]] |
| 391 | 596 | name = "libc" |
| 392 | 597 | version = "0.2.186" |
| ... | ... | @@ -481,6 +686,15 @@ dependencies = [ |
| 481 | 686 | "libc", |
| 482 | 687 | ] |
| 483 | 688 | |
| 689 | [[package]] | |
| 690 | name = "miniz_oxide" | |
| 691 | version = "0.8.9" | |
| 692 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 693 | checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" | |
| 694 | dependencies = [ | |
| 695 | "adler2", | |
| 696 | ] | |
| 697 | ||
| 484 | 698 | [[package]] |
| 485 | 699 | name = "mio" |
| 486 | 700 | version = "1.2.0" |
| ... | ... | @@ -537,6 +751,15 @@ dependencies = [ |
| 537 | 751 | "autocfg", |
| 538 | 752 | ] |
| 539 | 753 | |
| 754 | [[package]] | |
| 755 | name = "object" | |
| 756 | version = "0.37.3" | |
| 757 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 758 | checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" | |
| 759 | dependencies = [ | |
| 760 | "memchr", | |
| 761 | ] | |
| 762 | ||
| 540 | 763 | [[package]] |
| 541 | 764 | name = "once_cell" |
| 542 | 765 | version = "1.21.4" |
| ... | ... | @@ -549,6 +772,12 @@ version = "0.2.0" |
| 549 | 772 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 550 | 773 | checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" |
| 551 | 774 | |
| 775 | [[package]] | |
| 776 | name = "owo-colors" | |
| 777 | version = "4.3.0" | |
| 778 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 779 | checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" | |
| 780 | ||
| 552 | 781 | [[package]] |
| 553 | 782 | name = "parking_lot" |
| 554 | 783 | version = "0.12.5" |
| ... | ... | @@ -605,6 +834,12 @@ version = "0.2.17" |
| 605 | 834 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 606 | 835 | checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" |
| 607 | 836 | |
| 837 | [[package]] | |
| 838 | name = "portable-atomic" | |
| 839 | version = "1.13.1" | |
| 840 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 841 | checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" | |
| 842 | ||
| 608 | 843 | [[package]] |
| 609 | 844 | name = "ppv-lite86" |
| 610 | 845 | version = "0.2.21" |
| ... | ... | @@ -614,6 +849,15 @@ dependencies = [ |
| 614 | 849 | "zerocopy", |
| 615 | 850 | ] |
| 616 | 851 | |
| 852 | [[package]] | |
| 853 | name = "prettydiff" | |
| 854 | version = "0.9.0" | |
| 855 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 856 | checksum = "ac17546d82912e64874e3d5b40681ce32eac4e5834344f51efcf689ff1550a65" | |
| 857 | dependencies = [ | |
| 858 | "owo-colors", | |
| 859 | ] | |
| 860 | ||
| 617 | 861 | [[package]] |
| 618 | 862 | name = "prettyplease" |
| 619 | 863 | version = "0.2.37" |
| ... | ... | @@ -629,6 +873,8 @@ name = "priroda" |
| 629 | 873 | version = "0.1.0" |
| 630 | 874 | dependencies = [ |
| 631 | 875 | "miri", |
| 876 | "regex", | |
| 877 | "ui_test", | |
| 632 | 878 | ] |
| 633 | 879 | |
| 634 | 880 | [[package]] |
| ... | ... | @@ -724,15 +970,71 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" |
| 724 | 970 | dependencies = [ |
| 725 | 971 | "getrandom 0.2.17", |
| 726 | 972 | "libredox", |
| 727 | "thiserror", | |
| 973 | "thiserror 2.0.18", | |
| 728 | 974 | ] |
| 729 | 975 | |
| 976 | [[package]] | |
| 977 | name = "regex" | |
| 978 | version = "1.12.4" | |
| 979 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 980 | checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" | |
| 981 | dependencies = [ | |
| 982 | "aho-corasick", | |
| 983 | "memchr", | |
| 984 | "regex-automata", | |
| 985 | "regex-syntax", | |
| 986 | ] | |
| 987 | ||
| 988 | [[package]] | |
| 989 | name = "regex-automata" | |
| 990 | version = "0.4.14" | |
| 991 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 992 | checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" | |
| 993 | dependencies = [ | |
| 994 | "aho-corasick", | |
| 995 | "memchr", | |
| 996 | "regex-syntax", | |
| 997 | ] | |
| 998 | ||
| 999 | [[package]] | |
| 1000 | name = "regex-syntax" | |
| 1001 | version = "0.8.11" | |
| 1002 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1003 | checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" | |
| 1004 | ||
| 1005 | [[package]] | |
| 1006 | name = "rustc-demangle" | |
| 1007 | version = "0.1.27" | |
| 1008 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1009 | checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" | |
| 1010 | ||
| 730 | 1011 | [[package]] |
| 731 | 1012 | name = "rustc-hash" |
| 732 | 1013 | version = "1.1.0" |
| 733 | 1014 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 734 | 1015 | checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" |
| 735 | 1016 | |
| 1017 | [[package]] | |
| 1018 | name = "rustc_version" | |
| 1019 | version = "0.4.1" | |
| 1020 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1021 | checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" | |
| 1022 | dependencies = [ | |
| 1023 | "semver", | |
| 1024 | ] | |
| 1025 | ||
| 1026 | [[package]] | |
| 1027 | name = "rustfix" | |
| 1028 | version = "0.8.7" | |
| 1029 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1030 | checksum = "82fa69b198d894d84e23afde8e9ab2af4400b2cba20d6bf2b428a8b01c222c5a" | |
| 1031 | dependencies = [ | |
| 1032 | "serde", | |
| 1033 | "serde_json", | |
| 1034 | "thiserror 1.0.69", | |
| 1035 | "tracing", | |
| 1036 | ] | |
| 1037 | ||
| 736 | 1038 | [[package]] |
| 737 | 1039 | name = "rustix" |
| 738 | 1040 | version = "1.1.4" |
| ... | ... | @@ -763,6 +1065,10 @@ name = "semver" |
| 763 | 1065 | version = "1.0.28" |
| 764 | 1066 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 765 | 1067 | checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" |
| 1068 | dependencies = [ | |
| 1069 | "serde", | |
| 1070 | "serde_core", | |
| 1071 | ] | |
| 766 | 1072 | |
| 767 | 1073 | [[package]] |
| 768 | 1074 | name = "serde" |
| ... | ... | @@ -807,6 +1113,15 @@ dependencies = [ |
| 807 | 1113 | "zmij", |
| 808 | 1114 | ] |
| 809 | 1115 | |
| 1116 | [[package]] | |
| 1117 | name = "sharded-slab" | |
| 1118 | version = "0.1.7" | |
| 1119 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1120 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" | |
| 1121 | dependencies = [ | |
| 1122 | "lazy_static", | |
| 1123 | ] | |
| 1124 | ||
| 810 | 1125 | [[package]] |
| 811 | 1126 | name = "shlex" |
| 812 | 1127 | version = "1.3.0" |
| ... | ... | @@ -831,6 +1146,17 @@ version = "1.15.1" |
| 831 | 1146 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 832 | 1147 | checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" |
| 833 | 1148 | |
| 1149 | [[package]] | |
| 1150 | name = "spanned" | |
| 1151 | version = "0.4.1" | |
| 1152 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1153 | checksum = "c92d4b0c055fde758f086eb4a6e73410247df8a3837fd606d2caeeaf72aa566d" | |
| 1154 | dependencies = [ | |
| 1155 | "anyhow", | |
| 1156 | "bstr", | |
| 1157 | "color-eyre", | |
| 1158 | ] | |
| 1159 | ||
| 834 | 1160 | [[package]] |
| 835 | 1161 | name = "static_assertions" |
| 836 | 1162 | version = "1.1.0" |
| ... | ... | @@ -861,13 +1187,33 @@ dependencies = [ |
| 861 | 1187 | "windows-sys", |
| 862 | 1188 | ] |
| 863 | 1189 | |
| 1190 | [[package]] | |
| 1191 | name = "thiserror" | |
| 1192 | version = "1.0.69" | |
| 1193 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1194 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" | |
| 1195 | dependencies = [ | |
| 1196 | "thiserror-impl 1.0.69", | |
| 1197 | ] | |
| 1198 | ||
| 864 | 1199 | [[package]] |
| 865 | 1200 | name = "thiserror" |
| 866 | 1201 | version = "2.0.18" |
| 867 | 1202 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 868 | 1203 | checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" |
| 869 | 1204 | dependencies = [ |
| 870 | "thiserror-impl", | |
| 1205 | "thiserror-impl 2.0.18", | |
| 1206 | ] | |
| 1207 | ||
| 1208 | [[package]] | |
| 1209 | name = "thiserror-impl" | |
| 1210 | version = "1.0.69" | |
| 1211 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1212 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" | |
| 1213 | dependencies = [ | |
| 1214 | "proc-macro2", | |
| 1215 | "quote", | |
| 1216 | "syn", | |
| 871 | 1217 | ] |
| 872 | 1218 | |
| 873 | 1219 | [[package]] |
| ... | ... | @@ -881,24 +1227,112 @@ dependencies = [ |
| 881 | 1227 | "syn", |
| 882 | 1228 | ] |
| 883 | 1229 | |
| 1230 | [[package]] | |
| 1231 | name = "thread_local" | |
| 1232 | version = "1.1.9" | |
| 1233 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1234 | checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" | |
| 1235 | dependencies = [ | |
| 1236 | "cfg-if", | |
| 1237 | ] | |
| 1238 | ||
| 1239 | [[package]] | |
| 1240 | name = "tracing" | |
| 1241 | version = "0.1.44" | |
| 1242 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1243 | checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" | |
| 1244 | dependencies = [ | |
| 1245 | "pin-project-lite", | |
| 1246 | "tracing-core", | |
| 1247 | ] | |
| 1248 | ||
| 1249 | [[package]] | |
| 1250 | name = "tracing-core" | |
| 1251 | version = "0.1.36" | |
| 1252 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1253 | checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" | |
| 1254 | dependencies = [ | |
| 1255 | "once_cell", | |
| 1256 | "valuable", | |
| 1257 | ] | |
| 1258 | ||
| 1259 | [[package]] | |
| 1260 | name = "tracing-error" | |
| 1261 | version = "0.2.1" | |
| 1262 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1263 | checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" | |
| 1264 | dependencies = [ | |
| 1265 | "tracing", | |
| 1266 | "tracing-subscriber", | |
| 1267 | ] | |
| 1268 | ||
| 1269 | [[package]] | |
| 1270 | name = "tracing-subscriber" | |
| 1271 | version = "0.3.23" | |
| 1272 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1273 | checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" | |
| 1274 | dependencies = [ | |
| 1275 | "sharded-slab", | |
| 1276 | "thread_local", | |
| 1277 | "tracing-core", | |
| 1278 | ] | |
| 1279 | ||
| 884 | 1280 | [[package]] |
| 885 | 1281 | name = "typenum" |
| 886 | 1282 | version = "1.20.0" |
| 887 | 1283 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 888 | 1284 | checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" |
| 889 | 1285 | |
| 1286 | [[package]] | |
| 1287 | name = "ui_test" | |
| 1288 | version = "0.30.7" | |
| 1289 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1290 | checksum = "8c8811281d587a786747c0c49245925016c07767bc996305bdd34d5ce076786a" | |
| 1291 | dependencies = [ | |
| 1292 | "annotate-snippets", | |
| 1293 | "anyhow", | |
| 1294 | "bstr", | |
| 1295 | "cargo-platform", | |
| 1296 | "cargo_metadata", | |
| 1297 | "color-eyre", | |
| 1298 | "colored", | |
| 1299 | "comma", | |
| 1300 | "crossbeam-channel", | |
| 1301 | "indicatif", | |
| 1302 | "levenshtein", | |
| 1303 | "prettydiff", | |
| 1304 | "regex", | |
| 1305 | "rustc_version", | |
| 1306 | "rustfix", | |
| 1307 | "serde", | |
| 1308 | "serde_json", | |
| 1309 | "spanned", | |
| 1310 | ] | |
| 1311 | ||
| 890 | 1312 | [[package]] |
| 891 | 1313 | name = "unicode-ident" |
| 892 | 1314 | version = "1.0.24" |
| 893 | 1315 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 894 | 1316 | checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" |
| 895 | 1317 | |
| 1318 | [[package]] | |
| 1319 | name = "unicode-width" | |
| 1320 | version = "0.2.2" | |
| 1321 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1322 | checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" | |
| 1323 | ||
| 896 | 1324 | [[package]] |
| 897 | 1325 | name = "unicode-xid" |
| 898 | 1326 | version = "0.2.6" |
| 899 | 1327 | source = "registry+https://github.com/rust-lang/crates.io-index" |
| 900 | 1328 | checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" |
| 901 | 1329 | |
| 1330 | [[package]] | |
| 1331 | name = "unit-prefix" | |
| 1332 | version = "0.5.2" | |
| 1333 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1334 | checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" | |
| 1335 | ||
| 902 | 1336 | [[package]] |
| 903 | 1337 | name = "uuid" |
| 904 | 1338 | version = "1.23.1" |
| ... | ... | @@ -910,6 +1344,12 @@ dependencies = [ |
| 910 | 1344 | "wasm-bindgen", |
| 911 | 1345 | ] |
| 912 | 1346 | |
| 1347 | [[package]] | |
| 1348 | name = "valuable" | |
| 1349 | version = "0.1.1" | |
| 1350 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1351 | checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" | |
| 1352 | ||
| 913 | 1353 | [[package]] |
| 914 | 1354 | name = "wasi" |
| 915 | 1355 | version = "0.11.1+wasi-snapshot-preview1" |
| ... | ... | @@ -1013,6 +1453,16 @@ dependencies = [ |
| 1013 | 1453 | "semver", |
| 1014 | 1454 | ] |
| 1015 | 1455 | |
| 1456 | [[package]] | |
| 1457 | name = "web-time" | |
| 1458 | version = "1.1.0" | |
| 1459 | source = "registry+https://github.com/rust-lang/crates.io-index" | |
| 1460 | checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" | |
| 1461 | dependencies = [ | |
| 1462 | "js-sys", | |
| 1463 | "wasm-bindgen", | |
| 1464 | ] | |
| 1465 | ||
| 1016 | 1466 | [[package]] |
| 1017 | 1467 | name = "windows" |
| 1018 | 1468 | version = "0.61.3" |
src/tools/miri/priroda/Cargo.toml+9| ... | ... | @@ -10,10 +10,19 @@ edition = "2024" |
| 10 | 10 | [[bin]] |
| 11 | 11 | name = "priroda" |
| 12 | 12 | path = "src/main.rs" |
| 13 | test = false # Disable unit tests to avoid errors with --bless | |
| 13 | 14 | doctest = false # and no doc tests |
| 14 | 15 | |
| 16 | [[test]] | |
| 17 | name = "cli" | |
| 18 | harness = false | |
| 19 | ||
| 15 | 20 | [dependencies] |
| 16 | 21 | miri = { path = ".." } |
| 17 | 22 | |
| 18 | 23 | [package.metadata.rust-analyzer] |
| 19 | 24 | rustc_private = true |
| 25 | ||
| 26 | [dev-dependencies] | |
| 27 | ui_test = "0.30.2" | |
| 28 | regex = "1.5.5" |
src/tools/miri/priroda/README.md+43-6| ... | ... | @@ -1,13 +1,13 @@ |
| 1 | 1 | # Priroda |
| 2 | 2 | |
| 3 | Priroda is a step-through debugger for Rust programs running under | |
| 4 | Miri. | |
| 3 | Priroda is a step-through debugger for Rust programs running under Miri. | |
| 5 | 4 | |
| 6 | 5 | Current focus: |
| 7 | 6 | |
| 8 | 7 | - simple CLI prototype |
| 9 | 8 | - single-threaded stepping with Miri's interpreter |
| 10 | - commands: empty Enter, `s`, or `step` | |
| 9 | - source-location output after stepping | |
| 10 | - source-location breakpoint prototype | |
| 11 | 11 | |
| 12 | 12 | ## Setup |
| 13 | 13 | |
| ... | ... | @@ -28,10 +28,47 @@ export MIRI_SYSROOT="$(cargo +miri miri setup --print-sysroot)" |
| 28 | 28 | |
| 29 | 29 | ## Run |
| 30 | 30 | |
| 31 | Priroda currently reads `MIRI_SYSROOT` directly. After setup: | |
| 31 | Priroda currently reads `MIRI_SYSROOT` directly. After setup, run Priroda | |
| 32 | from `miri/priroda/`: | |
| 32 | 33 | |
| 33 | 34 | ```sh |
| 34 | cargo run -p priroda -- tests/pass/empty_main.rs | |
| 35 | cargo run -- ../tests/pass/empty_main.rs | |
| 35 | 36 | ``` |
| 36 | 37 | |
| 37 | At the prompt, press Enter or type `s` / `step`. | |
| 38 | ## Test | |
| 39 | ||
| 40 | Priroda's CLI tests also need `MIRI_SYSROOT`. Run them from `miri/priroda/`: | |
| 41 | ||
| 42 | ```sh | |
| 43 | cargo test | |
| 44 | ``` | |
| 45 | ||
| 46 | If the CLI tests fail due to mismatched output, you can update the expected output files by running the tests with the `--bless` flag: | |
| 47 | ||
| 48 | ```sh | |
| 49 | cargo test -- --bless | |
| 50 | ``` | |
| 51 | ||
| 52 | or | |
| 53 | ||
| 54 | ```sh | |
| 55 | RUSTC_BLESS=1 cargo test | |
| 56 | ``` | |
| 57 | ||
| 58 | ## Commands | |
| 59 | ||
| 60 | | Command | Description | | |
| 61 | |---|---| | |
| 62 | | Enter, `s`, `step` | Execute one Miri interpreter step. | | |
| 63 | | `c`, `continue` | Continue until the program finishes or reaches a breakpoint. | | |
| 64 | | `b <path>:<line>`, `break <path>:<line>` | Add a source-location breakpoint. | | |
| 65 | | `q`, `quit` | Exit Priroda. | | |
| 66 | ||
| 67 | EOF also exits Priroda cleanly. | |
| 68 | ||
| 69 | Example: | |
| 70 | ||
| 71 | ```text | |
| 72 | (priroda) break tests/pass/empty_main.rs:3 | |
| 73 | (priroda) continue | |
| 74 | ``` |
src/tools/miri/priroda/src/main.rs+291-37| ... | ... | @@ -10,16 +10,23 @@ extern crate rustc_interface; |
| 10 | 10 | extern crate rustc_log; |
| 11 | 11 | extern crate rustc_middle; |
| 12 | 12 | extern crate rustc_session; |
| 13 | extern crate rustc_span; | |
| 13 | 14 | |
| 15 | use std::collections::{HashMap, HashSet}; | |
| 14 | 16 | use std::io::{self, Write}; |
| 17 | use std::path::PathBuf; | |
| 15 | 18 | |
| 16 | 19 | use miri::*; |
| 17 | 20 | use rustc_driver::Compilation; |
| 18 | 21 | use rustc_hir::attrs::CrateType; |
| 19 | 22 | use rustc_interface::interface; |
| 23 | use rustc_middle::mir; | |
| 20 | 24 | use rustc_middle::ty::TyCtxt; |
| 21 | 25 | use rustc_session::EarlyDiagCtxt; |
| 22 | 26 | use rustc_session::config::ErrorOutputType; |
| 27 | use rustc_span::Span; | |
| 28 | use rustc_span::source_map::SourceMap; | |
| 29 | ||
| 23 | 30 | fn find_sysroot() -> String { |
| 24 | 31 | std::env::var("MIRI_SYSROOT") |
| 25 | 32 | .expect("set MIRI_SYSROOT to the path from `cargo miri setup --print-sysroot`") |
| ... | ... | @@ -38,13 +45,14 @@ fn main() { |
| 38 | 45 | args.push(sysroot_flag); |
| 39 | 46 | args.push(find_sysroot()); |
| 40 | 47 | } |
| 41 | //TODO: handle the same `-Z` flags that Miri accepts. | |
| 48 | // FIXME: handle the same `-Z` flags that Miri accepts. | |
| 42 | 49 | rustc_driver::run_compiler(&args, &mut PrirodaCompilerCalls::new()); |
| 43 | 50 | } |
| 44 | 51 | |
| 45 | 52 | struct PrirodaCompilerCalls; |
| 46 | 53 | |
| 47 | 54 | impl PrirodaCompilerCalls { |
| 55 | // FIXME: remove this constructor if PrirodaCompilerCalls remains a unit struct. | |
| 48 | 56 | fn new() -> Self { |
| 49 | 57 | Self |
| 50 | 58 | } |
| ... | ... | @@ -56,20 +64,25 @@ impl rustc_driver::Callbacks for PrirodaCompilerCalls { |
| 56 | 64 | tcx.dcx().abort_if_errors(); |
| 57 | 65 | |
| 58 | 66 | if !tcx.crate_types().contains(&CrateType::Executable) { |
| 59 | //TODO: support non-bin crates by listing functions and letting users call them with manually entered arguments. | |
| 67 | // FIXME: support non-bin crates by listing functions and letting users call them with manually entered arguments. | |
| 60 | 68 | tcx.dcx().fatal("priroda only makes sense on bin crates"); |
| 61 | 69 | } |
| 62 | 70 | |
| 63 | 71 | let ecx = create_ecx(tcx); |
| 64 | 72 | |
| 65 | 73 | let mut session = PrirodaContext::new(ecx); |
| 66 | let result = run_cli_loop(&mut session); | |
| 74 | let cli = CLI {}; | |
| 75 | let result = cli.run_cli_loop(&mut session); | |
| 67 | 76 | |
| 68 | 77 | match result.report_err() { |
| 69 | 78 | Ok(()) => {} |
| 70 | 79 | Err(err) => |
| 71 | 80 | if let Some((return_code, _leak_check)) = report_result(&session.ecx, err) { |
| 72 | //TODO: print the evaluated program's exit code and return to the debugger prompt instead of exiting Priroda. | |
| 81 | // FIXME: translate Miri termination into a Priroda execution-state enum so | |
| 82 | // the CLI loop can distinguish whole-program exit from individual thread | |
| 83 | // completion, run Miri-equivalent leak checks, print the exit code, and | |
| 84 | // return to the debugger prompt. | |
| 85 | println!("program finished with exit code {return_code}"); | |
| 73 | 86 | if return_code != 0 { |
| 74 | 87 | std::process::exit(return_code); |
| 75 | 88 | } |
| ... | ... | @@ -82,66 +95,307 @@ impl rustc_driver::Callbacks for PrirodaCompilerCalls { |
| 82 | 95 | |
| 83 | 96 | fn create_ecx<'tcx>(tcx: TyCtxt<'tcx>) -> MiriInterpCx<'tcx> { |
| 84 | 97 | let (entry_id, entry_type) = miri::entry_fn(tcx); |
| 98 | // FIXME: share Miri launcher configuration so interpreted programs receive | |
| 99 | // their program name, arguments, environment snapshot, and `MIRI_CWD`. | |
| 85 | 100 | let config = MiriConfig::default(); |
| 101 | // FIXME: report interpreter initialization failures instead of panicking. | |
| 86 | 102 | miri::create_ecx(tcx, entry_id, entry_type, &config, None).unwrap() |
| 87 | 103 | } |
| 88 | 104 | |
| 89 | pub struct PrirodaContext<'tcx> { | |
| 105 | /// Structured source information for frontends. | |
| 106 | struct SourceLocation { | |
| 107 | // storing `span` to use it lazily to compute path. | |
| 108 | span: Span, | |
| 109 | line: usize, | |
| 110 | } | |
| 111 | ||
| 112 | impl SourceLocation { | |
| 113 | fn local_path(&self, source_map: &SourceMap) -> Option<PathBuf> { | |
| 114 | let loc = source_map.lookup_char_pos(self.span.lo()); | |
| 115 | loc.file.name.clone().into_local_path().map(normalize_path) | |
| 116 | } | |
| 117 | } | |
| 118 | ||
| 119 | /// Source-level breakpoints indexed by normalized path, then line. | |
| 120 | type BreakpointTable = HashMap<PathBuf, HashSet<usize>>; | |
| 121 | ||
| 122 | /// Owns one interpreter session and its debugger state. | |
| 123 | /// | |
| 124 | /// Frontend rendering should eventually live outside this type. | |
| 125 | struct PrirodaContext<'tcx> { | |
| 90 | 126 | ecx: MiriInterpCx<'tcx>, |
| 127 | breakpoints: BreakpointTable, | |
| 128 | current_location: Option<SourceLocation>, | |
| 129 | last_location: Option<SourceLocation>, | |
| 130 | } | |
| 131 | ||
| 132 | /// Controls when execution returns to the frontend. | |
| 133 | enum ResumeMode { | |
| 134 | /// Stop at the next visible MIR instruction. | |
| 135 | MirInstruction, | |
| 136 | /// Continue until reaching a breakpoint. | |
| 137 | Continue, | |
| 138 | } | |
| 139 | ||
| 140 | /// Describes whether the current MIR instruction should be shown to the user. | |
| 141 | enum InstructionVisibility { | |
| 142 | NoInstruction, | |
| 143 | Hidden, | |
| 144 | Visible, | |
| 145 | } | |
| 146 | ||
| 147 | /// Describes why execution stopped and returned control to the frontend. | |
| 148 | enum StepResult { | |
| 149 | Step, | |
| 150 | Breakpoint, | |
| 151 | } | |
| 152 | ||
| 153 | fn normalize_path(path: PathBuf) -> PathBuf { | |
| 154 | path.canonicalize().unwrap_or(path) | |
| 91 | 155 | } |
| 92 | 156 | |
| 93 | 157 | impl<'tcx> PrirodaContext<'tcx> { |
| 94 | 158 | fn new(ecx: MiriInterpCx<'tcx>) -> Self { |
| 95 | Self { ecx } | |
| 159 | Self { ecx, breakpoints: HashMap::new(), current_location: None, last_location: None } | |
| 160 | } | |
| 161 | ||
| 162 | /// Step to the next visible MIR instruction. | |
| 163 | fn step(&mut self) -> InterpResult<'tcx, StepResult> { | |
| 164 | self.resume(ResumeMode::MirInstruction) | |
| 96 | 165 | } |
| 97 | 166 | |
| 98 | // TODO: return a StepResult enum once we distinguish breakpoint stops, | |
| 99 | // program exit, and other debugger states. | |
| 100 | pub fn step(&mut self) -> InterpResult<'tcx> { | |
| 101 | self.ecx.miri_step() | |
| 167 | /// Continue execution until reaching a breakpoint or propagating termination. | |
| 168 | fn continue_execution(&mut self) -> InterpResult<'tcx, StepResult> { | |
| 169 | self.resume(ResumeMode::Continue) | |
| 102 | 170 | } |
| 103 | 171 | |
| 104 | pub fn print_location(&self) { | |
| 172 | fn set_breakpoint(&mut self, path: PathBuf, line: usize) -> BreakpointSetResult { | |
| 173 | // FIXME: validate breakpoints here so every frontend gets the same behavior. | |
| 174 | // Reject empty paths, missing files, directories, and line 0. Decide whether | |
| 175 | // out-of-range lines should be rejected or kept as pending breakpoints. | |
| 176 | // Report duplicate registrations separately. | |
| 177 | ||
| 178 | let path = normalize_path(path); | |
| 179 | match self.breakpoints.entry(path.clone()).or_default().insert(line) { | |
| 180 | true => BreakpointSetResult::Added(path, line), | |
| 181 | false => BreakpointSetResult::Duplicate, | |
| 182 | } | |
| 183 | } | |
| 184 | ||
| 185 | /// Advance execution until the selected resume mode reaches a stopping point. | |
| 186 | fn resume(&mut self, mode: ResumeMode) -> InterpResult<'tcx, StepResult> { | |
| 187 | loop { | |
| 188 | self.advance()?; | |
| 189 | ||
| 190 | // An explicit breakpoint should stop execution even when the current | |
| 191 | // MIR instruction would normally be hidden during manual stepping. | |
| 192 | if self.is_at_breakpoint() { | |
| 193 | return interp_ok(StepResult::Breakpoint); | |
| 194 | } | |
| 195 | ||
| 196 | match mode { | |
| 197 | ResumeMode::MirInstruction | |
| 198 | if matches!( | |
| 199 | self.current_instruction_visibility(), | |
| 200 | InstructionVisibility::Visible | |
| 201 | ) => | |
| 202 | { | |
| 203 | return interp_ok(StepResult::Step); | |
| 204 | } | |
| 205 | ResumeMode::MirInstruction | ResumeMode::Continue => {} | |
| 206 | } | |
| 207 | } | |
| 208 | } | |
| 209 | ||
| 210 | /// Advance Miri by one interpreter-loop transition. | |
| 211 | fn advance(&mut self) -> InterpResult<'tcx> { | |
| 212 | // FIXME: use a Miri-owned scheduler-aware debugger step API before | |
| 213 | // claiming support for multi-threaded interpreted programs. | |
| 214 | ||
| 215 | // State inspection should happen only after a successful step. | |
| 216 | self.ecx.miri_step()?; | |
| 217 | self.last_location = self.current_location.take(); | |
| 218 | self.current_location = self.resolve_current_location(); | |
| 219 | interp_ok(()) | |
| 220 | } | |
| 221 | ||
| 222 | fn current_instruction_visibility(&self) -> InstructionVisibility { | |
| 223 | // If the active thread has no stack frame, there is no MIR instruction to show. | |
| 224 | let Some(frame) = self.ecx.active_thread_stack().last() else { | |
| 225 | return InstructionVisibility::NoInstruction; | |
| 226 | }; | |
| 227 | ||
| 228 | // `Right(span)` means the frame has source context but no precise MIR program-counter location. | |
| 229 | let Either::Left(location) = frame.current_loc() else { | |
| 230 | return InstructionVisibility::NoInstruction; | |
| 231 | }; | |
| 232 | ||
| 233 | let basic_block = &frame.body().basic_blocks[location.block]; | |
| 234 | ||
| 235 | // `statement_index == statements.len()` points at the block terminator. | |
| 236 | // Terminators affect control flow, so they are always visible. | |
| 237 | let Some(statement) = basic_block.statements.get(location.statement_index) else { | |
| 238 | return InstructionVisibility::Visible; | |
| 239 | }; | |
| 240 | ||
| 241 | // Hide bookkeeping-only MIR statements during manual stepping. | |
| 242 | match statement.kind { | |
| 243 | mir::StatementKind::StorageLive(_) | |
| 244 | | mir::StatementKind::StorageDead(_) | |
| 245 | | mir::StatementKind::Nop => InstructionVisibility::Hidden, | |
| 246 | _ => InstructionVisibility::Visible, | |
| 247 | } | |
| 248 | } | |
| 249 | ||
| 250 | fn is_at_breakpoint(&self) -> bool { | |
| 251 | // FIXME: avoid repeated stops when one source line maps to multiple MIR statements. | |
| 252 | let Some(location) = &self.current_location else { | |
| 253 | return false; | |
| 254 | }; | |
| 255 | ||
| 256 | let source_map = self.ecx.tcx.sess.source_map(); | |
| 257 | let Some(path) = &location.local_path(source_map) else { | |
| 258 | return false; | |
| 259 | }; | |
| 260 | ||
| 261 | let lines = match self.breakpoints.get(path) { | |
| 262 | Some(lines) => lines, | |
| 263 | None => return false, | |
| 264 | }; | |
| 265 | lines.contains(&location.line) | |
| 266 | } | |
| 267 | ||
| 268 | fn resolve_current_location(&self) -> Option<SourceLocation> { | |
| 269 | // FIXME: resolve macro-backed lines such as `println!` and `assert_eq!` | |
| 270 | // through `span.source_callsite()` before matching breakpoints. | |
| 105 | 271 | let span = self.ecx.machine.current_user_relevant_span(); |
| 106 | let location = self.ecx.tcx.sess.source_map().span_to_diagnostic_string(span); | |
| 107 | // TODO: skip noisy std/runtime spans and avoid printing `no-location` | |
| 108 | // once the basic command loop is solid. | |
| 109 | println!("{location}"); | |
| 110 | io::stdout().flush().unwrap(); | |
| 272 | if span.is_dummy() { | |
| 273 | return None; | |
| 274 | } | |
| 275 | ||
| 276 | let source_map = self.ecx.tcx.sess.source_map(); | |
| 277 | let loc = source_map.lookup_char_pos(span.lo()); | |
| 278 | ||
| 279 | Some(SourceLocation { span: span, line: loc.line }) | |
| 111 | 280 | } |
| 112 | fn run_command(&mut self, command: SessionCommand) -> InterpResult<'tcx> { | |
| 281 | ||
| 282 | fn run_command(&mut self, command: DebuggerCommand) -> InterpResult<'tcx, CommandResult> { | |
| 113 | 283 | match command { |
| 114 | SessionCommand::Step => self.step(), | |
| 284 | DebuggerCommand::Step => self.step().map(CommandResult::ExecutionStopped), | |
| 285 | DebuggerCommand::Continue => | |
| 286 | self.continue_execution().map(CommandResult::ExecutionStopped), | |
| 287 | DebuggerCommand::Breakpoint(path, line) => | |
| 288 | interp_ok(CommandResult::BreakpointResult(self.set_breakpoint(path, line))), | |
| 289 | DebuggerCommand::TerminateSession => interp_ok(CommandResult::TerminateSession), | |
| 115 | 290 | } |
| 116 | 291 | } |
| 117 | 292 | } |
| 118 | 293 | |
| 119 | enum SessionCommand { | |
| 294 | enum DebuggerCommand { | |
| 120 | 295 | Step, |
| 296 | TerminateSession, | |
| 297 | Continue, | |
| 298 | Breakpoint(PathBuf, usize), | |
| 121 | 299 | } |
| 122 | 300 | |
| 123 | fn parse_command(input: &str) -> Option<SessionCommand> { | |
| 124 | match input.trim() { | |
| 125 | "" | "s" | "step" => Some(SessionCommand::Step), | |
| 126 | _ => None, | |
| 127 | } | |
| 301 | enum BreakpointSetResult { | |
| 302 | Added(PathBuf, usize), | |
| 303 | Duplicate, | |
| 304 | // FIXME: add pending breakpoint support later if needed. | |
| 128 | 305 | } |
| 129 | 306 | |
| 130 | fn run_cli_loop<'tcx>(session: &mut PrirodaContext<'tcx>) -> InterpResult<'tcx> { | |
| 131 | loop { | |
| 132 | print!("(priroda) "); | |
| 133 | io::stdout().flush().unwrap(); | |
| 307 | enum CommandResult { | |
| 308 | ExecutionStopped(StepResult), | |
| 309 | BreakpointResult(BreakpointSetResult), | |
| 310 | // FIXME: distinguish terminating the debugger session from disconnecting a | |
| 311 | // frontend and terminating the interpreted program once multiple frontends exist. | |
| 312 | TerminateSession, | |
| 313 | } | |
| 314 | ||
| 315 | struct CLI; | |
| 316 | ||
| 317 | impl CLI { | |
| 318 | pub fn run_cli_loop<'tcx>(&self, session: &mut PrirodaContext<'tcx>) -> InterpResult<'tcx> { | |
| 319 | loop { | |
| 320 | print!("(priroda) "); | |
| 321 | io::stdout().flush().unwrap(); | |
| 322 | ||
| 323 | let mut input = String::new(); | |
| 324 | let bytes_read = io::stdin().read_line(&mut input).unwrap(); | |
| 134 | 325 | |
| 135 | let mut input = String::new(); | |
| 136 | // TODO: handle EOF explicitly so scripted input can stop the CLI instead | |
| 137 | // of being treated like an empty Enter step. | |
| 138 | io::stdin().read_line(&mut input).unwrap(); | |
| 326 | if bytes_read == 0 { | |
| 327 | println!("stdin closed, stopping"); | |
| 328 | return interp_ok(()); | |
| 329 | } | |
| 139 | 330 | |
| 140 | if let Some(command) = parse_command(&input) { | |
| 141 | session.run_command(command)?; | |
| 142 | session.print_location(); | |
| 143 | } else { | |
| 144 | println!("no command"); | |
| 331 | if let Some(command) = self.parse_command(&input) { | |
| 332 | match session.run_command(command)? { | |
| 333 | CommandResult::ExecutionStopped(result) => { | |
| 334 | if matches!(result, StepResult::Breakpoint) { | |
| 335 | println!("Hit breakpoint"); | |
| 336 | } | |
| 337 | self.print_location(&session); | |
| 338 | } | |
| 339 | CommandResult::BreakpointResult(res) => | |
| 340 | match res { | |
| 341 | BreakpointSetResult::Added(path, line) => | |
| 342 | println!("breakpoint added: {}:{}", path.display(), line), | |
| 343 | ||
| 344 | BreakpointSetResult::Duplicate => println!("Duplicate breakpoint"), | |
| 345 | }, | |
| 346 | CommandResult::TerminateSession => { | |
| 347 | println!("quitting"); | |
| 348 | return interp_ok(()); | |
| 349 | } | |
| 350 | } | |
| 351 | } else { | |
| 352 | println!("no command"); | |
| 353 | } | |
| 354 | ||
| 355 | io::stdout().flush().unwrap(); | |
| 356 | } | |
| 357 | } | |
| 358 | ||
| 359 | fn parse_command(&self, input: &str) -> Option<DebuggerCommand> { | |
| 360 | // TODO: look at the Spanned crate for how to easily produce errors in | |
| 361 | // rustc's style while manually parsing text input. | |
| 362 | // FIXME: we need to distinguish malformed input from the unknown commands by returning useful | |
| 363 | // command error that describes if it malformed or non exist command | |
| 364 | let input = input.trim(); | |
| 365 | let mut parts = input.splitn(2, char::is_whitespace); | |
| 366 | let command = parts.next().unwrap_or(""); | |
| 367 | let args = parts.next().unwrap_or("").trim(); | |
| 368 | ||
| 369 | match command { | |
| 370 | "" | "s" | "step" => Some(DebuggerCommand::Step), | |
| 371 | "q" | "quit" => Some(DebuggerCommand::TerminateSession), | |
| 372 | "c" | "continue" => Some(DebuggerCommand::Continue), | |
| 373 | "b" | "break" => self.parse_breakpoint(args), | |
| 374 | _ => None, | |
| 145 | 375 | } |
| 146 | 376 | } |
| 377 | ||
| 378 | fn print_location(&self, session: &PrirodaContext) { | |
| 379 | let source_map = session.ecx.tcx.sess.source_map(); | |
| 380 | match &session.current_location { | |
| 381 | Some(location) => | |
| 382 | if let Some(path) = location.local_path(source_map) { | |
| 383 | println!("{}:{}", path.display(), location.line); | |
| 384 | } else { | |
| 385 | println!("{}", source_map.span_to_diagnostic_string(location.span)); | |
| 386 | }, | |
| 387 | None => println!("no-location"), | |
| 388 | } | |
| 389 | io::stdout().flush().unwrap(); | |
| 390 | } | |
| 391 | ||
| 392 | fn parse_breakpoint(&self, input: &str) -> Option<DebuggerCommand> { | |
| 393 | // FIXME: return a typed CommandError so malformed breakpoint input is | |
| 394 | // distinguishable from an unknown command. Semantic validation belongs | |
| 395 | // in PrirodaContext::set_breakpoint so non-CLI frontends cannot bypass it. | |
| 396 | let (path, line) = input.rsplit_once(':')?; | |
| 397 | let line = line.parse().ok()?; | |
| 398 | ||
| 399 | Some(DebuggerCommand::Breakpoint(PathBuf::from(path), line)) | |
| 400 | } | |
| 147 | 401 | } |
src/tools/miri/priroda/tests/cli.rs created+58| ... | ... | @@ -0,0 +1,58 @@ |
| 1 | use std::env; | |
| 2 | use std::path::PathBuf; | |
| 3 | use std::process::Command; | |
| 4 | ||
| 5 | use regex::bytes::Regex; | |
| 6 | use ui_test::spanned::Spanned; | |
| 7 | use ui_test::status_emitter::StatusEmitter; | |
| 8 | use ui_test::{CommandBuilder, Config, default_file_filter, run_tests_generic}; | |
| 9 | ||
| 10 | fn main() -> Result<(), Box<dyn std::error::Error>> { | |
| 11 | let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); | |
| 12 | let miri_dir = manifest_dir.parent().unwrap(); | |
| 13 | ||
| 14 | let rustc_sysroot = Command::new("rustc").arg("--print").arg("sysroot").output()?; | |
| 15 | let rustc_sysroot = String::from_utf8(rustc_sysroot.stdout)?.trim().to_owned(); | |
| 16 | ||
| 17 | let mut program = CommandBuilder::rustc(); | |
| 18 | program.program = PathBuf::from(env!("CARGO_BIN_EXE_priroda")); | |
| 19 | ||
| 20 | // Remove logging env vars that might leak into stderr | |
| 21 | program.envs.push(("RUSTC_LOG".into(), None)); | |
| 22 | program.envs.push(("RUST_LOG".into(), None)); | |
| 23 | ||
| 24 | let mut config = Config { | |
| 25 | program, | |
| 26 | out_dir: PathBuf::from(env!("CARGO_TARGET_TMPDIR")).join("priroda_ui"), | |
| 27 | ..Config::rustc("tests/ui") | |
| 28 | }; | |
| 29 | ||
| 30 | // Replace the dynamic paths in the actual stdout with the stable placeholders | |
| 31 | let manifest_dir_regex = | |
| 32 | Regex::new(&regex::escape(&manifest_dir.display().to_string())).unwrap(); | |
| 33 | let miri_dir_regex = Regex::new(&regex::escape(&miri_dir.display().to_string())).unwrap(); | |
| 34 | let rustc_sysroot_regex = Regex::new(&regex::escape(&rustc_sysroot)).unwrap(); | |
| 35 | ||
| 36 | config.comment_defaults.base().normalize_stdout.extend([ | |
| 37 | (manifest_dir_regex.into(), b"{MANIFEST_DIR}".to_vec()), | |
| 38 | (miri_dir_regex.into(), b"{MIRI_DIR}".to_vec()), | |
| 39 | (rustc_sysroot_regex.into(), b"{RUSTC_SYSROOT}".to_vec()), | |
| 40 | ]); | |
| 41 | ||
| 42 | // Priroda CLI tests do not currently require annotation comments in the test files | |
| 43 | config.comment_defaults.base().exit_status = Spanned::dummy(0).into(); | |
| 44 | config.comment_defaults.base().require_annotations = Spanned::dummy(false).into(); | |
| 45 | ||
| 46 | let mut args = ui_test::Args::test()?; | |
| 47 | args.bless |= env::var_os("RUSTC_BLESS").is_some_and(|v| v != "0"); | |
| 48 | config.with_args(&args); | |
| 49 | ||
| 50 | run_tests_generic( | |
| 51 | vec![config], | |
| 52 | default_file_filter, | |
| 53 | |_, _| {}, | |
| 54 | Box::<dyn StatusEmitter>::from(args.format), | |
| 55 | )?; | |
| 56 | ||
| 57 | Ok(()) | |
| 58 | } |
src/tools/miri/priroda/tests/ui/continue_finishes_program.rs created+4| ... | ... | @@ -0,0 +1,4 @@ |
| 1 | // Verifies continue can drive the interpreted program to normal completion. | |
| 2 | // This may look trivial, but a bunch of code runs in std before | |
| 3 | // `main` is called, so we are ensuring that that all works. | |
| 4 | fn main() {} |
src/tools/miri/priroda/tests/ui/continue_finishes_program.stdin created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | continue |
src/tools/miri/priroda/tests/ui/continue_finishes_program.stdout created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | (priroda) program finished with exit code 0 |
src/tools/miri/priroda/tests/ui/continue_hits_breakpoint.rs created+7| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | // Verifies continue stops when execution reaches a registered source-location | |
| 2 | // breakpoint. | |
| 3 | // This may look trivial, but a bunch of code runs in std before | |
| 4 | // `main` is called, so we are ensuring that that all works. | |
| 5 | fn main() { | |
| 6 | let _value = 0; | |
| 7 | } |
src/tools/miri/priroda/tests/ui/continue_hits_breakpoint.stdin created+3| ... | ... | @@ -0,0 +1,3 @@ |
| 1 | break tests/ui/continue_hits_breakpoint.rs:6 | |
| 2 | continue | |
| 3 | quit |
src/tools/miri/priroda/tests/ui/continue_hits_breakpoint.stdout created+4| ... | ... | @@ -0,0 +1,4 @@ |
| 1 | (priroda) breakpoint added: {MANIFEST_DIR}/tests/ui/continue_hits_breakpoint.rs:6 | |
| 2 | (priroda) Hit breakpoint | |
| 3 | {MANIFEST_DIR}/tests/ui/continue_hits_breakpoint.rs:6 | |
| 4 | (priroda) quitting |
src/tools/miri/priroda/tests/ui/duplicate_breakpoint.rs created+4| ... | ... | @@ -0,0 +1,4 @@ |
| 1 | // Verifies breakpoint aliases and duplicate detection before execution starts. | |
| 2 | // This may look trivial, but a bunch of code runs in std before | |
| 3 | // `main` is called, so we are ensuring that that all works. | |
| 4 | fn main() {} |
src/tools/miri/priroda/tests/ui/duplicate_breakpoint.stdin created+3| ... | ... | @@ -0,0 +1,3 @@ |
| 1 | break nowhere.rs:12 | |
| 2 | b nowhere.rs:12 | |
| 3 | quit |
src/tools/miri/priroda/tests/ui/duplicate_breakpoint.stdout created+3| ... | ... | @@ -0,0 +1,3 @@ |
| 1 | (priroda) breakpoint added: nowhere.rs:12 | |
| 2 | (priroda) Duplicate breakpoint | |
| 3 | (priroda) quitting |
src/tools/miri/priroda/tests/ui/empty_main.rs created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | // Verifies Priroda can start on the simplest passing Rust program and accept | |
| 2 | // a scripted `quit` command. | |
| 3 | // This may look trivial, but a bunch of code runs in std before | |
| 4 | // `main` is called, so we are ensuring that that all works. | |
| 5 | fn main() {} |
src/tools/miri/priroda/tests/ui/empty_main.stdin created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | quit |
src/tools/miri/priroda/tests/ui/empty_main.stdout created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | (priroda) quitting |
src/tools/miri/priroda/tests/ui/eof_exits_cleanly.rs created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | // Verifies EOF exits the debugger loop cleanly without requiring an explicit | |
| 2 | // quit command. | |
| 3 | // This may look trivial, but a bunch of code runs in std before | |
| 4 | // `main` is called, so we are ensuring that that all works. | |
| 5 | fn main() {} |
src/tools/miri/priroda/tests/ui/eof_exits_cleanly.stdinsrc/tools/miri/priroda/tests/ui/eof_exits_cleanly.stdout created+1| ... | ... | @@ -0,0 +1 @@ |
| 1 | (priroda) stdin closed, stopping |
src/tools/miri/priroda/tests/ui/invalid_commands.rs created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | // Verifies unknown commands and malformed breakpoints are rejected without | |
| 2 | // mutating debugger state. | |
| 3 | // This may look trivial, but a bunch of code runs in std before | |
| 4 | // `main` is called, so we are ensuring that that all works. | |
| 5 | fn main() {} |
src/tools/miri/priroda/tests/ui/invalid_commands.stdin created+4| ... | ... | @@ -0,0 +1,4 @@ |
| 1 | wat | |
| 2 | break | |
| 3 | break nowhere.rs:not-a-line | |
| 4 | quit |
src/tools/miri/priroda/tests/ui/invalid_commands.stdout created+4| ... | ... | @@ -0,0 +1,4 @@ |
| 1 | (priroda) no command | |
| 2 | (priroda) no command | |
| 3 | (priroda) no command | |
| 4 | (priroda) quitting |
src/tools/miri/priroda/tests/ui/repeated_same_line_breakpoint.rs created+7| ... | ... | @@ -0,0 +1,7 @@ |
| 1 | // Documents the current repeated-stop behavior when multiple MIR locations map | |
| 2 | // to the same source breakpoint line. | |
| 3 | // This may look trivial, but a bunch of code runs in std before | |
| 4 | // `main` is called, so we are ensuring that that all works. | |
| 5 | fn main() { | |
| 6 | let _value = 0; | |
| 7 | } |
src/tools/miri/priroda/tests/ui/repeated_same_line_breakpoint.stdin created+4| ... | ... | @@ -0,0 +1,4 @@ |
| 1 | break tests/ui/repeated_same_line_breakpoint.rs:6 | |
| 2 | continue | |
| 3 | continue | |
| 4 | quit |
src/tools/miri/priroda/tests/ui/repeated_same_line_breakpoint.stdout created+6| ... | ... | @@ -0,0 +1,6 @@ |
| 1 | (priroda) breakpoint added: {MANIFEST_DIR}/tests/ui/repeated_same_line_breakpoint.rs:6 | |
| 2 | (priroda) Hit breakpoint | |
| 3 | {MANIFEST_DIR}/tests/ui/repeated_same_line_breakpoint.rs:6 | |
| 4 | (priroda) Hit breakpoint | |
| 5 | {MANIFEST_DIR}/tests/ui/repeated_same_line_breakpoint.rs:6 | |
| 6 | (priroda) quitting |
src/tools/miri/priroda/tests/ui/source_step_changes_line.rs created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | // Verifies source-level stepping advances until the displayed source location | |
| 2 | // changes. | |
| 3 | // This may look trivial, but a bunch of code runs in std before | |
| 4 | // `main` is called, so we are ensuring that that all works. | |
| 5 | fn main() {} |
src/tools/miri/priroda/tests/ui/source_step_changes_line.stdin created+3| ... | ... | @@ -0,0 +1,3 @@ |
| 1 | s | |
| 2 | step | |
| 3 | quit |
src/tools/miri/priroda/tests/ui/source_step_changes_line.stdout created+3| ... | ... | @@ -0,0 +1,3 @@ |
| 1 | (priroda) {RUSTC_SYSROOT}/lib/rustlib/src/rust/library/std/src/rt.rs:206 | |
| 2 | (priroda) {RUSTC_SYSROOT}/lib/rustlib/src/rust/library/std/src/rt.rs:206 | |
| 3 | (priroda) quitting |
src/tools/miri/priroda/tests/ui/step_aliases.rs created+5| ... | ... | @@ -0,0 +1,5 @@ |
| 1 | // Verifies every current spelling of MIR-instruction stepping advances | |
| 2 | // execution and reports a location. | |
| 3 | // This may look trivial, but a bunch of code runs in std before | |
| 4 | // `main` is called, so we are ensuring that that all works. | |
| 5 | fn main() {} |
src/tools/miri/priroda/tests/ui/step_aliases.stdin created+4| ... | ... | @@ -0,0 +1,4 @@ |
| 1 | s | |
| 2 | step | |
| 3 | ||
| 4 | quit |
src/tools/miri/priroda/tests/ui/step_aliases.stdout created+4| ... | ... | @@ -0,0 +1,4 @@ |
| 1 | (priroda) {RUSTC_SYSROOT}/lib/rustlib/src/rust/library/std/src/rt.rs:206 | |
| 2 | (priroda) {RUSTC_SYSROOT}/lib/rustlib/src/rust/library/std/src/rt.rs:206 | |
| 3 | (priroda) {RUSTC_SYSROOT}/lib/rustlib/src/rust/library/std/src/rt.rs:206 | |
| 4 | (priroda) quitting |
src/tools/miri/rust-version+1-1| ... | ... | @@ -1 +1 @@ |
| 1 | bef8e620f19adbfd1530e916ab8caa296ef9c3ee | |
| 1 | 029c9e18dd1f4668e1d42bb187c1c263dfe20093 |
src/tools/miri/src/shims/loongarch.rs+11-4| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | use rustc_abi::CanonAbi; | |
| 1 | use rustc_abi::{CanonAbi, Size}; | |
| 2 | 2 | use rustc_middle::ty::Ty; |
| 3 | 3 | use rustc_span::Symbol; |
| 4 | 4 | use rustc_target::callconv::FnAbi; |
| ... | ... | @@ -55,10 +55,17 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 55 | 55 | // b/h/w variants and i64 for the d variant, per the LLVM intrinsic |
| 56 | 56 | // definitions. |
| 57 | 57 | // https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/IR/IntrinsicsLoongArch.td |
| 58 | // If the higher bits are non-zero, `compute_crc32` will panic. We should probably | |
| 59 | // raise a proper error instead, but outside stdarch nobody can trigger this anyway. | |
| 58 | // LoongArch CRC b/h/w instructions ignore any bits above `bit_size`. | |
| 59 | // https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#crc-check-instructions | |
| 60 | // Miri's `compute_crc32` requires all higher bits to be zero and may | |
| 61 | // panic otherwise, so we explicitly mask them off here to reproduce the | |
| 62 | // hardware behavior. | |
| 60 | 63 | let crc = crc.to_u32()?; |
| 61 | let data = if bit_size == 64 { data.to_u64()? } else { u64::from(data.to_u32()?) }; | |
| 64 | let data = if bit_size == 64 { | |
| 65 | data.to_u64()? | |
| 66 | } else { | |
| 67 | Size::from_bits(bit_size).truncate(data.to_u32()?.into()).try_into().unwrap() | |
| 68 | }; | |
| 62 | 69 | |
| 63 | 70 | let result = compute_crc32(crc, data, bit_size, polynomial); |
| 64 | 71 | this.write_scalar(Scalar::from_u32(result), dest)?; |
src/tools/miri/src/shims/tls.rs+18-16| ... | ... | @@ -63,19 +63,19 @@ impl<'tcx> Default for TlsData<'tcx> { |
| 63 | 63 | |
| 64 | 64 | impl<'tcx> TlsData<'tcx> { |
| 65 | 65 | /// Generate a new TLS key with the given destructor. |
| 66 | /// `max_size` determines the integer size the key has to fit in. | |
| 66 | /// `key_size` determines the integer size the key has to fit in. | |
| 67 | 67 | #[expect(clippy::arithmetic_side_effects)] |
| 68 | 68 | pub fn create_tls_key( |
| 69 | 69 | &mut self, |
| 70 | 70 | dtor: Option<(ty::Instance<'tcx>, Span)>, |
| 71 | max_size: Size, | |
| 71 | key_size: Size, | |
| 72 | 72 | ) -> InterpResult<'tcx, TlsKey> { |
| 73 | 73 | let new_key = self.next_key; |
| 74 | 74 | self.next_key += 1; |
| 75 | 75 | self.keys.try_insert(new_key, TlsEntry { data: Default::default(), dtor }).unwrap(); |
| 76 | 76 | trace!("New TLS key allocated: {} with dtor {:?}", new_key, dtor); |
| 77 | 77 | |
| 78 | if max_size.bits() < 128 && new_key >= (1u128 << max_size.bits()) { | |
| 78 | if new_key > key_size.unsigned_int_max() { | |
| 79 | 79 | throw_unsup_format!("we ran out of TLS key space"); |
| 80 | 80 | } |
| 81 | 81 | interp_ok(new_key) |
| ... | ... | @@ -267,7 +267,13 @@ impl<'tcx> TlsDtorsState<'tcx> { |
| 267 | 267 | let fls_keys_with_dtors = this.lookup_windows_fls_keys_with_dtors()?; |
| 268 | 268 | |
| 269 | 269 | // And move to the next state, that runs them. |
| 270 | break 'new_state WindowsDtors(RunningWindowsDtorState { last_key: None, remaining_keys: fls_keys_with_dtors }, dtors); | |
| 270 | break 'new_state WindowsDtors( | |
| 271 | RunningWindowsDtorState { | |
| 272 | last_key: None, | |
| 273 | remaining_keys: fls_keys_with_dtors, | |
| 274 | }, | |
| 275 | dtors, | |
| 276 | ); | |
| 271 | 277 | } |
| 272 | 278 | _ => { |
| 273 | 279 | // No TLS dtor support. |
| ... | ... | @@ -453,35 +459,31 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 453 | 459 | // Keys without dtors will not be set to zero. |
| 454 | 460 | // [PflsCallbackFunction's docs]: https://learn.microsoft.com/en-us/windows/win32/api/winnt/nc-winnt-pfls_callback_function |
| 455 | 461 | // [`RtlProcessFlsData`]: https://github.com/wine-mirror/wine/blob/wine-11.0/dlls/ntdll/thread.c#L679 |
| 456 | ||
| 462 | ||
| 457 | 463 | // We are done running the previous key's destructor, so set it's value to zero. |
| 458 | 464 | if let Some(last_key) = state.last_key.take() { |
| 459 | 465 | if let Some(TlsEntry { data, .. }) = this.machine.tls.keys.get_mut(&last_key) { |
| 460 | data.remove(&active_thread); | |
| 466 | data.remove(&active_thread); | |
| 461 | 467 | }; |
| 462 | 468 | } |
| 463 | 469 | |
| 464 | 470 | while let Some(key) = state.remaining_keys.pop_front() { |
| 465 | 471 | // Fetch dtor for this `key`. |
| 466 | 472 | // If the key doesn't have a dtor or does not exist any more, move on to the next key. |
| 467 | let (data, dtor) = match this.machine.tls.keys.get(&key) { | |
| 468 | Some(TlsEntry { data, dtor: Some(dtor) }) => (data, dtor), | |
| 469 | _ => continue, | |
| 473 | let Some(TlsEntry { data, dtor: Some(dtor) }) = this.machine.tls.keys.get(&key) else { | |
| 474 | continue; | |
| 470 | 475 | }; |
| 471 | 476 | |
| 472 | 477 | let (instance, span) = dtor.to_owned(); |
| 473 | ||
| 478 | ||
| 474 | 479 | // If the key has no value in this thread, move on to the next key. |
| 475 | let ptr = match data.get(&active_thread) { | |
| 476 | Some(data_scalar) => *data_scalar, | |
| 477 | None => continue, | |
| 478 | }; | |
| 480 | let Some(&ptr) = data.get(&active_thread) else { continue }; | |
| 479 | 481 | |
| 480 | 482 | assert!( |
| 481 | 483 | ptr.to_target_usize(this).unwrap() != 0, |
| 482 | 484 | "TLS key's value can't be null (should be absent instead)" |
| 483 | 485 | ); |
| 484 | ||
| 486 | ||
| 485 | 487 | trace!("Running TLS dtor {:?} on {:?} at {:?}", instance, ptr, active_thread); |
| 486 | 488 | |
| 487 | 489 | // We'll clear this key's value next time we are called. |
| ... | ... | @@ -497,7 +499,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 497 | 499 | |
| 498 | 500 | return interp_ok(Poll::Pending); |
| 499 | 501 | } |
| 500 | ||
| 502 | ||
| 501 | 503 | // We are done scheduling all the keys. |
| 502 | 504 | interp_ok(Poll::Ready(())) |
| 503 | 505 | } |
src/tools/miri/src/shims/windows/foreign_items.rs+9| ... | ... | @@ -638,6 +638,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 638 | 638 | |
| 639 | 639 | // Thread-local storage |
| 640 | 640 | "TlsAlloc" => { |
| 641 | // FIXME: This does not have a direct test (#3179). | |
| 641 | 642 | // This just creates a key; Windows does not natively support TLS destructors. |
| 642 | 643 | |
| 643 | 644 | // Create key and return it. |
| ... | ... | @@ -651,6 +652,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 651 | 652 | this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?; |
| 652 | 653 | } |
| 653 | 654 | "TlsGetValue" => { |
| 655 | // FIXME: This does not have a direct test (#3179). | |
| 654 | 656 | let [key] = this.check_shim_sig( |
| 655 | 657 | shim_sig!(extern "system" fn(u32) -> *mut _), |
| 656 | 658 | link_name, |
| ... | ... | @@ -663,6 +665,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 663 | 665 | this.write_scalar(ptr, dest)?; |
| 664 | 666 | } |
| 665 | 667 | "TlsSetValue" => { |
| 668 | // FIXME: This does not have a direct test (#3179). | |
| 666 | 669 | let [key, new_ptr] = this.check_shim_sig( |
| 667 | 670 | shim_sig!(extern "system" fn(u32, *mut _) -> winapi::BOOL), |
| 668 | 671 | link_name, |
| ... | ... | @@ -678,6 +681,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 678 | 681 | this.write_int(1, dest)?; |
| 679 | 682 | } |
| 680 | 683 | "TlsFree" => { |
| 684 | // FIXME: This does not have a direct test (#3179). | |
| 681 | 685 | let [key] = this.check_shim_sig( |
| 682 | 686 | shim_sig!(extern "system" fn(u32) -> winapi::BOOL), |
| 683 | 687 | link_name, |
| ... | ... | @@ -693,6 +697,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 693 | 697 | |
| 694 | 698 | // Fiber-local storage - similar to TLS but supports destructors. |
| 695 | 699 | "FlsAlloc" => { |
| 700 | // FIXME: This does not have a direct test (#3179). | |
| 696 | 701 | // Create key and return it. |
| 697 | 702 | let [dtor] = this.check_shim_sig( |
| 698 | 703 | shim_sig!(extern "system" fn(winapi::PFLS_CALLBACK_FUNCTION) -> u32), |
| ... | ... | @@ -716,6 +721,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 716 | 721 | this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?; |
| 717 | 722 | } |
| 718 | 723 | "FlsGetValue" => { |
| 724 | // FIXME: This does not have a direct test (#3179). | |
| 719 | 725 | let [key] = this.check_shim_sig( |
| 720 | 726 | shim_sig!(extern "system" fn(u32) -> *mut _), |
| 721 | 727 | link_name, |
| ... | ... | @@ -728,6 +734,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 728 | 734 | this.write_scalar(ptr, dest)?; |
| 729 | 735 | } |
| 730 | 736 | "FlsSetValue" => { |
| 737 | // FIXME: This does not have a direct test (#3179). | |
| 731 | 738 | let [key, new_ptr] = this.check_shim_sig( |
| 732 | 739 | shim_sig!(extern "system" fn(u32, *mut _) -> winapi::BOOL), |
| 733 | 740 | link_name, |
| ... | ... | @@ -743,6 +750,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 743 | 750 | this.write_int(1, dest)?; |
| 744 | 751 | } |
| 745 | 752 | "FlsFree" => { |
| 753 | // FIXME: This does not have a direct test (#3179). | |
| 746 | 754 | let [key] = this.check_shim_sig( |
| 747 | 755 | shim_sig!(extern "system" fn(u32) -> winapi::BOOL), |
| 748 | 756 | link_name, |
| ... | ... | @@ -763,6 +771,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 763 | 771 | this.write_int(1, dest)?; |
| 764 | 772 | } |
| 765 | 773 | "IsThreadAFiber" => { |
| 774 | // FIXME: This does not have a direct test (#3179). | |
| 766 | 775 | let [] = this.check_shim_sig( |
| 767 | 776 | shim_sig!(extern "system" fn() -> winapi::BOOL), |
| 768 | 777 | link_name, |
src/tools/miri/src/shims/windows/fs.rs+137-90| ... | ... | @@ -157,29 +157,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 157 | 157 | throw_unsup_format!("CreateFileW: Template files are not supported"); |
| 158 | 158 | } |
| 159 | 159 | |
| 160 | // We need to know if the file is a directory to correctly open directory handles. This is | |
| 161 | // racy, but currently the stdlib doesn't appear to offer a better solution. We do later | |
| 162 | // verify that our guess was correct so worst-case, Miri ICEs here. | |
| 163 | // FIXME: retry in a loop if we get an error indicating we got the wrong file type? | |
| 164 | let is_dir = file_name.is_dir(); | |
| 165 | ||
| 166 | // BACKUP_SEMANTICS is how Windows calls the act of opening a directory handle. | |
| 167 | if !attributes.contains(FileAttributes::BACKUP_SEMANTICS) && is_dir { | |
| 168 | this.set_last_error(IoError::WindowsError("ERROR_ACCESS_DENIED"))?; | |
| 169 | return interp_ok(Handle::Invalid); | |
| 170 | } | |
| 171 | ||
| 172 | let desired_read = desired_access & generic_read != 0; | |
| 173 | let desired_write = desired_access & generic_write != 0; | |
| 174 | ||
| 175 | let mut options = fs::OpenOptions::new(); | |
| 176 | if desired_read { | |
| 160 | // Parse desired_access | |
| 161 | let mut desired_read = false; | |
| 162 | if desired_access & generic_read != 0 { | |
| 163 | desired_read = true; | |
| 177 | 164 | desired_access &= !generic_read; |
| 178 | options.read(true); | |
| 179 | 165 | } |
| 180 | if desired_write { | |
| 166 | let mut desired_write = false; | |
| 167 | if desired_access & generic_write != 0 { | |
| 168 | desired_write = true; | |
| 181 | 169 | desired_access &= !generic_write; |
| 182 | options.write(true); | |
| 183 | 170 | } |
| 184 | 171 | |
| 185 | 172 | if desired_access != 0 { |
| ... | ... | @@ -188,90 +175,150 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { |
| 188 | 175 | ); |
| 189 | 176 | } |
| 190 | 177 | |
| 191 | // Per the documentation: | |
| 192 | // If the specified file exists and is writable, the function truncates the file, | |
| 193 | // the function succeeds, and last-error code is set to ERROR_ALREADY_EXISTS. | |
| 194 | // If the specified file does not exist and is a valid path, a new file is created, | |
| 195 | // the function succeeds, and the last-error code is set to zero. | |
| 196 | // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew | |
| 197 | // | |
| 198 | // This is racy, but there doesn't appear to be an std API that both succeeds if a | |
| 199 | // file exists but tells us it isn't new. Either we accept racing one way or another, | |
| 200 | // or we use an iffy heuristic like file creation time. This implementation prefers | |
| 201 | // to fail in the direction of erroring more often. | |
| 202 | if let CreateAlways | OpenAlways = creation_disposition | |
| 203 | && file_name.exists() | |
| 204 | { | |
| 205 | this.set_last_error(IoError::WindowsError("ERROR_ALREADY_EXISTS"))?; | |
| 206 | } | |
| 178 | // We start a retry loop to deal with the `is_dir` and `exists_already` race, see below. | |
| 179 | // We add a retry counter to avoid infinite loops when things go wrong. | |
| 180 | let mut counter = 0u32; | |
| 181 | loop { | |
| 182 | if counter >= 100 { | |
| 183 | panic!( | |
| 184 | "CreateFileW seems stuck in an infinite retry loop. \ | |
| 185 | If you can reproduce this, please file a bug." | |
| 186 | ); | |
| 187 | } | |
| 188 | counter = counter.strict_add(1); | |
| 189 | ||
| 190 | // We need to know if the file is a directory to correctly open directory handles. | |
| 191 | // The standard library only lets us open something as a file or a directory, so | |
| 192 | // we check for that and then retry if we end up with the wrong thing. | |
| 193 | let is_dir = file_name.is_dir(); | |
| 207 | 194 | |
| 208 | let handle = if is_dir { | |
| 209 | // Open this as a directory. | |
| 210 | // FIXME: shouldn't we check `creation_disposition` here? | |
| 211 | Dir::open(&file_name).map(|dir| { | |
| 195 | // BACKUP_SEMANTICS is how Windows calls the act of opening a directory handle. | |
| 196 | if !attributes.contains(FileAttributes::BACKUP_SEMANTICS) && is_dir { | |
| 197 | this.set_last_error(IoError::WindowsError("ERROR_ACCESS_DENIED"))?; | |
| 198 | return interp_ok(Handle::Invalid); | |
| 199 | } | |
| 200 | ||
| 201 | if is_dir { | |
| 202 | // Open this as a directory. | |
| 203 | // FIXME: shouldn't we check `creation_disposition` here? We do know that it already | |
| 204 | // exists. | |
| 205 | let dir = match Dir::open(&file_name) { | |
| 206 | Ok(dir) => dir, | |
| 207 | Err(e) => { | |
| 208 | if e.kind() == io::ErrorKind::NotADirectory { | |
| 209 | // This changed from a directory to a file. Retry. | |
| 210 | continue; | |
| 211 | } | |
| 212 | this.set_last_error(e)?; | |
| 213 | return interp_ok(Handle::Invalid); | |
| 214 | } | |
| 215 | }; | |
| 212 | 216 | #[cfg(not(bootstrap))] |
| 213 | assert!( | |
| 214 | dir.metadata().unwrap().is_dir(), | |
| 215 | "we tried to open a directory and got a file" | |
| 216 | ); | |
| 217 | if !dir.metadata().unwrap().is_dir() { | |
| 218 | // This changed from a directory to a file. Retry. | |
| 219 | continue; | |
| 220 | } | |
| 221 | ||
| 222 | // Windows communicates information via the error code on success. | |
| 223 | if let CreateAlways | OpenAlways = creation_disposition { | |
| 224 | this.set_last_error(IoError::WindowsError("ERROR_ALREADY_EXISTS"))?; | |
| 225 | } | |
| 226 | ||
| 217 | 227 | let fd_num = this.machine.fds.insert_new(DirHandle { dir, path: file_name }); |
| 218 | Handle::File(fd_num) | |
| 219 | }) | |
| 220 | } else { | |
| 221 | // Open this as a standard file. We already set the `read`/`write` flags above, | |
| 222 | // but we still need to represent the `creation_disposition`. | |
| 223 | match creation_disposition { | |
| 224 | CreateAlways | OpenAlways => { | |
| 225 | options.create(true); | |
| 226 | if creation_disposition == CreateAlways { | |
| 227 | options.truncate(true); | |
| 228 | return interp_ok(Handle::File(fd_num)); | |
| 229 | } else { | |
| 230 | // Per the documentation: | |
| 231 | // If the specified file exists and is writable, the function truncates the file, | |
| 232 | // the function succeeds, and last-error code is set to ERROR_ALREADY_EXISTS. | |
| 233 | // If the specified file does not exist and is a valid path, a new file is created, | |
| 234 | // the function succeeds, and the last-error code is set to zero. | |
| 235 | // https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew | |
| 236 | // | |
| 237 | // We check whether it exists before trying to open it. This is racy, but there | |
| 238 | // doesn't appear to be an std API that both succeeds whether or not a file already | |
| 239 | // exists and tells us whether it is new. So instead we will open the file in a way | |
| 240 | // that we can verify whether our guess is correct, and retry if it is not. | |
| 241 | let exists_already = file_name.exists(); | |
| 242 | ||
| 243 | // Open this as a standard file. | |
| 244 | let mut options = fs::OpenOptions::new(); | |
| 245 | options.read(desired_read); | |
| 246 | options.write(desired_write); | |
| 247 | match creation_disposition { | |
| 248 | CreateAlways | OpenAlways => { | |
| 249 | // We verify `exists_already`: if we expect it to already exist, we set no | |
| 250 | // flag, thus failing if it doesn't exist. If we expect the file to not | |
| 251 | // exist, we use `create_new` to fail if it does exist. | |
| 252 | if !exists_already { | |
| 253 | options.create_new(true); | |
| 254 | } | |
| 255 | if creation_disposition == CreateAlways { | |
| 256 | options.truncate(true); | |
| 257 | } | |
| 228 | 258 | } |
| 229 | } | |
| 230 | CreateNew => { | |
| 231 | options.create_new(true); | |
| 232 | // Per `create_new` documentation: | |
| 233 | // The file must be opened with write or append access in order to create a new file. | |
| 234 | // https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.create_new | |
| 235 | if !desired_write { | |
| 236 | options.append(true); | |
| 259 | CreateNew => { | |
| 260 | options.create_new(true); | |
| 261 | // Per `create_new` documentation: | |
| 262 | // The file must be opened with write or append access in order to create a new file. | |
| 263 | // https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.create_new | |
| 264 | if !desired_write { | |
| 265 | options.append(true); | |
| 266 | } | |
| 237 | 267 | } |
| 238 | } | |
| 239 | OpenExisting => { | |
| 240 | if !desired_read && !desired_write { | |
| 241 | // Windows supports handles with no permissions. These allow things such as | |
| 242 | // reading metadata, but not file content. This is used by `Path::metadata`. | |
| 243 | // `std` does not support this. To ensure we behave correctly as often as | |
| 244 | // possible, we open the file for reading and live with the fact that this | |
| 245 | // might incorrectly return `PermissionDenied`. | |
| 246 | // FIXME: We could probably use `OpenOptionsExt`? On a Unix host, | |
| 247 | // `O_PATH` apparently can open files for metadata use only. | |
| 248 | options.read(true); | |
| 268 | OpenExisting => { | |
| 269 | if !desired_read && !desired_write { | |
| 270 | // Windows supports handles with no permissions. These allow things such as | |
| 271 | // reading metadata, but not file content. This is used by `Path::metadata`. | |
| 272 | // `std` does not support this. To ensure we behave correctly as often as | |
| 273 | // possible, we open the file for reading and live with the fact that this | |
| 274 | // might incorrectly return `PermissionDenied`. | |
| 275 | // FIXME: We could probably use `OpenOptionsExt`? On a Unix host, | |
| 276 | // `O_PATH` apparently can open files for metadata use only. | |
| 277 | options.read(true); | |
| 278 | } | |
| 279 | } | |
| 280 | TruncateExisting => { | |
| 281 | options.truncate(true); | |
| 249 | 282 | } |
| 250 | 283 | } |
| 251 | TruncateExisting => { | |
| 252 | options.truncate(true); | |
| 284 | ||
| 285 | let file = match options.open(&file_name) { | |
| 286 | Ok(file) => file, | |
| 287 | Err(e) => { | |
| 288 | let kind = e.kind(); | |
| 289 | if kind == io::ErrorKind::IsADirectory { | |
| 290 | // This changed from a file to a directory. Retry. | |
| 291 | continue; | |
| 292 | } | |
| 293 | if exists_already && kind == io::ErrorKind::NotFound { | |
| 294 | // The file disappeared. Retry. | |
| 295 | continue; | |
| 296 | } | |
| 297 | if !exists_already && kind == io::ErrorKind::AlreadyExists { | |
| 298 | // The file got created by something else. Retry. | |
| 299 | continue; | |
| 300 | } | |
| 301 | this.set_last_error(e)?; | |
| 302 | return interp_ok(Handle::Invalid); | |
| 303 | } | |
| 304 | }; | |
| 305 | if file.metadata().unwrap().is_dir() { | |
| 306 | // This changed from a file to a directory. Retry. | |
| 307 | continue; | |
| 253 | 308 | } |
| 254 | } | |
| 255 | 309 | |
| 256 | options.open(file_name).map(|file| { | |
| 257 | assert!( | |
| 258 | !file.metadata().unwrap().is_dir(), | |
| 259 | "we tried to open a file and got a directory" | |
| 260 | ); | |
| 310 | // Windows communicates information via the error code on success. | |
| 311 | if let CreateAlways | OpenAlways = creation_disposition | |
| 312 | && exists_already | |
| 313 | { | |
| 314 | this.set_last_error(IoError::WindowsError("ERROR_ALREADY_EXISTS"))?; | |
| 315 | } | |
| 261 | 316 | let fd_num = this.machine.fds.insert_new(FileHandle { |
| 262 | 317 | file, |
| 263 | 318 | writable: desired_write, |
| 264 | 319 | readable: desired_read, |
| 265 | 320 | }); |
| 266 | Handle::File(fd_num) | |
| 267 | }) | |
| 268 | }; | |
| 269 | ||
| 270 | match handle { | |
| 271 | Ok(handle) => interp_ok(handle), | |
| 272 | Err(e) => { | |
| 273 | this.set_last_error(e)?; | |
| 274 | interp_ok(Handle::Invalid) | |
| 321 | return interp_ok(Handle::File(fd_num)); | |
| 275 | 322 | } |
| 276 | 323 | } |
| 277 | 324 | } |
src/tools/miri/tests/fail/both_borrows/mixed_mutability_static.rs created+16| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | //! Ensure that we do not permit mutating the part of an interior mutable static | |
| 2 | //! that is outside the `Cell`. | |
| 3 | ||
| 4 | //@revisions: stack tree | |
| 5 | //@[tree]compile-flags: -Zmiri-tree-borrows | |
| 6 | ||
| 7 | use std::sync::atomic::*; | |
| 8 | ||
| 9 | static X: (i32, AtomicI32) = (0, AtomicI32::new(1)); | |
| 10 | ||
| 11 | fn main() { | |
| 12 | let ptr = &raw const X; | |
| 13 | unsafe { ptr.cast_mut().write((1, AtomicI32::new(0))) }; | |
| 14 | //~[stack]^ERROR: that tag only grants SharedReadOnly permission | |
| 15 | //~[tree]|ERROR: /write access .* is forbidden/ | |
| 16 | } |
src/tools/miri/tests/fail/both_borrows/mixed_mutability_static.stack.stderr created+18| ... | ... | @@ -0,0 +1,18 @@ |
| 1 | error: Undefined Behavior: attempting a write access using <TAG> at ALLOC[0x0], but that tag only grants SharedReadOnly permission for this location | |
| 2 | --> tests/fail/both_borrows/mixed_mutability_static.rs:LL:CC | |
| 3 | | | |
| 4 | LL | unsafe { ptr.cast_mut().write((1, AtomicI32::new(0))) }; | |
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this error occurs as part of an access at ALLOC[0x0..0x8] | |
| 6 | | | |
| 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental | |
| 8 | = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information | |
| 9 | help: <TAG> was created by a SharedReadOnly retag at offsets [0x0..0x4] | |
| 10 | --> tests/fail/both_borrows/mixed_mutability_static.rs:LL:CC | |
| 11 | | | |
| 12 | LL | let ptr = &raw const X; | |
| 13 | | ^^^^^^^^^^^^ | |
| 14 | ||
| 15 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | |
| 16 | ||
| 17 | error: aborting due to 1 previous error | |
| 18 |
src/tools/miri/tests/fail/both_borrows/mixed_mutability_static.tree.stderr created+19| ... | ... | @@ -0,0 +1,19 @@ |
| 1 | error: Undefined Behavior: write access through <TAG> at ALLOC[0x0] is forbidden | |
| 2 | --> tests/fail/both_borrows/mixed_mutability_static.rs:LL:CC | |
| 3 | | | |
| 4 | LL | unsafe { ptr.cast_mut().write((1, AtomicI32::new(0))) }; | |
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here | |
| 6 | | | |
| 7 | = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental | |
| 8 | = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information | |
| 9 | = help: the accessed tag <TAG> has state Frozen which forbids this child write access | |
| 10 | help: the accessed tag <TAG> was created here, in the initial state Cell | |
| 11 | --> tests/fail/both_borrows/mixed_mutability_static.rs:LL:CC | |
| 12 | | | |
| 13 | LL | let ptr = &raw const X; | |
| 14 | | ^ | |
| 15 | ||
| 16 | note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace | |
| 17 | ||
| 18 | error: aborting due to 1 previous error | |
| 19 |
src/tools/miri/tests/fail/validity/fn_arg_never_type.rs+2-1| ... | ... | @@ -2,7 +2,8 @@ use std::mem::transmute; |
| 2 | 2 | |
| 3 | 3 | enum Never {} |
| 4 | 4 | |
| 5 | fn foo(x: Never) { //~ERROR: invalid value of type Never | |
| 5 | fn foo(x: Never) { | |
| 6 | //~^ERROR: invalid value of type Never | |
| 6 | 7 | let ptr = &raw const x; |
| 7 | 8 | println!("{ptr:p}"); |
| 8 | 9 | } |
src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs+8-7| ... | ... | @@ -1,10 +1,11 @@ |
| 1 | 1 | //@only-target: linux android illumos |
| 2 | 2 | // test_epoll_block_then_unblock and test_epoll_race depend on a deterministic schedule. |
| 3 | //@compile-flags: -Zmiri-deterministic-concurrency | |
| 4 | 3 | //@revisions: edge_triggered level_triggered |
| 4 | //@run-native | |
| 5 | 5 | |
| 6 | 6 | use std::convert::TryInto; |
| 7 | 7 | use std::thread; |
| 8 | use std::time::Duration; | |
| 8 | 9 | |
| 9 | 10 | #[path = "../../utils/libc.rs"] |
| 10 | 11 | mod libc_utils; |
| ... | ... | @@ -71,8 +72,8 @@ fn test_epoll_block_then_unblock() { |
| 71 | 72 | check_epoll_wait(epfd, &[Ev { events: EPOLLOUT, data: fds[0] }], -1); |
| 72 | 73 | |
| 73 | 74 | let thread1 = thread::spawn(move || { |
| 74 | thread::yield_now(); | |
| 75 | // Due to deterministic concurrency, we'll only get here when the other thread blocks. | |
| 75 | thread::sleep(Duration::from_millis(10)); | |
| 76 | // Since we slept, we should only get here after the other thread blocks. | |
| 76 | 77 | write_all(fds[1], b"abcde").unwrap(); |
| 77 | 78 | }); |
| 78 | 79 | |
| ... | ... | @@ -80,7 +81,7 @@ fn test_epoll_block_then_unblock() { |
| 80 | 81 | // Edge-triggered epoll will block until the write succeeds and the buffer |
| 81 | 82 | // becomes readable. This is because we already read the writable edge |
| 82 | 83 | // before so at the time of calling `epoll_wait` there is no active readiness. |
| 83 | check_epoll_wait(epfd, &[Ev { events: EPOLLIN | EPOLLOUT, data: fds[0] }], 10); | |
| 84 | check_epoll_wait(epfd, &[Ev { events: EPOLLIN | EPOLLOUT, data: fds[0] }], 100); | |
| 84 | 85 | } else { |
| 85 | 86 | // Level-triggered epoll won't wait for the write to succeed because |
| 86 | 87 | // _some_ readiness is already set (in this case the EPOLLOUT). |
| ... | ... | @@ -142,7 +143,7 @@ fn test_epoll_race() { |
| 142 | 143 | // Write to the eventfd instance. |
| 143 | 144 | write_all(fd, &1_u64.to_ne_bytes()).unwrap(); |
| 144 | 145 | }); |
| 145 | thread::yield_now(); | |
| 146 | thread::sleep(Duration::from_millis(10)); | |
| 146 | 147 | // epoll_wait for EPOLLIN. |
| 147 | 148 | check_epoll_wait(epfd, &[Ev { events: EPOLLIN, data: fd }], -1); |
| 148 | 149 | // Read from the static mut variable. |
| ... | ... | @@ -168,7 +169,7 @@ fn wakeup_on_new_interest() { |
| 168 | 169 | check_epoll_wait(epfd, &[Ev { events: EPOLLIN | EPOLLOUT, data: fds[1] }], -1); |
| 169 | 170 | }); |
| 170 | 171 | // Ensure the thread is blocked. |
| 171 | std::thread::yield_now(); | |
| 172 | thread::sleep(Duration::from_millis(10)); | |
| 172 | 173 | |
| 173 | 174 | // Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLRDHUP (and EPOLLET if we're in the |
| 174 | 175 | // `edge_triggered` revision). |
| ... | ... | @@ -212,7 +213,7 @@ fn multiple_events_wake_multiple_threads() { |
| 212 | 213 | Ev { events: e.events.cast_signed(), data: e.u64.try_into().unwrap() } |
| 213 | 214 | }); |
| 214 | 215 | // Yield so both threads are waiting now. |
| 215 | thread::yield_now(); | |
| 216 | thread::sleep(Duration::from_millis(10)); | |
| 216 | 217 | |
| 217 | 218 | // Trigger the eventfd. This triggers two events at once! |
| 218 | 219 | write_all(fd1, &0_u64.to_ne_bytes()).unwrap(); |
src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs+1| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | //@only-target: linux android illumos |
| 2 | 2 | //@revisions: edge_triggered level_triggered |
| 3 | //@run-native | |
| 3 | 4 | |
| 4 | 5 | #[path = "../../utils/libc.rs"] |
| 5 | 6 | mod libc_utils; |
src/tools/miri/tests/pass-dep/libc/libc-fs-flock.rs+1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | //@ignore-target: windows # no libc |
| 2 | 2 | //@ignore-target: solaris # Does not have flock |
| 3 | 3 | //@compile-flags: -Zmiri-disable-isolation |
| 4 | //@run-native | |
| 4 | 5 | |
| 5 | 6 | //@revisions: windows_host unix_host |
| 6 | 7 | //@[unix_host] ignore-host: windows |
src/tools/miri/tests/pass-dep/libc/libc-fs-permissions.rs+1| ... | ... | @@ -1,6 +1,7 @@ |
| 1 | 1 | //@ignore-target: windows # no libc |
| 2 | 2 | //@ignore-host: windows # needs unix PermissionExt |
| 3 | 3 | //@compile-flags: -Zmiri-disable-isolation |
| 4 | //@run-native | |
| 4 | 5 | |
| 5 | 6 | use std::ffi::{CStr, CString}; |
| 6 | 7 | use std::mem::MaybeUninit; |
src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs+1| ... | ... | @@ -2,6 +2,7 @@ |
| 2 | 2 | //@ignore-host: windows # creating symlinks requires admin permissions on Windows |
| 3 | 3 | //@ignore-target: windows # File handling is not implemented yet |
| 4 | 4 | //@compile-flags: -Zmiri-disable-isolation |
| 5 | //@run-native | |
| 5 | 6 | |
| 6 | 7 | use std::ffi::CString; |
| 7 | 8 | use std::io::ErrorKind; |
src/tools/miri/tests/pass-dep/libc/libc-fs.rs+26-11| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | //@ignore-target: windows # no libc |
| 2 | 2 | //@compile-flags: -Zmiri-disable-isolation |
| 3 | //@run-native | |
| 3 | 4 | |
| 4 | 5 | use std::ffi::{CStr, CString, OsString}; |
| 5 | 6 | use std::fs::{self, File, canonicalize, create_dir, remove_dir, remove_file}; |
| ... | ... | @@ -423,17 +424,21 @@ fn test_posix_mkstemp() { |
| 423 | 424 | drop(file); |
| 424 | 425 | remove_file(path).unwrap(); |
| 425 | 426 | |
| 426 | let invalid_templates = vec!["foo", "barXX", "XXXXXXbaz", "whatXXXXXXever", "X"]; | |
| 427 | for t in invalid_templates { | |
| 428 | let ptr = CString::new(t).unwrap().into_raw(); | |
| 429 | let fd = unsafe { libc::mkstemp(ptr) }; | |
| 430 | let _ = unsafe { CString::from_raw(ptr) }; | |
| 431 | // "On error, -1 is returned, and errno is set to | |
| 432 | // indicate the error" | |
| 433 | assert_eq!(fd, -1); | |
| 434 | let e = std::io::Error::last_os_error(); | |
| 435 | assert_eq!(e.raw_os_error(), Some(libc::EINVAL)); | |
| 436 | assert_eq!(e.kind(), std::io::ErrorKind::InvalidInput); | |
| 427 | // Test invalid inputs. We skip this on native macOS since macOS apparently does | |
| 428 | // not bother to validate inputs. | |
| 429 | if !cfg!(all(not(miri), target_vendor = "apple")) { | |
| 430 | let invalid_templates = vec!["foo", "barXX", "XXXXXXbaz", "whatXXXXXXever", "X"]; | |
| 431 | for t in invalid_templates { | |
| 432 | let ptr = CString::new(t).unwrap().into_raw(); | |
| 433 | let fd = unsafe { libc::mkstemp(ptr) }; | |
| 434 | let _ = unsafe { CString::from_raw(ptr) }; | |
| 435 | // "On error, -1 is returned, and errno is set to | |
| 436 | // indicate the error" | |
| 437 | assert_eq!(fd, -1, "mkstemp succeeded on invalid template {t:?}"); | |
| 438 | let e = std::io::Error::last_os_error(); | |
| 439 | assert_eq!(e.raw_os_error(), Some(libc::EINVAL)); | |
| 440 | assert_eq!(e.kind(), std::io::ErrorKind::InvalidInput); | |
| 441 | } | |
| 437 | 442 | } |
| 438 | 443 | |
| 439 | 444 | env::set_current_dir(old_cwd).unwrap(); |
| ... | ... | @@ -935,6 +940,11 @@ fn test_readv() { |
| 935 | 940 | |
| 936 | 941 | /// Test that vectored reads without any buffers return zero. |
| 937 | 942 | fn test_readv_empty_bufs() { |
| 943 | if cfg!(all(not(miri), target_vendor = "apple")) { | |
| 944 | // native macOS returns an error here :shrug: | |
| 945 | return; | |
| 946 | } | |
| 947 | ||
| 938 | 948 | let path = utils::prepare_with_content("pass-libc-readv-empty-bufs.txt", &[1u8, 2, 3]); |
| 939 | 949 | let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); |
| 940 | 950 | let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; |
| ... | ... | @@ -1063,6 +1073,11 @@ fn test_writev() { |
| 1063 | 1073 | |
| 1064 | 1074 | /// Test that vectored writes without any buffers return zero. |
| 1065 | 1075 | fn test_writev_empty_bufs() { |
| 1076 | if cfg!(all(not(miri), target_vendor = "apple")) { | |
| 1077 | // native macOS returns an error here :shrug: | |
| 1078 | return; | |
| 1079 | } | |
| 1080 | ||
| 1066 | 1081 | let path = utils::prepare_with_content("pass-libc-writev-empty-bufs.txt", &[1u8, 2, 3]); |
| 1067 | 1082 | let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); |
| 1068 | 1083 | let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_WRONLY) }; |
src/tools/miri/tests/pass-dep/libc/libc-socket-address.rs+1| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | //@ignore-target: windows # No libc socket on Windows |
| 2 | 2 | //@compile-flags: -Zmiri-disable-isolation |
| 3 | //@run-native | |
| 3 | 4 | |
| 4 | 5 | #[path = "../../utils/libc.rs"] |
| 5 | 6 | mod libc_utils; |
src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs+1| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | //@only-target: linux android illumos |
| 2 | 2 | //@compile-flags: -Zmiri-disable-isolation |
| 3 | //@run-native | |
| 3 | 4 | |
| 4 | 5 | #![feature(io_error_inprogress)] |
| 5 | 6 |
src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking.rs+1| ... | ... | @@ -3,6 +3,7 @@ |
| 3 | 3 | //@revisions: windows_host unix_host |
| 4 | 4 | //@[unix_host] ignore-host: windows |
| 5 | 5 | //@[windows_host] only-host: windows |
| 6 | //@run-native | |
| 6 | 7 | |
| 7 | 8 | #![feature(io_error_inprogress)] |
| 8 | 9 |
src/tools/miri/tests/pass-dep/libc/libc-socket.rs+3-2| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | //@ignore-target: windows # No libc socket on Windows |
| 2 | 2 | //@compile-flags: -Zmiri-disable-isolation |
| 3 | //@run-native | |
| 3 | 4 | |
| 4 | 5 | #[path = "../../utils/libc.rs"] |
| 5 | 6 | mod libc_utils; |
| ... | ... | @@ -176,8 +177,8 @@ fn test_set_nosigpipe_invalid_len() { |
| 176 | 177 | let sockfd = |
| 177 | 178 | unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; |
| 178 | 179 | // Value should be of type `libc::c_int` which has size 4 bytes. |
| 179 | // By providing a u64 of size 8 bytes we trigger an invalid length error. | |
| 180 | let err = net::setsockopt(sockfd, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1u64).unwrap_err(); | |
| 180 | // By providing a u16 of size 2 bytes we trigger an invalid length error. | |
| 181 | let err = net::setsockopt(sockfd, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1u16).unwrap_err(); | |
| 181 | 182 | assert_eq!(err.kind(), ErrorKind::InvalidInput); |
| 182 | 183 | // Check that it is the right kind of `InvalidInput`. |
| 183 | 184 | assert_eq!(err.raw_os_error(), Some(libc::EINVAL)); |
src/tools/miri/tests/pass/both_borrows/c_variadics.rs created+21| ... | ... | @@ -0,0 +1,21 @@ |
| 1 | //@revisions: stack tree tree_implicit_writes | |
| 2 | //@[tree_implicit_writes]compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes | |
| 3 | //@[tree]compile-flags: -Zmiri-tree-borrows | |
| 4 | #![feature(c_variadic)] | |
| 5 | ||
| 6 | fn main() { | |
| 7 | unsafe extern "C" fn write_with_first_arg( | |
| 8 | ptr_to_val: *mut i32, | |
| 9 | _hidden_mut_ref_to_val: ... | |
| 10 | ) { | |
| 11 | // Retagging needs to be disabled for arguments | |
| 12 | // within the VaList. Otherwise, this write access | |
| 13 | // will be undefined behavior. | |
| 14 | unsafe { *ptr_to_val = 32; } | |
| 15 | } | |
| 16 | ||
| 17 | let mut val: i32 = 0; | |
| 18 | unsafe { | |
| 19 | write_with_first_arg(&raw mut val, &val); | |
| 20 | } | |
| 21 | } |
src/tools/miri/tests/pass/shims/fs.rs+6| ... | ... | @@ -1,4 +1,5 @@ |
| 1 | 1 | //@compile-flags: -Zmiri-disable-isolation |
| 2 | //@run-native | |
| 2 | 3 | |
| 3 | 4 | #![feature(io_error_more)] |
| 4 | 5 | #![feature(io_error_uncategorized)] |
| ... | ... | @@ -95,6 +96,11 @@ fn test_file() { |
| 95 | 96 | } |
| 96 | 97 | |
| 97 | 98 | fn test_file_partial_reads_writes() { |
| 99 | if !cfg!(miri) { | |
| 100 | // This test is not expected to work natively. | |
| 101 | return; | |
| 102 | } | |
| 103 | ||
| 98 | 104 | let path1 = utils::prepare_with_content("miri_test_fs_file1.txt", b"abcdefg"); |
| 99 | 105 | let path2 = utils::prepare_with_content("miri_test_fs_file2.txt", b"abcdefg"); |
| 100 | 106 |
src/tools/miri/tests/pass/shims/loongarch/intrinsics-loongarch64-crc.rs+24-1| ... | ... | @@ -1,9 +1,20 @@ |
| 1 | 1 | // We're testing loongarch64-specific intrinsics |
| 2 | 2 | //@only-target: loongarch64 |
| 3 | #![feature(stdarch_loongarch)] | |
| 3 | #![feature(abi_unadjusted, link_llvm_intrinsics, stdarch_loongarch)] | |
| 4 | 4 | |
| 5 | 5 | use std::arch::loongarch64::*; |
| 6 | 6 | |
| 7 | unsafe extern "unadjusted" { | |
| 8 | #[link_name = "llvm.loongarch.crc.w.b.w"] | |
| 9 | fn _crc_w_b_w(a: i32, b: i32) -> i32; | |
| 10 | #[link_name = "llvm.loongarch.crc.w.h.w"] | |
| 11 | fn _crc_w_h_w(a: i32, b: i32) -> i32; | |
| 12 | #[link_name = "llvm.loongarch.crcc.w.b.w"] | |
| 13 | fn _crcc_w_b_w(a: i32, b: i32) -> i32; | |
| 14 | #[link_name = "llvm.loongarch.crcc.w.h.w"] | |
| 15 | fn _crcc_w_h_w(a: i32, b: i32) -> i32; | |
| 16 | } | |
| 17 | ||
| 7 | 18 | fn main() { |
| 8 | 19 | test_crc_ieee(); |
| 9 | 20 | test_crc_castagnoli(); |
| ... | ... | @@ -12,13 +23,19 @@ fn main() { |
| 12 | 23 | fn test_crc_ieee() { |
| 13 | 24 | // crc.w.b.w: 8-bit input |
| 14 | 25 | assert_eq!(crc_w_b_w(0x01, 0x00000000), 0x77073096); |
| 26 | assert_eq!(unsafe { _crc_w_b_w(0x1_01, 0x00000000) }, 0x77073096); // higher bits in the first argument are ignored | |
| 15 | 27 | assert_eq!(crc_w_b_w(0x61, 0xffffffff_u32 as i32), 0x174841bc); |
| 28 | assert_eq!(unsafe { _crc_w_b_w(0x2_61, 0xffffffff_u32 as i32) }, 0x174841bc); | |
| 16 | 29 | assert_eq!(crc_w_b_w(0x2a, 0x2aa1e72b), 0x772d9171); |
| 30 | assert_eq!(unsafe { _crc_w_b_w(0x3_2a, 0x2aa1e72b) }, 0x772d9171); | |
| 17 | 31 | |
| 18 | 32 | // crc.w.h.w: 16-bit input |
| 19 | 33 | assert_eq!(crc_w_h_w(0x0001, 0x00000000), 0x191b3141); |
| 34 | assert_eq!(unsafe { _crc_w_h_w(0x1_0001, 0x00000000) }, 0x191b3141); | |
| 20 | 35 | assert_eq!(crc_w_h_w(0x1234, 0xffffffff_u32 as i32), 0xf6b56fbf_u32 as i32); |
| 36 | assert_eq!(unsafe { _crc_w_h_w(0x2_1234, 0xffffffff_u32 as i32) }, 0xf6b56fbf_u32 as i32); | |
| 21 | 37 | assert_eq!(crc_w_h_w(0x022b, 0x8ecec3b5_u32 as i32), 0x03a1db7c); |
| 38 | assert_eq!(unsafe { _crc_w_h_w(0x3_022b, 0x8ecec3b5_u32 as i32) }, 0x03a1db7c); | |
| 22 | 39 | |
| 23 | 40 | // crc.w.w.w: 32-bit input |
| 24 | 41 | assert_eq!(crc_w_w_w(0x00000001, 0x00000000), 0xb8bc6765_u32 as i32); |
| ... | ... | @@ -34,13 +51,19 @@ fn test_crc_ieee() { |
| 34 | 51 | fn test_crc_castagnoli() { |
| 35 | 52 | // crcc.w.b.w: 8-bit input |
| 36 | 53 | assert_eq!(crcc_w_b_w(0x01, 0x00000000), 0xf26b8303_u32 as i32); |
| 54 | assert_eq!(unsafe { _crcc_w_b_w(0x1_01, 0x00000000) }, 0xf26b8303_u32 as i32); | |
| 37 | 55 | assert_eq!(crcc_w_b_w(0x61, 0xffffffff_u32 as i32), 0x3e2fbccf); |
| 56 | assert_eq!(unsafe { _crcc_w_b_w(0x2_61, 0xffffffff_u32 as i32) }, 0x3e2fbccf); | |
| 38 | 57 | assert_eq!(crcc_w_b_w(0x2a, 0x2aa1e72b), 0xf24122e4_u32 as i32); |
| 58 | assert_eq!(unsafe { _crcc_w_b_w(0x3_2a, 0x2aa1e72b) }, 0xf24122e4_u32 as i32); | |
| 39 | 59 | |
| 40 | 60 | // crcc.w.h.w: 16-bit input |
| 41 | 61 | assert_eq!(crcc_w_h_w(0x0001, 0x00000000), 0x13a29877); |
| 62 | assert_eq!(unsafe { _crcc_w_h_w(0x1_0001, 0x00000000) }, 0x13a29877); | |
| 42 | 63 | assert_eq!(crcc_w_h_w(0x1234, 0xffffffff_u32 as i32), 0xf13f4cea_u32 as i32); |
| 64 | assert_eq!(unsafe { _crcc_w_h_w(0x2_1234, 0xffffffff_u32 as i32) }, 0xf13f4cea_u32 as i32); | |
| 43 | 65 | assert_eq!(crcc_w_h_w(0x022b, 0x8ecec3b5_u32 as i32), 0x013bb2fb); |
| 66 | assert_eq!(unsafe { _crcc_w_h_w(0x3_022b, 0x8ecec3b5_u32 as i32) }, 0x013bb2fb); | |
| 44 | 67 | |
| 45 | 68 | // crcc.w.w.w: 32-bit input |
| 46 | 69 | assert_eq!(crcc_w_w_w(0x00000001, 0x00000000), 0xdd45aab8_u32 as i32); |
src/tools/miri/tests/pass/shims/socket.rs+3-2| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | 1 | //@ignore-target: windows # No socket support on Windows |
| 2 | 2 | //@compile-flags: -Zmiri-disable-isolation |
| 3 | //@run-native | |
| 3 | 4 | |
| 4 | 5 | use std::io::{ErrorKind, Read, Write}; |
| 5 | 6 | use std::net::{Shutdown, TcpListener, TcpStream}; |
| ... | ... | @@ -184,7 +185,7 @@ fn test_sockopt_read_timeout() { |
| 184 | 185 | // By default, reads on blocking sockets should block indefinitely. |
| 185 | 186 | assert_eq!(stream.read_timeout().unwrap(), None); |
| 186 | 187 | |
| 187 | let short_read_timeout = Some(Duration::from_millis(10)); | |
| 188 | let short_read_timeout = Some(Duration::from_millis(40)); | |
| 188 | 189 | stream.set_read_timeout(short_read_timeout).unwrap(); |
| 189 | 190 | assert_eq!(stream.read_timeout().unwrap(), short_read_timeout); |
| 190 | 191 | |
| ... | ... | @@ -207,7 +208,7 @@ fn test_sockopt_write_timeout() { |
| 207 | 208 | // By default, writes on blocking sockets should block indefinitely. |
| 208 | 209 | assert_eq!(stream.write_timeout().unwrap(), None); |
| 209 | 210 | |
| 210 | let short_write_timeout = Some(Duration::from_millis(10)); | |
| 211 | let short_write_timeout = Some(Duration::from_millis(40)); | |
| 211 | 212 | stream.set_write_timeout(short_write_timeout).unwrap(); |
| 212 | 213 | assert_eq!(stream.write_timeout().unwrap(), short_write_timeout); |
| 213 | 214 |
src/tools/miri/tests/pass/too-big-type-behind-raw-ptr.rs created+10| ... | ... | @@ -0,0 +1,10 @@ |
| 1 | //! Regression test for <https://github.com/rust-lang/rust/issues/157654#issuecomment-4679655886>: | |
| 2 | //! the type behind a raw pointer should never have its layout computed. | |
| 3 | ||
| 4 | //@compile-flags: -Zmiri-permissive-provenance | |
| 5 | ||
| 6 | const PTR_BITS_MINUS_1: usize = std::mem::size_of::<*const ()>() * 8 - 1; | |
| 7 | ||
| 8 | fn main() { | |
| 9 | std::hint::black_box(0 as *const [u64; 1 << PTR_BITS_MINUS_1]); | |
| 10 | } |
src/tools/miri/tests/pass/tree_borrows/implicit_writes/unchecked_mut.rs created+28| ... | ... | @@ -0,0 +1,28 @@ |
| 1 | // This test reproduces the pattern used by `BorrowedCursor::as_mut`, which appears in `Socket::recv_with_flags` and `std::fs::read`. | |
| 2 | // Many crates depend on similar patterns. Before https://github.com/rust-lang/rust/pull/157202 this failed under Tree | |
| 3 | // Borrows with Implicit Writes. With the attribute `#[rustc_no_writable]` added to | |
| 4 | // `slice::get_unchecked_mut`, both this test and the affected crates work. | |
| 5 | //@compile-flags: -Zmiri-tree-borrows -Zmiri-tree-borrows-implicit-writes | |
| 6 | ||
| 7 | struct BorrowedBuf<'a> { | |
| 8 | buf: &'a mut [u8], | |
| 9 | } | |
| 10 | ||
| 11 | impl<'a> BorrowedBuf<'a> { | |
| 12 | fn capacity(&self) -> usize { | |
| 13 | self.buf.len() | |
| 14 | } | |
| 15 | ||
| 16 | unsafe fn as_mut(&mut self) -> &mut [u8] { | |
| 17 | unsafe { self.buf.get_unchecked_mut(..) } | |
| 18 | } | |
| 19 | } | |
| 20 | ||
| 21 | fn main() { | |
| 22 | let mut arr = [0u8; 4]; | |
| 23 | let mut buf = BorrowedBuf { buf: &mut arr }; | |
| 24 | ||
| 25 | let ptr = unsafe { buf.as_mut() }.as_mut_ptr(); | |
| 26 | let _ = buf.capacity(); | |
| 27 | unsafe { ptr.write(42); } | |
| 28 | } |
src/tools/miri/tests/ui.rs+157-54| ... | ... | @@ -1,13 +1,15 @@ |
| 1 | use std::env; | |
| 1 | #![allow(clippy::let_and_return)] | |
| 2 | 2 | use std::num::NonZero; |
| 3 | 3 | use std::path::{Path, PathBuf}; |
| 4 | 4 | use std::process::Command; |
| 5 | 5 | use std::sync::OnceLock; |
| 6 | use std::{env, fmt}; | |
| 6 | 7 | |
| 7 | 8 | use colored::*; |
| 8 | 9 | use regex::bytes::Regex; |
| 9 | 10 | use ui_test::build_manager::BuildManager; |
| 10 | 11 | use ui_test::color_eyre::eyre::{Context, Result}; |
| 12 | use ui_test::custom_flags::Flag; | |
| 11 | 13 | use ui_test::custom_flags::edition::Edition; |
| 12 | 14 | use ui_test::dependencies::DependencyBuilder; |
| 13 | 15 | use ui_test::per_test_config::TestConfig; |
| ... | ... | @@ -17,14 +19,37 @@ use ui_test::{CommandBuilder, Config, Match, ignore_output_conflict}; |
| 17 | 19 | |
| 18 | 20 | #[derive(Copy, Clone, Debug)] |
| 19 | 21 | enum Mode { |
| 20 | Pass, | |
| 22 | Pass { | |
| 23 | native: bool, | |
| 24 | }, | |
| 21 | 25 | /// Requires annotations |
| 22 | 26 | Fail, |
| 23 | 27 | /// Not used for tests, but for `miri run --dep` |
| 24 | RunDep, | |
| 28 | RunDep { | |
| 29 | native: bool, | |
| 30 | }, | |
| 31 | /// Test must panic. | |
| 25 | 32 | Panic, |
| 26 | 33 | } |
| 27 | 34 | |
| 35 | impl Mode { | |
| 36 | fn native(self) -> bool { | |
| 37 | matches!(self, Mode::Pass { native: true } | Mode::RunDep { native: true }) | |
| 38 | } | |
| 39 | } | |
| 40 | ||
| 41 | impl fmt::Display for Mode { | |
| 42 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| 43 | match self { | |
| 44 | Mode::Pass { native: false } => write!(f, "pass"), | |
| 45 | Mode::Pass { native: true } => write!(f, "pass-native"), | |
| 46 | Mode::Fail => write!(f, "fail"), | |
| 47 | Mode::Panic => write!(f, "panic"), | |
| 48 | Mode::RunDep { .. } => unreachable!(), | |
| 49 | } | |
| 50 | } | |
| 51 | } | |
| 52 | ||
| 28 | 53 | fn miri_path() -> PathBuf { |
| 29 | 54 | env!("CARGO_BIN_EXE_miri").into() |
| 30 | 55 | } |
| ... | ... | @@ -79,7 +104,7 @@ struct WithDependencies { |
| 79 | 104 | bless: bool, |
| 80 | 105 | } |
| 81 | 106 | |
| 82 | /// Does *not* set any args or env vars, since it is shared between the test runner and | |
| 107 | /// Does *not* set args or (most) env vars, since it is shared between the test runner and | |
| 83 | 108 | /// run_dep_mode. |
| 84 | 109 | fn miri_config( |
| 85 | 110 | target: &str, |
| ... | ... | @@ -90,6 +115,15 @@ fn miri_config( |
| 90 | 115 | // Miri is rustc-like, so we create a default builder for rustc and modify it |
| 91 | 116 | let mut program = CommandBuilder::rustc(); |
| 92 | 117 | program.program = miri_path(); |
| 118 | if mode.native() { | |
| 119 | // This means we build the program instead of running Miri. | |
| 120 | program.envs.push(("MIRI_BE_RUSTC".into(), Some("host".into()))); | |
| 121 | // Use the right linker, if necessary. We use the `CC_*` variable as that is set by CI and | |
| 122 | // unlike `CARGO_TARGET_*_LINKER` it does not require upper-casing the target. | |
| 123 | if let Ok(linker) = env::var(format!("CC_{target}")) { | |
| 124 | program.args.push(format!("-Clinker={linker}").into()); | |
| 125 | } | |
| 126 | } | |
| 93 | 127 | |
| 94 | 128 | let mut config = Config { |
| 95 | 129 | target: Some(target.to_owned()), |
| ... | ... | @@ -103,10 +137,17 @@ fn miri_config( |
| 103 | 137 | ..Config::rustc(path) |
| 104 | 138 | }; |
| 105 | 139 | |
| 140 | // Register custom comments. | |
| 141 | config.custom_comments.insert("run-native", |parser, _args, span| { | |
| 142 | // Just remember that this is present. | |
| 143 | parser.set_custom_once("run-native", (), span); | |
| 144 | }); | |
| 145 | ||
| 146 | // Adjust comment defaults. | |
| 106 | 147 | config.comment_defaults.base().exit_status = match mode { |
| 107 | Mode::Pass => Some(0), | |
| 148 | Mode::Pass { .. } => Some(0), | |
| 108 | 149 | Mode::Fail => Some(1), |
| 109 | Mode::RunDep => None, | |
| 150 | Mode::RunDep { .. } => None, | |
| 110 | 151 | Mode::Panic => Some(101), |
| 111 | 152 | } |
| 112 | 153 | .map(Spanned::dummy) |
| ... | ... | @@ -123,9 +164,12 @@ fn miri_config( |
| 123 | 164 | // keep in sync with `./miri run` |
| 124 | 165 | config.comment_defaults.base().add_custom("edition", Edition("2021".into())); |
| 125 | 166 | |
| 167 | // Building dependencies is also a "comment default". | |
| 126 | 168 | if let Some(WithDependencies { bless }) = with_dependencies { |
| 127 | config.comment_defaults.base().set_custom( | |
| 128 | "dependencies", | |
| 169 | let crate_manifest_path = Path::new("tests/deps").join("Cargo.toml"); | |
| 170 | let dep_builder = if mode.native() { | |
| 171 | DependencyBuilder { crate_manifest_path, ..Default::default() } | |
| 172 | } else { | |
| 129 | 173 | DependencyBuilder { |
| 130 | 174 | program: CommandBuilder { |
| 131 | 175 | // Set the `cargo-miri` binary, which we expect to be in the same folder as the `miri` binary. |
| ... | ... | @@ -147,12 +191,65 @@ fn miri_config( |
| 147 | 191 | ], |
| 148 | 192 | ..CommandBuilder::cargo() |
| 149 | 193 | }, |
| 150 | crate_manifest_path: Path::new("tests/deps").join("Cargo.toml"), | |
| 194 | crate_manifest_path, | |
| 151 | 195 | build_std: None, |
| 152 | 196 | bless_lockfile: bless, |
| 153 | }, | |
| 154 | ); | |
| 197 | } | |
| 198 | }; | |
| 199 | config.comment_defaults.base().set_custom("dependencies", dep_builder); | |
| 200 | } | |
| 201 | ||
| 202 | // We only want this for actual test runs, not native run-dep mode. | |
| 203 | if matches!(mode, Mode::Pass { native: true }) { | |
| 204 | // Overwrite "compile-flags" so that it does nothing. | |
| 205 | // FIXME: make it just skip `-Zmiri` flags. | |
| 206 | config.custom_comments.insert("compile-flags", |_parser, _args, _span| {}); | |
| 207 | ||
| 208 | // Add a default comment that interprets our custom `run-native` comment. | |
| 209 | #[derive(Debug)] | |
| 210 | struct NativeRunner; | |
| 211 | config.comment_defaults.base().set_custom("native-runner", NativeRunner); | |
| 212 | ||
| 213 | impl Flag for NativeRunner { | |
| 214 | fn clone_inner(&self) -> Box<dyn Flag> { | |
| 215 | Box::new(NativeRunner) | |
| 216 | } | |
| 217 | fn must_be_unique(&self) -> bool { | |
| 218 | true | |
| 219 | } | |
| 220 | ||
| 221 | fn test_condition( | |
| 222 | &self, | |
| 223 | _config: &Config, | |
| 224 | comments: &ui_test::Comments, | |
| 225 | revision: &str, | |
| 226 | ) -> bool { | |
| 227 | let should_run = comments | |
| 228 | .for_revision(revision) | |
| 229 | .any(|r| r.custom.iter().any(|(k, _v)| *k == "run-native")); | |
| 230 | // We return `true` when the test should be ignored. | |
| 231 | let ignore = !should_run; | |
| 232 | ignore | |
| 233 | } | |
| 234 | ||
| 235 | fn post_test_action( | |
| 236 | &self, | |
| 237 | config: &TestConfig, | |
| 238 | output: &std::process::Output, | |
| 239 | build_manager: &BuildManager, | |
| 240 | ) -> Result<(), ui_test::Errored> { | |
| 241 | // Delegate to the native run support. | |
| 242 | use ui_test::custom_flags::run::Run; | |
| 243 | Run::post_test_action( | |
| 244 | &Run { exit_code: 0, output_conflict_handling: None }, | |
| 245 | config, | |
| 246 | output, | |
| 247 | build_manager, | |
| 248 | ) | |
| 249 | } | |
| 250 | } | |
| 155 | 251 | } |
| 252 | ||
| 156 | 253 | config |
| 157 | 254 | } |
| 158 | 255 | |
| ... | ... | @@ -177,6 +274,9 @@ fn run_tests( |
| 177 | 274 | assert!(!args.bless, "cannot use RUSTC_BLESS and MIRI_SKIP_UI_CHECKS at the same time"); |
| 178 | 275 | config.output_conflict_handling = ignore_output_conflict; |
| 179 | 276 | } |
| 277 | if mode.native() { | |
| 278 | config.output_conflict_handling = ignore_output_conflict; | |
| 279 | } | |
| 180 | 280 | |
| 181 | 281 | // Add a test env var to do environment communication tests. |
| 182 | 282 | config.program.envs.push(("MIRI_ENV_VAR_TEST".into(), Some("0".into()))); |
| ... | ... | @@ -185,23 +285,26 @@ fn run_tests( |
| 185 | 285 | // If a test ICEs, we want to see a backtrace. |
| 186 | 286 | config.program.envs.push(("RUST_BACKTRACE".into(), Some("1".into()))); |
| 187 | 287 | |
| 188 | // Add some flags we always want. | |
| 189 | config.program.args.push( | |
| 190 | format!( | |
| 191 | "--sysroot={}", | |
| 192 | env::var("MIRI_SYSROOT").expect("MIRI_SYSROOT must be set to run the ui test suite") | |
| 193 | ) | |
| 194 | .into(), | |
| 195 | ); | |
| 288 | // Add rustc/Miri flags. | |
| 196 | 289 | config.program.args.push("-Dwarnings".into()); |
| 197 | 290 | config.program.args.push("-Dunused".into()); |
| 198 | 291 | config.program.args.push("-Ainternal_features".into()); |
| 199 | if let Ok(extra_flags) = env::var("MIRIFLAGS") { | |
| 200 | for flag in extra_flags.split_whitespace() { | |
| 201 | config.program.args.push(flag.into()); | |
| 292 | config.program.args.push("-Zui-testing".into()); | |
| 293 | if !mode.native() { | |
| 294 | config.program.args.push( | |
| 295 | format!( | |
| 296 | "--sysroot={}", | |
| 297 | env::var("MIRI_SYSROOT") | |
| 298 | .expect("MIRI_SYSROOT must be set to run the ui test suite") | |
| 299 | ) | |
| 300 | .into(), | |
| 301 | ); | |
| 302 | if let Ok(extra_flags) = env::var("MIRIFLAGS") { | |
| 303 | for flag in extra_flags.split_whitespace() { | |
| 304 | config.program.args.push(flag.into()); | |
| 305 | } | |
| 202 | 306 | } |
| 203 | 307 | } |
| 204 | config.program.args.push("-Zui-testing".into()); | |
| 205 | 308 | |
| 206 | 309 | // If we're testing the native-lib functionality, then build the shared object file for testing |
| 207 | 310 | // external C function calls and push the relevant compiler flag. |
| ... | ... | @@ -288,8 +391,8 @@ regexes! { |
| 288 | 391 | } |
| 289 | 392 | |
| 290 | 393 | enum Dependencies { |
| 291 | WithDependencies, | |
| 292 | WithoutDependencies, | |
| 394 | WithDeps, | |
| 395 | WithoutDeps, | |
| 293 | 396 | } |
| 294 | 397 | |
| 295 | 398 | use Dependencies::*; |
| ... | ... | @@ -301,12 +404,12 @@ fn ui( |
| 301 | 404 | with_dependencies: Dependencies, |
| 302 | 405 | tmpdir: &Path, |
| 303 | 406 | ) -> Result<()> { |
| 304 | let msg = format!("## Running ui tests in {path} for {target}"); | |
| 407 | let msg = format!("## Running {mode} ui tests in {path} for {target}"); | |
| 305 | 408 | println!("{}", msg.green().bold()); |
| 306 | 409 | |
| 307 | 410 | let with_dependencies = match with_dependencies { |
| 308 | WithDependencies => true, | |
| 309 | WithoutDependencies => false, | |
| 411 | WithDeps => true, | |
| 412 | WithoutDeps => false, | |
| 310 | 413 | }; |
| 311 | 414 | run_tests(mode, path, target, with_dependencies, tmpdir) |
| 312 | 415 | .with_context(|| format!("ui tests in {path} for {target} failed")) |
| ... | ... | @@ -331,17 +434,27 @@ fn main() -> Result<()> { |
| 331 | 434 | |
| 332 | 435 | // Check whether this is a `./miri run` invocation |
| 333 | 436 | if let Ok(mode) = env::var("MIRI_RUN_MODE") { |
| 334 | return run_mode(target, mode); | |
| 437 | return run_with_deps(target, mode); | |
| 335 | 438 | } |
| 336 | 439 | |
| 337 | ui(Mode::Pass, "tests/pass", &target, WithoutDependencies, tmpdir.path())?; | |
| 338 | ui(Mode::Pass, "tests/pass-dep", &target, WithDependencies, tmpdir.path())?; | |
| 339 | ui(Mode::Panic, "tests/panic", &target, WithDependencies, tmpdir.path())?; | |
| 340 | ui(Mode::Fail, "tests/fail", &target, WithoutDependencies, tmpdir.path())?; | |
| 341 | ui(Mode::Fail, "tests/fail-dep", &target, WithDependencies, tmpdir.path())?; | |
| 440 | ui(Mode::Pass { native: false }, "tests/pass", &target, WithoutDeps, tmpdir.path())?; | |
| 441 | ui(Mode::Pass { native: false }, "tests/pass-dep", &target, WithDeps, tmpdir.path())?; | |
| 442 | if target == host { | |
| 443 | ui(Mode::Pass { native: true }, "tests/pass", &target, WithoutDeps, tmpdir.path())?; | |
| 444 | ui(Mode::Pass { native: true }, "tests/pass-dep", &target, WithDeps, tmpdir.path())?; | |
| 445 | } | |
| 446 | ui(Mode::Panic, "tests/panic", &target, WithDeps, tmpdir.path())?; | |
| 447 | ui(Mode::Fail, "tests/fail", &target, WithoutDeps, tmpdir.path())?; | |
| 448 | ui(Mode::Fail, "tests/fail-dep", &target, WithDeps, tmpdir.path())?; | |
| 342 | 449 | if cfg!(all(unix, feature = "native-lib")) && target == host { |
| 343 | ui(Mode::Pass, "tests/native-lib/pass", &target, WithoutDependencies, tmpdir.path())?; | |
| 344 | ui(Mode::Fail, "tests/native-lib/fail", &target, WithoutDependencies, tmpdir.path())?; | |
| 450 | ui( | |
| 451 | Mode::Pass { native: false }, | |
| 452 | "tests/native-lib/pass", | |
| 453 | &target, | |
| 454 | WithoutDeps, | |
| 455 | tmpdir.path(), | |
| 456 | )?; | |
| 457 | ui(Mode::Fail, "tests/native-lib/fail", &target, WithoutDeps, tmpdir.path())?; | |
| 345 | 458 | } |
| 346 | 459 | |
| 347 | 460 | // We only enable GenMC tests when the `genmc` feature is enabled, but also only on platforms we support: |
| ... | ... | @@ -354,30 +467,19 @@ fn main() -> Result<()> { |
| 354 | 467 | target_endian = "little" |
| 355 | 468 | )) && host == target |
| 356 | 469 | { |
| 357 | ui(Mode::Pass, "tests/genmc/pass", &target, WithDependencies, tmpdir.path())?; | |
| 358 | ui(Mode::Fail, "tests/genmc/fail", &target, WithDependencies, tmpdir.path())?; | |
| 470 | ui(Mode::Pass { native: false }, "tests/genmc/pass", &target, WithDeps, tmpdir.path())?; | |
| 471 | ui(Mode::Fail, "tests/genmc/fail", &target, WithDeps, tmpdir.path())?; | |
| 359 | 472 | } |
| 360 | 473 | |
| 361 | 474 | Ok(()) |
| 362 | 475 | } |
| 363 | 476 | |
| 364 | fn run_mode(target: String, mode: String) -> Result<()> { | |
| 477 | fn run_with_deps(target: String, mode: String) -> Result<()> { | |
| 365 | 478 | let native = mode == "native"; |
| 366 | 479 | |
| 367 | 480 | let mut config = |
| 368 | miri_config(&target, "", Mode::RunDep, Some(WithDependencies { bless: false })); | |
| 481 | miri_config(&target, "", Mode::RunDep { native }, Some(WithDependencies { bless: false })); | |
| 369 | 482 | config.comment_defaults.base().custom.remove("edition"); // `./miri` adds an `--edition` in `args`, so don't set it twice |
| 370 | if native { | |
| 371 | // Patch things up so that we actually compile the program. | |
| 372 | config.program.envs.push(("MIRI_BE_RUSTC".into(), Some("host".into()))); | |
| 373 | config.comment_defaults.base().set_custom( | |
| 374 | "dependencies", | |
| 375 | DependencyBuilder { | |
| 376 | crate_manifest_path: Path::new("tests/deps").join("Cargo.toml"), | |
| 377 | ..Default::default() | |
| 378 | }, | |
| 379 | ); | |
| 380 | } | |
| 381 | 483 | config.fill_host_and_target()?; |
| 382 | 484 | // Reset `args` (otherwise we'll get JSON output). |
| 383 | 485 | config.program.args = vec![]; |
| ... | ... | @@ -385,7 +487,8 @@ fn run_mode(target: String, mode: String) -> Result<()> { |
| 385 | 487 | // Compute the actual Miri invocation command. |
| 386 | 488 | let test_config = TestConfig::one_off_runner(config.clone(), PathBuf::new()); |
| 387 | 489 | let mut cmd = test_config.config.program.build(&test_config.config.out_dir); |
| 388 | // For some reason we need to set the target ourselves. | |
| 490 | // We are not using `test_config.build_command` (as that would require us to know the filename | |
| 491 | // we are invoking), so we need to set the target ourselves. | |
| 389 | 492 | cmd.arg("--target").arg(&target); |
| 390 | 493 | // Also forward arguments to the program (skipping the binary name). |
| 391 | 494 | // We don't put this in the `config` since we don't want it to affect the dependency build. |
| ... | ... | @@ -407,8 +510,8 @@ fn run_mode(target: String, mode: String) -> Result<()> { |
| 407 | 510 | |
| 408 | 511 | if native { |
| 409 | 512 | // We just built the program, we still have to run it. We can't use the ui_test `Run` flag |
| 410 | // as that needs an actual BuildManager, not just the one-off stub we have here. So we | |
| 411 | // implement the core logic ourselves. | |
| 513 | // as (a) that always captures the output, and (b) that needs an actual BuildManager, not | |
| 514 | // just the one-off stub we have here. So we implement the core logic ourselves. | |
| 412 | 515 | |
| 413 | 516 | // First, figure out the output binary by re-running the compiler with `--print`. |
| 414 | 517 | cmd.arg("--print").arg("file-names"); |
src/tools/miri/triagebot.toml-6| ... | ... | @@ -17,12 +17,6 @@ allow-unauthenticated = [ |
| 17 | 17 | [assign] |
| 18 | 18 | warn_non_default_branch = true |
| 19 | 19 | contributing_url = "https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#pr-review-process" |
| 20 | [assign.custom_welcome_messages] | |
| 21 | welcome-message = "(unused)" | |
| 22 | welcome-message-no-reviewer = """ | |
| 23 | Thank you for contributing to Miri! A reviewer will take a look at your PR, typically within a week or two. | |
| 24 | Please remember to not force-push to the PR branch except when you need to rebase due to a conflict or when the reviewer asks you for it. | |
| 25 | """ | |
| 26 | 20 | |
| 27 | 21 | [no-merges] |
| 28 | 22 | exclude_titles = ["Rustup"] |
tests/assembly-llvm/targets/targets-elf.rs+3| ... | ... | @@ -394,6 +394,9 @@ |
| 394 | 394 | //@ revisions: powerpc64_unknown_linux_gnu |
| 395 | 395 | //@ [powerpc64_unknown_linux_gnu] compile-flags: --target powerpc64-unknown-linux-gnu |
| 396 | 396 | //@ [powerpc64_unknown_linux_gnu] needs-llvm-components: powerpc |
| 397 | //@ revisions: powerpc64_unknown_linux_gnuelfv2 | |
| 398 | //@ [powerpc64_unknown_linux_gnuelfv2] compile-flags: --target powerpc64-unknown-linux-gnuelfv2 | |
| 399 | //@ [powerpc64_unknown_linux_gnuelfv2] needs-llvm-components: powerpc | |
| 397 | 400 | //@ revisions: powerpc64_unknown_linux_musl |
| 398 | 401 | //@ [powerpc64_unknown_linux_musl] compile-flags: --target powerpc64-unknown-linux-musl |
| 399 | 402 | //@ [powerpc64_unknown_linux_musl] needs-llvm-components: powerpc |
tests/codegen-llvm/debuginfo-unsize-field.rs created+66| ... | ... | @@ -0,0 +1,66 @@ |
| 1 | //@ compile-flags:-g -Copt-level=0 -C panic=abort | |
| 2 | ||
| 3 | // Check that debug information for structs with embedded str and [u8] slices is distinct from | |
| 4 | // structs with embedded u8 | |
| 5 | ||
| 6 | #![crate_type = "lib"] | |
| 7 | ||
| 8 | // NOTE: regex for the CHECK directives, | |
| 9 | // depending on the target, u8/usize are basic types or typedefs | |
| 10 | // linux: !1 = !DIBasicType(name: "u8", | |
| 11 | // win: !1 = !DIDerivedType(tag: .*, name: "u8", | |
| 12 | // and references types are | |
| 13 | // linux: name: "&debuginfo_unsize_field::Foo" | |
| 14 | // win: name: "ref$<debuginfo_unsize_field::Foo>" | |
| 15 | ||
| 16 | // CHECK: ![[U8:[0-9]+]] = !DI{{Basic|Derived}}Type({{.*}}name: "u8", | |
| 17 | ||
| 18 | pub struct Foo { | |
| 19 | a: u32, | |
| 20 | b: str, | |
| 21 | } | |
| 22 | // CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "{{&|ref\$<}}{{[^"]+}}::Foo{{>?}}", {{.*}}elements: ![[FOO_REF_ELEMS:[0-9]+]] | |
| 23 | // CHECK: ![[FOO_REF_ELEMS]] = !{![[FOO_REF_PTR:[0-9]+]], ![[FOO_REF_LEN:[0-9]+]]} | |
| 24 | // CHECK: ![[FOO_REF_PTR]] = !DIDerivedType(tag: DW_TAG_member, name: "data_ptr", {{.*}}baseType: ![[FOO_PTR:[0-9]+]] | |
| 25 | // CHECK: ![[FOO_PTR]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[FOO:[0-9]+]] | |
| 26 | // CHECK: ![[FOO]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Foo", {{.*}}elements: ![[FOO_ELEMS:[0-9]+]] | |
| 27 | // CHECK: ![[FOO_ELEMS]] = !{![[FOO_A:[0-9]+]], ![[FOO_B:[0-9]+]]} | |
| 28 | // CHECK: ![[FOO_A]] = !DIDerivedType(tag: DW_TAG_member, name: "a" | |
| 29 | // CHECK: ![[FOO_B]] = !DIDerivedType(tag: DW_TAG_member, name: "b", {{.*}}baseType: ![[U8_SLICE:[0-9]+]] | |
| 30 | // | |
| 31 | // CHECK: ![[U8_SLICE]] = !DICompositeType(tag: DW_TAG_array_type, baseType: ![[U8]], {{.*}}elements: ![[U8_SLICE_ELEMS:[0-9]+]] | |
| 32 | // CHECK: ![[U8_SLICE_ELEMS]] = !{![[U8_SLICE_RANGE:[0-9]+]]} | |
| 33 | // this is special to embedded slices, there is no upper bound on the number of elements, | |
| 34 | // that info is stored in the length metadata for a reference to the parent struct | |
| 35 | // CHECK: ![[U8_SLICE_RANGE]] = !DISubrange(count: -1, lowerBound: 0) | |
| 36 | // | |
| 37 | // CHECK: ![[FOO_REF_LEN]] = !DIDerivedType(tag: DW_TAG_member, name: "length", {{.*}}baseType: ![[USIZE:[0-9]+]] | |
| 38 | // CHECK: ![[USIZE]] = !DI{{Basic|Derived}}Type({{.*}}name: "usize" | |
| 39 | pub struct Bar { | |
| 40 | a: u32, | |
| 41 | b: [u8], | |
| 42 | } | |
| 43 | // CHECK: !DICompositeType(tag: DW_TAG_structure_type, name: "{{&|ref\$<}}{{[^"]+}}::Bar{{>?}}", {{.*}}elements: ![[BAR_REF_ELEMS:[0-9]+]] | |
| 44 | // CHECK: ![[BAR_REF_ELEMS]] = !{![[BAR_REF_PTR:[0-9]+]], ![[BAR_REF_LEN:[0-9]+]]} | |
| 45 | // CHECK: ![[BAR_REF_PTR]] = !DIDerivedType(tag: DW_TAG_member, name: "data_ptr", {{.*}}baseType: ![[BAR_PTR:[0-9]+]] | |
| 46 | // CHECK: ![[BAR_PTR]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[BAR:[0-9]+]] | |
| 47 | // CHECK: ![[BAR]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Bar", {{.*}}elements: ![[BAR_ELEMS:[0-9]+]] | |
| 48 | // CHECK: ![[BAR_ELEMS]] = !{![[BAR_A:[0-9]+]], ![[BAR_B:[0-9]+]]} | |
| 49 | // CHECK: ![[BAR_A]] = !DIDerivedType(tag: DW_TAG_member, name: "a" | |
| 50 | // CHECK: ![[BAR_B]] = !DIDerivedType(tag: DW_TAG_member, name: "b", {{.*}}baseType: ![[U8_SLICE]] | |
| 51 | // CHECK: ![[BAR_REF_LEN]] = !DIDerivedType(tag: DW_TAG_member, name: "length", {{.*}}baseType: ![[USIZE:[0-9]+]] | |
| 52 | pub struct Baz { | |
| 53 | a: u32, | |
| 54 | b: u8, | |
| 55 | } | |
| 56 | // CHECK: !DIDerivedType(tag: DW_TAG_pointer_type, name: "{{&|ref\$<}}{{[^"]+}}::Baz{{>?}}", {{.*}}baseType: ![[BAZ:[0-9]+]] | |
| 57 | // CHECK: ![[BAZ]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Baz", {{.*}}elements: ![[BAZ_ELEMS:[0-9]+]] | |
| 58 | // CHECK: ![[BAZ_ELEMS]] = !{![[BAZ_A:[0-9]+]], ![[BAZ_B:[0-9]+]]} | |
| 59 | // CHECK: ![[BAZ_A]] = !DIDerivedType(tag: DW_TAG_member, name: "a" | |
| 60 | // CHECK: ![[BAZ_B]] = !DIDerivedType(tag: DW_TAG_member, name: "b", {{.*}}baseType: ![[U8]] | |
| 61 | ||
| 62 | #[no_mangle] | |
| 63 | pub fn test<'a>(a: &'a Foo, b: &'a Bar, c: &'a Baz) -> &'a u8 { | |
| 64 | // just use this somehow so the debuginfo isn't removed | |
| 65 | &a.b.as_bytes()[0] | |
| 66 | } |
tests/crashes/reborrow/coerce-shared-alias-projection.rs created+110| ... | ... | @@ -0,0 +1,110 @@ |
| 1 | //@ known-bug: unknown | |
| 2 | #![feature(reborrow)] | |
| 3 | #![allow(dead_code)] | |
| 4 | ||
| 5 | use std::marker::{CoerceShared, Reborrow}; | |
| 6 | ||
| 7 | // This test combines alias/projection normalization with the leaf `&mut T` to `&T` | |
| 8 | // shared reborrow path. | |
| 9 | ||
| 10 | struct InnerMut<'a, T> { | |
| 11 | value: &'a mut T, | |
| 12 | } | |
| 13 | ||
| 14 | impl<'a, T> Reborrow for InnerMut<'a, T> {} | |
| 15 | ||
| 16 | struct InnerRef<'a, T> { | |
| 17 | value: &'a T, | |
| 18 | } | |
| 19 | ||
| 20 | impl<'a, T> Clone for InnerRef<'a, T> { | |
| 21 | fn clone(&self) -> Self { | |
| 22 | *self | |
| 23 | } | |
| 24 | } | |
| 25 | ||
| 26 | impl<'a, T> Copy for InnerRef<'a, T> {} | |
| 27 | ||
| 28 | impl<'a, T> CoerceShared<InnerRef<'a, T>> for InnerMut<'a, T> {} | |
| 29 | ||
| 30 | type DirectInnerRef<'a, T> = InnerRef<'a, T>; | |
| 31 | ||
| 32 | trait RefFamily<'a, T> { | |
| 33 | type Ref; | |
| 34 | } | |
| 35 | ||
| 36 | struct Projected; | |
| 37 | ||
| 38 | impl<'a, T: 'a> RefFamily<'a, T> for Projected { | |
| 39 | type Ref = InnerRef<'a, T>; | |
| 40 | } | |
| 41 | ||
| 42 | type ProjectedInnerRef<'a, T> = <Projected as RefFamily<'a, T>>::Ref; | |
| 43 | ||
| 44 | struct OuterMut<'a, T> { | |
| 45 | inner: InnerMut<'a, T>, | |
| 46 | tag: usize, | |
| 47 | } | |
| 48 | ||
| 49 | impl<'a, T> Reborrow for OuterMut<'a, T> {} | |
| 50 | ||
| 51 | struct OuterAliasRef<'a, T> { | |
| 52 | inner: DirectInnerRef<'a, T>, | |
| 53 | tag: usize, | |
| 54 | } | |
| 55 | ||
| 56 | impl<'a, T> Clone for OuterAliasRef<'a, T> { | |
| 57 | fn clone(&self) -> Self { | |
| 58 | *self | |
| 59 | } | |
| 60 | } | |
| 61 | ||
| 62 | impl<'a, T> Copy for OuterAliasRef<'a, T> {} | |
| 63 | ||
| 64 | impl<'a, T> CoerceShared<OuterAliasRef<'a, T>> for OuterMut<'a, T> {} | |
| 65 | ||
| 66 | struct OuterProjectionRef<'a, T: 'a> { | |
| 67 | inner: ProjectedInnerRef<'a, T>, | |
| 68 | tag: usize, | |
| 69 | } | |
| 70 | ||
| 71 | impl<'a, T: 'a> Clone for OuterProjectionRef<'a, T> { | |
| 72 | fn clone(&self) -> Self { | |
| 73 | *self | |
| 74 | } | |
| 75 | } | |
| 76 | ||
| 77 | impl<'a, T: 'a> Copy for OuterProjectionRef<'a, T> {} | |
| 78 | ||
| 79 | impl<'a, T: 'a> CoerceShared<OuterProjectionRef<'a, T>> for OuterMut<'a, T> {} | |
| 80 | ||
| 81 | fn read_alias<'a>(outer: OuterAliasRef<'a, u32>) -> (&'a u32, usize) { | |
| 82 | (outer.inner.value, outer.tag) | |
| 83 | } | |
| 84 | ||
| 85 | fn read_projection<'a>(outer: OuterProjectionRef<'a, u32>) -> (&'a u32, usize) { | |
| 86 | (outer.inner.value, outer.tag) | |
| 87 | } | |
| 88 | ||
| 89 | const fn const_accept_projection(_outer: OuterProjectionRef<'_, u32>) {} | |
| 90 | ||
| 91 | const fn consteval_projection_reborrow() { | |
| 92 | let mut value = 11; | |
| 93 | const_accept_projection(OuterMut { | |
| 94 | inner: InnerMut { value: &mut value }, | |
| 95 | tag: 5, | |
| 96 | }); | |
| 97 | } | |
| 98 | ||
| 99 | fn main() { | |
| 100 | const { consteval_projection_reborrow(); } | |
| 101 | ||
| 102 | let mut value = 22; | |
| 103 | let outer = OuterMut { inner: InnerMut { value: &mut value }, tag: 7 }; | |
| 104 | ||
| 105 | let (alias_value, alias_tag) = read_alias(outer); | |
| 106 | assert_eq!((*alias_value, alias_tag), (22, 7)); | |
| 107 | ||
| 108 | let (projection_value, projection_tag) = read_projection(outer); | |
| 109 | assert_eq!((*projection_value, projection_tag), (22, 7)); | |
| 110 | } |
tests/crashes/reborrow/coerce-shared-different-layout.rs created+41| ... | ... | @@ -0,0 +1,41 @@ |
| 1 | //@ known-bug: unknown | |
| 2 | #![feature(reborrow)] | |
| 3 | #![allow(dead_code)] | |
| 4 | ||
| 5 | use std::marker::{CoerceShared, PhantomData, Reborrow}; | |
| 6 | use std::ptr::NonNull; | |
| 7 | ||
| 8 | struct ImbrisMut<'a, T> { | |
| 9 | ptr: NonNull<T>, | |
| 10 | metadata: usize, | |
| 11 | marker: PhantomData<&'a mut T>, | |
| 12 | } | |
| 13 | ||
| 14 | impl<'a, T> Reborrow for ImbrisMut<'a, T> {} | |
| 15 | ||
| 16 | struct ImbrisRef<'a, T> { | |
| 17 | ptr: NonNull<T>, | |
| 18 | marker: PhantomData<&'a T>, | |
| 19 | } | |
| 20 | ||
| 21 | impl<'a, T> Clone for ImbrisRef<'a, T> { | |
| 22 | fn clone(&self) -> Self { | |
| 23 | *self | |
| 24 | } | |
| 25 | } | |
| 26 | ||
| 27 | impl<'a, T> Copy for ImbrisRef<'a, T> {} | |
| 28 | ||
| 29 | impl<'a, T> CoerceShared<ImbrisRef<'a, T>> for ImbrisMut<'a, T> {} | |
| 30 | ||
| 31 | fn ptr(value: ImbrisRef<'_, i32>) -> NonNull<i32> { | |
| 32 | value.ptr | |
| 33 | } | |
| 34 | ||
| 35 | fn main() { | |
| 36 | let mut value = 1; | |
| 37 | let raw = NonNull::from(&mut value); | |
| 38 | let wrapped = ImbrisMut { ptr: raw, metadata: 32, marker: PhantomData }; | |
| 39 | ||
| 40 | assert_eq!(ptr(wrapped), raw); | |
| 41 | } |
tests/crashes/reborrow/coerce-shared-marker-no-target-lifetime.rs created+21| ... | ... | @@ -0,0 +1,21 @@ |
| 1 | //@ known-bug: unknown | |
| 2 | //@ edition: 2024 | |
| 3 | ||
| 4 | #![feature(reborrow)] | |
| 5 | ||
| 6 | use std::marker::{CoerceShared, PhantomData, Reborrow}; | |
| 7 | ||
| 8 | struct CustomMarker<'a>(PhantomData<&'a ()>); | |
| 9 | struct CustomMarkerRef; | |
| 10 | ||
| 11 | impl<'a> Reborrow for CustomMarker<'a> {} | |
| 12 | impl<'a> CoerceShared<CustomMarkerRef> for CustomMarker<'a> {} | |
| 13 | //~^ ERROR | |
| 14 | ||
| 15 | fn method(_a: CustomMarkerRef) {} | |
| 16 | ||
| 17 | fn main() { | |
| 18 | let a = CustomMarker(PhantomData); | |
| 19 | method(a); | |
| 20 | //~^ ERROR | |
| 21 | } |
tests/crashes/reborrow/coerce-shared-multi-field.rs created+58| ... | ... | @@ -0,0 +1,58 @@ |
| 1 | //@ known-bug: unknown | |
| 2 | #![feature(reborrow)] | |
| 3 | #![allow(dead_code)] | |
| 4 | ||
| 5 | use std::marker::{CoerceShared, PhantomData, Reborrow}; | |
| 6 | use std::ptr::NonNull; | |
| 7 | ||
| 8 | struct MatMut<'a, T> { | |
| 9 | ptr: NonNull<T>, | |
| 10 | rows: usize, | |
| 11 | cols: usize, | |
| 12 | row_stride: usize, | |
| 13 | col_stride: usize, | |
| 14 | marker: PhantomData<&'a mut T>, | |
| 15 | } | |
| 16 | ||
| 17 | impl<'a, T> Reborrow for MatMut<'a, T> {} | |
| 18 | ||
| 19 | struct MatRef<'a, T> { | |
| 20 | ptr: NonNull<T>, | |
| 21 | rows: usize, | |
| 22 | cols: usize, | |
| 23 | row_stride: usize, | |
| 24 | col_stride: usize, | |
| 25 | marker: PhantomData<&'a T>, | |
| 26 | } | |
| 27 | ||
| 28 | impl<'a, T> Clone for MatRef<'a, T> { | |
| 29 | fn clone(&self) -> Self { | |
| 30 | *self | |
| 31 | } | |
| 32 | } | |
| 33 | ||
| 34 | impl<'a, T> Copy for MatRef<'a, T> {} | |
| 35 | ||
| 36 | impl<'a, T> CoerceShared<MatRef<'a, T>> for MatMut<'a, T> {} | |
| 37 | ||
| 38 | fn dims<T>(mat: MatRef<'_, T>) -> (usize, usize, usize, usize) { | |
| 39 | let _ = mat.ptr; | |
| 40 | (mat.rows, mat.cols, mat.row_stride, mat.col_stride) | |
| 41 | } | |
| 42 | ||
| 43 | fn main() { | |
| 44 | let mut value = 0; | |
| 45 | let mat = MatMut { | |
| 46 | ptr: NonNull::from(&mut value), | |
| 47 | rows: 2, | |
| 48 | cols: 3, | |
| 49 | row_stride: 4, | |
| 50 | col_stride: 5, | |
| 51 | marker: PhantomData, | |
| 52 | }; | |
| 53 | ||
| 54 | assert_eq!(dims(mat), (2, 3, 4, 5)); | |
| 55 | // Reusing the same source proves repeated shared reborrows keep source-only data protected | |
| 56 | // without consuming the reborrowable value. | |
| 57 | assert_eq!(dims(mat), (2, 3, 4, 5)); | |
| 58 | } |
tests/crashes/reborrow/coerce-shared-nested.rs created+62| ... | ... | @@ -0,0 +1,62 @@ |
| 1 | //@ known-bug: unknown | |
| 2 | #![feature(reborrow)] | |
| 3 | #![allow(dead_code)] | |
| 4 | ||
| 5 | use std::marker::{CoerceShared, Reborrow}; | |
| 6 | ||
| 7 | struct InnerMut<'a, T> { | |
| 8 | value: &'a mut T, | |
| 9 | } | |
| 10 | ||
| 11 | impl<'a, T> Reborrow for InnerMut<'a, T> {} | |
| 12 | ||
| 13 | struct InnerRef<'a, T> { | |
| 14 | value: &'a T, | |
| 15 | } | |
| 16 | ||
| 17 | impl<'a, T> Clone for InnerRef<'a, T> { | |
| 18 | fn clone(&self) -> Self { | |
| 19 | *self | |
| 20 | } | |
| 21 | } | |
| 22 | ||
| 23 | impl<'a, T> Copy for InnerRef<'a, T> {} | |
| 24 | ||
| 25 | impl<'a, T> CoerceShared<InnerRef<'a, T>> for InnerMut<'a, T> {} | |
| 26 | ||
| 27 | struct OuterMut<'a, T> { | |
| 28 | inner: InnerMut<'a, T>, | |
| 29 | tag: usize, | |
| 30 | } | |
| 31 | ||
| 32 | impl<'a, T> Reborrow for OuterMut<'a, T> {} | |
| 33 | ||
| 34 | struct OuterRef<'a, T> { | |
| 35 | inner: InnerRef<'a, T>, | |
| 36 | tag: usize, | |
| 37 | } | |
| 38 | ||
| 39 | impl<'a, T> Clone for OuterRef<'a, T> { | |
| 40 | fn clone(&self) -> Self { | |
| 41 | *self | |
| 42 | } | |
| 43 | } | |
| 44 | ||
| 45 | impl<'a, T> Copy for OuterRef<'a, T> {} | |
| 46 | ||
| 47 | impl<'a, T> CoerceShared<OuterRef<'a, T>> for OuterMut<'a, T> {} | |
| 48 | ||
| 49 | fn get<'a>(outer: OuterRef<'a, i32>) -> (&'a i32, usize) { | |
| 50 | (outer.inner.value, outer.tag) | |
| 51 | } | |
| 52 | ||
| 53 | fn main() { | |
| 54 | let mut value = 22; | |
| 55 | let outer = OuterMut { inner: InnerMut { value: &mut value }, tag: 7 }; | |
| 56 | ||
| 57 | let (first, tag) = get(outer); | |
| 58 | assert_eq!((*first, tag), (22, 7)); | |
| 59 | ||
| 60 | let (second, tag) = get(outer); | |
| 61 | assert_eq!((*second, tag), (22, 7)); | |
| 62 | } |
tests/crashes/reborrow/coerce-shared-tuple-phantom-position.rs created+86| ... | ... | @@ -0,0 +1,86 @@ |
| 1 | //@ known-bug: unknown | |
| 2 | #![feature(reborrow)] | |
| 3 | #![allow(dead_code)] | |
| 4 | ||
| 5 | use std::marker::{CoerceShared, PhantomData, Reborrow}; | |
| 6 | ||
| 7 | struct SourceLeadingMut<'a, T>(PhantomData<&'a mut T>, &'a mut T); | |
| 8 | ||
| 9 | impl<'a, T> Reborrow for SourceLeadingMut<'a, T> {} | |
| 10 | ||
| 11 | struct SourceLeadingRef<'a, T>(&'a T); | |
| 12 | ||
| 13 | impl<'a, T> Clone for SourceLeadingRef<'a, T> { | |
| 14 | fn clone(&self) -> Self { | |
| 15 | *self | |
| 16 | } | |
| 17 | } | |
| 18 | ||
| 19 | impl<'a, T> Copy for SourceLeadingRef<'a, T> {} | |
| 20 | ||
| 21 | impl<'a, T> CoerceShared<SourceLeadingRef<'a, T>> for SourceLeadingMut<'a, T> {} | |
| 22 | ||
| 23 | struct TargetLeadingMut<'a, T>(&'a mut T); | |
| 24 | ||
| 25 | impl<'a, T> Reborrow for TargetLeadingMut<'a, T> {} | |
| 26 | ||
| 27 | struct TargetLeadingRef<'a, T>(PhantomData<&'a T>, &'a T); | |
| 28 | ||
| 29 | impl<'a, T> Clone for TargetLeadingRef<'a, T> { | |
| 30 | fn clone(&self) -> Self { | |
| 31 | *self | |
| 32 | } | |
| 33 | } | |
| 34 | ||
| 35 | impl<'a, T> Copy for TargetLeadingRef<'a, T> {} | |
| 36 | ||
| 37 | impl<'a, T> CoerceShared<TargetLeadingRef<'a, T>> for TargetLeadingMut<'a, T> {} | |
| 38 | ||
| 39 | struct InterleavedMut<'a, T, U>( | |
| 40 | PhantomData<&'a mut T>, | |
| 41 | &'a mut T, | |
| 42 | PhantomData<&'a mut U>, | |
| 43 | &'a mut U, | |
| 44 | ); | |
| 45 | ||
| 46 | impl<'a, T, U> Reborrow for InterleavedMut<'a, T, U> {} | |
| 47 | ||
| 48 | struct InterleavedRef<'a, T, U>(&'a T, PhantomData<&'a T>, &'a U, PhantomData<&'a U>); | |
| 49 | ||
| 50 | impl<'a, T, U> Clone for InterleavedRef<'a, T, U> { | |
| 51 | fn clone(&self) -> Self { | |
| 52 | *self | |
| 53 | } | |
| 54 | } | |
| 55 | ||
| 56 | impl<'a, T, U> Copy for InterleavedRef<'a, T, U> {} | |
| 57 | ||
| 58 | impl<'a, T, U> CoerceShared<InterleavedRef<'a, T, U>> for InterleavedMut<'a, T, U> {} | |
| 59 | ||
| 60 | fn read_source_leading<'a>(value: SourceLeadingRef<'a, i32>) -> &'a i32 { | |
| 61 | value.0 | |
| 62 | } | |
| 63 | ||
| 64 | fn read_target_leading<'a>(value: TargetLeadingRef<'a, i32>) -> &'a i32 { | |
| 65 | value.1 | |
| 66 | } | |
| 67 | ||
| 68 | fn read_interleaved<'a>(value: InterleavedRef<'a, i32, i64>) -> (&'a i32, &'a i64) { | |
| 69 | (value.0, value.2) | |
| 70 | } | |
| 71 | ||
| 72 | fn main() { | |
| 73 | let mut source_leading = 10; | |
| 74 | let wrapped = SourceLeadingMut(PhantomData, &mut source_leading); | |
| 75 | assert_eq!(*read_source_leading(wrapped), 10); | |
| 76 | ||
| 77 | let mut target_leading = 20; | |
| 78 | let wrapped = TargetLeadingMut(&mut target_leading); | |
| 79 | assert_eq!(*read_target_leading(wrapped), 20); | |
| 80 | ||
| 81 | let mut first = 30; | |
| 82 | let mut second = 40_i64; | |
| 83 | let wrapped = InterleavedMut(PhantomData, &mut first, PhantomData, &mut second); | |
| 84 | let (first, second) = read_interleaved(wrapped); | |
| 85 | assert_eq!((*first, *second), (30, 40)); | |
| 86 | } |
tests/crashes/reborrow/corrected-field-mismatch-coerce-shared-issue-156315.rs created+21| ... | ... | @@ -0,0 +1,21 @@ |
| 1 | //@ known-bug: unknown | |
| 2 | ||
| 3 | #![feature(reborrow)] | |
| 4 | ||
| 5 | use std::marker::{CoerceShared, Reborrow}; | |
| 6 | ||
| 7 | struct CustomMut<'a, T>(&'a mut T); | |
| 8 | ||
| 9 | impl<'a, T> Reborrow for CustomMut<'a, T> {} | |
| 10 | ||
| 11 | struct CustomRef<'a, T>(&'a CustomMut<'a, T>); | |
| 12 | //~^ ERROR | |
| 13 | ||
| 14 | impl<'a, T> CoerceShared<CustomRef<'a, T>> for CustomMut<'a, T> {} | |
| 15 | ||
| 16 | fn method(_a: CustomRef<'_, ()>) {} | |
| 17 | ||
| 18 | fn main() { | |
| 19 | let a = CustomMut(&mut ()); | |
| 20 | method(a); | |
| 21 | } |
tests/crashes/reborrow/malformed-marker-coerce-shared-issue-156309.rs created+30| ... | ... | @@ -0,0 +1,30 @@ |
| 1 | //@ known-bug: unknown | |
| 2 | //@ edition: 2024 | |
| 3 | ||
| 4 | #![feature(reborrow)] | |
| 5 | ||
| 6 | // Malformed no-ICE regression: the unrelated name and signature errors are intentional because the | |
| 7 | // original issue combined them with CoerceShared validation. | |
| 8 | ||
| 9 | use std::marker::{CoerceShared, PhantomData, Reborrow}; | |
| 10 | ||
| 11 | struct CustomMarker<'a>(PhantomData<&'a ()>); | |
| 12 | ||
| 13 | struct CustomMarkerRef<'a>(PhantomData<(Debug, Clone, Copy)>); | |
| 14 | //~^ ERROR | |
| 15 | //~| ERROR | |
| 16 | //~| ERROR | |
| 17 | ||
| 18 | impl<'a> Reborrow for CustomMarker<'a> {} | |
| 19 | impl<'a> CoerceShared<CustomMarkerRef<'a>> for CustomMarker<'a> {} | |
| 20 | //~^ ERROR | |
| 21 | ||
| 22 | fn method<'a>(_a: CustomMarkerRef<'a>) -> 'a () { | |
| 23 | //~^ ERROR | |
| 24 | &() | |
| 25 | } | |
| 26 | ||
| 27 | fn main() { | |
| 28 | let a = CustomMarker(PhantomData); | |
| 29 | let b = method(a); | |
| 30 | } |
tests/crashes/reborrow/missing-generic-args-coerce-shared-issue-156315.rs created+24| ... | ... | @@ -0,0 +1,24 @@ |
| 1 | //@ known-bug: unknown | |
| 2 | #![feature(reborrow)] | |
| 3 | ||
| 4 | // Malformed no-ICE regression: this intentionally keeps the missing generic arguments from the | |
| 5 | // original reproducer while the corrected test isolates the CoerceShared field error. | |
| 6 | ||
| 7 | use std::marker::{CoerceShared, Reborrow}; | |
| 8 | ||
| 9 | struct CustomMut<'a, T>(&'a mut T); | |
| 10 | ||
| 11 | impl<'a, T> Reborrow for CustomMut<'a, T> {} | |
| 12 | impl<'a, T> CoerceShared<CustomRef<'a, T>> for CustomMut<'a, T> {} | |
| 13 | ||
| 14 | struct CustomRef<'a, T>(&'a CustomMut); | |
| 15 | //~^ ERROR | |
| 16 | //~| ERROR | |
| 17 | //~| ERROR | |
| 18 | ||
| 19 | fn method(_a: CustomRef<'_, ()>) {} | |
| 20 | ||
| 21 | fn main() { | |
| 22 | let a = CustomMut(&mut ()); | |
| 23 | method(a); | |
| 24 | } |
tests/debuginfo/strings-and-strs.rs+15-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | //@ min-gdb-version: 14.0 | |
| 1 | //@ min-gdb-version: 15.1 | |
| 2 | 2 | // LLDB 1800+ tests were not tested in CI, broke, and now are disabled |
| 3 | 3 | //@ ignore-lldb |
| 4 | 4 | |
| ... | ... | @@ -23,6 +23,12 @@ |
| 23 | 23 | //@ gdb-command:print str_in_rc |
| 24 | 24 | //@ gdb-check:$5 = alloc::rc::Rc<&str, alloc::alloc::Global> {ptr: core::ptr::non_null::NonNull<alloc::rc::RcInner<&str>> {pointer: 0x[...]}, phantom: core::marker::PhantomData<alloc::rc::RcInner<&str>>, alloc: alloc::alloc::Global} |
| 25 | 25 | |
| 26 | //@ gdb-command:print box_str | |
| 27 | //@ gdb-check:$6 = alloc::boxed::Box<str, alloc::alloc::Global> [87, 111, 114, 108, 100] | |
| 28 | ||
| 29 | //@ gdb-command:print rc_str | |
| 30 | //@ gdb-check:$7 = alloc::rc::Rc<str, alloc::alloc::Global> {ptr: core::ptr::non_null::NonNull<alloc::rc::RcInner<str>> {pointer: alloc::rc::RcInner<str> {strong: core::cell::Cell<usize> {value: core::cell::UnsafeCell<usize> {value: 1}}, weak: core::cell::Cell<usize> {value: core::cell::UnsafeCell<usize> {value: 1}}, value: 0x[...]}}, phantom: core::marker::PhantomData<alloc::rc::RcInner<str>>, alloc: alloc::alloc::Global} | |
| 31 | ||
| 26 | 32 | // === LLDB TESTS ================================================================================== |
| 27 | 33 | //@ lldb-command:run |
| 28 | 34 | //@ lldb-command:v plain_string |
| ... | ... | @@ -40,6 +46,12 @@ |
| 40 | 46 | //@ lldb-command:v str_in_rc |
| 41 | 47 | //@ lldb-check:(alloc::rc::Rc<&str, alloc::alloc::Global>) str_in_rc = strong=1, weak=0 { value = "Hello" { [0] = 'H' [1] = 'e' [2] = 'l' [3] = 'l' [4] = 'o' } } |
| 42 | 48 | |
| 49 | //@ lldb-command:v box_str | |
| 50 | //@ lldb-check:(alloc::boxed::Box<unsigned char[], alloc::alloc::Global>) box_str = { __0 = { pointer = { pointer = { data_ptr = 0x[...] "World" length = 5 } } _marker = } __1 = } | |
| 51 | ||
| 52 | //@ lldb-command:v rc_str | |
| 53 | //@ lldb-check:(alloc::rc::Rc<unsigned char[], alloc::alloc::Global>) rc_str = strong=1, weak=0 { value = "World" } | |
| 54 | ||
| 43 | 55 | #![allow(unused_variables)] |
| 44 | 56 | |
| 45 | 57 | pub struct Foo<'a> { |
| ... | ... | @@ -53,6 +65,8 @@ fn main() { |
| 53 | 65 | let str_in_tuple = ("Hello", "World"); |
| 54 | 66 | |
| 55 | 67 | let str_in_rc = std::rc::Rc::new("Hello"); |
| 68 | let box_str: Box<str> = "World".into(); | |
| 69 | let rc_str: std::rc::Rc<str> = "World".into(); | |
| 56 | 70 | zzz(); // #break |
| 57 | 71 | } |
| 58 | 72 |
tests/ui/assumptions_on_binders/type_relation_binders_inside_solver-1.rs created+23| ... | ... | @@ -0,0 +1,23 @@ |
| 1 | //@ check-pass | |
| 2 | //@ compile-flags: -Znext-solver -Zassumptions-on-binders | |
| 3 | ||
| 4 | #![crate_type = "lib"] | |
| 5 | ||
| 6 | // When entering binders if we don't insert assumptions corresponding to the | |
| 7 | // binder then we'll ICE when later trying to eagerly handle placeholders. This | |
| 8 | // test checks that type relations involving higher ranked types insert assumptions | |
| 9 | // for the binders of the higher ranked types. | |
| 10 | // | |
| 11 | // This is specifically checking that type relations used from *inside* the trait | |
| 12 | // solver do this. In this case the type relation occurs as part of an `AliasRelate` | |
| 13 | // involving the rigid alias: `<T as Trait>::Assoc<for<'b> fn('b ()) -> &'?0 ()>`. | |
| 14 | ||
| 15 | trait Trait { type Assoc<T>; } | |
| 16 | ||
| 17 | fn foo<T: Trait, U>(_: U) -> <T as Trait>::Assoc::<U> { loop {} } | |
| 18 | ||
| 19 | fn mk<'a>() -> for<'b> fn(&'b ()) -> &'a () { loop {} } | |
| 20 | ||
| 21 | fn bar<T: Trait>() { | |
| 22 | foo::<T, _>(mk()); | |
| 23 | } |
tests/ui/assumptions_on_binders/type_relation_binders_inside_solver-2.rs created+15| ... | ... | @@ -0,0 +1,15 @@ |
| 1 | //@ check-pass | |
| 2 | //@ compile-flags: -Znext-solver -Zassumptions-on-binders | |
| 3 | ||
| 4 | #![crate_type = "lib"] | |
| 5 | ||
| 6 | // Slight derivative of `type_relation_binders_inside_solver-1.rs` except this time the | |
| 7 | // rigid alias is the opaque type `(impl Sized)<for<'b> fn(&'b ()) -> &'?0 ()>` instead. | |
| 8 | ||
| 9 | fn foo<T>(_: T) -> impl Sized + use<T> {} | |
| 10 | ||
| 11 | fn mk<'a>() -> for<'b> fn(&'b ()) -> &'a () { loop {} } | |
| 12 | ||
| 13 | fn bar() { | |
| 14 | foo(mk()); | |
| 15 | } |
tests/ui/assumptions_on_binders/type_relation_binders_inside_solver-3.rs created+21| ... | ... | @@ -0,0 +1,21 @@ |
| 1 | //@ check-pass | |
| 2 | //@ compile-flags: -Znext-solver -Zassumptions-on-binders | |
| 3 | ||
| 4 | #![crate_type = "lib"] | |
| 5 | ||
| 6 | // Same concept as `type_relation_binders_inside_solver-1.rs`, this time the type relation | |
| 7 | // occuring when checking `for<'b> fn(&'b ()): Trait` holds and we have to equate the two | |
| 8 | // higher ranked function pointer types. | |
| 9 | ||
| 10 | trait Trait { } | |
| 11 | ||
| 12 | fn req_trait<T: Trait>(_: T) { } | |
| 13 | ||
| 14 | fn mk() -> for<'b> fn(&'b ()) { loop {} } | |
| 15 | ||
| 16 | fn ice() | |
| 17 | where | |
| 18 | (for<'b> fn(&'b ())): Trait | |
| 19 | { | |
| 20 | req_trait(mk()); | |
| 21 | } |
tests/ui/impl-trait/unpin-for-future.rs created+22| ... | ... | @@ -0,0 +1,22 @@ |
| 1 | //@ edition:2024 | |
| 2 | // | |
| 3 | // Tests that you can't implement Unpin for a compiler-generated future using TAIT. | |
| 4 | ||
| 5 | #![feature(type_alias_impl_trait)] | |
| 6 | ||
| 7 | use core::marker::PhantomPinned; | |
| 8 | use core::pin::Pin; | |
| 9 | ||
| 10 | type MyFut = impl Future<Output = ()>; | |
| 11 | ||
| 12 | async fn my_async_fn() {} | |
| 13 | ||
| 14 | #[define_opaque(MyFut)] | |
| 15 | fn fut() -> MyFut { | |
| 16 | my_async_fn() | |
| 17 | } | |
| 18 | ||
| 19 | impl Unpin for MyFut {} | |
| 20 | //~^ ERROR: only traits defined in the current crate can be implemented for arbitrary types | |
| 21 | ||
| 22 | fn main() {} |
tests/ui/impl-trait/unpin-for-future.stderr created+15| ... | ... | @@ -0,0 +1,15 @@ |
| 1 | error[E0117]: only traits defined in the current crate can be implemented for arbitrary types | |
| 2 | --> $DIR/unpin-for-future.rs:19:1 | |
| 3 | | | |
| 4 | LL | impl Unpin for MyFut {} | |
| 5 | | ^^^^^^^^^^^^^^^----- | |
| 6 | | | | |
| 7 | | type alias impl trait is treated as if it were foreign, because its hidden type could be from a foreign crate | |
| 8 | | | |
| 9 | = note: impl doesn't have any local type before any uncovered type parameters | |
| 10 | = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules | |
| 11 | = note: define and implement a trait or new type instead | |
| 12 | ||
| 13 | error: aborting due to 1 previous error | |
| 14 | ||
| 15 | For more information about this error, try `rustc --explain E0117`. |
tests/ui/layout/issue-unsized-tail-restatic-ice-122488.rs+1-1| ... | ... | @@ -5,7 +5,7 @@ use std::ops::Deref; |
| 5 | 5 | struct ArenaSet<U: Deref, V: ?Sized = <U as Deref>::Target>(V, U); |
| 6 | 6 | //~^ ERROR the size for values of type `V` cannot be known at compilation time |
| 7 | 7 | |
| 8 | const DATA: *const ArenaSet<Vec<u8>> = std::ptr::null_mut(); | |
| 8 | const DATA: &ArenaSet<Vec<u8>> = unsafe { &* std::ptr::null_mut() }; | |
| 9 | 9 | //~^ ERROR the type `ArenaSet<Vec<u8>, [u8]>` has an unknown layout |
| 10 | 10 | |
| 11 | 11 | pub fn main() {} |
tests/ui/layout/issue-unsized-tail-restatic-ice-122488.stderr+3-3| ... | ... | @@ -23,10 +23,10 @@ LL | struct ArenaSet<U: Deref, V: ?Sized = <U as Deref>::Target>(Box<V>, U); |
| 23 | 23 | | ++++ + |
| 24 | 24 | |
| 25 | 25 | error[E0080]: the type `ArenaSet<Vec<u8>, [u8]>` has an unknown layout |
| 26 | --> $DIR/issue-unsized-tail-restatic-ice-122488.rs:8:1 | |
| 26 | --> $DIR/issue-unsized-tail-restatic-ice-122488.rs:8:43 | |
| 27 | 27 | | |
| 28 | LL | const DATA: *const ArenaSet<Vec<u8>> = std::ptr::null_mut(); | |
| 29 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `DATA` failed here | |
| 28 | LL | const DATA: &ArenaSet<Vec<u8>> = unsafe { &* std::ptr::null_mut() }; | |
| 29 | | ^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `DATA` failed here | |
| 30 | 30 | |
| 31 | 31 | error: aborting due to 2 previous errors |
| 32 | 32 |
tests/ui/parser/recover/raw-no-const-mut-arg-list.rs created+12| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | // Regression test for https://github.com/rust-lang/rust/issues/157015. | |
| 2 | ||
| 3 | fn takes_raw_ptr(_: *const u32) {} | |
| 4 | fn takes_raw_ptr_args(_: *const u32, _: *const u32) {} | |
| 5 | ||
| 6 | fn main() { | |
| 7 | let x = 0u32; | |
| 8 | takes_raw_ptr(&raw x); | |
| 9 | //~^ ERROR expected one of | |
| 10 | takes_raw_ptr_args(&raw x, &raw x); | |
| 11 | //~^ ERROR expected one of | |
| 12 | } |
tests/ui/parser/recover/raw-no-const-mut-arg-list.stderr created+28| ... | ... | @@ -0,0 +1,28 @@ |
| 1 | error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `const`, `mut`, `{`, or an operator, found `x` | |
| 2 | --> $DIR/raw-no-const-mut-arg-list.rs:8:24 | |
| 3 | | | |
| 4 | LL | takes_raw_ptr(&raw x); | |
| 5 | | ^ expected one of 10 possible tokens | |
| 6 | | | |
| 7 | help: `&raw` must be followed by `const` or `mut` to be a raw reference expression | |
| 8 | | | |
| 9 | LL | takes_raw_ptr(&raw const x); | |
| 10 | | +++++ | |
| 11 | LL | takes_raw_ptr(&raw mut x); | |
| 12 | | +++ | |
| 13 | ||
| 14 | error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `const`, `mut`, `{`, or an operator, found `x` | |
| 15 | --> $DIR/raw-no-const-mut-arg-list.rs:10:29 | |
| 16 | | | |
| 17 | LL | takes_raw_ptr_args(&raw x, &raw x); | |
| 18 | | ^ expected one of 10 possible tokens | |
| 19 | | | |
| 20 | help: `&raw` must be followed by `const` or `mut` to be a raw reference expression | |
| 21 | | | |
| 22 | LL | takes_raw_ptr_args(&raw const x, &raw x); | |
| 23 | | +++++ | |
| 24 | LL | takes_raw_ptr_args(&raw mut x, &raw x); | |
| 25 | | +++ | |
| 26 | ||
| 27 | error: aborting due to 2 previous errors | |
| 28 |
tests/ui/parser/recover/raw-no-const-mut.rs-3| ... | ... | @@ -6,8 +6,6 @@ fn a() { |
| 6 | 6 | fn b() { |
| 7 | 7 | [&raw const 1, &raw 2] |
| 8 | 8 | //~^ ERROR expected one of |
| 9 | //~| ERROR cannot find value `raw` in this scope | |
| 10 | //~| ERROR cannot take address of a temporary | |
| 11 | 9 | } |
| 12 | 10 | |
| 13 | 11 | fn c() { |
| ... | ... | @@ -18,7 +16,6 @@ fn c() { |
| 18 | 16 | fn d() { |
| 19 | 17 | f(&raw 2); |
| 20 | 18 | //~^ ERROR expected one of |
| 21 | //~| ERROR cannot find value `raw` in this scope | |
| 22 | 19 | //~| ERROR cannot find function `f` in this scope |
| 23 | 20 | } |
| 24 | 21 |
tests/ui/parser/recover/raw-no-const-mut.stderr+7-34| ... | ... | @@ -23,19 +23,15 @@ LL | [&raw const 1, &raw const 2] |
| 23 | 23 | | +++++ |
| 24 | 24 | LL | [&raw const 1, &raw mut 2] |
| 25 | 25 | | +++ |
| 26 | help: missing `,` | |
| 27 | | | |
| 28 | LL | [&raw const 1, &raw, 2] | |
| 29 | | + | |
| 30 | 26 | |
| 31 | 27 | error: expected `{`, found `z` |
| 32 | --> $DIR/raw-no-const-mut.rs:14:18 | |
| 28 | --> $DIR/raw-no-const-mut.rs:12:18 | |
| 33 | 29 | | |
| 34 | 30 | LL | if x == &raw z {} |
| 35 | 31 | | ^ expected `{` |
| 36 | 32 | | |
| 37 | 33 | note: the `if` expression is missing a block after this condition |
| 38 | --> $DIR/raw-no-const-mut.rs:14:8 | |
| 34 | --> $DIR/raw-no-const-mut.rs:12:8 | |
| 39 | 35 | | |
| 40 | 36 | LL | if x == &raw z {} |
| 41 | 37 | | ^^^^^^^^^ |
| ... | ... | @@ -47,7 +43,7 @@ LL | if x == &raw mut z {} |
| 47 | 43 | | +++ |
| 48 | 44 | |
| 49 | 45 | error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `const`, `mut`, `{`, or an operator, found `2` |
| 50 | --> $DIR/raw-no-const-mut.rs:19:12 | |
| 46 | --> $DIR/raw-no-const-mut.rs:17:12 | |
| 51 | 47 | | |
| 52 | 48 | LL | f(&raw 2); |
| 53 | 49 | | ^ expected one of 10 possible tokens |
| ... | ... | @@ -58,13 +54,9 @@ LL | f(&raw const 2); |
| 58 | 54 | | +++++ |
| 59 | 55 | LL | f(&raw mut 2); |
| 60 | 56 | | +++ |
| 61 | help: missing `,` | |
| 62 | | | |
| 63 | LL | f(&raw, 2); | |
| 64 | | + | |
| 65 | 57 | |
| 66 | 58 | error: expected one of `!`, `.`, `::`, `;`, `?`, `const`, `mut`, `{`, `}`, or an operator, found `1` |
| 67 | --> $DIR/raw-no-const-mut.rs:27:14 | |
| 59 | --> $DIR/raw-no-const-mut.rs:24:14 | |
| 68 | 60 | | |
| 69 | 61 | LL | x = &raw 1; |
| 70 | 62 | | ^ expected one of 10 possible tokens |
| ... | ... | @@ -76,26 +68,8 @@ LL | x = &raw const 1; |
| 76 | 68 | LL | x = &raw mut 1; |
| 77 | 69 | | +++ |
| 78 | 70 | |
| 79 | error[E0425]: cannot find value `raw` in this scope | |
| 80 | --> $DIR/raw-no-const-mut.rs:7:21 | |
| 81 | | | |
| 82 | LL | [&raw const 1, &raw 2] | |
| 83 | | ^^^ not found in this scope | |
| 84 | ||
| 85 | error[E0425]: cannot find value `raw` in this scope | |
| 86 | --> $DIR/raw-no-const-mut.rs:19:8 | |
| 87 | | | |
| 88 | LL | f(&raw 2); | |
| 89 | | ^^^ not found in this scope | |
| 90 | ||
| 91 | error[E0745]: cannot take address of a temporary | |
| 92 | --> $DIR/raw-no-const-mut.rs:7:17 | |
| 93 | | | |
| 94 | LL | [&raw const 1, &raw 2] | |
| 95 | | ^ temporary value | |
| 96 | ||
| 97 | 71 | error[E0425]: cannot find function `f` in this scope |
| 98 | --> $DIR/raw-no-const-mut.rs:19:5 | |
| 72 | --> $DIR/raw-no-const-mut.rs:17:5 | |
| 99 | 73 | | |
| 100 | 74 | LL | fn a() { |
| 101 | 75 | | ------ similarly named function `a` defined here |
| ... | ... | @@ -109,7 +83,6 @@ LL - f(&raw 2); |
| 109 | 83 | LL + a(&raw 2); |
| 110 | 84 | | |
| 111 | 85 | |
| 112 | error: aborting due to 9 previous errors | |
| 86 | error: aborting due to 6 previous errors | |
| 113 | 87 | |
| 114 | Some errors have detailed explanations: E0425, E0745. | |
| 115 | For more information about an error, try `rustc --explain E0425`. | |
| 88 | For more information about this error, try `rustc --explain E0425`. |
tests/ui/pin-ergonomics/auxiliary/non_pin_project.rs created+3| ... | ... | @@ -0,0 +1,3 @@ |
| 1 | // A plain, non-`#[pin_v2]` type defined in another crate, so it has no local span in the | |
| 2 | // downstream crate that projects through it. | |
| 3 | pub struct Foreign<T>(pub T); |
tests/ui/pin-ergonomics/pin-pattern-foreign-non-pin-project.rs created+22| ... | ... | @@ -0,0 +1,22 @@ |
| 1 | //@ edition:2024 | |
| 2 | //@ aux-build:non_pin_project.rs | |
| 3 | #![feature(pin_ergonomics)] | |
| 4 | #![allow(incomplete_features)] | |
| 5 | ||
| 6 | // Regression test for #157634, exercising the diagnostic good-practice raised in the #157542 | |
| 7 | // review: the projection error must be emitted even when the projected-through type comes from | |
| 8 | // another crate and therefore has no local span. `ProjectOnNonPinProjectType` carries its | |
| 9 | // `def_span`/`sugg_span` as `Option<Span>`, so for a foreign type the "type defined here" note | |
| 10 | // and the `#[pin_v2]` suggestion are dropped rather than suppressing the error itself. | |
| 11 | ||
| 12 | extern crate non_pin_project; | |
| 13 | ||
| 14 | use non_pin_project::Foreign; | |
| 15 | use std::pin::Pin; | |
| 16 | ||
| 17 | fn project<T>(p: Pin<&mut Foreign<T>>) { | |
| 18 | let &pin mut Foreign(ref pin mut _x) = p; | |
| 19 | //~^ ERROR cannot project on type that is not `#[pin_v2]` | |
| 20 | } | |
| 21 | ||
| 22 | fn main() {} |
tests/ui/pin-ergonomics/pin-pattern-foreign-non-pin-project.stderr created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | error: cannot project on type that is not `#[pin_v2]` | |
| 2 | --> $DIR/pin-pattern-foreign-non-pin-project.rs:18:18 | |
| 3 | | | |
| 4 | LL | let &pin mut Foreign(ref pin mut _x) = p; | |
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^ | |
| 6 | ||
| 7 | error: aborting due to 1 previous error | |
| 8 |
tests/ui/pin-ergonomics/pin-pattern-non-pin-project.rs created+61| ... | ... | @@ -0,0 +1,61 @@ |
| 1 | //@ edition:2024 | |
| 2 | #![feature(pin_ergonomics)] | |
| 3 | #![allow(incomplete_features)] | |
| 4 | ||
| 5 | // Regression test for #157634. | |
| 6 | // | |
| 7 | // The implicit pin-projection (via match ergonomics) is only allowed on `#[pin_v2]` types, but | |
| 8 | // the explicit `&pin mut` / `ref pin` pattern forms used to project through *any* type. That is | |
| 9 | // unsound: it lets safe code form a `Pin<&mut Field>` for a type that never opted into structural | |
| 10 | // pinning, breaking the `Pin` guarantee. Check that the explicit forms are now gated the same way | |
| 11 | // as the implicit one. | |
| 12 | ||
| 13 | use std::pin::Pin; | |
| 14 | ||
| 15 | struct NotPinProject<T>(T); | |
| 16 | ||
| 17 | struct NotPinProjectStruct<T> { | |
| 18 | x: T, | |
| 19 | } | |
| 20 | ||
| 21 | enum NotPinProjectEnum<T> { | |
| 22 | Tuple(T), | |
| 23 | Struct { x: T }, | |
| 24 | } | |
| 25 | ||
| 26 | fn tuple_struct<T>(p: Pin<&mut NotPinProject<T>>) { | |
| 27 | let &pin mut NotPinProject(ref pin mut _x) = p; | |
| 28 | //~^ ERROR cannot project on type that is not `#[pin_v2]` | |
| 29 | } | |
| 30 | ||
| 31 | fn struct_field<T>(p: Pin<&mut NotPinProjectStruct<T>>) { | |
| 32 | let &pin mut NotPinProjectStruct { x: ref pin mut _x } = p; | |
| 33 | //~^ ERROR cannot project on type that is not `#[pin_v2]` | |
| 34 | } | |
| 35 | ||
| 36 | fn shared<T>(p: Pin<&NotPinProject<T>>) { | |
| 37 | let &pin const NotPinProject(ref pin const _x) = p; | |
| 38 | //~^ ERROR cannot project on type that is not `#[pin_v2]` | |
| 39 | } | |
| 40 | ||
| 41 | fn enum_tuple<T>(p: Pin<&mut NotPinProjectEnum<T>>) { | |
| 42 | if let &pin mut NotPinProjectEnum::Tuple(ref pin mut _x) = p {} | |
| 43 | //~^ ERROR cannot project on type that is not `#[pin_v2]` | |
| 44 | } | |
| 45 | ||
| 46 | fn enum_struct<T>(p: Pin<&mut NotPinProjectEnum<T>>) { | |
| 47 | if let &pin mut NotPinProjectEnum::Struct { x: ref pin mut _x } = p {} | |
| 48 | //~^ ERROR cannot project on type that is not `#[pin_v2]` | |
| 49 | } | |
| 50 | ||
| 51 | // The exact shape from the issue: `Thing` unconditionally implements `Unpin`, so it must not be | |
| 52 | // possible to project a pinned reference to one of its fields. | |
| 53 | struct Thing<T>(T); | |
| 54 | impl<T> Unpin for Thing<T> {} | |
| 55 | ||
| 56 | fn issue_157634<T>(pinned_thing: Pin<&mut Thing<Option<T>>>) { | |
| 57 | let &pin mut Thing(ref pin mut _pinned_option) = pinned_thing; | |
| 58 | //~^ ERROR cannot project on type that is not `#[pin_v2]` | |
| 59 | } | |
| 60 | ||
| 61 | fn main() {} |
tests/ui/pin-ergonomics/pin-pattern-non-pin-project.stderr created+104| ... | ... | @@ -0,0 +1,104 @@ |
| 1 | error: cannot project on type that is not `#[pin_v2]` | |
| 2 | --> $DIR/pin-pattern-non-pin-project.rs:27:18 | |
| 3 | | | |
| 4 | LL | let &pin mut NotPinProject(ref pin mut _x) = p; | |
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 6 | | | |
| 7 | note: type defined here | |
| 8 | --> $DIR/pin-pattern-non-pin-project.rs:15:1 | |
| 9 | | | |
| 10 | LL | struct NotPinProject<T>(T); | |
| 11 | | ^^^^^^^^^^^^^^^^^^^^^^^ | |
| 12 | help: add `#[pin_v2]` here | |
| 13 | | | |
| 14 | LL + #[pin_v2] | |
| 15 | LL | struct NotPinProject<T>(T); | |
| 16 | | | |
| 17 | ||
| 18 | error: cannot project on type that is not `#[pin_v2]` | |
| 19 | --> $DIR/pin-pattern-non-pin-project.rs:32:18 | |
| 20 | | | |
| 21 | LL | let &pin mut NotPinProjectStruct { x: ref pin mut _x } = p; | |
| 22 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 23 | | | |
| 24 | note: type defined here | |
| 25 | --> $DIR/pin-pattern-non-pin-project.rs:17:1 | |
| 26 | | | |
| 27 | LL | struct NotPinProjectStruct<T> { | |
| 28 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 29 | help: add `#[pin_v2]` here | |
| 30 | | | |
| 31 | LL + #[pin_v2] | |
| 32 | LL | struct NotPinProjectStruct<T> { | |
| 33 | | | |
| 34 | ||
| 35 | error: cannot project on type that is not `#[pin_v2]` | |
| 36 | --> $DIR/pin-pattern-non-pin-project.rs:37:20 | |
| 37 | | | |
| 38 | LL | let &pin const NotPinProject(ref pin const _x) = p; | |
| 39 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 40 | | | |
| 41 | note: type defined here | |
| 42 | --> $DIR/pin-pattern-non-pin-project.rs:15:1 | |
| 43 | | | |
| 44 | LL | struct NotPinProject<T>(T); | |
| 45 | | ^^^^^^^^^^^^^^^^^^^^^^^ | |
| 46 | help: add `#[pin_v2]` here | |
| 47 | | | |
| 48 | LL + #[pin_v2] | |
| 49 | LL | struct NotPinProject<T>(T); | |
| 50 | | | |
| 51 | ||
| 52 | error: cannot project on type that is not `#[pin_v2]` | |
| 53 | --> $DIR/pin-pattern-non-pin-project.rs:42:21 | |
| 54 | | | |
| 55 | LL | if let &pin mut NotPinProjectEnum::Tuple(ref pin mut _x) = p {} | |
| 56 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 57 | | | |
| 58 | note: type defined here | |
| 59 | --> $DIR/pin-pattern-non-pin-project.rs:21:1 | |
| 60 | | | |
| 61 | LL | enum NotPinProjectEnum<T> { | |
| 62 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 63 | help: add `#[pin_v2]` here | |
| 64 | | | |
| 65 | LL + #[pin_v2] | |
| 66 | LL | enum NotPinProjectEnum<T> { | |
| 67 | | | |
| 68 | ||
| 69 | error: cannot project on type that is not `#[pin_v2]` | |
| 70 | --> $DIR/pin-pattern-non-pin-project.rs:47:21 | |
| 71 | | | |
| 72 | LL | if let &pin mut NotPinProjectEnum::Struct { x: ref pin mut _x } = p {} | |
| 73 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 74 | | | |
| 75 | note: type defined here | |
| 76 | --> $DIR/pin-pattern-non-pin-project.rs:21:1 | |
| 77 | | | |
| 78 | LL | enum NotPinProjectEnum<T> { | |
| 79 | | ^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 80 | help: add `#[pin_v2]` here | |
| 81 | | | |
| 82 | LL + #[pin_v2] | |
| 83 | LL | enum NotPinProjectEnum<T> { | |
| 84 | | | |
| 85 | ||
| 86 | error: cannot project on type that is not `#[pin_v2]` | |
| 87 | --> $DIR/pin-pattern-non-pin-project.rs:57:18 | |
| 88 | | | |
| 89 | LL | let &pin mut Thing(ref pin mut _pinned_option) = pinned_thing; | |
| 90 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 91 | | | |
| 92 | note: type defined here | |
| 93 | --> $DIR/pin-pattern-non-pin-project.rs:53:1 | |
| 94 | | | |
| 95 | LL | struct Thing<T>(T); | |
| 96 | | ^^^^^^^^^^^^^^^ | |
| 97 | help: add `#[pin_v2]` here | |
| 98 | | | |
| 99 | LL + #[pin_v2] | |
| 100 | LL | struct Thing<T>(T); | |
| 101 | | | |
| 102 | ||
| 103 | error: aborting due to 6 previous errors | |
| 104 |
tests/ui/pin-ergonomics/user-type-projection.rs+1| ... | ... | @@ -9,6 +9,7 @@ |
| 9 | 9 | // Historically, this could occur when the code handling those projections did not know |
| 10 | 10 | // about `&pin` patterns, and incorrectly treated them as plain `&`/`&mut` patterns instead. |
| 11 | 11 | |
| 12 | #[pin_v2] | |
| 12 | 13 | struct Data { |
| 13 | 14 | x: u32 |
| 14 | 15 | } |
tests/ui/reborrow/auxiliary/reborrow_foreign_private.rs created+6| ... | ... | @@ -0,0 +1,6 @@ |
| 1 | #![allow(dead_code)] | |
| 2 | ||
| 3 | #[derive(Clone, Copy)] | |
| 4 | pub struct ForeignRef<'a> { | |
| 5 | value: &'a i32, | |
| 6 | } |
tests/ui/reborrow/coerce-shared-associated-type-field.rs created+30| ... | ... | @@ -0,0 +1,30 @@ |
| 1 | #![feature(reborrow)] | |
| 2 | #![allow(dead_code)] | |
| 3 | ||
| 4 | use std::marker::{CoerceShared, Reborrow}; | |
| 5 | ||
| 6 | trait Trait { | |
| 7 | type Assoc; | |
| 8 | } | |
| 9 | ||
| 10 | impl Trait for i32 { | |
| 11 | type Assoc = i64; | |
| 12 | } | |
| 13 | ||
| 14 | struct MyMut<'a> { | |
| 15 | x: &'a (), | |
| 16 | y: i64, | |
| 17 | } | |
| 18 | ||
| 19 | #[derive(Copy, Clone)] | |
| 20 | struct MyRef<'a> { | |
| 21 | x: &'a (), | |
| 22 | y: <i32 as Trait>::Assoc, | |
| 23 | } | |
| 24 | ||
| 25 | impl Reborrow for MyMut<'_> {} | |
| 26 | ||
| 27 | impl<'a> CoerceShared<MyRef<'a>> for MyMut<'a> {} | |
| 28 | //~^ ERROR | |
| 29 | ||
| 30 | fn main() {} |
tests/ui/reborrow/coerce-shared-associated-type-field.stderr created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced | |
| 2 | --> $DIR/coerce-shared-associated-type-field.rs:27:10 | |
| 3 | | | |
| 4 | LL | impl<'a> CoerceShared<MyRef<'a>> for MyMut<'a> {} | |
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^ | |
| 6 | ||
| 7 | error: aborting due to 1 previous error | |
| 8 |
tests/ui/reborrow/coerce-shared-decl-macro-hygiene.rs created+26| ... | ... | @@ -0,0 +1,26 @@ |
| 1 | #![feature(reborrow, decl_macro)] | |
| 2 | #![allow(incomplete_features)] | |
| 3 | ||
| 4 | use std::marker::{CoerceShared, Reborrow}; | |
| 5 | ||
| 6 | macro my_macro($field:ident) { | |
| 7 | pub struct MyMut<'a> { | |
| 8 | $field: &'a i32, | |
| 9 | field: &'a i64, | |
| 10 | } | |
| 11 | ||
| 12 | #[derive(Clone, Copy)] | |
| 13 | pub struct MyRef<'a> { | |
| 14 | $field: &'a i32, | |
| 15 | field: &'a i64, | |
| 16 | } | |
| 17 | ||
| 18 | impl Reborrow for MyMut<'_> {} | |
| 19 | ||
| 20 | impl<'a> CoerceShared<MyRef<'a>> for MyMut<'a> {} | |
| 21 | //~^ ERROR | |
| 22 | } | |
| 23 | ||
| 24 | my_macro!(field); | |
| 25 | ||
| 26 | fn main() {} |
tests/ui/reborrow/coerce-shared-decl-macro-hygiene.stderr created+13| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced | |
| 2 | --> $DIR/coerce-shared-decl-macro-hygiene.rs:20:14 | |
| 3 | | | |
| 4 | LL | impl<'a> CoerceShared<MyRef<'a>> for MyMut<'a> {} | |
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^ | |
| 6 | ... | |
| 7 | LL | my_macro!(field); | |
| 8 | | ---------------- in this macro invocation | |
| 9 | | | |
| 10 | = note: this error originates in the macro `my_macro` (in Nightly builds, run with -Z macro-backtrace for more info) | |
| 11 | ||
| 12 | error: aborting due to 1 previous error | |
| 13 |
tests/ui/reborrow/coerce-shared-extra-marker.rs created+37| ... | ... | @@ -0,0 +1,37 @@ |
| 1 | //@ run-pass | |
| 2 | ||
| 3 | #![feature(reborrow)] | |
| 4 | #![allow(dead_code)] | |
| 5 | ||
| 6 | use std::marker::{CoerceShared, PhantomData, Reborrow}; | |
| 7 | ||
| 8 | struct MarkerExtraMut<'a, T> { | |
| 9 | value: &'a mut T, | |
| 10 | marker: PhantomData<&'a mut T>, | |
| 11 | } | |
| 12 | ||
| 13 | impl<'a, T> Reborrow for MarkerExtraMut<'a, T> {} | |
| 14 | ||
| 15 | struct MarkerExtraRef<'a, T> { | |
| 16 | value: &'a T, | |
| 17 | } | |
| 18 | ||
| 19 | impl<'a, T> Clone for MarkerExtraRef<'a, T> { | |
| 20 | fn clone(&self) -> Self { | |
| 21 | *self | |
| 22 | } | |
| 23 | } | |
| 24 | ||
| 25 | impl<'a, T> Copy for MarkerExtraRef<'a, T> {} | |
| 26 | ||
| 27 | impl<'a, T> CoerceShared<MarkerExtraRef<'a, T>> for MarkerExtraMut<'a, T> {} | |
| 28 | ||
| 29 | fn get<'a>(value: MarkerExtraRef<'a, i32>) -> &'a i32 { | |
| 30 | value.value | |
| 31 | } | |
| 32 | ||
| 33 | fn main() { | |
| 34 | let mut value = 1; | |
| 35 | let wrapped = MarkerExtraMut { value: &mut value, marker: PhantomData }; | |
| 36 | assert_eq!(*get(wrapped), 1); | |
| 37 | } |
tests/ui/reborrow/coerce-shared-field-lifetime-swap.rs created+21| ... | ... | @@ -0,0 +1,21 @@ |
| 1 | #![feature(reborrow)] | |
| 2 | ||
| 3 | use std::marker::{CoerceShared, Reborrow}; | |
| 4 | ||
| 5 | struct MyMut<'a> { | |
| 6 | x: &'static (), | |
| 7 | y: &'a (), | |
| 8 | } | |
| 9 | ||
| 10 | impl Reborrow for MyMut<'_> {} | |
| 11 | ||
| 12 | #[derive(Copy, Clone)] | |
| 13 | struct MyRef<'a> { | |
| 14 | x: &'a (), | |
| 15 | y: &'static (), | |
| 16 | } | |
| 17 | ||
| 18 | impl<'a> CoerceShared<MyRef<'a>> for MyMut<'a> {} | |
| 19 | //~^ ERROR | |
| 20 | ||
| 21 | fn main() {} |
tests/ui/reborrow/coerce-shared-field-lifetime-swap.stderr created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced | |
| 2 | --> $DIR/coerce-shared-field-lifetime-swap.rs:18:10 | |
| 3 | | | |
| 4 | LL | impl<'a> CoerceShared<MyRef<'a>> for MyMut<'a> {} | |
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^ | |
| 6 | ||
| 7 | error: aborting due to 1 previous error | |
| 8 |
tests/ui/reborrow/coerce-shared-field-relations.rs created+51| ... | ... | @@ -0,0 +1,51 @@ |
| 1 | #![feature(reborrow)] | |
| 2 | ||
| 3 | use std::marker::{CoerceShared, Reborrow}; | |
| 4 | ||
| 5 | struct CustomMut<'a, T> { | |
| 6 | value: &'a mut T, | |
| 7 | } | |
| 8 | ||
| 9 | impl<'a, T> Reborrow for CustomMut<'a, T> {} | |
| 10 | ||
| 11 | #[derive(Clone, Copy)] | |
| 12 | struct CustomRef<'a, T> { | |
| 13 | value: &'a T, | |
| 14 | } | |
| 15 | ||
| 16 | impl<'a, T> CoerceShared<CustomRef<'a, T>> for CustomMut<'a, T> {} | |
| 17 | ||
| 18 | struct RenamedMut<'a, T> { | |
| 19 | source: &'a mut T, | |
| 20 | } | |
| 21 | ||
| 22 | impl<'a, T> Reborrow for RenamedMut<'a, T> {} | |
| 23 | ||
| 24 | #[derive(Clone, Copy)] | |
| 25 | struct RenamedRef<'a, T> { | |
| 26 | target: &'a T, | |
| 27 | } | |
| 28 | ||
| 29 | impl<'a, T> CoerceShared<RenamedRef<'a, T>> for RenamedMut<'a, T> {} | |
| 30 | ||
| 31 | struct BadMut<'a, T> { | |
| 32 | value: &'a mut T, | |
| 33 | } | |
| 34 | ||
| 35 | impl<'a, T> Reborrow for BadMut<'a, T> {} | |
| 36 | ||
| 37 | #[derive(Clone, Copy)] | |
| 38 | struct BadRef<'a, T> { | |
| 39 | value: &'a u32, | |
| 40 | _marker: std::marker::PhantomData<T>, | |
| 41 | } | |
| 42 | ||
| 43 | impl<'a, T> CoerceShared<BadRef<'a, T>> for BadMut<'a, T> {} | |
| 44 | //~^ ERROR | |
| 45 | ||
| 46 | fn good(_value: CustomRef<'_, u32>) {} | |
| 47 | ||
| 48 | fn main() { | |
| 49 | let mut value = 1; | |
| 50 | good(CustomMut { value: &mut value }); | |
| 51 | } |
tests/ui/reborrow/coerce-shared-field-relations.stderr created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | error[E0277]: the trait bound `&'a mut T: CoerceShared<&'a u32>` is not satisfied | |
| 2 | --> $DIR/coerce-shared-field-relations.rs:43:1 | |
| 3 | | | |
| 4 | LL | impl<'a, T> CoerceShared<BadRef<'a, T>> for BadMut<'a, T> {} | |
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `CoerceShared<&'a u32>` is not implemented for `&'a mut T` | |
| 6 | ||
| 7 | error: aborting due to 1 previous error | |
| 8 | ||
| 9 | For more information about this error, try `rustc --explain E0277`. |
tests/ui/reborrow/coerce-shared-foreign-private-field.rs created+20| ... | ... | @@ -0,0 +1,20 @@ |
| 1 | //@ check-pass | |
| 2 | ||
| 3 | //@ aux-build: reborrow_foreign_private.rs | |
| 4 | ||
| 5 | #![feature(reborrow)] | |
| 6 | ||
| 7 | extern crate reborrow_foreign_private; | |
| 8 | ||
| 9 | use reborrow_foreign_private::ForeignRef; | |
| 10 | use std::marker::{CoerceShared, Reborrow}; | |
| 11 | ||
| 12 | struct LocalMut<'a> { | |
| 13 | value: &'a mut i32, | |
| 14 | } | |
| 15 | ||
| 16 | impl<'a> Reborrow for LocalMut<'a> {} | |
| 17 | ||
| 18 | impl<'a> CoerceShared<ForeignRef<'a>> for LocalMut<'a> {} | |
| 19 | ||
| 20 | fn main() {} |
tests/ui/reborrow/coerce-shared-foreign-private-tuple-field.rs created+22| ... | ... | @@ -0,0 +1,22 @@ |
| 1 | //@ check-pass | |
| 2 | ||
| 3 | #![feature(reborrow)] | |
| 4 | ||
| 5 | use std::marker::{CoerceShared, PhantomData, Reborrow}; | |
| 6 | ||
| 7 | mod foreign_ptr { | |
| 8 | use std::marker::PhantomData; | |
| 9 | ||
| 10 | #[derive(Clone, Copy)] | |
| 11 | pub struct ForeignPtrRef<'a>(*const i32, PhantomData<&'a ()>); | |
| 12 | } | |
| 13 | ||
| 14 | use foreign_ptr::ForeignPtrRef; | |
| 15 | ||
| 16 | struct LocalPtrMut<'a>(*const i32, PhantomData<&'a ()>); | |
| 17 | ||
| 18 | impl<'a> Reborrow for LocalPtrMut<'a> {} | |
| 19 | ||
| 20 | impl<'a> CoerceShared<ForeignPtrRef<'a>> for LocalPtrMut<'a> {} | |
| 21 | ||
| 22 | fn main() {} |
tests/ui/reborrow/coerce-shared-generics.rs created+41| ... | ... | @@ -0,0 +1,41 @@ |
| 1 | #![feature(reborrow)] | |
| 2 | #![allow(dead_code)] | |
| 3 | ||
| 4 | use std::marker::{CoerceShared, Reborrow}; | |
| 5 | ||
| 6 | struct BufferMut<'a, T, U, const N: usize> { | |
| 7 | data: &'a mut [T; N], | |
| 8 | meta: U, | |
| 9 | } | |
| 10 | ||
| 11 | impl<'a, T, U: Copy, const N: usize> Reborrow for BufferMut<'a, T, U, N> {} | |
| 12 | ||
| 13 | struct BufferRef<'a, T, U, const N: usize> { | |
| 14 | data: &'a [T; N], | |
| 15 | meta: U, | |
| 16 | } | |
| 17 | ||
| 18 | impl<'a, T, U: Copy, const N: usize> Clone for BufferRef<'a, T, U, N> { | |
| 19 | fn clone(&self) -> Self { | |
| 20 | *self | |
| 21 | } | |
| 22 | } | |
| 23 | ||
| 24 | impl<'a, T, U: Copy, const N: usize> Copy for BufferRef<'a, T, U, N> {} | |
| 25 | ||
| 26 | impl<'a, T, U: Copy, const N: usize> CoerceShared<BufferRef<'a, T, U, N>> | |
| 27 | //~^ ERROR | |
| 28 | for BufferMut<'a, T, U, N> | |
| 29 | { | |
| 30 | } | |
| 31 | ||
| 32 | fn inspect<const N: usize>(buffer: BufferRef<'_, u8, u16, N>) -> (usize, u16) { | |
| 33 | (buffer.data.len(), buffer.meta) | |
| 34 | } | |
| 35 | ||
| 36 | fn main() { | |
| 37 | let mut data = [1, 2, 3, 4]; | |
| 38 | let buffer = BufferMut { data: &mut data, meta: 9_u16 }; | |
| 39 | ||
| 40 | assert_eq!(inspect(buffer), (4, 9)); | |
| 41 | } |
tests/ui/reborrow/coerce-shared-generics.stderr created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced | |
| 2 | --> $DIR/coerce-shared-generics.rs:26:38 | |
| 3 | | | |
| 4 | LL | impl<'a, T, U: Copy, const N: usize> CoerceShared<BufferRef<'a, T, U, N>> | |
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 6 | ||
| 7 | error: aborting due to 1 previous error | |
| 8 |
tests/ui/reborrow/coerce-shared-lifetime-mismatch.rs created+23| ... | ... | @@ -0,0 +1,23 @@ |
| 1 | #![feature(reborrow)] | |
| 2 | ||
| 3 | // The impl is accepted, but using it to coerce a local marker into a `'static` | |
| 4 | // target still requires the local borrow to live for `'static`. | |
| 5 | ||
| 6 | use std::marker::{CoerceShared, PhantomData, Reborrow}; | |
| 7 | ||
| 8 | struct CustomMarker<'a>(PhantomData<&'a ()>); | |
| 9 | ||
| 10 | impl<'a> Reborrow for CustomMarker<'a> {} | |
| 11 | ||
| 12 | #[derive(Clone, Copy)] | |
| 13 | struct StaticMarkerRef<'a>(PhantomData<&'a ()>); | |
| 14 | ||
| 15 | impl<'a> CoerceShared<StaticMarkerRef<'static>> for CustomMarker<'a> {} | |
| 16 | ||
| 17 | fn method(_a: StaticMarkerRef<'static>) {} | |
| 18 | ||
| 19 | fn main() { | |
| 20 | let a = CustomMarker(PhantomData); | |
| 21 | method(a); | |
| 22 | //~^ ERROR | |
| 23 | } |
tests/ui/reborrow/coerce-shared-lifetime-mismatch.stderr created+17| ... | ... | @@ -0,0 +1,17 @@ |
| 1 | error[E0597]: `a` does not live long enough | |
| 2 | --> $DIR/coerce-shared-lifetime-mismatch.rs:21:12 | |
| 3 | | | |
| 4 | LL | let a = CustomMarker(PhantomData); | |
| 5 | | - binding `a` declared here | |
| 6 | LL | method(a); | |
| 7 | | -------^- | |
| 8 | | | | | |
| 9 | | | borrowed value does not live long enough | |
| 10 | | argument requires that `a` is borrowed for `'static` | |
| 11 | LL | | |
| 12 | LL | } | |
| 13 | | - `a` dropped here while still borrowed | |
| 14 | ||
| 15 | error: aborting due to 1 previous error | |
| 16 | ||
| 17 | For more information about this error, try `rustc --explain E0597`. |
tests/ui/reborrow/coerce-shared-missing-target-field.rs created+20| ... | ... | @@ -0,0 +1,20 @@ |
| 1 | #![feature(reborrow)] | |
| 2 | ||
| 3 | use std::marker::{CoerceShared, Reborrow}; | |
| 4 | ||
| 5 | struct MissingSourceMut<'a, T> { | |
| 6 | value: &'a mut T, | |
| 7 | } | |
| 8 | ||
| 9 | impl<'a, T> Reborrow for MissingSourceMut<'a, T> {} | |
| 10 | ||
| 11 | #[derive(Clone, Copy)] | |
| 12 | struct MissingSourceRef<'a, T> { | |
| 13 | value: &'a T, | |
| 14 | len: usize, | |
| 15 | } | |
| 16 | ||
| 17 | impl<'a, T> CoerceShared<MissingSourceRef<'a, T>> for MissingSourceMut<'a, T> {} | |
| 18 | //~^ ERROR | |
| 19 | ||
| 20 | fn main() {} |
tests/ui/reborrow/coerce-shared-missing-target-field.stderr created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced | |
| 2 | --> $DIR/coerce-shared-missing-target-field.rs:17:13 | |
| 3 | | | |
| 4 | LL | impl<'a, T> CoerceShared<MissingSourceRef<'a, T>> for MissingSourceMut<'a, T> {} | |
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 6 | ||
| 7 | error: aborting due to 1 previous error | |
| 8 |
tests/ui/reborrow/coerce-shared-mut-ref-field-validation.rs created+43| ... | ... | @@ -0,0 +1,43 @@ |
| 1 | #![feature(reborrow)] | |
| 2 | ||
| 3 | use std::marker::{CoerceShared, Reborrow}; | |
| 4 | ||
| 5 | trait Trait { | |
| 6 | type Assoc; | |
| 7 | } | |
| 8 | ||
| 9 | impl Trait for i32 { | |
| 10 | type Assoc = u32; | |
| 11 | } | |
| 12 | ||
| 13 | type AliasMutField<'a> = &'a mut <i32 as Trait>::Assoc; | |
| 14 | type AliasRefField<'a> = &'a u32; | |
| 15 | ||
| 16 | struct AliasMut<'a> { | |
| 17 | value: AliasMutField<'a>, | |
| 18 | } | |
| 19 | ||
| 20 | impl Reborrow for AliasMut<'_> {} | |
| 21 | ||
| 22 | #[derive(Copy, Clone)] | |
| 23 | struct AliasRef<'a> { | |
| 24 | value: AliasRefField<'a>, | |
| 25 | } | |
| 26 | ||
| 27 | impl<'a> CoerceShared<AliasRef<'a>> for AliasMut<'a> {} | |
| 28 | //~^ ERROR | |
| 29 | ||
| 30 | struct InnerLifetimeMut<'a> { | |
| 31 | value: &'a mut &'static (), | |
| 32 | } | |
| 33 | ||
| 34 | impl Reborrow for InnerLifetimeMut<'_> {} | |
| 35 | ||
| 36 | #[derive(Copy, Clone)] | |
| 37 | struct InnerLifetimeRef<'a> { | |
| 38 | value: &'a &'a (), | |
| 39 | } | |
| 40 | ||
| 41 | impl<'a> CoerceShared<InnerLifetimeRef<'a>> for InnerLifetimeMut<'a> {} | |
| 42 | ||
| 43 | fn main() {} |
tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | error[E0277]: the trait bound `&'a mut u32: CoerceShared<&'a u32>` is not satisfied | |
| 2 | --> $DIR/coerce-shared-mut-ref-field-validation.rs:27:1 | |
| 3 | | | |
| 4 | LL | impl<'a> CoerceShared<AliasRef<'a>> for AliasMut<'a> {} | |
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `CoerceShared<&'a u32>` is not implemented for `&'a mut u32` | |
| 6 | ||
| 7 | error: aborting due to 1 previous error | |
| 8 | ||
| 9 | For more information about this error, try `rustc --explain E0277`. |
tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.rs created+51| ... | ... | @@ -0,0 +1,51 @@ |
| 1 | #![feature(reborrow)] | |
| 2 | #![allow(dead_code)] | |
| 3 | ||
| 4 | use std::marker::{CoerceShared, Reborrow}; | |
| 5 | ||
| 6 | struct ExtraMut<'a, T> { | |
| 7 | value: &'a mut T, | |
| 8 | } | |
| 9 | ||
| 10 | impl<'a, T> Reborrow for ExtraMut<'a, T> {} | |
| 11 | ||
| 12 | struct OmitMut<'a, T> { | |
| 13 | value: &'a mut T, | |
| 14 | extra: ExtraMut<'a, T>, | |
| 15 | } | |
| 16 | ||
| 17 | impl<'a, T> Reborrow for OmitMut<'a, T> {} | |
| 18 | ||
| 19 | struct OmitRef<'a, T> { | |
| 20 | value: &'a T, | |
| 21 | } | |
| 22 | ||
| 23 | impl<'a, T> Clone for OmitRef<'a, T> { | |
| 24 | fn clone(&self) -> Self { | |
| 25 | *self | |
| 26 | } | |
| 27 | } | |
| 28 | ||
| 29 | impl<'a, T> Copy for OmitRef<'a, T> {} | |
| 30 | ||
| 31 | impl<'a, T> CoerceShared<OmitRef<'a, T>> for OmitMut<'a, T> {} | |
| 32 | //~^ ERROR | |
| 33 | ||
| 34 | fn read(value: OmitRef<'_, i32>) { | |
| 35 | assert_eq!(*value.value, 1); | |
| 36 | } | |
| 37 | ||
| 38 | fn main() { | |
| 39 | let mut value = 1; | |
| 40 | let mut extra_value = 2; | |
| 41 | ||
| 42 | { | |
| 43 | let extra = ExtraMut { value: &mut extra_value }; | |
| 44 | let wrapped = OmitMut { value: &mut value, extra }; | |
| 45 | ||
| 46 | read(wrapped); | |
| 47 | } | |
| 48 | ||
| 49 | extra_value = 3; | |
| 50 | assert_eq!(extra_value, 3); | |
| 51 | } |
tests/ui/reborrow/coerce-shared-omitted-reborrow-field-after-dead.stderr created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced | |
| 2 | --> $DIR/coerce-shared-omitted-reborrow-field-after-dead.rs:31:13 | |
| 3 | | | |
| 4 | LL | impl<'a, T> CoerceShared<OmitRef<'a, T>> for OmitMut<'a, T> {} | |
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 6 | ||
| 7 | error: aborting due to 1 previous error | |
| 8 |
tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.rs created+53| ... | ... | @@ -0,0 +1,53 @@ |
| 1 | #![feature(reborrow)] | |
| 2 | ||
| 3 | use std::marker::{CoerceShared, Reborrow}; | |
| 4 | ||
| 5 | struct ExtraMut<'a, T> { | |
| 6 | value: &'a mut T, | |
| 7 | } | |
| 8 | ||
| 9 | impl<'a, T> Reborrow for ExtraMut<'a, T> {} | |
| 10 | ||
| 11 | struct OmitMut<'a, T> { | |
| 12 | value: &'a mut T, | |
| 13 | extra: ExtraMut<'a, T>, | |
| 14 | } | |
| 15 | ||
| 16 | impl<'a, T> Reborrow for OmitMut<'a, T> {} | |
| 17 | ||
| 18 | struct OmitRef<'a, T> { | |
| 19 | value: &'a T, | |
| 20 | } | |
| 21 | ||
| 22 | impl<'a, T> Clone for OmitRef<'a, T> { | |
| 23 | fn clone(&self) -> Self { | |
| 24 | *self | |
| 25 | } | |
| 26 | } | |
| 27 | ||
| 28 | impl<'a, T> Copy for OmitRef<'a, T> {} | |
| 29 | ||
| 30 | impl<'a, T> CoerceShared<OmitRef<'a, T>> for OmitMut<'a, T> {} | |
| 31 | //~^ ERROR | |
| 32 | ||
| 33 | fn get<'a>(value: OmitRef<'a, i32>) -> &'a i32 { | |
| 34 | value.value | |
| 35 | } | |
| 36 | ||
| 37 | fn main() { | |
| 38 | let mut value = 1; | |
| 39 | let mut extra_value = 2; | |
| 40 | let extra = ExtraMut { value: &mut extra_value }; | |
| 41 | ||
| 42 | let mut wrapped = OmitMut { | |
| 43 | value: &mut value, | |
| 44 | extra, | |
| 45 | }; | |
| 46 | ||
| 47 | let shared = get(wrapped); | |
| 48 | ||
| 49 | *wrapped.extra.value = 3; | |
| 50 | //~^ ERROR cannot assign to `*wrapped.extra.value` because it is borrowed | |
| 51 | ||
| 52 | let _ = shared; | |
| 53 | } |
tests/ui/reborrow/coerce-shared-omitted-reborrow-field-locked.stderr created+21| ... | ... | @@ -0,0 +1,21 @@ |
| 1 | error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced | |
| 2 | --> $DIR/coerce-shared-omitted-reborrow-field-locked.rs:30:13 | |
| 3 | | | |
| 4 | LL | impl<'a, T> CoerceShared<OmitRef<'a, T>> for OmitMut<'a, T> {} | |
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 6 | ||
| 7 | error[E0506]: cannot assign to `*wrapped.extra.value` because it is borrowed | |
| 8 | --> $DIR/coerce-shared-omitted-reborrow-field-locked.rs:49:5 | |
| 9 | | | |
| 10 | LL | let shared = get(wrapped); | |
| 11 | | ------- `*wrapped.extra.value` is borrowed here | |
| 12 | LL | | |
| 13 | LL | *wrapped.extra.value = 3; | |
| 14 | | ^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 15 | | | | |
| 16 | | `*wrapped.extra.value` is assigned to here but it was already borrowed | |
| 17 | | borrow later used here | |
| 18 | ||
| 19 | error: aborting due to 2 previous errors | |
| 20 | ||
| 21 | For more information about this error, try `rustc --explain E0506`. |
tests/ui/reborrow/coerce-shared-omitted-reborrow-field.rs created+45| ... | ... | @@ -0,0 +1,45 @@ |
| 1 | #![feature(reborrow)] | |
| 2 | #![allow(dead_code)] | |
| 3 | ||
| 4 | use std::marker::{CoerceShared, Reborrow}; | |
| 5 | ||
| 6 | struct ExtraMut<'a, T> { | |
| 7 | value: &'a mut T, | |
| 8 | } | |
| 9 | ||
| 10 | impl<'a, T> Reborrow for ExtraMut<'a, T> {} | |
| 11 | ||
| 12 | struct OmitMut<'a, T> { | |
| 13 | value: &'a mut T, | |
| 14 | extra: ExtraMut<'a, T>, | |
| 15 | } | |
| 16 | ||
| 17 | impl<'a, T> Reborrow for OmitMut<'a, T> {} | |
| 18 | ||
| 19 | struct OmitRef<'a, T> { | |
| 20 | value: &'a T, | |
| 21 | } | |
| 22 | ||
| 23 | impl<'a, T> Clone for OmitRef<'a, T> { | |
| 24 | fn clone(&self) -> Self { | |
| 25 | *self | |
| 26 | } | |
| 27 | } | |
| 28 | ||
| 29 | impl<'a, T> Copy for OmitRef<'a, T> {} | |
| 30 | ||
| 31 | impl<'a, T> CoerceShared<OmitRef<'a, T>> for OmitMut<'a, T> {} | |
| 32 | //~^ ERROR | |
| 33 | ||
| 34 | fn get<'a>(value: OmitRef<'a, i32>) -> &'a i32 { | |
| 35 | value.value | |
| 36 | } | |
| 37 | ||
| 38 | fn main() { | |
| 39 | let mut value = 1; | |
| 40 | let mut extra = 2; | |
| 41 | let extra = ExtraMut { value: &mut extra }; | |
| 42 | let wrapped = OmitMut { value: &mut value, extra }; | |
| 43 | ||
| 44 | assert_eq!(*get(wrapped), 1); | |
| 45 | } |
tests/ui/reborrow/coerce-shared-omitted-reborrow-field.stderr created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced | |
| 2 | --> $DIR/coerce-shared-omitted-reborrow-field.rs:31:13 | |
| 3 | | | |
| 4 | LL | impl<'a, T> CoerceShared<OmitRef<'a, T>> for OmitMut<'a, T> {} | |
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 6 | ||
| 7 | error: aborting due to 1 previous error | |
| 8 |
tests/ui/reborrow/coerce-shared-reordered-field.rs created+32| ... | ... | @@ -0,0 +1,32 @@ |
| 1 | #![feature(reborrow)] | |
| 2 | #![allow(dead_code)] | |
| 3 | ||
| 4 | use std::marker::{CoerceShared, Reborrow}; | |
| 5 | ||
| 6 | struct ReorderMut<'a> { | |
| 7 | a: &'a mut u8, | |
| 8 | b: &'a mut u16, | |
| 9 | } | |
| 10 | ||
| 11 | impl<'a> Reborrow for ReorderMut<'a> {} | |
| 12 | ||
| 13 | #[derive(Clone, Copy)] | |
| 14 | struct ReorderRef<'a> { | |
| 15 | b: &'a u16, | |
| 16 | a: &'a u8, | |
| 17 | } | |
| 18 | ||
| 19 | impl<'a> CoerceShared<ReorderRef<'a>> for ReorderMut<'a> {} | |
| 20 | //~^ ERROR | |
| 21 | ||
| 22 | fn read(value: ReorderRef<'_>) -> (u16, u8) { | |
| 23 | (*value.b, *value.a) | |
| 24 | } | |
| 25 | ||
| 26 | fn main() { | |
| 27 | let mut a = 1; | |
| 28 | let mut b = 2; | |
| 29 | let wrapped = ReorderMut { a: &mut a, b: &mut b }; | |
| 30 | ||
| 31 | assert_eq!(read(wrapped), (2, 1)); | |
| 32 | } |
tests/ui/reborrow/coerce-shared-reordered-field.stderr created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced | |
| 2 | --> $DIR/coerce-shared-reordered-field.rs:19:10 | |
| 3 | | | |
| 4 | LL | impl<'a> CoerceShared<ReorderRef<'a>> for ReorderMut<'a> {} | |
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 6 | ||
| 7 | error: aborting due to 1 previous error | |
| 8 |
tests/ui/reborrow/coerce-shared-wrong-generic.rs created+21| ... | ... | @@ -0,0 +1,21 @@ |
| 1 | #![feature(reborrow)] | |
| 2 | ||
| 3 | use std::marker::{CoerceShared, PhantomData, Reborrow}; | |
| 4 | ||
| 5 | struct GenericMut<'a, T, U> { | |
| 6 | value: &'a mut T, | |
| 7 | marker: PhantomData<U>, | |
| 8 | } | |
| 9 | ||
| 10 | impl<'a, T, U> Reborrow for GenericMut<'a, T, U> {} | |
| 11 | ||
| 12 | #[derive(Clone, Copy)] | |
| 13 | struct GenericRef<'a, T, U> { | |
| 14 | value: &'a U, | |
| 15 | marker: PhantomData<T>, | |
| 16 | } | |
| 17 | ||
| 18 | impl<'a, T, U> CoerceShared<GenericRef<'a, T, U>> for GenericMut<'a, T, U> {} | |
| 19 | //~^ ERROR | |
| 20 | ||
| 21 | fn main() {} |
tests/ui/reborrow/coerce-shared-wrong-generic.stderr created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | error[E0277]: the trait bound `&'a mut T: CoerceShared<&'a U>` is not satisfied | |
| 2 | --> $DIR/coerce-shared-wrong-generic.rs:18:1 | |
| 3 | | | |
| 4 | LL | impl<'a, T, U> CoerceShared<GenericRef<'a, T, U>> for GenericMut<'a, T, U> {} | |
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `CoerceShared<&'a U>` is not implemented for `&'a mut T` | |
| 6 | ||
| 7 | error: aborting due to 1 previous error | |
| 8 | ||
| 9 | For more information about this error, try `rustc --explain E0277`. |
tests/ui/share-trait/share-trait-final-method.rs created+21| ... | ... | @@ -0,0 +1,21 @@ |
| 1 | #![feature(final_associated_functions)] | |
| 2 | #![feature(share_trait)] | |
| 3 | ||
| 4 | use std::clone::Share; | |
| 5 | ||
| 6 | struct Alias; | |
| 7 | ||
| 8 | impl Clone for Alias { | |
| 9 | fn clone(&self) -> Self { | |
| 10 | Alias | |
| 11 | } | |
| 12 | } | |
| 13 | ||
| 14 | impl Share for Alias { | |
| 15 | fn share(&self) -> Self { | |
| 16 | //~^ ERROR cannot override `share` because it already has a `final` definition in the trait | |
| 17 | Alias | |
| 18 | } | |
| 19 | } | |
| 20 | ||
| 21 | fn main() {} |
tests/ui/share-trait/share-trait-final-method.stderr created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | error: cannot override `share` because it already has a `final` definition in the trait | |
| 2 | --> $DIR/share-trait-final-method.rs:15:5 | |
| 3 | | | |
| 4 | LL | fn share(&self) -> Self { | |
| 5 | | ^^^^^^^^^^^^^^^^^^^^^^^ | |
| 6 | | | |
| 7 | note: `share` is marked final here | |
| 8 | --> $SRC_DIR/core/src/clone.rs:LL:COL | |
| 9 | ||
| 10 | error: aborting due to 1 previous error | |
| 11 |
tests/ui/sized/stack-overflow-trait-infer-98842.stderr+2-5| ... | ... | @@ -7,11 +7,8 @@ LL | struct Foo(<&'static Foo as ::core::ops::Deref>::Target); |
| 7 | 7 | note: ...which requires computing layout of `<&'static Foo as core::ops::deref::Deref>::Target`... |
| 8 | 8 | --> $SRC_DIR/core/src/ops/deref.rs:LL:COL |
| 9 | 9 | = note: ...which again requires computing layout of `Foo`, completing the cycle |
| 10 | note: cycle used when const-evaluating + checking `_` | |
| 11 | --> $DIR/stack-overflow-trait-infer-98842.rs:13:1 | |
| 12 | | | |
| 13 | LL | const _: *const Foo = 0 as _; | |
| 14 | | ^^^^^^^^^^^^^^^^^^^ | |
| 10 | note: cycle used when computing layout of `<&'static Foo as core::ops::deref::Deref>::Target` | |
| 11 | --> $SRC_DIR/core/src/ops/deref.rs:LL:COL | |
| 15 | 12 | = note: for more information, see <https://rustc-dev-guide.rust-lang.org/overview.html#queries> and <https://rustc-dev-guide.rust-lang.org/query.html> |
| 16 | 13 | |
| 17 | 14 | error: aborting due to 1 previous error |
tests/ui/wf/let-pat-inferred-non-wf.rs deleted-58| ... | ... | @@ -1,58 +0,0 @@ |
| 1 | // Regression test for https://github.com/rust-lang/rust/issues/150040 | |
| 2 | // When a `let PAT;` has no explicit type, later assignments can infer a non-well-formed | |
| 3 | // pattern type such as `[str; 2]` or `(str, i32)`. We must reject those array and tuple | |
| 4 | // patterns instead of accepting the invalid type or causing ICE. | |
| 5 | ||
| 6 | #![allow(unused)] | |
| 7 | ||
| 8 | struct S<T: ?Sized>(T); | |
| 9 | ||
| 10 | fn should_fail_1() { | |
| 11 | let ref y @ [ref x, _]; //~ ERROR E0277 | |
| 12 | x = ""; | |
| 13 | } | |
| 14 | ||
| 15 | fn should_fail_2() { | |
| 16 | let [ref x]; //~ ERROR E0277 | |
| 17 | x = ""; | |
| 18 | } | |
| 19 | ||
| 20 | fn should_fail_3() { | |
| 21 | let [[ref x], [_, y @ ..]]; //~ ERROR E0277 | |
| 22 | x = ""; | |
| 23 | y = []; | |
| 24 | } | |
| 25 | ||
| 26 | fn should_fail_4() { | |
| 27 | let [(ref a, b), x]; //~ ERROR E0277 | |
| 28 | a = ""; | |
| 29 | b = 5; | |
| 30 | } | |
| 31 | ||
| 32 | fn should_fail_5() { | |
| 33 | let (ref a, b); //~ ERROR E0277 | |
| 34 | a = ""; | |
| 35 | b = 5; | |
| 36 | } | |
| 37 | ||
| 38 | fn should_fail_6() { | |
| 39 | let [S(ref x)]; //~ ERROR E0277 | |
| 40 | x = ""; | |
| 41 | } | |
| 42 | ||
| 43 | fn should_pass_1() { | |
| 44 | let ref x; | |
| 45 | x = ""; | |
| 46 | } | |
| 47 | ||
| 48 | fn should_pass_2() { | |
| 49 | let ref y @ (ref x,); | |
| 50 | x = ""; | |
| 51 | } | |
| 52 | ||
| 53 | fn should_pass_3() { | |
| 54 | let S(ref x); | |
| 55 | x = ""; | |
| 56 | } | |
| 57 | ||
| 58 | fn main() {} |
tests/ui/wf/let-pat-inferred-non-wf.stderr deleted-62| ... | ... | @@ -1,62 +0,0 @@ |
| 1 | error[E0277]: the size for values of type `str` cannot be known at compilation time | |
| 2 | --> $DIR/let-pat-inferred-non-wf.rs:11:9 | |
| 3 | | | |
| 4 | LL | let ref y @ [ref x, _]; | |
| 5 | | ^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | |
| 6 | | | |
| 7 | = help: the trait `Sized` is not implemented for `str` | |
| 8 | = note: slice and array elements must have `Sized` type | |
| 9 | ||
| 10 | error[E0277]: the size for values of type `str` cannot be known at compilation time | |
| 11 | --> $DIR/let-pat-inferred-non-wf.rs:16:9 | |
| 12 | | | |
| 13 | LL | let [ref x]; | |
| 14 | | ^^^^^^^ doesn't have a size known at compile-time | |
| 15 | | | |
| 16 | = help: the trait `Sized` is not implemented for `str` | |
| 17 | = note: slice and array elements must have `Sized` type | |
| 18 | ||
| 19 | error[E0277]: the size for values of type `str` cannot be known at compilation time | |
| 20 | --> $DIR/let-pat-inferred-non-wf.rs:21:9 | |
| 21 | | | |
| 22 | LL | let [[ref x], [_, y @ ..]]; | |
| 23 | | ^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | |
| 24 | | | |
| 25 | = help: the trait `Sized` is not implemented for `str` | |
| 26 | = note: slice and array elements must have `Sized` type | |
| 27 | ||
| 28 | error[E0277]: the size for values of type `str` cannot be known at compilation time | |
| 29 | --> $DIR/let-pat-inferred-non-wf.rs:27:9 | |
| 30 | | | |
| 31 | LL | let [(ref a, b), x]; | |
| 32 | | ^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | |
| 33 | | | |
| 34 | = help: the trait `Sized` is not implemented for `str` | |
| 35 | = note: only the last element of a tuple may have a dynamically sized type | |
| 36 | ||
| 37 | error[E0277]: the size for values of type `str` cannot be known at compilation time | |
| 38 | --> $DIR/let-pat-inferred-non-wf.rs:33:9 | |
| 39 | | | |
| 40 | LL | let (ref a, b); | |
| 41 | | ^^^^^^^^^^ doesn't have a size known at compile-time | |
| 42 | | | |
| 43 | = help: the trait `Sized` is not implemented for `str` | |
| 44 | = note: only the last element of a tuple may have a dynamically sized type | |
| 45 | ||
| 46 | error[E0277]: the size for values of type `str` cannot be known at compilation time | |
| 47 | --> $DIR/let-pat-inferred-non-wf.rs:39:9 | |
| 48 | | | |
| 49 | LL | let [S(ref x)]; | |
| 50 | | ^^^^^^^^^^ doesn't have a size known at compile-time | |
| 51 | | | |
| 52 | = help: within `S<str>`, the trait `Sized` is not implemented for `str` | |
| 53 | note: required because it appears within the type `S<str>` | |
| 54 | --> $DIR/let-pat-inferred-non-wf.rs:8:8 | |
| 55 | | | |
| 56 | LL | struct S<T: ?Sized>(T); | |
| 57 | | ^ | |
| 58 | = note: slice and array elements must have `Sized` type | |
| 59 | ||
| 60 | error: aborting due to 6 previous errors | |
| 61 | ||
| 62 | For more information about this error, try `rustc --explain E0277`. |