Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add minicore test auxiliary and support //@ use-minicore directive in ui/assembly/codegen tests #130693

Draft
wants to merge 12 commits into
base: master
Choose a base branch
from

Conversation

jieyouxu
Copy link
Member

@jieyouxu jieyouxu commented Sep 22, 2024

Context: Real cross-compiling tests instead of #![no_core] silliness #130375
MCP: rust-lang/compiler-team#786
Tracking issue: #131485

This prototype PR is subject to further changes based on feedback.

New minicore test auxiliary and //@ use-minicore compiletest directive

This PR introduces a prototype implementation of a minicore auxiliary test helper that provides core prelude stubs for #![no_core] ui/assembly/codegen tests that need to build but not run on both the host and the cross-compiled target.

Key summary:

  • minicore contains stub definitions of core items intended for consumption by check-pass/build-pass tests that want the typical prelude items like Copy to be stubbed out under #![no_core] scenarios, so that the test can be built (not run) for cross-compiled target. Such tests don't want nor need full -Z build-std (e.g. tests/ui/abi/compatibility.rs).
  • minicore is intended for core items only, not std-only or alloc items. If stubs for alloc or std are wanted, they should be provided by an additional directive and test auxiliary, and not be conflated with minicore or core stubs. This is because a wider range of tests can benefit from core-only stubs.

Implementation

  • The minicore auxiliary is a single source file tests/auxiliary/minicore.rs.
  • The path to minicore is made avaiable from bootstrap to compiletest via the --minicore-path compiletest flag.
  • minicore is then built on-demand via the //@ use-minicore compiletest directive, for each test revision for the given target (this distinction is important for when host != target in cross-compilation scenario).
  • minicore is then made available to the test as an extern prelude.

Example usage

// tests/ui/abi/my-abi-test.rs

//@ check-pass
//@ use-minicore
//@ revisions: i686
//@[i686] compile-flags: --target i686-unknown-linux-gnu
//@[i686] needs-llvm-components: x86

#![feature(no_core, lang_items)]
#![no_std]
#![no_core]
#![allow(unused, internal_features)]

extern crate minicore;
use minicore::*;

#[lang = "clone"]
pub trait Clone: Sized {      // `Sized` is provided by `minicore`
    fn clone(&self) -> Self;
}

Implementation steps

  • 1. Add an initial minicore test auxiliary.
  • 2. Build minicore in bootstrap.
  • 3. Setup a --minicore-path compiletest cli flag and pass minicore build artifact path from bootstrap to compiletest.
  • 4. Assert use-minicore is mutually incompatible with tests that require to be run, as the stubs are only good for tests that only need to be built (i.e. no run-{pass,fail}).
  • 5. Add some self-tests to sanity check the behavior.
  • 6. Ensure that tests/auxiliary/minicore.rs is input stamped, i.e. modifying tests/auxiliary/minicore.rs should invalidate test cache and force the test to be rerun.

try-job: aarch64-apple
try-job: armhf-gnu
try-job: x86_64-msvc
try-job: test-various
try-job: dist-various-1

@rustbot rustbot added A-testsuite Area: The testsuite used to check the correctness of rustc S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Sep 22, 2024
@jieyouxu jieyouxu added needs-mcp This change is large enough that it needs a major change proposal before starting work. A-compiletest Area: The compiletest test runner S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 22, 2024
@jieyouxu
Copy link
Member Author

@bors try

bors added a commit to rust-lang-ci/rust that referenced this pull request Sep 22, 2024
[PROTOTYPE] Add minicore test auxiliary and support `//@ use-minicore` directive in ui/assembly/codegen tests

**TODO: work in progress prototype implementation, opened early for MCP to reference**

