Skip to content

Commit c61a38e

Browse files
committed
Clarify std plans
1 parent c894567 commit c61a38e

File tree

1 file changed

+100
-45
lines changed

1 file changed

+100
-45
lines changed

text/0000-portability-lint.md

Lines changed: 100 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -396,17 +396,9 @@ determination about how to set up the standard library.
396396
The "mainstream platform" will be expressed via a new primitive `cfg` pattern
397397
called `std`. This is the **default portability of all crates**, unless
398398
opted-out (see below on "subsetting `std`"). Likewise, most items in `std` will
399-
initially be exported at `std` portability level. These two facts together mean
400-
that existing uses of `std` will continue to work without issuing any warnings.
401-
402-
The `std` portability will include several implications, e.g.:
403-
404-
- `std` implies `any(windows, macos, linux)`
405-
- `std` implies `any(target_pointer_width = "32", target_pointer_width = "64")`
406-
407-
and so on. That means, in particular, that a `match_cfg` expression that covers
408-
*all* of Windows, macOS and Linux will be considered to have "mainstream
409-
portability" automatically.
399+
*initially* be exported at `std` portability level (but see subsets
400+
below). These two facts together mean that existing uses of `std` will continue
401+
to work without issuing any warnings.
410402

411403
### Expanding `std`
412404

