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:
bors 2021-06-10 20:32:43 +00:00
commit 16e18395ce
38 changed files with 374 additions and 187 deletions

View file

@ -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
);
}
});
}

View file

@ -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
}

View file

@ -0,0 +1,4 @@
fn main() {
#[cfg(FALSE)]
<() as module>::mac!(); //~ ERROR macros cannot use qualified paths
}

View file

@ -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

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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`.

View file

@ -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)
}

View file

@ -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`.

View file

@ -1,7 +0,0 @@
fn main() {
match 10 {
<T as Trait>::Type{key: value} => (),
//~^ ERROR unexpected `{` after qualified path
_ => (),
}
}

View file

@ -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

View file

@ -1,7 +0,0 @@
fn main() {
match 10 {
<T as Trait>::Type(2) => (),
//~^ ERROR unexpected `(` after qualified path
_ => (),
}
}

View file

@ -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