Auto merge of #45050 - petrochenkov:ambind, r=nikomatsakis
resolve: Use same rules for disambiguating fresh bindings in `match` and `let`
Resolve `Unit` as a unit struct pattern in
```rust
struct Unit;
let Unit = x;
```
consistently with
```rust
match x {
Unit => {}
}
```
It was previously an error.
(The change also applies to unit variants and constants.)
Fixes https://users.rust-lang.org/t/e0530-cannot-shadow-unit-structs-what-in-the-earthly-what/13054
(This particular change doesn't depend on a fix for the issue mentioned in https://users.rust-lang.org/t/e0530-cannot-shadow-unit-structs-what-in-the-earthly-what/13054/4)
cc @rust-lang/lang
r? @nikomatsakis
This commit is contained in:
@@ -260,7 +260,14 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
|
|||||||
"refutable pattern in {}: `{}` not covered",
|
"refutable pattern in {}: `{}` not covered",
|
||||||
origin, pattern_string
|
origin, pattern_string
|
||||||
);
|
);
|
||||||
diag.span_label(pat.span, format!("pattern `{}` not covered", pattern_string));
|
let label_msg = match pat.node {
|
||||||
|
PatKind::Path(hir::QPath::Resolved(None, ref path))
|
||||||
|
if path.segments.len() == 1 && path.segments[0].parameters.is_none() => {
|
||||||
|
format!("interpreted as a {} pattern, not new variable", path.def.kind_name())
|
||||||
|
}
|
||||||
|
_ => format!("pattern `{}` not covered", pattern_string),
|
||||||
|
};
|
||||||
|
diag.span_label(pat.span, label_msg);
|
||||||
diag.emit();
|
diag.emit();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -377,12 +377,6 @@ enum PatternSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PatternSource {
|
impl PatternSource {
|
||||||
fn is_refutable(self) -> bool {
|
|
||||||
match self {
|
|
||||||
PatternSource::Match | PatternSource::IfLet | PatternSource::WhileLet => true,
|
|
||||||
PatternSource::Let | PatternSource::For | PatternSource::FnParam => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn descr(self) -> &'static str {
|
fn descr(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
PatternSource::Match => "match binding",
|
PatternSource::Match => "match binding",
|
||||||
@@ -2388,20 +2382,24 @@ impl<'a> Resolver<'a> {
|
|||||||
false, pat.span)
|
false, pat.span)
|
||||||
.and_then(LexicalScopeBinding::item);
|
.and_then(LexicalScopeBinding::item);
|
||||||
let resolution = binding.map(NameBinding::def).and_then(|def| {
|
let resolution = binding.map(NameBinding::def).and_then(|def| {
|
||||||
let ivmode = BindingMode::ByValue(Mutability::Immutable);
|
let is_syntactic_ambiguity = opt_pat.is_none() &&
|
||||||
let always_binding = !pat_src.is_refutable() || opt_pat.is_some() ||
|
bmode == BindingMode::ByValue(Mutability::Immutable);
|
||||||
bmode != ivmode;
|
|
||||||
match def {
|
match def {
|
||||||
Def::StructCtor(_, CtorKind::Const) |
|
Def::StructCtor(_, CtorKind::Const) |
|
||||||
Def::VariantCtor(_, CtorKind::Const) |
|
Def::VariantCtor(_, CtorKind::Const) |
|
||||||
Def::Const(..) if !always_binding => {
|
Def::Const(..) if is_syntactic_ambiguity => {
|
||||||
// A unit struct/variant or constant pattern.
|
// Disambiguate in favor of a unit struct/variant
|
||||||
|
// or constant pattern.
|
||||||
self.record_use(ident.node, ValueNS, binding.unwrap(), ident.span);
|
self.record_use(ident.node, ValueNS, binding.unwrap(), ident.span);
|
||||||
Some(PathResolution::new(def))
|
Some(PathResolution::new(def))
|
||||||
}
|
}
|
||||||
Def::StructCtor(..) | Def::VariantCtor(..) |
|
Def::StructCtor(..) | Def::VariantCtor(..) |
|
||||||
Def::Const(..) | Def::Static(..) => {
|
Def::Const(..) | Def::Static(..) => {
|
||||||
// A fresh binding that shadows something unacceptable.
|
// This is unambiguously a fresh binding, either syntactically
|
||||||
|
// (e.g. `IDENT @ PAT` or `ref IDENT`) or because `IDENT` resolves
|
||||||
|
// to something unusable as a pattern (e.g. constructor function),
|
||||||
|
// but we still conservatively report an error, see
|
||||||
|
// issues/33118#issuecomment-233962221 for one reason why.
|
||||||
resolve_error(
|
resolve_error(
|
||||||
self,
|
self,
|
||||||
ident.span,
|
ident.span,
|
||||||
@@ -2410,7 +2408,7 @@ impl<'a> Resolver<'a> {
|
|||||||
);
|
);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
Def::Local(..) | Def::Upvar(..) | Def::Fn(..) | Def::Err => {
|
Def::Fn(..) | Def::Err => {
|
||||||
// These entities are explicitly allowed
|
// These entities are explicitly allowed
|
||||||
// to be shadowed by fresh bindings.
|
// to be shadowed by fresh bindings.
|
||||||
None
|
None
|
||||||
|
|||||||
@@ -12,6 +12,6 @@ mod foo { pub struct bar; }
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let bar = 5;
|
let bar = 5;
|
||||||
//~^ ERROR let bindings cannot shadow unit structs
|
//~^ ERROR mismatched types
|
||||||
use foo::bar;
|
use foo::bar;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,6 @@ struct Test;
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|| {
|
|| {
|
||||||
let Test = 1; //~ ERROR let bindings cannot shadow unit structs
|
let Test = 1; //~ ERROR mismatched types
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
67
src/test/compile-fail/pattern-binding-disambiguation.rs
Normal file
67
src/test/compile-fail/pattern-binding-disambiguation.rs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
// file at the top-level directory of this distribution and at
|
||||||
|
// http://rust-lang.org/COPYRIGHT.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
struct UnitStruct;
|
||||||
|
struct TupleStruct();
|
||||||
|
struct BracedStruct{}
|
||||||
|
|
||||||
|
enum E {
|
||||||
|
UnitVariant,
|
||||||
|
TupleVariant(),
|
||||||
|
BracedVariant{},
|
||||||
|
}
|
||||||
|
use E::*;
|
||||||
|
|
||||||
|
const CONST: () = ();
|
||||||
|
static STATIC: () = ();
|
||||||
|
|
||||||
|
fn function() {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let doesnt_matter = 0;
|
||||||
|
|
||||||
|
match UnitStruct {
|
||||||
|
UnitStruct => {} // OK, `UnitStruct` is a unit struct pattern
|
||||||
|
}
|
||||||
|
match doesnt_matter {
|
||||||
|
TupleStruct => {} //~ ERROR match bindings cannot shadow tuple structs
|
||||||
|
}
|
||||||
|
match doesnt_matter {
|
||||||
|
BracedStruct => {} // OK, `BracedStruct` is a fresh binding
|
||||||
|
}
|
||||||
|
match UnitVariant {
|
||||||
|
UnitVariant => {} // OK, `UnitVariant` is a unit variant pattern
|
||||||
|
}
|
||||||
|
match doesnt_matter {
|
||||||
|
TupleVariant => {} //~ ERROR match bindings cannot shadow tuple variants
|
||||||
|
}
|
||||||
|
match doesnt_matter {
|
||||||
|
BracedVariant => {} //~ ERROR match bindings cannot shadow struct variants
|
||||||
|
}
|
||||||
|
match CONST {
|
||||||
|
CONST => {} // OK, `CONST` is a const pattern
|
||||||
|
}
|
||||||
|
match doesnt_matter {
|
||||||
|
STATIC => {} //~ ERROR match bindings cannot shadow statics
|
||||||
|
}
|
||||||
|
match doesnt_matter {
|
||||||
|
function => {} // OK, `function` is a fresh binding
|
||||||
|
}
|
||||||
|
|
||||||
|
let UnitStruct = UnitStruct; // OK, `UnitStruct` is a unit struct pattern
|
||||||
|
let TupleStruct = doesnt_matter; //~ ERROR let bindings cannot shadow tuple structs
|
||||||
|
let BracedStruct = doesnt_matter; // OK, `BracedStruct` is a fresh binding
|
||||||
|
let UnitVariant = UnitVariant; // OK, `UnitVariant` is a unit variant pattern
|
||||||
|
let TupleVariant = doesnt_matter; //~ ERROR let bindings cannot shadow tuple variants
|
||||||
|
let BracedVariant = doesnt_matter; //~ ERROR let bindings cannot shadow struct variants
|
||||||
|
let CONST = CONST; // OK, `CONST` is a const pattern
|
||||||
|
let STATIC = doesnt_matter; //~ ERROR let bindings cannot shadow statics
|
||||||
|
let function = doesnt_matter; // OK, `function` is a fresh binding
|
||||||
|
}
|
||||||
@@ -13,17 +13,14 @@ mod foo {
|
|||||||
pub const d: u8 = 2;
|
pub const d: u8 = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
use foo::b as c; //~ NOTE is imported here
|
use foo::b as c;
|
||||||
use foo::d; //~ NOTE is imported here
|
use foo::d;
|
||||||
|
|
||||||
const a: u8 = 2; //~ NOTE is defined here
|
const a: u8 = 2;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let a = 4; //~ ERROR let bindings cannot shadow constants
|
let a = 4; //~ ERROR refutable pattern in local binding: `_` not covered
|
||||||
//~^ NOTE cannot be named the same as a constant
|
let c = 4; //~ ERROR refutable pattern in local binding: `_` not covered
|
||||||
let c = 4; //~ ERROR let bindings cannot shadow constants
|
let d = 4; //~ ERROR refutable pattern in local binding: `_` not covered
|
||||||
//~^ NOTE cannot be named the same as a constant
|
|
||||||
let d = 4; //~ ERROR let bindings cannot shadow constants
|
|
||||||
//~^ NOTE cannot be named the same as a constant
|
|
||||||
fn f() {} // Check that the `NOTE`s still work with an item here (c.f. issue #35115).
|
fn f() {} // Check that the `NOTE`s still work with an item here (c.f. issue #35115).
|
||||||
}
|
}
|
||||||
20
src/test/ui/const-pattern-irrefutable.stderr
Normal file
20
src/test/ui/const-pattern-irrefutable.stderr
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
error[E0005]: refutable pattern in local binding: `_` not covered
|
||||||
|
--> $DIR/const-pattern-irrefutable.rs:22:9
|
||||||
|
|
|
||||||
|
22 | let a = 4; //~ ERROR refutable pattern in local binding: `_` not covered
|
||||||
|
| ^ interpreted as a constant pattern, not new variable
|
||||||
|
|
||||||
|
error[E0005]: refutable pattern in local binding: `_` not covered
|
||||||
|
--> $DIR/const-pattern-irrefutable.rs:23:9
|
||||||
|
|
|
||||||
|
23 | let c = 4; //~ ERROR refutable pattern in local binding: `_` not covered
|
||||||
|
| ^ interpreted as a constant pattern, not new variable
|
||||||
|
|
||||||
|
error[E0005]: refutable pattern in local binding: `_` not covered
|
||||||
|
--> $DIR/const-pattern-irrefutable.rs:24:9
|
||||||
|
|
|
||||||
|
24 | let d = 4; //~ ERROR refutable pattern in local binding: `_` not covered
|
||||||
|
| ^ interpreted as a constant pattern, not new variable
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
@@ -8,11 +8,6 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use std::option::*;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let None: isize = 42; //~ ERROR let bindings cannot shadow unit variants
|
let None: isize = 42; //~ ERROR mismatched types
|
||||||
log(debug, None);
|
|
||||||
//~^ ERROR cannot find function `log` in this scope
|
|
||||||
//~| ERROR cannot find value `debug` in this scope
|
|
||||||
}
|
}
|
||||||
11
src/test/ui/resolve/name-clash-nullary.stderr
Normal file
11
src/test/ui/resolve/name-clash-nullary.stderr
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/name-clash-nullary.rs:12:7
|
||||||
|
|
|
||||||
|
12 | let None: isize = 42; //~ ERROR mismatched types
|
||||||
|
| ^^^^ expected isize, found enum `std::option::Option`
|
||||||
|
|
|
||||||
|
= note: expected type `isize`
|
||||||
|
found type `std::option::Option<_>`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
Reference in New Issue
Block a user