Commit Graph

578 Commits

Author SHA1 Message Date
Jubilee
f54350a9ba
Rollup merge of #122245 - saethlin:check-dep-graph-size, r=petrochenkov
Detect truncated DepGraph files

I suspect that the following issues are caused by truncated incr comp files:

* https://github.com/rust-lang/rust/issues/120582
* https://github.com/rust-lang/rust/issues/121499
* https://github.com/rust-lang/rust/issues/122210

We fail with an allocation failure or capacity overflow in this case because we assume that the ending bytes of an DepGraph file are the lengths of arrays. If the file has somehow been truncated then the ending bytes are probably some of our varint encoding, which tries to eliminate zero bytes, so interpreting a random 8 bytes as an array length has a very high chance of producing a byte capacity over `isize::MAX`.

Now theoretically since https://github.com/rust-lang/rust/pull/119510 merged I have fixed the out-of-disk issues and yet in https://github.com/rust-lang/rust/issues/120894#issuecomment-1945126700 I still see some decoding failures that look like out-of-disk ICEs, for example https://crater-reports.s3.amazonaws.com/beta-1.77-1/beta-2024-02-10/gh/scottfones.aoc_2022/log.txt

So this PR should ensure that we get an ICE that clearly identifies if the file in question is truncated.
2024-03-12 09:04:00 -07:00
bors
094a6204f5 Auto merge of #122064 - Zoxc:dep-graph-encode-tweaks, r=cjgillot
Dep node encoding cleanups

This does some cleanups around dep node encoding.

Performance change with `-Zthreads=2`:

<table><tr><td rowspan="2">Benchmark</td><td colspan="1"><b>Before</b></th><td colspan="2"><b>After</b></th><td colspan="1"><b>Before</b></th><td colspan="2"><b>After</b></th></tr><tr><td align="right">Time</td><td align="right">Time</td><td align="right">%</th><td align="right">Memory</td><td align="right">Memory</td><td align="right">%</th></tr><tr><td>🟣 <b>clap</b>:check:unchanged</td><td align="right">0.4337s</td><td align="right">0.4306s</td><td align="right"> -0.72%</td><td align="right">88.90 MiB</td><td align="right">89.04 MiB</td><td align="right"> 0.15%</td></tr><tr><td>🟣 <b>hyper</b>:check:unchanged</td><td align="right">0.1541s</td><td align="right">0.1528s</td><td align="right"> -0.86%</td><td align="right">51.99 MiB</td><td align="right">52.03 MiB</td><td align="right"> 0.07%</td></tr><tr><td>🟣 <b>regex</b>:check:unchanged</td><td align="right">0.3286s</td><td align="right">0.3248s</td><td align="right">💚  -1.15%</td><td align="right">71.89 MiB</td><td align="right">71.74 MiB</td><td align="right"> -0.21%</td></tr><tr><td>🟣 <b>syn</b>:check:unchanged</td><td align="right">0.6118s</td><td align="right">0.6057s</td><td align="right">💚  -1.01%</td><td align="right">106.59 MiB</td><td align="right">106.66 MiB</td><td align="right"> 0.06%</td></tr><tr><td>🟣 <b>syntex_syntax</b>:check:unchanged</td><td align="right">1.4570s</td><td align="right">1.4463s</td><td align="right"> -0.74%</td><td align="right">197.29 MiB</td><td align="right">197.33 MiB</td><td align="right"> 0.02%</td></tr><tr><td>Total</td><td align="right">2.9852s</td><td align="right">2.9601s</td><td align="right"> -0.84%</td><td align="right">516.66 MiB</td><td align="right">516.80 MiB</td><td align="right"> 0.03%</td></tr><tr><td>Summary</td><td align="right">1.0000s</td><td align="right">0.9911s</td><td align="right"> -0.89%</td><td align="right">1 byte</td><td align="right">1.00 bytes</td><td align="right"> 0.02%</td></tr></table>

r? `@cjgillot`
2024-03-10 06:27:41 +00:00
Ben Kimock
11f8866ca8 Detect truncated incr comp files 2024-03-09 15:43:50 -05:00
John Kåre Alsaker
87ab9e8c6e Some tweaks to the parallel query cycle handler 2024-03-09 00:24:14 +01:00
John Kåre Alsaker
9707e103ea Avoid the double lock around EncoderState 2024-03-06 04:40:39 +01:00
John Kåre Alsaker
a2499bdfbe Remove profiling from intern_node 2024-03-06 04:31:56 +01:00
John Kåre Alsaker
29cc76f0fc Add a profiler reference to GraphEncoder 2024-03-06 04:17:17 +01:00
John Kåre Alsaker
dd0004a129 Don't panic when waiting on poisoned queries 2024-03-02 19:51:56 +01:00
Nicholas Nethercote
260ae70140 Overhaul how stashed diagnostics work, again.
Stashed errors used to be counted as errors, but could then be
cancelled, leading to `ErrorGuaranteed` soundness holes. #120828 changed
that, closing the soundness hole. But it introduced other difficulties
because you sometimes have to account for pending stashed errors when
making decisions about whether errors have occured/will occur and it's
easy to overlook these.

This commit aims for a middle ground.
- Stashed errors (not warnings) are counted immediately as emitted
  errors, avoiding the possibility of forgetting to consider them.
- The ability to cancel (or downgrade) stashed errors is eliminated, by
  disallowing the use of `steal_diagnostic` with errors, and introducing
  the more restrictive methods `try_steal_{modify,replace}_and_emit_err`
  that can be used instead.

Other things:
- `DiagnosticBuilder::stash` and `DiagCtxt::stash_diagnostic` now both
  return `Option<ErrorGuaranteed>`, which enables the removal of two
  `delayed_bug` calls and one `Ty::new_error_with_message` call. This is
  possible because we store error guarantees in
  `DiagCtxt::stashed_diagnostics`.
- Storing the guarantees also saves us having to maintain a counter.
- Calls to the `stashed_err_count` method are no longer necessary
  alongside calls to `has_errors`, which is a nice simplification, and
  eliminates two more `span_delayed_bug` calls and one FIXME comment.
- Tests are added for three of the four fixed PRs mentioned below.
- `issue-121108.rs`'s output improved slightly, omitting a non-useful
  error message.

Fixes #121451.
Fixes #121477.
Fixes #121504.
Fixes #121508.
2024-02-29 11:08:27 +11:00
Nicholas Nethercote
899cb40809 Rename DiagnosticBuilder as Diag.
Much better!

Note that this involves renaming (and updating the value of)
`DIAGNOSTIC_BUILDER` in clippy.
2024-02-28 08:55:35 +11:00
Nicholas Nethercote
6588f5b749 Rename Diagnostic as DiagInner.
I started by changing it to `DiagData`, but that didn't feel right.
`DiagInner` felt much better.
2024-02-28 08:33:25 +11:00
Matthias Krüger
47bf8a6c28
Rollup merge of #121401 - eltociear:patch-25, r=nnethercote
Fix typo in serialized.rs

accomodate -> accommodate
2024-02-22 18:09:54 +01:00
Nicholas Nethercote
46f4983356 Adjust the has_errors* methods.
Currently `has_errors` excludes lint errors. This commit changes it to
include lint errors.

The motivation for this is that for most places it doesn't matter
whether lint errors are included or not. But there are multiple places
where they must be includes, and only one place where they must not be
included. So it makes sense for `has_errors` to do the thing that fits
the most situations, and the new `has_errors_excluding_lint_errors`
method in the one exceptional place.

The same change is made for `err_count`. Annoyingly, this requires the
introduction of `err_count_excluding_lint_errs` for one place, to
preserve existing error printing behaviour. But I still think the change
is worthwhile overall.
2024-02-22 08:03:47 +11:00
Ikko Eltociear Ashimine
00234f0f68
Fix typo in serialized.rs
accomodate -> accommodate
2024-02-22 00:33:23 +09:00
Markus Reiter
746a58d435
Use generic NonZero internally. 2024-02-15 08:09:42 +01:00
Nicholas Nethercote
05849e8c2f Use fewer delayed bugs.
For some cases where it's clear that an error has already occurred,
e.g.:
- there's a comment stating exactly that, or
- things like HIR lowering, where we are lowering an error kind

The commit also tweaks some comments around delayed bug sites.
2024-02-14 20:30:37 +11:00
Nicholas Nethercote
c1ffb0b675 Remove force_print_diagnostic.
There are a couple of places where we call
`inner.emitter.emit_diagnostic` directly rather than going through
`inner.emit_diagnostic`, to guarantee the diagnostic is printed. This
feels dubious to me, particularly the bypassing of `TRACK_DIAGNOSTIC`.

