Skip to content

Commit fa07e62

Browse files
author
Alexander Regueiro
committed
Fixed handling of unit variants of aliased enums in type NS.
1 parent 8eb1a9e commit fa07e62

File tree

5 files changed

+215
-294
lines changed

5 files changed

+215
-294
lines changed

src/librustc_typeck/astconv.rs

Lines changed: 147 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ use std::iter;
3232
use std::slice;
3333

3434
use super::{check_type_alias_enum_variants_enabled};
35+
use rustc_data_structures::fx::FxHashSet;
36+
37+
#[derive(Debug)]
38+
pub struct PathSeg(pub DefId, pub usize);
3539

3640
pub trait AstConv<'gcx, 'tcx> {
3741
fn tcx<'a>(&'a self) -> TyCtxt<'a, 'gcx, 'tcx>;
@@ -1470,6 +1474,134 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
14701474
err.span_label(span, "associated type not allowed here").emit();
14711475
}
14721476

1477+
pub fn def_ids_for_path_segments(&self,
1478+
segments: &[hir::PathSegment],
1479+
self_ty: Option<Ty<'tcx>>,
1480+
def: Def)
1481+
-> Vec<PathSeg> {
1482+
// We need to extract the type parameters supplied by the user in
1483+
// the path `path`. Due to the current setup, this is a bit of a
1484+
// tricky-process; the problem is that resolve only tells us the
1485+
// end-point of the path resolution, and not the intermediate steps.
1486+
// Luckily, we can (at least for now) deduce the intermediate steps
1487+
// just from the end-point.
1488+
//
1489+
// There are basically five cases to consider:
1490+
//
1491+
// 1. Reference to a constructor of a struct:
1492+
//
1493+
// struct Foo<T>(...)
1494+
//
1495+
// In this case, the parameters are declared in the type space.
1496+
//
1497+
// 2. Reference to a constructor of an enum variant:
1498+
//
1499+
// enum E<T> { Foo(...) }
1500+
//
1501+
// In this case, the parameters are defined in the type space,
1502+
// but may be specified either on the type or the variant.
1503+
//
1504+
// 3. Reference to a fn item or a free constant:
1505+
//
1506+
// fn foo<T>() { }
1507+
//
1508+
// In this case, the path will again always have the form
1509+
// `a::b::foo::<T>` where only the final segment should have
1510+
// type parameters. However, in this case, those parameters are
1511+
// declared on a value, and hence are in the `FnSpace`.
1512+
//
1513+
// 4. Reference to a method or an associated constant:
1514+
//
1515+
// impl<A> SomeStruct<A> {
1516+
// fn foo<B>(...)
1517+
// }
1518+
//
1519+
// Here we can have a path like
1520+
// `a::b::SomeStruct::<A>::foo::<B>`, in which case parameters
1521+
// may appear in two places. The penultimate segment,
1522+
// `SomeStruct::<A>`, contains parameters in TypeSpace, and the
1523+
// final segment, `foo::<B>` contains parameters in fn space.
1524+
//
1525+
// 5. Reference to a local variable
1526+
//
1527+
// Local variables can't have any type parameters.
1528+
//
1529+
// The first step then is to categorize the segments appropriately.
1530+
1531+
let tcx = self.tcx();
1532+
1533+
assert!(!segments.is_empty());
1534+
let last = segments.len() - 1;
1535+
1536+
let mut path_segs = vec![];
1537+
1538+
match def {
1539+
// Case 1. Reference to a struct constructor.
1540+
Def::StructCtor(def_id, ..) |
1541+
Def::SelfCtor(.., def_id) => {
1542+
// Everything but the final segment should have no
1543+
// parameters at all.
1544+
let generics = tcx.generics_of(def_id);
1545+
// Variant and struct constructors use the
1546+
// generics of their parent type definition.
1547+
let generics_def_id = generics.parent.unwrap_or(def_id);
1548+
path_segs.push(PathSeg(generics_def_id, last));
1549+
}
1550+
1551+
// Case 2. Reference to a variant constructor.
1552+
Def::Variant(def_id) |
1553+
Def::VariantCtor(def_id, ..) => {
1554+
let adt_def = self_ty.and_then(|t| t.ty_adt_def());
1555+
let (generics_def_id, index) = if let Some(adt_def) = adt_def {
1556+
debug_assert!(adt_def.is_enum());
1557+
(adt_def.did, last)
1558+
} else if last >= 1 && segments[last - 1].args.is_some() {
1559+
// Everything but the penultimate segment should have no
1560+
// parameters at all.
1561+
let enum_def_id = tcx.parent_def_id(def_id).unwrap();
1562+
(enum_def_id, last - 1)
1563+
} else {
1564+
// FIXME: lint here recommending `Enum::<...>::Variant` form
1565+
// instead of `Enum::Variant::<...>` form.
1566+
1567+
// Everything but the final segment should have no
1568+
// parameters at all.
1569+
let generics = tcx.generics_of(def_id);
1570+
// Variant and struct constructors use the
1571+
// generics of their parent type definition.
1572+
(generics.parent.unwrap_or(def_id), last)
1573+
};
1574+
path_segs.push(PathSeg(generics_def_id, index));
1575+
}
1576+
1577+
// Case 3. Reference to a top-level value.
1578+
Def::Fn(def_id) |
1579+
Def::Const(def_id) |
1580+
Def::Static(def_id, _) => {
1581+
path_segs.push(PathSeg(def_id, last));
1582+
}
1583+
1584+
// Case 4. Reference to a method or associated const.
1585+
Def::Method(def_id) |
1586+
Def::AssociatedConst(def_id) => {
1587+
if segments.len() >= 2 {
1588+
let generics = tcx.generics_of(def_id);
1589+
path_segs.push(PathSeg(generics.parent.unwrap(), last - 1));
1590+
}
1591+
path_segs.push(PathSeg(def_id, last));
1592+
}
1593+
1594+
// Case 5. Local variable, no generics.
1595+
Def::Local(..) | Def::Upvar(..) => {}
1596+
1597+
_ => bug!("unexpected definition: {:?}", def),
1598+
}
1599+
1600+
debug!("path_segs = {:?}", path_segs);
1601+
1602+
path_segs
1603+
}
1604+
14731605
// Check a type `Path` and convert it to a `Ty`.
14741606
pub fn def_to_ty(&self,
14751607
opt_self_ty: Option<Ty<'tcx>>,
@@ -1500,14 +1632,24 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
15001632
self.prohibit_generics(path.segments.split_last().unwrap().1);
15011633
self.ast_path_to_ty(span, did, path.segments.last().unwrap())
15021634
}
1503-
Def::Variant(did) if permit_variants => {
1635+
Def::Variant(_) if permit_variants => {
15041636
// Convert "variant type" as if it were a real type.
15051637
// The resulting `Ty` is type of the variant's enum for now.
15061638
assert_eq!(opt_self_ty, None);
1507-
self.prohibit_generics(path.segments.split_last().unwrap().1);
1508-
self.ast_path_to_ty(span,
1509-
tcx.parent_def_id(did).unwrap(),
1510-
path.segments.last().unwrap())
1639+
1640+
let path_segs = self.def_ids_for_path_segments(&path.segments, None, path.def);
1641+
let generic_segs: FxHashSet<_> =
1642+
path_segs.iter().map(|PathSeg(_, index)| index).collect();
1643+
self.prohibit_generics(path.segments.iter().enumerate().filter_map(|(index, seg)| {
1644+
if !generic_segs.contains(&index) {
1645+
Some(seg)
1646+
} else {
1647+
None
1648+
}
1649+
}));
1650+
1651+
let PathSeg(def_id, index) = path_segs.last().unwrap();
1652+
self.ast_path_to_ty(span, *def_id, &path.segments[*index])
15111653
}
15121654
Def::TyParam(did) => {
15131655
assert_eq!(opt_self_ty, None);

src/librustc_typeck/check/mod.rs

Lines changed: 2 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ mod generator_interior;
8383
mod intrinsic;
8484
mod op;
8585

86-
use astconv::AstConv;
86+
use astconv::{AstConv, PathSeg};
8787
use errors::{Applicability, DiagnosticBuilder, DiagnosticId};
8888
use rustc::hir::{self, ExprKind, GenericArg, ItemKind, Node, PatKind, QPath};
8989
use rustc::hir::def::{CtorKind, Def};
@@ -507,9 +507,6 @@ impl<'gcx, 'tcx> EnclosingBreakables<'gcx, 'tcx> {
507507
}
508508
}
509509

510-
#[derive(Debug)]
511-
struct PathSeg(DefId, usize);
512-
513510
pub struct FnCtxt<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
514511
body_id: ast::NodeId,
515512

@@ -5060,131 +5057,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
50605057
Applicability::MachineApplicable);
50615058
}
50625059

