Auto merge of #80080 - rylev:qpath-on-struct, r=petrochenkov
Allow qualified paths in struct construction (both expressions and patterns) Fixes #79658
This commit is contained in:
commit
16e18395ce
38 changed files with 374 additions and 187 deletions
|
|
@ -19,45 +19,35 @@
|
|||
|
||||
#![feature(rustc_private)]
|
||||
|
||||
extern crate rustc_ast;
|
||||
extern crate rustc_ast_pretty;
|
||||
extern crate rustc_data_structures;
|
||||
extern crate rustc_ast;
|
||||
extern crate rustc_parse;
|
||||
extern crate rustc_session;
|
||||
extern crate rustc_span;
|
||||
|
||||
use rustc_ast::mut_visit::{self, visit_clobber, MutVisitor};
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::*;
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_data_structures::thin_vec::ThinVec;
|
||||
use rustc_parse::new_parser_from_source_str;
|
||||
use rustc_session::parse::ParseSess;
|
||||
use rustc_span::source_map::{Spanned, DUMMY_SP, FileName};
|
||||
use rustc_span::source_map::FilePathMapping;
|
||||
use rustc_span::source_map::{FileName, Spanned, DUMMY_SP};
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_ast::*;
|
||||
use rustc_ast::mut_visit::{self, MutVisitor, visit_clobber};
|
||||
use rustc_ast::ptr::P;
|
||||
|
||||
fn parse_expr(ps: &ParseSess, src: &str) -> Option<P<Expr>> {
|
||||
let src_as_string = src.to_string();
|
||||
|
||||
let mut p = new_parser_from_source_str(
|
||||
ps,
|
||||
FileName::Custom(src_as_string.clone()),
|
||||
src_as_string,
|
||||
);
|
||||
let mut p =
|
||||
new_parser_from_source_str(ps, FileName::Custom(src_as_string.clone()), src_as_string);
|
||||
p.parse_expr().map_err(|mut e| e.cancel()).ok()
|
||||
}
|
||||
|
||||
|
||||
// Helper functions for building exprs
|
||||
fn expr(kind: ExprKind) -> P<Expr> {
|
||||
P(Expr {
|
||||
id: DUMMY_NODE_ID,
|
||||
kind,
|
||||
span: DUMMY_SP,
|
||||
attrs: ThinVec::new(),
|
||||
tokens: None
|
||||
})
|
||||
P(Expr { id: DUMMY_NODE_ID, kind, span: DUMMY_SP, attrs: ThinVec::new(), tokens: None })
|
||||
}
|
||||
|
||||
fn make_x() -> P<Expr> {
|
||||
|
|
@ -83,11 +73,13 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P<Expr>)) {
|
|||
1 => iter_exprs(depth - 1, &mut |e| g(ExprKind::Call(e, vec![]))),
|
||||
2 => {
|
||||
let seg = PathSegment::from_ident(Ident::from_str("x"));
|
||||
iter_exprs(depth - 1, &mut |e| g(ExprKind::MethodCall(
|
||||
seg.clone(), vec![e, make_x()], DUMMY_SP)));
|
||||
iter_exprs(depth - 1, &mut |e| g(ExprKind::MethodCall(
|
||||
seg.clone(), vec![make_x(), e], DUMMY_SP)));
|
||||
},
|
||||
iter_exprs(depth - 1, &mut |e| {
|
||||
g(ExprKind::MethodCall(seg.clone(), vec![e, make_x()], DUMMY_SP))
|
||||
});
|
||||
iter_exprs(depth - 1, &mut |e| {
|
||||
g(ExprKind::MethodCall(seg.clone(), vec![make_x(), e], DUMMY_SP))
|
||||
});
|
||||
}
|
||||
3..=8 => {
|
||||
let op = Spanned {
|
||||
span: DUMMY_SP,
|
||||
|
|
@ -99,14 +91,14 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P<Expr>)) {
|
|||
7 => BinOpKind::Or,
|
||||
8 => BinOpKind::Lt,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
},
|
||||
};
|
||||
iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, e, make_x())));
|
||||
iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, make_x(), e)));
|
||||
},
|
||||
}
|
||||
9 => {
|
||||
iter_exprs(depth - 1, &mut |e| g(ExprKind::Unary(UnOp::Deref, e)));
|
||||
},
|
||||
}
|
||||
10 => {
|
||||
let block = P(Block {
|
||||
stmts: Vec::new(),
|
||||
|
|
@ -116,67 +108,66 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P<Expr>)) {
|
|||
tokens: None,
|
||||
});
|
||||
iter_exprs(depth - 1, &mut |e| g(ExprKind::If(e, block.clone(), None)));
|
||||
},
|
||||
}
|
||||
11 => {
|
||||
let decl = P(FnDecl {
|
||||
inputs: vec![],
|
||||
output: FnRetTy::Default(DUMMY_SP),
|
||||
let decl = P(FnDecl { inputs: vec![], output: FnRetTy::Default(DUMMY_SP) });
|
||||
iter_exprs(depth - 1, &mut |e| {
|
||||
g(ExprKind::Closure(
|
||||
CaptureBy::Value,
|
||||
Async::No,
|
||||
Movability::Movable,
|
||||
decl.clone(),
|
||||
e,
|
||||
DUMMY_SP,
|
||||
))
|
||||
});
|
||||
iter_exprs(depth - 1, &mut |e| g(
|
||||
ExprKind::Closure(CaptureBy::Value,
|
||||
Async::No,
|
||||
Movability::Movable,
|
||||
decl.clone(),
|
||||
e,
|
||||
DUMMY_SP)));
|
||||
},
|
||||
}
|
||||
12 => {
|
||||
iter_exprs(depth - 1, &mut |e| g(ExprKind::Assign(e, make_x(), DUMMY_SP)));
|
||||
iter_exprs(depth - 1, &mut |e| g(ExprKind::Assign(make_x(), e, DUMMY_SP)));
|
||||
},
|
||||
}
|
||||
13 => {
|
||||
iter_exprs(depth - 1, &mut |e| g(ExprKind::Field(e, Ident::from_str("f"))));
|
||||
},
|
||||
}
|
||||
14 => {
|
||||
iter_exprs(depth - 1, &mut |e| g(ExprKind::Range(
|
||||
Some(e), Some(make_x()), RangeLimits::HalfOpen)));
|
||||
iter_exprs(depth - 1, &mut |e| g(ExprKind::Range(
|
||||
Some(make_x()), Some(e), RangeLimits::HalfOpen)));
|
||||
},
|
||||
iter_exprs(depth - 1, &mut |e| {
|
||||
g(ExprKind::Range(Some(e), Some(make_x()), RangeLimits::HalfOpen))
|
||||
});
|
||||
iter_exprs(depth - 1, &mut |e| {
|
||||
g(ExprKind::Range(Some(make_x()), Some(e), RangeLimits::HalfOpen))
|
||||
});
|
||||
}
|
||||
15 => {
|
||||
iter_exprs(
|
||||
depth - 1,
|
||||
&mut |e| g(ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, e)),
|
||||
);
|
||||
},
|
||||
iter_exprs(depth - 1, &mut |e| {
|
||||
g(ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, e))
|
||||
});
|
||||
}
|
||||
16 => {
|
||||
g(ExprKind::Ret(None));
|
||||
iter_exprs(depth - 1, &mut |e| g(ExprKind::Ret(Some(e))));
|
||||
},
|
||||
}
|
||||
17 => {
|
||||
let path = Path::from_ident(Ident::from_str("S"));
|
||||
g(ExprKind::Struct(P(StructExpr {
|
||||
path, fields: vec![], rest: StructRest::Base(make_x())
|
||||
qself: None,
|
||||
path,
|
||||
fields: vec![],
|
||||
rest: StructRest::Base(make_x()),
|
||||
})));
|
||||
},
|
||||
}
|
||||
18 => {
|
||||
iter_exprs(depth - 1, &mut |e| g(ExprKind::Try(e)));
|
||||
},
|
||||
}
|
||||
19 => {
|
||||
let pat = P(Pat {
|
||||
id: DUMMY_NODE_ID,
|
||||
kind: PatKind::Wild,
|
||||
span: DUMMY_SP,
|
||||
tokens: None,
|
||||
});
|
||||
let pat =
|
||||
P(Pat { id: DUMMY_NODE_ID, kind: PatKind::Wild, span: DUMMY_SP, tokens: None });
|
||||
iter_exprs(depth - 1, &mut |e| g(ExprKind::Let(pat.clone(), e)))
|
||||
},
|
||||
}
|
||||
_ => panic!("bad counter value in iter_exprs"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Folders for manipulating the placement of `Paren` nodes. See below for why this is needed.
|
||||
|
||||
/// `MutVisitor` that removes all `ExprKind::Paren` nodes.
|
||||
|
|
@ -192,7 +183,6 @@ impl MutVisitor for RemoveParens {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// `MutVisitor` that inserts `ExprKind::Paren` nodes around every `Expr`.
|
||||
struct AddParens;
|
||||
|
||||
|
|
@ -205,7 +195,7 @@ impl MutVisitor for AddParens {
|
|||
kind: ExprKind::Paren(e),
|
||||
span: DUMMY_SP,
|
||||
attrs: ThinVec::new(),
|
||||
tokens: None
|
||||
tokens: None,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
|
@ -238,9 +228,12 @@ fn run() {
|
|||
RemoveParens.visit_expr(&mut parsed);
|
||||
AddParens.visit_expr(&mut parsed);
|
||||
let text2 = pprust::expr_to_string(&parsed);
|
||||
assert!(text1 == text2,
|
||||
"exprs are not equal:\n e = {:?}\n parsed = {:?}",
|
||||
text1, text2);
|
||||
assert!(
|
||||
text1 == text2,
|
||||
"exprs are not equal:\n e = {:?}\n parsed = {:?}",
|
||||
text1,
|
||||
text2
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
// check-pass
|
||||
|
||||
#![feature(destructuring_assignment)]
|
||||
#![feature(more_qualified_paths)]
|
||||
|
||||
enum E { V() }
|
||||
|
||||
fn main() {
|
||||
<E>::V() = E::V(); // OK, destructuring assignment
|
||||
<E>::V {} = E::V(); // OK, destructuring assignment
|
||||
}
|
||||
4
src/test/ui/associated-types/associated-type-macro.rs
Normal file
4
src/test/ui/associated-types/associated-type-macro.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
fn main() {
|
||||
#[cfg(FALSE)]
|
||||
<() as module>::mac!(); //~ ERROR macros cannot use qualified paths
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
error: macros cannot use qualified paths
|
||||
--> $DIR/associated-type-macro.rs:3:5
|
||||
|
|
||||
LL | <() as module>::mac!();
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// Make sure that users can construct structs through associated types
|
||||
// in both expressions and patterns
|
||||
|
||||
#![feature(more_qualified_paths)]
|
||||
|
||||
// check-pass
|
||||
fn main() {
|
||||
let <Foo as A>::Assoc { br } = <Foo as A>::Assoc { br: 2 };
|
||||
assert!(br == 2);
|
||||
}
|
||||
|
||||
struct StructStruct {
|
||||
br: i8,
|
||||
}
|
||||
|
||||
struct Foo;
|
||||
|
||||
trait A {
|
||||
type Assoc;
|
||||
}
|
||||
|
||||
impl A for Foo {
|
||||
type Assoc = StructStruct;
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// Users cannot yet construct structs through associated types
|
||||
// in both expressions and patterns
|
||||
|
||||
#![feature(more_qualified_paths)]
|
||||
|
||||
fn main() {
|
||||
let <Foo as A>::Assoc(n) = <Foo as A>::Assoc(2);
|
||||
//~^ ERROR expected method or associated constant, found associated type
|
||||
//~| ERROR expected method or associated constant, found associated type
|
||||
assert!(n == 2);
|
||||
}
|
||||
|
||||
struct TupleStruct(i8);
|
||||
|
||||
struct Foo;
|
||||
|
||||
|
||||
trait A {
|
||||
type Assoc;
|
||||
}
|
||||
|
||||
impl A for Foo {
|
||||
type Assoc = TupleStruct;
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
error[E0575]: expected method or associated constant, found associated type `A::Assoc`
|
||||
--> $DIR/associated-type-tuple-struct-construction.rs:7:32
|
||||
|
|
||||
LL | let <Foo as A>::Assoc(n) = <Foo as A>::Assoc(2);
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: can't use a type alias as a constructor
|
||||
|
||||
error[E0575]: expected method or associated constant, found associated type `A::Assoc`
|
||||
--> $DIR/associated-type-tuple-struct-construction.rs:7:9
|
||||
|
|
||||
LL | let <Foo as A>::Assoc(n) = <Foo as A>::Assoc(2);
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: can't use a type alias as a constructor
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0575`.
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
fn main() {
|
||||
// destructure through a qualified path
|
||||
let <Foo as A>::Assoc { br } = StructStruct { br: 2 };
|
||||
//~^ ERROR usage of qualified paths in this context is experimental
|
||||
let _ = <Foo as A>::Assoc { br: 2 };
|
||||
//~^ ERROR usage of qualified paths in this context is experimental
|
||||
let <E>::V(..) = E::V(0);
|
||||
//~^ ERROR usage of qualified paths in this context is experimental
|
||||
}
|
||||
|
||||
struct StructStruct {
|
||||
br: i8,
|
||||
}
|
||||
|
||||
struct Foo;
|
||||
|
||||
trait A {
|
||||
type Assoc;
|
||||
}
|
||||
|
||||
impl A for Foo {
|
||||
type Assoc = StructStruct;
|
||||
}
|
||||
|
||||
enum E {
|
||||
V(u8)
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
error[E0658]: usage of qualified paths in this context is experimental
|
||||
--> $DIR/feature-gate-more-qualified-paths.rs:3:9
|
||||
|
|
||||
LL | let <Foo as A>::Assoc { br } = StructStruct { br: 2 };
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #80080 <https://github.com/rust-lang/rust/issues/80080> for more information
|
||||
= help: add `#![feature(more_qualified_paths)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: usage of qualified paths in this context is experimental
|
||||
--> $DIR/feature-gate-more-qualified-paths.rs:5:13
|
||||
|
|
||||
LL | let _ = <Foo as A>::Assoc { br: 2 };
|
||||
| ^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #80080 <https://github.com/rust-lang/rust/issues/80080> for more information
|
||||
= help: add `#![feature(more_qualified_paths)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: usage of qualified paths in this context is experimental
|
||||
--> $DIR/feature-gate-more-qualified-paths.rs:7:9
|
||||
|
|
||||
LL | let <E>::V(..) = E::V(0);
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: see issue #80080 <https://github.com/rust-lang/rust/issues/80080> for more information
|
||||
= help: add `#![feature(more_qualified_paths)]` to the crate attributes to enable
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
fn main() {
|
||||
match 10 {
|
||||
<T as Trait>::Type{key: value} => (),
|
||||
//~^ ERROR unexpected `{` after qualified path
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
error: unexpected `{` after qualified path
|
||||
--> $DIR/brace-after-qualified-path-in-match.rs:3:27
|
||||
|
|
||||
LL | <T as Trait>::Type{key: value} => (),
|
||||
| ------------------^ unexpected `{` after qualified path
|
||||
| |
|
||||
| the qualified path
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
fn main() {
|
||||
match 10 {
|
||||
<T as Trait>::Type(2) => (),
|
||||
//~^ ERROR unexpected `(` after qualified path
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
error: unexpected `(` after qualified path
|
||||
--> $DIR/paren-after-qualified-path-in-match.rs:3:27
|
||||
|
|
||||
LL | <T as Trait>::Type(2) => (),
|
||||
| ------------------^ unexpected `(` after qualified path
|
||||
| |
|
||||
| the qualified path
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue