Merge from rustc

This commit is contained in:
The Miri Cronjob Bot 2025-06-06 05:02:49 +00:00
commit c44bc10b67
291 changed files with 4610 additions and 2821 deletions

View file

@ -443,23 +443,26 @@ impl Step for Llvm {
// See https://github.com/rust-lang/rust/pull/50104
cfg.define("LLVM_ENABLE_LIBXML2", "OFF");
if !enabled_llvm_projects.is_empty() {
enabled_llvm_projects.sort();
enabled_llvm_projects.dedup();
cfg.define("LLVM_ENABLE_PROJECTS", enabled_llvm_projects.join(";"));
}
let mut enabled_llvm_runtimes = Vec::new();
if helpers::forcing_clang_based_tests() {
enabled_llvm_runtimes.push("compiler-rt");
}
// This is an experimental flag, which likely builds more than necessary.
// We will optimize it when we get closer to releasing it on nightly.
if builder.config.llvm_offload {
enabled_llvm_runtimes.push("offload");
//FIXME(ZuseZ4): LLVM intends to drop the offload dependency on openmp.
//Remove this line once they achieved it.
enabled_llvm_runtimes.push("openmp");
enabled_llvm_projects.push("compiler-rt");
}
if !enabled_llvm_projects.is_empty() {
enabled_llvm_projects.sort();
enabled_llvm_projects.dedup();
cfg.define("LLVM_ENABLE_PROJECTS", enabled_llvm_projects.join(";"));
}
if !enabled_llvm_runtimes.is_empty() {

View file

@ -1795,11 +1795,12 @@ Executed at: {executed_at}"#,
let now = t!(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH));
let _ = fs::rename(dst, format!("{}-{}", dst.display(), now.as_nanos()));
}
let metadata = t!(src.symlink_metadata(), format!("src = {}", src.display()));
let mut metadata = t!(src.symlink_metadata(), format!("src = {}", src.display()));
let mut src = src.to_path_buf();
if metadata.file_type().is_symlink() {
if dereference_symlinks {
src = t!(fs::canonicalize(src));
metadata = t!(fs::metadata(&src), format!("target = {}", src.display()));
} else {
let link = t!(fs::read_link(src));
t!(self.symlink_file(link, dst));

View file

@ -96,14 +96,13 @@ ENV RUST_CONFIGURE_ARGS \
--set rust.lto=thin \
--set rust.codegen-units=1
# Note that `rust.debug` is set to true *only* for `opt-dist`
ENV SCRIPT python3 ../x.py build --set rust.debug=true opt-dist && \
./build/$HOSTS/stage0-tools-bin/opt-dist linux-ci -- python3 ../x.py dist \
--host $HOSTS --target $HOSTS \
--include-default-paths \
build-manifest bootstrap && \
# Use GCC for building GCC, as it seems to behave badly when built with Clang
CC=/rustroot/bin/cc CXX=/rustroot/bin/c++ python3 ../x.py dist gcc
ARG SCRIPT_ARG
COPY host-x86_64/dist-x86_64-linux/dist.sh /scripts/
COPY host-x86_64/dist-x86_64-linux/dist-alt.sh /scripts/
ENV SCRIPT /scripts/${SCRIPT_ARG}
ENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=clang
# This is the only builder which will create source tarballs

View file

@ -0,0 +1,8 @@
#!/bin/bash
set -eux
python3 ../x.py dist \
--host $HOSTS --target $HOSTS \
--include-default-paths \
build-manifest bootstrap

View file

@ -0,0 +1,13 @@
#!/bin/bash
set -eux
python3 ../x.py build --set rust.debug=true opt-dist
./build/$HOSTS/stage0-tools-bin/opt-dist linux-ci -- python3 ../x.py dist \
--host $HOSTS --target $HOSTS \
--include-default-paths \
build-manifest bootstrap
# Use GCC for building GCC, as it seems to behave badly when built with Clang
CC=/rustroot/bin/cc CXX=/rustroot/bin/c++ python3 ../x.py dist gcc

View file

@ -10,6 +10,10 @@ runners:
free_disk: true
<<: *base-job
- &job-linux-4c-largedisk
os: ubuntu-24.04-4core-16gb
<<: *base-job
- &job-linux-8c
os: ubuntu-24.04-8core-32gb
<<: *base-job
@ -105,6 +109,15 @@ envs:
pr:
PR_CI_JOB: 1
jobs:
dist-x86_64-linux: &job-dist-x86_64-linux
name: dist-x86_64-linux
env:
CODEGEN_BACKENDS: llvm,cranelift
DOCKER_SCRIPT: dist.sh
<<: *job-linux-36c-codebuild
# Jobs that run on each push to a pull request (PR)
# These jobs automatically inherit envs.pr, to avoid repeating
# it in each job definition.
@ -138,10 +151,7 @@ pr:
# These jobs automatically inherit envs.try, to avoid repeating
# it in each job definition.
try:
- name: dist-x86_64-linux
env:
CODEGEN_BACKENDS: llvm,cranelift
<<: *job-linux-36c-codebuild
- <<: *job-dist-x86_64-linux
# Main CI jobs that have to be green to merge a commit into master
# These jobs automatically inherit envs.auto, to avoid repeating
@ -234,16 +244,14 @@ auto:
- name: dist-x86_64-illumos
<<: *job-linux-4c
- name: dist-x86_64-linux
env:
CODEGEN_BACKENDS: llvm,cranelift
<<: *job-linux-36c-codebuild
- <<: *job-dist-x86_64-linux
- name: dist-x86_64-linux-alt
env:
IMAGE: dist-x86_64-linux
CODEGEN_BACKENDS: llvm,cranelift
<<: *job-linux-16c
DOCKER_SCRIPT: dist-alt.sh
<<: *job-linux-4c-largedisk
- name: dist-x86_64-musl
env:

View file

@ -1 +1 @@
99e7c15e81385b38a8186b51edc4577d5d7b5bdd
c68032fd4c442d275f4daa571ba19c076106b490

View file

@ -16,7 +16,9 @@ LooseTypes // Risk incorrect derivatives instead of aborting when missing Type I
```
<div class="warning">
`LooseTypes` is often helpful to get rid of Enzyme errors stating `Can not deduce type of <X>` and to be able to run some code. But please keep in mind that this flag absolutely has the chance to cause incorrect gradients. Even worse, the gradients might be correct for certain input values, but not for others. So please create issues about such bugs and only use this flag temporarily while you wait for your bug to be fixed.
</div>
### Benchmark flags

View file

@ -1,6 +1,10 @@
# Async closures/"coroutine-closures"
<!-- toc -->
Please read [RFC 3668](https://rust-lang.github.io/rfcs/3668-async-closures.html) to understand the general motivation of the feature. This is a very technical and somewhat "vertical" chapter; ideally we'd split this and sprinkle it across all the relevant chapters, but for the purposes of understanding async closures *holistically*, I've put this together all here in one chapter.
# Coroutine-closures -- a technical deep dive
## Coroutine-closures -- a technical deep dive
Coroutine-closures are a generalization of async closures, being special syntax for closure expressions which return a coroutine, notably one that is allowed to capture from the closure's upvars.
@ -8,9 +12,11 @@ For now, the only usable kind of coroutine-closure is the async closure, and sup
As a consequence of the code being somewhat general, this document may flip between calling them "async closures" and "coroutine-closures". The future that is returned by the async closure will generally be called the "coroutine" or the "child coroutine".
## HIR
### HIR
Async closures (and in the future, other coroutine flavors such as `gen`) are represented in HIR as a `hir::Closure` whose closure-kind is `ClosureKind::CoroutineClosure(_)`[^k1], which wraps an async block, which is also represented in HIR as a `hir::Closure`) and whose closure-kind is `ClosureKind::Closure(CoroutineKind::Desugared(_, CoroutineSource::Closure))`[^k2].
Async closures (and in the future, other coroutine flavors such as `gen`) are represented in HIR as a `hir::Closure`.
The closure-kind of the `hir::Closure` is `ClosureKind::CoroutineClosure(_)`[^k1], which wraps an async block, which is also represented in HIR as a `hir::Closure`.
The closure-kind of the async block is `ClosureKind::Closure(CoroutineKind::Desugared(_, CoroutineSource::Closure))`[^k2].
[^k1]: <https://github.com/rust-lang/rust/blob/5ca0e9fa9b2f92b463a0a2b0b34315e09c0b7236/compiler/rustc_ast_lowering/src/expr.rs#L1147>
@ -24,7 +30,7 @@ Like `async fn`, when lowering an async closure's body, we need to unconditional
[^l3]: <https://github.com/rust-lang/rust/blob/5ca0e9fa9b2f92b463a0a2b0b34315e09c0b7236/compiler/rustc_hir_typeck/src/upvar.rs#L250-L256>
## `rustc_middle::ty` Representation
### `rustc_middle::ty` Representation
For the purposes of keeping the implementation mostly future-compatible (i.e. with gen `|| {}` and `async gen || {}`), most of this section calls async closures "coroutine-closures".
@ -72,7 +78,7 @@ To most easily construct the `Coroutine` that a coroutine-closure returns, you c
Most of the args to that function will be components that you can get out of the `CoroutineArgs`, except for the `goal_kind: ClosureKind` which controls which flavor of coroutine to return based off of the `ClosureKind` passed in -- i.e. it will prepare the by-ref coroutine if `ClosureKind::Fn | ClosureKind::FnMut`, and the by-move coroutine if `ClosureKind::FnOnce`.
## Trait Hierarchy
### Trait Hierarchy
We introduce a parallel hierarchy of `Fn*` traits that are implemented for . The motivation for the introduction was covered in a blog post: [Async Closures](https://hackmd.io/@compiler-errors/async-closures).
@ -98,11 +104,11 @@ We mention above that "regular" callable types can implement `AsyncFn*`, but the
See the "follow-up: when do..." section below for an elaborated answer. The full answer describes a pretty interesting and hopefully thorough heuristic that is used to ensure that most async closures "just work".
## Tale of two bodies...
### Tale of two bodies...
When async closures are called with `AsyncFn`/`AsyncFnMut`, they return a coroutine that borrows from the closure. However, when they are called via `AsyncFnOnce`, we consume that closure, and cannot return a coroutine that borrows from data that is now dropped.
To work around around this limitation, we synthesize a separate by-move MIR body for calling `AsyncFnOnce::call_once` on a coroutine-closure that can be called by-ref.
To work around this limitation, we synthesize a separate by-move MIR body for calling `AsyncFnOnce::call_once` on a coroutine-closure that can be called by-ref.
This body operates identically to the "normal" coroutine returned from calling the coroutine-closure, except for the fact that it has a different set of upvars, since we must *move* the captures from the parent coroutine-closure into the child coroutine.
@ -120,7 +126,7 @@ Since we've synthesized a new def id, this query is also responsible for feeding
[^b3]: <https://github.com/rust-lang/rust/blob/5ca0e9fa9b2f92b463a0a2b0b34315e09c0b7236/compiler/rustc_mir_transform/src/lib.rs#L339-L342>
## Closure signature inference
### Closure signature inference
The closure signature inference algorithm for async closures is a bit more complicated than the inference algorithm for "traditional" closures. Like closures, we iterate through all of the clauses that may be relevant (for the expectation type passed in)[^deduce1].
@ -173,7 +179,7 @@ s.as_bytes();
So *instead*, we use this alias (in this case, a projection: `AsyncFnKindHelper::Upvars<'env, ...>`) to delay the computation of the *tupled upvars* and give us something to put in its place, while still allowing us to return a `TyKind::Coroutine` (which is a rigid type) and we may successfully confirm the built-in traits we need (in our case, `Future`), since the `Future` implementation doesn't depend on the upvars at all.
## Upvar analysis
### Upvar analysis
By and large, the upvar analysis for coroutine-closures and their child coroutines proceeds like normal upvar analysis. However, there are several interesting bits that happen to account for async closures' special natures:
@ -262,7 +268,7 @@ let c = async || {
If either of these cases apply, then we should capture the borrow with the lifetime of the parent coroutine-closure's env. Luckily, if this function is not correct, then the program is not unsound, since we still borrowck and validate the choices made from this function -- the only side-effect is that the user may receive unnecessary borrowck errors.
## Instance resolution
### Instance resolution
If a coroutine-closure has a closure-kind of `FnOnce`, then its `AsyncFnOnce::call_once` and `FnOnce::call_once` implementations resolve to the coroutine-closure's body[^res1], and the `Future::poll` of the coroutine that gets returned resolves to the body of the child closure.
@ -282,7 +288,7 @@ This is represented by the `ConstructCoroutineInClosureShim`[^i1]. The `receiver
[^i3]: <https://github.com/rust-lang/rust/blob/07cbbdd69363da97075650e9be24b78af0bcdd23/compiler/rustc_middle/src/ty/instance.rs#L841>
## Borrow-checking
### Borrow-checking
It turns out that borrow-checking async closures is pretty straightforward. After adding a new `DefiningTy::CoroutineClosure`[^bck1] variant, and teaching borrowck how to generate the signature of the coroutine-closure[^bck2], borrowck proceeds totally fine.

View file

@ -265,13 +265,13 @@ Another problem was that it was not possible to normalize `ParamEnv`s correctly
Given a type such as `for<'a> fn(<?x as Trait<'a>::Assoc>)`, it is not possible to correctly handle this with the old solver's approach to normalization.
If we were to normalize it to `for<'a> fn(?y)` and register a goal to normalize `for<'a> <?x as Trait<'a>>::Assoc -> ?y`, this would result in errors in cases where `<?x as Trait<'a>>::Assoc` normalized to `&'a u32`. The inference variable `?y` would be in a lower [universe][universes] than the placeholders made when instantiating the `for<'a>` binder.
If we were to normalize it to `for<'a> fn(?y)` and register a goal to normalize `for<'a> <?x as Trait<'a>>::Assoc -> ?y`, this would result in errors in cases where `<?x as Trait<'a>>::Assoc` normalized to `&'a u32`. The inference variable `?y` would be in a lower [universe] than the placeholders made when instantiating the `for<'a>` binder.
Leaving the alias unnormalized would also be wrong as the old solver expects all aliases to be rigid. This was a soundness bug before the new solver was stabilized in coherence: [relating projection substs is unsound during coherence](https://github.com/rust-lang/rust/issues/102048).
Ultimately this means that it is not always possible to ensure all aliases inside of a value are rigid.
[universes]: https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/placeholders_and_universes.html#what-is-a-universe
[universe]: borrow_check/region_inference/placeholders_and_universes.md#what-is-a-universe
[deeply_normalize]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/traits/normalize/trait.NormalizeExt.html#tymethod.deeply_normalize
## Handling uses of diverging aliases

View file

@ -186,9 +186,11 @@ Note that if you start the default try job using `@bors try`, it will skip build
Multiple try builds can execute concurrently across different PRs.
<div class="warning">
bors identify try jobs by commit hash. This means that if you have two PRs
Bors identifies try jobs by commit hash. This means that if you have two PRs
containing the same (latest) commits, running `@bors try` will result in the
*same* try job and it really confuses `bors`. Please refrain from doing so.
</div>
[rustc-perf]: https://github.com/rust-lang/rustc-perf

View file

@ -438,7 +438,9 @@ To work around this when working on a particular test, temporarily create a
with these contents:
<div class="warning">
Be careful not to add this `Cargo.toml` or its `Cargo.lock` to your actual PR!
</div>
```toml

View file

@ -248,11 +248,13 @@ ignoring debuggers.
| `no-prefer-dynamic` | Don't use `-C prefer-dynamic`, don't build as a dylib via a `--crate-type=dylib` preset flag | `ui`, `crashes` | N/A |
<div class="warning">
Tests (outside of `run-make`) that want to use incremental tests not in the
incremental test-suite must not pass `-C incremental` via `compile-flags`, and
must instead use the `//@ incremental` directive.
Consider writing the test as a proper incremental test instead.
</div>
### Rustdoc

View file

@ -7,9 +7,11 @@ ui/codegen/assembly test suites. It provides `core` stubs for tests that need to
build for cross-compiled targets but do not need/want to run.
<div class="warning">
Please note that [`minicore`] is only intended for `core` items, and explicitly
**not** `std` or `alloc` items because `core` items are applicable to a wider
range of tests.
</div>
A test can use [`minicore`] by specifying the `//@ add-core-stubs` directive.

View file

@ -8,6 +8,7 @@ development because it takes a really long time. For local development, see the
subsection after on how to run a subset of tests.
<div class="warning">
Running plain `./x test` will build the stage 1 compiler and then run the whole
test suite. This not only include `tests/`, but also `library/`, `compiler/`,
`src/tools/` package tests and more.
@ -16,6 +17,7 @@ You usually only want to run a subset of the test suites (or even a smaller set
of tests than that) which you expect will exercise your changes. PR CI exercises
a subset of test collections, and merge queue CI will exercise all of the test
collection.
</div>
```text
@ -116,8 +118,10 @@ By listing which test suites you want to run,
you avoid having to run tests for components you did not change at all.
<div class="warning">
Note that bors only runs the tests with the full stage 2 build; therefore, while
the tests **usually** work fine with stage 1, there are some limitations.
</div>
### Run all tests using a stage 2 compiler

View file

@ -10,7 +10,8 @@ path = "lib.rs"
default = ["rustc-hash"]
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde = "1.0"
serde_derive = "1.0"
rustc-hash = { version = "2.0", optional = true }
[dev-dependencies]

View file

@ -21,7 +21,7 @@ use std::path::PathBuf;
#[cfg(feature = "rustc-hash")]
use rustc_hash::FxHashMap as HashMap;
use serde::{Deserialize, Serialize};
use serde_derive::{Deserialize, Serialize};
pub type FxHashMap<K, V> = HashMap<K, V>; // re-export for use in src/librustdoc

View file

@ -77,11 +77,11 @@ fn main() {
struct NoIntoIter();
impl NoIntoIter {
fn iter(&self) -> slice::Iter<u8> {
fn iter(&self) -> slice::Iter<'_, u8> {
unimplemented!()
}
fn iter_mut(&mut self) -> slice::IterMut<u8> {
fn iter_mut(&mut self) -> slice::IterMut<'_, u8> {
unimplemented!()
}
}

View file

@ -77,11 +77,11 @@ fn main() {
struct NoIntoIter();
impl NoIntoIter {
fn iter(&self) -> slice::Iter<u8> {
fn iter(&self) -> slice::Iter<'_, u8> {
unimplemented!()
}
fn iter_mut(&mut self) -> slice::IterMut<u8> {
fn iter_mut(&mut self) -> slice::IterMut<'_, u8> {
unimplemented!()
}
}

View file

@ -8,7 +8,7 @@ fn main() {
struct Unrelated(&'static [u8]);
impl Unrelated {
fn next(&self) -> std::slice::Iter<u8> {
fn next(&self) -> std::slice::Iter<'_, u8> {
self.0.iter()
}
}

View file

@ -71,7 +71,7 @@ impl S {
struct S2([u8]);
impl S2 {
fn iter(&self) -> core::slice::Iter<u8> {
fn iter(&self) -> core::slice::Iter<'_, u8> {
self.0.iter()
}
}

View file

@ -49,7 +49,7 @@ struct Lt2<'a> {
impl<'a> Lt2<'a> {
// The lifetime is different, but thats irrelevant; see issue #734.
pub fn new(s: &str) -> Lt2 {
pub fn new(s: &str) -> Lt2<'_> {
unimplemented!()
}
}

View file

@ -10,7 +10,7 @@
clippy::unnecessary_wraps,
dyn_drop,
clippy::get_first,
elided_named_lifetimes
mismatched_lifetime_syntaxes,
)]
extern crate proc_macros;

View file

@ -10,7 +10,7 @@
clippy::unnecessary_wraps,
dyn_drop,
clippy::get_first,
elided_named_lifetimes
mismatched_lifetime_syntaxes,
)]
extern crate proc_macros;

View file

@ -312,7 +312,7 @@ mod issue_9218 {
// Inferred to be `&'a str`, afaik.
fn cow_good_ret_ty<'a>(input: &'a Cow<'a, str>) -> &str {
//~^ ERROR: elided lifetime has a name
//~^ ERROR: lifetime flowing from input to output with different syntax
todo!()
}
}