This commit removes those.
- In `print_error_count`, it uses `ForceWarning` instead of `Warning`.
- It removes `DiagCtxtInner::failure_note`, because it only has three
  uses and direct use of `emit_diagnostic` is consistent with other
  similar locations.
- It removes `force_print_diagnostic`, and adds `struct_failure_note`,
  and updates `print_query_stack` accordingly, which makes it more
  normal. That location doesn't seem to need forced printing anyway.
2024-02-14 07:51:53 +11:00
Matthias Krüger
46a0448405
Rollup merge of #120693 - nnethercote:invert-diagnostic-lints, r=davidtwco
Invert diagnostic lints.

That is, change `diagnostic_outside_of_impl` and `untranslatable_diagnostic` from `allow` to `deny`, because more than half of the compiler has been converted to use translated diagnostics.

This commit removes more `deny` attributes than it adds `allow` attributes, which proves that this change is warranted.

r? ````@davidtwco````
2024-02-09 14:41:50 +01:00
Nicholas Nethercote
0ac1195ee0 Invert diagnostic lints.
That is, change `diagnostic_outside_of_impl` and
`untranslatable_diagnostic` from `allow` to `deny`, because more than
half of the compiler has be converted to use translated diagnostics.

This commit removes more `deny` attributes than it adds `allow`
attributes, which proves that this change is warranted.
2024-02-06 13:12:33 +11:00
Michael Goulet
6b2a8249c1 Remove dead args from functions 2024-02-02 22:47:26 +00:00
Nicholas Nethercote
5d9dfbd08f Stop using String for error codes.
Error codes are integers, but `String` is used everywhere to represent
them. Gross!

This commit introduces `ErrCode`, an integral newtype for error codes,
replacing `String`. It also introduces a constant for every error code,
e.g. `E0123`, and removes the `error_code!` macro. The constants are
imported wherever used with `use rustc_errors::codes::*`.

With the old code, we have three different ways to specify an error code
at a use point:
```
error_code!(E0123)  // macro call

struct_span_code_err!(dcx, span, E0123, "msg");  // bare ident arg to macro call

\#[diag(name, code = "E0123")]  // string
struct Diag;
```

With the new code, they all use the `E0123` constant.
```
E0123  // constant

struct_span_code_err!(dcx, span, E0123, "msg");  // constant

\#[diag(name, code = E0123)]  // constant
struct Diag;
```

The commit also changes the structure of the error code definitions:
- `rustc_error_codes` now just defines a higher-order macro listing the
  used error codes and nothing else.
- Because that's now the only thing in the `rustc_error_codes` crate, I
  moved it into the `lib.rs` file and removed the `error_codes.rs` file.
- `rustc_errors` uses that macro to define everything, e.g. the error
  code constants and the `DIAGNOSTIC_TABLES`. This is in its new
  `codes.rs` file.
2024-01-29 07:41:41 +11:00
Nicholas Nethercote
1f9fa2305a Tweak error counting.
We have several methods indicating the presence of errors, lint errors,
and delayed bugs. I find it frustrating that it's very unclear which one
you should use in any particular spot. This commit attempts to instill a
basic principle of "use the least general one possible", because that
reflects reality in practice -- `has_errors` is the least general one
and has by far the most uses (esp. via `abort_if_errors`).

Specifics:
- Add some comments giving some usage guidelines.
- Prefer `has_errors` to comparing `err_count` to zero.
- Remove `has_errors_or_span_delayed_bugs` because it's a weird one: in
  the cases where we need to count delayed bugs, we should really be
  counting lint errors as well.
- Rename `is_compilation_going_to_fail` as
  `has_errors_or_lint_errors_or_span_delayed_bugs`, for consistency with
  `has_errors` and `has_errors_or_lint_errors`.
- Change a few other `has_errors_or_lint_errors` calls to `has_errors`,
  as per the "least general" principle.

This didn't turn out to be as neat as I hoped when I started, but I
think it's still an improvement.
2024-01-22 10:14:01 +11:00
bors
159bdc1e93 Auto merge of #108359 - Zoxc:side-effects-tweak, r=cjgillot
Avoid code generation for ThinVec<Diagnostic>'s destructor in the query system

