mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-18 01:44:04 +00:00
Implemented variants on type aliases in both ctor and pattern position.
This commit is contained in:
parent
1b150c4043
commit
c77fdbf2eb
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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! (
|
||||
|
Loading…
Reference in New Issue
Block a user