View file

@ -1,12 +1,3 @@
error: elided lifetime has a name
--> tests/ui/ptr_arg.rs:314:56
|
LL | fn cow_good_ret_ty<'a>(input: &'a Cow<'a, str>) -> &str {
| -- lifetime `'a` declared here ^ this elided lifetime gets resolved as `'a`
|
= note: `-D elided-named-lifetimes` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(elided_named_lifetimes)]`
error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
--> tests/ui/ptr_arg.rs:13:14
|
@ -240,5 +231,21 @@ error: writing `&String` instead of `&str` involves a new object where a slice w
LL | fn good(v1: &String, v2: &String) {
| ^^^^^^^ help: change this to: `&str`
error: lifetime flowing from input to output with different syntax can be confusing
--> tests/ui/ptr_arg.rs:314:36
|
LL | fn cow_good_ret_ty<'a>(input: &'a Cow<'a, str>) -> &str {
| ^^ ^^ ---- the lifetime gets resolved as `'a`
| | |
| | these lifetimes flow to the output
| these lifetimes flow to the output
|
= note: `-D mismatched-lifetime-syntaxes` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(mismatched_lifetime_syntaxes)]`
help: one option is to consistently use `'a`
|
LL | fn cow_good_ret_ty<'a>(input: &'a Cow<'a, str>) -> &'a str {
| ++
error: aborting due to 27 previous errors

View file

@ -191,7 +191,7 @@ struct CounterWrapper<'a> {
}
impl<'a> CounterWrapper<'a> {
fn new(counter: &Counter) -> CounterWrapper {
fn new(counter: &Counter) -> CounterWrapper<'_> {
counter.i.fetch_add(1, Ordering::Relaxed);
CounterWrapper { counter }
}
@ -204,7 +204,7 @@ impl<'a> Drop for CounterWrapper<'a> {
}
impl Counter {
fn temp_increment(&self) -> Vec<CounterWrapper> {
fn temp_increment(&self) -> Vec<CounterWrapper<'_>> {
vec![CounterWrapper::new(self), CounterWrapper::new(self)]
}
}
@ -480,7 +480,7 @@ impl StateWithBoxedMutexGuard {
fn new() -> StateWithBoxedMutexGuard {
StateWithBoxedMutexGuard { u: Mutex::new(42) }
}
fn lock(&self) -> Box<MutexGuard<u64>> {
fn lock(&self) -> Box<MutexGuard<'_, u64>> {
Box::new(self.u.lock().unwrap())
}
}
@ -507,7 +507,7 @@ impl StateStringWithBoxedMutexGuard {
s: Mutex::new("A String".to_owned()),
}
}
fn lock(&self) -> Box<MutexGuard<String>> {
fn lock(&self) -> Box<MutexGuard<'_, String>> {
Box::new(self.s.lock().unwrap())
}
}
@ -686,11 +686,11 @@ struct Guard<'a, T>(MutexGuard<'a, T>);
struct Ref<'a, T>(&'a T);
impl<'a, T> Guard<'a, T> {
fn guard(&self) -> &MutexGuard<T> {
fn guard(&self) -> &MutexGuard<'_, T> {
&self.0
}
fn guard_ref(&self) -> Ref<MutexGuard<T>> {
fn guard_ref(&self) -> Ref<'_, MutexGuard<'_, T>> {
Ref(&self.0)
}

View file

@ -41,6 +41,7 @@ fn good_return_explicit_lt_ref<'a>(foo: &'a Foo) -> &'a u32 {
&foo.0
}
#[allow(mismatched_lifetime_syntaxes)]
fn good_return_implicit_lt_struct(foo: &Foo) -> FooRef {
FooRef { foo }
}

