Skip to content

Commit aafdbb6

Browse files
committed
Treat ExprKind::Path and ExprKind::Struct the same for the purposes of path checking
1 parent 187f3d7 commit aafdbb6

File tree

8 files changed

+172
-41
lines changed

8 files changed

+172
-41
lines changed

src/librustc_ast_lowering/path.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
3131
self.resolver.get_partial_res(id).unwrap_or_else(|| PartialRes::new(Res::Err));
3232

3333
let proj_start = p.segments.len() - partial_res.unresolved_segments();
34+
let mut type_param_in_enum = false;
3435
let path = self.arena.alloc(hir::Path {
3536
res: self.lower_res(partial_res.base_res()),
3637
segments: self.arena.alloc_from_iter(p.segments[..proj_start].iter().enumerate().map(
@@ -55,7 +56,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
5556
Res::Def(DefKind::AssocTy, def_id) if i + 2 == proj_start => {
5657
Some(parent_def_id(self, def_id))
5758
}
58-
Res::Def(DefKind::Variant, def_id) if i + 1 == proj_start => {
59+
Res::Def(DefKind::Variant, def_id)
60+
if i + 2 == proj_start && segment.args.is_some() =>
61+
{
62+
// Handle `Enum::<Param>::Variant {}`.
63+
// We need to choose one here so that the method `res_to_ty` in
64+
// `astconv` works correctly, but it has to be *only* one, so track
65+
// whether we've already set the `DefId` in the previous segment.
66+
type_param_in_enum = true;
67+
Some(parent_def_id(self, def_id))
68+
}
69+
Res::Def(DefKind::Variant, def_id)
70+
if i + 1 == proj_start
71+
&& (segment.args.is_some() || !type_param_in_enum) =>
72+
{
73+
// Handle `Enum::Variant::<Param> {}`.
5974
Some(parent_def_id(self, def_id))
6075
}
6176
Res::Def(DefKind::Struct, def_id)

src/librustc_error_codes/error_codes/E0109.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@ type X = u32; // ok!
1717
type Y = bool; // ok!
1818
```
1919

20-
Note that generic arguments for enum variant constructors go after the variant,
21-
not after the enum. For example, you would write `Option::None::<u32>`,
22-
rather than `Option::<u32>::None`.
20+
Note that generic arguments for enum variant constructors can go after the
21+
variant or after the enum. For example, you can write `Option::None::<u32>`,
22+
rather than `Option::<u32>::None`, but the later style is preferred.

src/librustc_typeck/astconv.rs

+55-17
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,8 @@ enum ConvertedBindingKind<'a, 'tcx> {
125125
Constraint(&'a [hir::GenericBound<'a>]),
126126
}
127127

128-
#[derive(PartialEq)]
129-
enum GenericArgPosition {
128+
#[derive(PartialEq, Debug)]
129+
pub enum GenericArgPosition {
130130
Type,
131131
Value, // e.g., functions
132132
MethodCall,
@@ -195,6 +195,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
195195
span: Span,
196196
def_id: DefId,
197197
item_segment: &hir::PathSegment<'_>,
198+
generic_arg_position: GenericArgPosition,
198199
) -> SubstsRef<'tcx> {
199200
let (substs, assoc_bindings, _) = self.create_substs_for_ast_path(
200201
span,
@@ -203,6 +204,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
203204
item_segment.generic_args(),
204205
item_segment.infer_args,
205206
None,
207+
generic_arg_position,
206208
);
207209

208210
assoc_bindings.first().map(|b| Self::prohibit_assoc_ty_binding(self.tcx(), b.span));
@@ -630,6 +632,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
630632
generic_args: &'a hir::GenericArgs<'_>,
631633
infer_args: bool,
632634
self_ty: Option<Ty<'tcx>>,
635+
generic_arg_position: GenericArgPosition,
633636
) -> (SubstsRef<'tcx>, Vec<ConvertedBinding<'a, 'tcx>>, Option<Vec<Span>>) {
634637
// If the type is parameterized by this region, then replace this
635638
// region with the current anon region binding (in other words,
@@ -661,7 +664,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
661664
span,
662665
&generic_params,
663666
&generic_args,
664-
GenericArgPosition::Type,
667+
generic_arg_position,
665668
self_ty.is_some(),
666669
infer_args,
667670
);
@@ -804,6 +807,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
804807
item_def_id: DefId,
805808
item_segment: &hir::PathSegment<'_>,
806809
parent_substs: SubstsRef<'tcx>,
810+
generic_arg_position: GenericArgPosition,
807811
) -> SubstsRef<'tcx> {
808812
if tcx.generics_of(item_def_id).params.is_empty() {
809813
self.prohibit_generics(slice::from_ref(item_segment));
@@ -817,6 +821,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
817821
item_segment.generic_args(),
818822
item_segment.infer_args,
819823
None,
824+
generic_arg_position,
820825
)
821826
.0
822827
}
@@ -1100,6 +1105,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
11001105
trait_segment.generic_args(),
11011106
trait_segment.infer_args,
11021107
Some(self_ty),
1108+
GenericArgPosition::Type,
11031109
)
11041110
}
11051111

@@ -1416,8 +1422,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
14161422
span: Span,
14171423
did: DefId,
14181424
item_segment: &hir::PathSegment<'_>,
1425+
generic_arg_position: GenericArgPosition,
14191426
) -> Ty<'tcx> {
1420-
let substs = self.ast_path_substs_for_ty(span, did, item_segment);
1427+
let substs = self.ast_path_substs_for_ty(span, did, item_segment, generic_arg_position);
14211428
self.normalize_ty(span, self.tcx().at(span).type_of(did).subst(self.tcx(), substs))
14221429
}
14231430

@@ -2253,6 +2260,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
22532260
item_def_id: DefId,
22542261
trait_segment: &hir::PathSegment<'_>,
22552262
item_segment: &hir::PathSegment<'_>,
2263+
generic_arg_position: GenericArgPosition,
22562264
) -> Ty<'tcx> {
22572265
let tcx = self.tcx();
22582266

@@ -2305,13 +2313,36 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
23052313
item_def_id,
23062314
item_segment,
23072315
trait_ref.substs,
2316+
generic_arg_position,
23082317
);
23092318

23102319
debug!("qpath_to_ty: trait_ref={:?}", trait_ref);
23112320

23122321
self.normalize_ty(span, tcx.mk_projection(item_def_id, item_substs))
23132322
}
23142323

2324+
pub fn prohibit_multiple_params<'a, T: IntoIterator<Item = &'a hir::PathSegment<'a>>>(
2325+
&self,
2326+
segments: T,
2327+
) -> bool {
2328+
let segments_with_params = segments
2329+
.into_iter()
2330+
.filter_map(|segment| segment.args.map(|_| segment.ident.span))
2331+
.collect::<Vec<_>>();
2332+
if segments_with_params.len() <= 1 {
2333+
return false;
2334+
}
2335+
self.tcx()
2336+
.sess
2337+
.struct_span_err(
2338+
segments_with_params,
2339+
"multiple segments with type parameters are not allowed",
2340+
)
2341+
.note("only a single path segment can specify type parameters")
2342+
.emit();
2343+
true
2344+
}
2345+
23152346
pub fn prohibit_generics<'a, T: IntoIterator<Item = &'a hir::PathSegment<'a>>>(
23162347
&self,
23172348
segments: T,
@@ -2509,13 +2540,13 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
25092540
&self,
25102541
opt_self_ty: Option<Ty<'tcx>>,
25112542
path: &hir::Path<'_>,
2512-
permit_variants: bool,
2543+
generic_arg_position: GenericArgPosition,
25132544
) -> Ty<'tcx> {
25142545
let tcx = self.tcx();
25152546

25162547
debug!(
2517-
"res_to_ty(res={:?}, opt_self_ty={:?}, path_segments={:?})",
2518-
path.res, opt_self_ty, path.segments
2548+
"res_to_ty(res={:?}, opt_self_ty={:?}, path_segments={:?} generic_arg_pos={:?})",
2549+
path.res, opt_self_ty, path.segments, generic_arg_position
25192550
);
25202551

25212552
let span = path.span;
@@ -2525,7 +2556,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
25252556
assert!(ty::is_impl_trait_defn(tcx, did).is_none());
25262557
let item_segment = path.segments.split_last().unwrap();
25272558
self.prohibit_generics(item_segment.1);
2528-
let substs = self.ast_path_substs_for_ty(span, did, item_segment.0);
2559+
let substs =
2560+
self.ast_path_substs_for_ty(span, did, item_segment.0, generic_arg_position);
25292561
self.normalize_ty(span, tcx.mk_opaque(did, substs))
25302562
}
25312563
Res::Def(DefKind::Enum, did)
@@ -2535,9 +2567,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
25352567
| Res::Def(DefKind::ForeignTy, did) => {
25362568
assert_eq!(opt_self_ty, None);
25372569
self.prohibit_generics(path.segments.split_last().unwrap().1);
2538-
self.ast_path_to_ty(span, did, path.segments.last().unwrap())
2570+
self.ast_path_to_ty(span, did, path.segments.last().unwrap(), generic_arg_position)
25392571
}
2540-
Res::Def(kind @ DefKind::Variant, def_id) if permit_variants => {
2572+
Res::Def(kind @ DefKind::Variant, def_id)
2573+
if generic_arg_position == GenericArgPosition::Value =>
2574+
{
25412575
// Convert "variant type" as if it were a real type.
25422576
// The resulting `Ty` is type of the variant's enum for now.
25432577
assert_eq!(opt_self_ty, None);
@@ -2546,14 +2580,17 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
25462580
self.def_ids_for_value_path_segments(&path.segments, None, kind, def_id);
25472581
let generic_segs: FxHashSet<_> =
25482582
path_segs.iter().map(|PathSeg(_, index)| index).collect();
2549-
self.prohibit_generics(path.segments.iter().enumerate().filter_map(
2550-
|(index, seg)| {
2551-
if !generic_segs.contains(&index) { Some(seg) } else { None }
2552-
},
2553-
));
2583+
2584+
if !self.prohibit_multiple_params(path.segments.iter()) {
2585+
self.prohibit_generics(path.segments.iter().enumerate().filter_map(
2586+
|(index, seg)| {
2587+
if !generic_segs.contains(&index) { Some(seg) } else { None }
2588+
},
2589+
));
2590+
}
25542591

25552592
let PathSeg(def_id, index) = path_segs.last().unwrap();
2556-
self.ast_path_to_ty(span, *def_id, &path.segments[*index])
2593+
self.ast_path_to_ty(span, *def_id, &path.segments[*index], generic_arg_position)
25572594
}
25582595
Res::Def(DefKind::TyParam, def_id) => {
25592596
assert_eq!(opt_self_ty, None);
@@ -2588,6 +2625,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
25882625
def_id,
25892626
&path.segments[path.segments.len() - 2],
25902627
path.segments.last().unwrap(),
2628+
generic_arg_position,
25912629
)
25922630
}
25932631
Res::PrimTy(prim_ty) => {
@@ -2642,7 +2680,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
26422680
hir::TyKind::Path(hir::QPath::Resolved(ref maybe_qself, ref path)) => {
26432681
debug!("ast_ty_to_ty: maybe_qself={:?} path={:?}", maybe_qself, path);
26442682
let opt_self_ty = maybe_qself.as_ref().map(|qself| self.ast_ty_to_ty(qself));
2645-
self.res_to_ty(opt_self_ty, path, false)
2683+
self.res_to_ty(opt_self_ty, path, GenericArgPosition::Type)
26462684
}
26472685
hir::TyKind::Def(item_id, ref lifetimes) => {
26482686
let did = tcx.hir().local_def_id(item_id.id);

src/librustc_typeck/check/mod.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ mod upvar;
8787
mod wfcheck;
8888
pub mod writeback;
8989

90-
use crate::astconv::{AstConv, PathSeg};
90+
use crate::astconv::{AstConv, GenericArgPosition, PathSeg};
9191
use crate::middle::lang_items;
9292
use rustc::hir::map::blocks::FnLikeNode;
9393
use rustc::hir::map::Map;
@@ -2787,6 +2787,7 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
27872787
item_def_id,
27882788
item_segment,
27892789
trait_ref.substs,
2790+
GenericArgPosition::Type,
27902791
);
27912792

27922793
self.tcx().mk_projection(item_def_id, item_substs)
@@ -4381,7 +4382,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
43814382
match *qpath {
43824383
QPath::Resolved(ref maybe_qself, ref path) => {
43834384
let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself));
4384-
let ty = AstConv::res_to_ty(self, self_ty, path, true);
4385+
let ty = AstConv::res_to_ty(self, self_ty, path, GenericArgPosition::Value);
43854386
(path.res, ty)
43864387
}
43874388
QPath::TypeRelative(ref qself, ref segment) => {

src/librustc_typeck/collect.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
//! At present, however, we do run collection across all items in the
1515
//! crate as a kind of pass. This should eventually be factored away.
1616
17-
use crate::astconv::{AstConv, Bounds, SizedByDefault};
17+
use crate::astconv::{AstConv, Bounds, GenericArgPosition, SizedByDefault};
1818
use crate::check::intrinsic::intrinsic_operation_unsafety;
1919
use crate::constrained_generic_params as cgp;
2020
use crate::lint;
@@ -349,6 +349,7 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> {
349349
item_def_id,
350350
item_segment,
351351
trait_ref.substs,
352+
GenericArgPosition::Type,
352353
);
353354
self.tcx().mk_projection(item_def_id, item_substs)
354355
} else {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// check-pass
2+
3+
pub enum Foo<'a, T: 'a> {
4+
Struct {},
5+
Tuple(),
6+
Unit,
7+
Usage(&'a T),
8+
}
9+
10+
pub fn main() {
11+
let _ = Foo::<String>::Unit;
12+
let _ = Foo::<String>::Tuple();
13+
let _ = Foo::<String>::Struct {};
14+
if let Foo::<String>::Unit = Foo::<String>::Unit {}
15+
if let Foo::<String>::Tuple() = Foo::<String>::Tuple() {}
16+
if let Foo::<String>::Struct {} = (Foo::<String>::Struct {}) {}
17+
// // FIXME: these should be linted against.
18+
let _ = Foo::Unit::<String>;
19+
let _ = Foo::Tuple::<String>();
20+
let _ = Foo::Struct::<String> {};
21+
if let Foo::Unit::<String> = Foo::Unit::<String> {}
22+
if let Foo::Tuple::<String>() = Foo::Tuple::<String>() {}
23+
if let Foo::Struct::<String> {} = (Foo::Struct::<String> {}) {}
24+
}

src/test/ui/nll/user-annotations/pattern_substs_on_brace_enum_variant.rs

+35-6
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,48 @@ enum Foo<'a> {
33
}
44

55
fn in_let() {
6+
let y = 22;
7+
let foo = Foo::Bar { field: &y };
8+
//~^ ERROR `y` does not live long enough
9+
let Foo::Bar::<'static> { field: _z } = foo;
10+
}
11+
12+
fn in_match() {
13+
let y = 22;
14+
let foo = Foo::Bar { field: &y };
15+
//~^ ERROR `y` does not live long enough
16+
match foo {
17+
Foo::Bar::<'static> { field: _z } => {}
18+
}
19+
}
20+
21+
fn in_let_2() {
22+
let y = 22;
23+
let foo = Foo::Bar { field: &y };
24+
//~^ ERROR `y` does not live long enough
25+
let Foo::<'static>::Bar { field: _z } = foo;
26+
}
27+
28+
fn in_match_2() {
29+
let y = 22;
30+
let foo = Foo::Bar { field: &y };
31+
//~^ ERROR `y` does not live long enough
32+
match foo {
33+
Foo::<'static>::Bar { field: _z } => {}
34+
}
35+
}
36+
37+
fn in_let_3() {
638
let y = 22;
739
let foo = Foo::Bar { field: &y };
8-
//~^ ERROR `y` does not live long enough
9-
let Foo::Bar::<'static> { field: _z } = foo;
40+
let Foo::Bar { field: _z } = foo;
1041
}
1142

12-
fn in_match() {
43+
fn in_match_3() {
1344
let y = 22;
1445
let foo = Foo::Bar { field: &y };
15-
//~^ ERROR `y` does not live long enough
1646
match foo {
17-
Foo::Bar::<'static> { field: _z } => {
18-
}
47+
Foo::Bar { field: _z } => {}
1948
}
2049
}
2150

0 commit comments

Comments
 (0)