Skip to content

Commit 16e1839

Browse files
committed
Auto merge of #80080 - rylev:qpath-on-struct, r=petrochenkov
Allow qualified paths in struct construction (both expressions and patterns) Fixes #79658
2 parents c622840 + 6936349 commit 16e1839

38 files changed

+374
-187
lines changed

compiler/rustc_ast/src/ast.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -623,12 +623,13 @@ impl Pat {
623623
PatKind::Ident(_, _, Some(p)) => p.walk(it),
624624

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

628628
// Sequence of patterns.
629-
PatKind::TupleStruct(_, s) | PatKind::Tuple(s) | PatKind::Slice(s) | PatKind::Or(s) => {
630-
s.iter().for_each(|p| p.walk(it))
631-
}
629+
PatKind::TupleStruct(_, _, s)
630+
| PatKind::Tuple(s)
631+
| PatKind::Slice(s)
632+
| PatKind::Or(s) => s.iter().for_each(|p| p.walk(it)),
632633

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

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

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

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

12481249
#[derive(Clone, Encodable, Decodable, Debug)]
12491250
pub struct StructExpr {
1251+
pub qself: Option<QSelf>,
12501252
pub path: Path,
12511253
pub fields: Vec<ExprField>,
12521254
pub rest: StructRest,

compiler/rustc_ast/src/mut_visit.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -1139,15 +1139,17 @@ pub fn noop_visit_pat<T: MutVisitor>(pat: &mut P<Pat>, vis: &mut T) {
11391139
visit_opt(sub, |sub| vis.visit_pat(sub));
11401140
}
11411141
PatKind::Lit(e) => vis.visit_expr(e),
1142-
PatKind::TupleStruct(path, elems) => {
1142+
PatKind::TupleStruct(qself, path, elems) => {
1143+
vis.visit_qself(qself);
11431144
vis.visit_path(path);
11441145
visit_vec(elems, |elem| vis.visit_pat(elem));
11451146
}
11461147
PatKind::Path(qself, path) => {
11471148
vis.visit_qself(qself);
11481149
vis.visit_path(path);
11491150
}
1150-
PatKind::Struct(path, fields, _etc) => {
1151+
PatKind::Struct(qself, path, fields, _etc) => {
1152+
vis.visit_qself(qself);
11511153
vis.visit_path(path);
11521154
fields.flat_map_in_place(|field| vis.flat_map_pat_field(field));
11531155
}
@@ -1333,7 +1335,8 @@ pub fn noop_visit_expr<T: MutVisitor>(
13331335
}
13341336
ExprKind::MacCall(mac) => vis.visit_mac_call(mac),
13351337
ExprKind::Struct(se) => {
1336-
let StructExpr { path, fields, rest } = se.deref_mut();
1338+
let StructExpr { qself, path, fields, rest } = se.deref_mut();
1339+
vis.visit_qself(qself);
13371340
vis.visit_path(path);
13381341
fields.flat_map_in_place(|field| vis.flat_map_expr_field(field));
13391342
match rest {

compiler/rustc_ast/src/visit.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,10 @@ pub fn walk_assoc_ty_constraint<'a, V: Visitor<'a>>(
497497

498498
pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
499499
match pattern.kind {
500-
PatKind::TupleStruct(ref path, ref elems) => {
500+
PatKind::TupleStruct(ref opt_qself, ref path, ref elems) => {
501+
if let Some(ref qself) = *opt_qself {
502+
visitor.visit_ty(&qself.ty);
503+
}
501504
visitor.visit_path(path, pattern.id);
502505
walk_list!(visitor, visit_pat, elems);
503506
}
@@ -507,7 +510,10 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) {
507510
}
508511
visitor.visit_path(path, pattern.id)
509512
}
510-
PatKind::Struct(ref path, ref fields, _) => {
513+
PatKind::Struct(ref opt_qself, ref path, ref fields, _) => {
514+
if let Some(ref qself) = *opt_qself {
515+
visitor.visit_ty(&qself.ty);
516+
}
511517
visitor.visit_path(path, pattern.id);
512518
walk_list!(visitor, visit_pat_field, fields);
513519
}
@@ -740,6 +746,9 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
740746
visitor.visit_anon_const(count)
741747
}
742748
ExprKind::Struct(ref se) => {
749+
if let Some(ref qself) = se.qself {
750+
visitor.visit_ty(&qself.ty);
751+
}
743752
visitor.visit_path(&se.path, expression.id);
744753
walk_list!(visitor, visit_expr_field, &se.fields);
745754
match &se.rest {

compiler/rustc_ast_lowering/src/expr.rs

+11-9
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
237237
hir::ExprKind::Struct(
238238
self.arena.alloc(self.lower_qpath(
239239
e.id,
240-
&None,
240+
&se.qself,
241241
&se.path,
242242
ParamMode::Optional,
243243
ImplTraitContext::disallowed(),
@@ -1041,18 +1041,20 @@ impl<'hir> LoweringContext<'_, 'hir> {
10411041
/// It is not a complete check, but just tries to reject most paths early
10421042
/// if they are not tuple structs.
10431043
/// Type checking will take care of the full validation later.
1044-
fn extract_tuple_struct_path<'a>(&mut self, expr: &'a Expr) -> Option<&'a Path> {
1045-
// For tuple struct destructuring, it must be a non-qualified path (like in patterns).
1046-
if let ExprKind::Path(None, path) = &expr.kind {
1047-
// Does the path resolves to something disallowed in a tuple struct/variant pattern?
1044+
fn extract_tuple_struct_path<'a>(
1045+
&mut self,
1046+
expr: &'a Expr,
1047+
) -> Option<(&'a Option<QSelf>, &'a Path)> {
1048+
if let ExprKind::Path(qself, path) = &expr.kind {
1049+
// Does the path resolve to something disallowed in a tuple struct/variant pattern?
10481050
if let Some(partial_res) = self.resolver.get_partial_res(expr.id) {
10491051
if partial_res.unresolved_segments() == 0
10501052
&& !partial_res.base_res().expected_in_tuple_struct_pat()
10511053
{
10521054
return None;
10531055
}
10541056
}
1055-
return Some(path);
1057+
return Some((qself, path));
10561058
}
10571059
None
10581060
}
@@ -1088,7 +1090,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
10881090
}
10891091
// Tuple structs.
10901092
ExprKind::Call(callee, args) => {
1091-
if let Some(path) = self.extract_tuple_struct_path(callee) {
1093+
if let Some((qself, path)) = self.extract_tuple_struct_path(callee) {
10921094
let (pats, rest) = self.destructure_sequence(
10931095
args,
10941096
"tuple struct or variant",
@@ -1097,7 +1099,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
10971099
);
10981100
let qpath = self.lower_qpath(
10991101
callee.id,
1100-
&None,
1102+
qself,
11011103
path,
11021104
ParamMode::Optional,
11031105
ImplTraitContext::disallowed(),
@@ -1122,7 +1124,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
11221124
}));
11231125
let qpath = self.lower_qpath(
11241126
lhs.id,
1125-
&None,
1127+
&se.qself,
11261128
&se.path,
11271129
ParamMode::Optional,
11281130
ImplTraitContext::disallowed(),

compiler/rustc_ast_lowering/src/pat.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
2121
break self.lower_pat_ident(pattern, binding_mode, ident, lower_sub);
2222
}
2323
PatKind::Lit(ref e) => break hir::PatKind::Lit(self.lower_expr(e)),
24-
PatKind::TupleStruct(ref path, ref pats) => {
24+
PatKind::TupleStruct(ref qself, ref path, ref pats) => {
2525
let qpath = self.lower_qpath(
2626
pattern.id,
27-
&None,
27+
qself,
2828
path,
2929
ParamMode::Optional,
3030
ImplTraitContext::disallowed(),
@@ -47,10 +47,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
4747
);
4848
break hir::PatKind::Path(qpath);
4949
}
50-
PatKind::Struct(ref path, ref fields, etc) => {
50+
PatKind::Struct(ref qself, ref path, ref fields, etc) => {
5151
let qpath = self.lower_qpath(
5252
pattern.id,
53-
&None,
53+
qself,
5454
path,
5555
ParamMode::Optional,
5656
ImplTraitContext::disallowed(),

compiler/rustc_ast_passes/src/feature_gate.rs

+1
Original file line numberDiff line numberDiff line change
@@ -705,6 +705,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
705705
"async closures are unstable",
706706
"to use an async block, remove the `||`: `async {`"
707707
);
708+
gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental");
708709
gate_all!(generators, "yield syntax is experimental");
709710
gate_all!(raw_ref_op, "raw address of syntax is experimental");
710711
gate_all!(const_trait_bound_opt_out, "`?const` on trait bounds is experimental");

compiler/rustc_ast_pretty/src/pprust/state.rs

+19-6
Original file line numberDiff line numberDiff line change
@@ -1713,11 +1713,16 @@ impl<'a> State<'a> {
17131713

17141714
fn print_expr_struct(
17151715
&mut self,
1716+
qself: &Option<ast::QSelf>,
17161717
path: &ast::Path,
17171718
fields: &[ast::ExprField],
17181719
rest: &ast::StructRest,
17191720
) {
1720-
self.print_path(path, true, 0);
1721+
if let Some(qself) = qself {
1722+
self.print_qpath(path, qself, true);
1723+
} else {
1724+
self.print_path(path, true, 0);
1725+
}
17211726
self.s.word("{");
17221727
self.commasep_cmnt(
17231728
Consistent,
@@ -1874,7 +1879,7 @@ impl<'a> State<'a> {
18741879
self.print_expr_repeat(element, count);
18751880
}
18761881
ast::ExprKind::Struct(ref se) => {
1877-
self.print_expr_struct(&se.path, &se.fields, &se.rest);
1882+
self.print_expr_struct(&se.qself, &se.path, &se.fields, &se.rest);
18781883
}
18791884
ast::ExprKind::Tup(ref exprs) => {
18801885
self.print_expr_tup(exprs);
@@ -2340,8 +2345,12 @@ impl<'a> State<'a> {
23402345
self.print_pat(p);
23412346
}
23422347
}
2343-
PatKind::TupleStruct(ref path, ref elts) => {
2344-
self.print_path(path, true, 0);
2348+
PatKind::TupleStruct(ref qself, ref path, ref elts) => {
2349+
if let Some(qself) = qself {
2350+
self.print_qpath(path, qself, true);
2351+
} else {
2352+
self.print_path(path, true, 0);
2353+
}
23452354
self.popen();
23462355
self.commasep(Inconsistent, &elts[..], |s, p| s.print_pat(p));
23472356
self.pclose();
@@ -2355,8 +2364,12 @@ impl<'a> State<'a> {
23552364
PatKind::Path(Some(ref qself), ref path) => {
23562365
self.print_qpath(path, qself, false);
23572366
}
2358-
PatKind::Struct(ref path, ref fields, etc) => {
2359-
self.print_path(path, true, 0);
2367+
PatKind::Struct(ref qself, ref path, ref fields, etc) => {
2368+
if let Some(qself) = qself {
2369+
self.print_qpath(path, qself, true);
2370+
} else {
2371+
self.print_path(path, true, 0);
2372+
}
23602373
self.nbsp();
23612374
self.word_space("{");
23622375
self.commasep_cmnt(

compiler/rustc_expand/src/build.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,12 @@ impl<'a> ExtCtxt<'a> {
275275
) -> P<ast::Expr> {
276276
self.expr(
277277
span,
278-
ast::ExprKind::Struct(P(ast::StructExpr { path, fields, rest: ast::StructRest::None })),
278+
ast::ExprKind::Struct(P(ast::StructExpr {
279+
qself: None,
280+
path,
281+
fields,
282+
rest: ast::StructRest::None,
283+
})),
279284
)
280285
}
281286
pub fn expr_struct_ident(
@@ -405,15 +410,15 @@ impl<'a> ExtCtxt<'a> {
405410
path: ast::Path,
406411
subpats: Vec<P<ast::Pat>>,
407412
) -> P<ast::Pat> {
408-
self.pat(span, PatKind::TupleStruct(path, subpats))
413+
self.pat(span, PatKind::TupleStruct(None, path, subpats))
409414
}
410415
pub fn pat_struct(
411416
&self,
412417
span: Span,
413418
path: ast::Path,
414419
field_pats: Vec<ast::PatField>,
415420
) -> P<ast::Pat> {
416-
self.pat(span, PatKind::Struct(path, field_pats, false))
421+
self.pat(span, PatKind::Struct(None, path, field_pats, false))
417422
}
418423
pub fn pat_tuple(&self, span: Span, pats: Vec<P<ast::Pat>>) -> P<ast::Pat> {
419424
self.pat(span, PatKind::Tuple(pats))

compiler/rustc_feature/src/active.rs

+3
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,9 @@ declare_features! (
663663
/// Allows unnamed fields of struct and union type
664664
(active, unnamed_fields, "1.53.0", Some(49804), None),
665665

666+
/// Allows qualified paths in struct expressions, struct patterns and tuple struct patterns.
667+
(active, more_qualified_paths, "1.54.0", Some(80080), None),
668+
666669
// -------------------------------------------------------------------------
667670
// feature-group-end: actual feature gates
668671
// -------------------------------------------------------------------------

compiler/rustc_lint/src/unused.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -858,10 +858,10 @@ impl EarlyLintPass for UnusedParens {
858858
// The other cases do not contain sub-patterns.
859859
| Wild | Rest | Lit(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) => {},
860860
// These are list-like patterns; parens can always be removed.
861-
TupleStruct(_, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps {
861+
TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps {
862862
self.check_unused_parens_pat(cx, p, false, false);
863863
},
864-
Struct(_, fps, _) => for f in fps {
864+
Struct(_, _, fps, _) => for f in fps {
865865
self.check_unused_parens_pat(cx, &f.pat, false, false);
866866
},
867867
// Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106.

compiler/rustc_parse/src/parser/attr.rs

-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ impl<'a> Parser<'a> {
3232
let mut just_parsed_doc_comment = false;
3333
let start_pos = self.token_cursor.num_next_calls;
3434
loop {
35-
debug!("parse_outer_attributes: self.token={:?}", self.token);
3635
let attr = if self.check(&token::Pound) {
3736
let inner_error_reason = if just_parsed_doc_comment {
3837
"an inner attribute is not permitted following an outer doc comment"

compiler/rustc_parse/src/parser/diagnostics.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ impl<'a> Parser<'a> {
366366
let mut snapshot = self.clone();
367367
let path =
368368
Path { segments: vec![], span: self.prev_token.span.shrink_to_lo(), tokens: None };
369-
let struct_expr = snapshot.parse_struct_expr(path, AttrVec::new(), false);
369+
let struct_expr = snapshot.parse_struct_expr(None, path, AttrVec::new(), false);
370370
let block_tail = self.parse_block_tail(lo, s, AttemptLocalParseRecovery::No);
371371
return Some(match (struct_expr, block_tail) {
372372
(Ok(expr), Err(mut err)) => {

0 commit comments

Comments
 (0)