View file

@ -41,6 +41,7 @@ fn good_return_explicit_lt_ref<'a>(foo: &'a Foo) -> &'a u32 {
&foo.0
}
#[allow(mismatched_lifetime_syntaxes)]
fn good_return_implicit_lt_struct(foo: &Foo) -> FooRef {
FooRef { foo }
}

View file

@ -1,5 +1,5 @@
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:53:11
--> tests/ui/trivially_copy_pass_by_ref.rs:54:11
|
LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `u32`
@ -11,103 +11,103 @@ LL | #![deny(clippy::trivially_copy_pass_by_ref)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:53:20
--> tests/ui/trivially_copy_pass_by_ref.rs:54:20
|
LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `Foo`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:53:29
--> tests/ui/trivially_copy_pass_by_ref.rs:54:29
|
LL | fn bad(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `Baz`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:63:12
--> tests/ui/trivially_copy_pass_by_ref.rs:64:12
|
LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
| ^^^^^ help: consider passing by value instead: `self`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:63:22
--> tests/ui/trivially_copy_pass_by_ref.rs:64:22
|
LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `u32`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:63:31
--> tests/ui/trivially_copy_pass_by_ref.rs:64:31
|
LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `Foo`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:63:40
--> tests/ui/trivially_copy_pass_by_ref.rs:64:40
|
LL | fn bad(&self, x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `Baz`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:69:16
--> tests/ui/trivially_copy_pass_by_ref.rs:70:16
|
LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `u32`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:69:25
--> tests/ui/trivially_copy_pass_by_ref.rs:70:25
|
LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `Foo`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:69:34
--> tests/ui/trivially_copy_pass_by_ref.rs:70:34
|
LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `Baz`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:74:35
--> tests/ui/trivially_copy_pass_by_ref.rs:75:35
|
LL | fn bad_issue7518(self, other: &Self) {}
| ^^^^^ help: consider passing by value instead: `Self`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:87:16
--> tests/ui/trivially_copy_pass_by_ref.rs:88:16
|
LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `u32`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:87:25
--> tests/ui/trivially_copy_pass_by_ref.rs:88:25
|
LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `Foo`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:87:34
--> tests/ui/trivially_copy_pass_by_ref.rs:88:34
|
LL | fn bad2(x: &u32, y: &Foo, z: &Baz) {}
| ^^^^ help: consider passing by value instead: `Baz`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:94:33
--> tests/ui/trivially_copy_pass_by_ref.rs:95:33
|
LL | fn trait_method(&self, foo: &Foo);
| ^^^^ help: consider passing by value instead: `Foo`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:132:21
--> tests/ui/trivially_copy_pass_by_ref.rs:133:21
|
LL | fn foo_never(x: &i32) {
| ^^^^ help: consider passing by value instead: `i32`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:138:15
--> tests/ui/trivially_copy_pass_by_ref.rs:139:15
|
LL | fn foo(x: &i32) {
| ^^^^ help: consider passing by value instead: `i32`
error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
--> tests/ui/trivially_copy_pass_by_ref.rs:164:36
--> tests/ui/trivially_copy_pass_by_ref.rs:165:36
|
LL | fn unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 {
| ^^^^^^^ help: consider passing by value instead: `u32`

View file

@ -69,7 +69,7 @@ mod lifetimes {
impl<'a> Foo<'a> {
// Cannot use `Self` as return type, because the function is actually `fn foo<'b>(s: &'b str) ->
// Foo<'b>`
fn foo(s: &str) -> Foo {
fn foo(s: &str) -> Foo<'_> {
Foo { foo_str: s }
}
// cannot replace with `Self`, because that's `Foo<'a>`

View file

@ -69,7 +69,7 @@ mod lifetimes {
impl<'a> Foo<'a> {
// Cannot use `Self` as return type, because the function is actually `fn foo<'b>(s: &'b str) ->
// Foo<'b>`
fn foo(s: &str) -> Foo {
fn foo(s: &str) -> Foo<'_> {
Foo { foo_str: s }
}
// cannot replace with `Self`, because that's `Foo<'a>`

View file

@ -4,7 +4,7 @@ use getopts::Options;
pub struct Config {
/// The directory documentation output was generated in
pub doc_dir: String,
/// The file documentation was generated for, with docck commands to check
/// The file documentation was generated for, with docck directives to check
pub template: String,
}

View file

@ -0,0 +1,232 @@
use std::borrow::Cow;
use serde_json::Value;
use crate::cache::Cache;
#[derive(Debug)]
pub struct Directive {
pub kind: DirectiveKind,
pub path: String,
pub lineno: usize,
}
#[derive(Debug)]
pub enum DirectiveKind {
/// `//@ has <path>`
///
/// Checks the path exists.
HasPath,
/// `//@ has <path> <value>`
///
/// Check one thing at the path is equal to the value.
HasValue { value: String },
/// `//@ !has <path>`
///
/// Checks the path doesn't exist.
HasNotPath,
/// `//@ !has <path> <value>`
///
/// Checks the path exists, but doesn't have the given value.
HasNotValue { value: String },
/// `//@ is <path> <value>`
///
/// Check the path is the given value.
Is { value: String },
/// `//@ is <path> <value> <value>...`
///
/// Check that the path matches to exactly every given value.
IsMany { values: Vec<String> },
/// `//@ !is <path> <value>`
///
/// Check the path isn't the given value.
IsNot { value: String },
/// `//@ count <path> <value>`
///
/// Check the path has the expected number of matches.
CountIs { expected: usize },
/// `//@ set <name> = <path>`
Set { variable: String },
}
impl DirectiveKind {
/// Returns both the kind and the path.
///
/// Returns `None` if the directive isn't from jsondocck (e.g. from compiletest).
pub fn parse<'a>(
directive_name: &str,
negated: bool,
args: &'a [String],
) -> Option<(Self, &'a str)> {
let kind = match (directive_name, negated) {
("count", false) => {
assert_eq!(args.len(), 2);
let expected = args[1].parse().expect("invalid number for `count`");
Self::CountIs { expected }
}
("ismany", false) => {
// FIXME: Make this >= 3, and migrate len(values)==1 cases to @is
assert!(args.len() >= 2, "Not enough args to `ismany`");
let values = args[1..].to_owned();
Self::IsMany { values }
}
("is", false) => {
assert_eq!(args.len(), 2);
Self::Is { value: args[1].clone() }
}
("is", true) => {
assert_eq!(args.len(), 2);
Self::IsNot { value: args[1].clone() }
}
("set", false) => {
assert_eq!(args.len(), 3);
assert_eq!(args[1], "=");
return Some((Self::Set { variable: args[0].clone() }, &args[2]));
}
("has", false) => match args {
[_path] => Self::HasPath,
[_path, value] => Self::HasValue { value: value.clone() },
_ => panic!("`//@ has` must have 2 or 3 arguments, but got {args:?}"),
},
("has", true) => match args {
[_path] => Self::HasNotPath,
[_path, value] => Self::HasNotValue { value: value.clone() },
_ => panic!("`//@ !has` must have 2 or 3 arguments, but got {args:?}"),
},
// Ignore compiletest directives, like //@ edition
(_, false) if KNOWN_DIRECTIVE_NAMES.contains(&directive_name) => {
return None;
}
_ => {
panic!("Invalid directive `//@ {}{directive_name}`", if negated { "!" } else { "" })
}
};
Some((kind, &args[0]))
}
}
impl Directive {
/// Performs the actual work of ensuring a directive passes.
pub fn check(&self, cache: &mut Cache) -> Result<(), String> {
let matches = cache.select(&self.path);
match &self.kind {
DirectiveKind::HasPath => {
if matches.is_empty() {
return Err("matched to no values".to_owned());
}
}
DirectiveKind::HasNotPath => {
if !matches.is_empty() {
return Err(format!("matched to {matches:?}, but wanted no matches"));
}
}
DirectiveKind::HasValue { value } => {
let want_value = string_to_value(value, cache);
if !matches.contains(&want_value.as_ref()) {
return Err(format!(
"matched to {matches:?}, which didn't contain {want_value:?}"
));
}
}
DirectiveKind::HasNotValue { value } => {
let wantnt_value = string_to_value(value, cache);
if matches.contains(&wantnt_value.as_ref()) {
return Err(format!(
"matched to {matches:?}, which contains unwanted {wantnt_value:?}"
));
} else if matches.is_empty() {
return Err(format!(
"got no matches, but expected some matched (not containing {wantnt_value:?}"
));
}
}
DirectiveKind::Is { value } => {
let want_value = string_to_value(value, cache);
let matched = get_one(&matches)?;
if matched != want_value.as_ref() {
return Err(format!("matched to {matched:?} but want {want_value:?}"));
}
}
DirectiveKind::IsNot { value } => {
let wantnt_value = string_to_value(value, cache);
let matched = get_one(&matches)?;
if matched == wantnt_value.as_ref() {
return Err(format!("got value {wantnt_value:?}, but want anything else"));
}
}
DirectiveKind::IsMany { values } => {
// Serde json doesn't implement Ord or Hash for Value, so we must
// use a Vec here. While in theory that makes setwize equality
// O(n^2), in practice n will never be large enough to matter.
let expected_values =
values.iter().map(|v| string_to_value(v, cache)).collect::<Vec<_>>();
if expected_values.len() != matches.len() {
return Err(format!(
"Expected {} values, but matched to {} values ({:?})",
expected_values.len(),
matches.len(),
matches
));
};
for got_value in matches {
if !expected_values.iter().any(|exp| &**exp == got_value) {
return Err(format!("has match {got_value:?}, which was not expected",));
}
}
}
DirectiveKind::CountIs { expected } => {
if *expected != matches.len() {
return Err(format!(
"matched to `{matches:?}` with length {}, but expected length {expected}",
matches.len(),
));
}
}
DirectiveKind::Set { variable } => {
let value = get_one(&matches)?;
let r = cache.variables.insert(variable.to_owned(), value.clone());
assert!(r.is_none(), "name collision: {variable:?} is duplicated");
}
}
Ok(())
}
}
fn get_one<'a>(matches: &[&'a Value]) -> Result<&'a Value, String> {
match matches {
[] => Err("matched to no values".to_owned()),
[matched] => Ok(matched),
_ => Err(format!("matched to multiple values {matches:?}, but want exactly 1")),
}
}
// FIXME: This setup is temporary until we figure out how to improve this situation.
// See <https://github.com/rust-lang/rust/issues/125813#issuecomment-2141953780>.
include!(concat!(env!("CARGO_MANIFEST_DIR"), "/../compiletest/src/directive-list.rs"));
fn string_to_value<'a>(s: &str, cache: &'a Cache) -> Cow<'a, Value> {
if s.starts_with("$") {
Cow::Borrowed(&cache.variables.get(&s[1..]).unwrap_or_else(|| {
// FIXME(adotinthevoid): Show line number
panic!("No variable: `{}`. Current state: `{:?}`", &s[1..], cache.variables)
}))
} else {
Cow::Owned(serde_json::from_str(s).expect(&format!("Cannot convert `{}` to json", s)))
}
}

View file

@ -1,7 +1,7 @@
use crate::Command;
use crate::Directive;
#[derive(Debug)]
pub struct CkError {
pub message: String,
pub command: Command,
pub directive: Directive,
}

View file

@ -1,17 +1,17 @@
use std::borrow::Cow;
use std::process::ExitCode;
use std::sync::LazyLock;
use std::{env, fs};
use regex::{Regex, RegexBuilder};
use serde_json::Value;
mod cache;
mod config;
mod directive;
mod error;
use cache::Cache;
use config::parse_config;
use directive::{Directive, DirectiveKind};
use error::CkError;
fn main() -> ExitCode {
@ -19,14 +19,14 @@ fn main() -> ExitCode {
let mut failed = Vec::new();
let mut cache = Cache::new(&config);
let Ok(commands) = get_commands(&config.template) else {
let Ok(directives) = get_directives(&config.template) else {
eprintln!("Jsondocck failed for {}", &config.template);
return ExitCode::FAILURE;
};
for command in commands {
if let Err(message) = check_command(&command, &mut cache) {
failed.push(CkError { command, message });
for directive in directives {
if let Err(message) = directive.check(&mut cache) {
failed.push(CkError { directive, message });
}
}
@ -34,130 +34,20 @@ fn main() -> ExitCode {
ExitCode::SUCCESS
} else {
for i in failed {
eprintln!("{}:{}, command failed", config.template, i.command.lineno);
eprintln!("{}:{}, directive failed", config.template, i.directive.lineno);
eprintln!("{}", i.message)
}
ExitCode::FAILURE
}
}
#[derive(Debug)]
pub struct Command {
kind: CommandKind,
path: String,
lineno: usize,
}
#[derive(Debug)]
enum CommandKind {
/// `//@ has <path>`
///
/// Checks the path exists.
HasPath,
/// `//@ has <path> <value>`
///
/// Check one thing at the path is equal to the value.
HasValue { value: String },
/// `//@ !has <path>`
///
/// Checks the path doesn't exist.
HasNotPath,
/// `//@ !has <path> <value>`
///
/// Checks the path exists, but doesn't have the given value.
HasNotValue { value: String },
/// `//@ is <path> <value>`
///
/// Check the path is the given value.
Is { value: String },
/// `//@ is <path> <value> <value>...`
///
/// Check that the path matches to exactly every given value.
IsMany { values: Vec<String> },
/// `//@ !is <path> <value>`
///
/// Check the path isn't the given value.
IsNot { value: String },
/// `//@ count <path> <value>`
///
/// Check the path has the expected number of matches.
CountIs { expected: usize },
/// `//@ set <name> = <path>`
Set { variable: String },
}
impl CommandKind {
/// Returns both the kind and the path.
///
/// Returns `None` if the command isn't from jsondocck (e.g. from compiletest).
fn parse<'a>(command_name: &str, negated: bool, args: &'a [String]) -> Option<(Self, &'a str)> {
let kind = match (command_name, negated) {
("count", false) => {
assert_eq!(args.len(), 2);
let expected = args[1].parse().expect("invalid number for `count`");
Self::CountIs { expected }
}
("ismany", false) => {
// FIXME: Make this >= 3, and migrate len(values)==1 cases to @is
assert!(args.len() >= 2, "Not enough args to `ismany`");
let values = args[1..].to_owned();
Self::IsMany { values }
}
("is", false) => {
assert_eq!(args.len(), 2);
Self::Is { value: args[1].clone() }
}
("is", true) => {
assert_eq!(args.len(), 2);
Self::IsNot { value: args[1].clone() }
}
("set", false) => {
assert_eq!(args.len(), 3);
assert_eq!(args[1], "=");
return Some((Self::Set { variable: args[0].clone() }, &args[2]));
}
("has", false) => match args {
[_path] => Self::HasPath,
[_path, value] => Self::HasValue { value: value.clone() },
_ => panic!("`//@ has` must have 2 or 3 arguments, but got {args:?}"),
},
("has", true) => match args {
[_path] => Self::HasNotPath,
[_path, value] => Self::HasNotValue { value: value.clone() },
_ => panic!("`//@ !has` must have 2 or 3 arguments, but got {args:?}"),
},
(_, false) if KNOWN_DIRECTIVE_NAMES.contains(&command_name) => {
return None;
}
_ => {
panic!("Invalid command `//@ {}{command_name}`", if negated { "!" } else { "" })
}
};
Some((kind, &args[0]))
}
}
static LINE_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
RegexBuilder::new(
r#"
^\s*
//@\s+
(?P<negated>!?)
(?P<cmd>[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)
(?P<directive>[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)
(?P<args>.*)$
"#,
)
@ -180,16 +70,12 @@ static DEPRECATED_LINE_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
});
fn print_err(msg: &str, lineno: usize) {
eprintln!("Invalid command: {} on line {}", msg, lineno)
eprintln!("Invalid directive: {} on line {}", msg, lineno)
}
// FIXME: This setup is temporary until we figure out how to improve this situation.
// See <https://github.com/rust-lang/rust/issues/125813#issuecomment-2141953780>.
include!(concat!(env!("CARGO_MANIFEST_DIR"), "/../compiletest/src/directive-list.rs"));
/// Get a list of commands from a file.
fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
let mut commands = Vec::new();
/// Get a list of directives from a file.
fn get_directives(template: &str) -> Result<Vec<Directive>, ()> {
let mut directives = Vec::new();
let mut errors = false;
let file = fs::read_to_string(template).unwrap();
@ -197,7 +83,7 @@ fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
let lineno = lineno + 1;
if DEPRECATED_LINE_PATTERN.is_match(line) {
print_err("Deprecated command syntax, replace `// @` with `//@ `", lineno);
print_err("Deprecated directive syntax, replace `// @` with `//@ `", lineno);
errors = true;
continue;
}
@ -215,115 +101,10 @@ fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
continue;
};
if let Some((kind, path)) = CommandKind::parse(&cap["cmd"], negated, &args) {
commands.push(Command { kind, lineno, path: path.to_owned() })
if let Some((kind, path)) = DirectiveKind::parse(&cap["directive"], negated, &args) {
directives.push(Directive { kind, lineno, path: path.to_owned() })
}
}
if !errors { Ok(commands) } else { Err(()) }
}
/// Performs the actual work of ensuring a command passes.
fn check_command(command: &Command, cache: &mut Cache) -> Result<(), String> {
let matches = cache.select(&command.path);
match &command.kind {
CommandKind::HasPath => {
if matches.is_empty() {
return Err("matched to no values".to_owned());
}
}
CommandKind::HasNotPath => {
if !matches.is_empty() {
return Err(format!("matched to {matches:?}, but wanted no matches"));
}
}
CommandKind::HasValue { value } => {
let want_value = string_to_value(value, cache);
if !matches.contains(&want_value.as_ref()) {
return Err(format!("matched to {matches:?}, which didn't contain {want_value:?}"));
}
}
CommandKind::HasNotValue { value } => {
let wantnt_value = string_to_value(value, cache);
if matches.contains(&wantnt_value.as_ref()) {
return Err(format!(
"matched to {matches:?}, which contains unwanted {wantnt_value:?}"
));
} else if matches.is_empty() {
return Err(format!(
"got no matches, but expected some matched (not containing {wantnt_value:?}"
));
}
}
CommandKind::Is { value } => {
let want_value = string_to_value(value, cache);
let matched = get_one(&matches)?;
if matched != want_value.as_ref() {
return Err(format!("matched to {matched:?} but want {want_value:?}"));
}
}
CommandKind::IsNot { value } => {
let wantnt_value = string_to_value(value, cache);
let matched = get_one(&matches)?;
if matched == wantnt_value.as_ref() {
return Err(format!("got value {wantnt_value:?}, but want anything else"));
}
}
CommandKind::IsMany { values } => {
// Serde json doesn't implement Ord or Hash for Value, so we must
// use a Vec here. While in theory that makes setwize equality
// O(n^2), in practice n will never be large enough to matter.
let expected_values =
values.iter().map(|v| string_to_value(v, cache)).collect::<Vec<_>>();
if expected_values.len() != matches.len() {
return Err(format!(
"Expected {} values, but matched to {} values ({:?})",
expected_values.len(),
matches.len(),
matches
));
};
for got_value in matches {
if !expected_values.iter().any(|exp| &**exp == got_value) {
return Err(format!("has match {got_value:?}, which was not expected",));
}
}
}
CommandKind::CountIs { expected } => {
if *expected != matches.len() {
return Err(format!(
"matched to `{matches:?}` with length {}, but expected length {expected}",
matches.len(),
));
}
}
CommandKind::Set { variable } => {
let value = get_one(&matches)?;
let r = cache.variables.insert(variable.to_owned(), value.clone());
assert!(r.is_none(), "name collision: {variable:?} is duplicated");
}
}
Ok(())
}
fn get_one<'a>(matches: &[&'a Value]) -> Result<&'a Value, String> {
match matches {
[] => Err("matched to no values".to_owned()),
[matched] => Ok(matched),
_ => Err(format!("matched to multiple values {matches:?}, but want exactly 1")),
}
}
fn string_to_value<'a>(s: &str, cache: &'a Cache) -> Cow<'a, Value> {
if s.starts_with("$") {
Cow::Borrowed(&cache.variables.get(&s[1..]).unwrap_or_else(|| {
// FIXME(adotinthevoid): Show line number
panic!("No variable: `{}`. Current state: `{:?}`", &s[1..], cache.variables)
}))
} else {
Cow::Owned(serde_json::from_str(s).expect(&format!("Cannot convert `{}` to json", s)))
}
if !errors { Ok(directives) } else { Err(()) }
}

View file

@ -0,0 +1,25 @@
//! FIXME: This test should pass! However, `async fn` does not yet use `UnsafePinned`.
//! This is a regression test for <https://github.com/rust-lang/rust/issues/137750>:
//! `UnsafePinned` must include the effects of `UnsafeCell`.
//@revisions: stack tree
//@[tree]compile-flags: -Zmiri-tree-borrows
//@normalize-stderr-test: "\[0x[a-fx\d.]+\]" -> "[OFFSET]"
use core::future::Future;
use core::pin::{Pin, pin};
use core::task::{Context, Poll, Waker};
fn main() {
let mut f = pin!(async move {
let x = &mut 0u8;
core::future::poll_fn(move |_| {
*x = 1; //~ERROR: write access
Poll::<()>::Pending
})
.await
});
let mut cx = Context::from_waker(&Waker::noop());
assert_eq!(f.as_mut().poll(&mut cx), Poll::Pending);
let _: Pin<&_> = f.as_ref(); // Or: `f.as_mut().into_ref()`.
assert_eq!(f.as_mut().poll(&mut cx), Poll::Pending);
}

View file

@ -0,0 +1,43 @@
error: Undefined Behavior: attempting a write access using <TAG> at ALLOC[OFFSET], but that tag does not exist in the borrow stack for this location
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | *x = 1;
| ^^^^^^
| |
| attempting a write access using <TAG> at ALLOC[OFFSET], but that tag does not exist in the borrow stack for this location
| this error occurs as part of an access at ALLOC[OFFSET]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
help: <TAG> was created by a Unique retag at offsets [OFFSET]
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | / core::future::poll_fn(move |_| {
LL | | *x = 1;
LL | | Poll::<()>::Pending
LL | | })
LL | | .await
| |______________^
help: <TAG> was later invalidated at offsets [OFFSET] by a SharedReadOnly retag
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | let _: Pin<&_> = f.as_ref(); // Or: `f.as_mut().into_ref()`.
| ^^^^^^^^^^
= note: BACKTRACE (of the first span):
= note: inside closure at tests/fail/async-shared-mutable.rs:LL:CC
= note: inside `<std::future::PollFn<{closure@tests/fail/async-shared-mutable.rs:LL:CC}> as std::future::Future>::poll` at RUSTLIB/core/src/future/poll_fn.rs:LL:CC
note: inside closure
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | .await
| ^^^^^
note: inside `main`
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | assert_eq!(f.as_mut().poll(&mut cx), Poll::Pending);
| ^^^^^^^^^^^^^^^^^^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -0,0 +1,47 @@
error: Undefined Behavior: write access through <TAG> at ALLOC[OFFSET] is forbidden
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | *x = 1;
| ^^^^^^ write access through <TAG> at ALLOC[OFFSET] is forbidden
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
= help: the accessed tag <TAG> has state Frozen which forbids this child write access
help: the accessed tag <TAG> was created here, in the initial state Reserved
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | / core::future::poll_fn(move |_| {
LL | | *x = 1;
LL | | Poll::<()>::Pending
LL | | })
LL | | .await
| |______________^
help: the accessed tag <TAG> later transitioned to Active due to a child write access at offsets [OFFSET]
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | *x = 1;
| ^^^^^^
= help: this transition corresponds to the first write to a 2-phase borrowed mutable reference
help: the accessed tag <TAG> later transitioned to Frozen due to a reborrow (acting as a foreign read access) at offsets [OFFSET]
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | let _: Pin<&_> = f.as_ref(); // Or: `f.as_mut().into_ref()`.
| ^^^^^^^^^^
= help: this transition corresponds to a loss of write permissions
= note: BACKTRACE (of the first span):
= note: inside closure at tests/fail/async-shared-mutable.rs:LL:CC
= note: inside `<std::future::PollFn<{closure@tests/fail/async-shared-mutable.rs:LL:CC}> as std::future::Future>::poll` at RUSTLIB/core/src/future/poll_fn.rs:LL:CC
note: inside closure
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | .await
| ^^^^^
note: inside `main`
--> tests/fail/async-shared-mutable.rs:LL:CC
|
LL | assert_eq!(f.as_mut().poll(&mut cx), Poll::Pending);
| ^^^^^^^^^^^^^^^^^^^^^^^^
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error

View file

@ -0,0 +1,16 @@
//@revisions: stack tree
//@[tree]compile-flags: -Zmiri-tree-borrows
#![feature(unsafe_pinned)]
use std::pin::UnsafePinned;
fn mutate(x: &UnsafePinned<i32>) {
let ptr = x as *const _ as *mut i32;
unsafe { ptr.write(42) };
}
fn main() {
let x = UnsafePinned::new(0);
mutate(&x);
assert_eq!(x.into_inner(), 42);
}

View file

@ -19,11 +19,11 @@ fn fat_ptr_via_local(a: &[u8]) -> &[u8] {
x
}
fn fat_ptr_from_struct(s: FatPtrContainer) -> &[u8] {
fn fat_ptr_from_struct(s: FatPtrContainer<'_>) -> &[u8] {
s.ptr
}
fn fat_ptr_to_struct(a: &[u8]) -> FatPtrContainer {
fn fat_ptr_to_struct(a: &[u8]) -> FatPtrContainer<'_> {
FatPtrContainer { ptr: a }
}