about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorAlexander Regueiro <alexreg@me.com>2018-12-03 21:50:49 +0000
committerAlexander Regueiro <alexreg@me.com>2018-12-26 21:27:08 +0000
commitc77fdbf2ebfa6a2f17017c38eebf18b4a802336a (patch)
tree38ec1d72f31fa698cd4f2e383425d1797d204ec0 /src
parent1b150c4043654f93fcf7250b560581d44fc2b43d (diff)
downloadrust-c77fdbf2ebfa6a2f17017c38eebf18b4a802336a.tar.gz
rust-c77fdbf2ebfa6a2f17017c38eebf18b4a802336a.zip
Implemented variants on type aliases in both ctor and pattern position.
Diffstat (limited to 'src')
-rw-r--r--src/librustc_mir/hair/cx/expr.rs5
-rw-r--r--src/librustc_typeck/astconv.rs24
-rw-r--r--src/librustc_typeck/check/method/mod.rs35
-rw-r--r--src/librustc_typeck/check/mod.rs106
-rw-r--r--src/librustc_typeck/lib.rs26
-rw-r--r--src/libsyntax/feature_gate.rs5
6 files changed, 166 insertions, 35 deletions
diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs
index 46d666cd81d..2b790e3e1d3 100644
--- a/src/librustc_mir/hair/cx/expr.rs
+++ b/src/librustc_mir/hair/cx/expr.rs
@@ -481,10 +481,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
                             }
                         }
                         AdtKind::Enum => {
-                            let def = match *qpath {
-                                hir::QPath::Resolved(_, ref path) => path.def,
-                                hir::QPath::TypeRelative(..) => Def::Err,
-                            };
+                            let def = cx.tables().qpath_def(qpath, expr.hir_id);
                             match def {
                                 Def::Variant(variant_id) => {
                                     assert!(base.is_none());
diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs
index f33060dde4f..18c19352a60 100644
--- a/src/librustc_typeck/astconv.rs
+++ b/src/librustc_typeck/astconv.rs
@@ -31,6 +31,8 @@ use std::collections::BTreeSet;
 use std::iter;
 use std::slice;
 
+use super::{allow_type_alias_enum_variants};
+
 pub trait AstConv<'gcx, 'tcx> {
     fn tcx<'a>(&'a self) -> TyCtxt<'a, 'gcx, 'tcx>;
 
@@ -1275,6 +1277,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
                                      ref_id: ast::NodeId,
                                      span: Span,
                                      ty: Ty<'tcx>,
+                                     ty_hir: &hir::Ty,
                                      ty_path_def: Def,
                                      item_segment: &hir::PathSegment)
                                      -> (Ty<'tcx>, Def)
@@ -1286,6 +1289,21 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
 
         self.prohibit_generics(slice::from_ref(item_segment));
 
+        // Check if we have an enum variant here.
+        if let ty::Adt(adt_def, _) = ty.sty {
+            if adt_def.is_enum() {
+                if allow_type_alias_enum_variants(tcx, ty_hir, span) {
+                    let variant_def = adt_def.variants.iter().find(|vd| {
+                        tcx.hygienic_eq(assoc_name, vd.ident, adt_def.did)
+                    });
+                    if let Some(variant_def) = variant_def {
+                        let def = Def::Variant(variant_def.did);
+                        return (ty, def);
+                    }
+                }
+            }
+        }
+
         // Find the type of the associated item, and the trait where the associated
         // item is declared.
         let bound = match (&ty.sty, ty_path_def) {
@@ -1342,7 +1360,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
                 return (tcx.types.err, Def::Err);
             }
             _ => {
-                // Don't print TyErr to the user.
+                // Don't print `TyErr` to the user.
                 if !ty.references_error() {
                     self.report_ambiguous_associated_type(span,
                                                           &ty.to_string(),
@@ -1505,10 +1523,8 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
             }
             Def::SelfTy(_, Some(def_id)) => {
                 // `Self` in impl (we know the concrete type)
-
                 assert_eq!(opt_self_ty, None);
                 self.prohibit_generics(&path.segments);
-
                 tcx.at(span).type_of(def_id)
             }
             Def::SelfTy(Some(_), None) => {
@@ -1602,7 +1618,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
                 } else {
                     Def::Err
                 };
-                self.associated_path_def_to_ty(ast_ty.id, ast_ty.span, ty, def, segment).0
+                self.associated_path_def_to_ty(ast_ty.id, ast_ty.span, ty, qself, def, segment).0
             }
             hir::TyKind::Array(ref ty, ref length) => {
                 let length_def_id = tcx.hir().local_def_id(length.id);
diff --git a/src/librustc_typeck/check/method/mod.rs b/src/librustc_typeck/check/method/mod.rs
index 2bc1dfaf04e..f594690f77f 100644
--- a/src/librustc_typeck/check/method/mod.rs
+++ b/src/librustc_typeck/check/method/mod.rs
@@ -1,4 +1,4 @@
-//! Method lookup: the secret sauce of Rust. See the [rustc guide] chapter.
+//! Method lookup: the secret sauce of Rust. See the [rustc guide] for more information.
 //!
 //! [rustc guide]: https://rust-lang.github.io/rustc-guide/method-lookup.html
 
@@ -25,6 +25,7 @@ use rustc::infer::{self, InferOk};
 use syntax::ast;
 use syntax_pos::Span;
 
+use crate::{allow_type_alias_enum_variants};
 use self::probe::{IsSuggestion, ProbeScope};
 
 pub fn provide(providers: &mut ty::query::Providers) {
@@ -360,21 +361,45 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                         span: Span,
                         method_name: ast::Ident,
                         self_ty: Ty<'tcx>,
+                        self_ty_hir: &hir::Ty,
                         expr_id: ast::NodeId)
                         -> Result<Def, MethodError<'tcx>> {
+        debug!("resolve_ufcs: method_name={:?} self_ty={:?} expr_id={:?}",
+            method_name,
+            self_ty,
+            expr_id
+        );
+
+        let tcx = self.tcx;
+
+        // Check if we have an enum variant here.
+        if let ty::Adt(adt_def, _) = self_ty.sty {
+            if adt_def.is_enum() {
+                if allow_type_alias_enum_variants(tcx, self_ty_hir, span) {
+                    let variant_def = adt_def.variants.iter().find(|vd| {
+                        tcx.hygienic_eq(method_name, vd.ident, adt_def.did)
+                    });
+                    if let Some(variant_def) = variant_def {
+                        let def = Def::VariantCtor(variant_def.did, variant_def.ctor_kind);
+                        return Ok(def);
+                    }
+                }
+            }
+        }
+
         let mode = probe::Mode::Path;
         let pick = self.probe_for_name(span, mode, method_name, IsSuggestion(false),
                                        self_ty, expr_id, ProbeScope::TraitsInScope)?;
 
         if let Some(import_id) = pick.import_id {
-            let import_def_id = self.tcx.hir().local_def_id(import_id);
-            debug!("used_trait_import: {:?}", import_def_id);
+            let import_def_id = tcx.hir().local_def_id(import_id);
+            debug!("resolve_ufcs: used_trait_import: {:?}", import_def_id);
             Lrc::get_mut(&mut self.tables.borrow_mut().used_trait_imports)
-                                        .unwrap().insert(import_def_id);
+                                         .unwrap().insert(import_def_id);
         }
 
         let def = pick.item.def();
-        self.tcx.check_stability(def.def_id(), Some(expr_id), span);
+        tcx.check_stability(def.def_id(), Some(expr_id), span);
 
         Ok(def)
     }
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index 414cc363f97..9914ae7ed89 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -101,14 +101,14 @@ use rustc::infer::opaque_types::OpaqueTypeDecl;
 use rustc::infer::type_variable::{TypeVariableOrigin};
 use rustc::middle::region;
 use rustc::mir::interpret::{ConstValue, GlobalId};
-use rustc::ty::subst::{CanonicalUserSubsts, UnpackedKind, Subst, Substs,
-                       UserSelfTy, UserSubsts};
 use rustc::traits::{self, ObligationCause, ObligationCauseCode, TraitEngine};
 use rustc::ty::{self, AdtKind, Ty, TyCtxt, GenericParamDefKind, RegionKind, Visibility,
                 ToPolyTraitRef, ToPredicate};
 use rustc::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
 use rustc::ty::fold::TypeFoldable;
 use rustc::ty::query::Providers;
+use rustc::ty::subst::{CanonicalUserSubsts, UnpackedKind, Subst, Substs,
+                       UserSelfTy, UserSubsts};
 use rustc::ty::util::{Representability, IntTypeExt, Discr};
 use rustc::ty::layout::VariantIdx;
 use syntax_pos::{self, BytePos, Span, MultiSpan};
@@ -4539,7 +4539,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     Def::Err
                 };
                 let (ty, def) = AstConv::associated_path_def_to_ty(self, node_id, path_span,
-                                                                   ty, def, segment);
+                                                                   ty, qself, def, segment);
 
                 // Write back the new resolution.
                 let hir_id = self.tcx.hir().node_to_hir_id(node_id);
@@ -4558,14 +4558,14 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                                        span: Span)
                                        -> (Def, Option<Ty<'tcx>>, &'b [hir::PathSegment])
     {
-        let (ty, item_segment) = match *qpath {
+        let (ty, ty_hir, item_segment) = match *qpath {
             hir::QPath::Resolved(ref opt_qself, ref path) => {
                 return (path.def,
                         opt_qself.as_ref().map(|qself| self.to_ty(qself)),
                         &path.segments[..]);
             }
             hir::QPath::TypeRelative(ref qself, ref segment) => {
-                (self.to_ty(qself), segment)
+                (self.to_ty(qself), qself, segment)
             }
         };
         let hir_id = self.tcx.hir().node_to_hir_id(node_id);
@@ -4575,7 +4575,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             return (*cached_def, Some(ty), slice::from_ref(&**item_segment))
         }
         let item_name = item_segment.ident;
-        let def = match self.resolve_ufcs(span, item_name, ty, node_id) {
+        let def = match self.resolve_ufcs(span, item_name, ty, ty_hir, node_id) {
             Ok(def) => def,
             Err(error) => {
                 let def = match error {
@@ -5042,6 +5042,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
 
     fn def_ids_for_path_segments(&self,
                                  segments: &[hir::PathSegment],
+                                 self_ty: Option<Ty<'tcx>>,
                                  def: Def)
                                  -> Vec<PathSeg> {
         // We need to extract the type parameters supplied by the user in
@@ -5053,15 +5054,20 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         //
         // There are basically four cases to consider:
         //
-        // 1. Reference to a constructor of enum variant or struct:
+        // 1. Reference to a constructor of a struct:
         //
         //        struct Foo<T>(...)
+        //
+        //    In this case, the parameters are declared in the type space.
+        //
+        // 2. Reference to a constructor of an enum variant:
+        //
         //        enum E<T> { Foo(...) }
         //
-        //    In these cases, the parameters are declared in the type
-        //    space.
+        //    In this case, the parameters are defined in the type space,
+        //    but may be specified either on the type or the variant.
         //
-        // 2. Reference to a fn item or a free constant:
+        // 3. Reference to a fn item or a free constant:
         //
         //        fn foo<T>() { }
         //
@@ -5070,7 +5076,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         //    type parameters. However, in this case, those parameters are
         //    declared on a value, and hence are in the `FnSpace`.
         //
-        // 3. Reference to a method or an associated constant:
+        // 4. Reference to a method or an associated constant:
         //
         //        impl<A> SomeStruct<A> {
         //            fn foo<B>(...)
@@ -5082,7 +5088,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         //    `SomeStruct::<A>`, contains parameters in TypeSpace, and the
         //    final segment, `foo::<B>` contains parameters in fn space.
         //
-        // 4. Reference to a local variable
+        // 5. Reference to a local variable
         //
         //    Local variables can't have any type parameters.
         //
@@ -5094,9 +5100,8 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         let mut path_segs = vec![];
 
         match def {
-            // Case 1. Reference to a struct/variant constructor.
+            // Case 1. Reference to a struct constructor.
             Def::StructCtor(def_id, ..) |
-            Def::VariantCtor(def_id, ..) |
             Def::SelfCtor(.., def_id) => {
                 // Everything but the final segment should have no
                 // parameters at all.
@@ -5107,14 +5112,49 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 path_segs.push(PathSeg(generics_def_id, last));
             }
 
-            // Case 2. Reference to a top-level value.
+            // Case 2. Reference to a variant constructor.
+            Def::VariantCtor(def_id, ..) => {
+                if tcx.features().type_alias_enum_variants {
+                    let adt_def = self_ty.and_then(|t| t.ty_adt_def());
+                    let (generics_def_id, index) = if let Some(adt_def) = adt_def {
+                        debug_assert!(adt_def.is_enum());
+                        (adt_def.did, last)
+                    } else if last >= 1 && segments[last - 1].args.is_some() {
+                        // Everything but the penultimate segment should have no
+                        // parameters at all.
+                        let enum_def_id = self.tcx.parent_def_id(def_id).unwrap();
+                        (enum_def_id, last - 1)
+                    } else {
+                        // FIXME: lint here suggesting `Enum::<...>::Variant` form
+                        // instead of `Enum::Variant::<...>` form.
+
+                        // Everything but the final segment should have no
+                        // parameters at all.
+                        let generics = self.tcx.generics_of(def_id);
+                        // Variant and struct constructors use the
+                        // generics of their parent type definition.
+                        (generics.parent.unwrap_or(def_id), last)
+                    };
+                    path_segs.push(PathSeg(generics_def_id, index));
+                } else {
+                    // Everything but the final segment should have no
+                    // parameters at all.
+                    let generics = self.tcx.generics_of(def_id);
+                    // Variant and struct constructors use the
+                    // generics of their parent type definition.
+                    let generics_def_id = generics.parent.unwrap_or(def_id);
+                    path_segs.push(PathSeg(generics_def_id, last));
+                }
+            }
+
+            // Case 3. Reference to a top-level value.
             Def::Fn(def_id) |
             Def::Const(def_id) |
             Def::Static(def_id, _) => {
                 path_segs.push(PathSeg(def_id, last));
             }
 
-            // Case 3. Reference to a method or associated const.
+            // Case 4. Reference to a method or associated const.
             Def::Method(def_id) |
             Def::AssociatedConst(def_id) => {
                 if segments.len() >= 2 {
@@ -5124,7 +5164,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                 path_segs.push(PathSeg(def_id, last));
             }
 
-            // Case 4. Local variable, no generics.
+            // Case 5. Local variable, no generics.
             Def::Local(..) | Def::Upvar(..) => {}
 
             _ => bug!("unexpected definition: {:?}", def),
@@ -5152,7 +5192,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
             node_id,
         );
 
-        let path_segs = self.def_ids_for_path_segments(segments, def);
+        let path_segs = self.def_ids_for_path_segments(segments, self_ty, def);
 
         let mut user_self_ty = None;
         match def {
@@ -5187,10 +5227,18 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         // provided (if any) into their appropriate spaces. We'll also report
         // errors if type parameters are provided in an inappropriate place.
 
-        let generic_segs = path_segs.iter().map(|PathSeg(_, index)| index)
-            .collect::<FxHashSet<_>>();
+        let is_alias_variant_ctor = if tcx.features().type_alias_enum_variants {
+            match def {
+                Def::VariantCtor(_, _) if self_ty.is_some() => true,
+                _ => false,
+            };
+        } else {
+            false
+        };
+
+        let generic_segs: FxHashSet<_> = path_segs.iter().map(|PathSeg(_, index)| index).collect();
         AstConv::prohibit_generics(self, segments.iter().enumerate().filter_map(|(index, seg)| {
-            if !generic_segs.contains(&index) {
+            if !generic_segs.contains(&index) || is_alias_variant_ctor {
                 Some(seg)
             } else {
                 None
@@ -5274,6 +5322,22 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
                     }
                 }
             }
+            Def::VariantCtor(_, _) if self_ty.is_some() => {
+                let def_id = def.def_id();
+
+                let ty = self.tcx.type_of(def_id);
+                if tcx.features().type_alias_enum_variants {
+                    if let Some(self_ty) = self_ty {
+                        match ty.ty_adt_def() {
+                            Some(adt_def) if adt_def.is_enum() => {
+                                return (self_ty, def);
+                            }
+                            _ => {}
+                        }
+                    }
+                }
+                (def_id, ty)
+            }
             _ => {
                 let def_id = def.def_id();
 
diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs
index cba07ab3602..d63e5a3fe93 100644
--- a/src/librustc_typeck/lib.rs
+++ b/src/librustc_typeck/lib.rs
@@ -105,12 +105,14 @@ mod outlives;
 mod variance;
 
 use hir::Node;
+use hir::def::Def;
 use rustc_target::spec::abi::Abi;
 use rustc::hir;
 use rustc::infer::InferOk;
 use rustc::lint;
 use rustc::middle;
 use rustc::session;
+use rustc::session::config::nightly_options;
 use rustc::traits::{ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt};
 use rustc::ty::subst::Substs;
 use rustc::ty::{self, Ty, TyCtxt};
@@ -129,6 +131,30 @@ pub struct TypeAndSubsts<'tcx> {
     ty: Ty<'tcx>,
 }
 
+fn allow_type_alias_enum_variants<'a, 'gcx, 'tcx>(tcx: TyCtxt<'a, 'gcx, 'tcx>,
+                                                  ty_hir: &hir::Ty,
+                                                  span: Span) -> bool {
+    let allow_feature = tcx.features().type_alias_enum_variants;
+    if !allow_feature {
+        // Only print error if we know the type is an alias.
+        if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) = ty_hir.node {
+            if let Def::TyAlias(_) = path.def {
+                let mut err = tcx.sess.struct_span_err(
+                    span,
+                    "type alias enum variants are not yet allowed"
+                );
+                if nightly_options::is_nightly_build() {
+                    help!(&mut err,
+                        "add `#![feature(type_alias_enum_variants)]` to the \
+                        crate attributes to enable");
+                }
+                err.emit();
+            }
+        }
+    }
+    allow_feature
+}
+
 fn require_c_abi_if_variadic(tcx: TyCtxt,
                              decl: &hir::FnDecl,
                              abi: Abi,
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 62269a8f163..6a3b2156f3a 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -471,11 +471,14 @@ declare_features! (
     // Allows `const _: TYPE = VALUE`.
     (active, underscore_const_names, "1.31.0", Some(54912), None),
 
-    // `reason = ` in lint attributes and `expect` lint attribute
+    // Adds `reason` and `expect` lint attributes.
     (active, lint_reasons, "1.31.0", Some(54503), None),
 
     // `extern crate self as foo;` puts local crate root into extern prelude under name `foo`.
     (active, extern_crate_self, "1.31.0", Some(56409), None),
+
+    // Allows paths to enum variants on type aliases.
+    (active, type_alias_enum_variants, "1.31.0", Some(49683), None),
 );
 
 declare_features! (