Context: [Real cross-compiling tests instead of `#![no_core]` silliness rust-lang#130375](rust-lang#130375)

This PR introduces a prototype implementation of `minicore` auxiliary test helper. `minicore` contains stub definitions of std/core prelude items intended for consumption by tests that want the typical prelude items like `Copy` or `Result` in cross-compilation scenarios, but don't want nor need full `-Z build-std` (e.g. `tests/ui/abi/compatibility.rs`).

The `minicore` auxiliary is a single source file `tests/auxiliary/minicore.rs`. The path to this auxiliary is made avaiable from bootstrap to compiletest via the `--minicore-path` compiletest flag. The `minicore` auxiliary is then built, on demand via `//@ use-minicore` compiletest directives, for each test revision for the given target (this distinction is important for when host != target in cross-compilation scenario).

### Implementation steps

- [ ] 1. File an MCP to describe `tests/auxiliary/minicore.rs`, `--minicore-path` compiletest flag, `//@ use-minicore` compiletest directive and the behavior.
- [ ] 2. And some self-tests to sanity check the behavior.
- [ ] 3. Update rustc-dev-guide to describe the new `use-minicore` directive and provide an example, noting that `use-minicore` both requires `no_std` + `no_core` and implies `-C panic=abort` for the test file.

r? `@ghost` (not yet ready for full review, still needs some self-tests and some try-jobs)

(TODO: cc interested people once this passes initial try jobs in cross-compilation scenarios)
cc `@/workingjubilee` `@/RalfJung` `@/nikic` `@/chrisnc` (if this makes sense to you in terms of functionality and UX)

try-job: aarch64-apple
try-job: armhf-gnu
try-job: x86_64-msvc
try-job: test-various
try-job: dist-various-1
@bors
Copy link
Contributor

bors commented Sep 22, 2024

⌛ Trying commit 9d6f01a with merge 01b2fff...

@jieyouxu jieyouxu added S-waiting-on-MCP Status: PR has a compiler MCP and is waiting for the compiler MCP to complete. and removed needs-mcp This change is large enough that it needs a major change proposal before starting work. labels Sep 22, 2024
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@bors
Copy link
Contributor

bors commented Sep 22, 2024

💔 Test failed - checks-actions

pub struct ManuallyDrop<T: ?Sized> {
value: T,
}
impl<T: Copy + ?Sized> Copy for ManuallyDrop<T> {}
Copy link
Member

@bjorn3 bjorn3 Sep 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can derive Copy if you add

#[rustc_builtin_macro]
pub macro Copy($item:item) {
    /* compiler built-in */
}

and the same applies to Clone.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not make this change because I couldn't figure out how to make use of this, just kept manual impl Copy for ... for now.

tests/auxiliary/minicore.rs Outdated Show resolved Hide resolved
tests/auxiliary/minicore.rs Outdated Show resolved Hide resolved
@jieyouxu
Copy link
Member Author

jieyouxu commented Sep 28, 2024

Changes since last review:

  • Fixed bootstrap path of tests/auxiliary/minicore.rs to be constructed from a builder.src prefix.
  • Trimmed minicore.rs to only include a minimal subset of core items, excluding the ones that are easy to abuse like #[rustc_layout_scalar_valid_range_start(1)] or #[rustc_nonnull_optimization_guaranteed].
  • Made sure no alloc or std-specific items get included in minicore.
  • Removed cfg(host) and cfg(non(host)) distinction in tests/abi/compatibility.rs, and just unconditionally stub out core items. Not sure if this is desirable.
  • Fixed minicore doc comments to no longer mention std, instead emphasize core items only. Also added remarks about rustc-attribute-annotated core items.

@jieyouxu jieyouxu force-pushed the minicore branch 2 times, most recently from f4a7c38 to 8bd9644 Compare September 28, 2024 14:19
src/bootstrap/src/core/build_steps/test.rs Outdated Show resolved Hide resolved
src/tools/compiletest/src/common.rs Outdated Show resolved Hide resolved
src/tools/compiletest/src/header.rs Outdated Show resolved Hide resolved
@@ -1129,6 +1135,37 @@ impl<'test> TestCx<'test> {
)
}