This avoids 2 instances of the destructor of `ThinVec<Diagnostic>` from being included in `execute_job`. It also outlines the cold branch in `store_side_effects` / `store_side_effects_for_anon_node`.
2024-01-20 15:20:15 +00:00
John Kåre Alsaker
862011e1ca Avoid code generation for ThinVec<Diagnostic>'s destructor in the query system 2024-01-20 13:43:05 +01:00
bors
d3c9082a44 Auto merge of #120006 - cjgillot:no-hir-owner, r=wesleywiser
Get rid of the hir_owner query.

This query was meant as a firewall between `hir_owner_nodes` which is supposed to change often, and the queries that only depend on the item signature. That firewall was inefficient, leaking the contents of the HIR body through `HirId`s.

`hir_owner` incurs a significant cost, as we need to hash HIR twice in multiple modes. This PR proposes to remove it, and simplify the hashing scheme.

For the future, `def_kind`, `def_span`... are much more efficient for incremental decoupling, and should be preferred.
2024-01-19 02:36:13 +00:00
Camille GILLOT
d59968b5f6 Simplify BodyId hashing. 2024-01-16 23:52:30 +00:00
bors
098d4fd74c Auto merge of #119977 - Mark-Simulacrum:defid-cache, r=cjgillot
Cache local DefId-keyed queries without hashing

This caches local DefId-keyed queries using just an IndexVec. This costs ~5% extra max-rss at most but brings significant runtime improvement, up to 13% cycle counts (mean: 4%) on primary benchmarks. It's possible that further tweaks could reduce the memory overhead further but this win seems worth landing despite the increased memory, particularly with regards to eliminating the present set in non-incr or storing it inline (skip list?) with the main data.

We tried applying this scheme to all keys in the [first perf run] but found that it carried a significant memory hit (50%). instructions/cycle counts were also much more mixed, though that may have been due to the lack of the present set optimization (needed for fast iter() calls in incremental scenarios).

Closes https://github.com/rust-lang/rust/issues/45275

[first perf run]: https://perf.rust-lang.org/compare.html?start=30dfb9e046aeb878db04332c74de76e52fb7db10&end=6235575300d8e6e2cc6f449cb9048722ef43f9c7&stat=instructions:u
2024-01-16 21:58:10 +00:00
Mark Rousskov
37849643c6 Cache local DefId-keyed queries without hashing
Foreign maps are used to cache external DefIds, typically backed by
metadata decoding. In the future we might skip caching `V` there (since
loading from metadata usually is already cheap enough), but for now this
cuts down on the impact to memory usage and time to None-init a bunch of
memory. Foreign data is usually much sparser, since we're not usually
loading *all* entries from the foreign crate(s).
2024-01-15 17:16:45 -05:00
Camille GILLOT
c6f83b8ff6 Inline 2 functions that appear in dep-graph profiles. 2024-01-14 12:57:13 +00:00
Nicholas Nethercote
2ea7a37e11 Add DiagCtxt::delayed_bug.
We have `span_delayed_bug` and often pass it a `DUMMY_SP`. This commit
adds `delayed_bug`, which matches pairs like `err`/`span_err` and
`warn`/`span_warn`.
2024-01-10 07:33:07 +11:00
Guillaume Gomez
d3574beb5d
Rollup merge of #119527 - klensy:ordering, r=compiler-errors
don't reexport atomic::ordering via rustc_data_structures, use std import

This looks simpler.
2024-01-09 13:23:17 +01:00
Michael Goulet
82a2215481 Don't check for recursion in generator witness fields 2024-01-08 20:30:21 +00:00
Michael Goulet
755b2da841 Value recovery can take the whole CycleError 2024-01-08 20:30:10 +00:00
Nicholas Nethercote
b1b9278851 Make DiagnosticBuilder::emit consuming.
This works for most of its call sites. This is nice, because `emit` very
much makes sense as a consuming operation -- indeed,
`DiagnosticBuilderState` exists to ensure no diagnostic is emitted
twice, but it uses runtime checks.

For the small number of call sites where a consuming emit doesn't work,
the commit adds `DiagnosticBuilder::emit_without_consuming`. (This will
be removed in subsequent commits.)

Likewise, `emit_unless` becomes consuming. And `delay_as_bug` becomes
consuming, while `delay_as_bug_without_consuming` is added (which will
also be removed in subsequent commits.)

