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:
commit
652f13d838
9 changed files with 213 additions and 23 deletions
|
@ -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();
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
19
src/test/ui/hygiene/duplicate_lifetimes.rs
Normal file
19
src/test/ui/hygiene/duplicate_lifetimes.rs
Normal 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() {}
|
27
src/test/ui/hygiene/duplicate_lifetimes.stderr
Normal file
27
src/test/ui/hygiene/duplicate_lifetimes.stderr
Normal 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`.
|
104
src/test/ui/hygiene/generic_params.rs
Normal file
104
src/test/ui/hygiene/generic_params.rs
Normal 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() {}
|
6
src/test/ui/hygiene/generic_params.stderr
Normal file
6
src/test/ui/hygiene/generic_params.stderr
Normal 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)]
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
32
src/test/ui/hygiene/issue-61574-const-parameters.rs
Normal file
32
src/test/ui/hygiene/issue-61574-const-parameters.rs
Normal 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() {}
|
6
src/test/ui/hygiene/issue-61574-const-parameters.stderr
Normal file
6
src/test/ui/hygiene/issue-61574-const-parameters.stderr
Normal 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)]
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
|
@ -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() {}
|
|
Loading…
Reference in a new issue