Skip to content

Commit

Permalink
Auto merge of #80080 - rylev:qpath-on-struct, r=petrochenkov
Browse files Browse the repository at this point in the history
Allow qualified paths in struct construction (both expressions and patterns)

Fixes #79658
  • Loading branch information
bors committed Jun 10, 2021
2 parents c622840 + 6936349 commit 16e1839
Show file tree
Hide file tree
Showing 38 changed files with 374 additions and 187 deletions.
14 changes: 8 additions & 6 deletions compiler/rustc_ast/src/ast.rs
Expand Up @@ -623,12 +623,13 @@ impl Pat {
PatKind::Ident(_, _, Some(p)) => p.walk(it),

// Walk into each field of struct.
PatKind::Struct(_, fields, _) => fields.iter().for_each(|field| field.pat.walk(it)),
PatKind::Struct(_, _, fields, _) => fields.iter().for_each(|field| field.pat.walk(it)),

// Sequence of patterns.
PatKind::TupleStruct(_, s) | PatKind::Tuple(s) | PatKind::Slice(s) | PatKind::Or(s) => {
s.iter().for_each(|p| p.walk(it))
}
PatKind::TupleStruct(_, _, s)
| PatKind::Tuple(s)
| PatKind::Slice(s)
| PatKind::Or(s) => s.iter().for_each(|p| p.walk(it)),

// Trivial wrappers over inner patterns.
PatKind::Box(s) | PatKind::Ref(s, _) | PatKind::Paren(s) => s.walk(it),
Expand Down Expand Up @@ -701,10 +702,10 @@ pub enum PatKind {

/// A struct or struct variant pattern (e.g., `Variant {x, y, ..}`).
/// The `bool` is `true` in the presence of a `..`.
Struct(Path, Vec<PatField>, /* recovered */ bool),
Struct(Option<QSelf>, Path, Vec<PatField>, /* recovered */ bool),

/// A tuple struct/variant pattern (`Variant(x, y, .., z)`).
TupleStruct(Path, Vec<P<Pat>>),
TupleStruct(Option<QSelf>, Path, Vec<P<Pat>>),

/// An or-pattern `A | B | C`.
/// Invariant: `pats.len() >= 2`.
Expand Down Expand Up @@ -1247,6 +1248,7 @@ pub enum StructRest {

#[derive(Clone, Encodable, Decodable, Debug)]
pub struct StructExpr {
pub qself: Option<QSelf>,
pub path: Path,
pub fields: Vec<ExprField>,
pub rest: StructRest,
Expand Down
9 changes: 6 additions & 3 deletions compiler/rustc_ast/src/mut_visit.rs
Expand Up @@ -1139,15 +1139,17 @@ pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, vis: &mut T) {
visit_opt(sub, |sub| vis.visit_pat(sub));
}
PatKind::Lit(e) => vis.visit_expr(e),
PatKind::TupleStruct(path, elems) => {
PatKind::TupleStruct(qself, path, elems) => {
vis.visit_qself(qself);
vis.visit_path(path);
visit_vec(elems, |elem| vis.visit_pat(elem));
}
PatKind::Path(qself, path) => {
vis.visit_qself(qself);
vis.visit_path(path);
}
PatKind::Struct(path, fields, _etc) => {
PatKind::Struct(qself, path, fields, _etc) => {
vis.visit_qself(qself);
vis.visit_path(path);
fields.flat_map_in_place(|field| vis.flat_map_pat_field(field));
}
Expand Down Expand Up @@ -1333,7 +1335,8 @@ pub fn noop_visit_expr<T: MutVisitor>(
}
ExprKind::MacCall(mac) => vis.visit_mac_call(mac),
ExprKind::Struct(se) => {
let StructExpr { path, fields, rest } = se.deref_mut();
let StructExpr { qself, path, fields, rest } = se.deref_mut();
vis.visit_qself(qself);
vis.visit_path(path);
fields.flat_map_in_place(|field| vis.flat_map_expr_field(field));
match rest {
Expand Down
13 changes: 11 additions & 2 deletions compiler/rustc_ast/src/visit.rs
Expand Up @@ -497,7 +497,10 @@ pub fn walk_assoc_ty_constraint<'a, V: Visitor<'a>>(

pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
match pattern.kind {
PatKind::TupleStruct(ref path, ref elems) => {
PatKind::TupleStruct(ref opt_qself, ref path, ref elems) => {
if let Some(ref qself) = *opt_qself {
visitor.visit_ty(&qself.ty);
}
visitor.visit_path(path, pattern.id);
walk_list!(visitor, visit_pat, elems);
}
Expand All @@ -507,7 +510,10 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
}
visitor.visit_path(path, pattern.id)
}
PatKind::Struct(ref path, ref fields, _) => {
PatKind::Struct(ref opt_qself, ref path, ref fields, _) => {
if let Some(ref qself) = *opt_qself {
visitor.visit_ty(&qself.ty);
}
visitor.visit_path(path, pattern.id);
walk_list!(visitor, visit_pat_field, fields);
}
Expand Down Expand Up @@ -740,6 +746,9 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
visitor.visit_anon_const(count)
}
ExprKind::Struct(ref se) => {
if let Some(ref qself) = se.qself {
visitor.visit_ty(&qself.ty);
}
visitor.visit_path(&se.path, expression.id);
walk_list!(visitor, visit_expr_field, &se.fields);
match &se.rest {
Expand Down
20 changes: 11 additions & 9 deletions compiler/rustc_ast_lowering/src/expr.rs
Expand Up @@ -237,7 +237,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
hir::ExprKind::Struct(
self.arena.alloc(self.lower_qpath(
e.id,
&None,
&se.qself,
&se.path,
ParamMode::Optional,
ImplTraitContext::disallowed(),
Expand Down Expand Up @@ -1041,18 +1041,20 @@ impl<'hir> LoweringContext<'_, 'hir> {
/// It is not a complete check, but just tries to reject most paths early
/// if they are not tuple structs.
/// Type checking will take care of the full validation later.
fn extract_tuple_struct_path<'a>(&mut self, expr: &'a Expr) -> Option<&'a Path> {
// For tuple struct destructuring, it must be a non-qualified path (like in patterns).
if let ExprKind::Path(None, path) = &expr.kind {
// Does the path resolves to something disallowed in a tuple struct/variant pattern?
fn extract_tuple_struct_path<'a>(
&mut self,
expr: &'a Expr,
) -> Option<(&'a Option<QSelf>, &'a Path)> {
if let ExprKind::Path(qself, path) = &expr.kind {
// Does the path resolve to something disallowed in a tuple struct/variant pattern?
if let Some(partial_res) = self.resolver.get_partial_res(expr.id) {
if partial_res.unresolved_segments() == 0
&& !partial_res.base_res().expected_in_tuple_struct_pat()
{
return None;
}
}
return Some(path);
return Some((qself, path));
}
None
}
Expand Down Expand Up @@ -1088,7 +1090,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
// Tuple structs.
ExprKind::Call(callee, args) => {
if let Some(path) = self.extract_tuple_struct_path(callee) {
if let Some((qself, path)) = self.extract_tuple_struct_path(callee) {
let (pats, rest) = self.destructure_sequence(
args,
"tuple struct or variant",
Expand All @@ -1097,7 +1099,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
);
let qpath = self.lower_qpath(
callee.id,
&None,
qself,
path,
ParamMode::Optional,
ImplTraitContext::disallowed(),
Expand All @@ -1122,7 +1124,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
}));
let qpath = self.lower_qpath(
lhs.id,
&None,
&se.qself,
&se.path,
ParamMode::Optional,
ImplTraitContext::disallowed(),
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_ast_lowering/src/pat.rs
Expand Up @@ -21,10 +21,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
break self.lower_pat_ident(pattern, binding_mode, ident, lower_sub);
}
PatKind::Lit(ref e) => break hir::PatKind::Lit(self.lower_expr(e)),
PatKind::TupleStruct(ref path, ref pats) => {
PatKind::TupleStruct(ref qself, ref path, ref pats) => {
let qpath = self.lower_qpath(
pattern.id,
&None,
qself,
path,
ParamMode::Optional,
ImplTraitContext::disallowed(),
Expand All @@ -47,10 +47,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
);
break hir::PatKind::Path(qpath);
}
PatKind::Struct(ref path, ref fields, etc) => {
PatKind::Struct(ref qself, ref path, ref fields, etc) => {
let qpath = self.lower_qpath(
pattern.id,
&None,
qself,
path,
ParamMode::Optional,
ImplTraitContext::disallowed(),
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Expand Up @@ -705,6 +705,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
"async closures are unstable",
"to use an async block, remove the `||`: `async {`"
);
gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental");
gate_all!(generators, "yield syntax is experimental");
gate_all!(raw_ref_op, "raw address of syntax is experimental");
gate_all!(const_trait_bound_opt_out, "`?const` on trait bounds is experimental");
Expand Down
25 changes: 19 additions & 6 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Expand Up @@ -1713,11 +1713,16 @@ impl<'a> State<'a> {

fn print_expr_struct(
&mut self,
qself: &Option<ast::QSelf>,
path: &ast::Path,
fields: &[ast::ExprField],
rest: &ast::StructRest,
) {
self.print_path(path, true, 0);
if let Some(qself) = qself {
self.print_qpath(path, qself, true);
} else {
self.print_path(path, true, 0);
}
self.s.word("{");
self.commasep_cmnt(
Consistent,
Expand Down Expand Up @@ -1874,7 +1879,7 @@ impl<'a> State<'a> {
self.print_expr_repeat(element, count);
}
ast::ExprKind::Struct(ref se) => {
self.print_expr_struct(&se.path, &se.fields, &se.rest);
self.print_expr_struct(&se.qself, &se.path, &se.fields, &se.rest);
}
ast::ExprKind::Tup(ref exprs) => {
self.print_expr_tup(exprs);
Expand Down Expand Up @@ -2340,8 +2345,12 @@ impl<'a> State<'a> {
self.print_pat(p);
}
}
PatKind::TupleStruct(ref path, ref elts) => {
self.print_path(path, true, 0);
PatKind::TupleStruct(ref qself, ref path, ref elts) => {
if let Some(qself) = qself {
self.print_qpath(path, qself, true);
} else {
self.print_path(path, true, 0);
}
self.popen();
self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(p));
self.pclose();
Expand All @@ -2355,8 +2364,12 @@ impl<'a> State<'a> {
PatKind::Path(Some(ref qself), ref path) => {
self.print_qpath(path, qself, false);
}
PatKind::Struct(ref path, ref fields, etc) => {
self.print_path(path, true, 0);
PatKind::Struct(ref qself, ref path, ref fields, etc) => {
if let Some(qself) = qself {
self.print_qpath(path, qself, true);
} else {
self.print_path(path, true, 0);
}
self.nbsp();
self.word_space("{");
self.commasep_cmnt(
Expand Down
11 changes: 8 additions & 3 deletions compiler/rustc_expand/src/build.rs
Expand Up @@ -275,7 +275,12 @@ impl<'a> ExtCtxt<'a> {
) -> P<ast::Expr> {
self.expr(
span,
ast::ExprKind::Struct(P(ast::StructExpr { path, fields, rest: ast::StructRest::None })),
ast::ExprKind::Struct(P(ast::StructExpr {
qself: None,
path,
fields,
rest: ast::StructRest::None,
})),
)
}
pub fn expr_struct_ident(
Expand Down Expand Up @@ -405,15 +410,15 @@ impl<'a> ExtCtxt<'a> {
path: ast::Path,
subpats: Vec<P<ast::Pat>>,
) -> P<ast::Pat> {
self.pat(span, PatKind::TupleStruct(path, subpats))
self.pat(span, PatKind::TupleStruct(None, path, subpats))
}
pub fn pat_struct(
&self,
span: Span,
path: ast::Path,
field_pats: Vec<ast::PatField>,
) -> P<ast::Pat> {
self.pat(span, PatKind::Struct(path, field_pats, false))
self.pat(span, PatKind::Struct(None, path, field_pats, false))
}
pub fn pat_tuple(&self, span: Span, pats: Vec<P<ast::Pat>>) -> P<ast::Pat> {
self.pat(span, PatKind::Tuple(pats))
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_feature/src/active.rs
Expand Up @@ -663,6 +663,9 @@ declare_features! (
/// Allows unnamed fields of struct and union type
(active, unnamed_fields, "1.53.0", Some(49804), None),

/// Allows qualified paths in struct expressions, struct patterns and tuple struct patterns.
(active, more_qualified_paths, "1.54.0", Some(80080), None),

// -------------------------------------------------------------------------
// feature-group-end: actual feature gates
// -------------------------------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_lint/src/unused.rs
Expand Up @@ -858,10 +858,10 @@ impl EarlyLintPass for UnusedParens {
// The other cases do not contain sub-patterns.
| Wild | Rest | Lit(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) => {},
// These are list-like patterns; parens can always be removed.
TupleStruct(_, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps {
TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps {
self.check_unused_parens_pat(cx, p, false, false);
},
Struct(_, fps, _) => for f in fps {
Struct(_, _, fps, _) => for f in fps {
self.check_unused_parens_pat(cx, &f.pat, false, false);
},
// Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106.
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_parse/src/parser/attr.rs
Expand Up @@ -32,7 +32,6 @@ impl<'a> Parser<'a> {
let mut just_parsed_doc_comment = false;
let start_pos = self.token_cursor.num_next_calls;
loop {
debug!("parse_outer_attributes: self.token={:?}", self.token);
let attr = if self.check(&token::Pound) {
let inner_error_reason = if just_parsed_doc_comment {
"an inner attribute is not permitted following an outer doc comment"
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_parse/src/parser/diagnostics.rs
Expand Up @@ -366,7 +366,7 @@ impl<'a> Parser<'a> {
let mut snapshot = self.clone();
let path =
Path { segments: vec![], span: self.prev_token.span.shrink_to_lo(), tokens: None };
let struct_expr = snapshot.parse_struct_expr(path, AttrVec::new(), false);
let struct_expr = snapshot.parse_struct_expr(None, path, AttrVec::new(), false);
let block_tail = self.parse_block_tail(lo, s, AttemptLocalParseRecovery::No);
return Some(match (struct_expr, block_tail) {
(Ok(expr), Err(mut err)) => {
Expand Down

0 comments on commit 16e1839

Please sign in to comment.