All this requires significant changes to `DiagnosticBuilder`'s chaining
methods. Currently `DiagnosticBuilder` method chaining uses a
non-consuming `&mut self -> &mut Self` style, which allows chaining to
be used when the chain ends in `emit()`, like so:
```
    struct_err(msg).span(span).emit();
```
But it doesn't work when producing a `DiagnosticBuilder` value,
requiring this:
```
    let mut err = self.struct_err(msg);
    err.span(span);
    err
```
This style of chaining won't work with consuming `emit` though. For
that, we need to use to a `self -> Self` style. That also would allow
`DiagnosticBuilder` production to be chained, e.g.:
```
    self.struct_err(msg).span(span)
```
However, removing the `&mut self -> &mut Self` style would require that
individual modifications of a `DiagnosticBuilder` go from this:
```
    err.span(span);
```
to this:
```
    err = err.span(span);
```
There are *many* such places. I have a high tolerance for tedious
refactorings, but even I gave up after a long time trying to convert
them all.

Instead, this commit has it both ways: the existing `&mut self -> Self`
chaining methods are kept, and new `self -> Self` chaining methods are
added, all of which have a `_mv` suffix (short for "move"). Changes to
the existing `forward!` macro lets this happen with very little
additional boilerplate code. I chose to add the suffix to the new
chaining methods rather than the existing ones, because the number of
changes required is much smaller that way.

This doubled chainging is a bit clumsy, but I think it is worthwhile
because it allows a *lot* of good things to subsequently happen. In this
commit, there are many `mut` qualifiers removed in places where
diagnostics are emitted without being modified. In subsequent commits:
- chaining can be used more, making the code more concise;
- more use of chaining also permits the removal of redundant diagnostic
  APIs like `struct_err_with_code`, which can be replaced easily with
  `struct_err` + `code_mv`;
- `emit_without_diagnostic` can be removed, which simplifies a lot of
  machinery, removing the need for `DiagnosticBuilderState`.
2024-01-08 15:24:49 +11:00
klensy
56173611d6 don't reexport atomic::ordering via rustc_data_structures, use std import 2024-01-06 15:01:10 +03:00
León Orell Valerian Liehr
093bd0888a
Rollup merge of #119086 - RossSmyth:query_panics, r=compiler-errors
Query panic!() to useful diagnostic

Changes some more ICEs from bare panic!()s

Adds an `expect_job()` helper method as that is a moral equivalent of what was happening at the uses.

re:#118955
2024-01-03 16:08:23 +01:00
Ross Smyth
0d421c5ace Add useful panic messages if queries fail to start 2024-01-03 09:51:58 -05:00
bors
2271c26e4a Auto merge of #119146 - nnethercote:rm-DiagCtxt-api-duplication, r=compiler-errors
Remove `DiagCtxt` API duplication

`DiagCtxt` defines the internal API for creating and emitting diagnostics: methods like `struct_err`, `struct_span_warn`, `note`, `create_fatal`, `emit_bug`. There are over 50 methods.

Some of these methods are then duplicated across several other types: `Session`, `ParseSess`, `Parser`, `ExtCtxt`, and `MirBorrowckCtxt`. `Session` duplicates the most, though half the ones it does are unused. Each duplicated method just calls forward to the corresponding method in `DiagCtxt`. So this duplication exists to (in the best case) shorten chains like `ecx.tcx.sess.parse_sess.dcx.emit_err()` to `ecx.emit_err()`.

This API duplication is ugly and has been bugging me for a while. And it's inconsistent: there's no real logic about which methods are duplicated, and the use of `#[rustc_lint_diagnostic]` and `#[track_caller]` attributes vary across the duplicates.

This PR removes the duplicated API methods and makes all diagnostic creation and emission go through `DiagCtxt`. It also adds `dcx` getter methods to several types to shorten chains. This approach scales *much* better than API duplication; indeed, the PR adds `dcx()` to numerous types that didn't have API duplication: `TyCtxt`, `LoweringCtxt`, `ConstCx`, `FnCtxt`, `TypeErrCtxt`, `InferCtxt`, `CrateLoader`, `CheckAttrVisitor`, and `Resolver`. These result in a lot of changes from `foo.tcx.sess.emit_err()` to `foo.dcx().emit_err()`. (You could do this with more types, but it gets into diminishing returns territory for types that don't emit many diagnostics.)

