Skip to content

Commit ac945fc

Browse files
committed
Auto merge of #56053 - petrochenkov:absedihyg, r=<try>
[beta] resolve: Implement edition hygiene for imports and absolute paths The changes in behavior of imports and absolute paths are the most significant breaking changes of 2018 edition. However, these changes are not covered by edition hygiene, so macros defined by 2015 edition crates expanded in 2018 edition crates are still interpreted in the 2018 edition way when they contain imports or absolute paths. This means the promise of seamless integration of crates built with different editions, including use of macros, doesn't hold fully. This PR fixes that and implements edition hygiene for imports and absolute paths, so they behave according to the edition in which they were written, even in macros. ### Detailed rules (mostly taken from #50376) #### Preface We keep edition data per-span in the compiler. This means each span knows its edition. Each identifier has a span, so each identifier knows its edition. #### Absolute paths Explicitly written absolute paths `::ident::...` are desugared into something like `{{root}}::ident::...` in the compiler, where `{{root}}` is also treated as an identifier. `{{root}}` inherits its span/hygienic-context from the token `::`. If the span is from 2015 edition, then `{{root}}` is interpreted as the current crate root (`crate::...` with same hygienic context). If the span is from 2018 edition, then `{{root}}` is interpreted as "crate universe" (`extern::...`). #### Imports To resolve an import `use ident::...` we need to resolve `ident` first. The idea in this PR is that `ident` fully knows how to resolve itself. If `ident`'s span is from 2015 edition, then the identifier is resolved in the current crate root (effectively `crate::ident::...` where `crate` has the same hygienic context as `ident`). If `ident`'s span is from 2018 edition, then the identifier is resolved in the current scope, without prepending anything (well, at least with uniform paths). There's one corner case in which there's no identifier - prefix-less glob import `use *;`. In this case the span is inherited from the token `*`. `use *;` && `is_2015(span(*))` -> `use crate::*;` && `span(crate) == span(*)`. `use *;` && `is_2018(span(*))` -> error. --- Why beta: - Compatibility of 2015 edition crates with 2018 edition crates, including macros, is one of the main promises of the edition initiative. - This is technically a breaking change for 2018 edition crates or crates depending on 2018 edition crates. - ~This PR is based on #55884 which hasn't landed on nightly yet :)~ No longer true (#56042). Previous attempt #50999 Closes #50376 Closes #52848 Closes #53007 r? @ghost
2 parents 1c16fa4 + 1401cd2 commit ac945fc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+643
-543
lines changed

src/librustc/ty/context.rs

+2-9
Original file line numberDiff line numberDiff line change
@@ -1484,15 +1484,8 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
14841484
/// done with either: `-Ztwo-phase-borrows`, `#![feature(nll)]`,
14851485
/// or by opting into an edition after 2015.
14861486
pub fn two_phase_borrows(self) -> bool {
1487-
if self.features().nll || self.sess.opts.debugging_opts.two_phase_borrows {
1488-
return true;
1489-
}
1490-
1491-
match self.sess.edition() {
1492-
Edition::Edition2015 => false,
1493-
Edition::Edition2018 => true,
1494-
_ => true,
1495-
}
1487+
self.sess.rust_2018() || self.features().nll ||
1488+
self.sess.opts.debugging_opts.two_phase_borrows
14961489
}
14971490

14981491
/// What mode(s) of borrowck should we run? AST? MIR? both?

src/librustc/ty/item_path.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ use ty::{self, DefIdTree, Ty, TyCtxt};
1414
use middle::cstore::{ExternCrate, ExternCrateSource};
1515
use syntax::ast;
1616
use syntax::symbol::{keywords, LocalInternedString, Symbol};
17-
use syntax_pos::edition::Edition;
1817

1918
use std::cell::Cell;
2019
use std::fmt::Debug;
@@ -140,7 +139,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
140139
debug!("push_krate_path: name={:?}", name);
141140
buffer.push(&name);
142141
}
143-
} else if self.sess.edition() == Edition::Edition2018 && !pushed_prelude_crate {
142+
} else if self.sess.rust_2018() && !pushed_prelude_crate {
144143
SHOULD_PREFIX_WITH_CRATE.with(|flag| {
145144
// We only add the `crate::` keyword where appropriate. In particular,
146145
// when we've not previously pushed a prelude crate to this path.

src/librustc_resolve/build_reduced_graph.rs

+14-10
Original file line numberDiff line numberDiff line change
@@ -132,17 +132,21 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
132132
// so prefixes are prepended with crate root segment if necessary.
133133
// The root is prepended lazily, when the first non-empty prefix or terminating glob
134134
// appears, so imports in braced groups can have roots prepended independently.
135+
// 2015 identifiers used on global 2018 edition enter special "virtual 2015 mode", don't
136+
// get crate root prepended, but get special treatment during in-scope resolution instead.
135137
let is_glob = if let ast::UseTreeKind::Glob = use_tree.kind { true } else { false };
136-
let crate_root = if !self.session.rust_2018() &&
137-
prefix_iter.peek().map_or(is_glob, |seg| !seg.ident.is_path_segment_keyword())
138-
{
139-
Some(Segment::from_ident(Ident::new(
140-
keywords::CrateRoot.name(),
141-
use_tree.prefix.span.shrink_to_lo(),
142-
)))
143-
} else {
144-
None
145-
};
138+
let crate_root = match prefix_iter.peek() {
139+
Some(seg) if !seg.ident.is_path_segment_keyword() &&
140+
seg.ident.span.rust_2015() && self.session.rust_2015() => {
141+
Some(seg.ident.span.ctxt())
142+
}
143+
None if is_glob && use_tree.span.rust_2015() => {
144+
Some(use_tree.span.ctxt())
145+
}
146+
_ => None,
147+
}.map(|ctxt| Segment::from_ident(Ident::new(
148+
keywords::CrateRoot.name(), use_tree.prefix.span.shrink_to_lo().with_ctxt(ctxt)
149+
)));
146150

147151
let prefix = crate_root.into_iter().chain(prefix_iter).collect::<Vec<_>>();
148152
debug!("build_reduced_graph_for_use_tree: prefix={:?}", prefix);

src/librustc_resolve/error_reporting.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
3333
(Some(fst), Some(snd)) if fst.ident.name == keywords::CrateRoot.name() &&
3434
!snd.ident.is_path_segment_keyword() => {}
3535
// `ident::...` on 2018
36-
(Some(fst), _) if self.session.rust_2018() && !fst.ident.is_path_segment_keyword() => {
36+
(Some(fst), _) if fst.ident.span.rust_2018() &&
37+
!fst.ident.is_path_segment_keyword() => {
3738
// Insert a placeholder that's later replaced by `self`/`super`/etc.
3839
path.insert(0, Segment::from_ident(keywords::Invalid.ident()));
3940
}
@@ -141,7 +142,7 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> {
141142
mut path: Vec<Segment>,
142143
parent_scope: &ParentScope<'b>,
143144
) -> Option<(Vec<Segment>, Option<String>)> {
144-
if !self.session.rust_2018() {
145+
if path[1].ident.span.rust_2015() {
145146
return None;
146147
}
147148

0 commit comments

Comments
 (0)