diff options
| author | Alexander Regueiro <alexreg@me.com> | 2018-12-03 21:50:49 +0000 |
|---|---|---|
| committer | Alexander Regueiro <alexreg@me.com> | 2018-12-26 21:27:08 +0000 |
| commit | c77fdbf2ebfa6a2f17017c38eebf18b4a802336a (patch) | |
| tree | 38ec1d72f31fa698cd4f2e383425d1797d204ec0 /src | |
| parent | 1b150c4043654f93fcf7250b560581d44fc2b43d (diff) | |
| download | rust-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.rs | 5 | ||||
| -rw-r--r-- | src/librustc_typeck/astconv.rs | 24 | ||||
| -rw-r--r-- | src/librustc_typeck/check/method/mod.rs | 35 | ||||
| -rw-r--r-- | src/librustc_typeck/check/mod.rs | 106 | ||||
| -rw-r--r-- | src/librustc_typeck/lib.rs | 26 | ||||
| -rw-r--r-- | src/libsyntax/feature_gate.rs | 5 |
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! ( |
