The Generics now contain one Vec of an enum for the generic parameters,
rather than two separate Vec's for lifetime and type parameters.
Additionally, places that previously used Vec<LifetimeDef> now use
Vec<GenericParam> instead.
Use hir::ItemLocalId as keys in TypeckTables.
This PR makes `TypeckTables` use `ItemLocalId` instead of `NodeId` as key. This is needed for incremental compilation -- for stable hashing and for being able to persist and reload these tables. The PR implements the most important part of https://github.com/rust-lang/rust/issues/40303.
Some notes on the implementation:
* The PR adds the `HirId` to HIR nodes where needed (`Expr`, `Local`, `Block`, `Pat`) which obviates the need to store a `NodeId -> HirId` mapping in crate metadata. Thanks @eddyb for the suggestion! In the future the `HirId` should completely replace the `NodeId` in HIR nodes.
* Before something is read or stored in one of the various `TypeckTables` subtables, the entry's key is validated via the new `TypeckTables::validate_hir_id()` method. This makes sure that we are not mixing information from different items in a single table.
That last part could be made a bit nicer by either (a) new-typing the table-key and making `validate_hir_id()` the only way to convert a `HirId` to the new-typed key, or (b) just encapsulate sub-table access a little better. This PR, however, contents itself with not making things significantly worse.
Also, there's quite a bit of switching around between `NodeId`, `HirId`, and `DefIndex`. These conversions are cheap except for `HirId -> NodeId`, so if the valued reviewer finds such an instance in a performance critical place, please let me know.
Ideally we convert more and more code from `NodeId` to `HirId` in the future so that there are no more `NodeId`s after HIR lowering anywhere. Then the amount of switching should be minimal again.
r? @eddyb, maybe?
In preparation for incremental compilation this commit refactors the lint
handling infrastructure in the compiler to be more "eager" and overall more
incremental-friendly. Many passes of the compiler can emit lints at various
points but before this commit all lints were buffered in a table to be emitted
at the very end of compilation. This commit changes these lints to be emitted
immediately during compilation using pre-calculated lint level-related data
structures.
Linting today is split into two phases, one set of "early" lints run on the
`syntax::ast` and a "late" set of lints run on the HIR. This commit moves the
"early" lints to running as late as possible in compilation, just before HIR
lowering. This notably means that we're catching resolve-related lints just
before HIR lowering. The early linting remains a pass very similar to how it was
before, maintaining context of the current lint level as it walks the tree.
Post-HIR, however, linting is structured as a method on the `TyCtxt` which
transitively executes a query to calculate lint levels. Each request to lint on
a `TyCtxt` will query the entire crate's 'lint level data structure' and then go
from there about whether the lint should be emitted or not.
The query depends on the entire HIR crate but should be very quick to calculate
(just a quick walk of the HIR) and the red-green system should notice that the
lint level data structure rarely changes, and should hopefully preserve
incrementality.
Overall this resulted in a pretty big change to the test suite now that lints
are emitted much earlier in compilation (on-demand vs only at the end). This in
turn necessitated the addition of many `#![allow(warnings)]` directives
throughout the compile-fail test suite and a number of updates to the UI test
suite.
Previously, conflicting forbid/allow attributes for a lint group would
result in a separate "allow(L) overruled by outer forbid(L)" error for
every lint L in the group. This was needlessly and annoyingly verbose;
we prefer to just have one error pointing out the conflicting
attributes.
Resolves#42873.
Long ago, in the before-time, the find_lint method was created with the
unused_variables ("unused_variable" in the singular, as it was called at
the time) attribute in anticipation of using the session and span in the
handling of renamed lints (31b7d64fd), and indeed, the session and span
came to be used in this method, while the unused_variables attribute
remained (1ad1e2e29). In modern times, the session and span are again no
longer used (ca81d3dd); it seems we can safely prune them from the
method signature, for justice, and mercy.
incr.comp.: Make DepNode `Copy` and valid across compilation sessions
This PR moves `DepNode` to a representation that does not need retracing and thus simplifies comparing dep-graphs from different compilation sessions. The code also gets a lot simpler in many places, since we don't need the generic parameter on `DepNode` anymore. See https://github.com/rust-lang/rust/issues/42294 for details.
~~NOTE: Only the last commit of this is new, the rest is already reviewed in https://github.com/rust-lang/rust/pull/42504.~~
This PR is almost done but there are some things I still want to do:
- [x] Add some module-level documentation to `dep_node.rs`, explaining especially what the `define_dep_nodes!()` macro is about.
- [x] Do another pass over the dep-graph loading logic. I suspect that we can get rid of building the `edges` map and also use arrays instead of hash maps in some places.
cc @rust-lang/compiler
r? @nikomatsakis
Initial implementation of declarative macros 2.0
Implement declarative macros 2.0 (rust-lang/rfcs#1584) behind `#![feature(decl_macro)]`.
Differences from `macro_rules!` include:
- new syntax: `macro m(..) { .. }` instead of `macro_rules! m { (..) => { .. } }`
- declarative macros are items:
```rust
// crate A:
pub mod foo {
m!(); // use before definition; declaration order is irrelevant
pub macro m() {} // `pub`, `pub(super)`, etc. work
}
fn main() {
foo::m!(); // named like other items
{ use foo::m as n; n!(); } // imported like other items
}
pub use foo::m; // re-exported like other items
// crate B:
extern crate A; // no need for `#[macro_use]`
A::foo::m!(); A::m!();
```
- Racket-like hygiene for items, imports, methods, fields, type parameters, privacy, etc.
- Intuitively, names in a macro definition are resolved in the macro definition's scope, not the scope in which the macro is used.
- This [explaination](http://beautifulracket.com/explainer/hygiene.html) of hygiene for Racket applies here (except for the "Breaking Hygiene" section). I wrote a similar [explanation](https://github.com/jseyfried/rfcs/blob/hygiene/text/0000-hygiene.md) for Rust.
- Generally speaking, if `fn f() { <body> }` resolves, `pub macro m() { <body> } ... m!()` also resolves, even if `m!()` is in a separate crate.
- `::foo::bar` in a `macro` behaves like `$crate::foo::bar` in a `macro_rules!`, except it can access everything visible from the `macro` (thus more permissive).
- See [`src/test/{run-pass, compile-fail}/hygiene`](afe7d89858) for examples. Small example:
```rust
mod foo {
fn f() { println!("hello world"); }
pub macro m() { f(); }
}
fn main() { foo::m!(); }
```
Limitations:
- This does not address planned changes to matchers (`expr`,`ty`, etc.), c.f. #26361.
- Lints (including stability and deprecation) and `unsafe` are not hygienic.
- adding hygiene here will be mostly or entirely backwards compatible
- Nested macro definitions (a `macro` inside another `macro`) don't always work correctly when invoked from external crates.
- pending improvements in how we encode macro definitions in crate metadata
- There is no way to "escape" hygiene without using a procedural macro.
r? @nrc
* #42007 happens because the Session LintStore is emptied when linting.
* The Session LintStore is emptied because the checker (Early/LateContext)
wants ownership.
* The checker wants ownership because it wants to mutate the pass objects
and lint levels.
The ownership of the whole store is not essential, only the lint levels and
pass objects need to be owned. Therefore, these parts are extracted out of
the LintStore into a separate structure `LintSession`. The "check crates"
methods can operate on `&mut LintSession` instead of `&mut LintStore`.
This is a minor BREAKING CHANGE for lint writers since the `LintContext`
trait is changed: the `mut_lints` and `level_stack` methods are removed.
But no one outside of `librustc/lint/context.rs` is using these functions,
so it should be safe.
Add lint for unused macros
Addresses parts of #34938, to add a lint for unused macros.
We now output warnings by default when we encounter a macro that we didn't use for expansion.
Issues to be resolved before this PR is ready for merge:
- [x] fix the NodeId issue described above
- [x] remove all unused macros from rustc and the libraries or set `#[allow(unused_macros)]` next to them if they should be kept for some reason. This is needed for successful boostrap and bors to accept the PR. -> #41934
- [x] ~~implement the full extent of #34938, that means the macro match arm checking as well.~~ *let's not do this for now*
This commit extends the current unused macro linter
to support directives like #[allow(unused_macros)]
or #[deny(unused_macros)] directly next to the macro
definition, or in one of the modules the macro is
inside. Before, we only supported such directives
at a per crate level, due to the crate's NodeId
being passed to session.add_lint.
We also had to implement handling of the macro's
NodeId in the lint visitor.
Previously, the note/message for the source of a lint being the command
line unconditionally named the individual lint, even if the actual
command specified a lint group (e.g., `-D warnings`); here, we take note
of the actual command options so we can be more specific.
This remains in the matter of #36846.
Warning or error messages set via a lint group attribute
(e.g. `#[deny(warnings)]`) should still make it clear which individual
lint (by name) was triggered, similarly to how we include "on by
default" language for default lints. This—and, while we're here, the
existing "on by default" language—can be tucked into a note rather than
cluttering the main error message. This occasions the slightest of
refactorings (we now have to get the diagnostic-builder with the main
message first, before matching on the lint source).
This is in the matter of #36846.