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 => {
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());

View File

@ -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);

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
@ -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)
}

View File

@ -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();

View File

@ -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,

View File

@ -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! (