authorbors <bors@rust-lang.org> 2026-06-12 20:35:50 UTC
committerbors <bors@rust-lang.org> 2026-06-12 20:35:50 UTC
log65407954098ca3c19f0d46092cb374b5d3e9dc3c
treef1f7a80add3c963293a2970daff75f3ba7a0aa1a
parent3bdd7f86fcecc0ea65d79e62e39a70c8a2aabf15
parent5e5789c936b707dd626677242471f04314525859

Auto merge of #157828 - JonathanBrouwer:rollup-2tPqsx9, r=JonathanBrouwer

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 = [
48924892name = "rustc_type_ir_macros"
48934893version = "0.0.0"
48944894dependencies = [
4895 "indexmap",
48954896 "proc-macro2",
48964897 "quote",
48974898 "syn",
compiler/rustc_borrowck/src/diagnostics/find_use.rs+1-1
......@@ -35,7 +35,7 @@ impl<'a, 'tcx> UseFinder<'a, 'tcx> {
3535
3636 queue.push_back(self.start_point);
3737 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) {
3939 continue;
4040 }
4141
compiler/rustc_borrowck/src/region_infer/mod.rs+41-92
......@@ -30,7 +30,7 @@ use crate::diagnostics::{RegionErrorKind, RegionErrors, UniverseInfo};
3030use crate::handle_placeholders::{LoweredConstraints, RegionTracker};
3131use crate::polonius::LiveLoans;
3232use crate::polonius::legacy::PoloniusOutput;
33use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues, ToElementIndex};
33use crate::region_infer::values::{LivenessValues, RegionElement, RegionValues};
3434use crate::type_check::Locations;
3535use crate::type_check::free_region_relations::UniversalRegionRelations;
3636use crate::universal_regions::UniversalRegions;
......@@ -345,7 +345,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
345345 outlives_constraints,
346346 scc_annotations,
347347 type_tests,
348 liveness_constraints,
348 mut liveness_constraints,
349349 universe_causes,
350350 placeholder_indices,
351351 } = lowered_constraints;
......@@ -364,106 +364,54 @@ impl<'tcx> RegionInferenceContext<'tcx> {
364364 let mut scc_values =
365365 RegionValues::new(location_map, universal_regions.len(), placeholder_indices);
366366
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() {
368369 let scc = constraint_sccs.scc(region);
369 scc_values.merge_liveness(scc, region, &liveness_constraints);
370 }
371370
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:
448376 NllRegionVariableOrigin::FreeRegion => {
449 // For each free, universally quantified region X:
450
451377 // 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);
454379
455380 // Add `end(X)` into the set for X.
456 self.scc_values.add_element(scc, variable);
381 scc_values.add_free_region(scc, region);
457382 }
458383
459384 NllRegionVariableOrigin::Placeholder(placeholder) => {
460 self.scc_values.add_element(scc, placeholder);
385 scc_values.add_placeholder(scc, placeholder);
461386 }
462387
463388 NllRegionVariableOrigin::Existential { .. } => {
464389 // For existential, regions, nothing to do.
465390 }
466391 }
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,
467415 }
468416 }
469417
......@@ -495,9 +443,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
495443 /// Returns `true` if the region `r` contains the point `p`.
496444 ///
497445 /// 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 {
499447 let scc = self.constraint_sccs.scc(r);
500 self.scc_values.contains(scc, p)
448 self.scc_values.contains_point(scc, p)
501449 }
502450
503451 /// Returns the lowest statement index in `start..=end` which is not contained by `r`.
......@@ -608,7 +556,8 @@ impl<'tcx> RegionInferenceContext<'tcx> {
608556 // To propagate constraints, we walk the DAG induced by the
609557 // SCC. For each SCC `A`, we visit its successors and compute
610558 // 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.
612561 for scc_a in self.constraint_sccs.all_sccs() {
613562 // Walk each SCC `B` such that `A: B`...
614563 for &scc_b in self.constraint_sccs.successors(scc_a) {
......@@ -1643,10 +1592,10 @@ impl<'tcx> RegionInferenceContext<'tcx> {
16431592 &self.definitions[r]
16441593 }
16451594
1646 /// Check if the SCC of `r` contains `upper`.
1595 /// Check if the SCC of `r` contains `upper`, a free region.
16471596 pub(crate) fn upper_bound_in_region_scc(&self, r: RegionVid, upper: RegionVid) -> bool {
16481597 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)
16501599 }
16511600
16521601 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> {
7878 let placeholder_indices = Default::default();
7979 let mut scc_values =
8080 RegionValues::new(location_map, universal_regions.len(), placeholder_indices);
81 for variable in definitions.indices() {
81 for (variable, definition) in definitions.iter_enumerated() {
8282 let scc = constraint_sccs.scc(variable);
83 match definitions[variable].origin {
83 match definition.origin {
8484 NllRegionVariableOrigin::FreeRegion => {
85 scc_values.add_element(scc, variable);
85 scc_values.add_free_region(scc, variable);
8686 }
8787 _ => {}
8888 }
compiler/rustc_borrowck/src/region_infer/values.rs+22-64
......@@ -95,9 +95,10 @@ impl LivenessValues {
9595 }
9696 }
9797
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)
101102 }
102103
103104 /// Iterate through each region that has a value in this set.
......@@ -166,13 +167,12 @@ impl LivenessValues {
166167 /// [`point`][rustc_mir_dataflow::points::PointIndex].
167168 #[inline]
168169 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))
170171 }
171172
172173 /// Returns an iterator of all the points where `region` is live.
173174 fn live_points(&self, region: RegionVid) -> impl Iterator<Item = PointIndex> {
174 self.points()
175 .row(region)
175 self.point_liveness(region)
176176 .into_iter()
177177 .flat_map(|set| set.iter())
178178 .take_while(|&p| self.location_map.point_in_range(p))
......@@ -296,18 +296,6 @@ impl<'tcx, N: Idx> RegionValues<'tcx, N> {
296296 }
297297 }
298298
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
311299 /// Adds all elements in `r_from` to `r_to` (because e.g., `r_to:
312300 /// r_from`).
313301 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> {
316304 | self.placeholders.union_rows(r_from, r_to)
317305 }
318306
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
324307 /// Returns the lowest statement index in `start..=end` which is not contained by `r`.
325308 pub(crate) fn first_non_contained_inclusive(
326309 &self,
......@@ -337,13 +320,9 @@ impl<'tcx, N: Idx> RegionValues<'tcx, N> {
337320 Some(first_unset.index() - block.index())
338321 }
339322
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);
347326 }
348327
349328 /// Returns `true` if `sup_region` contains all the CFG points that
......@@ -405,47 +384,26 @@ impl<'tcx, N: Idx> RegionValues<'tcx, N> {
405384 pub(crate) fn region_value_str(&self, r: N) -> String {
406385 pretty_print_region_elements(self.elements_contained_in(r))
407386 }
408}
409
410pub(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
416impl 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 }
421387
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
428impl 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);
431391 }
432392
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);
435396 }
436}
437397
438impl<'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)
443402 }
444403
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)
449407 }
450408}
451409
compiler/rustc_codegen_llvm/src/context.rs+8
......@@ -201,6 +201,14 @@ pub(crate) unsafe fn create_module<'ll>(
201201 if sess.target.arch == Arch::PowerPC64 {
202202 // LLVM 22 updated the ABI alignment for double on AIX: https://github.com/llvm/llvm-project/pull/144673
203203 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 }
204212 }
205213 if sess.target.arch == Arch::AmdGpu {
206214 // 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>(
152152 cx.size_and_align_of(Ty::new_mut_ptr(cx.tcx, pointee_type))
153153 );
154154
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 };
156169
157170 return_if_di_node_created_in_meantime!(cx, unique_type_id);
158171
......@@ -389,26 +402,11 @@ fn build_dyn_type_di_node<'ll, 'tcx>(
389402}
390403
391404/// 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.
408405fn build_slice_type_di_node<'ll, 'tcx>(
409406 cx: &CodegenCx<'ll, 'tcx>,
410407 slice_type: Ty<'tcx>,
411408 unique_type_id: UniqueTypeId<'tcx>,
409 span: Span,
412410) -> DINodeCreationResult<'ll> {
413411 let element_type = match slice_type.kind() {
414412 ty::Slice(element_type) => *element_type,
......@@ -423,7 +421,20 @@ fn build_slice_type_di_node<'ll, 'tcx>(
423421
424422 let element_type_di_node = type_di_node(cx, element_type);
425423 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 }
427438}
428439
429440/// Get the debuginfo node for the given type.
......@@ -454,7 +465,7 @@ pub(crate) fn spanned_type_di_node<'ll, 'tcx>(
454465 }
455466 ty::Tuple(elements) if elements.is_empty() => build_basic_type_di_node(cx, t),
456467 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),
458469 ty::Dynamic(..) => build_dyn_type_di_node(cx, t, unique_type_id),
459470 ty::Foreign(..) => build_foreign_type_di_node(cx, t, unique_type_id),
460471 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> {
502502 let mplace = ecx.force_allocation(&place)?;
503503
504504 // 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
507511 // When the frame is dropped, these variable arguments are deallocated.
508512 ecx.frame_mut().va_list = varargs.clone();
509513 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> {
925925 }
926926 interp_ok(true)
927927 }
928 ty::RawPtr(..) => {
928 ty::RawPtr(pointee, ..) => {
929929 let ptr = self.read_immediate(value, ExpectedKind::RawPtr)?;
930930 if self.reset_provenance_and_padding {
931931 self.reset_pointer_provenance(value, &ptr)?;
......@@ -933,8 +933,12 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
933933 self.add_data_range_place(value);
934934 }
935935
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());
938942 self.check_wide_ptr_meta(place.meta(), place.layout)?;
939943 }
940944 interp_ok(true)
compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs-10
......@@ -903,16 +903,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
903903
904904 // Type check the pattern. Override if necessary to avoid knock-on errors.
905905 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 }
916906 let pat_ty = self.node_ty(decl.pat.hir_id);
917907 self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, pat_ty);
918908
compiler/rustc_hir_typeck/src/pat.rs+37-15
......@@ -544,21 +544,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
544544 {
545545 debug!("scrutinee ty {expected:?} is a pinned reference, inserting pin deref");
546546
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
562547 // Use the old pat info to keep `current_depth` to its old value.
563548 let new_pat_info =
564549 self.adjust_pat_info(Pinnedness::Pinned, inner_mutability, old_pat_info);
......@@ -1523,6 +1508,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15231508 Ok(ResolvedPat { ty: pat_ty, kind: ResolvedPatKind::Struct { variant } })
15241509 }
15251510
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
15261544 fn check_pat_struct(
15271545 &self,
15281546 pat: &'tcx Pat<'tcx>,
......@@ -1533,6 +1551,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15331551 expected: Ty<'tcx>,
15341552 pat_info: PatInfo<'tcx>,
15351553 ) -> Ty<'tcx> {
1554 self.check_pin_projection(pat, pat_ty, pat_info);
1555
15361556 // Type-check the path.
15371557 let had_err = self.demand_eqtype_pat(pat.span, expected, pat_ty, &pat_info.top_info);
15381558
......@@ -1791,6 +1811,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
17911811 expected: Ty<'tcx>,
17921812 pat_info: PatInfo<'tcx>,
17931813 ) -> Ty<'tcx> {
1814 self.check_pin_projection(pat, pat_ty, pat_info);
1815
17941816 let tcx = self.tcx;
17951817 let on_error = |e| {
17961818 for pat in subpats {
compiler/rustc_incremental/src/assert_dep_graph.rs+9-8
......@@ -52,7 +52,7 @@ use rustc_middle::ty::TyCtxt;
5252use rustc_span::{Span, Symbol, sym};
5353use tracing::debug;
5454
55use crate::errors;
55use crate::diagnostics;
5656
5757#[allow(missing_docs)]
5858pub(crate) fn assert_dep_graph(tcx: TyCtxt<'_>) {
......@@ -128,7 +128,7 @@ impl<'tcx> IfThisChanged<'tcx> {
128128 Err(()) => self
129129 .tcx
130130 .dcx()
131 .emit_fatal(errors::UnrecognizedDepNode { span, name: n }),
131 .emit_fatal(diagnostics::UnrecognizedDepNode { span, name: n }),
132132 }
133133 }
134134 };
......@@ -139,9 +139,10 @@ impl<'tcx> IfThisChanged<'tcx> {
139139 let Ok(dep_node) =
140140 DepNode::from_label_string(self.tcx, n.as_str(), def_path_hash)
141141 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 });
145146 };
146147 self.then_this_would_need.push((n.span, n.name, hir_id, dep_node));
147148 }
......@@ -186,7 +187,7 @@ fn check_paths<'tcx>(
186187) {
187188 if if_this_changed.is_empty() {
188189 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 });
190191 }
191192 return;
192193 }
......@@ -195,13 +196,13 @@ fn check_paths<'tcx>(
195196 let dependents = query.transitive_predecessors(source_dep_node);
196197 for &(target_span, ref target_pass, _, ref target_dep_node) in then_this_would_need {
197198 if !dependents.contains(&target_dep_node) {
198 tcx.dcx().emit_err(errors::NoPath {
199 tcx.dcx().emit_err(diagnostics::NoPath {
199200 span: target_span,
200201 source: tcx.def_path_str(source_def_id),
201202 target: *target_pass,
202203 });
203204 } else {
204 tcx.dcx().emit_err(errors::Ok { span: target_span });
205 tcx.dcx().emit_err(diagnostics::Ok { span: target_span });
205206 }
206207 }
207208 }
compiler/rustc_incremental/src/diagnostics.rs created+289
......@@ -0,0 +1,289 @@
1use std::path::{Path, PathBuf};
2
3use rustc_macros::Diagnostic;
4use rustc_span::{Span, Symbol};
5
6#[derive(Diagnostic)]
7#[diag("unrecognized `DepNode` variant: {$name}")]
8pub(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")]
16pub(crate) struct MissingIfThisChanged {
17 #[primary_span]
18 pub span: Span,
19}
20
21#[derive(Diagnostic)]
22#[diag("OK")]
23pub(crate) struct Ok {
24 #[primary_span]
25 pub span: Span,
26}
27
28#[derive(Diagnostic)]
29#[diag("no path from `{$source}` to `{$target}`")]
30pub(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}\"")]
39pub(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}")]
48pub(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}")]
56pub(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")]
64pub(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")]
72pub(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")]
80pub(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")]
88pub(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")]
96pub(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")]
104pub(crate) struct UncheckedClean {
105 #[primary_span]
106 pub span: Span,
107}
108#[derive(Diagnostic)]
109#[diag("unable to delete old {$name} at `{$path}`: {$err}")]
110pub(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}")]
118pub(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}")]
126pub(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}")]
134pub(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}")]
141pub(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}")]
149pub(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}")]
167pub(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)]
176pub(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}")]
182pub(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}")]
189pub(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")]
197pub(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)]
206pub(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)]
215pub(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}")]
222pub(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")]
229pub(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)]
235pub(crate) struct AssertLoaded;
236
237#[derive(Diagnostic)]
238#[diag(
239 "failed to delete invalidated or incompatible incremental compilation session directory contents `{$path}`: {$err}"
240)]
241pub(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}")]
248pub(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}")]
255pub(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}")]
263pub(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}")]
270pub(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}")]
278pub(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)]
287pub(crate) struct CorruptFile<'a> {
288 pub path: &'a Path,
289}
compiler/rustc_incremental/src/errors.rs deleted-289
......@@ -1,289 +0,0 @@
1use std::path::{Path, PathBuf};
2
3use rustc_macros::Diagnostic;
4use rustc_span::{Span, Symbol};
5
6#[derive(Diagnostic)]
7#[diag("unrecognized `DepNode` variant: {$name}")]
8pub(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")]
16pub(crate) struct MissingIfThisChanged {
17 #[primary_span]
18 pub span: Span,
19}
20
21#[derive(Diagnostic)]
22#[diag("OK")]
23pub(crate) struct Ok {
24 #[primary_span]
25 pub span: Span,
26}
27
28#[derive(Diagnostic)]
29#[diag("no path from `{$source}` to `{$target}`")]
30pub(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}\"")]
39pub(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}")]
48pub(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}")]
56pub(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")]
64pub(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")]
72pub(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")]
80pub(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")]
88pub(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")]
96pub(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")]
104pub(crate) struct UncheckedClean {
105 #[primary_span]
106 pub span: Span,
107}
108#[derive(Diagnostic)]
109#[diag("unable to delete old {$name} at `{$path}`: {$err}")]
110pub(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}")]
118pub(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}")]
126pub(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}")]
134pub(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}")]
141pub(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}")]
149pub(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}")]
167pub(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)]
176pub(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}")]
182pub(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}")]
189pub(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")]
197pub(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)]
206pub(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)]
215pub(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}")]
222pub(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")]
229pub(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)]
235pub(crate) struct AssertLoaded;
236
237#[derive(Diagnostic)]
238#[diag(
239 "failed to delete invalidated or incompatible incremental compilation session directory contents `{$path}`: {$err}"
240)]
241pub(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}")]
248pub(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}")]
255pub(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}")]
263pub(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}")]
270pub(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}")]
278pub(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)]
287pub(crate) struct CorruptFile<'a> {
288 pub path: &'a Path,
289}
compiler/rustc_incremental/src/lib.rs+1-1
......@@ -6,7 +6,7 @@
66// tidy-alphabetical-end
77
88mod assert_dep_graph;
9mod errors;
9mod diagnostics;
1010mod persist;
1111
1212pub use persist::{
compiler/rustc_incremental/src/persist/clean.rs+11-11
......@@ -33,7 +33,7 @@ use rustc_middle::ty::TyCtxt;
3333use rustc_span::{Span, Symbol};
3434use tracing::debug;
3535
36use crate::errors;
36use crate::diagnostics;
3737
3838// Base and Extra labels to build up the labels
3939
......@@ -193,7 +193,7 @@ impl<'tcx> CleanVisitor<'tcx> {
193193 let loaded_from_disk = self.loaded_from_disk(attr);
194194 for e in except.items().into_sorted_stable_ord() {
195195 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 });
197197 }
198198 }
199199 Assertion { clean: auto, dirty: except, loaded_from_disk }
......@@ -267,7 +267,7 @@ impl<'tcx> CleanVisitor<'tcx> {
267267 // An implementation, eg `impl<A> Trait for Foo { .. }`
268268 HirItem::Impl { .. } => ("ItemKind::Impl", LABELS_IMPL),
269269
270 _ => self.tcx.dcx().emit_fatal(errors::UndefinedCleanDirtyItem {
270 _ => self.tcx.dcx().emit_fatal(diagnostics::UndefinedCleanDirtyItem {
271271 span,
272272 kind: format!("{:?}", item.kind),
273273 }),
......@@ -286,7 +286,7 @@ impl<'tcx> CleanVisitor<'tcx> {
286286 _ => self
287287 .tcx
288288 .dcx()
289 .emit_fatal(errors::UndefinedCleanDirty { span, kind: format!("{node:?}") }),
289 .emit_fatal(diagnostics::UndefinedCleanDirty { span, kind: format!("{node:?}") }),
290290 };
291291 let labels =
292292 Labels::from_iter(labels.iter().flat_map(|s| s.iter().map(|l| (*l).to_string())));
......@@ -301,13 +301,13 @@ impl<'tcx> CleanVisitor<'tcx> {
301301 if out.contains(label_str) {
302302 self.tcx
303303 .dcx()
304 .emit_fatal(errors::RepeatedDepNodeLabel { span, label: label_str });
304 .emit_fatal(diagnostics::RepeatedDepNodeLabel { span, label: label_str });
305305 }
306306 out.insert(label_str.to_string());
307307 } else {
308308 self.tcx
309309 .dcx()
310 .emit_fatal(errors::UnrecognizedDepNodeLabel { span, label: label_str });
310 .emit_fatal(diagnostics::UnrecognizedDepNodeLabel { span, label: label_str });
311311 }
312312 }
313313 out
......@@ -328,7 +328,7 @@ impl<'tcx> CleanVisitor<'tcx> {
328328 let dep_node_str = self.dep_node_str(&dep_node);
329329 self.tcx
330330 .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 });
332332 }
333333 }
334334
......@@ -339,7 +339,7 @@ impl<'tcx> CleanVisitor<'tcx> {
339339 let dep_node_str = self.dep_node_str(&dep_node);
340340 self.tcx
341341 .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 });
343343 }
344344 }
345345
......@@ -369,7 +369,7 @@ impl<'tcx> CleanVisitor<'tcx> {
369369 Ok(dep_node) => {
370370 if !self.tcx.dep_graph.debug_was_loaded_from_disk(dep_node) {
371371 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 {
373373 span: item_span,
374374 dep_node_str: &dep_node_str,
375375 });
......@@ -379,7 +379,7 @@ impl<'tcx> CleanVisitor<'tcx> {
379379 Err(()) => {
380380 let dep_kind = dep_kind_from_label(label);
381381 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 {
383383 span: item_span,
384384 dep_node_str: &label,
385385 });
......@@ -407,7 +407,7 @@ impl<'tcx> FindAllAttrs<'tcx> {
407407 fn report_unchecked_attrs(&self, mut checked_attrs: FxHashSet<Span>) {
408408 for attr in &self.found_attrs {
409409 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 });
411411 checked_attrs.insert(attr.span);
412412 }
413413 }
compiler/rustc_incremental/src/persist/file_format.rs+4-4
......@@ -20,7 +20,7 @@ use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
2020use rustc_session::Session;
2121use tracing::debug;
2222
23use crate::errors;
23use crate::diagnostics;
2424
2525/// The first few bytes of files generated by incremental compilation.
2626const FILE_MAGIC: &[u8] = b"RSIC";
......@@ -57,12 +57,12 @@ where
5757 debug!("save: remove old file");
5858 }
5959 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 }),
6161 }
6262
6363 let mut encoder = match FileEncoder::new(&path_buf) {
6464 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 }),
6666 };
6767
6868 write_file_header(&mut encoder, sess);
......@@ -76,7 +76,7 @@ where
7676 );
7777 debug!("save: data written to disk successfully");
7878 }
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 }),
8080 }
8181}
8282
compiler/rustc_incremental/src/persist/fs.rs+14-12
......@@ -120,7 +120,7 @@ use rustc_session::{Session, StableCrateId};
120120use rustc_span::Symbol;
121121use tracing::debug;
122122
123use crate::errors;
123use crate::diagnostics;
124124
125125#[cfg(test)]
126126mod tests;
......@@ -233,7 +233,7 @@ pub(crate) fn prepare_session_directory(
233233 let crate_dir = match try_canonicalize(&crate_dir) {
234234 Ok(v) => v,
235235 Err(err) => {
236 sess.dcx().emit_fatal(errors::CanonicalizePath { path: crate_dir, err });
236 sess.dcx().emit_fatal(diagnostics::CanonicalizePath { path: crate_dir, err });
237237 }
238238 };
239239
......@@ -276,7 +276,7 @@ pub(crate) fn prepare_session_directory(
276276 debug!("successfully copied data from: {}", source_directory.display());
277277
278278 if !allows_links {
279 sess.dcx().emit_warn(errors::HardLinkFailed { path: &session_dir });
279 sess.dcx().emit_warn(diagnostics::HardLinkFailed { path: &session_dir });
280280 }
281281
282282 sess.init_incr_comp_session(session_dir, directory_lock);
......@@ -291,7 +291,7 @@ pub(crate) fn prepare_session_directory(
291291 // Try to remove the session directory we just allocated. We don't
292292 // know if there's any garbage in it from the failed copy action.
293293 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 });
295295 }
296296
297297 delete_session_dir_lock_file(sess, &lock_file_path);
......@@ -325,7 +325,7 @@ pub fn finalize_session_directory(sess: &Session, svh: Option<Svh>) {
325325 );
326326
327327 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 });
329329 }
330330
331331 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>) {
364364 }
365365 Err(e) => {
366366 // 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 });
368368
369369 debug!("finalize_session_directory() - error, marking as invalid");
370370 // Drop the file lock, so we can garage collect
......@@ -464,7 +464,9 @@ fn create_dir(sess: &Session, path: &Path, dir_tag: &str) {
464464 Ok(()) => {
465465 debug!("{} directory created successfully", dir_tag);
466466 }
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 }
468470 }
469471}
470472
......@@ -483,7 +485,7 @@ fn lock_directory(sess: &Session, session_dir: &Path) -> (flock::Lock, PathBuf)
483485 Ok(lock) => (lock, lock_file_path),
484486 Err(lock_err) => {
485487 let is_unsupported_lock = flock::Lock::error_unsupported(&lock_err);
486 sess.dcx().emit_fatal(errors::CreateLock {
488 sess.dcx().emit_fatal(diagnostics::CreateLock {
487489 lock_err,
488490 session_dir,
489491 is_unsupported_lock,
......@@ -495,7 +497,7 @@ fn lock_directory(sess: &Session, session_dir: &Path) -> (flock::Lock, PathBuf)
495497
496498fn delete_session_dir_lock_file(sess: &Session, lock_file_path: &Path) {
497499 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 });
499501 }
500502}
501503
......@@ -708,7 +710,7 @@ pub(crate) fn garbage_collect_session_directories(sess: &Session) -> io::Result<
708710 if !lock_file_to_session_dir.items().any(|(_, dir)| *dir == directory_name) {
709711 let path = crate_directory.join(directory_name);
710712 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 });
712714 }
713715 }
714716 }
......@@ -840,7 +842,7 @@ pub(crate) fn garbage_collect_session_directories(sess: &Session) -> io::Result<
840842 debug!("garbage_collect_session_directories() - deleting `{}`", path.display());
841843
842844 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 });
844846 } else {
845847 delete_session_dir_lock_file(sess, &lock_file_path(&path));
846848 }
......@@ -858,7 +860,7 @@ fn delete_old(sess: &Session, path: &Path) {
858860 debug!("garbage_collect_session_directories() - deleting `{}`", path.display());
859861
860862 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 });
862864 } else {
863865 delete_session_dir_lock_file(sess, &lock_file_path(path));
864866 }
compiler/rustc_incremental/src/persist/load.rs+10-9
......@@ -18,7 +18,7 @@ use tracing::{debug, warn};
1818use super::data::*;
1919use super::fs::*;
2020use super::{file_format, work_product};
21use crate::errors;
21use crate::diagnostics;
2222use crate::persist::file_format::{OpenFile, OpenFileError};
2323
2424#[derive(Debug)]
......@@ -60,7 +60,7 @@ fn load_dep_graph(sess: &Session) -> LoadResult {
6060 {
6161 // Decode the list of work_products
6262 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 });
6464 return LoadResult::DataOutOfDate;
6565 };
6666 let work_products: Vec<SerializedWorkProduct> =
......@@ -93,7 +93,7 @@ fn load_dep_graph(sess: &Session) -> LoadResult {
9393 Err(OpenFileError::IoError { err }) => LoadResult::IoError { path: path.to_owned(), err },
9494 Ok(OpenFile { mmap, start_pos }) => {
9595 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 });
9797 return LoadResult::DataOutOfDate;
9898 };
9999 let prev_commandline_args_hash = Hash64::decode(&mut decoder);
......@@ -135,7 +135,7 @@ pub fn load_query_result_cache(sess: &Session) -> Option<OnDiskCache> {
135135 match file_format::open_incremental_file(sess, &path) {
136136 Ok(OpenFile { mmap, start_pos }) => {
137137 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 });
139139 OnDiskCache::new_empty()
140140 });
141141 Some(cache)
......@@ -161,12 +161,12 @@ fn maybe_assert_incr_state(sess: &Session, load_result: &LoadResult) {
161161 match assertion {
162162 IncrementalStateAssertion::Loaded => {
163163 if !loaded {
164 sess.dcx().emit_fatal(errors::AssertLoaded);
164 sess.dcx().emit_fatal(diagnostics::AssertLoaded);
165165 }
166166 }
167167 IncrementalStateAssertion::NotLoaded => {
168168 if loaded {
169 sess.dcx().emit_fatal(errors::AssertNotLoaded)
169 sess.dcx().emit_fatal(diagnostics::AssertNotLoaded)
170170 }
171171 }
172172 }
......@@ -205,12 +205,13 @@ pub fn setup_dep_graph(
205205
206206 let (prev_graph, prev_work_products) = match load_result {
207207 LoadResult::IoError { path, err } => {
208 sess.dcx().emit_warn(errors::LoadDepGraph { path, err });
208 sess.dcx().emit_warn(diagnostics::LoadDepGraph { path, err });
209209 Default::default()
210210 }
211211 LoadResult::DataOutOfDate => {
212212 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 });
214215 }
215216 Default::default()
216217 }
......@@ -223,7 +224,7 @@ pub fn setup_dep_graph(
223224 let mut encoder = FileEncoder::new(&path_buf).unwrap_or_else(|err| {
224225 // We're in incremental mode but couldn't set up streaming output of the dep graph.
225226 // 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 })
227228 });
228229
229230 file_format::write_file_header(&mut encoder, sess);
compiler/rustc_incremental/src/persist/save.rs+2-2
......@@ -13,7 +13,7 @@ use super::data::*;
1313use super::fs::*;
1414use super::{clean, file_format, work_product};
1515use crate::assert_dep_graph::assert_dep_graph;
16use crate::errors;
16use crate::diagnostics;
1717
1818/// Saves and writes the [`DepGraph`] to the file system.
1919///
......@@ -45,7 +45,7 @@ pub(crate) fn save_dep_graph(tcx: TyCtxt<'_>) {
4545 move || {
4646 sess.time("incr_comp_persist_dep_graph", || {
4747 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 {
4949 from: &staging_dep_graph_path,
5050 to: &dep_graph_path,
5151 err,
compiler/rustc_incremental/src/persist/work_product.rs+3-3
......@@ -11,7 +11,7 @@ use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
1111use rustc_session::Session;
1212use tracing::debug;
1313
14use crate::errors;
14use crate::diagnostics;
1515use crate::persist::fs::*;
1616
1717/// 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(
4040 let _ = saved_files.insert(ext.to_string(), file_name);
4141 }
4242 Err(err) => {
43 sess.dcx().emit_warn(errors::CopyWorkProductToCache {
43 sess.dcx().emit_warn(diagnostics::CopyWorkProductToCache {
4444 from: path,
4545 to: &path_in_incr_dir,
4646 err,
......@@ -60,7 +60,7 @@ pub(crate) fn delete_workproduct_files(sess: &Session, work_product: &WorkProduc
6060 for (_, path) in work_product.saved_files.items().into_sorted_stable_ord() {
6161 let path = in_incr_comp_dir_sess(sess, path);
6262 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 });
6464 }
6565 }
6666}
compiler/rustc_index/src/interval.rs+4
......@@ -370,6 +370,10 @@ impl<R: Idx, C: Step + Idx> SparseIntervalMatrix<R, C> {
370370 self.rows.get(row)
371371 }
372372
373 pub fn iter_enumerated(&self) -> impl Iterator<Item = (R, &IntervalSet<C>)> {
374 self.rows.iter_enumerated()
375 }
376
373377 fn ensure_row(&mut self, row: R) -> &mut IntervalSet<C> {
374378 self.rows.ensure_contains_elem(row, || IntervalSet::new(self.column_size))
375379 }
compiler/rustc_infer/src/infer/context.rs+15-1
......@@ -209,7 +209,7 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
209209 )
210210 }
211211
212 fn enter_forall<T: TypeFoldable<TyCtxt<'tcx>>, U>(
212 fn enter_forall_without_assumptions<T: TypeFoldable<TyCtxt<'tcx>>, U>(
213213 &self,
214214 value: ty::Binder<'tcx, T>,
215215 f: impl FnOnce(T) -> U,
......@@ -217,6 +217,20 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
217217 self.enter_forall(value, f)
218218 }
219219
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
220234 fn equate_ty_vids_raw(&self, a: ty::TyVid, b: ty::TyVid) {
221235 self.inner.borrow_mut().type_variables().equate(a, b);
222236 }
compiler/rustc_interface/src/tests.rs+28-1
......@@ -25,7 +25,7 @@ use rustc_session::utils::{CanonicalizedPath, NativeLib};
2525use rustc_session::{CompilerIO, EarlyDiagCtxt, Session, build_session, getopts};
2626use rustc_span::edition::{DEFAULT_EDITION, Edition};
2727use rustc_span::source_map::{RealFileLoader, SourceMapInputs};
28use rustc_span::{FileName, SourceFileHashAlgorithm, sym};
28use rustc_span::{FileName, RealFileName, RemapPathScopeComponents, SourceFileHashAlgorithm, sym};
2929use rustc_target::spec::{
3030 CodeModel, FramePointer, LinkerFlavorCli, MergeFunctions, OnBrokenPipe, PanicStrategy,
3131 RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel,
......@@ -175,6 +175,33 @@ fn test_can_print_warnings() {
175175 });
176176}
177177
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]
182fn 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
178205#[test]
179206fn test_output_types_tracking_hash_different_paths() {
180207 let mut v1 = Options::default();
compiler/rustc_monomorphize/src/collector.rs+2-2
......@@ -235,7 +235,7 @@ use rustc_session::config::{DebugInfo, EntryFnType};
235235use rustc_span::{DUMMY_SP, Span, Spanned, dummy_spanned, respan};
236236use tracing::{debug, instrument, trace};
237237
238use crate::errors::{
238use crate::diagnostics::{
239239 self, EncounteredErrorWhileInstantiating, EncounteredErrorWhileInstantiatingGlobalAsm,
240240 NoOptimizedMir, RecursionLimit,
241241};
......@@ -1702,7 +1702,7 @@ impl<'v> RootCollector<'_, 'v> {
17021702 }
17031703
17041704 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);
17061706 };
17071707 let main_ret_ty = self.tcx.fn_sig(main_def_id).no_bound_vars().unwrap().output();
17081708
compiler/rustc_monomorphize/src/diagnostics.rs created+186
......@@ -0,0 +1,186 @@
1use rustc_macros::Diagnostic;
2use rustc_middle::ty::{Instance, Ty};
3use rustc_span::{Span, Symbol};
4
5#[derive(Diagnostic)]
6#[diag("reached the recursion limit while instantiating `{$instance}`")]
7pub(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}`")]
18pub(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)]
32pub(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")]
41pub(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}")]
49pub(crate) struct CouldntDumpMonoStats {
50 pub error: String,
51}
52
53#[derive(Diagnostic)]
54#[diag("the above error was encountered while instantiating `{$kind} {$instance}`")]
55pub(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`")]
64pub(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)]
74pub(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)]
90pub(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")]
115pub(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)]
136pub(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)]
161pub(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}`")]
179pub(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 @@
1use rustc_macros::Diagnostic;
2use rustc_middle::ty::{Instance, Ty};
3use rustc_span::{Span, Symbol};
4
5#[derive(Diagnostic)]
6#[diag("reached the recursion limit while instantiating `{$instance}`")]
7pub(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}`")]
18pub(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)]
32pub(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")]
41pub(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}")]
49pub(crate) struct CouldntDumpMonoStats {
50 pub error: String,
51}
52
53#[derive(Diagnostic)]
54#[diag("the above error was encountered while instantiating `{$kind} {$instance}`")]
55pub(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`")]
64pub(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)]
74pub(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)]
90pub(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")]
115pub(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)]
136pub(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)]
161pub(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}`")]
179pub(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;
88use rustc_middle::ty::TyCtxt;
99
1010use crate::collector::UsageMap;
11use crate::errors;
11use crate::diagnostics;
1212
1313#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
1414struct StaticNodeIdx(usize);
......@@ -105,7 +105,7 @@ pub(super) fn check_static_initializers_are_acyclic<'tcx, 'a, 'b>(
105105 let head_def = statics[nodes[0].index()];
106106 let head_span = tcx.def_span(head_def);
107107
108 tcx.dcx().emit_err(errors::StaticInitializerCyclic {
108 tcx.dcx().emit_err(diagnostics::StaticInitializerCyclic {
109109 span: head_span,
110110 labels: nodes.iter().map(|&n| tcx.def_span(statics[n.index()])).collect(),
111111 head: &tcx.def_path_str(head_def),
compiler/rustc_monomorphize/src/lib.rs+1-1
......@@ -13,7 +13,7 @@ use rustc_middle::{bug, traits};
1313use rustc_span::ErrorGuaranteed;
1414
1515mod collector;
16mod errors;
16mod diagnostics;
1717mod graph_checks;
1818mod mono_checks;
1919mod partitioning;
compiler/rustc_monomorphize/src/mono_checks/abi_check.rs+6-6
......@@ -8,7 +8,7 @@ use rustc_span::def_id::DefId;
88use rustc_span::{DUMMY_SP, Span, Symbol, sym};
99use rustc_target::callconv::{FnAbi, PassMode};
1010
11use crate::errors;
11use crate::diagnostics;
1212
1313/// Are vector registers used?
1414enum UsesVectorRegisters {
......@@ -71,7 +71,7 @@ fn do_check_simd_vector_abi<'tcx>(
7171 Some((_, feature)) => feature,
7272 None => {
7373 let (span, _hir_id) = loc();
74 tcx.dcx().emit_err(errors::AbiErrorUnsupportedVectorType {
74 tcx.dcx().emit_err(diagnostics::AbiErrorUnsupportedVectorType {
7575 span,
7676 ty: arg_abi.layout.ty,
7777 is_call,
......@@ -81,7 +81,7 @@ fn do_check_simd_vector_abi<'tcx>(
8181 };
8282 if !feature.is_empty() && !have_feature(Symbol::intern(feature)) {
8383 let (span, _hir_id) = loc();
84 tcx.dcx().emit_err(errors::AbiErrorDisabledVectorType {
84 tcx.dcx().emit_err(diagnostics::AbiErrorDisabledVectorType {
8585 span,
8686 required_feature: feature,
8787 ty: arg_abi.layout.ty,
......@@ -98,7 +98,7 @@ fn do_check_simd_vector_abi<'tcx>(
9898 };
9999 if !required_feature.is_empty() && !have_feature(Symbol::intern(required_feature)) {
100100 let (span, _) = loc();
101 tcx.dcx().emit_err(errors::AbiErrorDisabledVectorType {
101 tcx.dcx().emit_err(diagnostics::AbiErrorDisabledVectorType {
102102 span,
103103 required_feature,
104104 ty: arg_abi.layout.ty,
......@@ -115,7 +115,7 @@ fn do_check_simd_vector_abi<'tcx>(
115115 // The `vectorcall` ABI is special in that it requires SSE2 no matter which types are being passed.
116116 if abi.conv == CanonAbi::X86(X86Call::Vectorcall) && !have_feature(sym::sse2) {
117117 let (span, _hir_id) = loc();
118 tcx.dcx().emit_err(errors::AbiRequiredTargetFeature {
118 tcx.dcx().emit_err(diagnostics::AbiRequiredTargetFeature {
119119 span,
120120 required_feature: "sse2",
121121 abi: "vectorcall",
......@@ -142,7 +142,7 @@ fn do_check_unsized_params<'tcx>(
142142 for arg_abi in fn_abi.args.iter() {
143143 if !arg_abi.layout.layout.is_sized() {
144144 let (span, _hir_id) = loc();
145 tcx.dcx().emit_err(errors::AbiErrorUnsupportedUnsizedParameter {
145 tcx.dcx().emit_err(diagnostics::AbiErrorUnsupportedUnsizedParameter {
146146 span,
147147 ty: arg_abi.layout.ty,
148148 is_call,
compiler/rustc_monomorphize/src/mono_checks/move_check.rs+1-1
......@@ -9,7 +9,7 @@ use rustc_session::lint::builtin::LARGE_ASSIGNMENTS;
99use rustc_span::{Span, Spanned, sym};
1010use tracing::{debug, trace};
1111
12use crate::errors::LargeAssignmentsLint;
12use crate::diagnostics::LargeAssignmentsLint;
1313
1414struct MoveCheckVisitor<'tcx> {
1515 tcx: TyCtxt<'tcx>,
compiler/rustc_monomorphize/src/partitioning.rs+1-1
......@@ -124,7 +124,7 @@ use rustc_target::spec::SymbolVisibility;
124124use tracing::debug;
125125
126126use crate::collector::{self, MonoItemCollectionStrategy, UsageMap};
127use crate::errors::{CouldntDumpMonoStats, SymbolAlreadyDefined};
127use crate::diagnostics::{CouldntDumpMonoStats, SymbolAlreadyDefined};
128128use crate::graph_checks::target_specific_checks;
129129
130130struct PartitioningCx<'a, 'tcx> {
compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs+3-3
......@@ -1331,8 +1331,8 @@ where
13311331 self.delegate.instantiate_binder_with_infer(value)
13321332 }
13331333
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.
13361336 ///
13371337 /// The `param_env` is used to *compute* the assumptions of the binder, not *as* the
13381338 /// assumptions associated with the binder.
......@@ -1344,7 +1344,7 @@ where
13441344 param_env: I::ParamEnv,
13451345 f: impl FnOnce(&mut Self, T) -> U,
13461346 ) -> U {
1347 self.delegate.enter_forall(value, |value| {
1347 self.delegate.enter_forall_without_assumptions(value, |value| {
13481348 let u = self.delegate.universe();
13491349 let assumptions = if self.cx().assumptions_on_binders() {
13501350 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> {
752752 Err(err)
753753 }
754754
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
755762 /// Adds a label when `&raw EXPR` was written instead of `&raw const EXPR`/`&raw mut EXPR`.
756763 ///
757764 /// Given that not all parser diagnostics flow through `expected_one_of_not_found`, this
758765 /// label may need added to other diagnostics emission paths as needed.
759766 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() {
765768 err.span_suggestions(
766769 self.prev_token.span.shrink_to_hi(),
767770 "`&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> {
12791279 };
12801280 let open_paren = self.token.span;
12811281
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 };
12851293 match self.maybe_recover_struct_lit_bad_delims(lo, open_paren, seq, snapshot) {
12861294 Ok(expr) => expr,
12871295 Err(err) => self.recover_seq_parse_error(exp!(OpenParen), exp!(CloseParen), lo, err),
12881296 }
12891297 }
12901298
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
12911314 /// If we encounter a parser state that looks like the user has written a `struct` literal with
12921315 /// parentheses instead of braces, recover the parser state and provide suggestions.
12931316 #[instrument(skip(self, seq, snapshot), level = "trace")]
compiler/rustc_parse/src/parser/mod.rs+7
......@@ -917,6 +917,13 @@ impl<'a> Parser<'a> {
917917 }
918918
919919 // 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 }
920927 self.last_unexpected_token_span = None;
921928 match f(self) {
922929 Ok(t) => {
compiler/rustc_session/src/config.rs+28-16
......@@ -1384,9 +1384,21 @@ pub fn host_tuple() -> &'static str {
13841384
13851385fn file_path_mapping(
13861386 remap_path_prefix: Vec<(PathBuf, PathBuf)>,
1387 remap_cwd_prefix: Option<&Path>,
13871388 remap_path_scope: RemapPathScopeComponents,
13881389) -> 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)
13901402}
13911403
13921404impl Default for Options {
......@@ -1398,7 +1410,8 @@ impl Default for Options {
13981410 // to create a default working directory.
13991411 let working_dir = {
14001412 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());
14021415 file_mapping.to_real_filename(&RealFileName::empty(), &working_dir)
14031416 };
14041417
......@@ -1459,7 +1472,11 @@ impl Options {
14591472 }
14601473
14611474 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 )
14631480 }
14641481
14651482 /// Returns `true` if there will be an output file generated.
......@@ -2393,9 +2410,8 @@ pub fn parse_externs(
23932410fn parse_remap_path_prefix(
23942411 early_dcx: &EarlyDiagCtxt,
23952412 matches: &getopts::Matches,
2396 unstable_opts: &UnstableOptions,
23972413) -> Vec<(PathBuf, PathBuf)> {
2398 let mut mapping: Vec<(PathBuf, PathBuf)> = matches
2414 matches
23992415 .opt_strs("remap-path-prefix")
24002416 .into_iter()
24012417 .map(|remap| match remap.rsplit_once('=') {
......@@ -2404,15 +2420,7 @@ fn parse_remap_path_prefix(
24042420 }
24052421 Some((from, to)) => (PathBuf::from(from), PathBuf::from(to)),
24062422 })
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()
24162424}
24172425
24182426fn parse_logical_env(
......@@ -2678,7 +2686,7 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
26782686
26792687 let externs = parse_externs(early_dcx, matches, &unstable_opts);
26802688
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);
26822690 let remap_path_scope = parse_remap_path_scope(early_dcx, matches, &unstable_opts);
26832691
26842692 let pretty = parse_pretty(early_dcx, &unstable_opts);
......@@ -2746,7 +2754,11 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
27462754 early_dcx.early_fatal(format!("Current directory is invalid: {e}"));
27472755 });
27482756
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 );
27502762 file_mapping.to_real_filename(&RealFileName::empty(), &working_dir)
27512763 };
27522764
compiler/rustc_target/src/spec/mod.rs+1
......@@ -1462,6 +1462,7 @@ supported_targets! {
14621462 ("powerpc-unknown-linux-muslspe", powerpc_unknown_linux_muslspe),
14631463 ("powerpc64-ibm-aix", powerpc64_ibm_aix),
14641464 ("powerpc64-unknown-linux-gnu", powerpc64_unknown_linux_gnu),
1465 ("powerpc64-unknown-linux-gnuelfv2", powerpc64_unknown_linux_gnuelfv2),
14651466 ("powerpc64-unknown-linux-musl", powerpc64_unknown_linux_musl),
14661467 ("powerpc64le-unknown-linux-gnu", powerpc64le_unknown_linux_gnu),
14671468 ("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 {
77 llvm_target: "aarch64-unknown-freebsd".into(),
88 metadata: TargetMetadata {
99 description: Some("ARM64 FreeBSD".into()),
10 tier: Some(3),
10 tier: Some(2),
1111 host_tools: Some(true),
1212 std: Some(true),
1313 },
compiler/rustc_target/src/spec/targets/powerpc64_unknown_linux_gnuelfv2.rs created+30
......@@ -0,0 +1,30 @@
1use rustc_abi::Endian;
2
3use crate::spec::{
4 Arch, Cc, CfgAbi, LinkerFlavor, Lld, LlvmAbi, StackProbeType, Target, TargetMetadata,
5 TargetOptions, base,
6};
7
8pub(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 {
1010 base.llvm_abiname = LlvmAbi::Lp64d;
1111 base.max_atomic_width = Some(64);
1212 base.stack_probes = StackProbeType::Inline;
13 base.supported_sanitizers = SanitizerSet::SHADOWCALLSTACK;
13 base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::SHADOWCALLSTACK;
1414 base.default_sanitizers = SanitizerSet::SHADOWCALLSTACK;
1515 base.supports_xray = true;
1616
compiler/rustc_type_ir/src/infer_ctxt.rs+10-1
......@@ -406,7 +406,16 @@ pub trait InferCtxtLike: Sized {
406406 value: ty::Binder<Self::Interner, T>,
407407 ) -> T;
408408
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>(
410419 &self,
411420 value: ty::Binder<Self::Interner, T>,
412421 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>
10961096
10971097 let prev_universe = infcx.universe();
10981098
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)| {
11041100 for bound_type_outlives in assumptions.type_outlives.iter() {
11051101 let OutlivesPredicate(alias2, r2) =
11061102 infcx.instantiate_binder_with_infer(*bound_type_outlives);
......@@ -1187,14 +1183,14 @@ impl<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeRelation<I>
11871183 where
11881184 T: Relate<I>,
11891185 {
1190 self.infcx.enter_forall(a, |a| {
1186 self.infcx.enter_forall_with_empty_assumptions(a, |a| {
11911187 let u = self.infcx.universe();
11921188 self.infcx.insert_placeholder_assumptions(u, Some(Assumptions::empty()));
11931189 let b = self.infcx.instantiate_binder_with_infer(b);
11941190 self.relate(a, b)
11951191 })?;
11961192
1197 self.infcx.enter_forall(b, |b| {
1193 self.infcx.enter_forall_with_empty_assumptions(b, |b| {
11981194 let u = self.infcx.universe();
11991195 self.infcx.insert_placeholder_assumptions(u, Some(Assumptions::empty()));
12001196 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
311311 //
312312 // [rd]: https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/placeholders_and_universes.html
313313 ty::Covariant => {
314 self.infcx.enter_forall(b, |b| {
314 self.infcx.enter_forall_with_empty_assumptions(b, |b| {
315315 let a = self.infcx.instantiate_binder_with_infer(a);
316316 self.relate(a, b)
317317 })?;
318318 }
319319 ty::Contravariant => {
320 self.infcx.enter_forall(a, |a| {
320 self.infcx.enter_forall_with_empty_assumptions(a, |a| {
321321 let b = self.infcx.instantiate_binder_with_infer(b);
322322 self.relate(a, b)
323323 })?;
......@@ -334,13 +334,13 @@ where
334334 // `exists<..> A == for<..> B` and `exists<..> B == for<..> A`.
335335 // Check if `exists<..> A == for<..> B`
336336 ty::Invariant => {
337 self.infcx.enter_forall(b, |b| {
337 self.infcx.enter_forall_with_empty_assumptions(b, |b| {
338338 let a = self.infcx.instantiate_binder_with_infer(a);
339339 self.relate(a, b)
340340 })?;
341341
342342 // Check if `exists<..> B == for<..> A`.
343 self.infcx.enter_forall(a, |a| {
343 self.infcx.enter_forall_with_empty_assumptions(a, |a| {
344344 let b = self.infcx.instantiate_binder_with_infer(b);
345345 self.relate(a, b)
346346 })?;
compiler/rustc_type_ir_macros/Cargo.toml+1
......@@ -11,6 +11,7 @@ nightly = []
1111
1212[dependencies]
1313# tidy-alphabetical-start
14indexmap = "2.4.0"
1415proc-macro2 = "1"
1516quote = "1"
1617syn = { version = "2.0.9", features = ["full", "visit-mut"] }
compiler/rustc_type_ir_macros/src/lib.rs+91-20
......@@ -1,3 +1,4 @@
1use indexmap::IndexSet;
12use quote::{ToTokens, quote};
23use syn::visit_mut::VisitMut;
34use syn::{Attribute, parse_quote};
......@@ -17,11 +18,24 @@ decl_derive!(
1718 [GenericTypeVisitable] => customizable_type_visitable_derive
1819);
1920
20struct LiftedTy {
21struct TransformedTy {
2122 ty: syn::Type,
22 generic_parameter_bounds: Vec<syn::Ident>,
23 generic_parameter_bounds: IndexSet<syn::Ident>,
2324}
2425
26enum TypeParameterPath {
27 Interner,
28 GenericParameter(syn::Ident),
29}
30
31enum TypeParameterTransform {
32 Continue,
33 Stop,
34}
35
36type TypeParameterVisitor =
37 fn(TypeParameterPath, &mut syn::TypePath, &mut IndexSet<syn::Ident>) -> TypeParameterTransform;
38
2539fn has_ignore_attr(attrs: &[Attribute], name: &'static str, meta: &'static str) -> bool {
2640 let mut ignored = false;
2741 attrs.iter().for_each(|attr| {
......@@ -91,6 +105,9 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke
91105
92106 s.add_where_predicate(parse_quote! { I: Interner });
93107 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();
94111 s.bind_with(|_| synstructure::BindStyle::Move);
95112 let body_try_fold = s.each_variant(|vi| {
96113 let bindings = vi.bindings();
......@@ -101,6 +118,12 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke
101118 if has_ignore_attr(&bind.ast().attrs, "type_foldable", "identity") {
102119 bind.to_token_stream()
103120 } 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
104127 quote! {
105128 ::rustc_type_ir::TypeFoldable::try_fold_with(#bind, __folder)?
106129 }
......@@ -129,6 +152,9 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke
129152 // to generate code for them.
130153 s.filter(|bi| !has_ignore_attr(&bi.ast().attrs, "type_foldable", "identity"));
131154 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 }
132158 s.bound_impl(
133159 quote!(::rustc_type_ir::TypeFoldable<I>),
134160 quote! {
......@@ -149,6 +175,19 @@ fn type_foldable_derive(mut s: synstructure::Structure<'_>) -> proc_macro2::Toke
149175 )
150176}
151177
178fn 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
152191/// `Lift_Generic` is specialised for structs/enums parameterised by an interner
153192/// `I: Interner`. It derives `Lift<J>` by rewriting interner associated types
154193/// 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 {
251290 get_first_path_segment(ty).is_some_and(|segment| segment.ident == "PhantomData")
252291}
253292
254fn lift(mut ty: syn::Type, generic_parameters: &[syn::Ident]) -> LiftedTy {
255 struct ItoJ<'a> {
293fn 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
309fn transform_type_parameters(
310 mut ty: syn::Type,
311 generic_parameters: &[syn::Ident],
312 visit: TypeParameterVisitor,
313) -> TransformedTy {
314 struct TypeParameterTransformer<'a> {
256315 generic_parameters: &'a [syn::Ident],
257 generic_parameter_bounds: Vec<syn::Ident>,
316 generic_parameter_bounds: IndexSet<syn::Ident>,
317 visit: TypeParameterVisitor,
258318 }
259319
260 impl VisitMut for ItoJ<'_> {
320 impl VisitMut for TypeParameterTransformer<'_> {
261321 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() {
263323 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| {
266325 if first.ident == "I" {
267 *first = parse_quote! { J };
326 Some(TypeParameterPath::Interner)
268327 } else if segments_len == 1
269328 && matches!(first.arguments, syn::PathArguments::None)
270 && self.generic_parameters.iter().any(|param| first.ident == *param)
329 && self.generic_parameters.contains(&first.ident)
271330 {
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
279334 }
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;
280345 }
281346 }
347
282348 syn::visit_mut::visit_type_path_mut(self, i);
283349 }
284350 }
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 };
286357 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 }
288359}
289360
290361#[cfg(not(feature = "nightly"))]
library/core/src/clone.rs+30-13
......@@ -293,21 +293,36 @@ pub macro Clone($item:item) {
293293/// A trait for types whose [`Clone`] operation creates another alias to the same
294294/// logical resource or shared state.
295295///
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.
301301///
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`.
305313///
306314/// Shared references, `Rc<T>`, `Arc<T>`, `Sender<T>`, and `SyncSender<T>` are
307315/// 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.
311326///
312327/// # Examples
313328///
......@@ -367,9 +382,11 @@ pub macro Clone($item:item) {
367382pub trait Share: Clone {
368383 /// Creates another alias to the same underlying resource or shared state.
369384 ///
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.
371388 #[unstable(feature = "share_trait", issue = "156756")]
372 fn share(&self) -> Self {
389 final fn share(&self) -> Self {
373390 Clone::clone(self)
374391 }
375392}
library/core/src/lib.rs+1
......@@ -138,6 +138,7 @@
138138#![feature(f16)]
139139#![feature(f128)]
140140#![feature(field_projections)]
141#![feature(final_associated_functions)]
141142#![feature(freeze_impls)]
142143#![feature(fundamental)]
143144#![feature(funnel_shifts)]
library/core/src/slice/mod.rs+2
......@@ -596,6 +596,7 @@ impl<T> [T] {
596596 #[inline]
597597 #[must_use]
598598 #[rustc_const_unstable(feature = "const_index", issue = "143775")]
599 #[rustc_no_writable]
599600 pub const fn get_mut<I>(&mut self, index: I) -> Option<&mut I::Output>
600601 where
601602 I: [const] SliceIndex<Self>,
......@@ -681,6 +682,7 @@ impl<T> [T] {
681682 #[must_use]
682683 #[track_caller]
683684 #[rustc_const_unstable(feature = "const_index", issue = "143775")]
685 #[rustc_no_writable]
684686 pub const unsafe fn get_unchecked_mut<I>(&mut self, index: I) -> &mut I::Output
685687 where
686688 I: [const] SliceIndex<Self>,
rust-bors.toml+9
......@@ -26,6 +26,15 @@ labels_blocking_approval = [
2626 "S-waiting-on-t-clippy",
2727 # PR manually set to blocked
2828 "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"
2938]
3039
3140# 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 {
3737/// when the newly-bumped stage 0 compiler now knows about the formerly-missing targets.
3838const STAGE0_MISSING_TARGETS: &[&str] = &[
3939 // just a dummy comment so the list doesn't get onelined
40 "powerpc64-unknown-linux-gnuelfv2",
4041];
4142
4243/// 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 @@
1FROM ubuntu:22.04
2
3ARG DEBIAN_FRONTEND=noninteractive
4RUN 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
23COPY scripts/sccache.sh /scripts/
24RUN sh /scripts/sccache.sh
25
26ENV RUST_CONFIGURE_ARGS="--build=x86_64-unknown-linux-gnu"
27
28COPY scripts/x86_64-gnu-pre-stabilization.sh /scripts/
29ENV SCRIPT="/scripts/x86_64-gnu-pre-stabilization.sh"
src/ci/docker/scripts/x86_64-gnu-llvm.sh-5
......@@ -15,8 +15,3 @@ set -ex
1515
1616# Run the UI test suite in `--pass=check` mode, to ensure it continues to work.
1717../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.
21RUSTFLAGS_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
3set -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
13RUSTFLAGS_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
22RUSTFLAGS_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:
2222 os: ubuntu-24.04-16core-64gb
2323 <<: *base-job
2424
25 - &job-macos
25 - &job-macos-15
2626 os: macos-15 # macOS 15 Arm64
2727 <<: *base-job
2828
29 - &job-macos-26
30 os: macos-26 # macOS 26 Arm64
31 <<: *base-job
32
2933 - &job-windows
3034 os: windows-2025
3135 <<: *base-job
......@@ -152,6 +156,14 @@ pr:
152156 env:
153157 CODEGEN_BACKENDS: gcc
154158 <<: *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
155167
156168# Jobs that run when you perform a try build (@bors try)
157169# These jobs automatically inherit envs.try, to avoid repeating
......@@ -476,7 +488,7 @@ auto:
476488 DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer
477489 DIST_REQUIRE_ALL_TOOLS: 1
478490 CODEGEN_BACKENDS: llvm,cranelift
479 <<: *job-macos
491 <<: *job-macos-15
480492
481493 - name: dist-apple-various
482494 env:
......@@ -513,7 +525,7 @@ auto:
513525 MACOSX_DEPLOYMENT_TARGET: 10.12
514526 MACOSX_STD_DEPLOYMENT_TARGET: 10.12
515527 DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer
516 <<: *job-macos
528 <<: *job-macos-15
517529
518530 - name: dist-aarch64-apple
519531 env:
......@@ -537,7 +549,7 @@ auto:
537549 DEVELOPER_DIR: /Applications/Xcode_26.2.app/Contents/Developer
538550 DIST_REQUIRE_ALL_TOOLS: 1
539551 CODEGEN_BACKENDS: llvm,cranelift
540 <<: *job-macos
552 <<: *job-macos-15
541553
542554 - name: aarch64-apple
543555 env:
......@@ -553,7 +565,29 @@ auto:
553565 # supports the hardware, so only need to test it there.
554566 MACOSX_DEPLOYMENT_TARGET: 11.0
555567 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
557591
558592 ######################
559593 # Windows Builders #
src/doc/rustc/src/SUMMARY.md+1
......@@ -107,6 +107,7 @@
107107 - [powerpc-unknown-linux-gnuspe](platform-support/powerpc-unknown-linux-gnuspe.md)
108108 - [powerpc-unknown-linux-muslspe](platform-support/powerpc-unknown-linux-muslspe.md)
109109 - [powerpc64-ibm-aix](platform-support/aix.md)
110 - [powerpc64-unknown-linux-gnuelfv2](platform-support/powerpc64-unknown-linux-gnuelfv2.md)
110111 - [powerpc64-unknown-linux-musl](platform-support/powerpc64-unknown-linux-musl.md)
111112 - [powerpc64le-unknown-linux-gnu](platform-support/powerpc64le-unknown-linux-gnu.md)
112113 - [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.
8989target | notes
9090-------|-------
9191[`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
9293[`aarch64-unknown-linux-musl`](platform-support/aarch64-unknown-linux-musl.md) | ARM64 Linux with musl 1.2.5
9394[`aarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ARM64 OpenHarmony
9495`arm-unknown-linux-gnueabi` | Armv6 Linux (kernel 3.2+, glibc 2.17)
......@@ -266,7 +267,6 @@ target | std | host | notes
266267-------|:---:|:----:|-------
267268[`aarch64-kmc-solid_asp3`](platform-support/kmc-solid.md) | ✓ | | ARM64 SOLID with TOPPERS/ASP3
268269[`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
270270[`aarch64-unknown-helenos`](platform-support/helenos.md) | ✓ | | ARM64 HelenOS
271271[`aarch64-unknown-hermit`](platform-support/hermit.md) | ✓ | | ARM64 Hermit
272272[`aarch64-unknown-illumos`](platform-support/illumos.md) | ✓ | ✓ | ARM64 illumos
......@@ -385,6 +385,7 @@ target | std | host | notes
385385[`powerpc-wrs-vxworks-spe`](platform-support/vxworks.md) | ✓ | |
386386[`powerpc64-ibm-aix`](platform-support/aix.md) | ? | | 64-bit AIX (7.2 and newer)
387387[`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)
388389[`powerpc64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/powerpc64
389390[`powerpc64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | |
390391[`powerpc64le-unknown-freebsd`](platform-support/freebsd.md) | ✓ | ✓ | PPC64LE FreeBSD
src/doc/rustc/src/platform-support/freebsd.md+7-5
......@@ -11,8 +11,9 @@
1111
1212## Requirements
1313
14The `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.
14The `x86_64-unknown-freebsd` and `aarch64-unknown-freebsd` targets are Tier 2
15with host tools. `i686-unknown-freebsd` is Tier 2 without host tools.
16Other targets are Tier 3.
1617See [platform-support.md](../platform-support.md) for the full list.
1718
1819We commit that rustc will run on all currently supported releases of
......@@ -34,9 +35,10 @@ FreeBSD OS binaries use the ELF file format.
3435
3536## Building Rust programs
3637
37The `x86_64-unknown-freebsd` and `i686-unknown-freebsd` artifacts are
38distributed by the rust project and may be installed with rustup. Other
39targets are built by the ports system and may be installed with
38The `x86_64-unknown-freebsd`, `aarch64-unknown-freebsd` and
39`i686-unknown-freebsd` artifacts are distributed by the rust
40project and may be installed with rustup. Other targets are
41built by the ports system and may be installed with
4042[pkg(7)][pkg] or [ports(7)][ports].
4143
4244By 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
5Target for 64-bit big endian PowerPC Linux programs using the ELFv2 ABI and
6the GNU C library.
7
8## Target maintainers
9
10[@Gelbpunkt](https://github.com/Gelbpunkt)
11
12## Requirements
13
14Building the target itself requires a 64-bit big endian PowerPC compiler that
15uses the ELFv2 ABI and is supported by `cc-rs`.
16
17## Building the target
18
19The target can be built by enabling it for a `rustc` build.
20
21```toml
22[build]
23target = ["powerpc64-unknown-linux-gnuelfv2"]
24```
25
26Make 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]
31cc = "powerpc64-linux-gnu-gcc"
32cxx = "powerpc64-linux-gnu-g++"
33ar = "powerpc64-linux-gnu-ar"
34linker = "powerpc64-linux-gnu-gcc"
35```
36
37## Building Rust programs
38
39Rust does not yet ship pre-compiled artifacts for this target. To compile for
40this target, you will first need to build Rust with the target enabled (see
41"Building the target" above).
42
43## Cross-compilation
44
45This target can be cross-compiled from any host.
46
47## Testing
48
49This target can be tested as normal with `x.py` on a 64-bit big endian PowerPC
50host 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
1616reproducible, such that the same execution will work on all machines, regardless
1717of build environment.
1818
19Unlike passing the equivalent mapping through `--remap-path-prefix`, the current
20working directory does not take part in incremental compilation's dependency
21tracking. Building the same sources from different directories (for example, a
22sandboxed or per-build checkout path) therefore reuses the incremental cache
23rather than invalidating it.
24
1925## Example
2026```sh
2127# 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):
103103printer.add(RustType.StdString, StdStringProvider)
104104printer.add(RustType.StdOsString, StdOsStringProvider)
105105printer.add(RustType.StdStr, StdStrProvider)
106printer.add(RustType.StdBoxStr, StdBoxStrProvider)
106107printer.add(RustType.StdSlice, StdSliceProvider)
107108printer.add(RustType.StdVec, StdVecProvider)
108109printer.add(RustType.StdVecDeque, StdVecDequeProvider)
src/etc/gdb_providers.py+20
......@@ -142,6 +142,20 @@ class StdSliceProvider(printer_base):
142142 return "array"
143143
144144
145class 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
145159class StdVecProvider(printer_base):
146160 def __init__(self, valobj):
147161 self._valobj = valobj
......@@ -209,6 +223,12 @@ class StdRcProvider(printer_base):
209223 self._is_atomic = is_atomic
210224 self._ptr = unwrap_unique_or_non_null(valobj["ptr"])
211225 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)
212232 self._strong = unwrap_scalar_wrappers(self._ptr["strong"])
213233 self._weak = unwrap_scalar_wrappers(self._ptr["weak"]) - 1
214234
src/etc/rust_types.py+3
......@@ -37,12 +37,14 @@ class RustType(Enum):
3737 StdNonZeroNumber = 29
3838 StdPath = 30
3939 StdPathBuf = 31
40 StdBoxStr = 32
4041
4142
4243STD_STRING_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)String$")
4344STD_STR_REGEX = re.compile(r"^&(mut )?str$")
4445STD_SLICE_REGEX = re.compile(r"^&(mut )?\[.+\]$")
4546STD_OS_STRING_REGEX = re.compile(r"^(std::ffi::([a-z_]+::)+)OsString$")
47STD_BOX_STR_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)Box<str,.+>$")
4648STD_VEC_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)Vec<.+>$")
4749STD_VEC_DEQUE_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)VecDeque<.+>$")
4850STD_BTREE_SET_REGEX = re.compile(r"^(alloc::([a-z_]+::)+)BTreeSet<.+>$")
......@@ -67,6 +69,7 @@ STD_TYPE_TO_REGEX = {
6769 RustType.StdString: STD_STRING_REGEX,
6870 RustType.StdOsString: STD_OS_STRING_REGEX,
6971 RustType.StdStr: STD_STR_REGEX,
72 RustType.StdBoxStr: STD_BOX_STR_REGEX,
7073 RustType.StdSlice: STD_SLICE_REGEX,
7174 RustType.StdVec: STD_VEC_REGEX,
7275 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>(
29552955fn render_attributes_in_code(
29562956 w: &mut impl fmt::Write,
29572957 item: &clean::Item,
2958 prefix: &str,
2958 prefix: impl fmt::Display,
29592959 cx: &Context<'_>,
29602960) -> fmt::Result {
29612961 render_attributes_in_code_with_options(w, item, prefix, cx, true, "")
......@@ -2964,14 +2964,14 @@ fn render_attributes_in_code(
29642964pub(super) fn render_attributes_in_code_with_options(
29652965 w: &mut impl fmt::Write,
29662966 item: &clean::Item,
2967 prefix: &str,
2967 prefix: impl fmt::Display,
29682968 cx: &Context<'_>,
29692969 render_doc_hidden: bool,
29702970 open_tag: &str,
29712971) -> fmt::Result {
29722972 w.write_str(open_tag)?;
29732973 if render_doc_hidden && item.is_doc_hidden() {
2974 render_code_attribute(prefix, "#[doc(hidden)]", w)?;
2974 render_code_attribute(&prefix, "#[doc(hidden)]", w)?;
29752975 }
29762976 for attr in &item.attrs.other_attrs {
29772977 let hir::Attribute::Parsed(kind) = attr else { continue };
......@@ -2986,7 +2986,7 @@ pub(super) fn render_attributes_in_code_with_options(
29862986 AttributeKind::NonExhaustive(..) => Cow::Borrowed("#[non_exhaustive]"),
29872987 _ => continue,
29882988 };
2989 render_code_attribute(prefix, attr.as_ref(), w)?;
2989 render_code_attribute(&prefix, attr.as_ref(), w)?;
29902990 }
29912991
29922992 if let Some(def_id) = item.def_id()
......@@ -3008,7 +3008,11 @@ fn render_repr_attribute_in_code(
30083008 Ok(())
30093009}
30103010
3011fn render_code_attribute(prefix: &str, attr: &str, w: &mut impl fmt::Write) -> fmt::Result {
3011fn render_code_attribute(
3012 prefix: impl fmt::Display,
3013 attr: impl fmt::Display,
3014 w: &mut impl fmt::Write,
3015) -> fmt::Result {
30123016 write!(w, "<div class=\"code-attribute\">{prefix}{attr}</div>")
30133017}
30143018
src/librustdoc/html/render/print_item.rs+22-28
......@@ -1,3 +1,4 @@
1use std::borrow::Cow;
12use std::cmp::Ordering;
23use std::fmt::{self, Display, Write as _};
34use std::iter;
......@@ -395,25 +396,26 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i
395396 let (stab_tags, deprecation) = match import.source.did {
396397 Some(import_def_id) => {
397398 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));
400400 let deprecation = tcx
401401 .lookup_deprecation(import_def_id)
402402 .is_some_and(|deprecation| deprecation.is_in_effect());
403 (stab_tags, deprecation)
403 (Some(stab_tags), deprecation)
404404 }
405 None => (String::new(), item.is_deprecated(tcx)),
405 None => (None, item.is_deprecated(tcx)),
406406 };
407407 let visibility_and_hidden = visibility_and_hidden(myitem);
408408 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,
413414 };
414415 write!(
415416 w,
416417 "<dt{id}{deprecation_attr}><code>",
418 id = id.maybe_display(),
417419 deprecation_attr = deprecation_class_attr(deprecation)
418420 )?;
419421 write!(
......@@ -423,6 +425,7 @@ fn item_module(cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) -> i
423425 vis = visibility_print_with_space(myitem, cx),
424426 imp = print_import(import, cx),
425427 visibility_and_hidden = visibility_and_hidden,
428 stab_tags = stab_tags.maybe_display(),
426429 )?;
427430 }
428431 _ => {
......@@ -512,18 +515,14 @@ fn print_extra_info_tags(
512515 write!(f, "{}", tag_html("unstable", "", "Experimental"))?;
513516 }
514517
518 debug!(name = ?item.name, cfg = ?item.cfg, parent_cfg = ?parent.cfg, "Portability");
519
515520 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),
518523 };
519524
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 {
527526 write!(
528527 f,
529528 "{}",
......@@ -976,7 +975,7 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt:
976975 "Dyn Compatibility",
977976 "dyn-compatibility",
978977 None,
979 format!(
978 format_args!(
980979 "<div class=\"dyn-compatibility-info\"><p>This trait {} \
981980 <a href=\"{base}/reference/items/traits.html#dyn-compatibility\">dyn compatible</a>.</p>\
982981 <p><i>In older versions of Rust, dyn compatibility was called \"object safety\".</i></p></div>",
......@@ -1775,10 +1774,10 @@ fn item_variants(
17751774 w,
17761775 "{}",
17771776 write_section_heading(
1778 &format!("Variants{}", document_non_exhaustive_header(it)),
1777 format_args!("Variants{}", document_non_exhaustive_header(it)),
17791778 "variants",
17801779 Some("variants"),
1781 format!("{}<div class=\"variants\">", document_non_exhaustive(it)),
1780 format_args!("{}<div class=\"variants\">", document_non_exhaustive(it)),
17821781 ),
17831782 )?;
17841783
......@@ -2105,7 +2104,7 @@ fn item_fields(
21052104 if let None | Some(CtorKind::Fn) = ctor_kind
21062105 && fields.peek().is_some()
21072106 {
2108 let title = format!(
2107 let title = format_args!(
21092108 "{}{}",
21102109 if ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
21112110 document_non_exhaustive_header(it),
......@@ -2113,12 +2112,7 @@ fn item_fields(
21132112 write!(
21142113 w,
21152114 "{}",
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))
21222116 )?;
21232117 for (index, (field, ty)) in fields.enumerate() {
21242118 let field_name =
......@@ -2554,7 +2548,7 @@ fn render_struct_fields(
25542548 }
25552549 for field in fields {
25562550 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)?;
25582552 writeln!(
25592553 w,
25602554 "{tab} {vis}{name}: {ty},",
src/librustdoc/html/render/write_shared.rs+1-1
......@@ -413,7 +413,7 @@ impl CratesIndexPart {
413413 let layout = &cx.shared.layout;
414414 let style_files = &cx.shared.style_files;
415415 const DELIMITER: &str = "\u{FFFC}"; // users are being naughty if they have this
416 let content = format!(
416 let content = format_args!(
417417 "<div class=\"main-heading\">\
418418 <h1>List of all crates</h1>\
419419 <rustdoc-toolbar></rustdoc-toolbar>\
src/tools/miri/.github/workflows/ci.yml+11-4
......@@ -6,7 +6,7 @@ on:
66 branches:
77 - 'master'
88 schedule:
9 - cron: '44 4 * * *' # At 4:44 UTC every day.
9 - cron: '14 4 * * *' # At 4:14 UTC every day.
1010
1111defaults:
1212 run:
......@@ -157,15 +157,22 @@ jobs:
157157
158158 # This job is intentionally separate from `test` so that Priroda can be
159159 # developed as a separate crate inside the Miri repository for now.
160 priroda-build:
160 priroda:
161161 name: Priroda
162162 runs-on: ubuntu-latest
163163 steps:
164164 - uses: actions/checkout@v6
165165 - uses: ./.github/workflows/setup
166 - name: install Miri driver
167 run: ./miri install
166168 - name: build Priroda
167169 working-directory: priroda
168170 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
169176
170177 coverage:
171178 name: coverage report
......@@ -180,7 +187,7 @@ jobs:
180187 # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
181188 # And they should be added below in `cron-fail-notify` as well.
182189 conclusion:
183 needs: [test, style, bootstrap, coverage, priroda-build]
190 needs: [test, style, bootstrap, coverage, priroda]
184191 # We need to ensure this job does *not* get skipped if its dependencies fail,
185192 # because a skipped job is considered a success by GitHub. So we have to
186193 # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run
......@@ -264,7 +271,7 @@ jobs:
264271 cron-fail-notify:
265272 name: cronjob failure notification
266273 runs-on: ubuntu-latest
267 needs: [test, style, bootstrap, coverage, priroda-build]
274 needs: [test, style, bootstrap, coverage, priroda]
268275 if: ${{ github.event_name == 'schedule' && failure() }}
269276 steps:
270277 # Send a Zulip notification
src/tools/miri/Cargo.lock+2-2
......@@ -19,9 +19,9 @@ checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
1919
2020[[package]]
2121name = "aes"
22version = "0.9.0"
22version = "0.9.1"
2323source = "registry+https://github.com/rust-lang/crates.io-index"
24checksum = "66bd29a732b644c0431c6140f370d097879203d79b80c94a6747ba0872adaef8"
24checksum = "f1fc76eaeac4c9164506c466d4ffdd8ec9d0c5bf57ee97177c4d8eceb3a0e138"
2525dependencies = [
2626 "cipher",
2727 "cpubits",
src/tools/miri/README.md+4
......@@ -1,5 +1,9 @@
11# Miri
22
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
37Miri is an [Undefined Behavior][reference-ub] detection tool for Rust. It can run binaries and test
48suites of cargo projects and detect unsafe code that fails to uphold its safety requirements. For
59instance:
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 @@
22# It is not intended for manual editing.
33version = 4
44
5[[package]]
6name = "addr2line"
7version = "0.25.1"
8source = "registry+https://github.com/rust-lang/crates.io-index"
9checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b"
10dependencies = [
11 "gimli",
12]
13
14[[package]]
15name = "adler2"
16version = "2.0.1"
17source = "registry+https://github.com/rust-lang/crates.io-index"
18checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
19
520[[package]]
621name = "aes"
722version = "0.9.0"
......@@ -13,6 +28,31 @@ dependencies = [
1328 "cpufeatures",
1429]
1530
31[[package]]
32name = "aho-corasick"
33version = "1.1.4"
34source = "registry+https://github.com/rust-lang/crates.io-index"
35checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
36dependencies = [
37 "memchr",
38]
39
40[[package]]
41name = "annotate-snippets"
42version = "0.11.5"
43source = "registry+https://github.com/rust-lang/crates.io-index"
44checksum = "710e8eae58854cdc1790fcb56cca04d712a17be849eeb81da2a724bf4bae2bc4"
45dependencies = [
46 "anstyle",
47 "unicode-width",
48]
49
50[[package]]
51name = "anstyle"
52version = "1.0.14"
53source = "registry+https://github.com/rust-lang/crates.io-index"
54checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
55
1656[[package]]
1757name = "anyhow"
1858version = "1.0.102"
......@@ -25,6 +65,21 @@ version = "1.5.0"
2565source = "registry+https://github.com/rust-lang/crates.io-index"
2666checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
2767
68[[package]]
69name = "backtrace"
70version = "0.3.76"
71source = "registry+https://github.com/rust-lang/crates.io-index"
72checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6"
73dependencies = [
74 "addr2line",
75 "cfg-if",
76 "libc",
77 "miniz_oxide",
78 "object",
79 "rustc-demangle",
80 "windows-link 0.2.1",
81]
82
2883[[package]]
2984name = "bincode"
3085version = "1.3.3"
......@@ -40,12 +95,32 @@ version = "2.11.1"
4095source = "registry+https://github.com/rust-lang/crates.io-index"
4196checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
4297
98[[package]]
99name = "bstr"
100version = "1.12.1"
101source = "registry+https://github.com/rust-lang/crates.io-index"
102checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
103dependencies = [
104 "memchr",
105 "regex-automata",
106 "serde",
107]
108
43109[[package]]
44110name = "bumpalo"
45111version = "3.20.2"
46112source = "registry+https://github.com/rust-lang/crates.io-index"
47113checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
48114
115[[package]]
116name = "camino"
117version = "1.2.2"
118source = "registry+https://github.com/rust-lang/crates.io-index"
119checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48"
120dependencies = [
121 "serde_core",
122]
123
49124[[package]]
50125name = "capstone"
51126version = "0.14.0"
......@@ -65,6 +140,29 @@ dependencies = [
65140 "cc",
66141]
67142
143[[package]]
144name = "cargo-platform"
145version = "0.3.1"
146source = "registry+https://github.com/rust-lang/crates.io-index"
147checksum = "122ec45a44b270afd1402f351b782c676b173e3c3fb28d86ff7ebfb4d86a4ee4"
148dependencies = [
149 "serde",
150]
151
152[[package]]
153name = "cargo_metadata"
154version = "0.23.1"
155source = "registry+https://github.com/rust-lang/crates.io-index"
156checksum = "ef987d17b0a113becdd19d3d0022d04d7ef41f9efe4f3fb63ac44ba61df3ade9"
157dependencies = [
158 "camino",
159 "cargo-platform",
160 "semver",
161 "serde",
162 "serde_json",
163 "thiserror 2.0.18",
164]
165
68166[[package]]
69167name = "cc"
70168version = "1.2.62"
......@@ -127,6 +225,60 @@ dependencies = [
127225 "inout",
128226]
129227
228[[package]]
229name = "color-eyre"
230version = "0.6.5"
231source = "registry+https://github.com/rust-lang/crates.io-index"
232checksum = "e5920befb47832a6d61ee3a3a846565cfa39b331331e68a3b1d1116630f2f26d"
233dependencies = [
234 "backtrace",
235 "color-spantrace",
236 "eyre",
237 "indenter",
238 "once_cell",
239 "owo-colors",
240 "tracing-error",
241]
242
243[[package]]
244name = "color-spantrace"
245version = "0.3.0"
246source = "registry+https://github.com/rust-lang/crates.io-index"
247checksum = "b8b88ea9df13354b55bc7234ebcce36e6ef896aca2e42a15de9e10edce01b427"
248dependencies = [
249 "once_cell",
250 "owo-colors",
251 "tracing-core",
252 "tracing-error",
253]
254
255[[package]]
256name = "colored"
257version = "3.1.1"
258source = "registry+https://github.com/rust-lang/crates.io-index"
259checksum = "faf9468729b8cbcea668e36183cb69d317348c2e08e994829fb56ebfdfbaac34"
260dependencies = [
261 "windows-sys",
262]
263
264[[package]]
265name = "comma"
266version = "1.0.0"
267source = "registry+https://github.com/rust-lang/crates.io-index"
268checksum = "55b672471b4e9f9e95499ea597ff64941a309b2cdbffcc46f2cc5e2d971fd335"
269
270[[package]]
271name = "console"
272version = "0.16.3"
273source = "registry+https://github.com/rust-lang/crates.io-index"
274checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87"
275dependencies = [
276 "encode_unicode",
277 "libc",
278 "unicode-width",
279 "windows-sys",
280]
281
130282[[package]]
131283name = "cpubits"
132284version = "0.1.1"
......@@ -187,6 +339,12 @@ dependencies = [
187339 "windows-sys",
188340]
189341
342[[package]]
343name = "encode_unicode"
344version = "1.0.0"
345source = "registry+https://github.com/rust-lang/crates.io-index"
346checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0"
347
190348[[package]]
191349name = "equivalent"
192350version = "1.0.2"
......@@ -203,6 +361,16 @@ dependencies = [
203361 "windows-sys",
204362]
205363
364[[package]]
365name = "eyre"
366version = "0.6.12"
367source = "registry+https://github.com/rust-lang/crates.io-index"
368checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
369dependencies = [
370 "indenter",
371 "once_cell",
372]
373
206374[[package]]
207375name = "fastrand"
208376version = "2.4.1"
......@@ -288,6 +456,12 @@ dependencies = [
288456 "wasip3",
289457]
290458
459[[package]]
460name = "gimli"
461version = "0.32.3"
462source = "registry+https://github.com/rust-lang/crates.io-index"
463checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
464
291465[[package]]
292466name = "hashbrown"
293467version = "0.15.5"
......@@ -324,6 +498,12 @@ version = "2.3.0"
324498source = "registry+https://github.com/rust-lang/crates.io-index"
325499checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
326500
501[[package]]
502name = "indenter"
503version = "0.3.4"
504source = "registry+https://github.com/rust-lang/crates.io-index"
505checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5"
506
327507[[package]]
328508name = "indexmap"
329509version = "2.14.0"
......@@ -336,6 +516,19 @@ dependencies = [
336516 "serde_core",
337517]
338518
519[[package]]
520name = "indicatif"
521version = "0.18.4"
522source = "registry+https://github.com/rust-lang/crates.io-index"
523checksum = "25470f23803092da7d239834776d653104d551bc4d7eacaf31e6837854b8e9eb"
524dependencies = [
525 "console",
526 "portable-atomic",
527 "unicode-width",
528 "unit-prefix",
529 "web-time",
530]
531
339532[[package]]
340533name = "inout"
341534version = "0.2.2"
......@@ -381,12 +574,24 @@ dependencies = [
381574 "wasm-bindgen",
382575]
383576
577[[package]]
578name = "lazy_static"
579version = "1.5.0"
580source = "registry+https://github.com/rust-lang/crates.io-index"
581checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
582
384583[[package]]
385584name = "leb128fmt"
386585version = "0.1.0"
387586source = "registry+https://github.com/rust-lang/crates.io-index"
388587checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
389588
589[[package]]
590name = "levenshtein"
591version = "1.0.5"
592source = "registry+https://github.com/rust-lang/crates.io-index"
593checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760"
594
390595[[package]]
391596name = "libc"
392597version = "0.2.186"
......@@ -481,6 +686,15 @@ dependencies = [
481686 "libc",
482687]
483688
689[[package]]
690name = "miniz_oxide"
691version = "0.8.9"
692source = "registry+https://github.com/rust-lang/crates.io-index"
693checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
694dependencies = [
695 "adler2",
696]
697
484698[[package]]
485699name = "mio"
486700version = "1.2.0"
......@@ -537,6 +751,15 @@ dependencies = [
537751 "autocfg",
538752]
539753
754[[package]]
755name = "object"
756version = "0.37.3"
757source = "registry+https://github.com/rust-lang/crates.io-index"
758checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
759dependencies = [
760 "memchr",
761]
762
540763[[package]]
541764name = "once_cell"
542765version = "1.21.4"
......@@ -549,6 +772,12 @@ version = "0.2.0"
549772source = "registry+https://github.com/rust-lang/crates.io-index"
550773checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
551774
775[[package]]
776name = "owo-colors"
777version = "4.3.0"
778source = "registry+https://github.com/rust-lang/crates.io-index"
779checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d"
780
552781[[package]]
553782name = "parking_lot"
554783version = "0.12.5"
......@@ -605,6 +834,12 @@ version = "0.2.17"
605834source = "registry+https://github.com/rust-lang/crates.io-index"
606835checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
607836
837[[package]]
838name = "portable-atomic"
839version = "1.13.1"
840source = "registry+https://github.com/rust-lang/crates.io-index"
841checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49"
842
608843[[package]]
609844name = "ppv-lite86"
610845version = "0.2.21"
......@@ -614,6 +849,15 @@ dependencies = [
614849 "zerocopy",
615850]
616851
852[[package]]
853name = "prettydiff"
854version = "0.9.0"
855source = "registry+https://github.com/rust-lang/crates.io-index"
856checksum = "ac17546d82912e64874e3d5b40681ce32eac4e5834344f51efcf689ff1550a65"
857dependencies = [
858 "owo-colors",
859]
860
617861[[package]]
618862name = "prettyplease"
619863version = "0.2.37"
......@@ -629,6 +873,8 @@ name = "priroda"
629873version = "0.1.0"
630874dependencies = [
631875 "miri",
876 "regex",
877 "ui_test",
632878]
633879
634880[[package]]
......@@ -724,15 +970,71 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
724970dependencies = [
725971 "getrandom 0.2.17",
726972 "libredox",
727 "thiserror",
973 "thiserror 2.0.18",
728974]
729975
976[[package]]
977name = "regex"
978version = "1.12.4"
979source = "registry+https://github.com/rust-lang/crates.io-index"
980checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba"
981dependencies = [
982 "aho-corasick",
983 "memchr",
984 "regex-automata",
985 "regex-syntax",
986]
987
988[[package]]
989name = "regex-automata"
990version = "0.4.14"
991source = "registry+https://github.com/rust-lang/crates.io-index"
992checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
993dependencies = [
994 "aho-corasick",
995 "memchr",
996 "regex-syntax",
997]
998
999[[package]]
1000name = "regex-syntax"
1001version = "0.8.11"
1002source = "registry+https://github.com/rust-lang/crates.io-index"
1003checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
1004
1005[[package]]
1006name = "rustc-demangle"
1007version = "0.1.27"
1008source = "registry+https://github.com/rust-lang/crates.io-index"
1009checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
1010
7301011[[package]]
7311012name = "rustc-hash"
7321013version = "1.1.0"
7331014source = "registry+https://github.com/rust-lang/crates.io-index"
7341015checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
7351016
1017[[package]]
1018name = "rustc_version"
1019version = "0.4.1"
1020source = "registry+https://github.com/rust-lang/crates.io-index"
1021checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
1022dependencies = [
1023 "semver",
1024]
1025
1026[[package]]
1027name = "rustfix"
1028version = "0.8.7"
1029source = "registry+https://github.com/rust-lang/crates.io-index"
1030checksum = "82fa69b198d894d84e23afde8e9ab2af4400b2cba20d6bf2b428a8b01c222c5a"
1031dependencies = [
1032 "serde",
1033 "serde_json",
1034 "thiserror 1.0.69",
1035 "tracing",
1036]
1037
7361038[[package]]
7371039name = "rustix"
7381040version = "1.1.4"
......@@ -763,6 +1065,10 @@ name = "semver"
7631065version = "1.0.28"
7641066source = "registry+https://github.com/rust-lang/crates.io-index"
7651067checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
1068dependencies = [
1069 "serde",
1070 "serde_core",
1071]
7661072
7671073[[package]]
7681074name = "serde"
......@@ -807,6 +1113,15 @@ dependencies = [
8071113 "zmij",
8081114]
8091115
1116[[package]]
1117name = "sharded-slab"
1118version = "0.1.7"
1119source = "registry+https://github.com/rust-lang/crates.io-index"
1120checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
1121dependencies = [
1122 "lazy_static",
1123]
1124
8101125[[package]]
8111126name = "shlex"
8121127version = "1.3.0"
......@@ -831,6 +1146,17 @@ version = "1.15.1"
8311146source = "registry+https://github.com/rust-lang/crates.io-index"
8321147checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
8331148
1149[[package]]
1150name = "spanned"
1151version = "0.4.1"
1152source = "registry+https://github.com/rust-lang/crates.io-index"
1153checksum = "c92d4b0c055fde758f086eb4a6e73410247df8a3837fd606d2caeeaf72aa566d"
1154dependencies = [
1155 "anyhow",
1156 "bstr",
1157 "color-eyre",
1158]
1159
8341160[[package]]
8351161name = "static_assertions"
8361162version = "1.1.0"
......@@ -861,13 +1187,33 @@ dependencies = [
8611187 "windows-sys",
8621188]
8631189
1190[[package]]
1191name = "thiserror"
1192version = "1.0.69"
1193source = "registry+https://github.com/rust-lang/crates.io-index"
1194checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
1195dependencies = [
1196 "thiserror-impl 1.0.69",
1197]
1198
8641199[[package]]
8651200name = "thiserror"
8661201version = "2.0.18"
8671202source = "registry+https://github.com/rust-lang/crates.io-index"
8681203checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
8691204dependencies = [
870 "thiserror-impl",
1205 "thiserror-impl 2.0.18",
1206]
1207
1208[[package]]
1209name = "thiserror-impl"
1210version = "1.0.69"
1211source = "registry+https://github.com/rust-lang/crates.io-index"
1212checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
1213dependencies = [
1214 "proc-macro2",
1215 "quote",
1216 "syn",
8711217]
8721218
8731219[[package]]
......@@ -881,24 +1227,112 @@ dependencies = [
8811227 "syn",
8821228]
8831229
1230[[package]]
1231name = "thread_local"
1232version = "1.1.9"
1233source = "registry+https://github.com/rust-lang/crates.io-index"
1234checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
1235dependencies = [
1236 "cfg-if",
1237]
1238
1239[[package]]
1240name = "tracing"
1241version = "0.1.44"
1242source = "registry+https://github.com/rust-lang/crates.io-index"
1243checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
1244dependencies = [
1245 "pin-project-lite",
1246 "tracing-core",
1247]
1248
1249[[package]]
1250name = "tracing-core"
1251version = "0.1.36"
1252source = "registry+https://github.com/rust-lang/crates.io-index"
1253checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
1254dependencies = [
1255 "once_cell",
1256 "valuable",
1257]
1258
1259[[package]]
1260name = "tracing-error"
1261version = "0.2.1"
1262source = "registry+https://github.com/rust-lang/crates.io-index"
1263checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db"
1264dependencies = [
1265 "tracing",
1266 "tracing-subscriber",
1267]
1268
1269[[package]]
1270name = "tracing-subscriber"
1271version = "0.3.23"
1272source = "registry+https://github.com/rust-lang/crates.io-index"
1273checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
1274dependencies = [
1275 "sharded-slab",
1276 "thread_local",
1277 "tracing-core",
1278]
1279
8841280[[package]]
8851281name = "typenum"
8861282version = "1.20.0"
8871283source = "registry+https://github.com/rust-lang/crates.io-index"
8881284checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de"
8891285
1286[[package]]
1287name = "ui_test"
1288version = "0.30.7"
1289source = "registry+https://github.com/rust-lang/crates.io-index"
1290checksum = "8c8811281d587a786747c0c49245925016c07767bc996305bdd34d5ce076786a"
1291dependencies = [
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
8901312[[package]]
8911313name = "unicode-ident"
8921314version = "1.0.24"
8931315source = "registry+https://github.com/rust-lang/crates.io-index"
8941316checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
8951317
1318[[package]]
1319name = "unicode-width"
1320version = "0.2.2"
1321source = "registry+https://github.com/rust-lang/crates.io-index"
1322checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
1323
8961324[[package]]
8971325name = "unicode-xid"
8981326version = "0.2.6"
8991327source = "registry+https://github.com/rust-lang/crates.io-index"
9001328checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
9011329
1330[[package]]
1331name = "unit-prefix"
1332version = "0.5.2"
1333source = "registry+https://github.com/rust-lang/crates.io-index"
1334checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3"
1335
9021336[[package]]
9031337name = "uuid"
9041338version = "1.23.1"
......@@ -910,6 +1344,12 @@ dependencies = [
9101344 "wasm-bindgen",
9111345]
9121346
1347[[package]]
1348name = "valuable"
1349version = "0.1.1"
1350source = "registry+https://github.com/rust-lang/crates.io-index"
1351checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
1352
9131353[[package]]
9141354name = "wasi"
9151355version = "0.11.1+wasi-snapshot-preview1"
......@@ -1013,6 +1453,16 @@ dependencies = [
10131453 "semver",
10141454]
10151455
1456[[package]]
1457name = "web-time"
1458version = "1.1.0"
1459source = "registry+https://github.com/rust-lang/crates.io-index"
1460checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
1461dependencies = [
1462 "js-sys",
1463 "wasm-bindgen",
1464]
1465
10161466[[package]]
10171467name = "windows"
10181468version = "0.61.3"
src/tools/miri/priroda/Cargo.toml+9
......@@ -10,10 +10,19 @@ edition = "2024"
1010[[bin]]
1111name = "priroda"
1212path = "src/main.rs"
13test = false # Disable unit tests to avoid errors with --bless
1314doctest = false # and no doc tests
1415
16[[test]]
17name = "cli"
18harness = false
19
1520[dependencies]
1621miri = { path = ".." }
1722
1823[package.metadata.rust-analyzer]
1924rustc_private = true
25
26[dev-dependencies]
27ui_test = "0.30.2"
28regex = "1.5.5"
src/tools/miri/priroda/README.md+43-6
......@@ -1,13 +1,13 @@
11# Priroda
22
3Priroda is a step-through debugger for Rust programs running under
4Miri.
3Priroda is a step-through debugger for Rust programs running under Miri.
54
65Current focus:
76
87- simple CLI prototype
98- 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
1111
1212## Setup
1313
......@@ -28,10 +28,47 @@ export MIRI_SYSROOT="$(cargo +miri miri setup --print-sysroot)"
2828
2929## Run
3030
31Priroda currently reads `MIRI_SYSROOT` directly. After setup:
31Priroda currently reads `MIRI_SYSROOT` directly. After setup, run Priroda
32from `miri/priroda/`:
3233
3334```sh
34cargo run -p priroda -- tests/pass/empty_main.rs
35cargo run -- ../tests/pass/empty_main.rs
3536```
3637
37At the prompt, press Enter or type `s` / `step`.
38## Test
39
40Priroda's CLI tests also need `MIRI_SYSROOT`. Run them from `miri/priroda/`:
41
42```sh
43cargo test
44```
45
46If 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
49cargo test -- --bless
50```
51
52or
53
54```sh
55RUSTC_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
67EOF also exits Priroda cleanly.
68
69Example:
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;
1010extern crate rustc_log;
1111extern crate rustc_middle;
1212extern crate rustc_session;
13extern crate rustc_span;
1314
15use std::collections::{HashMap, HashSet};
1416use std::io::{self, Write};
17use std::path::PathBuf;
1518
1619use miri::*;
1720use rustc_driver::Compilation;
1821use rustc_hir::attrs::CrateType;
1922use rustc_interface::interface;
23use rustc_middle::mir;
2024use rustc_middle::ty::TyCtxt;
2125use rustc_session::EarlyDiagCtxt;
2226use rustc_session::config::ErrorOutputType;
27use rustc_span::Span;
28use rustc_span::source_map::SourceMap;
29
2330fn find_sysroot() -> String {
2431 std::env::var("MIRI_SYSROOT")
2532 .expect("set MIRI_SYSROOT to the path from `cargo miri setup --print-sysroot`")
......@@ -38,13 +45,14 @@ fn main() {
3845 args.push(sysroot_flag);
3946 args.push(find_sysroot());
4047 }
41 //TODO: handle the same `-Z` flags that Miri accepts.
48 // FIXME: handle the same `-Z` flags that Miri accepts.
4249 rustc_driver::run_compiler(&args, &mut PrirodaCompilerCalls::new());
4350}
4451
4552struct PrirodaCompilerCalls;
4653
4754impl PrirodaCompilerCalls {
55 // FIXME: remove this constructor if PrirodaCompilerCalls remains a unit struct.
4856 fn new() -> Self {
4957 Self
5058 }
......@@ -56,20 +64,25 @@ impl rustc_driver::Callbacks for PrirodaCompilerCalls {
5664 tcx.dcx().abort_if_errors();
5765
5866 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.
6068 tcx.dcx().fatal("priroda only makes sense on bin crates");
6169 }
6270
6371 let ecx = create_ecx(tcx);
6472
6573 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);
6776
6877 match result.report_err() {
6978 Ok(()) => {}
7079 Err(err) =>
7180 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}");
7386 if return_code != 0 {
7487 std::process::exit(return_code);
7588 }
......@@ -82,66 +95,307 @@ impl rustc_driver::Callbacks for PrirodaCompilerCalls {
8295
8396fn create_ecx<'tcx>(tcx: TyCtxt<'tcx>) -> MiriInterpCx<'tcx> {
8497 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`.
85100 let config = MiriConfig::default();
101 // FIXME: report interpreter initialization failures instead of panicking.
86102 miri::create_ecx(tcx, entry_id, entry_type, &config, None).unwrap()
87103}
88104
89pub struct PrirodaContext<'tcx> {
105/// Structured source information for frontends.
106struct SourceLocation {
107 // storing `span` to use it lazily to compute path.
108 span: Span,
109 line: usize,
110}
111
112impl 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.
120type 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.
125struct PrirodaContext<'tcx> {
90126 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.
133enum 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.
141enum InstructionVisibility {
142 NoInstruction,
143 Hidden,
144 Visible,
145}
146
147/// Describes why execution stopped and returned control to the frontend.
148enum StepResult {
149 Step,
150 Breakpoint,
151}
152
153fn normalize_path(path: PathBuf) -> PathBuf {
154 path.canonicalize().unwrap_or(path)
91155}
92156
93157impl<'tcx> PrirodaContext<'tcx> {
94158 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)
96165 }
97166
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)
102170 }
103171
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.
105271 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 })
111280 }
112 fn run_command(&mut self, command: SessionCommand) -> InterpResult<'tcx> {
281
282 fn run_command(&mut self, command: DebuggerCommand) -> InterpResult<'tcx, CommandResult> {
113283 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),
115290 }
116291 }
117292}
118293
119enum SessionCommand {
294enum DebuggerCommand {
120295 Step,
296 TerminateSession,
297 Continue,
298 Breakpoint(PathBuf, usize),
121299}
122300
123fn parse_command(input: &str) -> Option<SessionCommand> {
124 match input.trim() {
125 "" | "s" | "step" => Some(SessionCommand::Step),
126 _ => None,
127 }
301enum BreakpointSetResult {
302 Added(PathBuf, usize),
303 Duplicate,
304 // FIXME: add pending breakpoint support later if needed.
128305}
129306
130fn run_cli_loop<'tcx>(session: &mut PrirodaContext<'tcx>) -> InterpResult<'tcx> {
131 loop {
132 print!("(priroda) ");
133 io::stdout().flush().unwrap();
307enum 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
315struct CLI;
316
317impl 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();
134325
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 }
139330
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,
145375 }
146376 }
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 }
147401}
src/tools/miri/priroda/tests/cli.rs created+58
......@@ -0,0 +1,58 @@
1use std::env;
2use std::path::PathBuf;
3use std::process::Command;
4
5use regex::bytes::Regex;
6use ui_test::spanned::Spanned;
7use ui_test::status_emitter::StatusEmitter;
8use ui_test::{CommandBuilder, Config, default_file_filter, run_tests_generic};
9
10fn 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.
4fn main() {}
src/tools/miri/priroda/tests/ui/continue_finishes_program.stdin created+1
......@@ -0,0 +1 @@
1continue
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.
5fn main() {
6 let _value = 0;
7}
src/tools/miri/priroda/tests/ui/continue_hits_breakpoint.stdin created+3
......@@ -0,0 +1,3 @@
1break tests/ui/continue_hits_breakpoint.rs:6
2continue
3quit
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.
4fn main() {}
src/tools/miri/priroda/tests/ui/duplicate_breakpoint.stdin created+3
......@@ -0,0 +1,3 @@
1break nowhere.rs:12
2b nowhere.rs:12
3quit
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.
5fn main() {}
src/tools/miri/priroda/tests/ui/empty_main.stdin created+1
......@@ -0,0 +1 @@
1quit
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.
5fn main() {}
src/tools/miri/priroda/tests/ui/eof_exits_cleanly.stdin
src/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.
5fn main() {}
src/tools/miri/priroda/tests/ui/invalid_commands.stdin created+4
......@@ -0,0 +1,4 @@
1wat
2break
3break nowhere.rs:not-a-line
4quit
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.
5fn main() {
6 let _value = 0;
7}
src/tools/miri/priroda/tests/ui/repeated_same_line_breakpoint.stdin created+4
......@@ -0,0 +1,4 @@
1break tests/ui/repeated_same_line_breakpoint.rs:6
2continue
3continue
4quit
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.
5fn main() {}
src/tools/miri/priroda/tests/ui/source_step_changes_line.stdin created+3
......@@ -0,0 +1,3 @@
1s
2step
3quit
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.
5fn main() {}
src/tools/miri/priroda/tests/ui/step_aliases.stdin created+4
......@@ -0,0 +1,4 @@
1s
2step
3
4quit
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 @@
1bef8e620f19adbfd1530e916ab8caa296ef9c3ee
1029c9e18dd1f4668e1d42bb187c1c263dfe20093
src/tools/miri/src/shims/loongarch.rs+11-4
......@@ -1,4 +1,4 @@
1use rustc_abi::CanonAbi;
1use rustc_abi::{CanonAbi, Size};
22use rustc_middle::ty::Ty;
33use rustc_span::Symbol;
44use rustc_target::callconv::FnAbi;
......@@ -55,10 +55,17 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
5555 // b/h/w variants and i64 for the d variant, per the LLVM intrinsic
5656 // definitions.
5757 // 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.
6063 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 };
6269
6370 let result = compute_crc32(crc, data, bit_size, polynomial);
6471 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> {
6363
6464impl<'tcx> TlsData<'tcx> {
6565 /// 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.
6767 #[expect(clippy::arithmetic_side_effects)]
6868 pub fn create_tls_key(
6969 &mut self,
7070 dtor: Option<(ty::Instance<'tcx>, Span)>,
71 max_size: Size,
71 key_size: Size,
7272 ) -> InterpResult<'tcx, TlsKey> {
7373 let new_key = self.next_key;
7474 self.next_key += 1;
7575 self.keys.try_insert(new_key, TlsEntry { data: Default::default(), dtor }).unwrap();
7676 trace!("New TLS key allocated: {} with dtor {:?}", new_key, dtor);
7777
78 if max_size.bits() < 128 && new_key >= (1u128 << max_size.bits()) {
78 if new_key > key_size.unsigned_int_max() {
7979 throw_unsup_format!("we ran out of TLS key space");
8080 }
8181 interp_ok(new_key)
......@@ -267,7 +267,13 @@ impl<'tcx> TlsDtorsState<'tcx> {
267267 let fls_keys_with_dtors = this.lookup_windows_fls_keys_with_dtors()?;
268268
269269 // 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 );
271277 }
272278 _ => {
273279 // No TLS dtor support.
......@@ -453,35 +459,31 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
453459 // Keys without dtors will not be set to zero.
454460 // [PflsCallbackFunction's docs]: https://learn.microsoft.com/en-us/windows/win32/api/winnt/nc-winnt-pfls_callback_function
455461 // [`RtlProcessFlsData`]: https://github.com/wine-mirror/wine/blob/wine-11.0/dlls/ntdll/thread.c#L679
456
462
457463 // We are done running the previous key's destructor, so set it's value to zero.
458464 if let Some(last_key) = state.last_key.take() {
459465 if let Some(TlsEntry { data, .. }) = this.machine.tls.keys.get_mut(&last_key) {
460 data.remove(&active_thread);
466 data.remove(&active_thread);
461467 };
462468 }
463469
464470 while let Some(key) = state.remaining_keys.pop_front() {
465471 // Fetch dtor for this `key`.
466472 // 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;
470475 };
471476
472477 let (instance, span) = dtor.to_owned();
473
478
474479 // 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 };
479481
480482 assert!(
481483 ptr.to_target_usize(this).unwrap() != 0,
482484 "TLS key's value can't be null (should be absent instead)"
483485 );
484
486
485487 trace!("Running TLS dtor {:?} on {:?} at {:?}", instance, ptr, active_thread);
486488
487489 // We'll clear this key's value next time we are called.
......@@ -497,7 +499,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
497499
498500 return interp_ok(Poll::Pending);
499501 }
500
502
501503 // We are done scheduling all the keys.
502504 interp_ok(Poll::Ready(()))
503505 }
src/tools/miri/src/shims/windows/foreign_items.rs+9
......@@ -638,6 +638,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
638638
639639 // Thread-local storage
640640 "TlsAlloc" => {
641 // FIXME: This does not have a direct test (#3179).
641642 // This just creates a key; Windows does not natively support TLS destructors.
642643
643644 // Create key and return it.
......@@ -651,6 +652,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
651652 this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
652653 }
653654 "TlsGetValue" => {
655 // FIXME: This does not have a direct test (#3179).
654656 let [key] = this.check_shim_sig(
655657 shim_sig!(extern "system" fn(u32) -> *mut _),
656658 link_name,
......@@ -663,6 +665,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
663665 this.write_scalar(ptr, dest)?;
664666 }
665667 "TlsSetValue" => {
668 // FIXME: This does not have a direct test (#3179).
666669 let [key, new_ptr] = this.check_shim_sig(
667670 shim_sig!(extern "system" fn(u32, *mut _) -> winapi::BOOL),
668671 link_name,
......@@ -678,6 +681,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
678681 this.write_int(1, dest)?;
679682 }
680683 "TlsFree" => {
684 // FIXME: This does not have a direct test (#3179).
681685 let [key] = this.check_shim_sig(
682686 shim_sig!(extern "system" fn(u32) -> winapi::BOOL),
683687 link_name,
......@@ -693,6 +697,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
693697
694698 // Fiber-local storage - similar to TLS but supports destructors.
695699 "FlsAlloc" => {
700 // FIXME: This does not have a direct test (#3179).
696701 // Create key and return it.
697702 let [dtor] = this.check_shim_sig(
698703 shim_sig!(extern "system" fn(winapi::PFLS_CALLBACK_FUNCTION) -> u32),
......@@ -716,6 +721,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
716721 this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
717722 }
718723 "FlsGetValue" => {
724 // FIXME: This does not have a direct test (#3179).
719725 let [key] = this.check_shim_sig(
720726 shim_sig!(extern "system" fn(u32) -> *mut _),
721727 link_name,
......@@ -728,6 +734,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
728734 this.write_scalar(ptr, dest)?;
729735 }
730736 "FlsSetValue" => {
737 // FIXME: This does not have a direct test (#3179).
731738 let [key, new_ptr] = this.check_shim_sig(
732739 shim_sig!(extern "system" fn(u32, *mut _) -> winapi::BOOL),
733740 link_name,
......@@ -743,6 +750,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
743750 this.write_int(1, dest)?;
744751 }
745752 "FlsFree" => {
753 // FIXME: This does not have a direct test (#3179).
746754 let [key] = this.check_shim_sig(
747755 shim_sig!(extern "system" fn(u32) -> winapi::BOOL),
748756 link_name,
......@@ -763,6 +771,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
763771 this.write_int(1, dest)?;
764772 }
765773 "IsThreadAFiber" => {
774 // FIXME: This does not have a direct test (#3179).
766775 let [] = this.check_shim_sig(
767776 shim_sig!(extern "system" fn() -> winapi::BOOL),
768777 link_name,
src/tools/miri/src/shims/windows/fs.rs+137-90
......@@ -157,29 +157,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
157157 throw_unsup_format!("CreateFileW: Template files are not supported");
158158 }
159159
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;
177164 desired_access &= !generic_read;
178 options.read(true);
179165 }
180 if desired_write {
166 let mut desired_write = false;
167 if desired_access & generic_write != 0 {
168 desired_write = true;
181169 desired_access &= !generic_write;
182 options.write(true);
183170 }
184171
185172 if desired_access != 0 {
......@@ -188,90 +175,150 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
188175 );
189176 }
190177
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();
207194
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 };
212216 #[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
217227 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 }
228258 }
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 }
237267 }
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);
249282 }
250283 }
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;
253308 }
254 }
255309
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 }
261316 let fd_num = this.machine.fds.insert_new(FileHandle {
262317 file,
263318 writable: desired_write,
264319 readable: desired_read,
265320 });
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));
275322 }
276323 }
277324 }
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
7use std::sync::atomic::*;
8
9static X: (i32, AtomicI32) = (0, AtomicI32::new(1));
10
11fn 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 @@
1error: 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 |
4LL | 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
9help: <TAG> was created by a SharedReadOnly retag at offsets [0x0..0x4]
10 --> tests/fail/both_borrows/mixed_mutability_static.rs:LL:CC
11 |
12LL | let ptr = &raw const X;
13 | ^^^^^^^^^^^^
14
15note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
16
17error: 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 @@
1error: Undefined Behavior: write access through <TAG> at ALLOC[0x0] is forbidden
2 --> tests/fail/both_borrows/mixed_mutability_static.rs:LL:CC
3 |
4LL | 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
10help: the accessed tag <TAG> was created here, in the initial state Cell
11 --> tests/fail/both_borrows/mixed_mutability_static.rs:LL:CC
12 |
13LL | let ptr = &raw const X;
14 | ^
15
16note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
17
18error: 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;
22
33enum Never {}
44
5fn foo(x: Never) { //~ERROR: invalid value of type Never
5fn foo(x: Never) {
6 //~^ERROR: invalid value of type Never
67 let ptr = &raw const x;
78 println!("{ptr:p}");
89}
src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs+8-7
......@@ -1,10 +1,11 @@
11//@only-target: linux android illumos
22// test_epoll_block_then_unblock and test_epoll_race depend on a deterministic schedule.
3//@compile-flags: -Zmiri-deterministic-concurrency
43//@revisions: edge_triggered level_triggered
4//@run-native
55
66use std::convert::TryInto;
77use std::thread;
8use std::time::Duration;
89
910#[path = "../../utils/libc.rs"]
1011mod libc_utils;
......@@ -71,8 +72,8 @@ fn test_epoll_block_then_unblock() {
7172 check_epoll_wait(epfd, &[Ev { events: EPOLLOUT, data: fds[0] }], -1);
7273
7374 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.
7677 write_all(fds[1], b"abcde").unwrap();
7778 });
7879
......@@ -80,7 +81,7 @@ fn test_epoll_block_then_unblock() {
8081 // Edge-triggered epoll will block until the write succeeds and the buffer
8182 // becomes readable. This is because we already read the writable edge
8283 // 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);
8485 } else {
8586 // Level-triggered epoll won't wait for the write to succeed because
8687 // _some_ readiness is already set (in this case the EPOLLOUT).
......@@ -142,7 +143,7 @@ fn test_epoll_race() {
142143 // Write to the eventfd instance.
143144 write_all(fd, &1_u64.to_ne_bytes()).unwrap();
144145 });
145 thread::yield_now();
146 thread::sleep(Duration::from_millis(10));
146147 // epoll_wait for EPOLLIN.
147148 check_epoll_wait(epfd, &[Ev { events: EPOLLIN, data: fd }], -1);
148149 // Read from the static mut variable.
......@@ -168,7 +169,7 @@ fn wakeup_on_new_interest() {
168169 check_epoll_wait(epfd, &[Ev { events: EPOLLIN | EPOLLOUT, data: fds[1] }], -1);
169170 });
170171 // Ensure the thread is blocked.
171 std::thread::yield_now();
172 thread::sleep(Duration::from_millis(10));
172173
173174 // Register fd[1] with EPOLLIN|EPOLLOUT|EPOLLRDHUP (and EPOLLET if we're in the
174175 // `edge_triggered` revision).
......@@ -212,7 +213,7 @@ fn multiple_events_wake_multiple_threads() {
212213 Ev { events: e.events.cast_signed(), data: e.u64.try_into().unwrap() }
213214 });
214215 // Yield so both threads are waiting now.
215 thread::yield_now();
216 thread::sleep(Duration::from_millis(10));
216217
217218 // Trigger the eventfd. This triggers two events at once!
218219 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 @@
11//@only-target: linux android illumos
22//@revisions: edge_triggered level_triggered
3//@run-native
34
45#[path = "../../utils/libc.rs"]
56mod libc_utils;
src/tools/miri/tests/pass-dep/libc/libc-fs-flock.rs+1
......@@ -1,6 +1,7 @@
11//@ignore-target: windows # no libc
22//@ignore-target: solaris # Does not have flock
33//@compile-flags: -Zmiri-disable-isolation
4//@run-native
45
56//@revisions: windows_host unix_host
67//@[unix_host] ignore-host: windows
src/tools/miri/tests/pass-dep/libc/libc-fs-permissions.rs+1
......@@ -1,6 +1,7 @@
11//@ignore-target: windows # no libc
22//@ignore-host: windows # needs unix PermissionExt
33//@compile-flags: -Zmiri-disable-isolation
4//@run-native
45
56use std::ffi::{CStr, CString};
67use std::mem::MaybeUninit;
src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs+1
......@@ -2,6 +2,7 @@
22//@ignore-host: windows # creating symlinks requires admin permissions on Windows
33//@ignore-target: windows # File handling is not implemented yet
44//@compile-flags: -Zmiri-disable-isolation
5//@run-native
56
67use std::ffi::CString;
78use std::io::ErrorKind;
src/tools/miri/tests/pass-dep/libc/libc-fs.rs+26-11
......@@ -1,5 +1,6 @@
11//@ignore-target: windows # no libc
22//@compile-flags: -Zmiri-disable-isolation
3//@run-native
34
45use std::ffi::{CStr, CString, OsString};
56use std::fs::{self, File, canonicalize, create_dir, remove_dir, remove_file};
......@@ -423,17 +424,21 @@ fn test_posix_mkstemp() {
423424 drop(file);
424425 remove_file(path).unwrap();
425426
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 }
437442 }
438443
439444 env::set_current_dir(old_cwd).unwrap();
......@@ -935,6 +940,11 @@ fn test_readv() {
935940
936941/// Test that vectored reads without any buffers return zero.
937942fn 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
938948 let path = utils::prepare_with_content("pass-libc-readv-empty-bufs.txt", &[1u8, 2, 3]);
939949 let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap();
940950 let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) };
......@@ -1063,6 +1073,11 @@ fn test_writev() {
10631073
10641074/// Test that vectored writes without any buffers return zero.
10651075fn 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
10661081 let path = utils::prepare_with_content("pass-libc-writev-empty-bufs.txt", &[1u8, 2, 3]);
10671082 let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap();
10681083 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 @@
11//@ignore-target: windows # No libc socket on Windows
22//@compile-flags: -Zmiri-disable-isolation
3//@run-native
34
45#[path = "../../utils/libc.rs"]
56mod libc_utils;
src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs+1
......@@ -1,5 +1,6 @@
11//@only-target: linux android illumos
22//@compile-flags: -Zmiri-disable-isolation
3//@run-native
34
45#![feature(io_error_inprogress)]
56
src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking.rs+1
......@@ -3,6 +3,7 @@
33//@revisions: windows_host unix_host
44//@[unix_host] ignore-host: windows
55//@[windows_host] only-host: windows
6//@run-native
67
78#![feature(io_error_inprogress)]
89
src/tools/miri/tests/pass-dep/libc/libc-socket.rs+3-2
......@@ -1,5 +1,6 @@
11//@ignore-target: windows # No libc socket on Windows
22//@compile-flags: -Zmiri-disable-isolation
3//@run-native
34
45#[path = "../../utils/libc.rs"]
56mod libc_utils;
......@@ -176,8 +177,8 @@ fn test_set_nosigpipe_invalid_len() {
176177 let sockfd =
177178 unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() };
178179 // 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();
181182 assert_eq!(err.kind(), ErrorKind::InvalidInput);
182183 // Check that it is the right kind of `InvalidInput`.
183184 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
6fn 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 @@
11//@compile-flags: -Zmiri-disable-isolation
2//@run-native
23
34#![feature(io_error_more)]
45#![feature(io_error_uncategorized)]
......@@ -95,6 +96,11 @@ fn test_file() {
9596}
9697
9798fn test_file_partial_reads_writes() {
99 if !cfg!(miri) {
100 // This test is not expected to work natively.
101 return;
102 }
103
98104 let path1 = utils::prepare_with_content("miri_test_fs_file1.txt", b"abcdefg");
99105 let path2 = utils::prepare_with_content("miri_test_fs_file2.txt", b"abcdefg");
100106
src/tools/miri/tests/pass/shims/loongarch/intrinsics-loongarch64-crc.rs+24-1
......@@ -1,9 +1,20 @@
11// We're testing loongarch64-specific intrinsics
22//@only-target: loongarch64
3#![feature(stdarch_loongarch)]
3#![feature(abi_unadjusted, link_llvm_intrinsics, stdarch_loongarch)]
44
55use std::arch::loongarch64::*;
66
7unsafe 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
718fn main() {
819 test_crc_ieee();
920 test_crc_castagnoli();
......@@ -12,13 +23,19 @@ fn main() {
1223fn test_crc_ieee() {
1324 // crc.w.b.w: 8-bit input
1425 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
1527 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);
1629 assert_eq!(crc_w_b_w(0x2a, 0x2aa1e72b), 0x772d9171);
30 assert_eq!(unsafe { _crc_w_b_w(0x3_2a, 0x2aa1e72b) }, 0x772d9171);
1731
1832 // crc.w.h.w: 16-bit input
1933 assert_eq!(crc_w_h_w(0x0001, 0x00000000), 0x191b3141);
34 assert_eq!(unsafe { _crc_w_h_w(0x1_0001, 0x00000000) }, 0x191b3141);
2035 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);
2137 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);
2239
2340 // crc.w.w.w: 32-bit input
2441 assert_eq!(crc_w_w_w(0x00000001, 0x00000000), 0xb8bc6765_u32 as i32);
......@@ -34,13 +51,19 @@ fn test_crc_ieee() {
3451fn test_crc_castagnoli() {
3552 // crcc.w.b.w: 8-bit input
3653 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);
3755 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);
3857 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);
3959
4060 // crcc.w.h.w: 16-bit input
4161 assert_eq!(crcc_w_h_w(0x0001, 0x00000000), 0x13a29877);
62 assert_eq!(unsafe { _crcc_w_h_w(0x1_0001, 0x00000000) }, 0x13a29877);
4263 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);
4365 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);
4467
4568 // crcc.w.w.w: 32-bit input
4669 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 @@
11//@ignore-target: windows # No socket support on Windows
22//@compile-flags: -Zmiri-disable-isolation
3//@run-native
34
45use std::io::{ErrorKind, Read, Write};
56use std::net::{Shutdown, TcpListener, TcpStream};
......@@ -184,7 +185,7 @@ fn test_sockopt_read_timeout() {
184185 // By default, reads on blocking sockets should block indefinitely.
185186 assert_eq!(stream.read_timeout().unwrap(), None);
186187
187 let short_read_timeout = Some(Duration::from_millis(10));
188 let short_read_timeout = Some(Duration::from_millis(40));
188189 stream.set_read_timeout(short_read_timeout).unwrap();
189190 assert_eq!(stream.read_timeout().unwrap(), short_read_timeout);
190191
......@@ -207,7 +208,7 @@ fn test_sockopt_write_timeout() {
207208 // By default, writes on blocking sockets should block indefinitely.
208209 assert_eq!(stream.write_timeout().unwrap(), None);
209210
210 let short_write_timeout = Some(Duration::from_millis(10));
211 let short_write_timeout = Some(Duration::from_millis(40));
211212 stream.set_write_timeout(short_write_timeout).unwrap();
212213 assert_eq!(stream.write_timeout().unwrap(), short_write_timeout);
213214
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
6const PTR_BITS_MINUS_1: usize = std::mem::size_of::<*const ()>() * 8 - 1;
7
8fn 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
7struct BorrowedBuf<'a> {
8 buf: &'a mut [u8],
9}
10
11impl<'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
21fn 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 @@
1use std::env;
1#![allow(clippy::let_and_return)]
22use std::num::NonZero;
33use std::path::{Path, PathBuf};
44use std::process::Command;
55use std::sync::OnceLock;
6use std::{env, fmt};
67
78use colored::*;
89use regex::bytes::Regex;
910use ui_test::build_manager::BuildManager;
1011use ui_test::color_eyre::eyre::{Context, Result};
12use ui_test::custom_flags::Flag;
1113use ui_test::custom_flags::edition::Edition;
1214use ui_test::dependencies::DependencyBuilder;
1315use ui_test::per_test_config::TestConfig;
......@@ -17,14 +19,37 @@ use ui_test::{CommandBuilder, Config, Match, ignore_output_conflict};
1719
1820#[derive(Copy, Clone, Debug)]
1921enum Mode {
20 Pass,
22 Pass {
23 native: bool,
24 },
2125 /// Requires annotations
2226 Fail,
2327 /// Not used for tests, but for `miri run --dep`
24 RunDep,
28 RunDep {
29 native: bool,
30 },
31 /// Test must panic.
2532 Panic,
2633}
2734
35impl Mode {
36 fn native(self) -> bool {
37 matches!(self, Mode::Pass { native: true } | Mode::RunDep { native: true })
38 }
39}
40
41impl 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
2853fn miri_path() -> PathBuf {
2954 env!("CARGO_BIN_EXE_miri").into()
3055}
......@@ -79,7 +104,7 @@ struct WithDependencies {
79104 bless: bool,
80105}
81106
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
83108/// run_dep_mode.
84109fn miri_config(
85110 target: &str,
......@@ -90,6 +115,15 @@ fn miri_config(
90115 // Miri is rustc-like, so we create a default builder for rustc and modify it
91116 let mut program = CommandBuilder::rustc();
92117 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 }
93127
94128 let mut config = Config {
95129 target: Some(target.to_owned()),
......@@ -103,10 +137,17 @@ fn miri_config(
103137 ..Config::rustc(path)
104138 };
105139
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.
106147 config.comment_defaults.base().exit_status = match mode {
107 Mode::Pass => Some(0),
148 Mode::Pass { .. } => Some(0),
108149 Mode::Fail => Some(1),
109 Mode::RunDep => None,
150 Mode::RunDep { .. } => None,
110151 Mode::Panic => Some(101),
111152 }
112153 .map(Spanned::dummy)
......@@ -123,9 +164,12 @@ fn miri_config(
123164 // keep in sync with `./miri run`
124165 config.comment_defaults.base().add_custom("edition", Edition("2021".into()));
125166
167 // Building dependencies is also a "comment default".
126168 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 {
129173 DependencyBuilder {
130174 program: CommandBuilder {
131175 // 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(
147191 ],
148192 ..CommandBuilder::cargo()
149193 },
150 crate_manifest_path: Path::new("tests/deps").join("Cargo.toml"),
194 crate_manifest_path,
151195 build_std: None,
152196 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 }
155251 }
252
156253 config
157254}
158255
......@@ -177,6 +274,9 @@ fn run_tests(
177274 assert!(!args.bless, "cannot use RUSTC_BLESS and MIRI_SKIP_UI_CHECKS at the same time");
178275 config.output_conflict_handling = ignore_output_conflict;
179276 }
277 if mode.native() {
278 config.output_conflict_handling = ignore_output_conflict;
279 }
180280
181281 // Add a test env var to do environment communication tests.
182282 config.program.envs.push(("MIRI_ENV_VAR_TEST".into(), Some("0".into())));
......@@ -185,23 +285,26 @@ fn run_tests(
185285 // If a test ICEs, we want to see a backtrace.
186286 config.program.envs.push(("RUST_BACKTRACE".into(), Some("1".into())));
187287
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.
196289 config.program.args.push("-Dwarnings".into());
197290 config.program.args.push("-Dunused".into());
198291 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 }
202306 }
203307 }
204 config.program.args.push("-Zui-testing".into());
205308
206309 // If we're testing the native-lib functionality, then build the shared object file for testing
207310 // external C function calls and push the relevant compiler flag.
......@@ -288,8 +391,8 @@ regexes! {
288391}
289392
290393enum Dependencies {
291 WithDependencies,
292 WithoutDependencies,
394 WithDeps,
395 WithoutDeps,
293396}
294397
295398use Dependencies::*;
......@@ -301,12 +404,12 @@ fn ui(
301404 with_dependencies: Dependencies,
302405 tmpdir: &Path,
303406) -> Result<()> {
304 let msg = format!("## Running ui tests in {path} for {target}");
407 let msg = format!("## Running {mode} ui tests in {path} for {target}");
305408 println!("{}", msg.green().bold());
306409
307410 let with_dependencies = match with_dependencies {
308 WithDependencies => true,
309 WithoutDependencies => false,
411 WithDeps => true,
412 WithoutDeps => false,
310413 };
311414 run_tests(mode, path, target, with_dependencies, tmpdir)
312415 .with_context(|| format!("ui tests in {path} for {target} failed"))
......@@ -331,17 +434,27 @@ fn main() -> Result<()> {
331434
332435 // Check whether this is a `./miri run` invocation
333436 if let Ok(mode) = env::var("MIRI_RUN_MODE") {
334 return run_mode(target, mode);
437 return run_with_deps(target, mode);
335438 }
336439
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())?;
342449 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())?;
345458 }
346459
347460 // 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<()> {
354467 target_endian = "little"
355468 )) && host == target
356469 {
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())?;
359472 }
360473
361474 Ok(())
362475}
363476
364fn run_mode(target: String, mode: String) -> Result<()> {
477fn run_with_deps(target: String, mode: String) -> Result<()> {
365478 let native = mode == "native";
366479
367480 let mut config =
368 miri_config(&target, "", Mode::RunDep, Some(WithDependencies { bless: false }));
481 miri_config(&target, "", Mode::RunDep { native }, Some(WithDependencies { bless: false }));
369482 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 }
381483 config.fill_host_and_target()?;
382484 // Reset `args` (otherwise we'll get JSON output).
383485 config.program.args = vec![];
......@@ -385,7 +487,8 @@ fn run_mode(target: String, mode: String) -> Result<()> {
385487 // Compute the actual Miri invocation command.
386488 let test_config = TestConfig::one_off_runner(config.clone(), PathBuf::new());
387489 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.
389492 cmd.arg("--target").arg(&target);
390493 // Also forward arguments to the program (skipping the binary name).
391494 // 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<()> {
407510
408511 if native {
409512 // 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.
412515
413516 // First, figure out the output binary by re-running the compiler with `--print`.
414517 cmd.arg("--print").arg("file-names");
src/tools/miri/triagebot.toml-6
......@@ -17,12 +17,6 @@ allow-unauthenticated = [
1717[assign]
1818warn_non_default_branch = true
1919contributing_url = "https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#pr-review-process"
20[assign.custom_welcome_messages]
21welcome-message = "(unused)"
22welcome-message-no-reviewer = """
23Thank you for contributing to Miri! A reviewer will take a look at your PR, typically within a week or two.
24Please 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"""
2620
2721[no-merges]
2822exclude_titles = ["Rustup"]
tests/assembly-llvm/targets/targets-elf.rs+3
......@@ -394,6 +394,9 @@
394394//@ revisions: powerpc64_unknown_linux_gnu
395395//@ [powerpc64_unknown_linux_gnu] compile-flags: --target powerpc64-unknown-linux-gnu
396396//@ [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
397400//@ revisions: powerpc64_unknown_linux_musl
398401//@ [powerpc64_unknown_linux_musl] compile-flags: --target powerpc64-unknown-linux-musl
399402//@ [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
18pub 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"
39pub 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]+]]
52pub 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]
63pub 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
5use std::marker::{CoerceShared, Reborrow};
6
7// This test combines alias/projection normalization with the leaf `&mut T` to `&T`
8// shared reborrow path.
9
10struct InnerMut<'a, T> {
11 value: &'a mut T,
12}
13
14impl<'a, T> Reborrow for InnerMut<'a, T> {}
15
16struct InnerRef<'a, T> {
17 value: &'a T,
18}
19
20impl<'a, T> Clone for InnerRef<'a, T> {
21 fn clone(&self) -> Self {
22 *self
23 }
24}
25
26impl<'a, T> Copy for InnerRef<'a, T> {}
27
28impl<'a, T> CoerceShared<InnerRef<'a, T>> for InnerMut<'a, T> {}
29
30type DirectInnerRef<'a, T> = InnerRef<'a, T>;
31
32trait RefFamily<'a, T> {
33 type Ref;
34}
35
36struct Projected;
37
38impl<'a, T: 'a> RefFamily<'a, T> for Projected {
39 type Ref = InnerRef<'a, T>;
40}
41
42type ProjectedInnerRef<'a, T> = <Projected as RefFamily<'a, T>>::Ref;
43
44struct OuterMut<'a, T> {
45 inner: InnerMut<'a, T>,
46 tag: usize,
47}
48
49impl<'a, T> Reborrow for OuterMut<'a, T> {}
50
51struct OuterAliasRef<'a, T> {
52 inner: DirectInnerRef<'a, T>,
53 tag: usize,
54}
55
56impl<'a, T> Clone for OuterAliasRef<'a, T> {
57 fn clone(&self) -> Self {
58 *self
59 }
60}
61
62impl<'a, T> Copy for OuterAliasRef<'a, T> {}
63
64impl<'a, T> CoerceShared<OuterAliasRef<'a, T>> for OuterMut<'a, T> {}
65
66struct OuterProjectionRef<'a, T: 'a> {
67 inner: ProjectedInnerRef<'a, T>,
68 tag: usize,
69}
70
71impl<'a, T: 'a> Clone for OuterProjectionRef<'a, T> {
72 fn clone(&self) -> Self {
73 *self
74 }
75}
76
77impl<'a, T: 'a> Copy for OuterProjectionRef<'a, T> {}
78
79impl<'a, T: 'a> CoerceShared<OuterProjectionRef<'a, T>> for OuterMut<'a, T> {}
80
81fn read_alias<'a>(outer: OuterAliasRef<'a, u32>) -> (&'a u32, usize) {
82 (outer.inner.value, outer.tag)
83}
84
85fn read_projection<'a>(outer: OuterProjectionRef<'a, u32>) -> (&'a u32, usize) {
86 (outer.inner.value, outer.tag)
87}
88
89const fn const_accept_projection(_outer: OuterProjectionRef<'_, u32>) {}
90
91const 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
99fn 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
5use std::marker::{CoerceShared, PhantomData, Reborrow};
6use std::ptr::NonNull;
7
8struct ImbrisMut<'a, T> {
9 ptr: NonNull<T>,
10 metadata: usize,
11 marker: PhantomData<&'a mut T>,
12}
13
14impl<'a, T> Reborrow for ImbrisMut<'a, T> {}
15
16struct ImbrisRef<'a, T> {
17 ptr: NonNull<T>,
18 marker: PhantomData<&'a T>,
19}
20
21impl<'a, T> Clone for ImbrisRef<'a, T> {
22 fn clone(&self) -> Self {
23 *self
24 }
25}
26
27impl<'a, T> Copy for ImbrisRef<'a, T> {}
28
29impl<'a, T> CoerceShared<ImbrisRef<'a, T>> for ImbrisMut<'a, T> {}
30
31fn ptr(value: ImbrisRef<'_, i32>) -> NonNull<i32> {
32 value.ptr
33}
34
35fn 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
6use std::marker::{CoerceShared, PhantomData, Reborrow};
7
8struct CustomMarker<'a>(PhantomData<&'a ()>);
9struct CustomMarkerRef;
10
11impl<'a> Reborrow for CustomMarker<'a> {}
12impl<'a> CoerceShared<CustomMarkerRef> for CustomMarker<'a> {}
13//~^ ERROR
14
15fn method(_a: CustomMarkerRef) {}
16
17fn 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
5use std::marker::{CoerceShared, PhantomData, Reborrow};
6use std::ptr::NonNull;
7
8struct 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
17impl<'a, T> Reborrow for MatMut<'a, T> {}
18
19struct 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
28impl<'a, T> Clone for MatRef<'a, T> {
29 fn clone(&self) -> Self {
30 *self
31 }
32}
33
34impl<'a, T> Copy for MatRef<'a, T> {}
35
36impl<'a, T> CoerceShared<MatRef<'a, T>> for MatMut<'a, T> {}
37
38fn 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
43fn 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
5use std::marker::{CoerceShared, Reborrow};
6
7struct InnerMut<'a, T> {
8 value: &'a mut T,
9}
10
11impl<'a, T> Reborrow for InnerMut<'a, T> {}
12
13struct InnerRef<'a, T> {
14 value: &'a T,
15}
16
17impl<'a, T> Clone for InnerRef<'a, T> {
18 fn clone(&self) -> Self {
19 *self
20 }
21}
22
23impl<'a, T> Copy for InnerRef<'a, T> {}
24
25impl<'a, T> CoerceShared<InnerRef<'a, T>> for InnerMut<'a, T> {}
26
27struct OuterMut<'a, T> {
28 inner: InnerMut<'a, T>,
29 tag: usize,
30}
31
32impl<'a, T> Reborrow for OuterMut<'a, T> {}
33
34struct OuterRef<'a, T> {
35 inner: InnerRef<'a, T>,
36 tag: usize,
37}
38
39impl<'a, T> Clone for OuterRef<'a, T> {
40 fn clone(&self) -> Self {
41 *self
42 }
43}
44
45impl<'a, T> Copy for OuterRef<'a, T> {}
46
47impl<'a, T> CoerceShared<OuterRef<'a, T>> for OuterMut<'a, T> {}
48
49fn get<'a>(outer: OuterRef<'a, i32>) -> (&'a i32, usize) {
50 (outer.inner.value, outer.tag)
51}
52
53fn 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
5use std::marker::{CoerceShared, PhantomData, Reborrow};
6
7struct SourceLeadingMut<'a, T>(PhantomData<&'a mut T>, &'a mut T);
8
9impl<'a, T> Reborrow for SourceLeadingMut<'a, T> {}
10
11struct SourceLeadingRef<'a, T>(&'a T);
12
13impl<'a, T> Clone for SourceLeadingRef<'a, T> {
14 fn clone(&self) -> Self {
15 *self
16 }
17}
18
19impl<'a, T> Copy for SourceLeadingRef<'a, T> {}
20
21impl<'a, T> CoerceShared<SourceLeadingRef<'a, T>> for SourceLeadingMut<'a, T> {}
22
23struct TargetLeadingMut<'a, T>(&'a mut T);
24
25impl<'a, T> Reborrow for TargetLeadingMut<'a, T> {}
26
27struct TargetLeadingRef<'a, T>(PhantomData<&'a T>, &'a T);
28
29impl<'a, T> Clone for TargetLeadingRef<'a, T> {
30 fn clone(&self) -> Self {
31 *self
32 }
33}
34
35impl<'a, T> Copy for TargetLeadingRef<'a, T> {}
36
37impl<'a, T> CoerceShared<TargetLeadingRef<'a, T>> for TargetLeadingMut<'a, T> {}
38
39struct InterleavedMut<'a, T, U>(
40 PhantomData<&'a mut T>,
41 &'a mut T,
42 PhantomData<&'a mut U>,
43 &'a mut U,
44);
45
46impl<'a, T, U> Reborrow for InterleavedMut<'a, T, U> {}
47
48struct InterleavedRef<'a, T, U>(&'a T, PhantomData<&'a T>, &'a U, PhantomData<&'a U>);
49
50impl<'a, T, U> Clone for InterleavedRef<'a, T, U> {
51 fn clone(&self) -> Self {
52 *self
53 }
54}
55
56impl<'a, T, U> Copy for InterleavedRef<'a, T, U> {}
57
58impl<'a, T, U> CoerceShared<InterleavedRef<'a, T, U>> for InterleavedMut<'a, T, U> {}
59
60fn read_source_leading<'a>(value: SourceLeadingRef<'a, i32>) -> &'a i32 {
61 value.0
62}
63
64fn read_target_leading<'a>(value: TargetLeadingRef<'a, i32>) -> &'a i32 {
65 value.1
66}
67
68fn read_interleaved<'a>(value: InterleavedRef<'a, i32, i64>) -> (&'a i32, &'a i64) {
69 (value.0, value.2)
70}
71
72fn 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
5use std::marker::{CoerceShared, Reborrow};
6
7struct CustomMut<'a, T>(&'a mut T);
8
9impl<'a, T> Reborrow for CustomMut<'a, T> {}
10
11struct CustomRef<'a, T>(&'a CustomMut<'a, T>);
12//~^ ERROR
13
14impl<'a, T> CoerceShared<CustomRef<'a, T>> for CustomMut<'a, T> {}
15
16fn method(_a: CustomRef<'_, ()>) {}
17
18fn 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
9use std::marker::{CoerceShared, PhantomData, Reborrow};
10
11struct CustomMarker<'a>(PhantomData<&'a ()>);
12
13struct CustomMarkerRef<'a>(PhantomData<(Debug, Clone, Copy)>);
14//~^ ERROR
15//~| ERROR
16//~| ERROR
17
18impl<'a> Reborrow for CustomMarker<'a> {}
19impl<'a> CoerceShared<CustomMarkerRef<'a>> for CustomMarker<'a> {}
20//~^ ERROR
21
22fn method<'a>(_a: CustomMarkerRef<'a>) -> 'a () {
23 //~^ ERROR
24 &()
25}
26
27fn 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
7use std::marker::{CoerceShared, Reborrow};
8
9struct CustomMut<'a, T>(&'a mut T);
10
11impl<'a, T> Reborrow for CustomMut<'a, T> {}
12impl<'a, T> CoerceShared<CustomRef<'a, T>> for CustomMut<'a, T> {}
13
14struct CustomRef<'a, T>(&'a CustomMut);
15//~^ ERROR
16//~| ERROR
17//~| ERROR
18
19fn method(_a: CustomRef<'_, ()>) {}
20
21fn 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
22// LLDB 1800+ tests were not tested in CI, broke, and now are disabled
33//@ ignore-lldb
44
......@@ -23,6 +23,12 @@
2323//@ gdb-command:print str_in_rc
2424//@ 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}
2525
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
2632// === LLDB TESTS ==================================================================================
2733//@ lldb-command:run
2834//@ lldb-command:v plain_string
......@@ -40,6 +46,12 @@
4046//@ lldb-command:v str_in_rc
4147//@ 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' } }
4248
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
4355#![allow(unused_variables)]
4456
4557pub struct Foo<'a> {
......@@ -53,6 +65,8 @@ fn main() {
5365 let str_in_tuple = ("Hello", "World");
5466
5567 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();
5670 zzz(); // #break
5771}
5872
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
15trait Trait { type Assoc<T>; }
16
17fn foo<T: Trait, U>(_: U) -> <T as Trait>::Assoc::<U> { loop {} }
18
19fn mk<'a>() -> for<'b> fn(&'b ()) -> &'a () { loop {} }
20
21fn 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
9fn foo<T>(_: T) -> impl Sized + use<T> {}
10
11fn mk<'a>() -> for<'b> fn(&'b ()) -> &'a () { loop {} }
12
13fn 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
10trait Trait { }
11
12fn req_trait<T: Trait>(_: T) { }
13
14fn mk() -> for<'b> fn(&'b ()) { loop {} }
15
16fn ice()
17where
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
7use core::marker::PhantomPinned;
8use core::pin::Pin;
9
10type MyFut = impl Future<Output = ()>;
11
12async fn my_async_fn() {}
13
14#[define_opaque(MyFut)]
15fn fut() -> MyFut {
16 my_async_fn()
17}
18
19impl Unpin for MyFut {}
20//~^ ERROR: only traits defined in the current crate can be implemented for arbitrary types
21
22fn main() {}
tests/ui/impl-trait/unpin-for-future.stderr created+15
......@@ -0,0 +1,15 @@
1error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
2 --> $DIR/unpin-for-future.rs:19:1
3 |
4LL | 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
13error: aborting due to 1 previous error
14
15For 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;
55struct ArenaSet<U: Deref, V: ?Sized = <U as Deref>::Target>(V, U);
66//~^ ERROR the size for values of type `V` cannot be known at compilation time
77
8const DATA: *const ArenaSet<Vec<u8>> = std::ptr::null_mut();
8const DATA: &ArenaSet<Vec<u8>> = unsafe { &* std::ptr::null_mut() };
99//~^ ERROR the type `ArenaSet<Vec<u8>, [u8]>` has an unknown layout
1010
1111pub 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);
2323 | ++++ +
2424
2525error[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
2727 |
28LL | const DATA: *const ArenaSet<Vec<u8>> = std::ptr::null_mut();
29 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `DATA` failed here
28LL | const DATA: &ArenaSet<Vec<u8>> = unsafe { &* std::ptr::null_mut() };
29 | ^^^^^^^^^^^^^^^^^^^^^^^ evaluation of `DATA` failed here
3030
3131error: aborting due to 2 previous errors
3232
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
3fn takes_raw_ptr(_: *const u32) {}
4fn takes_raw_ptr_args(_: *const u32, _: *const u32) {}
5
6fn 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 @@
1error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `const`, `mut`, `{`, or an operator, found `x`
2 --> $DIR/raw-no-const-mut-arg-list.rs:8:24
3 |
4LL | takes_raw_ptr(&raw x);
5 | ^ expected one of 10 possible tokens
6 |
7help: `&raw` must be followed by `const` or `mut` to be a raw reference expression
8 |
9LL | takes_raw_ptr(&raw const x);
10 | +++++
11LL | takes_raw_ptr(&raw mut x);
12 | +++
13
14error: expected one of `!`, `)`, `,`, `.`, `::`, `?`, `const`, `mut`, `{`, or an operator, found `x`
15 --> $DIR/raw-no-const-mut-arg-list.rs:10:29
16 |
17LL | takes_raw_ptr_args(&raw x, &raw x);
18 | ^ expected one of 10 possible tokens
19 |
20help: `&raw` must be followed by `const` or `mut` to be a raw reference expression
21 |
22LL | takes_raw_ptr_args(&raw const x, &raw x);
23 | +++++
24LL | takes_raw_ptr_args(&raw mut x, &raw x);
25 | +++
26
27error: aborting due to 2 previous errors
28
tests/ui/parser/recover/raw-no-const-mut.rs-3
......@@ -6,8 +6,6 @@ fn a() {
66fn b() {
77 [&raw const 1, &raw 2]
88 //~^ ERROR expected one of
9 //~| ERROR cannot find value `raw` in this scope
10 //~| ERROR cannot take address of a temporary
119}
1210
1311fn c() {
......@@ -18,7 +16,6 @@ fn c() {
1816fn d() {
1917 f(&raw 2);
2018 //~^ ERROR expected one of
21 //~| ERROR cannot find value `raw` in this scope
2219 //~| ERROR cannot find function `f` in this scope
2320}
2421
tests/ui/parser/recover/raw-no-const-mut.stderr+7-34
......@@ -23,19 +23,15 @@ LL | [&raw const 1, &raw const 2]
2323 | +++++
2424LL | [&raw const 1, &raw mut 2]
2525 | +++
26help: missing `,`
27 |
28LL | [&raw const 1, &raw, 2]
29 | +
3026
3127error: expected `{`, found `z`
32 --> $DIR/raw-no-const-mut.rs:14:18
28 --> $DIR/raw-no-const-mut.rs:12:18
3329 |
3430LL | if x == &raw z {}
3531 | ^ expected `{`
3632 |
3733note: 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
3935 |
4036LL | if x == &raw z {}
4137 | ^^^^^^^^^
......@@ -47,7 +43,7 @@ LL | if x == &raw mut z {}
4743 | +++
4844
4945error: 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
5147 |
5248LL | f(&raw 2);
5349 | ^ expected one of 10 possible tokens
......@@ -58,13 +54,9 @@ LL | f(&raw const 2);
5854 | +++++
5955LL | f(&raw mut 2);
6056 | +++
61help: missing `,`
62 |
63LL | f(&raw, 2);
64 | +
6557
6658error: 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
6860 |
6961LL | x = &raw 1;
7062 | ^ expected one of 10 possible tokens
......@@ -76,26 +68,8 @@ LL | x = &raw const 1;
7668LL | x = &raw mut 1;
7769 | +++
7870
79error[E0425]: cannot find value `raw` in this scope
80 --> $DIR/raw-no-const-mut.rs:7:21
81 |
82LL | [&raw const 1, &raw 2]
83 | ^^^ not found in this scope
84
85error[E0425]: cannot find value `raw` in this scope
86 --> $DIR/raw-no-const-mut.rs:19:8
87 |
88LL | f(&raw 2);
89 | ^^^ not found in this scope
90
91error[E0745]: cannot take address of a temporary
92 --> $DIR/raw-no-const-mut.rs:7:17
93 |
94LL | [&raw const 1, &raw 2]
95 | ^ temporary value
96
9771error[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
9973 |
10074LL | fn a() {
10175 | ------ similarly named function `a` defined here
......@@ -109,7 +83,6 @@ LL - f(&raw 2);
10983LL + a(&raw 2);
11084 |
11185
112error: aborting due to 9 previous errors
86error: aborting due to 6 previous errors
11387
114Some errors have detailed explanations: E0425, E0745.
115For more information about an error, try `rustc --explain E0425`.
88For 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.
3pub 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
12extern crate non_pin_project;
13
14use non_pin_project::Foreign;
15use std::pin::Pin;
16
17fn 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
22fn main() {}
tests/ui/pin-ergonomics/pin-pattern-foreign-non-pin-project.stderr created+8
......@@ -0,0 +1,8 @@
1error: cannot project on type that is not `#[pin_v2]`
2 --> $DIR/pin-pattern-foreign-non-pin-project.rs:18:18
3 |
4LL | let &pin mut Foreign(ref pin mut _x) = p;
5 | ^^^^^^^^^^^^^^^^^^^^^^^
6
7error: 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
13use std::pin::Pin;
14
15struct NotPinProject<T>(T);
16
17struct NotPinProjectStruct<T> {
18 x: T,
19}
20
21enum NotPinProjectEnum<T> {
22 Tuple(T),
23 Struct { x: T },
24}
25
26fn 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
31fn 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
36fn 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
41fn 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
46fn 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.
53struct Thing<T>(T);
54impl<T> Unpin for Thing<T> {}
55
56fn 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
61fn main() {}
tests/ui/pin-ergonomics/pin-pattern-non-pin-project.stderr created+104
......@@ -0,0 +1,104 @@
1error: cannot project on type that is not `#[pin_v2]`
2 --> $DIR/pin-pattern-non-pin-project.rs:27:18
3 |
4LL | let &pin mut NotPinProject(ref pin mut _x) = p;
5 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6 |
7note: type defined here
8 --> $DIR/pin-pattern-non-pin-project.rs:15:1
9 |
10LL | struct NotPinProject<T>(T);
11 | ^^^^^^^^^^^^^^^^^^^^^^^
12help: add `#[pin_v2]` here
13 |
14LL + #[pin_v2]
15LL | struct NotPinProject<T>(T);
16 |
17
18error: cannot project on type that is not `#[pin_v2]`
19 --> $DIR/pin-pattern-non-pin-project.rs:32:18
20 |
21LL | let &pin mut NotPinProjectStruct { x: ref pin mut _x } = p;
22 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
23 |
24note: type defined here
25 --> $DIR/pin-pattern-non-pin-project.rs:17:1
26 |
27LL | struct NotPinProjectStruct<T> {
28 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
29help: add `#[pin_v2]` here
30 |
31LL + #[pin_v2]
32LL | struct NotPinProjectStruct<T> {
33 |
34
35error: cannot project on type that is not `#[pin_v2]`
36 --> $DIR/pin-pattern-non-pin-project.rs:37:20
37 |
38LL | let &pin const NotPinProject(ref pin const _x) = p;
39 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
40 |
41note: type defined here
42 --> $DIR/pin-pattern-non-pin-project.rs:15:1
43 |
44LL | struct NotPinProject<T>(T);
45 | ^^^^^^^^^^^^^^^^^^^^^^^
46help: add `#[pin_v2]` here
47 |
48LL + #[pin_v2]
49LL | struct NotPinProject<T>(T);
50 |
51
52error: cannot project on type that is not `#[pin_v2]`
53 --> $DIR/pin-pattern-non-pin-project.rs:42:21
54 |
55LL | if let &pin mut NotPinProjectEnum::Tuple(ref pin mut _x) = p {}
56 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
57 |
58note: type defined here
59 --> $DIR/pin-pattern-non-pin-project.rs:21:1
60 |
61LL | enum NotPinProjectEnum<T> {
62 | ^^^^^^^^^^^^^^^^^^^^^^^^^
63help: add `#[pin_v2]` here
64 |
65LL + #[pin_v2]
66LL | enum NotPinProjectEnum<T> {
67 |
68
69error: cannot project on type that is not `#[pin_v2]`
70 --> $DIR/pin-pattern-non-pin-project.rs:47:21
71 |
72LL | if let &pin mut NotPinProjectEnum::Struct { x: ref pin mut _x } = p {}
73 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
74 |
75note: type defined here
76 --> $DIR/pin-pattern-non-pin-project.rs:21:1
77 |
78LL | enum NotPinProjectEnum<T> {
79 | ^^^^^^^^^^^^^^^^^^^^^^^^^
80help: add `#[pin_v2]` here
81 |
82LL + #[pin_v2]
83LL | enum NotPinProjectEnum<T> {
84 |
85
86error: cannot project on type that is not `#[pin_v2]`
87 --> $DIR/pin-pattern-non-pin-project.rs:57:18
88 |
89LL | let &pin mut Thing(ref pin mut _pinned_option) = pinned_thing;
90 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
91 |
92note: type defined here
93 --> $DIR/pin-pattern-non-pin-project.rs:53:1
94 |
95LL | struct Thing<T>(T);
96 | ^^^^^^^^^^^^^^^
97help: add `#[pin_v2]` here
98 |
99LL + #[pin_v2]
100LL | struct Thing<T>(T);
101 |
102
103error: aborting due to 6 previous errors
104
tests/ui/pin-ergonomics/user-type-projection.rs+1
......@@ -9,6 +9,7 @@
99// Historically, this could occur when the code handling those projections did not know
1010// about `&pin` patterns, and incorrectly treated them as plain `&`/`&mut` patterns instead.
1111
12#[pin_v2]
1213struct Data {
1314 x: u32
1415}
tests/ui/reborrow/auxiliary/reborrow_foreign_private.rs created+6
......@@ -0,0 +1,6 @@
1#![allow(dead_code)]
2
3#[derive(Clone, Copy)]
4pub 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
4use std::marker::{CoerceShared, Reborrow};
5
6trait Trait {
7 type Assoc;
8}
9
10impl Trait for i32 {
11 type Assoc = i64;
12}
13
14struct MyMut<'a> {
15 x: &'a (),
16 y: i64,
17}
18
19#[derive(Copy, Clone)]
20struct MyRef<'a> {
21 x: &'a (),
22 y: <i32 as Trait>::Assoc,
23}
24
25impl Reborrow for MyMut<'_> {}
26
27impl<'a> CoerceShared<MyRef<'a>> for MyMut<'a> {}
28//~^ ERROR
29
30fn main() {}
tests/ui/reborrow/coerce-shared-associated-type-field.stderr created+8
......@@ -0,0 +1,8 @@
1error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced
2 --> $DIR/coerce-shared-associated-type-field.rs:27:10
3 |
4LL | impl<'a> CoerceShared<MyRef<'a>> for MyMut<'a> {}
5 | ^^^^^^^^^^^^^^^^^^^^^^^
6
7error: 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
4use std::marker::{CoerceShared, Reborrow};
5
6macro 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
24my_macro!(field);
25
26fn main() {}
tests/ui/reborrow/coerce-shared-decl-macro-hygiene.stderr created+13
......@@ -0,0 +1,13 @@
1error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced
2 --> $DIR/coerce-shared-decl-macro-hygiene.rs:20:14
3 |
4LL | impl<'a> CoerceShared<MyRef<'a>> for MyMut<'a> {}
5 | ^^^^^^^^^^^^^^^^^^^^^^^
6...
7LL | 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
12error: 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
6use std::marker::{CoerceShared, PhantomData, Reborrow};
7
8struct MarkerExtraMut<'a, T> {
9 value: &'a mut T,
10 marker: PhantomData<&'a mut T>,
11}
12
13impl<'a, T> Reborrow for MarkerExtraMut<'a, T> {}
14
15struct MarkerExtraRef<'a, T> {
16 value: &'a T,
17}
18
19impl<'a, T> Clone for MarkerExtraRef<'a, T> {
20 fn clone(&self) -> Self {
21 *self
22 }
23}
24
25impl<'a, T> Copy for MarkerExtraRef<'a, T> {}
26
27impl<'a, T> CoerceShared<MarkerExtraRef<'a, T>> for MarkerExtraMut<'a, T> {}
28
29fn get<'a>(value: MarkerExtraRef<'a, i32>) -> &'a i32 {
30 value.value
31}
32
33fn 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
3use std::marker::{CoerceShared, Reborrow};
4
5struct MyMut<'a> {
6 x: &'static (),
7 y: &'a (),
8}
9
10impl Reborrow for MyMut<'_> {}
11
12#[derive(Copy, Clone)]
13struct MyRef<'a> {
14 x: &'a (),
15 y: &'static (),
16}
17
18impl<'a> CoerceShared<MyRef<'a>> for MyMut<'a> {}
19//~^ ERROR
20
21fn main() {}
tests/ui/reborrow/coerce-shared-field-lifetime-swap.stderr created+8
......@@ -0,0 +1,8 @@
1error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced
2 --> $DIR/coerce-shared-field-lifetime-swap.rs:18:10
3 |
4LL | impl<'a> CoerceShared<MyRef<'a>> for MyMut<'a> {}
5 | ^^^^^^^^^^^^^^^^^^^^^^^
6
7error: 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
3use std::marker::{CoerceShared, Reborrow};
4
5struct CustomMut<'a, T> {
6 value: &'a mut T,
7}
8
9impl<'a, T> Reborrow for CustomMut<'a, T> {}
10
11#[derive(Clone, Copy)]
12struct CustomRef<'a, T> {
13 value: &'a T,
14}
15
16impl<'a, T> CoerceShared<CustomRef<'a, T>> for CustomMut<'a, T> {}
17
18struct RenamedMut<'a, T> {
19 source: &'a mut T,
20}
21
22impl<'a, T> Reborrow for RenamedMut<'a, T> {}
23
24#[derive(Clone, Copy)]
25struct RenamedRef<'a, T> {
26 target: &'a T,
27}
28
29impl<'a, T> CoerceShared<RenamedRef<'a, T>> for RenamedMut<'a, T> {}
30
31struct BadMut<'a, T> {
32 value: &'a mut T,
33}
34
35impl<'a, T> Reborrow for BadMut<'a, T> {}
36
37#[derive(Clone, Copy)]
38struct BadRef<'a, T> {
39 value: &'a u32,
40 _marker: std::marker::PhantomData<T>,
41}
42
43impl<'a, T> CoerceShared<BadRef<'a, T>> for BadMut<'a, T> {}
44//~^ ERROR
45
46fn good(_value: CustomRef<'_, u32>) {}
47
48fn 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 @@
1error[E0277]: the trait bound `&'a mut T: CoerceShared<&'a u32>` is not satisfied
2 --> $DIR/coerce-shared-field-relations.rs:43:1
3 |
4LL | 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
7error: aborting due to 1 previous error
8
9For 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
7extern crate reborrow_foreign_private;
8
9use reborrow_foreign_private::ForeignRef;
10use std::marker::{CoerceShared, Reborrow};
11
12struct LocalMut<'a> {
13 value: &'a mut i32,
14}
15
16impl<'a> Reborrow for LocalMut<'a> {}
17
18impl<'a> CoerceShared<ForeignRef<'a>> for LocalMut<'a> {}
19
20fn 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
5use std::marker::{CoerceShared, PhantomData, Reborrow};
6
7mod foreign_ptr {
8 use std::marker::PhantomData;
9
10 #[derive(Clone, Copy)]
11 pub struct ForeignPtrRef<'a>(*const i32, PhantomData<&'a ()>);
12}
13
14use foreign_ptr::ForeignPtrRef;
15
16struct LocalPtrMut<'a>(*const i32, PhantomData<&'a ()>);
17
18impl<'a> Reborrow for LocalPtrMut<'a> {}
19
20impl<'a> CoerceShared<ForeignPtrRef<'a>> for LocalPtrMut<'a> {}
21
22fn main() {}
tests/ui/reborrow/coerce-shared-generics.rs created+41
......@@ -0,0 +1,41 @@
1#![feature(reborrow)]
2#![allow(dead_code)]
3
4use std::marker::{CoerceShared, Reborrow};
5
6struct BufferMut<'a, T, U, const N: usize> {
7 data: &'a mut [T; N],
8 meta: U,
9}
10
11impl<'a, T, U: Copy, const N: usize> Reborrow for BufferMut<'a, T, U, N> {}
12
13struct BufferRef<'a, T, U, const N: usize> {
14 data: &'a [T; N],
15 meta: U,
16}
17
18impl<'a, T, U: Copy, const N: usize> Clone for BufferRef<'a, T, U, N> {
19 fn clone(&self) -> Self {
20 *self
21 }
22}
23
24impl<'a, T, U: Copy, const N: usize> Copy for BufferRef<'a, T, U, N> {}
25
26impl<'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
32fn inspect<const N: usize>(buffer: BufferRef<'_, u8, u16, N>) -> (usize, u16) {
33 (buffer.data.len(), buffer.meta)
34}
35
36fn 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 @@
1error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced
2 --> $DIR/coerce-shared-generics.rs:26:38
3 |
4LL | impl<'a, T, U: Copy, const N: usize> CoerceShared<BufferRef<'a, T, U, N>>
5 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6
7error: 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
6use std::marker::{CoerceShared, PhantomData, Reborrow};
7
8struct CustomMarker<'a>(PhantomData<&'a ()>);
9
10impl<'a> Reborrow for CustomMarker<'a> {}
11
12#[derive(Clone, Copy)]
13struct StaticMarkerRef<'a>(PhantomData<&'a ()>);
14
15impl<'a> CoerceShared<StaticMarkerRef<'static>> for CustomMarker<'a> {}
16
17fn method(_a: StaticMarkerRef<'static>) {}
18
19fn 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 @@
1error[E0597]: `a` does not live long enough
2 --> $DIR/coerce-shared-lifetime-mismatch.rs:21:12
3 |
4LL | let a = CustomMarker(PhantomData);
5 | - binding `a` declared here
6LL | method(a);
7 | -------^-
8 | | |
9 | | borrowed value does not live long enough
10 | argument requires that `a` is borrowed for `'static`
11LL |
12LL | }
13 | - `a` dropped here while still borrowed
14
15error: aborting due to 1 previous error
16
17For 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
3use std::marker::{CoerceShared, Reborrow};
4
5struct MissingSourceMut<'a, T> {
6 value: &'a mut T,
7}
8
9impl<'a, T> Reborrow for MissingSourceMut<'a, T> {}
10
11#[derive(Clone, Copy)]
12struct MissingSourceRef<'a, T> {
13 value: &'a T,
14 len: usize,
15}
16
17impl<'a, T> CoerceShared<MissingSourceRef<'a, T>> for MissingSourceMut<'a, T> {}
18//~^ ERROR
19
20fn main() {}
tests/ui/reborrow/coerce-shared-missing-target-field.stderr created+8
......@@ -0,0 +1,8 @@
1error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced
2 --> $DIR/coerce-shared-missing-target-field.rs:17:13
3 |
4LL | impl<'a, T> CoerceShared<MissingSourceRef<'a, T>> for MissingSourceMut<'a, T> {}
5 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6
7error: 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
3use std::marker::{CoerceShared, Reborrow};
4
5trait Trait {
6 type Assoc;
7}
8
9impl Trait for i32 {
10 type Assoc = u32;
11}
12
13type AliasMutField<'a> = &'a mut <i32 as Trait>::Assoc;
14type AliasRefField<'a> = &'a u32;
15
16struct AliasMut<'a> {
17 value: AliasMutField<'a>,
18}
19
20impl Reborrow for AliasMut<'_> {}
21
22#[derive(Copy, Clone)]
23struct AliasRef<'a> {
24 value: AliasRefField<'a>,
25}
26
27impl<'a> CoerceShared<AliasRef<'a>> for AliasMut<'a> {}
28//~^ ERROR
29
30struct InnerLifetimeMut<'a> {
31 value: &'a mut &'static (),
32}
33
34impl Reborrow for InnerLifetimeMut<'_> {}
35
36#[derive(Copy, Clone)]
37struct InnerLifetimeRef<'a> {
38 value: &'a &'a (),
39}
40
41impl<'a> CoerceShared<InnerLifetimeRef<'a>> for InnerLifetimeMut<'a> {}
42
43fn main() {}
tests/ui/reborrow/coerce-shared-mut-ref-field-validation.stderr created+9
......@@ -0,0 +1,9 @@
1error[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 |
4LL | 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
7error: aborting due to 1 previous error
8
9For 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
4use std::marker::{CoerceShared, Reborrow};
5
6struct ExtraMut<'a, T> {
7 value: &'a mut T,
8}
9
10impl<'a, T> Reborrow for ExtraMut<'a, T> {}
11
12struct OmitMut<'a, T> {
13 value: &'a mut T,
14 extra: ExtraMut<'a, T>,
15}
16
17impl<'a, T> Reborrow for OmitMut<'a, T> {}
18
19struct OmitRef<'a, T> {
20 value: &'a T,
21}
22
23impl<'a, T> Clone for OmitRef<'a, T> {
24 fn clone(&self) -> Self {
25 *self
26 }
27}
28
29impl<'a, T> Copy for OmitRef<'a, T> {}
30
31impl<'a, T> CoerceShared<OmitRef<'a, T>> for OmitMut<'a, T> {}
32//~^ ERROR
33
34fn read(value: OmitRef<'_, i32>) {
35 assert_eq!(*value.value, 1);
36}
37
38fn 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 @@
1error: 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 |
4LL | impl<'a, T> CoerceShared<OmitRef<'a, T>> for OmitMut<'a, T> {}
5 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6
7error: 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
3use std::marker::{CoerceShared, Reborrow};
4
5struct ExtraMut<'a, T> {
6 value: &'a mut T,
7}
8
9impl<'a, T> Reborrow for ExtraMut<'a, T> {}
10
11struct OmitMut<'a, T> {
12 value: &'a mut T,
13 extra: ExtraMut<'a, T>,
14}
15
16impl<'a, T> Reborrow for OmitMut<'a, T> {}
17
18struct OmitRef<'a, T> {
19 value: &'a T,
20}
21
22impl<'a, T> Clone for OmitRef<'a, T> {
23 fn clone(&self) -> Self {
24 *self
25 }
26}
27
28impl<'a, T> Copy for OmitRef<'a, T> {}
29
30impl<'a, T> CoerceShared<OmitRef<'a, T>> for OmitMut<'a, T> {}
31//~^ ERROR
32
33fn get<'a>(value: OmitRef<'a, i32>) -> &'a i32 {
34 value.value
35}
36
37fn 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 @@
1error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced
2 --> $DIR/coerce-shared-omitted-reborrow-field-locked.rs:30:13
3 |
4LL | impl<'a, T> CoerceShared<OmitRef<'a, T>> for OmitMut<'a, T> {}
5 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6
7error[E0506]: cannot assign to `*wrapped.extra.value` because it is borrowed
8 --> $DIR/coerce-shared-omitted-reborrow-field-locked.rs:49:5
9 |
10LL | let shared = get(wrapped);
11 | ------- `*wrapped.extra.value` is borrowed here
12LL |
13LL | *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
19error: aborting due to 2 previous errors
20
21For 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
4use std::marker::{CoerceShared, Reborrow};
5
6struct ExtraMut<'a, T> {
7 value: &'a mut T,
8}
9
10impl<'a, T> Reborrow for ExtraMut<'a, T> {}
11
12struct OmitMut<'a, T> {
13 value: &'a mut T,
14 extra: ExtraMut<'a, T>,
15}
16
17impl<'a, T> Reborrow for OmitMut<'a, T> {}
18
19struct OmitRef<'a, T> {
20 value: &'a T,
21}
22
23impl<'a, T> Clone for OmitRef<'a, T> {
24 fn clone(&self) -> Self {
25 *self
26 }
27}
28
29impl<'a, T> Copy for OmitRef<'a, T> {}
30
31impl<'a, T> CoerceShared<OmitRef<'a, T>> for OmitMut<'a, T> {}
32//~^ ERROR
33
34fn get<'a>(value: OmitRef<'a, i32>) -> &'a i32 {
35 value.value
36}
37
38fn 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 @@
1error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced
2 --> $DIR/coerce-shared-omitted-reborrow-field.rs:31:13
3 |
4LL | impl<'a, T> CoerceShared<OmitRef<'a, T>> for OmitMut<'a, T> {}
5 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6
7error: 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
4use std::marker::{CoerceShared, Reborrow};
5
6struct ReorderMut<'a> {
7 a: &'a mut u8,
8 b: &'a mut u16,
9}
10
11impl<'a> Reborrow for ReorderMut<'a> {}
12
13#[derive(Clone, Copy)]
14struct ReorderRef<'a> {
15 b: &'a u16,
16 a: &'a u8,
17}
18
19impl<'a> CoerceShared<ReorderRef<'a>> for ReorderMut<'a> {}
20//~^ ERROR
21
22fn read(value: ReorderRef<'_>) -> (u16, u8) {
23 (*value.b, *value.a)
24}
25
26fn 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 @@
1error: implementing `CoerceShared` does not allow multiple lifetimes or fields to be coerced
2 --> $DIR/coerce-shared-reordered-field.rs:19:10
3 |
4LL | impl<'a> CoerceShared<ReorderRef<'a>> for ReorderMut<'a> {}
5 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6
7error: 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
3use std::marker::{CoerceShared, PhantomData, Reborrow};
4
5struct GenericMut<'a, T, U> {
6 value: &'a mut T,
7 marker: PhantomData<U>,
8}
9
10impl<'a, T, U> Reborrow for GenericMut<'a, T, U> {}
11
12#[derive(Clone, Copy)]
13struct GenericRef<'a, T, U> {
14 value: &'a U,
15 marker: PhantomData<T>,
16}
17
18impl<'a, T, U> CoerceShared<GenericRef<'a, T, U>> for GenericMut<'a, T, U> {}
19//~^ ERROR
20
21fn main() {}
tests/ui/reborrow/coerce-shared-wrong-generic.stderr created+9
......@@ -0,0 +1,9 @@
1error[E0277]: the trait bound `&'a mut T: CoerceShared<&'a U>` is not satisfied
2 --> $DIR/coerce-shared-wrong-generic.rs:18:1
3 |
4LL | 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
7error: aborting due to 1 previous error
8
9For 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
4use std::clone::Share;
5
6struct Alias;
7
8impl Clone for Alias {
9 fn clone(&self) -> Self {
10 Alias
11 }
12}
13
14impl 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
21fn main() {}
tests/ui/share-trait/share-trait-final-method.stderr created+11
......@@ -0,0 +1,11 @@
1error: cannot override `share` because it already has a `final` definition in the trait
2 --> $DIR/share-trait-final-method.rs:15:5
3 |
4LL | fn share(&self) -> Self {
5 | ^^^^^^^^^^^^^^^^^^^^^^^
6 |
7note: `share` is marked final here
8 --> $SRC_DIR/core/src/clone.rs:LL:COL
9
10error: 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);
77note: ...which requires computing layout of `<&'static Foo as core::ops::deref::Deref>::Target`...
88 --> $SRC_DIR/core/src/ops/deref.rs:LL:COL
99 = note: ...which again requires computing layout of `Foo`, completing the cycle
10note: cycle used when const-evaluating + checking `_`
11 --> $DIR/stack-overflow-trait-infer-98842.rs:13:1
12 |
13LL | const _: *const Foo = 0 as _;
14 | ^^^^^^^^^^^^^^^^^^^
10note: cycle used when computing layout of `<&'static Foo as core::ops::deref::Deref>::Target`
11 --> $SRC_DIR/core/src/ops/deref.rs:LL:COL
1512 = 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>
1613
1714error: 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
8struct S<T: ?Sized>(T);
9
10fn should_fail_1() {
11 let ref y @ [ref x, _]; //~ ERROR E0277
12 x = "";
13}
14
15fn should_fail_2() {
16 let [ref x]; //~ ERROR E0277
17 x = "";
18}
19
20fn should_fail_3() {
21 let [[ref x], [_, y @ ..]]; //~ ERROR E0277
22 x = "";
23 y = [];
24}
25
26fn should_fail_4() {
27 let [(ref a, b), x]; //~ ERROR E0277
28 a = "";
29 b = 5;
30}
31
32fn should_fail_5() {
33 let (ref a, b); //~ ERROR E0277
34 a = "";
35 b = 5;
36}
37
38fn should_fail_6() {
39 let [S(ref x)]; //~ ERROR E0277
40 x = "";
41}
42
43fn should_pass_1() {
44 let ref x;
45 x = "";
46}
47
48fn should_pass_2() {
49 let ref y @ (ref x,);
50 x = "";
51}
52
53fn should_pass_3() {
54 let S(ref x);
55 x = "";
56}
57
58fn main() {}
tests/ui/wf/let-pat-inferred-non-wf.stderr deleted-62
......@@ -1,62 +0,0 @@
1error[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 |
4LL | 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
10error[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 |
13LL | 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
19error[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 |
22LL | 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
28error[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 |
31LL | 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
37error[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 |
40LL | 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
46error[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 |
49LL | 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`
53note: required because it appears within the type `S<str>`
54 --> $DIR/let-pat-inferred-non-wf.rs:8:8
55 |
56LL | struct S<T: ?Sized>(T);
57 | ^
58 = note: slice and array elements must have `Sized` type
59
60error: aborting due to 6 previous errors
61
62For more information about this error, try `rustc --explain E0277`.