@@ -424,7 +416,7 @@ impl File {
424416
```
425417

426418
and the portability of `as_raw_fd` will be `all(std, unix)`. Thus, any code
427-
using `as_raw_fd` will need to be in a `unix` context.
419+
using `as_raw_fd` will need to be in a `unix` context in particular.
428420

429421
We can thus deprecate the `std::os` module in favor of these in-place
430422
APIs. Doing so leverages the fact that we're using a portability *lint*: these
@@ -433,42 +425,75 @@ generate new warnings, but this is considered an acceptable change. After all,
433425
lints on dependencies are automatically capped, and the lint will not prevent
434426
code from compiling--and can be silenced.
435427

436-
Expanding to include new atomics, SIMD, and other desired extensions should
437-
amount to a straightforward use of `cfg`.
428+
For hardware features like additional atomics or SIMD, we can use the
429+
`target_feature` cfg key to label the APIs -- which has to be done anyway, but
430+
will also do the right thing for the lint.
431+
432+
In short, for expansions there's basically nothing to do. You just add the API
433+
in its natural location, with its natural `cfg`, and everything works out.
438434

439435
### Subsetting `std`
440436

441-
What about subsets of `std` (or `core`)? First of all, if you apply `cfg` to
442-
your *crate* definition, you opt out of the default `std` portability level in
443-
favor of the `cfg` you write. Doing so will deny access to many APIs in `std`.
444-
445-
Over time, APIs within `std` and `core` will be labeled with new, more narrow
446-
portabilities. Let's take, for example, threading--which we wish to not provide
447-
on platforms like Emscripten. The `std` threading APIs might be revised to use
448-
`cfg(threads)`, rather than `cfg(std)`; at the same time, `std` would be set up
449-
to imply `threads`, so that no new warnings would be generated. To check for
450-
compatibility with Emscripten, you can opt out of the `std` scenario, and avoid
451-
opting into `threads` (or use `match_cfg` if you want to do so only optionally,
452-
for example to use optional parallelism).
453-
454-
Similarly, `libcore` can be annotated with new `cfg`s, like `cfg(float)` for
455-
floating point support.
456-
457-
Thus, library authors shooting for maximal portability should opt out of
458-
`cfg(std)`, and use `cfg` as little as possible. And over time, we can allow
459-
increasingly fine-grained subsets of `std` by introducing new `cfg` flags.
460-
461-
## Backwards compatibility and lint evolution
462-
463-
The fact that the portability lint is a *lint* gives us a lot of
464-
flexibility. Take, for example, the assumption that `std` implies `any(windows,
465-
macos, linux)`. Conceivably, we may want to add more mainstream platforms in the
466-
future. Doing so may generate new warnings--particularly for people who had used
467-
`match_cfg` previously to exhaustively match against these cases. But (1) it's
468-
merely a new *warning*, which is fine to introduce, and can be silenced; (2)
469-
this is actually a highly desirable outcome if we ever did add a new mainstream
470-
platform, since existing code would get a heads-up that it may not longer be
471-
compatible with all mainstream platforms.
437+
What about subsets of `std`?
438+
439+
**What use case do we want to address?** Going back to the Portability Goals
440+
discussed earlier, the goal of subsetting `std` is mostly about helping people
441+
who want *maximum portability*. For this use case, you should opt out of the
442+
mainstream platform, and then *whitelist* the various features you need, thus
443+
giving you assistance in using the minimal set of assumptions needed.
444+
445+
**Opting out of the mainstream platform**. To opt out of the `std` platform, you
446+
can just apply a `cfg` to your *crate* definition. The assumptions of that `cfg`
447+
will form the baseline for the crate.
448+
449+
**Carving up `std` into whitelistable features**. When we want to provide
450+
subsets of `std`, we can introduce a new set of target features, along the
451+
following lines:
452+
453+
- each integer size
454+
- each float size
455+
- each atomics size
456+
- allocation
457+
- OS facilities
458+
- env
459+
- fs
460+
- net
461+
- process
462+
- thread
463+
- rng
464+
465+
**To introduce these features, we would change APIs in `std` from being marked as
466+
`#[cfg(std)]` to instead being labeled with the particular feature**, e.g.:
467+
468+
```rust
469+
// previously: #[cfg(std)]
470+
#[cfg(target_feature = "thread")]
471+
mod thread;
472+
473+
// previously: #[cfg(std)]
474+
#[cfg(target_feature = "fs")]
475+
mod fs;
476+
```
477+
478+
and so on. We can then set up axioms such that `std` *implies* all of these
479+
features. That way existing code written at the default portability level will
480+
not produce warnings when using the standard library. And in general, we can
481+
carve out increasingly fine-grained subsets, setting up implications between the
482+
previous coarse-grained features and the new subsets.
483+
484+
On the other side, library authors shooting for maximal portability should opt
485+
out of `cfg(std)`, and use `cfg` as little as possible, adding features to their
486+
whitelist only after deciding they're truly needed, or abstracting over them
487+
(such as using threading for parallelism only when it was available).
488+
489+
## Proposed rollout
490+
491+
The most pressing problem in `std` is the desire for expansion, rather than
492+
subsetting, so we should start there. The `cfg` needed for expansion is totally
493+
straightforward, and will allow us to gain experience with the lint.
494+
495+
Later, we can start exploring subsets of `std`, which will likely require some
496+
more thoughtful design to find the right granularity.
472497

473498
# Drawbacks
474499
[drawbacks]: #drawbacks
@@ -527,6 +552,14 @@ the scenarios that arise in practice.
527552
# Unresolved questions
528553
[unresolved]: #unresolved-questions
529554

555+
### Extensions to `cfg` itself
556+
557+
If we allow `cfg` to go beyond simple key-value pairs, for example to talk about
558+
ranges, we will need to accommodate that somehow in the lint. One plausible
559+
approach would be to use something more like SMT solving, which incorporates
560+
reasoning about things like ordering constraints in addition to basic SAT
561+
questions.
562+
530563
### External libraries
531564

532565
It's not clear what the story should be for a library like `libc`, which
@@ -538,8 +571,30 @@ approach such cases before landing the RFC.
538571
To what extent does this proposal obviate the need for the `std` facade? Might
539572
it be possible to deprecate `libcore` in favor of the "subsetting `std`" approach?
540573

574+
### Cargo features
575+
576+
It's unclear whether, or how, to extend this approach to deal with Cargo
577+
features. In particular, features are namespaced per crate, so there's no way to
578+
use the `cfg` system today to talk about upstream features.
579+
541580
# Appendix: possible extensions
542581

582+
## Subsetting `std`
583+
584+
585+
cfgs:
586+
587+
- each integer size
588+
- each float size
589+
- each atomics size
590+
- allocation
591+
- env
592+
- fs
593+
- net
594+
- process
595+
- thread
596+
- OS rng
597+
543598
## `match_cfg`
544599

545600
The original version of this RFC was more expansive, and proposed a `match_cfg`

0 commit comments

Comments
 (0)