From 250dc4e5691d68ecb051a1fd11a21cbee8c68fb7 Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Mon, 3 Aug 2015 23:40:18 +0300 Subject: [PATCH] Allow anonymized return types to be declared in associated types. --- src/librustc_typeck/astconv.rs | 2 +- src/librustc_typeck/check/closure.rs | 1 + src/librustc_typeck/check/mod.rs | 77 +- src/librustc_typeck/check/writeback.rs | 40 +- src/librustc_typeck/collect.rs | 4 +- src/librustc_typeck/diagnostics.rs | 7 +- .../run-pass/anon-types-example-calendar.rs | 946 ++++++++++++++++++ src/test/run-pass/anon-types-example-st.rs | 39 + 8 files changed, 1089 insertions(+), 27 deletions(-) create mode 100644 src/test/run-pass/anon-types-example-calendar.rs create mode 100644 src/test/run-pass/anon-types-example-st.rs diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 5062903d2d39f..b330c03545465 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -1598,7 +1598,7 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>, (None, _) => { span_err!(this.tcx().sess, ast_ty.span, E0439, "anonymized types are not allowed outside of function and \ - impl method return types"); + impl method return types and impl associated types"); this.tcx().types.err } (Some(_), Err(_)) => this.tcx().types.err, diff --git a/src/librustc_typeck/check/closure.rs b/src/librustc_typeck/check/closure.rs index a2ea33b841c73..5ac5353164660 100644 --- a/src/librustc_typeck/check/closure.rs +++ b/src/librustc_typeck/check/closure.rs @@ -84,6 +84,7 @@ fn check_closure<'a,'tcx>(fcx: &FnCtxt<'a,'tcx>, expr.id, &fn_sig, decl, + None, expr.id, &*body, fcx.inh); diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index a2747850f96a3..7d9137c014004 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -172,11 +172,13 @@ pub struct Inherited<'a, 'tcx: 'a> { deferred_cast_checks: RefCell>>, - // Anonymized types found in explicit return types and their - // associated fresh inference variable. Writeback resolves these - // variables to get the concrete type, which can be used to - // deanonymize TyAnon, after typeck is done with all functions. - anon_types: RefCell)>>, + // Anonymized types found in explicit return types, their param + // space of definition (TypeSpace in associated types, FnSpace in + // function return types) and their respective fresh inference + // variable. Writeback resolves these variables to get the concrete + // type, which can be used to deanonymize TyAnon, after typeck is + // done processing all functions. + anon_types: RefCell)>>, } trait DeferredCallResolution<'tcx> { @@ -364,6 +366,7 @@ fn static_inherited_fields<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>, struct CheckItemTypesVisitor<'a, 'tcx: 'a> { ccx: &'a CrateCtxt<'a, 'tcx> } struct CheckItemBodiesVisitor<'a, 'tcx: 'a> { ccx: &'a CrateCtxt<'a, 'tcx> } +struct CheckAnonTypesVisitor<'a, 'tcx: 'a> { ccx: &'a CrateCtxt<'a, 'tcx> } impl<'a, 'tcx> Visitor<'tcx> for CheckItemTypesVisitor<'a, 'tcx> { fn visit_item(&mut self, i: &'tcx ast::Item) { @@ -390,6 +393,22 @@ impl<'a, 'tcx> Visitor<'tcx> for CheckItemBodiesVisitor<'a, 'tcx> { } } +impl<'a, 'tcx> Visitor<'tcx> for CheckAnonTypesVisitor<'a, 'tcx> { + fn visit_ty(&mut self, ty: &'tcx ast::Ty) { + if let ast::TyAnon(_) = ty.node { + if !self.ccx.tcx.tcache.borrow().contains_key(&local_def(ty.id)) { + span_err!(self.ccx.tcx.sess, ty.span, E0442, + "anonymized types must be used in a return \ + type in the same impl"); + } + } + visit::walk_ty(self, ty); + } + + // Do not recurse into items. + fn visit_item(&mut self, _: &ast::Item) {} +} + pub fn check_item_types(ccx: &CrateCtxt) { let krate = ccx.tcx.map.krate(); let mut visit = wf::CheckTypeWellFormedVisitor::new(ccx); @@ -425,6 +444,7 @@ pub fn check_item_types(ccx: &CrateCtxt) { } fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, + impl_id: Option, decl: &'tcx ast::FnDecl, body: &'tcx ast::Block, fn_id: ast::NodeId, @@ -449,7 +469,7 @@ fn check_bare_fn<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, &fn_sig); let fcx = check_fn(ccx, fn_ty.unsafety, fn_id, &fn_sig, - decl, fn_id, body, &inh); + decl, impl_id, fn_id, body, &inh); fcx.select_all_obligations_and_apply_defaults(); upvar::closure_analyze_fn(&fcx, fn_id, decl, body); @@ -559,6 +579,7 @@ fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>, unsafety_id: ast::NodeId, fn_sig: &ty::FnSig<'tcx>, decl: &'tcx ast::FnDecl, + impl_id: Option, fn_id: ast::NodeId, body: &'tcx ast::Block, inherited: &'a Inherited<'a, 'tcx>) @@ -632,7 +653,7 @@ fn check_fn<'a, 'tcx>(ccx: &'a CrateCtxt<'a, 'tcx>, check_block_with_expected(&fcx, body, match ret_ty { ty::FnConverging(result_type) => { - ExpectHasType(fcx.instantiate_anon_types(result_type)) + ExpectHasType(fcx.instantiate_anon_types(result_type, fn_id, impl_id)) } ty::FnDiverging => NoExpectation }); @@ -725,7 +746,7 @@ pub fn check_item_body<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) { ast::ItemFn(ref decl, _, _, _, _, ref body) => { let fn_pty = ccx.tcx.lookup_item_type(ast_util::local_def(it.id)); let param_env = ParameterEnvironment::for_item(ccx.tcx, it.id); - check_bare_fn(ccx, &**decl, &**body, it.id, it.span, fn_pty.ty, param_env); + check_bare_fn(ccx, None, &**decl, &**body, it.id, it.span, fn_pty.ty, param_env); } ast::ItemImpl(_, _, _, _, _, ref impl_items) => { debug!("ItemImpl {} with id {}", it.ident, it.id); @@ -738,8 +759,8 @@ pub fn check_item_body<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) { check_const(ccx, impl_item.span, &*expr, impl_item.id) } ast::MethodImplItem(ref sig, ref body) => { - check_method_body(ccx, &impl_pty.generics, sig, body, - impl_item.id, impl_item.span); + check_method_body(ccx, Some(it.id), &impl_pty.generics, sig, + body, impl_item.id, impl_item.span); } ast::TypeImplItem(_) | ast::MacImplItem(_) => { @@ -747,6 +768,14 @@ pub fn check_item_body<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) { } } } + + // Check that if we had anonymized types in an associated type, + // they were assigned by one of the methods in this impl. + for impl_item in impl_items { + if let ast::TypeImplItem(ref ty) = impl_item.node { + CheckAnonTypesVisitor { ccx: ccx }.visit_ty(ty); + } + } } ast::ItemTrait(_, _, _, ref trait_items) => { let trait_def = ccx.tcx.lookup_trait_def(local_def(it.id)); @@ -758,8 +787,8 @@ pub fn check_item_body<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>, it: &'tcx ast::Item) { ast::MethodTraitItem(ref sig, Some(ref body)) => { check_trait_fn_not_const(ccx, trait_item.span, sig.constness); - check_method_body(ccx, &trait_def.generics, sig, body, - trait_item.id, trait_item.span); + check_method_body(ccx, None, &trait_def.generics, sig, + body, trait_item.id, trait_item.span); } ast::MethodTraitItem(ref sig, None) => { check_trait_fn_not_const(ccx, trait_item.span, sig.constness); @@ -842,6 +871,7 @@ fn check_trait_on_unimplemented<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, /// * `self_bound`: bound for the `Self` type parameter, if any /// * `method`: the method definition fn check_method_body<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, + impl_id: Option, item_generics: &ty::Generics<'tcx>, sig: &'tcx ast::MethodSig, body: &'tcx ast::Block, @@ -853,7 +883,7 @@ fn check_method_body<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, let fty = ccx.tcx.node_id_to_type(id); debug!("check_method_body: fty={:?}", fty); - check_bare_fn(ccx, &sig.decl, body, id, span, fty, param_env); + check_bare_fn(ccx, impl_id, &sig.decl, body, id, span, fty, param_env); } fn check_impl_items_against_trait<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, @@ -1363,15 +1393,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Replace all anonymized types with fresh inference variables /// and record them for writeback. - fn instantiate_anon_types(&self, ty: Ty<'tcx>) -> Ty<'tcx> { + fn instantiate_anon_types(&self, ty: Ty<'tcx>, fn_id: ast::NodeId, + impl_id: Option) -> Ty<'tcx> { let tcx = self.tcx(); let mut anon_types = self.inh.anon_types.borrow_mut(); ty.fold_with(&mut BottomUpFolder { tcx: tcx, fldop: |ty| { if let ty::TyAnon(def_id, _, ref data) = ty.sty { + // Do not instantiate an `impl Trait` type unless it was + // defined in the same function's return type or in an + // associated type of the impl, in the case of methods. + if def_id.krate != ast::LOCAL_CRATE { + return ty; + } + + let parent = tcx.map.get_parent(def_id.node); + let param_space = if parent == fn_id { + subst::FnSpace + } else if Some(tcx.map.get_parent(parent)) == impl_id { + subst::TypeSpace + } else { + return ty; + }; + let ty_var = self.infcx().next_ty_var(); - anon_types.push((def_id, ty_var)); + anon_types.push((def_id, param_space, ty_var)); let span = tcx.map.def_id_span(def_id, codemap::DUMMY_SP); let cause = traits::ObligationCause::new(span, self.body_id, diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index 495a42ae4cf3d..61da89c7435ab 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -14,8 +14,9 @@ use self::ResolveReason::*; use astconv::AstConv; -use check::FnCtxt; +use check::{FnCtxt, demand}; use middle::pat_util; +use middle::subst::{FnSpace, TypeSpace}; use middle::ty::{self, Ty, MethodCall, MethodCallee}; use middle::ty_fold::{TypeFolder,TypeFoldable}; use middle::infer; @@ -246,12 +247,35 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { return } - for &(def_id, concrete_ty) in &*self.fcx.inh.anon_types.borrow() { - let concrete_ty = self.resolve(&concrete_ty, ResolvingAnonTy(def_id)); - self.fcx.tcx().tcache.borrow_mut().insert(def_id, ty::TypeScheme { - ty: concrete_ty, - generics: ty::Generics::empty() - }); + for &(def_id, param_space, concrete_ty) in &*self.fcx.inh.anon_types.borrow() { + let reason = ResolvingAnonTy(def_id); + let concrete_ty = self.resolve(&concrete_ty, reason); + let old_ty = self.fcx.tcx().tcache.borrow_mut().get(&def_id).as_ref().map(|t| t.ty); + + if let Some(old_ty) = old_ty { + // Ensure all anonymized type assignments agree with eachother. + demand::eqtype(self.fcx, reason.span(self.fcx.tcx()), old_ty, concrete_ty); + } else { + // Do not let method type parameters escape into anonymized types + // defined inside an associated type, which is only parametrized + // on the impl type parameters. + if param_space == TypeSpace { + for ty in concrete_ty.walk() { + if let ty::TyParam(ty::ParamTy { space: FnSpace, .. }) = ty.sty { + let span = reason.span(self.fcx.tcx()); + span_err!(self.fcx.tcx().sess, span, E0441, + "method type parameter `{}` cannot be used in an \ + anonymized type defined in an associated type", + ty); + } + } + } + + self.fcx.tcx().tcache.borrow_mut().insert(def_id, ty::TypeScheme { + ty: concrete_ty, + generics: ty::Generics::empty() + }); + } } } @@ -441,7 +465,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { ResolvingAnonTy(_) => { let span = self.reason.span(self.tcx); - span_err!(self.tcx.sess, span, E0399, + span_err!(self.tcx.sess, span, E0440, "cannot determine a concrete type for this anonymized type") } } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 7808f57758306..d9e8cb241b715 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -916,7 +916,9 @@ fn convert_item(ccx: &CrateCtxt, it: &ast::Item) { "associated types are not allowed in inherent impls"); } - let typ = ccx.icx(&ty_predicates).to_ty(&ExplicitRscope, ty); + let anon_scope = Some(AnonTypeScope::new(&ty_generics)); + let rscope = MaybeWithAnonTypes::new(ExplicitRscope, anon_scope); + let typ = ccx.icx(&ty_predicates).to_ty(&rscope, ty); convert_associated_type(ccx, ImplContainer(local_def(it.id)), impl_item.ident, impl_item.id, impl_item.vis, diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 157986a9534c8..aeee349c9021d 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -2474,6 +2474,9 @@ register_diagnostics! { // type `{}` was overridden E0436, // functional record update requires a struct E0439, // anonymized types are not allowed outside of function and - // impl method return types - E0440 // cannot determine a concrete type for this anonymized type + // impl method return types and impl associated types + E0440, // cannot determine a concrete type for this anonymized type + E0441, // method type parameter `{}` cannot be used in an anonymized type + // defined in an associated type + E0442 // anonymized types must be used in a return type in the same impl } diff --git a/src/test/run-pass/anon-types-example-calendar.rs b/src/test/run-pass/anon-types-example-calendar.rs new file mode 100644 index 0000000000000..5001c94dc87ef --- /dev/null +++ b/src/test/run-pass/anon-types-example-calendar.rs @@ -0,0 +1,946 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(core, step_trait, unboxed_closures, zero_one)] + +//! Derived from: +//! . + +use std::fmt::Write; + +/// Date representation. +#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +struct NaiveDate(i32, u32, u32); + +impl NaiveDate { + pub fn from_ymd(y: i32, m: u32, d: u32) -> NaiveDate { + assert!(1 <= m && m <= 12, "m = {:?}", m); + assert!(1 <= d && d <= NaiveDate(y, m, 1).days_in_month(), "d = {:?}", d); + NaiveDate(y, m, d) + } + + pub fn year(&self) -> i32 { + self.0 + } + + pub fn month(&self) -> u32 { + self.1 + } + + pub fn day(&self) -> u32 { + self.2 + } + + pub fn succ(&self) -> NaiveDate { + let (mut y, mut m, mut d, n) = ( + self.year(), self.month(), self.day()+1, self.days_in_month()); + if d > n { + d = 1; + m += 1; + } + if m > 12 { + m = 1; + y += 1; + } + NaiveDate::from_ymd(y, m, d) + } + + pub fn weekday(&self) -> Weekday { + use Weekday::*; + + // 0 = Sunday + let year = self.year(); + let dow_jan_1 = (year*365 + ((year-1) / 4) - ((year-1) / 100) + ((year-1) / 400)) % 7; + let dow = (dow_jan_1 + (self.day_of_year() as i32 - 1)) % 7; + [Sun, Mon, Tue, Wed, Thu, Fri, Sat][dow as usize] + } + + pub fn isoweekdate(&self) -> (i32, u32, Weekday) { + let first_dow_mon_0 = self.year_first_day_of_week().num_days_from_monday(); + + // Work out this date's DOtY and week number, not including year adjustment. + let doy_0 = self.day_of_year() - 1; + let mut week_mon_0: i32 = ((first_dow_mon_0 + doy_0) / 7) as i32; + + if self.first_week_in_prev_year() { + week_mon_0 -= 1; + } + + let weeks_in_year = self.last_week_number(); + + // Work out the final result. If the week is -1 or >= weeks_in_year, we will need to adjust the year. + let year = self.year(); + let wd = self.weekday(); + + if week_mon_0 < 0 { + (year - 1, NaiveDate::from_ymd(year - 1, 1, 1).last_week_number(), wd) + } else if week_mon_0 >= weeks_in_year as i32 { + (year + 1, (week_mon_0 + 1 - weeks_in_year as i32) as u32, wd) + } else { + (year, (week_mon_0 + 1) as u32, wd) + } + } + + fn first_week_in_prev_year(&self) -> bool { + let first_dow_mon_0 = self.year_first_day_of_week().num_days_from_monday(); + + // Any day in the year *before* the first Monday of that year is considered to be in the last week of the previous year, assuming the first week has *less* than four days in it. Adjust the week appropriately. + ((7 - first_dow_mon_0) % 7) < 4 + } + + fn year_first_day_of_week(&self) -> Weekday { + NaiveDate::from_ymd(self.year(), 1, 1).weekday() + } + + fn weeks_in_year(&self) -> u32 { + let days_in_last_week = self.year_first_day_of_week().num_days_from_monday() + 1; + if days_in_last_week >= 4 { 53 } else { 52 } + } + + fn last_week_number(&self) -> u32 { + let wiy = self.weeks_in_year(); + if self.first_week_in_prev_year() { wiy - 1 } else { wiy } + } + + fn day_of_year(&self) -> u32 { + (1..self.1).map(|m| NaiveDate::from_ymd(self.year(), m, 1).days_in_month()) + .fold(0, |a,b| a+b) + self.day() + } + + fn is_leap_year(&self) -> bool { + let year = self.year(); + if year % 4 != 0 { + return false + } else if year % 100 != 0 { + return true + } else if year % 400 != 0 { + return false + } else { + return true + } + } + + fn days_in_month(&self) -> u32 { + match self.month() { + /* Jan */ 1 => 31, + /* Feb */ 2 => if self.is_leap_year() { 29 } else { 28 }, + /* Mar */ 3 => 31, + /* Apr */ 4 => 30, + /* May */ 5 => 31, + /* Jun */ 6 => 30, + /* Jul */ 7 => 31, + /* Aug */ 8 => 31, + /* Sep */ 9 => 30, + /* Oct */ 10 => 31, + /* Nov */ 11 => 30, + /* Dec */ 12 => 31, + _ => unreachable!() + } + } +} + +impl std::num::One for NaiveDate { + fn one() -> NaiveDate { + NaiveDate(0, 0, 1) + } +} + +impl<'a, 'b> std::ops::Add<&'b NaiveDate> for &'a NaiveDate { + type Output = NaiveDate; + + fn add(self, other: &'b NaiveDate) -> NaiveDate { + assert_eq!(*other, NaiveDate(0, 0, 1)); + self.succ() + } +} + +impl std::iter::Step for NaiveDate { + fn step(&self, by: &Self) -> Option { + Some(self + by) + } + fn steps_between(_start: &Self, _end: &Self, _by: &Self) -> Option { + unimplemented!() + } +} + +#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub enum Weekday { + Mon, + Tue, + Wed, + Thu, + Fri, + Sat, + Sun, +} + +impl Weekday { + pub fn num_days_from_monday(&self) -> u32 { + use Weekday::*; + match *self { + Mon => 0, + Tue => 1, + Wed => 2, + Thu => 3, + Fri => 4, + Sat => 5, + Sun => 6, + } + } + + pub fn num_days_from_sunday(&self) -> u32 { + use Weekday::*; + match *self { + Sun => 0, + Mon => 1, + Tue => 2, + Wed => 3, + Thu => 4, + Fri => 5, + Sat => 6, + } + } +} + +/// Wrapper for zero-sized closures. +struct Fn0(std::marker::PhantomData); + +impl Copy for Fn0 {} +impl Clone for Fn0 { + fn clone(&self) -> Self { *self } +} + +impl, A> FnOnce for Fn0 { + type Output = F::Output; + + extern "rust-call" fn call_once(self, args: A) -> Self::Output { + let f = unsafe { std::mem::uninitialized::() }; + f.call_once(args) + } +} + +impl, A> FnMut for Fn0 { + extern "rust-call" fn call_mut(&mut self, args: A) -> Self::Output { + let mut f = unsafe { std::mem::uninitialized::() }; + f.call_mut(args) + } +} + +trait AsFn0 { + fn copyable(self) -> Fn0; +} + +impl, A> AsFn0 for F { + fn copyable(self) -> Fn0 { + assert_eq!(std::mem::size_of::(), 0); + Fn0(std::marker::PhantomData) + } +} + +/// GroupBy implementation. +struct GroupBy { + it: std::iter::Peekable, + f: F, +} + +impl Clone for GroupBy +where It: CloneIterator, It::Item: Clone, F: Clone { + fn clone(&self) -> GroupBy { + GroupBy { + it: self.it.clone(), + f: self.f.clone() + } + } +} + +impl<'a, G, It: 'a, F: 'a> Iterator for GroupBy +where It: CloneIterator, + It::Item: Clone, + F: Clone + FnMut(&It::Item) -> G, + G: Eq + Clone +{ + type Item = (G, InGroup, F, G>); + + fn next(&mut self) -> Option { + self.it.peek().map(&mut self.f).map(|key| { + let start = self.it.clone(); + while let Some(k) = self.it.peek().map(&mut self.f) { + if key != k { + break; + } + self.it.next(); + } + + (key.clone(), InGroup { + it: start, + f: self.f.clone(), + g: key + }) + }) + } +} + +#[derive(Copy, Clone)] +struct InGroup { + it: It, + f: F, + g: G +} + +impl G, G: Eq> Iterator for InGroup { + type Item = It::Item; + + fn next(&mut self) -> Option { + self.it.next().and_then(|x| { + if (self.f)(&x) == self.g { Some(x) } else { None } + }) + } +} + +trait IteratorExt: Iterator + Sized { + fn group_by(self, f: F) -> GroupBy> + where F: FnMut(&Self::Item) -> G, + G: Eq + { + GroupBy { + it: self.peekable(), + f: f.copyable(), + } + } + + fn join(mut self, sep: &str) -> String + where Self::Item: std::fmt::Display { + let mut s = String::new(); + if let Some(e) = self.next() { + write!(s, "{}", e); + for e in self { + s.push_str(sep); + write!(s, "{}", e); + } + } + s + } +} + +impl IteratorExt for It where It: Iterator {} + +trait CloneIterator: Clone+Iterator {} +impl CloneIterator for It {} + +/// +/// Generates an iterator that yields exactly n spaces. +/// +fn spaces(n: usize) -> std::iter::Take> { + std::iter::repeat(' ').take(n) +} + +fn test_spaces() { + assert_eq!(spaces(0).collect::(), ""); + assert_eq!(spaces(10).collect::(), " ") +} + +/// +/// Returns an iterator of dates in a given year. +/// +fn dates_in_year(year: i32) -> impl CloneIterator { + InGroup { + it: NaiveDate::from_ymd(year, 1, 1).., + f: (|d: &NaiveDate| d.year()).copyable(), + g: year + } +} + +fn test_dates_in_year() { + { + let mut dates = dates_in_year(2013); + assert_eq!(dates.next(), Some(NaiveDate::from_ymd(2013, 1, 1))); + + // Check increment + assert_eq!(dates.next(), Some(NaiveDate::from_ymd(2013, 1, 2))); + + // Check monthly rollover + for _ in 3..31 { + assert!(dates.next() != None); + } + + assert_eq!(dates.next(), Some(NaiveDate::from_ymd(2013, 1, 31))); + assert_eq!(dates.next(), Some(NaiveDate::from_ymd(2013, 2, 1))); + } + + { + // Check length of year + let mut dates = dates_in_year(2013); + for _ in 0..365 { + assert!(dates.next() != None); + } + assert_eq!(dates.next(), None); + } + + { + // Check length of leap year + let mut dates = dates_in_year(1984); + for _ in 0..366 { + assert!(dates.next() != None); + } + assert_eq!(dates.next(), None); + } +} + +/// +/// Convenience trait for verifying that a given type iterates over +/// `NaiveDate`s. +/// +trait DateIterator: CloneIterator {} +impl DateIterator for It where It: CloneIterator {} + +fn test_group_by() { + let input = [ + [1, 1], + [1, 1], + [1, 2], + [2, 2], + [2, 3], + [2, 3], + [3, 3] + ]; + + let by_x = input.iter().cloned().group_by(|a| a[0]); + let expected_1: &[&[[i32; 2]]] = &[ + &[[1, 1], [1, 1], [1, 2]], + &[[2, 2], [2, 3], [2, 3]], + &[[3, 3]] + ]; + for ((_, a), b) in by_x.zip(expected_1.iter().cloned()) { + assert_eq!(&a.collect::>()[..], b); + } + + let by_y = input.iter().cloned().group_by(|a| a[1]); + let expected_2: &[&[[i32; 2]]] = &[ + &[[1, 1], [1, 1]], + &[[1, 2], [2, 2]], + &[[2, 3], [2, 3], [3, 3]] + ]; + for ((_, a), b) in by_y.zip(expected_2.iter().cloned()) { + assert_eq!(&a.collect::>()[..], b); + } +} + +/// +/// Groups an iterator of dates by month. +/// + +trait ByMonth<'a> { + type Months: 'a; + + fn by_month(self) -> Self::Months; +} + +impl<'a, It: 'a> ByMonth<'a> for It where It: DateIterator { + type Months = impl CloneIterator + 'a)> + 'a; + + fn by_month(self) -> Self::Months { + self.group_by(|d| d.month()) + } +} + +fn test_by_month() { + let mut months = dates_in_year(2013).by_month(); + for (month, (_, mut date)) in (1..13).zip(&mut months) { + assert_eq!(date.nth(0).unwrap(), NaiveDate::from_ymd(2013, month, 1)); + } + assert!(months.next().is_none()); +} + +/// +/// Groups an iterator of dates by week. +/// + +trait ByWeek<'a> { + type Weeks: 'a; + + fn by_week(self) -> Self::Weeks; +} + +impl<'a, It: 'a> ByWeek<'a> for It where It: DateIterator { + type Weeks = impl CloneIterator + 'a)> + 'a; + + fn by_week(self) -> Self::Weeks { + // We go forward one day because `isoweekdate` considers the week to start on a Monday. + self.group_by(|d| d.succ().isoweekdate().1) + } +} + +fn test_isoweekdate() { + fn weeks_uniq(year: i32) -> Vec<((i32, u32), u32)> { + let mut weeks = dates_in_year(year).map(|d| d.isoweekdate()) + .map(|(y,w,_)| (y,w)); + let mut result = vec![]; + let mut accum = (weeks.next().unwrap(), 1); + for yw in weeks { + if accum.0 == yw { + accum.1 += 1; + } else { + result.push(accum); + accum = (yw, 1); + } + } + result.push(accum); + result + } + + let wu_1984 = weeks_uniq(1984); + assert_eq!(&wu_1984[..2], &[((1983, 52), 1), ((1984, 1), 7)]); + assert_eq!(&wu_1984[wu_1984.len()-2..], &[((1984, 52), 7), ((1985, 1), 1)]); + + let wu_2013 = weeks_uniq(2013); + assert_eq!(&wu_2013[..2], &[((2013, 1), 6), ((2013, 2), 7)]); + assert_eq!(&wu_2013[wu_2013.len()-2..], &[((2013, 52), 7), ((2014, 1), 2)]); + + let wu_2015 = weeks_uniq(2015); + assert_eq!(&wu_2015[..2], &[((2015, 1), 4), ((2015, 2), 7)]); + assert_eq!(&wu_2015[wu_2015.len()-2..], &[((2015, 52), 7), ((2015, 53), 4)]); +} + +fn test_by_week() { + let mut weeks = dates_in_year(2013).by_week(); + assert_eq!( + &*weeks.next().unwrap().1.collect::>(), + &[ + NaiveDate::from_ymd(2013, 1, 1), + NaiveDate::from_ymd(2013, 1, 2), + NaiveDate::from_ymd(2013, 1, 3), + NaiveDate::from_ymd(2013, 1, 4), + NaiveDate::from_ymd(2013, 1, 5), + ] + ); + assert_eq!( + &*weeks.next().unwrap().1.collect::>(), + &[ + NaiveDate::from_ymd(2013, 1, 6), + NaiveDate::from_ymd(2013, 1, 7), + NaiveDate::from_ymd(2013, 1, 8), + NaiveDate::from_ymd(2013, 1, 9), + NaiveDate::from_ymd(2013, 1, 10), + NaiveDate::from_ymd(2013, 1, 11), + NaiveDate::from_ymd(2013, 1, 12), + ] + ); + assert_eq!(weeks.next().unwrap().1.nth(0).unwrap(), NaiveDate::from_ymd(2013, 1, 13)); +} + +/// The number of columns per day in the formatted output. +const COLS_PER_DAY: u32 = 3; + +/// The number of columns per week in the formatted output. +const COLS_PER_WEEK: u32 = 7 * COLS_PER_DAY; + +/// +/// Formats an iterator of weeks into an iterator of strings. +/// + +trait FormatWeeks<'a> { + type WeekDays: 'a; + + fn format_weeks(self) -> Self::WeekDays; +} + +impl<'a, It: 'a> FormatWeeks<'a> for It +where It: Iterator, It::Item: DateIterator { + type WeekDays = impl Iterator + 'a; + + fn format_weeks(self) -> Self::WeekDays { + self.map(|week| { + let mut buf = String::with_capacity((COLS_PER_DAY * COLS_PER_WEEK + 2) as usize); + + // Format each day into its own cell and append to target string. + let mut last_day = 0; + let mut first = true; + for d in week { + last_day = d.weekday().num_days_from_sunday(); + + // Insert enough filler to align the first day with its respective day-of-week. + if first { + buf.extend(spaces((COLS_PER_DAY * last_day) as usize)); + first = false; + } + + write!(buf, " {:>2}", d.day()); + } + + // Insert more filler at the end to fill up the remainder of the week, + // if its a short week (e.g. at the end of the month). + buf.extend(spaces((COLS_PER_DAY * (6 - last_day)) as usize)); + buf + }) + } +} + +fn test_format_weeks() { + let jan_2013 = dates_in_year(2013) + .by_month().next() // pick January 2013 for testing purposes + // NOTE: This `map` is because `next` returns an `Option<_>`. + .map(|(_, month)| + month.by_week() + .map(|(_, weeks)| weeks) + .format_weeks() + .join("\n")); + + assert_eq!( + jan_2013.as_ref().map(|s| &**s), + Some(" 1 2 3 4 5\n\ + \x20 6 7 8 9 10 11 12\n\ + \x2013 14 15 16 17 18 19\n\ + \x2020 21 22 23 24 25 26\n\ + \x2027 28 29 30 31 ") + ); +} + +/// +/// Formats the name of a month, centered on COLS_PER_WEEK. +/// +fn month_title(month: u32) -> String { + const MONTH_NAMES: &'static [&'static str] = &[ + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" + ]; + assert_eq!(MONTH_NAMES.len(), 12); + + // Determine how many spaces before and after the month name we need to center it over the formatted weeks in the month. + let name = MONTH_NAMES[(month - 1) as usize]; + assert!(name.len() < COLS_PER_WEEK as usize); + let before = (COLS_PER_WEEK as usize - name.len()) / 2; + let after = COLS_PER_WEEK as usize - name.len() - before; + + // NOTE: Being slightly more verbose to avoid extra allocations. + let mut result = String::with_capacity(COLS_PER_WEEK as usize); + result.extend(spaces(before)); + result.push_str(name); + result.extend(spaces(after)); + result +} + +fn test_month_title() { + assert_eq!(month_title(1).len(), COLS_PER_WEEK as usize); +} + +/// +/// Formats a month. +/// + +trait FormatMonth<'a> { + type MonthLines: 'a; + + fn format_month(self) -> Self::MonthLines; +} + +impl<'a, It: 'a> FormatMonth<'a> for It where It: DateIterator { + type MonthLines = impl Iterator + 'a; + + fn format_month(self) -> Self::MonthLines { + let mut month_days = self.peekable(); + let title = month_title(month_days.peek().unwrap().month()); + + Some(title).into_iter() + .chain(month_days.by_week() + .map(|(_, week)| week) + .format_weeks()) + } +} + +fn test_format_month() { + let month_fmt = dates_in_year(2013) + .by_month().next() // Pick January as a test case + .map(|(_, days)| days.into_iter() + .format_month() + .join("\n")); + + assert_eq!( + month_fmt.as_ref().map(|s| &**s), + Some(" January \n\ + \x20 1 2 3 4 5\n\ + \x20 6 7 8 9 10 11 12\n\ + \x2013 14 15 16 17 18 19\n\ + \x2020 21 22 23 24 25 26\n\ + \x2027 28 29 30 31 ") + ); +} + + +/// +/// Formats an iterator of months. +/// + +trait FormatMonths<'a> { + // I gave up trying to remember WTF this should be called. + type Output: 'a; + + fn format_months(self) -> Self::Output; +} + +impl<'a, It: 'a> FormatMonths<'a> for It +where It: Iterator, It::Item: DateIterator + 'a { + type Output = impl Iterator + 'a> + 'a; + + fn format_months(self) -> Self::Output { + self.map(<_>::format_month) + } +} + +/// +/// Takes an iterator of iterators of strings; the sub-iterators are consumed +/// in lock-step, with their elements joined together. +/// +trait PasteBlocks: Iterator + Sized +where Self::Item: Iterator { + fn paste_blocks(self, sep_width: usize) -> PasteBlocksIter { + PasteBlocksIter { + iters: self.collect(), + cache: vec![], + col_widths: None, + sep_width: sep_width, + } + } +} + +impl PasteBlocks for It where It: Iterator, It::Item: Iterator {} + +struct PasteBlocksIter +where StrIt: Iterator { + iters: Vec, + cache: Vec>, + col_widths: Option>, + sep_width: usize, +} + +impl Iterator for PasteBlocksIter +where StrIt: Iterator { + type Item = String; + + fn next(&mut self) -> Option { + self.cache.clear(); + + // `cache` is now the next line from each iterator. + self.cache.extend(self.iters.iter_mut().map(|it| it.next())); + + // If every line in `cache` is `None`, we have nothing further to do. + if self.cache.iter().all(|e| e.is_none()) { return None } + + // Get the column widths if we haven't already. + let col_widths = match self.col_widths { + Some(ref v) => &**v, + None => { + self.col_widths = Some(self.cache.iter() + .map(|ms| ms.as_ref().map(|s| s.len()).unwrap_or(0)) + .collect()); + &**self.col_widths.as_ref().unwrap() + } + }; + + // Fill in any `None`s with spaces. + let mut parts = col_widths.iter().cloned().zip(self.cache.iter_mut()) + .map(|(w,ms)| ms.take().unwrap_or_else(|| spaces(w).collect())); + + // Join them all together. + let first = parts.next().unwrap_or(String::new()); + let sep_width = self.sep_width; + Some(parts.fold(first, |mut accum, next| { + accum.extend(spaces(sep_width)); + accum.push_str(&next); + accum + })) + } +} + +fn test_paste_blocks() { + let row = dates_in_year(2013) + .by_month().map(|(_, days)| days) + .take(3) + .format_months() + .paste_blocks(1) + .join("\n"); + assert_eq!( + &*row, + " January February March \n\ + \x20 1 2 3 4 5 1 2 1 2\n\ + \x20 6 7 8 9 10 11 12 3 4 5 6 7 8 9 3 4 5 6 7 8 9\n\ + \x2013 14 15 16 17 18 19 10 11 12 13 14 15 16 10 11 12 13 14 15 16\n\ + \x2020 21 22 23 24 25 26 17 18 19 20 21 22 23 17 18 19 20 21 22 23\n\ + \x2027 28 29 30 31 24 25 26 27 28 24 25 26 27 28 29 30\n\ + \x20 31 " + ); +} + +/// +/// Produces an iterator that yields `n` elements at a time. +/// +trait Chunks: Iterator + Sized { + fn chunks(self, n: usize) -> ChunksIter { + assert!(n > 0); + ChunksIter { + it: self, + n: n, + } + } +} + +impl Chunks for It where It: Iterator {} + +struct ChunksIter +where It: Iterator { + it: It, + n: usize, +} + +// NOTE: `chunks` in Rust is more-or-less impossible without overhead of some kind. Aliasing rules mean you need to add dynamic borrow checking, and the design of `Iterator` means that you need to have the iterator's state kept in an allocation that is jointly owned by the iterator itself and the sub-iterator. As such, I've chosen to cop-out and just heap-allocate each chunk. + +impl Iterator for ChunksIter +where It: Iterator { + type Item = Vec; + + fn next(&mut self) -> Option> { + let first = match self.it.next() { + Some(e) => e, + None => return None + }; + + let mut result = Vec::with_capacity(self.n); + result.push(first); + + Some((&mut self.it).take(self.n-1) + .fold(result, |mut acc, next| { acc.push(next); acc })) + } +} + +fn test_chunks() { + let r = &[1, 2, 3, 4, 5, 6, 7]; + let c = r.iter().cloned().chunks(3).collect::>(); + assert_eq!(&*c, &[vec![1, 2, 3], vec![4, 5, 6], vec![7]]); +} + +/// +/// Formats a year. +/// +fn format_year(year: i32, months_per_row: usize) -> String { + const COL_SPACING: usize = 1; + + // Start by generating all dates for the given year. + dates_in_year(year) + + // Group them by month and throw away month number. + .by_month().map(|(_, days)| days) + + // Group the months into horizontal rows. + .chunks(months_per_row) + + // Format each row + .map(|r| r.into_iter() + // By formatting each month + .format_months() + + // Horizontally pasting each respective month's lines together. + .paste_blocks(COL_SPACING) + .join("\n") + ) + + // Insert a blank line between each row + .join("\n\n") +} + +fn test_format_year() { + const MONTHS_PER_ROW: usize = 3; + + macro_rules! assert_eq_cal { + ($lhs:expr, $rhs:expr) => { + if $lhs != $rhs { + println!("got:\n```\n{}\n```\n", $lhs.replace(" ", ".")); + println!("expected:\n```\n{}\n```", $rhs.replace(" ", ".")); + panic!("calendars didn't match!"); + } + } + } + + assert_eq_cal!(&format_year(1984, MONTHS_PER_ROW), "\ +\x20 January February March \n\ +\x20 1 2 3 4 5 6 7 1 2 3 4 1 2 3\n\ +\x20 8 9 10 11 12 13 14 5 6 7 8 9 10 11 4 5 6 7 8 9 10\n\ +\x2015 16 17 18 19 20 21 12 13 14 15 16 17 18 11 12 13 14 15 16 17\n\ +\x2022 23 24 25 26 27 28 19 20 21 22 23 24 25 18 19 20 21 22 23 24\n\ +\x2029 30 31 26 27 28 29 25 26 27 28 29 30 31\n\ +\n\ +\x20 April May June \n\ +\x20 1 2 3 4 5 6 7 1 2 3 4 5 1 2\n\ +\x20 8 9 10 11 12 13 14 6 7 8 9 10 11 12 3 4 5 6 7 8 9\n\ +\x2015 16 17 18 19 20 21 13 14 15 16 17 18 19 10 11 12 13 14 15 16\n\ +\x2022 23 24 25 26 27 28 20 21 22 23 24 25 26 17 18 19 20 21 22 23\n\ +\x2029 30 27 28 29 30 31 24 25 26 27 28 29 30\n\ +\n\ +\x20 July August September \n\ +\x20 1 2 3 4 5 6 7 1 2 3 4 1\n\ +\x20 8 9 10 11 12 13 14 5 6 7 8 9 10 11 2 3 4 5 6 7 8\n\ +\x2015 16 17 18 19 20 21 12 13 14 15 16 17 18 9 10 11 12 13 14 15\n\ +\x2022 23 24 25 26 27 28 19 20 21 22 23 24 25 16 17 18 19 20 21 22\n\ +\x2029 30 31 26 27 28 29 30 31 23 24 25 26 27 28 29\n\ +\x20 30 \n\ +\n\ +\x20 October November December \n\ +\x20 1 2 3 4 5 6 1 2 3 1\n\ +\x20 7 8 9 10 11 12 13 4 5 6 7 8 9 10 2 3 4 5 6 7 8\n\ +\x2014 15 16 17 18 19 20 11 12 13 14 15 16 17 9 10 11 12 13 14 15\n\ +\x2021 22 23 24 25 26 27 18 19 20 21 22 23 24 16 17 18 19 20 21 22\n\ +\x2028 29 30 31 25 26 27 28 29 30 23 24 25 26 27 28 29\n\ +\x20 30 31 "); + + assert_eq_cal!(&format_year(2015, MONTHS_PER_ROW), "\ +\x20 January February March \n\ +\x20 1 2 3 1 2 3 4 5 6 7 1 2 3 4 5 6 7\n\ +\x20 4 5 6 7 8 9 10 8 9 10 11 12 13 14 8 9 10 11 12 13 14\n\ +\x2011 12 13 14 15 16 17 15 16 17 18 19 20 21 15 16 17 18 19 20 21\n\ +\x2018 19 20 21 22 23 24 22 23 24 25 26 27 28 22 23 24 25 26 27 28\n\ +\x2025 26 27 28 29 30 31 29 30 31 \n\ +\n\ +\x20 April May June \n\ +\x20 1 2 3 4 1 2 1 2 3 4 5 6\n\ +\x20 5 6 7 8 9 10 11 3 4 5 6 7 8 9 7 8 9 10 11 12 13\n\ +\x2012 13 14 15 16 17 18 10 11 12 13 14 15 16 14 15 16 17 18 19 20\n\ +\x2019 20 21 22 23 24 25 17 18 19 20 21 22 23 21 22 23 24 25 26 27\n\ +\x2026 27 28 29 30 24 25 26 27 28 29 30 28 29 30 \n\ +\x20 31 \n\ +\n\ +\x20 July August September \n\ +\x20 1 2 3 4 1 1 2 3 4 5\n\ +\x20 5 6 7 8 9 10 11 2 3 4 5 6 7 8 6 7 8 9 10 11 12\n\ +\x2012 13 14 15 16 17 18 9 10 11 12 13 14 15 13 14 15 16 17 18 19\n\ +\x2019 20 21 22 23 24 25 16 17 18 19 20 21 22 20 21 22 23 24 25 26\n\ +\x2026 27 28 29 30 31 23 24 25 26 27 28 29 27 28 29 30 \n\ +\x20 30 31 \n\ +\n\ +\x20 October November December \n\ +\x20 1 2 3 1 2 3 4 5 6 7 1 2 3 4 5\n\ +\x20 4 5 6 7 8 9 10 8 9 10 11 12 13 14 6 7 8 9 10 11 12\n\ +\x2011 12 13 14 15 16 17 15 16 17 18 19 20 21 13 14 15 16 17 18 19\n\ +\x2018 19 20 21 22 23 24 22 23 24 25 26 27 28 20 21 22 23 24 25 26\n\ +\x2025 26 27 28 29 30 31 29 30 27 28 29 30 31 "); +} + +fn main() { + // Run tests. + test_spaces(); + test_dates_in_year(); + test_group_by(); + test_by_month(); + test_isoweekdate(); + test_by_week(); + test_format_weeks(); + test_month_title(); + test_format_month(); + test_paste_blocks(); + test_chunks(); + test_format_year(); +} diff --git a/src/test/run-pass/anon-types-example-st.rs b/src/test/run-pass/anon-types-example-st.rs new file mode 100644 index 0000000000000..8c88a9cd9a646 --- /dev/null +++ b/src/test/run-pass/anon-types-example-st.rs @@ -0,0 +1,39 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +struct State; +type Error = (); + +trait Bind<'a, F> { + type Output: 'a; + fn bind(self, f: F) -> Self::Output; +} + +impl<'a, T, U, A: 'a, B, F: FnMut(T) -> B + 'a> Bind<'a, F> for A +where A: FnMut(&mut State) -> Result, + B: FnMut(&mut State) -> Result +{ + type Output = impl FnMut(&mut State) -> Result + 'a; + fn bind(mut self, mut f: F) -> Self::Output { + move |state | { + let r = try!(self(state)); + f(r)(state) + } + } +} + +fn atom<'a, T: 'a>(x: T) -> impl FnMut(&mut State) -> Result + 'a { + let mut x = Some(x); + move |_| x.take().map_or(Err(()), Ok) +} + +fn main() { + assert_eq!(atom(5).bind(|x| atom(x > 4))(&mut State), Ok(true)); +}