From 2cdd9f1c97652ee799f6bb0af7c063115ff368ea Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 11 Jun 2016 18:47:47 +0300 Subject: [PATCH] Rewrite check_pat_enum, split it into check_pat_tuple_struct and check_pat_path Update definitions in def_map for associated types written in unqualified form (like `Self::Output`) Cleanup finish_resolving_def_to_ty/resolve_ty_and_def_ufcs Make VariantDef's available through constructor IDs --- src/librustc/hir/def.rs | 9 - src/librustc/hir/pat_util.rs | 19 -- src/librustc/ty/context.rs | 12 +- src/librustc/ty/mod.rs | 14 + src/librustc_metadata/decoder.rs | 14 +- src/librustc_typeck/astconv.rs | 78 ++--- src/librustc_typeck/check/_match.rs | 270 +++++++----------- src/librustc_typeck/check/mod.rs | 63 ++-- src/librustc_typeck/collect.rs | 18 +- src/librustc_typeck/diagnostics.rs | 37 +-- .../cache/project-fn-ret-contravariant.rs | 1 + .../cache/project-fn-ret-invariant.rs | 1 + .../compile-fail/empty-struct-braces-pat-1.rs | 6 +- src/test/compile-fail/issue-32004.rs | 2 +- .../compile-fail/method-path-in-pattern.rs | 7 +- .../compile-fail/qualified-path-params.rs | 3 +- src/test/run-pass/issue-28550.rs | 2 + 17 files changed, 229 insertions(+), 327 deletions(-) diff --git a/src/librustc/hir/def.rs b/src/librustc/hir/def.rs index 72261c473e5..218681efb7d 100644 --- a/src/librustc/hir/def.rs +++ b/src/librustc/hir/def.rs @@ -137,15 +137,6 @@ impl Def { } } - pub fn variant_def_ids(&self) -> Option<(DefId, DefId)> { - match *self { - Def::Variant(enum_id, var_id) => { - Some((enum_id, var_id)) - } - _ => None - } - } - pub fn kind_name(&self) -> &'static str { match *self { Def::Fn(..) => "function", diff --git a/src/librustc/hir/pat_util.rs b/src/librustc/hir/pat_util.rs index 8282cd6bfcb..234edc647f0 100644 --- a/src/librustc/hir/pat_util.rs +++ b/src/librustc/hir/pat_util.rs @@ -12,15 +12,12 @@ use hir::def::*; use hir::def_id::DefId; use hir::{self, PatKind}; use ty::TyCtxt; -use util::nodemap::FnvHashMap; use syntax::ast; use syntax::codemap::Spanned; use syntax_pos::{Span, DUMMY_SP}; use std::iter::{Enumerate, ExactSizeIterator}; -pub type PatIdMap = FnvHashMap; - pub struct EnumerateAndAdjust { enumerate: Enumerate, gap_pos: usize, @@ -97,22 +94,6 @@ pub fn pat_is_const(dm: &DefMap, pat: &hir::Pat) -> bool { } } -// Same as above, except that partially-resolved defs cause `false` to be -// returned instead of a panic. -pub fn pat_is_resolved_const(dm: &DefMap, pat: &hir::Pat) -> bool { - match pat.node { - PatKind::Path(..) | PatKind::QPath(..) => { - match dm.get(&pat.id) - .and_then(|d| if d.depth == 0 { Some(d.base_def) } - else { None } ) { - Some(Def::Const(..)) | Some(Def::AssociatedConst(..)) => true, - _ => false - } - } - _ => false - } -} - /// Call `f` on every "binding" in a pattern, e.g., on `a` in /// `match foo() { Some(a) => (), None => () }` pub fn pat_bindings(pat: &hir::Pat, mut f: F) diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 219cb5e383a..4c8fa80dd0b 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -591,6 +591,13 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.global_interners.arenas.trait_defs.alloc(def) } + pub fn insert_adt_def(self, did: DefId, adt_def: ty::AdtDefMaster<'gcx>) { + // this will need a transmute when reverse-variance is removed + if let Some(prev) = self.adt_defs.borrow_mut().insert(did, adt_def) { + bug!("Tried to overwrite interned AdtDef: {:?}", prev) + } + } + pub fn intern_adt_def(self, did: DefId, kind: ty::AdtKind, @@ -598,10 +605,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { -> ty::AdtDefMaster<'gcx> { let def = ty::AdtDefData::new(self, did, kind, variants); let interned = self.global_interners.arenas.adt_defs.alloc(def); - // this will need a transmute when reverse-variance is removed - if let Some(prev) = self.adt_defs.borrow_mut().insert(did, interned) { - bug!("Tried to overwrite interned AdtDef: {:?}", prev) - } + self.insert_adt_def(did, interned); interned } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 4dc7ac4138b..93a4b1ac0f4 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -2454,6 +2454,20 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { self.def_map.borrow().get(&id).map(|resolution| resolution.full_def()) } + // Returns `ty::VariantDef` if `def` refers to a struct, + // or variant or their constructors, panics otherwise. + pub fn expect_variant_def(self, def: Def) -> VariantDef<'tcx> { + match def { + Def::Variant(enum_did, did) => { + self.lookup_adt_def(enum_did).variant_with_id(did) + } + Def::Struct(did) => { + self.lookup_adt_def(did).struct_variant() + } + _ => bug!("expect_variant_def used with unexpected def {:?}", def) + } + } + pub fn def_key(self, id: DefId) -> ast_map::DefKey { if id.is_local() { self.map.def_key(id) diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index eada2a9cd7a..4ccfcd9e903 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -471,23 +471,29 @@ pub fn get_adt_def<'a, 'tcx>(intr: &IdentInterner, let doc = cdata.lookup_item(item_id); let did = DefId { krate: cdata.cnum, index: item_id }; + let mut ctor_did = None; let (kind, variants) = match item_family(doc) { Enum => { (ty::AdtKind::Enum, get_enum_variants(intr, cdata, doc)) } Struct(..) => { - let ctor_did = - reader::maybe_get_doc(doc, tag_items_data_item_struct_ctor). - map_or(did, |ctor_doc| translated_def_id(cdata, ctor_doc)); + // Use separate constructor id for unit/tuple structs and reuse did for braced structs. + ctor_did = reader::maybe_get_doc(doc, tag_items_data_item_struct_ctor).map(|ctor_doc| { + translated_def_id(cdata, ctor_doc) + }); (ty::AdtKind::Struct, - vec![get_struct_variant(intr, cdata, doc, ctor_did)]) + vec![get_struct_variant(intr, cdata, doc, ctor_did.unwrap_or(did))]) } _ => bug!("get_adt_def called on a non-ADT {:?} - {:?}", item_family(doc), did) }; let adt = tcx.intern_adt_def(did, kind, variants); + if let Some(ctor_did) = ctor_did { + // Make adt definition available through constructor id as well. + tcx.insert_adt_def(ctor_did, adt); + } // this needs to be done *after* the variant is interned, // to support recursive structures diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 088ac1aac1a..9ff30f9ede2 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -53,7 +53,7 @@ use rustc_const_eval::{eval_const_expr_partial, ConstEvalErr}; use rustc_const_eval::EvalHint::UncheckedExprHint; use rustc_const_eval::ErrKind::ErroneousReferencedConstant; use hir::{self, SelfKind}; -use hir::def::{self, Def}; +use hir::def::{Def, PathResolution}; use hir::def_id::DefId; use hir::print as pprust; use middle::resolve_lifetime as rl; @@ -1327,7 +1327,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { }; if self.ensure_super_predicates(span, trait_did).is_err() { - return (tcx.types.err, ty_path_def); + return (tcx.types.err, Def::Err); } let candidates: Vec = @@ -1341,7 +1341,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { &assoc_name.as_str(), span) { Ok(bound) => bound, - Err(ErrorReported) => return (tcx.types.err, ty_path_def), + Err(ErrorReported) => return (tcx.types.err, Def::Err), } } (&ty::TyParam(_), Def::SelfTy(Some(trait_did), None)) => { @@ -1351,7 +1351,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { assoc_name, span) { Ok(bound) => bound, - Err(ErrorReported) => return (tcx.types.err, ty_path_def), + Err(ErrorReported) => return (tcx.types.err, Def::Err), } } (&ty::TyParam(_), Def::TyParam(_, _, param_did, param_name)) => { @@ -1361,7 +1361,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { assoc_name, span) { Ok(bound) => bound, - Err(ErrorReported) => return (tcx.types.err, ty_path_def), + Err(ErrorReported) => return (tcx.types.err, Def::Err), } } _ => { @@ -1369,7 +1369,7 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { &ty.to_string(), "Trait", &assoc_name.as_str()); - return (tcx.types.err, ty_path_def); + return (tcx.types.err, Def::Err); } }; @@ -1574,45 +1574,46 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { } } - // Note that both base_segments and assoc_segments may be empty, although not at - // the same time. + // Resolve possibly associated type path into a type and final definition. + // Note that both base_segments and assoc_segments may be empty, although not at same time. pub fn finish_resolving_def_to_ty(&self, rscope: &RegionScope, span: Span, param_mode: PathParamMode, - mut def: Def, + base_def: Def, opt_self_ty: Option>, base_path_ref_id: ast::NodeId, base_segments: &[hir::PathSegment], assoc_segments: &[hir::PathSegment]) -> (Ty<'tcx>, Def) { - debug!("finish_resolving_def_to_ty(def={:?}, \ + // Convert the base type. + debug!("finish_resolving_def_to_ty(base_def={:?}, \ base_segments={:?}, \ assoc_segments={:?})", - def, + base_def, base_segments, assoc_segments); - let mut ty = self.base_def_to_ty(rscope, - span, - param_mode, - def, - opt_self_ty, - base_path_ref_id, - base_segments); - debug!("finish_resolving_def_to_ty: base_def_to_ty returned {:?}", ty); + let base_ty = self.base_def_to_ty(rscope, + span, + param_mode, + base_def, + opt_self_ty, + base_path_ref_id, + base_segments); + debug!("finish_resolving_def_to_ty: base_def_to_ty returned {:?}", base_ty); + // If any associated type segments remain, attempt to resolve them. + let (mut ty, mut def) = (base_ty, base_def); for segment in assoc_segments { debug!("finish_resolving_def_to_ty: segment={:?}", segment); - if ty.sty == ty::TyError { + // This is pretty bad (it will fail except for T::A and Self::A). + let (new_ty, new_def) = self.associated_path_def_to_ty(span, ty, def, segment); + ty = new_ty; + def = new_def; + + if def == Def::Err { break; } - // This is pretty bad (it will fail except for T::A and Self::A). - let (a_ty, a_def) = self.associated_path_def_to_ty(span, - ty, - def, - segment); - ty = a_ty; - def = a_def; } (ty, def) } @@ -1719,23 +1720,22 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o { hir::TyPath(ref maybe_qself, ref path) => { debug!("ast_ty_to_ty: maybe_qself={:?} path={:?}", maybe_qself, path); let path_res = tcx.expect_resolution(ast_ty.id); - let def = path_res.base_def; let base_ty_end = path.segments.len() - path_res.depth; let opt_self_ty = maybe_qself.as_ref().map(|qself| { self.ast_ty_to_ty(rscope, &qself.ty) }); - let (ty, _def) = self.finish_resolving_def_to_ty(rscope, - ast_ty.span, - PathParamMode::Explicit, - def, - opt_self_ty, - ast_ty.id, - &path.segments[..base_ty_end], - &path.segments[base_ty_end..]); + let (ty, def) = self.finish_resolving_def_to_ty(rscope, + ast_ty.span, + PathParamMode::Explicit, + path_res.base_def, + opt_self_ty, + ast_ty.id, + &path.segments[..base_ty_end], + &path.segments[base_ty_end..]); - if path_res.depth != 0 && ty.sty != ty::TyError { - // Write back the new resolution. - tcx.def_map.borrow_mut().insert(ast_ty.id, def::PathResolution::new(def)); + // Write back the new resolution. + if path_res.depth != 0 { + tcx.def_map.borrow_mut().insert(ast_ty.id, PathResolution::new(def)); } ty diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 069a09183a7..82c676adc70 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -10,13 +10,12 @@ use hir::def::Def; use rustc::infer::{self, InferOk, TypeOrigin}; -use hir::pat_util::{EnumerateAndAdjustIterator, pat_is_resolved_const}; +use hir::pat_util::EnumerateAndAdjustIterator; use rustc::ty::subst::Substs; -use rustc::ty::{self, Ty, TypeFoldable, LvaluePreference}; +use rustc::ty::{self, Ty, TypeFoldable, LvaluePreference, VariantKind}; use check::{FnCtxt, Expectation}; use lint; use util::nodemap::FnvHashMap; -use session::Session; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::cmp; @@ -28,20 +27,6 @@ use syntax_pos::Span; use rustc::hir::{self, PatKind}; use rustc::hir::print as pprust; -// This function exists due to the warning "diagnostic code E0164 already used" -fn bad_struct_kind_err(sess: &Session, pat: &hir::Pat, path: &hir::Path, lint: bool) { - let name = pprust::path_to_string(path); - let msg = format!("`{}` does not name a tuple variant or a tuple struct", name); - if lint { - sess.add_lint(lint::builtin::MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT, - pat.id, - pat.span, - msg); - } else { - span_err!(sess, pat.span, E0164, "{}", msg); - } -} - impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { pub fn check_pat(&self, pat: &'gcx hir::Pat, expected: Ty<'tcx>) { let tcx = self.tcx; @@ -136,22 +121,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // subtyping doesn't matter here, as the value is some kind of scalar self.demand_eqtype(pat.span, expected, lhs_ty); } - PatKind::Path(..) if pat_is_resolved_const(&tcx.def_map.borrow(), pat) => { - let const_did = tcx.expect_def(pat.id).def_id(); - let const_scheme = tcx.lookup_item_type(const_did); - assert!(const_scheme.generics.is_empty()); - let const_ty = self.instantiate_type_scheme(pat.span, - &Substs::empty(), - &const_scheme.ty); - self.write_ty(pat.id, const_ty); - - // FIXME(#20489) -- we should limit the types here to scalars or something! - - // As with PatKind::Lit, what we really want here is that there - // exist a LUB, but for the cases that can occur, subtype - // is good enough. - self.demand_suptype(pat.span, expected, const_ty); - } PatKind::Binding(bm, _, ref sub) => { let typ = self.local_ty(pat.span, pat.id); match bm { @@ -197,33 +166,13 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } } PatKind::TupleStruct(ref path, ref subpats, ddpos) => { - self.check_pat_enum(pat, path, &subpats, ddpos, expected, true); + self.check_pat_tuple_struct(pat, path, &subpats, ddpos, expected); } PatKind::Path(ref path) => { - self.check_pat_enum(pat, path, &[], None, expected, false); + self.check_pat_path(pat, None, path, expected); } PatKind::QPath(ref qself, ref path) => { - let self_ty = self.to_ty(&qself.ty); - let path_res = tcx.expect_resolution(pat.id); - if path_res.base_def == Def::Err { - self.set_tainted_by_errors(); - self.write_error(pat.id); - return; - } - if let Some((opt_ty, segments, def)) = - self.resolve_ty_and_def_ufcs(path_res, Some(self_ty), - path, pat.span, pat.id) { - if self.check_assoc_item_is_const(def, pat.span) { - let scheme = tcx.lookup_item_type(def.def_id()); - let predicates = tcx.lookup_predicates(def.def_id()); - self.instantiate_path(segments, scheme, &predicates, - opt_ty, def, pat.span, pat.id); - let const_ty = self.node_ty(pat.id); - self.demand_suptype(pat.span, expected, const_ty); - } else { - self.write_error(pat.id) - } - } + self.check_pat_path(pat, Some(self.to_ty(&qself.ty)), path, expected); } PatKind::Struct(ref path, ref fields, etc) => { self.check_pat_struct(pat, path, fields, etc, expected); @@ -403,20 +352,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { // subtyping. } - fn check_assoc_item_is_const(&self, def: Def, span: Span) -> bool { - match def { - Def::AssociatedConst(..) => true, - Def::Method(..) => { - span_err!(self.tcx.sess, span, E0327, - "associated items in match patterns must be constants"); - false - } - _ => { - span_bug!(span, "non-associated item in check_assoc_item_is_const"); - } - } - } - pub fn check_dereferencable(&self, span: Span, expected: Ty<'tcx>, inner: &hir::Pat) -> bool { if let PatKind::Binding(..) = inner.node { if let Some(mt) = self.shallow_resolve(expected).builtin_deref(true, ty::NoPreference) { @@ -589,132 +524,137 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }); } - fn check_pat_enum(&self, + fn check_pat_path(&self, pat: &hir::Pat, + opt_self_ty: Option>, path: &hir::Path, - subpats: &'gcx [P], - ddpos: Option, - expected: Ty<'tcx>, - is_tuple_struct_pat: bool) + expected: Ty<'tcx>) { - // Typecheck the path. let tcx = self.tcx; - - let path_res = tcx.expect_resolution(pat.id); - if path_res.base_def == Def::Err { - self.set_tainted_by_errors(); + let report_unexpected_def = || { + span_err!(tcx.sess, pat.span, E0533, + "`{}` does not name a unit variant, unit struct or a constant", + pprust::path_to_string(path)); self.write_error(pat.id); - - for pat in subpats { - self.check_pat(&pat, tcx.types.err); - } - return; - } - - let (opt_ty, segments, def) = match self.resolve_ty_and_def_ufcs(path_res, - None, path, - pat.span, pat.id) { - Some(resolution) => resolution, - // Error handling done inside resolve_ty_and_def_ufcs, so if - // resolution fails just return. - None => {return;} }; - // Items that were partially resolved before should have been resolved to - // associated constants (i.e. not methods). - if path_res.depth != 0 && !self.check_assoc_item_is_const(def, pat.span) { - self.write_error(pat.id); - return; + // Resolve the path and check the definition for errors. + let (def, opt_ty, segments) = self.resolve_ty_and_def_ufcs(tcx.expect_resolution(pat.id), + opt_self_ty, path, pat.span, pat.id); + match def { + Def::Err => { + self.set_tainted_by_errors(); + self.write_error(pat.id); + return; + } + Def::Method(..) => { + report_unexpected_def(); + return; + } + Def::Variant(..) | Def::Struct(..) => { + let variant = tcx.expect_variant_def(def); + if variant.kind != VariantKind::Unit { + report_unexpected_def(); + return; + } + } + Def::Const(..) | Def::AssociatedConst(..) => {} // OK + _ => bug!("unexpected pattern definition {:?}", def) } - let enum_def = def.variant_def_ids() - .map_or_else(|| def.def_id(), |(enum_def, _)| enum_def); + // Type check the path. + let scheme = tcx.lookup_item_type(def.def_id()); + let predicates = tcx.lookup_predicates(def.def_id()); + self.instantiate_path(segments, scheme, &predicates, opt_ty, def, pat.span, pat.id); + let pat_ty = self.node_ty(pat.id); + self.demand_suptype(pat.span, expected, pat_ty); + } - let ctor_scheme = tcx.lookup_item_type(enum_def); - let ctor_predicates = tcx.lookup_predicates(enum_def); - let path_scheme = if ctor_scheme.ty.is_fn() { - let fn_ret = tcx.no_late_bound_regions(&ctor_scheme.ty.fn_ret()).unwrap(); - ty::TypeScheme { - ty: fn_ret.unwrap(), - generics: ctor_scheme.generics, - } - } else { - ctor_scheme - }; - self.instantiate_path(segments, path_scheme, &ctor_predicates, - opt_ty, def, pat.span, pat.id); - let report_bad_struct_kind = |is_warning| { - bad_struct_kind_err(tcx.sess, pat, path, is_warning); - if is_warning { return; } + fn check_pat_tuple_struct(&self, + pat: &hir::Pat, + path: &hir::Path, + subpats: &'gcx [P], + ddpos: Option, + expected: Ty<'tcx>) + { + let tcx = self.tcx; + let on_error = || { self.write_error(pat.id); for pat in subpats { self.check_pat(&pat, tcx.types.err); } }; - - // If we didn't have a fully resolved path to start with, we had an - // associated const, and we should quit now, since the rest of this - // function uses checks specific to structs and enums. - if path_res.depth != 0 { - if is_tuple_struct_pat { - report_bad_struct_kind(false); + let report_unexpected_def = |is_lint| { + let msg = format!("`{}` does not name a tuple variant or a tuple struct", + pprust::path_to_string(path)); + if is_lint { + tcx.sess.add_lint(lint::builtin::MATCH_OF_UNIT_VARIANT_VIA_PAREN_DOTDOT, + pat.id, pat.span, msg); } else { - let pat_ty = self.node_ty(pat.id); - self.demand_suptype(pat.span, expected, pat_ty); + span_err!(tcx.sess, pat.span, E0164, "{}", msg); + on_error(); } + }; + + // Resolve the path and check the definition for errors. + let (def, opt_ty, segments) = self.resolve_ty_and_def_ufcs(tcx.expect_resolution(pat.id), + None, path, pat.span, pat.id); + match def { + Def::Err => { + self.set_tainted_by_errors(); + on_error(); + return; + } + Def::Const(..) | Def::AssociatedConst(..) | Def::Method(..) => { + report_unexpected_def(false); + return; + } + Def::Variant(..) | Def::Struct(..) => {} // OK + _ => bug!("unexpected pattern definition {:?}", def) + } + let variant = tcx.expect_variant_def(def); + if variant.kind == VariantKind::Unit && subpats.is_empty() && ddpos.is_some() { + // Matching unit structs with tuple variant patterns (`UnitVariant(..)`) + // is allowed for backward compatibility. + report_unexpected_def(true); + } else if variant.kind != VariantKind::Tuple { + report_unexpected_def(false); return; } + // Type check the path. + let scheme = tcx.lookup_item_type(def.def_id()); + let scheme = if scheme.ty.is_fn() { + // Replace constructor type with constructed type for tuple struct patterns. + let fn_ret = tcx.no_late_bound_regions(&scheme.ty.fn_ret()).unwrap().unwrap(); + ty::TypeScheme { ty: fn_ret, generics: scheme.generics } + } else { + // Leave the type as is for unit structs (backward compatibility). + scheme + }; + let predicates = tcx.lookup_predicates(def.def_id()); + self.instantiate_path(segments, scheme, &predicates, opt_ty, def, pat.span, pat.id); let pat_ty = self.node_ty(pat.id); self.demand_eqtype(pat.span, expected, pat_ty); - let real_path_ty = self.node_ty(pat.id); - let (kind_name, variant, expected_substs) = match real_path_ty.sty { - ty::TyEnum(enum_def, expected_substs) => { - let variant = enum_def.variant_of_def(def); - ("variant", variant, expected_substs) - } - ty::TyStruct(struct_def, expected_substs) => { - let variant = struct_def.struct_variant(); - ("struct", variant, expected_substs) - } - _ => { - report_bad_struct_kind(false); - return; - } - }; - - match (is_tuple_struct_pat, variant.kind()) { - (true, ty::VariantKind::Unit) if subpats.is_empty() && ddpos.is_some() => { - // Matching unit structs with tuple variant patterns (`UnitVariant(..)`) - // is allowed for backward compatibility. - report_bad_struct_kind(true); - } - (true, ty::VariantKind::Unit) | - (false, ty::VariantKind::Tuple) | - (_, ty::VariantKind::Struct) => { - report_bad_struct_kind(false); - return - } - _ => {} - } - + // Type check subpatterns. if subpats.len() == variant.fields.len() || subpats.len() < variant.fields.len() && ddpos.is_some() { + let expected_substs = match pat_ty.sty { + ty::TyEnum(_, expected_substs) => expected_substs, + ty::TyStruct(_, expected_substs) => expected_substs, + ref ty => bug!("unexpected pattern type {:?}", ty), + }; for (i, subpat) in subpats.iter().enumerate_and_adjust(variant.fields.len(), ddpos) { let field_ty = self.field_ty(subpat.span, &variant.fields[i], expected_substs); self.check_pat(&subpat, field_ty); } } else { span_err!(tcx.sess, pat.span, E0023, - "this pattern has {} field{}, but the corresponding {} has {} field{}", - subpats.len(), if subpats.len() == 1 {""} else {"s"}, - kind_name, - variant.fields.len(), if variant.fields.len() == 1 {""} else {"s"}); - - for pat in subpats { - self.check_pat(&pat, tcx.types.err); - } + "this pattern has {} field{s}, but the corresponding {} has {} field{s}", + subpats.len(), def.kind_name(), variant.fields.len(), + s = if variant.fields.len() == 1 {""} else {"s"}); + on_error(); } } diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 3bc90f05d25..8ea32ebbcc6 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -84,7 +84,7 @@ use astconv::{AstConv, ast_region_to_region, PathParamMode}; use dep_graph::DepNode; use fmt_macros::{Parser, Piece, Position}; use middle::cstore::LOCAL_CRATE; -use hir::def::{self, Def}; +use hir::def::{Def, PathResolution}; use hir::def_id::DefId; use hir::pat_util; use rustc::infer::{self, InferCtxt, InferOk, TypeOrigin, TypeTrace, type_variable}; @@ -3349,24 +3349,17 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { }; self.write_ty(id, oprnd_t); } - hir::ExprPath(ref maybe_qself, ref path) => { - let opt_self_ty = maybe_qself.as_ref().map(|qself| { - self.to_ty(&qself.ty) - }); - - let path_res = tcx.expect_resolution(id); - if let Some((opt_ty, segments, def)) = - self.resolve_ty_and_def_ufcs(path_res, opt_self_ty, path, - expr.span, expr.id) { - if def != Def::Err { - let (scheme, predicates) = self.type_scheme_and_predicates_for_def(expr.span, - def); - self.instantiate_path(segments, scheme, &predicates, - opt_ty, def, expr.span, id); - } else { - self.set_tainted_by_errors(); - self.write_ty(id, self.tcx.types.err); - } + hir::ExprPath(ref opt_qself, ref path) => { + let opt_self_ty = opt_qself.as_ref().map(|qself| self.to_ty(&qself.ty)); + let (def, opt_ty, segments) = self.resolve_ty_and_def_ufcs(tcx.expect_resolution(id), + opt_self_ty, path, expr.span, expr.id); + if def != Def::Err { + let (scheme, predicates) = self.type_scheme_and_predicates_for_def(expr.span, + def); + self.instantiate_path(segments, scheme, &predicates, opt_ty, def, expr.span, id); + } else { + self.set_tainted_by_errors(); + self.write_error(id); } // We always require that the type provided as the value for @@ -3704,37 +3697,40 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { expected); } + // Resolve associated value path into a base type and associated constant or method definition. + // The newly resolved definition is written into `def_map`. pub fn resolve_ty_and_def_ufcs<'b>(&self, - path_res: def::PathResolution, + path_res: PathResolution, opt_self_ty: Option>, path: &'b hir::Path, span: Span, node_id: ast::NodeId) - -> Option<(Option>, &'b [hir::PathSegment], Def)> + -> (Def, Option>, &'b [hir::PathSegment]) { - // If fully resolved already, we don't have to do anything. if path_res.depth == 0 { - Some((opt_self_ty, &path.segments, path_res.base_def)) + (path_res.base_def, opt_self_ty, &path.segments) } else { - let def = path_res.base_def; + // Try to resolve everything except for the last segment as a type. let ty_segments = path.segments.split_last().unwrap().1; let base_ty_end = path.segments.len() - path_res.depth; let (ty, _def) = AstConv::finish_resolving_def_to_ty(self, self, span, PathParamMode::Optional, - def, + path_res.base_def, opt_self_ty, node_id, &ty_segments[..base_ty_end], &ty_segments[base_ty_end..]); + + // Resolve an associated constant or method on the previously resolved type. let item_segment = path.segments.last().unwrap(); let item_name = item_segment.name; let def = match self.resolve_ufcs(span, item_name, ty, node_id) { - Ok(def) => Some(def), + Ok(def) => def, Err(error) => { let def = match error { - method::MethodError::PrivateMatch(def) => Some(def), - _ => None, + method::MethodError::PrivateMatch(def) => def, + _ => Def::Err, }; if item_name != keywords::Invalid.name() { self.report_method_error(span, ty, item_name, None, error); @@ -3743,14 +3739,9 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> { } }; - if let Some(def) = def { - // Write back the new resolution. - self.tcx().def_map.borrow_mut().insert(node_id, def::PathResolution::new(def)); - Some((Some(ty), slice::ref_slice(item_segment), def)) - } else { - self.write_error(node_id); - None - } + // Write back the new resolution. + self.tcx().def_map.borrow_mut().insert(node_id, PathResolution::new(def)); + (def, Some(ty), slice::ref_slice(item_segment)) } } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 2c33d1a8155..cc5886f8bbf 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1040,15 +1040,17 @@ fn convert_struct_def<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, def: &hir::VariantData) -> ty::AdtDefMaster<'tcx> { - let did = ccx.tcx.map.local_def_id(it.id); - let ctor_id = if !def.is_struct() { - ccx.tcx.map.local_def_id(def.id()) - } else { - did - }; - ccx.tcx.intern_adt_def(did, ty::AdtKind::Struct, - vec![convert_struct_variant(ccx, ctor_id, it.name, ConstInt::Infer(0), def)]) + // Use separate constructor id for unit/tuple structs and reuse did for braced structs. + let ctor_id = if !def.is_struct() { Some(ccx.tcx.map.local_def_id(def.id())) } else { None }; + let variants = vec![convert_struct_variant(ccx, ctor_id.unwrap_or(did), it.name, + ConstInt::Infer(0), def)]; + let adt = ccx.tcx.intern_adt_def(did, ty::AdtKind::Struct, variants); + if let Some(ctor_id) = ctor_id { + // Make adt definition available through constructor id as well. + ccx.tcx.insert_adt_def(ctor_id, adt); + } + adt } fn evaluate_disr_expr(ccx: &CrateCtxt, repr_ty: attr::IntType, e: &hir::Expr) diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index cdac66a2272..b4d1f710704 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -3225,42 +3225,6 @@ impl Foo for Bar { ``` "##, -E0327: r##" -You cannot use associated items other than constant items as patterns. This -includes method items. Example of erroneous code: - -```compile_fail -enum B {} - -impl B { - fn bb() -> i32 { 0 } -} - -fn main() { - match 0 { - B::bb => {} // error: associated items in match patterns must - // be constants - } -} -``` - -Please check that you're not using a method as a pattern. Example: - -``` -enum B { - ba, - bb -} - -fn main() { - match B::ba { - B::bb => {} // ok! - _ => {} - } -} -``` -"##, - E0329: r##" An attempt was made to access an associated constant through either a generic type parameter or `Self`. This is not supported yet. An example causing this @@ -4162,4 +4126,5 @@ register_diagnostics! { E0527, // expected {} elements, found {} E0528, // expected at least {} elements, found {} E0529, // slice pattern expects array or slice, not `{}` + E0533, // `{}` does not name a unit variant, unit struct or a constant } diff --git a/src/test/compile-fail/associated-types/cache/project-fn-ret-contravariant.rs b/src/test/compile-fail/associated-types/cache/project-fn-ret-contravariant.rs index c5557cee7cc..9404803a32d 100644 --- a/src/test/compile-fail/associated-types/cache/project-fn-ret-contravariant.rs +++ b/src/test/compile-fail/associated-types/cache/project-fn-ret-contravariant.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(fn_traits)] #![feature(unboxed_closures)] #![feature(rustc_attrs)] diff --git a/src/test/compile-fail/associated-types/cache/project-fn-ret-invariant.rs b/src/test/compile-fail/associated-types/cache/project-fn-ret-invariant.rs index a15422e42d9..99568213d99 100644 --- a/src/test/compile-fail/associated-types/cache/project-fn-ret-invariant.rs +++ b/src/test/compile-fail/associated-types/cache/project-fn-ret-invariant.rs @@ -18,6 +18,7 @@ // revisions: ok oneuse transmute krisskross +#![feature(fn_traits)] #![allow(dead_code, unused_variables)] use std::marker::PhantomData; diff --git a/src/test/compile-fail/empty-struct-braces-pat-1.rs b/src/test/compile-fail/empty-struct-braces-pat-1.rs index a5c740d9f63..74546152ca9 100644 --- a/src/test/compile-fail/empty-struct-braces-pat-1.rs +++ b/src/test/compile-fail/empty-struct-braces-pat-1.rs @@ -31,12 +31,14 @@ fn main() { Empty1 => () // Not an error, `Empty1` is interpreted as a new binding } match e3 { - E::Empty3 => () //~ ERROR `E::Empty3` does not name a tuple variant or a tuple struct + E::Empty3 => () + //~^ ERROR `E::Empty3` does not name a unit variant, unit struct or a constant } match xe1 { XEmpty1 => () // Not an error, `XEmpty1` is interpreted as a new binding } match xe3 { - XE::XEmpty3 => () //~ ERROR `XE::XEmpty3` does not name a tuple variant or a tuple struct + XE::XEmpty3 => () + //~^ ERROR `XE::XEmpty3` does not name a unit variant, unit struct or a constant } } diff --git a/src/test/compile-fail/issue-32004.rs b/src/test/compile-fail/issue-32004.rs index 8d74154655f..576451f7292 100644 --- a/src/test/compile-fail/issue-32004.rs +++ b/src/test/compile-fail/issue-32004.rs @@ -18,7 +18,7 @@ struct S; fn main() { match Foo::Baz { Foo::Bar => {} - //~^ ERROR `Foo::Bar` does not name a tuple variant or a tuple struct + //~^ ERROR `Foo::Bar` does not name a unit variant, unit struct or a constant _ => {} } diff --git a/src/test/compile-fail/method-path-in-pattern.rs b/src/test/compile-fail/method-path-in-pattern.rs index faf6d255c9a..ef011c89c62 100644 --- a/src/test/compile-fail/method-path-in-pattern.rs +++ b/src/test/compile-fail/method-path-in-pattern.rs @@ -22,12 +22,13 @@ impl MyTrait for Foo {} fn main() { match 0u32 { - Foo::bar => {} //~ ERROR E0327 + Foo::bar => {} //~ ERROR `Foo::bar` does not name a unit variant, unit struct or a constant } match 0u32 { - ::bar => {} //~ ERROR E0327 + ::bar => {} //~ ERROR `bar` does not name a unit variant, unit struct or a constant } match 0u32 { - ::trait_bar => {} //~ ERROR E0327 + ::trait_bar => {} + //~^ ERROR `trait_bar` does not name a unit variant, unit struct or a constant } } diff --git a/src/test/compile-fail/qualified-path-params.rs b/src/test/compile-fail/qualified-path-params.rs index 86873022f0f..9034e24a6fe 100644 --- a/src/test/compile-fail/qualified-path-params.rs +++ b/src/test/compile-fail/qualified-path-params.rs @@ -27,7 +27,8 @@ impl S { fn main() { match 10 { - ::A::f:: => {} //~ ERROR associated items in match patterns must be constants + ::A::f:: => {} + //~^ ERROR `Tr::A::f` does not name a unit variant, unit struct or a constant 0 ... ::A::f:: => {} //~ ERROR only char and numeric types are allowed in range } } diff --git a/src/test/run-pass/issue-28550.rs b/src/test/run-pass/issue-28550.rs index f44a535e817..83e3e40b3a8 100644 --- a/src/test/run-pass/issue-28550.rs +++ b/src/test/run-pass/issue-28550.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(fn_traits)] + struct AT,T>(F::Output); struct BT,T>(A);