/// Builds `minicore`. Returns the path to the minicore rlib within the base test output
/// directory.
fn build_minicore(&self) -> PathBuf {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remark: I don't like how convoluted the top-level runtest logic is, but intended for future compiletest cleanup PRs instead of this PR.

Comment on lines 1460 to 1461
// `use-minicore` requires `#![no_std]` and `#![no_core]`, which means no unwinding panics.
if self.props.use_minicore {
rustc.arg("-Cpanic=abort");
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: I need to document //@ use-minicore implies and mandates -C panic=abort for the test in rustc-dev-guide.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 1460 to 1459
// `use-minicore` requires `#![no_std]` and `#![no_core]`, which means no unwinding panics.
if self.props.use_minicore {
Copy link
Member Author

@jieyouxu jieyouxu Sep 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remark: technically, this also needs to check and ensure that the test writer does not manually override -C panic=xxx where xxx != abort, but I would prefer that happen after untangling how directives are handled. The flag is now set in the final position, which will override custom user compile-flags.

@jieyouxu jieyouxu changed the title [PROTOTYPE] Add minicore test auxiliary and support //@ use-minicore directive in ui/assembly/codegen tests Add minicore test auxiliary and support //@ use-minicore directive in ui/assembly/codegen tests Sep 28, 2024
@@ -1,5 +1,5 @@
//@ check-pass
//@ revisions: host
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can still have the host revision, we just don't want all the cfg(host) stuff any more and instead use the minicore approach for all revisions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are there two of these smoke tests?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because in compiletest, different test suites have different code paths through the complicated runtest mechanism...

//! Basic smoke test for `minicore` test auxiliary.

//@ use-minicore
//@ revisions: meow
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When there's just a single revision, why use the revision system at all?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, this was there during testing, I forgot I only had one revision left.

@jieyouxu
Copy link
Member Author

Hm fair -- I had not considered that. Maybe aux-crate is indeed better then. I don't know about that mechanism, how does it differ from aux-build?

aux-crate is a bit different in that it accepts the --extern syntax like foo=path/to/file.rs, and makes foo available via extern prelude.

@workingjubilee
Copy link
Member

more concretely, for either include! or aux-crate or anything else, I wouldn't want to have to rewrite the path used every time I move a test, unless it's out of a given suite entirely (at which point I probably have to rewrite the test anyways, but even if I didn't).

so we'd want something like the variable-controlled filepath Urgau mentioned.

@jieyouxu
Copy link
Member Author

jieyouxu commented Oct 12, 2024

I don't think it's very easy to reuse existing aux-crate/aux-build logic.

If I use something like

//@ aux-crate: minicore={{rust-src-base}}/../auxiliary/minicore.rs

The build mechanism related to that normalization + directive spontaneously combusts and instead outputs to test sources directory tests/auxiliary/minicore.<revision_name>/.

If I use

//@ aux-crate: minicore=../../../auxiliary/minicore.rs

compiletest can find minicore.rs, however it dies at extern minicore; because something is off about the --extern configuration aux-crate setup uses. EDIT: oh it's because minicore rlib is under compatibility.x86-64-win/auxiliary but then ../../../auxiliary/minicore sends it somewhere completely unrelated. Incredible.

error: extern location for minicore does not exist: E:\Repos\rust\build\x86_64-pc-windows-msvc\test\ui\abi\compatibility.x86-64-win\auxiliary/../../../auxiliary/minicore.dll
  --> E:\Repos\rust\tests\ui\abi\compatibility.rs:71:1
   |
LL | extern crate minicore;
   | ^^^^^^^^^^^^^^^^^^^^^^

... I'll come back to this later.

@jieyouxu jieyouxu added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Oct 12, 2024
@jieyouxu
Copy link
Member Author

I might temporarily shelve this unless we just use the //@ use-minicore magic (because I find that easier implementation-wise, I still haven't figured out why aux-crate is doing what it's doing), or maybe wait until we manage to tame the compiletest internals (e.g. ideas in https://rust-lang.zulipchat.com/#narrow/stream/326414-t-infra.2Fbootstrap/topic/.28Rubberducking.29.20compiletest.20test.20discovery.20.2F.20directives).

@jieyouxu jieyouxu added S-blocked Status: Marked as blocked ❌ on something else such as an RFC or other implementation work. S-experimental Status: Ongoing experiment that does not require reviewing and won't be merged in its current state. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Oct 12, 2024
@jieyouxu jieyouxu marked this pull request as draft October 12, 2024 23:24
@RalfJung
Copy link
Member

Given that the alternatives have been considered, a new directive seems fine for me. But let's see what t-compiler says. :)

@RalfJung RalfJung added the I-compiler-nominated Nominated for discussion during a compiler team meeting. label Oct 13, 2024
@Urgau
Copy link
Member

Urgau commented Oct 13, 2024

Note that if we go with include!, then each test that want to use minicore.rs will have to copy-pasta the include! decl because we don't have core

It's unfortunate, but given that //@ aux-crate is unusable for our needs, I think it's an acceptable trade-off given the currently involved and complex //@ use-minicore alternative.

I would propose that we go with this:

// usage of `minicore.rs` in every test that want to use it

#[rustc_builtin_macro]
macro_rules! include { ($file:expr $(,)?) => {{ /* compiler built-in */ }}; }

include!("../../auxiliary/minicore.rs");

and we ensure -Cpanic=abort with #[cfg(not(panic = "abort"))] as proposed earlier.

@workingjubilee
Copy link
Member

You forgot several lines:

#![no_core]
#![crate_type = "lib"]
#![feature(rustc_attrs)]
#![feature(no_core)]
#![feature(lang_items)]
#![allow(internal_features)]

@workingjubilee
Copy link
Member

Remember: #66920

@Urgau
Copy link
Member

Urgau commented Oct 14, 2024

You forgot several lines:

Sure, but they also need to be included with all the other proposal I think, so using include! doesn't change this.

@RalfJung
Copy link
Member

RalfJung commented Oct 14, 2024 via email

@Urgau
Copy link
Member

Urgau commented Oct 14, 2024

I still see (some) of those lines in the current minicore-smoke-test.rs file with //@ use-minicore (which compiles minicore as a separate crate):

https://github.com/jieyouxu/rust/blob/90b6aae363cd7418ead533102204814808093e22/tests/codegen/compiletest-self-test/minicore-smoke-test.rs#L8-L11

(but there would probably be fewer of them)

(and while I don't like //@ use-minicore, it's still an acceptable solution, just not my favorite)

@jieyouxu
Copy link
Member Author

jieyouxu commented Oct 17, 2024

Summary for T-compiler triage

minicore is a test auxiliary containing core stubs for cross-compiling #![no_core] ui/codegen/assembly tests. This is to help #![no_core] tests avoid needing to reinvent core stubs in every test. We are asking T-compiler to decide on an approach on how to implement minicore that's the most convenient/ergonomic for test writers.

Context: Real cross-compiling tests instead of #![no_core] silliness #130375
MCP: rust-lang/compiler-team#786
Tracking issue: #131485

Candidate Approaches

Approach 1: special //@ use-minicore directive

Add a special compiletest directive //@ use-minicore permitted for check/build (but not run) pass mode ui/codegen/assembly tests. This will build minicore as a crate and makes it available as an extern prelude to the test. //@ use-minicore will imply -C panic=abort since the test needs to be #![no_core] anyway.

Toy example:

//@ check-pass
//@ use-minicore
//@ compile-flags: --target i686-unknown-linux-gnu
//@ needs-llvm-components: x86

#![feature(no_core)]
#![allow(internal_features)]
#![no_core]
#![crate_type = "lib"]

extern crate minicore;
use minicore::*;

struct Zst;
impl Copy for Zst {}

// CHECK-LABEL: foo
#[no_mangle]
fn foo(_x: Zst) {}

Approach 2: include!("../../../minicore.rs")

Just use the good-ol' include! macro.

Toy example:

//@ check-pass
//@ compile-flags: -Cpanic=abort
//@ compile-flags: --target i686-unknown-linux-gnu
//@ needs-llvm-components: x86

#![crate_type = "lib"]
#![feature(no_core, rustc_attrs, lang_items)]
#![no_core]
#![allow(internal_features)]

// Needed in each test because we don't have `include!` otherwise.
#[rustc_builtin_macro]
macro_rules! include { ($file:expr $(,)?) => {{ /* compiler built-in */ }}; }

include!("../../auxiliary/minicore.rs");

struct Zst;
impl Copy for Zst {}

// CHECK-LABEL: foo
#[no_mangle]
fn foo(_x: Zst) {}

Approach 3: retrofit aux-crate directive

compiletest currently has aux-crate aux build directives and associated logic. However, they don't handle relative paths well nor special path remapping like //@ aux-crate: minicore={{MINICORE_PATH}} well. They also assume an additional auxiliary/ subdirectory that current tests rely on. compilest currently has some bugs around auxiliaries as well. See #130693 (comment) for experimental history.

If these bugs are fixed, then the following will probably be what it looks like:

Toy example:

//@ check-pass
//@ aux-crate: minicore={{MINICORE_PATH}}
//@ compile-flags: -Cpanic=abort
//@ compile-flags: --target i686-unknown-linux-gnu
//@ needs-llvm-components: x86

#![crate_type = "lib"]
#![feature(no_core, rustc_attrs, lang_items)]
#![no_core]
#![allow(internal_features)]

extern crate minicore;
use minicore::*;

struct Zst;
impl Copy for Zst {}

// CHECK-LABEL: foo
#[no_mangle]
fn foo(_x: Zst) {}

Remarks

If minicore is a separate crate, those lines will not be needed in the test.

@RalfJung The bare minimum crate-level attribute set of

#![crate_type = "lib"]
#![feature(no_core)]
#![no_core]
#![allow(internal_features)]

is needed in the test file itself, regardless of whichever approach we take here (outside of literal copy-paste à la #include <minicore.h>) because #![no_core] is crate-local and isn't "infectious". So it's needed by the test itself in any case.

@jieyouxu jieyouxu added S-waiting-on-team Status: Awaiting decision from the relevant subteam (see the T-<team> label). and removed S-blocked Status: Marked as blocked ❌ on something else such as an RFC or other implementation work. S-experimental Status: Ongoing experiment that does not require reviewing and won't be merged in its current state. labels Oct 17, 2024
@apiraino
Copy link
Contributor

Visited during T-compiler triage meeting on Zulip, discussion can continue (asynchronously) here.

@rustbot label -I-compiler-nominated

@rustbot rustbot removed the I-compiler-nominated Nominated for discussion during a compiler team meeting. label Oct 17, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-compiletest Area: The compiletest test runner A-test-infra Area: test infrastructure (may span bootstrap/compiletest/more) A-testsuite Area: The testsuite used to check the correctness of rustc S-waiting-on-team Status: Awaiting decision from the relevant subteam (see the T-<team> label). T-bootstrap Relevant to the bootstrap subteam: Rust's build system (x.py and src/bootstrap) T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
Status: In progress
Development

Successfully merging this pull request may close these issues.

10 participants