After all these changes, some call sites are more verbose, some are less verbose, and many are the same. The total number of lines is reduced, mostly because of the removed API duplication. And consistency is increased, because calls to `emit_err` and friends are always preceded with `.dcx()` or `.dcx`.

r? `@compiler-errors`
2023-12-26 02:24:39 +00:00
bors
bf8716f1cd Auto merge of #119139 - michaelwoerister:cleanup-stable-source-file-id, r=cjgillot
Unify SourceFile::name_hash and StableSourceFileId

This PR adapts the existing `StableSourceFileId` type so that it can be used instead of the `name_hash` field of `SourceFile`. This simplifies a few things that were kind of duplicated before.

The PR should also fix issues https://github.com/rust-lang/rust/issues/112700 and https://github.com/rust-lang/rust/issues/115835, but I was not able to reproduce these issues in a regression test. As far as I can tell, the root cause of these issues is that the id of the originating crate is not hashed in the `HashStable` impl of `Span` and thus cache entries that should have been considered invalidated were loaded. After this PR, the `stable_id` field of `SourceFile` includes information about the originating crate, so that ICE should not occur anymore.
2023-12-24 21:58:39 +00:00
Nicholas Nethercote
8a9db25459 Remove more Session methods that duplicate DiagCtxt methods. 2023-12-24 08:17:47 +11:00
Nicholas Nethercote
99472c7049 Remove Session methods that duplicate DiagCtxt methods.
Also add some `dcx` methods to types that wrap `TyCtxt`, for easier
access.
2023-12-24 08:05:28 +11:00
Nicholas Nethercote
757d6f6ef8 Give DiagnosticBuilder a default type.
`IntoDiagnostic` defaults to `ErrorGuaranteed`, because errors are the
most common diagnostic level. It makes sense to do likewise for the
closely-related (and much more widely used) `DiagnosticBuilder` type,
letting us write `DiagnosticBuilder<'a, ErrorGuaranteed>` as just
`DiagnosticBuilder<'a>`. This cuts over 200 lines of code due to many
multi-line things becoming single line things.
2023-12-23 13:23:10 +11:00
Michael Woerister
fa8ef25372 Unify SourceFile::name_hash and StableSourceFileId 2023-12-19 22:34:26 +01:00
Nicholas Nethercote
cea683c08f Use .into_diagnostic() less.
This commit replaces this pattern:
```
err.into_diagnostic(dcx)
```
with this pattern:
```
dcx.create_err(err)
```
in a lot of places.

It's a little shorter, makes the error level explicit, avoids some
`IntoDiagnostic` imports, and is a necessary prerequisite for the next
commit which will add a `level` arg to `into_diagnostic`.

This requires adding `track_caller` on `create_err` to avoid mucking up
the output of `tests/ui/track-diagnostics/track4.rs`. It probably should
have been there already.
2023-12-18 20:46:13 +11:00
Nicholas Nethercote
f6aa418c9f Rename many DiagCtxt and EarlyDiagCtxt locals. 2023-12-18 16:06:22 +11:00
Nicholas Nethercote
f422dca3ae Rename many DiagCtxt arguments. 2023-12-18 16:06:22 +11:00
Nicholas Nethercote
09af8a667c Rename Session::span_diagnostic as Session::dcx. 2023-12-18 16:06:21 +11:00
Nicholas Nethercote
cde19c016e Rename Handler as DiagCtxt. 2023-12-18 16:06:19 +11:00
Jubilee
9e872b7cd8
Rollup merge of #118933 - nnethercote:cleanup-errors-even-more, r=compiler-errors
Cleanup errors handlers even more

A sequel to #118587.

r? `@compiler-errors`
2023-12-14 16:07:48 -08:00
Nicholas Nethercote
9a78412511 Split Handler::emit_diagnostic in two.
Currently, `emit_diagnostic` takes `&mut self`.

This commit changes it so `emit_diagnostic` takes `self` and the new
`emit_diagnostic_without_consuming` function takes `&mut self`.

I find the distinction useful. The former case is much more common, and
avoids a bunch of `mut` and `&mut` occurrences. We can also restrict the
latter with `pub(crate)` which is nice.
2023-12-15 10:13:12 +11:00