Expansion-driven outline module parsing#69838
Conversation
|
cc @matklad |
This comment has been minimized.
This comment has been minimized.
|
r=me with #69838 (comment) addressed. |
This comment has been minimized.
This comment has been minimized.
|
So the pretty test ( // exec-env:RUSTC_LOG=rustc_expand=debug
// rustc-env:RUST_BACKTRACE=1
// rustc-env:RUSTC_LOG=rustc_expand=debug
// compile-flags:-Ztreat-err-as-bug
// pp-exact
// The next line should not be expanded
#[path = "issue-12590-b.rs"]
mod issue_12590_b;
fn main() { }In the same directory, there is a file ( Running the test with results in: and it fails on For debugging purposes I have added: log::debug!(
"parse_external_mod(id={:?}, span={:?}, ownership={:?}, path={:?}, attrs={:?}",
id,
span,
ownership,
path,
attrs
);on function entry and I have: log::debug!("parse_external_mod, mp(ownership={:?}, path={:?})", mp.ownership, mp.path);
// Actually parse the external file as amodule.
let mut p0 = new_sub_parser_from_file(sess, &mp.path, Some(id.to_string()), span);In pub fn expand_crate(&mut self, mut krate: ast::Crate) -> ast::Crate {
let mut module = ModuleData {
mod_path: vec![Ident::from_str(&self.cx.ecfg.crate_name)],
directory: match self.cx.source_map().span_to_unmapped_path(krate.span) {
FileName::Real(path) => path,
other => PathBuf::from(other.to_string()),
},
};
log::debug!("expand_crate, module #0 = {:?}", module);
module.directory.pop();
log::debug!("expand_crate, module #1 = {:?}", module);
self.cx.root_path = module.directory.clone();
self.cx.current_expansion.module = Rc::new(module);I suspect there may be a deficiency in compiletest itself... investigating further. |
|
So // Finally, let's make sure it actually appears to remain valid code
let proc_res = self.typecheck_source(actual);
if !proc_res.status.success() {
self.fatal_proc_rec("pretty-printed source does not typecheck", &proc_res);
}where |
@petrochenkov I've done this in 42ab820 but would like feedback on it before r=you-ing. |
|
Using stdin seems odd, maybe compiletest should set the
|
Alright; then let's do: @bors r=petrochenkov,eddyb |
|
📌 Commit 42ab820 has been approved by |
|
🌲 The tree is currently closed for pull requests below priority 1000, this pull request will be tested once the tree is reopened |
…ov,eddyb
Expansion-driven outline module parsing
After this PR, the parser will not do any conditional compilation or loading of external module files when `mod foo;` is encountered. Instead, the parser only leaves `mod foo;` in place in the AST, with no items filled in. Expansion later kicks in and will load the actual files and do the parsing. This entails that the following is now valid:
```rust
#[cfg(FALSE)]
mod foo {
mod bar {
mod baz; // `foo/bar/baz.rs` doesn't exist, but no error!
}
}
```
Fixes rust-lang#64197.
r? @petrochenkov
…ov,eddyb
Expansion-driven outline module parsing
After this PR, the parser will not do any conditional compilation or loading of external module files when `mod foo;` is encountered. Instead, the parser only leaves `mod foo;` in place in the AST, with no items filled in. Expansion later kicks in and will load the actual files and do the parsing. This entails that the following is now valid:
```rust
#[cfg(FALSE)]
mod foo {
mod bar {
mod baz; // `foo/bar/baz.rs` doesn't exist, but no error!
}
}
```
Fixes rust-lang#64197.
r? @petrochenkov
Rollup of 4 pull requests Successful merges: - #5326 (rustup rust-lang/rust#69838) - #5333 (rustup rust-lang/rust#69189) - #5336 (rustup rust-lang/rust#69920) - #5341 (Rustup to rust-lang/rust#66131) Failed merges: r? @ghost changelog: none
Rollup of 4 pull requests Successful merges: - #5326 (rustup rust-lang/rust#69838) - #5333 (rustup rust-lang/rust#69189) - #5336 (rustup rust-lang/rust#69920) - #5341 (Rustup to rust-lang/rust#66131) Failed merges: r? @ghost changelog: none
Rollup of 4 pull requests Successful merges: - #5326 (rustup rust-lang/rust#69838) - #5333 (rustup rust-lang/rust#69189) - #5336 (rustup rust-lang/rust#69920) - #5341 (Rustup to rust-lang/rust#66131) Failed merges: r? @ghost changelog: none
Rollup of 4 pull requests Successful merges: - #5326 (rustup rust-lang/rust#69838) - #5333 (rustup rust-lang/rust#69189) - #5336 (rustup rust-lang/rust#69920) - #5341 (Rustup to rust-lang/rust#66131) Failed merges: r? @ghost changelog: none
| }; | ||
| let directory_ownership = DirectoryOwnership::Owned { relative: None }; | ||
| let p = new_sub_parser_from_file(cx.parse_sess(), &file, directory_ownership, None, sp); | ||
| let p = new_sub_parser_from_file(cx.parse_sess(), &file, None, sp); |
There was a problem hiding this comment.
This apparently broke Servo's script crate, or at least the copy on perf.rust-lang.org.
cc @Mark-Simulacrum can you post some details?
IIUC, this can't be fixed locally either, because the mod bar; parsed by include!("directory/included.rs"); will be expanded later, so for expanding mod bar; to load directory/bar.rs (which is apparently the old behavior? how are we not testing for it), we'd need to detect that an include! happened, or something similar.
EDIT: adjusted example to fit @Mark-Simulacrum's reproduction.
There was a problem hiding this comment.
This is a minimal reproduction:
$ tree .
.
|-- directory
| |-- bar.rs
| `-- included.rs
`-- foo.rs
// foo.rs
include!("directory/included.rs");
fn main() {}
// directory/included.rs
mod bar;
// directory/bar.rs
// empty
$ rustc +3c6f982cc908aacc39c3ac97f31c989f81cc213c foo.rs
error[E0583]: file not found for module `bar`
--> directory/included.rs:1:1
|
1 | mod bar;
| ^^^^^^^^
|
= help: to create the module `bar`, create file "bar.rs"
error: aborting due to previous error
For more information about this error, try `rustc --explain E0583`.
There was a problem hiding this comment.
I've just recalled a related issue - #58818.
Could you check whether this PR fixed it?
There was a problem hiding this comment.
we'd need to detect that an include! happened, or something similar.
FWIW, we have Definitions::expansions_that_defined that can link from the mod bar; item to the macro that produced it. Then we have ExpnData::parent that can link from that macro to the macro that produced it (recursively).
Perhaps using these data we can hack up something reproducing the old behavior.
There was a problem hiding this comment.
We can only detect include comparing the macro's name in ExpnData with sym::include.
fn expansion_cause used in for a bunch of various stuff also hard-codes the name include like this, so it should be ok for now.
(The proper fix is a flag in struct SyntaxExtension, other file-including built-in macros should have it as well.)
There was a problem hiding this comment.
@petrochenkov So I tried two different approaches to set ownership before loading the external module. The first one was adding Incovation::is_current_expansion_macro_named defined like so:
fn is_current_expansion_macro_named(&self, sym: Symbol) -> bool {
let mut curr = self.cx.current_expansion.id;
loop {
let expn_data = curr.expn_data();
if expn_data.kind == ExpnKind::Macro(MacroKind::Bang, sym) {
// Stop going up the backtrace once `sym` is encountered.
return true;
}
if expn_data.is_root() {
// Reached root, need to bail to ensure termination.
return false;
}
curr = expn_data.call_site.ctxt().outer_expn();
}
}and then using it in flat_map_item before parse_external_mod happens:
let pushed = &mut false; // Record `parse_external_mod` pushing so we can pop.
let mut dir = Directory { ownership: orig_ownership, path: module.directory };
if self.is_current_expansion_macro_named(sym::include) {
log::debug!("flat_map_item, is_current_expansion_macro_named = true");
dir.ownership = DirectoryOwnership::Owned { relative: None };
}The log message here does trigger on @Mark-Simulacrum's regression test, but this doesn't fix the issue.
I also tweaked SyntaxExtension, adding a flag foobar: bool which is used in fully_expand_fragment:
let (expanded_fragment, new_invocations) = match res {
InvocationRes::Single(ext) => match self.expand_invoc(invoc, &ext.kind) {
ExpandResult::Ready(fragment) => {
if ext.foobar {
self.cx.current_expansion.directory_ownership =
DirectoryOwnership::Owned { relative: None };
log::debug!("fully_expand_fragment, using foobar");
}
self.collect_invocations(fragment, &[])and which is set in compile_macro:
if result.is_builtin {
// The macro was marked with `#[rustc_builtin_macro]`.
if let Some(ext) = self.builtin_macros.remove(&item.ident.name) {
// The macro is a built-in, replace its expander function
// while still taking everything else from the source code.
result.kind = ext.kind;
if item.ident.name == sym::include {
result.foobar = true;
log::debug!("compile_macro, setting foobar");
}The log message also triggers here, but also does nothing to fix the issue, as the approach is the same as the other mechanism.
I'm not sure why it doesn't work. :(
…etrochenkov expand_include: set `.directory` to dir of included file. Resolves the regression noted in rust-lang#69838. r? @petrochenkov cc @eddyb @Mark-Simulacrum
…etrochenkov expand_include: set `.directory` to dir of included file. Resolves the regression noted in rust-lang#69838. r? @petrochenkov cc @eddyb @Mark-Simulacrum
…etrochenkov expand_include: set `.directory` to dir of included file. Resolves the regression noted in rust-lang#69838. r? @petrochenkov cc @eddyb @Mark-Simulacrum
Pkgsrc changes:
* Remove a couple diffs which are now integrated upstream.
* Adjust cargo checksums after upstream upgrades.
* Belatedly bump the curl dependency
* Unset DESTDIR during the build phase, to work around a mysterious
build bug deep in the bowels of llvm.
* Bump nearly all bootstraps to 1.43.1.
Upstream changes:
Version 1.44.0 (2020-06-04)
==========================
Language
--------
- [You can now use `async/.await` with `#[no_std]` enabled.][69033]
- [Added the `unused_braces` lint.][70081]
**Syntax-only changes**
- [Expansion-driven outline module parsing][69838]
```rust
#[cfg(FALSE)]
mod foo {
mod bar {
mod baz; // `foo/bar/baz.rs` doesn't exist, but no error!
}
}
```
These are still rejected semantically, so you will likely receive an error but
these changes can be seen and parsed by macros and conditional compilation.
Compiler
--------
- [Rustc now respects the `-C codegen-units` flag in incremental mode.][70156]
Additionally when in incremental mode rustc defaults to 256 codegen units.
- [Refactored `catch_unwind`, to have zero-cost unless unwinding is enabled and
a panic is thrown.][67502]
- [Added tier 3\* support for the `aarch64-unknown-none` and
`aarch64-unknown-none-softfloat` targets.][68334]
- [Added tier 3 support for `arm64-apple-tvos` and
`x86_64-apple-tvos` targets.][68191]
Libraries
---------
- [Special cased `vec![]` to map directly to `Vec::new()`.][70632] This allows
`vec![]` to be able to be used in `const` contexts.
- [`convert::Infallible` now implements `Hash`.][70281]
- [`OsString` now implements `DerefMut` and `IndexMut` returning
a `&mut OsStr`.][70048]
- [Unicode 13 is now supported.][69929]
- [`String` now implements `From<&mut str>`.][69661]
- [`IoSlice` now implements `Copy`.][69403]
- [`Vec<T>` now implements `From<[T; N]>`.][68692] Where `N` is less than 32.
- [`proc_macro::LexError` now implements `fmt::Display` and `Error`.][68899]
- [`from_le_bytes`, `to_le_bytes`, `from_be_bytes`, `to_be_bytes`,
`from_ne_bytes`, and `to_ne_bytes` methods are now `const` for all
integer types.][69373]
Stabilized APIs
---------------
- [`PathBuf::with_capacity`]
- [`PathBuf::capacity`]
- [`PathBuf::clear`]
- [`PathBuf::reserve`]
- [`PathBuf::reserve_exact`]
- [`PathBuf::shrink_to_fit`]
- [`f32::to_int_unchecked`]
- [`f64::to_int_unchecked`]
- [`Layout::align_to`]
- [`Layout::pad_to_align`]
- [`Layout::array`]
- [`Layout::extend`]
Cargo
-----
- [Added the `cargo tree` command which will print a tree graph of
your dependencies.][cargo/8062] E.g.
```
mdbook v0.3.2 (/Users/src/rust/mdbook)
+-- ammonia v3.0.0
| +-- html5ever v0.24.0
| | +-- log v0.4.8
| | | +-- cfg-if v0.1.9
| | +-- mac v0.1.1
| | +-- markup5ever v0.9.0
| | +-- log v0.4.8 (*)
| | +-- phf v0.7.24
| | | +-- phf_shared v0.7.24
| | | +-- siphasher v0.2.3
| | | +-- unicase v1.4.2
| | | [build-dependencies]
| | | +-- version_check v0.1.5
...
```
You can also display dependencies on multiple versions of the same crate with
`cargo tree -d` (short for `cargo tree --duplicates`).
Misc
----
- [Rustdoc now allows you to specify `--crate-version` to have rustdoc include
the version in the sidebar.][69494]
Compatibility Notes
-------------------
- [Rustc now correctly generates static libraries on Windows GNU targets with
the `.a` extension, rather than the previous `.lib`.][70937]
- [Removed the `-C no_integrated_as` flag from rustc.][70345]
- [The `file_name` property in JSON output of macro errors now points the actual
source file rather than the previous format of `<NAME macros>`.][70969]
**Note:** this may not point a file that actually exists on the user's system.
- [The minimum required external LLVM version has been bumped to LLVM 8.][71147]
- [`mem::{zeroed, uninitialised}` will now panic when used with types that do
not allow zero initialization such as `NonZeroU8`.][66059] This was
previously a warning.
- [In 1.45.0 (the next release) converting a `f64` to `u32` using the `as`
operator has been defined as a saturating operation.][71269] This was
previously undefined behaviour, you can use the `{f64, f32}::to_int_unchecked`
methods to continue using the current behaviour which may desirable in rare
performance sensitive situations.
Internal Only
-------------
These changes provide no direct user facing benefits, but represent significant
improvements to the internals and overall performance of rustc and
related tools.
- [dep_graph Avoid allocating a set on when the number reads are small.][69778]
- [Replace big JS dict with JSON parsing.][71250]
[69373]: rust-lang/rust#69373
[66059]: rust-lang/rust#66059
[68191]: rust-lang/rust#68191
[68899]: rust-lang/rust#68899
[71147]: rust-lang/rust#71147
[71250]: rust-lang/rust#71250
[70937]: rust-lang/rust#70937
[70969]: rust-lang/rust#70969
[70632]: rust-lang/rust#70632
[70281]: rust-lang/rust#70281
[70345]: rust-lang/rust#70345
[70048]: rust-lang/rust#70048
[70081]: rust-lang/rust#70081
[70156]: rust-lang/rust#70156
[71269]: rust-lang/rust#71269
[69838]: rust-lang/rust#69838
[69929]: rust-lang/rust#69929
[69661]: rust-lang/rust#69661
[69778]: rust-lang/rust#69778
[69494]: rust-lang/rust#69494
[69403]: rust-lang/rust#69403
[69033]: rust-lang/rust#69033
[68692]: rust-lang/rust#68692
[68334]: rust-lang/rust#68334
[67502]: rust-lang/rust#67502
[cargo/8062]: rust-lang/cargo#8062
[`PathBuf::with_capacity`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.with_capacity
[`PathBuf::capacity`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.capacity
[`PathBuf::clear`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.clear
[`PathBuf::reserve`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.reserve
[`PathBuf::reserve_exact`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.reserve_exact
[`PathBuf::shrink_to_fit`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.shrink_to_fit
[`f32::to_int_unchecked`]: https://doc.rust-lang.org/std/primitive.f32.html#method.to_int_unchecked
[`f64::to_int_unchecked`]: https://doc.rust-lang.org/std/primitive.f64.html#method.to_int_unchecked
[`Layout::align_to`]: https://doc.rust-lang.org/std/alloc/struct.Layout.html#method.align_to
[`Layout::pad_to_align`]: https://doc.rust-lang.org/std/alloc/struct.Layout.html#method.pad_to_align
[`Layout::array`]: https://doc.rust-lang.org/std/alloc/struct.Layout.html#method.array
[`Layout::extend`]: https://doc.rust-lang.org/std/alloc/struct.Layout.html#method.extend
After this PR, the parser will not do any conditional compilation or loading of external module files when
mod foo;is encountered. Instead, the parser only leavesmod foo;in place in the AST, with no items filled in. Expansion later kicks in and will load the actual files and do the parsing. This entails that the following is now valid:Fixes #64197.
r? @petrochenkov