Implemented variants on type aliases in both ctor and pattern position.

This commit is contained in:
Alexander Regueiro 2018-12-03 21:50:49 +00:00
parent 1b150c4043
commit c77fdbf2eb
6 changed files with 166 additions and 35 deletions

View File

@ -481,10 +481,7 @@ fn make_mirror_unadjusted<'a, 'gcx, 'tcx>(cx: &mut Cx<'a, 'gcx, 'tcx>,
} }
} }
AdtKind::Enum => { AdtKind::Enum => {
let def = match *qpath { let def = cx.tables().qpath_def(qpath, expr.hir_id);
hir::QPath::Resolved(_, ref path) => path.def,
hir::QPath::TypeRelative(..) => Def::Err,
};
match def { match def {
Def::Variant(variant_id) => { Def::Variant(variant_id) => {
assert!(base.is_none()); assert!(base.is_none());

View File

@ -31,6 +31,8 @@ use std::collections::BTreeSet;
use std::iter; use std::iter;
use std::slice; use std::slice;
use super::{allow_type_alias_enum_variants};
pub trait AstConv<'gcx, 'tcx> { pub trait AstConv<'gcx, 'tcx> {
fn tcx<'a>(&'a self) -> TyCtxt<'a, '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, ref_id: ast::NodeId,
span: Span, span: Span,
ty: Ty<'tcx>, ty: Ty<'tcx>,
ty_hir: &hir::Ty,
ty_path_def: Def, ty_path_def: Def,
item_segment: &hir::PathSegment) item_segment: &hir::PathSegment)
-> (Ty<'tcx>, Def) -> (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)); 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 // Find the type of the associated item, and the trait where the associated
// item is declared. // item is declared.
let bound = match (&ty.sty, ty_path_def) { 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); return (tcx.types.err, Def::Err);
} }
_ => { _ => {
// Don't print TyErr to the user. // Don't print `TyErr` to the user.
if !ty.references_error() { if !ty.references_error() {
self.report_ambiguous_associated_type(span, self.report_ambiguous_associated_type(span,
&ty.to_string(), &ty.to_string(),
@ -1505,10 +1523,8 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
} }
Def::SelfTy(_, Some(def_id)) => { Def::SelfTy(_, Some(def_id)) => {
// `Self` in impl (we know the concrete type) // `Self` in impl (we know the concrete type)
assert_eq!(opt_self_ty, None); assert_eq!(opt_self_ty, None);
self.prohibit_generics(&path.segments); self.prohibit_generics(&path.segments);
tcx.at(span).type_of(def_id) tcx.at(span).type_of(def_id)
} }
Def::SelfTy(Some(_), None) => { Def::SelfTy(Some(_), None) => {
@ -1602,7 +1618,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> dyn AstConv<'gcx, 'tcx> + 'o {
} else { } else {
Def::Err 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) => { hir::TyKind::Array(ref ty, ref length) => {
let length_def_id = tcx.hir().local_def_id(length.id); let length_def_id = tcx.hir().local_def_id(length.id);

View File

@ -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 //! [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::ast;
use syntax_pos::Span; use syntax_pos::Span;
use crate::{allow_type_alias_enum_variants};
use self::probe::{IsSuggestion, ProbeScope}; use self::probe::{IsSuggestion, ProbeScope};
pub fn provide(providers: &mut ty::query::Providers) { pub fn provide(providers: &mut ty::query::Providers) {
@ -360,21 +361,45 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
span: Span, span: Span,
method_name: ast::Ident, method_name: ast::Ident,
self_ty: Ty<'tcx>, self_ty: Ty<'tcx>,
self_ty_hir: &hir::Ty,
expr_id: ast::NodeId) expr_id: ast::NodeId)
-> Result<Def, MethodError<'tcx>> { -> 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 mode = probe::Mode::Path;
let pick = self.probe_for_name(span, mode, method_name, IsSuggestion(false), let pick = self.probe_for_name(span, mode, method_name, IsSuggestion(false),
self_ty, expr_id, ProbeScope::TraitsInScope)?; self_ty, expr_id, ProbeScope::TraitsInScope)?;
if let Some(import_id) = pick.import_id { if let Some(import_id) = pick.import_id {
let import_def_id = self.tcx.hir().local_def_id(import_id); let import_def_id = tcx.hir().local_def_id(import_id);
debug!("used_trait_import: {:?}", import_def_id); debug!("resolve_ufcs: used_trait_import: {:?}", import_def_id);
Lrc::get_mut(&mut self.tables.borrow_mut().used_trait_imports) 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(); 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) Ok(def)
} }

View File

@ -101,14 +101,14 @@ use rustc::infer::opaque_types::OpaqueTypeDecl;
use rustc::infer::type_variable::{TypeVariableOrigin}; use rustc::infer::type_variable::{TypeVariableOrigin};
use rustc::middle::region; use rustc::middle::region;
use rustc::mir::interpret::{ConstValue, GlobalId}; 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::traits::{self, ObligationCause, ObligationCauseCode, TraitEngine};
use rustc::ty::{self, AdtKind, Ty, TyCtxt, GenericParamDefKind, RegionKind, Visibility, use rustc::ty::{self, AdtKind, Ty, TyCtxt, GenericParamDefKind, RegionKind, Visibility,
ToPolyTraitRef, ToPredicate}; ToPolyTraitRef, ToPredicate};
use rustc::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; use rustc::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability};
use rustc::ty::fold::TypeFoldable; use rustc::ty::fold::TypeFoldable;
use rustc::ty::query::Providers; 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::util::{Representability, IntTypeExt, Discr};
use rustc::ty::layout::VariantIdx; use rustc::ty::layout::VariantIdx;
use syntax_pos::{self, BytePos, Span, MultiSpan}; use syntax_pos::{self, BytePos, Span, MultiSpan};
@ -4539,7 +4539,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
Def::Err Def::Err
}; };
let (ty, def) = AstConv::associated_path_def_to_ty(self, node_id, path_span, 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. // Write back the new resolution.
let hir_id = self.tcx.hir().node_to_hir_id(node_id); 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) span: Span)
-> (Def, Option<Ty<'tcx>>, &'b [hir::PathSegment]) -> (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) => { hir::QPath::Resolved(ref opt_qself, ref path) => {
return (path.def, return (path.def,
opt_qself.as_ref().map(|qself| self.to_ty(qself)), opt_qself.as_ref().map(|qself| self.to_ty(qself)),
&path.segments[..]); &path.segments[..]);
} }
hir::QPath::TypeRelative(ref qself, ref segment) => { 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); 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)) return (*cached_def, Some(ty), slice::from_ref(&**item_segment))
} }
let item_name = item_segment.ident; 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, Ok(def) => def,
Err(error) => { Err(error) => {
let def = match error { let def = match error {
@ -5042,6 +5042,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
fn def_ids_for_path_segments(&self, fn def_ids_for_path_segments(&self,
segments: &[hir::PathSegment], segments: &[hir::PathSegment],
self_ty: Option<Ty<'tcx>>,
def: Def) def: Def)
-> Vec<PathSeg> { -> Vec<PathSeg> {
// We need to extract the type parameters supplied by the user in // 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: // 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>(...) // 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(...) } // enum E<T> { Foo(...) }
// //
// In these cases, the parameters are declared in the type // In this case, the parameters are defined in the type space,
// 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>() { } // fn foo<T>() { }
// //
@ -5070,7 +5076,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// type parameters. However, in this case, those parameters are // type parameters. However, in this case, those parameters are
// declared on a value, and hence are in the `FnSpace`. // 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> { // impl<A> SomeStruct<A> {
// fn foo<B>(...) // fn foo<B>(...)
@ -5082,7 +5088,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
// `SomeStruct::<A>`, contains parameters in TypeSpace, and the // `SomeStruct::<A>`, contains parameters in TypeSpace, and the
// final segment, `foo::<B>` contains parameters in fn space. // 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. // 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![]; let mut path_segs = vec![];
match def { match def {
// Case 1. Reference to a struct/variant constructor. // Case 1. Reference to a struct constructor.
Def::StructCtor(def_id, ..) | Def::StructCtor(def_id, ..) |
Def::VariantCtor(def_id, ..) |
Def::SelfCtor(.., def_id) => { Def::SelfCtor(.., def_id) => {
// Everything but the final segment should have no // Everything but the final segment should have no
// parameters at all. // parameters at all.
@ -5107,14 +5112,49 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
path_segs.push(PathSeg(generics_def_id, last)); 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::Fn(def_id) |
Def::Const(def_id) | Def::Const(def_id) |
Def::Static(def_id, _) => { Def::Static(def_id, _) => {
path_segs.push(PathSeg(def_id, last)); 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::Method(def_id) |
Def::AssociatedConst(def_id) => { Def::AssociatedConst(def_id) => {
if segments.len() >= 2 { if segments.len() >= 2 {
@ -5124,7 +5164,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
path_segs.push(PathSeg(def_id, last)); path_segs.push(PathSeg(def_id, last));
} }
// Case 4. Local variable, no generics. // Case 5. Local variable, no generics.
Def::Local(..) | Def::Upvar(..) => {} Def::Local(..) | Def::Upvar(..) => {}
_ => bug!("unexpected definition: {:?}", def), _ => bug!("unexpected definition: {:?}", def),
@ -5152,7 +5192,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
node_id, 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; let mut user_self_ty = None;
match def { 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 // provided (if any) into their appropriate spaces. We'll also report
// errors if type parameters are provided in an inappropriate place. // errors if type parameters are provided in an inappropriate place.
let generic_segs = path_segs.iter().map(|PathSeg(_, index)| index) let is_alias_variant_ctor = if tcx.features().type_alias_enum_variants {
.collect::<FxHashSet<_>>(); 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)| { 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) Some(seg)
} else { } else {
None 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(); let def_id = def.def_id();

View File

@ -105,12 +105,14 @@ mod outlives;
mod variance; mod variance;
use hir::Node; use hir::Node;
use hir::def::Def;
use rustc_target::spec::abi::Abi; use rustc_target::spec::abi::Abi;
use rustc::hir; use rustc::hir;
use rustc::infer::InferOk; use rustc::infer::InferOk;
use rustc::lint; use rustc::lint;
use rustc::middle; use rustc::middle;
use rustc::session; use rustc::session;
use rustc::session::config::nightly_options;
use rustc::traits::{ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt}; use rustc::traits::{ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt};
use rustc::ty::subst::Substs; use rustc::ty::subst::Substs;
use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::{self, Ty, TyCtxt};
@ -129,6 +131,30 @@ pub struct TypeAndSubsts<'tcx> {
ty: Ty<'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, fn require_c_abi_if_variadic(tcx: TyCtxt,
decl: &hir::FnDecl, decl: &hir::FnDecl,
abi: Abi, abi: Abi,

View File

@ -471,11 +471,14 @@ declare_features! (
// Allows `const _: TYPE = VALUE`. // Allows `const _: TYPE = VALUE`.
(active, underscore_const_names, "1.31.0", Some(54912), None), (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), (active, lint_reasons, "1.31.0", Some(54503), None),
// `extern crate self as foo;` puts local crate root into extern prelude under name `foo`. // `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), (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! ( declare_features! (