5063-
fn def_ids_for_path_segments(&self,
5064-
segments: &[hir::PathSegment],
5065-
self_ty: Option<Ty<'tcx>>,
5066-
def: Def)
5067-
-> Vec<PathSeg> {
5068-
// We need to extract the type parameters supplied by the user in
5069-
// the path `path`. Due to the current setup, this is a bit of a
5070-
// tricky-process; the problem is that resolve only tells us the
5071-
// end-point of the path resolution, and not the intermediate steps.
5072-
// Luckily, we can (at least for now) deduce the intermediate steps
5073-
// just from the end-point.
5074-
//
5075-
// There are basically five cases to consider:
5076-
//
5077-
// 1. Reference to a constructor of a struct:
5078-
//
5079-
// struct Foo<T>(...)
5080-
//
5081-
// In this case, the parameters are declared in the type space.
5082-
//
5083-
// 2. Reference to a constructor of an enum variant:
5084-
//
5085-
// enum E<T> { Foo(...) }
5086-
//
5087-
// In this case, the parameters are defined in the type space,
5088-
// but may be specified either on the type or the variant.
5089-
//
5090-
// 3. Reference to a fn item or a free constant:
5091-
//
5092-
// fn foo<T>() { }
5093-
//
5094-
// In this case, the path will again always have the form
5095-
// `a::b::foo::<T>` where only the final segment should have
5096-
// type parameters. However, in this case, those parameters are
5097-
// declared on a value, and hence are in the `FnSpace`.
5098-
//
5099-
// 4. Reference to a method or an associated constant:
5100-
//
5101-
// impl<A> SomeStruct<A> {
5102-
// fn foo<B>(...)
5103-
// }
5104-
//
5105-
// Here we can have a path like
5106-
// `a::b::SomeStruct::<A>::foo::<B>`, in which case parameters
5107-
// may appear in two places. The penultimate segment,
5108-
// `SomeStruct::<A>`, contains parameters in TypeSpace, and the
5109-
// final segment, `foo::<B>` contains parameters in fn space.
5110-
//
5111-
// 5. Reference to a local variable
5112-
//
5113-
// Local variables can't have any type parameters.
5114-
//
5115-
// The first step then is to categorize the segments appropriately.
5116-
5117-
assert!(!segments.is_empty());
5118-
let last = segments.len() - 1;
5119-
5120-
let mut path_segs = vec![];
5121-
5122-
match def {
5123-
// Case 1. Reference to a struct constructor.
5124-
Def::StructCtor(def_id, ..) |
5125-
Def::SelfCtor(.., def_id) => {
5126-
// Everything but the final segment should have no
5127-
// parameters at all.
5128-
let generics = self.tcx.generics_of(def_id);
5129-
// Variant and struct constructors use the
5130-
// generics of their parent type definition.
5131-
let generics_def_id = generics.parent.unwrap_or(def_id);
5132-
path_segs.push(PathSeg(generics_def_id, last));
5133-
}
5134-
5135-
// Case 2. Reference to a variant constructor.
5136-
Def::VariantCtor(def_id, ..) => {
5137-
let adt_def = self_ty.and_then(|t| t.ty_adt_def());
5138-
let (generics_def_id, index) = if let Some(adt_def) = adt_def {
5139-
debug_assert!(adt_def.is_enum());
5140-
(adt_def.did, last)
5141-
} else if last >= 1 && segments[last - 1].args.is_some() {
5142-
// Everything but the penultimate segment should have no
5143-
// parameters at all.
5144-
let enum_def_id = self.tcx.parent_def_id(def_id).unwrap();
5145-
(enum_def_id, last - 1)
5146-
} else {
5147-
// FIXME: lint here suggesting `Enum::<...>::Variant` form
5148-
// instead of `Enum::Variant::<...>` form.
5149-
5150-
// Everything but the final segment should have no
5151-
// parameters at all.
5152-
let generics = self.tcx.generics_of(def_id);
5153-
// Variant and struct constructors use the
5154-
// generics of their parent type definition.
5155-
(generics.parent.unwrap_or(def_id), last)
5156-
};
5157-
path_segs.push(PathSeg(generics_def_id, index));
5158-
}
5159-
5160-
// Case 3. Reference to a top-level value.
5161-
Def::Fn(def_id) |
5162-
Def::Const(def_id) |
5163-
Def::Static(def_id, _) => {
5164-
path_segs.push(PathSeg(def_id, last));
5165-
}
5166-
5167-
// Case 4. Reference to a method or associated const.
5168-
Def::Method(def_id) |
5169-
Def::AssociatedConst(def_id) => {
5170-
if segments.len() >= 2 {
5171-
let generics = self.tcx.generics_of(def_id);
5172-
path_segs.push(PathSeg(generics.parent.unwrap(), last - 1));
5173-
}
5174-
path_segs.push(PathSeg(def_id, last));
5175-
}
5176-
5177-
// Case 5. Local variable, no generics.
5178-
Def::Local(..) | Def::Upvar(..) => {}
5179-
5180-
_ => bug!("unexpected definition: {:?}", def),
5181-
}
5182-
5183-
debug!("path_segs = {:?}", path_segs);
5184-
5185-
path_segs
5186-
}
5187-
51885060
// Instantiates the given path, which must refer to an item with the given
51895061
// number of type parameters and type.
51905062
pub fn instantiate_value_path(&self,
@@ -5204,7 +5076,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
52045076

52055077
let tcx = self.tcx;
52065078

5207-
let path_segs = self.def_ids_for_path_segments(segments, self_ty, def);
5079+
let path_segs = AstConv::def_ids_for_path_segments(self, segments, self_ty, def);
52085080

52095081
let mut user_self_ty = None;
52105082
match def {

src/test/run-pass/enum-variant-generic-args.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ type Alias<T> = Enum<T>;
88
type AliasFixed = Enum<()>;
99

1010
macro_rules! is_variant {
11-
(TSVariant, $expr:expr) => (is_variant!(@TSVariant, (_), $expr));
12-
(SVariant, $expr:expr) => (is_variant!(@SVariant, { v: _ }, $expr));
13-
(@$variant:ident, $matcher:tt, $expr:expr) => (
11+
(TSVariant, $expr:expr) => (is_variant!(@check TSVariant, (_), $expr));
12+
(SVariant, $expr:expr) => (is_variant!(@check SVariant, { v: _ }, $expr));
13+
(@check $variant:ident, $matcher:tt, $expr:expr) => (
1414
assert!(if let Enum::$variant::<()> $matcher = $expr { true } else { false },
1515
"expr does not have correct type");
1616
);

src/test/ui/enum-variant-generic-args.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@ impl<T> Enum<T> {
1616
}
1717

1818
fn s_variant() {
19-
Self::SVariant::<()>(());
19+
Self::SVariant::<()> { v: () };
2020
//~^ ERROR type arguments are not allowed on this entity [E0109]
21-
Self::<()>::SVariant(());
21+
//~^^ ERROR mismatched types [E0308]
22+
Self::<()>::SVariant { v: () };
2223
//~^ ERROR type arguments are not allowed on this entity [E0109]
23-
Self::<()>::SVariant::<()>(());
24+
//~^^ ERROR mismatched types [E0308]
25+
Self::<()>::SVariant::<()> { v: () };
2426
//~^ ERROR type arguments are not allowed on this entity [E0109]
2527
//~^^ ERROR type arguments are not allowed on this entity [E0109]
28+
//~^^^ ERROR mismatched types [E0308]
2629
}
2730
}
2831

@@ -47,19 +50,19 @@ fn main() {
4750

4851
// Struct variant
4952

50-
Enum::<()>::SVariant::<()>(());
53+
Enum::<()>::SVariant::<()> { v: () };
5154
//~^ ERROR type arguments are not allowed on this entity [E0109]
5255

53-
Alias::SVariant::<()>(());
56+
Alias::SVariant::<()> { v: () };
5457
//~^ ERROR type arguments are not allowed on this entity [E0109]
55-
Alias::<()>::SVariant::<()>(());
58+
Alias::<()>::SVariant::<()> { v: () };
5659
//~^ ERROR type arguments are not allowed on this entity [E0109]
5760

58-
AliasFixed::SVariant::<()>(());
61+
AliasFixed::SVariant::<()> { v: () };
5962
//~^ ERROR type arguments are not allowed on this entity [E0109]
60-
AliasFixed::<()>::SVariant(());
63+
AliasFixed::<()>::SVariant { v: () };
6164
//~^ ERROR wrong number of type arguments: expected 0, found 1 [E0107]
62-
AliasFixed::<()>::SVariant::<()>(());
65+
AliasFixed::<()>::SVariant::<()> { v: () };
6366
//~^ ERROR type arguments are not allowed on this entity [E0109]
6467
//~^^ ERROR wrong number of type arguments: expected 0, found 1 [E0107]
6568
}

0 commit comments

Comments
 (0)