Rollup merge of #63083 - matthewjasper:parameter-hygiene, r=petrochenkov

Make generic parameters always use modern hygiene

* E0263 (lifetime parameter declared twice in the same scope) now compares modernized identifiers.
* Const parameters are now resolved with modern hygiene.

Closes #58307
Closes #60746
Closes #61574
Closes #62433
This commit is contained in:
Mazdak Farrokhzad 2019-07-30 05:37:34 +02:00 committed by GitHub
commit 652f13d838
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 213 additions and 23 deletions

View file

@ -2568,7 +2568,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
let lifetimes: Vec<_> = params let lifetimes: Vec<_> = params
.iter() .iter()
.filter_map(|param| match param.kind { .filter_map(|param| match param.kind {
GenericParamKind::Lifetime { .. } => Some((param, param.name)), GenericParamKind::Lifetime { .. } => Some((param, param.name.modern())),
_ => None, _ => None,
}) })
.collect(); .collect();

View file

@ -869,8 +869,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Resolver<'a> {
debug!("(resolving function) entering function"); debug!("(resolving function) entering function");
let rib_kind = match function_kind { let rib_kind = match function_kind {
FnKind::ItemFn(..) => FnItemRibKind, FnKind::ItemFn(..) => FnItemRibKind,
FnKind::Method(..) => AssocItemRibKind, FnKind::Method(..) | FnKind::Closure(_) => NormalRibKind,
FnKind::Closure(_) => NormalRibKind,
}; };
// Create a value rib for the function. // Create a value rib for the function.
@ -2307,21 +2306,32 @@ impl<'a> Resolver<'a> {
if ident.name == kw::Invalid { if ident.name == kw::Invalid {
return Some(LexicalScopeBinding::Res(Res::Err)); return Some(LexicalScopeBinding::Res(Res::Err));
} }
ident.span = if ident.name == kw::SelfUpper { let (general_span, modern_span) = if ident.name == kw::SelfUpper {
// FIXME(jseyfried) improve `Self` hygiene // FIXME(jseyfried) improve `Self` hygiene
ident.span.with_ctxt(SyntaxContext::empty()) let empty_span = ident.span.with_ctxt(SyntaxContext::empty());
(empty_span, empty_span)
} else if ns == TypeNS { } else if ns == TypeNS {
ident.span.modern() let modern_span = ident.span.modern();
(modern_span, modern_span)
} else { } else {
ident.span.modern_and_legacy() (ident.span.modern_and_legacy(), ident.span.modern())
}; };
ident.span = general_span;
let modern_ident = Ident { span: modern_span, ..ident };
// Walk backwards up the ribs in scope. // Walk backwards up the ribs in scope.
let record_used = record_used_id.is_some(); let record_used = record_used_id.is_some();
let mut module = self.graph_root; let mut module = self.graph_root;
for i in (0 .. self.ribs[ns].len()).rev() { for i in (0 .. self.ribs[ns].len()).rev() {
debug!("walk rib\n{:?}", self.ribs[ns][i].bindings); debug!("walk rib\n{:?}", self.ribs[ns][i].bindings);
if let Some(res) = self.ribs[ns][i].bindings.get(&ident).cloned() { // Use the rib kind to determine whether we are resolving parameters
// (modern hygiene) or local variables (legacy hygiene).
let rib_ident = if let AssocItemRibKind | ItemRibKind = self.ribs[ns][i].kind {
modern_ident
} else {
ident
};
if let Some(res) = self.ribs[ns][i].bindings.get(&rib_ident).cloned() {
// The ident resolves to a type parameter or local variable. // The ident resolves to a type parameter or local variable.
return Some(LexicalScopeBinding::Res( return Some(LexicalScopeBinding::Res(
self.validate_res_from_ribs(ns, i, res, record_used, path_span), self.validate_res_from_ribs(ns, i, res, record_used, path_span),
@ -2357,7 +2367,7 @@ impl<'a> Resolver<'a> {
} }
} }
ident.span = ident.span.modern(); ident = modern_ident;
let mut poisoned = None; let mut poisoned = None;
loop { loop {
let opt_module = if let Some(node_id) = record_used_id { let opt_module = if let Some(node_id) = record_used_id {

View file

@ -0,0 +1,19 @@
// Ensure that lifetime parameter names are modernized before we check for
// duplicates.
#![feature(decl_macro, rustc_attrs)]
#[rustc_macro_transparency = "semitransparent"]
macro m($a:lifetime) {
fn g<$a, 'a>() {} //~ ERROR lifetime name `'a` declared twice
}
#[rustc_macro_transparency = "transparent"]
macro n($a:lifetime) {
fn h<$a, 'a>() {} //~ ERROR lifetime name `'a` declared twice
}
m!('a);
n!('a);
fn main() {}

View file

@ -0,0 +1,27 @@
error[E0263]: lifetime name `'a` declared twice in the same scope
--> $DIR/duplicate_lifetimes.rs:8:14
|
LL | fn g<$a, 'a>() {}
| ^^ declared twice
...
LL | m!('a);
| -------
| | |
| | previous declaration here
| in this macro invocation
error[E0263]: lifetime name `'a` declared twice in the same scope
--> $DIR/duplicate_lifetimes.rs:13:14
|
LL | fn h<$a, 'a>() {}
| ^^ declared twice
...
LL | n!('a);
| -------
| | |
| | previous declaration here
| in this macro invocation
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0263`.

View file

@ -0,0 +1,104 @@
// Ensure that generic parameters always have modern hygiene.
// check-pass
// ignore-pretty pretty-printing is unhygienic
#![feature(decl_macro, rustc_attrs, const_generics)]
mod type_params {
macro m($T:ident) {
fn f<$T: Clone, T: PartialEq>(t1: $T, t2: T) -> ($T, bool) {
(t1.clone(), t2 == t2)
}
}
#[rustc_macro_transparency = "semitransparent"]
macro n($T:ident) {
fn g<$T: Clone>(t1: $T, t2: T) -> (T, $T) {
(t1.clone(), t2.clone())
}
fn h<T: Clone>(t1: $T, t2: T) -> (T, $T) {
(t1.clone(), t2.clone())
}
}
#[rustc_macro_transparency = "transparent"]
macro p($T:ident) {
fn j<$T: Clone>(t1: $T, t2: T) -> (T, $T) {
(t1.clone(), t2.clone())
}
fn k<T: Clone>(t1: $T, t2: T) -> (T, $T) {
(t1.clone(), t2.clone())
}
}
m!(T);
n!(T);
p!(T);
}
mod lifetime_params {
macro m($a:lifetime) {
fn f<'b, 'c, $a: 'b, 'a: 'c>(t1: &$a(), t2: &'a ()) -> (&'b (), &'c ()) {
(t1, t2)
}
}
#[rustc_macro_transparency = "semitransparent"]
macro n($a:lifetime) {
fn g<$a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) {
(t1, t2)
}
fn h<'a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) {
(t1, t2)
}
}
#[rustc_macro_transparency = "transparent"]
macro p($a:lifetime) {
fn j<$a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) {
(t1, t2)
}
fn k<'a>(t1: &$a(), t2: &'a ()) -> (&'a (), &$a ()) {
(t1, t2)
}
}
m!('a);
n!('a);
p!('a);
}
mod const_params {
macro m($C:ident) {
fn f<const $C: usize, const C: usize>(t1: [(); $C], t2: [(); C]) -> ([(); $C], [(); C]) {
(t1, t2)
}
}
#[rustc_macro_transparency = "semitransparent"]
macro n($C:ident) {
fn g<const $C: usize>(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) {
(t1, t2)
}
fn h<const C: usize>(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) {
(t1, t2)
}
}
#[rustc_macro_transparency = "transparent"]
macro p($C:ident) {
fn j<const $C: usize>(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) {
(t1, t2)
}
fn k<const C: usize>(t1: [(); $C], t2: [(); C]) -> ([(); C], [(); $C]) {
(t1, t2)
}
}
m!(C);
n!(C);
p!(C);
}
fn main() {}

View file

@ -0,0 +1,6 @@
warning: the feature `const_generics` is incomplete and may cause the compiler to crash
--> $DIR/generic_params.rs:6:37
|
LL | #![feature(decl_macro, rustc_attrs, const_generics)]
| ^^^^^^^^^^^^^^

View file

@ -0,0 +1,32 @@
// A more comprehensive test that const parameters have correctly implemented
// hygiene
// check-pass
#![feature(const_generics)]
use std::ops::Add;
struct VectorLike<T, const SIZE: usize>([T; {SIZE}]);
macro_rules! impl_operator_overload {
($trait_ident:ident, $method_ident:ident) => {
impl<T, const SIZE: usize> $trait_ident for VectorLike<T, {SIZE}>
where
T: $trait_ident,
{
type Output = VectorLike<T, {SIZE}>;
fn $method_ident(self, _: VectorLike<T, {SIZE}>) -> VectorLike<T, {SIZE}> {
let _ = SIZE;
unimplemented!()
}
}
}
}
impl_operator_overload!(Add, add);
fn main() {}

View file

@ -0,0 +1,6 @@
warning: the feature `const_generics` is incomplete and may cause the compiler to crash
--> $DIR/issue-61574-const-parameters.rs:6:12
|
LL | #![feature(const_generics)]
| ^^^^^^^^^^^^^^

View file

@ -1,14 +0,0 @@
// check-pass
// ignore-pretty pretty-printing is unhygienic
#![feature(decl_macro)]
macro m($T:ident) {
fn f<T, $T>(t: T, t2: $T) -> (T, $T) {
(t, t2)
}
}
m!(T);
fn main() {}