typeck: record impl Trait
concrete resolutions.
This commit is contained in:
parent
1ef7ddfda3
commit
d92e594c38
10 changed files with 1224 additions and 96 deletions
|
@ -194,6 +194,14 @@ impl<'ast> Visitor<'ast> for NodeCollector<'ast> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn visit_ty(&mut self, ty: &'ast Ty) {
|
||||||
|
self.insert(ty.id, NodeTy(ty));
|
||||||
|
|
||||||
|
self.with_parent(ty.id, |this| {
|
||||||
|
intravisit::walk_ty(this, ty);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn visit_fn(&mut self, fk: intravisit::FnKind<'ast>, fd: &'ast FnDecl,
|
fn visit_fn(&mut self, fk: intravisit::FnKind<'ast>, fd: &'ast FnDecl,
|
||||||
b: &'ast Block, s: Span, id: NodeId) {
|
b: &'ast Block, s: Span, id: NodeId) {
|
||||||
assert_eq!(self.parent_node, id);
|
assert_eq!(self.parent_node, id);
|
||||||
|
|
|
@ -50,6 +50,7 @@ pub enum Node<'ast> {
|
||||||
NodeVariant(&'ast Variant),
|
NodeVariant(&'ast Variant),
|
||||||
NodeExpr(&'ast Expr),
|
NodeExpr(&'ast Expr),
|
||||||
NodeStmt(&'ast Stmt),
|
NodeStmt(&'ast Stmt),
|
||||||
|
NodeTy(&'ast Ty),
|
||||||
NodeLocal(&'ast Pat),
|
NodeLocal(&'ast Pat),
|
||||||
NodePat(&'ast Pat),
|
NodePat(&'ast Pat),
|
||||||
NodeBlock(&'ast Block),
|
NodeBlock(&'ast Block),
|
||||||
|
@ -76,6 +77,7 @@ pub enum MapEntry<'ast> {
|
||||||
EntryVariant(NodeId, &'ast Variant),
|
EntryVariant(NodeId, &'ast Variant),
|
||||||
EntryExpr(NodeId, &'ast Expr),
|
EntryExpr(NodeId, &'ast Expr),
|
||||||
EntryStmt(NodeId, &'ast Stmt),
|
EntryStmt(NodeId, &'ast Stmt),
|
||||||
|
EntryTy(NodeId, &'ast Ty),
|
||||||
EntryLocal(NodeId, &'ast Pat),
|
EntryLocal(NodeId, &'ast Pat),
|
||||||
EntryPat(NodeId, &'ast Pat),
|
EntryPat(NodeId, &'ast Pat),
|
||||||
EntryBlock(NodeId, &'ast Block),
|
EntryBlock(NodeId, &'ast Block),
|
||||||
|
@ -104,6 +106,7 @@ impl<'ast> MapEntry<'ast> {
|
||||||
NodeVariant(n) => EntryVariant(p, n),
|
NodeVariant(n) => EntryVariant(p, n),
|
||||||
NodeExpr(n) => EntryExpr(p, n),
|
NodeExpr(n) => EntryExpr(p, n),
|
||||||
NodeStmt(n) => EntryStmt(p, n),
|
NodeStmt(n) => EntryStmt(p, n),
|
||||||
|
NodeTy(n) => EntryTy(p, n),
|
||||||
NodeLocal(n) => EntryLocal(p, n),
|
NodeLocal(n) => EntryLocal(p, n),
|
||||||
NodePat(n) => EntryPat(p, n),
|
NodePat(n) => EntryPat(p, n),
|
||||||
NodeBlock(n) => EntryBlock(p, n),
|
NodeBlock(n) => EntryBlock(p, n),
|
||||||
|
@ -122,6 +125,7 @@ impl<'ast> MapEntry<'ast> {
|
||||||
EntryVariant(id, _) => id,
|
EntryVariant(id, _) => id,
|
||||||
EntryExpr(id, _) => id,
|
EntryExpr(id, _) => id,
|
||||||
EntryStmt(id, _) => id,
|
EntryStmt(id, _) => id,
|
||||||
|
EntryTy(id, _) => id,
|
||||||
EntryLocal(id, _) => id,
|
EntryLocal(id, _) => id,
|
||||||
EntryPat(id, _) => id,
|
EntryPat(id, _) => id,
|
||||||
EntryBlock(id, _) => id,
|
EntryBlock(id, _) => id,
|
||||||
|
@ -144,6 +148,7 @@ impl<'ast> MapEntry<'ast> {
|
||||||
EntryVariant(_, n) => NodeVariant(n),
|
EntryVariant(_, n) => NodeVariant(n),
|
||||||
EntryExpr(_, n) => NodeExpr(n),
|
EntryExpr(_, n) => NodeExpr(n),
|
||||||
EntryStmt(_, n) => NodeStmt(n),
|
EntryStmt(_, n) => NodeStmt(n),
|
||||||
|
EntryTy(_, n) => NodeTy(n),
|
||||||
EntryLocal(_, n) => NodeLocal(n),
|
EntryLocal(_, n) => NodeLocal(n),
|
||||||
EntryPat(_, n) => NodePat(n),
|
EntryPat(_, n) => NodePat(n),
|
||||||
EntryBlock(_, n) => NodeBlock(n),
|
EntryBlock(_, n) => NodeBlock(n),
|
||||||
|
@ -257,6 +262,7 @@ impl<'ast> Map<'ast> {
|
||||||
EntryVariant(p, _) |
|
EntryVariant(p, _) |
|
||||||
EntryExpr(p, _) |
|
EntryExpr(p, _) |
|
||||||
EntryStmt(p, _) |
|
EntryStmt(p, _) |
|
||||||
|
EntryTy(p, _) |
|
||||||
EntryLocal(p, _) |
|
EntryLocal(p, _) |
|
||||||
EntryPat(p, _) |
|
EntryPat(p, _) |
|
||||||
EntryBlock(p, _) |
|
EntryBlock(p, _) |
|
||||||
|
@ -297,6 +303,7 @@ impl<'ast> Map<'ast> {
|
||||||
EntryVariant(p, _) |
|
EntryVariant(p, _) |
|
||||||
EntryExpr(p, _) |
|
EntryExpr(p, _) |
|
||||||
EntryStmt(p, _) |
|
EntryStmt(p, _) |
|
||||||
|
EntryTy(p, _) |
|
||||||
EntryLocal(p, _) |
|
EntryLocal(p, _) |
|
||||||
EntryPat(p, _) |
|
EntryPat(p, _) |
|
||||||
EntryBlock(p, _) |
|
EntryBlock(p, _) |
|
||||||
|
@ -680,6 +687,7 @@ impl<'ast> Map<'ast> {
|
||||||
Some(NodeVariant(variant)) => variant.span,
|
Some(NodeVariant(variant)) => variant.span,
|
||||||
Some(NodeExpr(expr)) => expr.span,
|
Some(NodeExpr(expr)) => expr.span,
|
||||||
Some(NodeStmt(stmt)) => stmt.span,
|
Some(NodeStmt(stmt)) => stmt.span,
|
||||||
|
Some(NodeTy(ty)) => ty.span,
|
||||||
Some(NodeLocal(pat)) => pat.span,
|
Some(NodeLocal(pat)) => pat.span,
|
||||||
Some(NodePat(pat)) => pat.span,
|
Some(NodePat(pat)) => pat.span,
|
||||||
Some(NodeBlock(block)) => block.span,
|
Some(NodeBlock(block)) => block.span,
|
||||||
|
@ -971,6 +979,7 @@ impl<'a> NodePrinter for pprust::State<'a> {
|
||||||
NodeVariant(a) => self.print_variant(&a),
|
NodeVariant(a) => self.print_variant(&a),
|
||||||
NodeExpr(a) => self.print_expr(&a),
|
NodeExpr(a) => self.print_expr(&a),
|
||||||
NodeStmt(a) => self.print_stmt(&a),
|
NodeStmt(a) => self.print_stmt(&a),
|
||||||
|
NodeTy(a) => self.print_type(&a),
|
||||||
NodePat(a) => self.print_pat(&a),
|
NodePat(a) => self.print_pat(&a),
|
||||||
NodeBlock(a) => self.print_block(&a),
|
NodeBlock(a) => self.print_block(&a),
|
||||||
NodeLifetime(a) => self.print_lifetime(&a),
|
NodeLifetime(a) => self.print_lifetime(&a),
|
||||||
|
@ -1059,6 +1068,9 @@ fn node_id_to_string(map: &Map, id: NodeId, include_id: bool) -> String {
|
||||||
Some(NodeStmt(ref stmt)) => {
|
Some(NodeStmt(ref stmt)) => {
|
||||||
format!("stmt {}{}", pprust::stmt_to_string(&stmt), id_str)
|
format!("stmt {}{}", pprust::stmt_to_string(&stmt), id_str)
|
||||||
}
|
}
|
||||||
|
Some(NodeTy(ref ty)) => {
|
||||||
|
format!("type {}{}", pprust::ty_to_string(&ty), id_str)
|
||||||
|
}
|
||||||
Some(NodeLocal(ref pat)) => {
|
Some(NodeLocal(ref pat)) => {
|
||||||
format!("local {}{}", pprust::pat_to_string(&pat), id_str)
|
format!("local {}{}", pprust::pat_to_string(&pat), id_str)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1390,6 +1390,20 @@ impl<'a, 'b, 'c, 'tcx> Visitor<'tcx> for EncodeVisitor<'a, 'b, 'c, 'tcx> {
|
||||||
intravisit::walk_foreign_item(self, ni);
|
intravisit::walk_foreign_item(self, ni);
|
||||||
encode_info_for_foreign_item(self.ecx, self.rbml_w_for_visit_item, ni, self.index);
|
encode_info_for_foreign_item(self.ecx, self.rbml_w_for_visit_item, ni, self.index);
|
||||||
}
|
}
|
||||||
|
fn visit_ty(&mut self, ty: &'tcx hir::Ty) {
|
||||||
|
intravisit::walk_ty(self, ty);
|
||||||
|
|
||||||
|
if let hir::TyImplTrait(_) = ty.node {
|
||||||
|
let rbml_w = &mut *self.rbml_w_for_visit_item;
|
||||||
|
let def_id = self.ecx.tcx.map.local_def_id(ty.id);
|
||||||
|
let _task = self.index.record(def_id, rbml_w);
|
||||||
|
rbml_w.start_tag(tag_items_data_item);
|
||||||
|
encode_def_id_and_key(self.ecx, rbml_w, def_id);
|
||||||
|
encode_family(rbml_w, 'y');
|
||||||
|
encode_bounds_and_type_for_item(rbml_w, self.ecx, self.index, ty.id);
|
||||||
|
rbml_w.end_tag();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn encode_info_for_items<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
|
fn encode_info_for_items<'a, 'tcx>(ecx: &EncodeContext<'a, 'tcx>,
|
||||||
|
|
|
@ -1763,25 +1763,28 @@ impl<'o, 'gcx: 'tcx, 'tcx> AstConv<'gcx, 'tcx>+'o {
|
||||||
|
|
||||||
// Create the anonymized type.
|
// Create the anonymized type.
|
||||||
let def_id = tcx.map.local_def_id(ast_ty.id);
|
let def_id = tcx.map.local_def_id(ast_ty.id);
|
||||||
let substs = if let Some(anon_scope) = rscope.anon_type_scope() {
|
if let Some(anon_scope) = rscope.anon_type_scope() {
|
||||||
anon_scope.fresh_substs(tcx)
|
let substs = anon_scope.fresh_substs(tcx);
|
||||||
|
let ty = tcx.mk_anon(tcx.map.local_def_id(ast_ty.id), substs);
|
||||||
|
|
||||||
|
// Collect the bounds, i.e. the `A+B+'c` in `impl A+B+'c`.
|
||||||
|
let bounds = compute_bounds(self, ty, bounds,
|
||||||
|
SizedByDefault::Yes,
|
||||||
|
Some(anon_scope),
|
||||||
|
ast_ty.span);
|
||||||
|
let predicates = bounds.predicates(tcx, ty);
|
||||||
|
let predicates = tcx.lift_to_global(&predicates).unwrap();
|
||||||
|
tcx.predicates.borrow_mut().insert(def_id, ty::GenericPredicates {
|
||||||
|
predicates: VecPerParamSpace::new(vec![], vec![], predicates)
|
||||||
|
});
|
||||||
|
|
||||||
|
ty
|
||||||
} else {
|
} else {
|
||||||
span_err!(tcx.sess, ast_ty.span, E0562,
|
span_err!(tcx.sess, ast_ty.span, E0562,
|
||||||
"`impl Trait` not allowed outside of function \
|
"`impl Trait` not allowed outside of function \
|
||||||
and inherent method return types");
|
and inherent method return types");
|
||||||
tcx.mk_substs(Substs::empty())
|
tcx.types.err
|
||||||
};
|
}
|
||||||
let ty = tcx.mk_anon(tcx.map.local_def_id(ast_ty.id), substs);
|
|
||||||
|
|
||||||
// Collect the bounds, i.e. the `A+B+'c` in `impl A+B+'c`.
|
|
||||||
let bounds = compute_bounds(self, ty, bounds, SizedByDefault::Yes, ast_ty.span);
|
|
||||||
let predicates = tcx.lift_to_global(&bounds.predicates(tcx, ty)).unwrap();
|
|
||||||
let predicates = ty::GenericPredicates {
|
|
||||||
predicates: VecPerParamSpace::new(vec![], vec![], predicates)
|
|
||||||
};
|
|
||||||
tcx.predicates.borrow_mut().insert(def_id, predicates);
|
|
||||||
|
|
||||||
ty
|
|
||||||
}
|
}
|
||||||
hir::TyPath(ref maybe_qself, ref path) => {
|
hir::TyPath(ref maybe_qself, ref path) => {
|
||||||
debug!("ast_ty_to_ty: maybe_qself={:?} path={:?}", maybe_qself, path);
|
debug!("ast_ty_to_ty: maybe_qself={:?} path={:?}", maybe_qself, path);
|
||||||
|
|
|
@ -96,7 +96,7 @@ use rustc::ty::{LvaluePreference, NoPreference, PreferMutLvalue};
|
||||||
use rustc::ty::{self, ToPolyTraitRef, Ty, TyCtxt, Visibility};
|
use rustc::ty::{self, ToPolyTraitRef, Ty, TyCtxt, Visibility};
|
||||||
use rustc::ty::{MethodCall, MethodCallee};
|
use rustc::ty::{MethodCall, MethodCallee};
|
||||||
use rustc::ty::adjustment;
|
use rustc::ty::adjustment;
|
||||||
use rustc::ty::fold::TypeFoldable;
|
use rustc::ty::fold::{BottomUpFolder, TypeFoldable};
|
||||||
use rustc::ty::util::{Representability, IntTypeExt};
|
use rustc::ty::util::{Representability, IntTypeExt};
|
||||||
use require_c_abi_if_variadic;
|
use require_c_abi_if_variadic;
|
||||||
use rscope::{ElisionFailureInfo, RegionScope};
|
use rscope::{ElisionFailureInfo, RegionScope};
|
||||||
|
@ -172,6 +172,12 @@ pub struct Inherited<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
|
||||||
deferred_call_resolutions: RefCell<DefIdMap<Vec<DeferredCallResolutionHandler<'gcx, 'tcx>>>>,
|
deferred_call_resolutions: RefCell<DefIdMap<Vec<DeferredCallResolutionHandler<'gcx, 'tcx>>>>,
|
||||||
|
|
||||||
deferred_cast_checks: RefCell<Vec<cast::CastCheck<'tcx>>>,
|
deferred_cast_checks: RefCell<Vec<cast::CastCheck<'tcx>>>,
|
||||||
|
|
||||||
|
// 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<DefIdMap<Ty<'tcx>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> Deref for Inherited<'a, 'gcx, 'tcx> {
|
impl<'a, 'gcx, 'tcx> Deref for Inherited<'a, 'gcx, 'tcx> {
|
||||||
|
@ -408,6 +414,7 @@ impl<'a, 'gcx, 'tcx> InheritedBuilder<'a, 'gcx, 'tcx> {
|
||||||
locals: RefCell::new(NodeMap()),
|
locals: RefCell::new(NodeMap()),
|
||||||
deferred_call_resolutions: RefCell::new(DefIdMap()),
|
deferred_call_resolutions: RefCell::new(DefIdMap()),
|
||||||
deferred_cast_checks: RefCell::new(Vec::new()),
|
deferred_cast_checks: RefCell::new(Vec::new()),
|
||||||
|
anon_types: RefCell::new(DefIdMap()),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -631,32 +638,29 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
|
||||||
body: &'gcx hir::Block)
|
body: &'gcx hir::Block)
|
||||||
-> FnCtxt<'a, 'gcx, 'tcx>
|
-> FnCtxt<'a, 'gcx, 'tcx>
|
||||||
{
|
{
|
||||||
let arg_tys = &fn_sig.inputs;
|
let mut fn_sig = fn_sig.clone();
|
||||||
let ret_ty = fn_sig.output;
|
|
||||||
|
|
||||||
debug!("check_fn(arg_tys={:?}, ret_ty={:?}, fn_id={})",
|
debug!("check_fn(sig={:?}, fn_id={})", fn_sig, fn_id);
|
||||||
arg_tys,
|
|
||||||
ret_ty,
|
|
||||||
fn_id);
|
|
||||||
|
|
||||||
// Create the function context. This is either derived from scratch or,
|
// Create the function context. This is either derived from scratch or,
|
||||||
// in the case of function expressions, based on the outer context.
|
// in the case of function expressions, based on the outer context.
|
||||||
let fcx = FnCtxt::new(inherited, ret_ty, body.id);
|
let mut fcx = FnCtxt::new(inherited, fn_sig.output, body.id);
|
||||||
*fcx.ps.borrow_mut() = UnsafetyState::function(unsafety, unsafety_id);
|
*fcx.ps.borrow_mut() = UnsafetyState::function(unsafety, unsafety_id);
|
||||||
|
|
||||||
if let ty::FnConverging(ret_ty) = ret_ty {
|
fn_sig.output = match fcx.ret_ty {
|
||||||
fcx.require_type_is_sized(ret_ty, decl.output.span(), traits::ReturnType);
|
ty::FnConverging(orig_ret_ty) => {
|
||||||
|
fcx.require_type_is_sized(orig_ret_ty, decl.output.span(), traits::ReturnType);
|
||||||
|
ty::FnConverging(fcx.instantiate_anon_types(&orig_ret_ty))
|
||||||
}
|
}
|
||||||
|
ty::FnDiverging => ty::FnDiverging
|
||||||
debug!("fn-sig-map: fn_id={} fn_sig={:?}", fn_id, fn_sig);
|
};
|
||||||
|
fcx.ret_ty = fn_sig.output;
|
||||||
inherited.tables.borrow_mut().liberated_fn_sigs.insert(fn_id, fn_sig.clone());
|
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut visit = GatherLocalsVisitor { fcx: &fcx, };
|
let mut visit = GatherLocalsVisitor { fcx: &fcx, };
|
||||||
|
|
||||||
// Add formal parameters.
|
// Add formal parameters.
|
||||||
for (arg_ty, input) in arg_tys.iter().zip(&decl.inputs) {
|
for (arg_ty, input) in fn_sig.inputs.iter().zip(&decl.inputs) {
|
||||||
// The type of the argument must be well-formed.
|
// The type of the argument must be well-formed.
|
||||||
//
|
//
|
||||||
// NB -- this is now checked in wfcheck, but that
|
// NB -- this is now checked in wfcheck, but that
|
||||||
|
@ -672,21 +676,20 @@ fn check_fn<'a, 'gcx, 'tcx>(inherited: &'a Inherited<'a, 'gcx, 'tcx>,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check the pattern.
|
// Check the pattern.
|
||||||
fcx.check_pat(&input.pat, *arg_ty);
|
fcx.check_pat(&input.pat, arg_ty);
|
||||||
|
fcx.write_ty(input.id, arg_ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
visit.visit_block(body);
|
visit.visit_block(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
fcx.check_block_with_expected(body, match ret_ty {
|
inherited.tables.borrow_mut().liberated_fn_sigs.insert(fn_id, fn_sig);
|
||||||
|
|
||||||
|
fcx.check_block_with_expected(body, match fcx.ret_ty {
|
||||||
ty::FnConverging(result_type) => ExpectHasType(result_type),
|
ty::FnConverging(result_type) => ExpectHasType(result_type),
|
||||||
ty::FnDiverging => NoExpectation
|
ty::FnDiverging => NoExpectation
|
||||||
});
|
});
|
||||||
|
|
||||||
for (input, arg) in decl.inputs.iter().zip(arg_tys) {
|
|
||||||
fcx.write_ty(input.id, arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
fcx
|
fcx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1623,6 +1626,41 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Replace all anonymized types with fresh inference variables
|
||||||
|
/// and record them for writeback.
|
||||||
|
fn instantiate_anon_types<T: TypeFoldable<'tcx>>(&self, value: &T) -> T {
|
||||||
|
value.fold_with(&mut BottomUpFolder { tcx: self.tcx, fldop: |ty| {
|
||||||
|
if let ty::TyAnon(def_id, substs) = ty.sty {
|
||||||
|
// Use the same type variable if the exact same TyAnon appears more
|
||||||
|
// than once in the return type (e.g. if it's pased to a type alias).
|
||||||
|
if let Some(ty_var) = self.anon_types.borrow().get(&def_id) {
|
||||||
|
return ty_var;
|
||||||
|
}
|
||||||
|
let ty_var = self.next_ty_var();
|
||||||
|
self.anon_types.borrow_mut().insert(def_id, ty_var);
|
||||||
|
|
||||||
|
let item_predicates = self.tcx.lookup_predicates(def_id);
|
||||||
|
let bounds = item_predicates.instantiate(self.tcx, substs);
|
||||||
|
|
||||||
|
let span = self.tcx.map.def_id_span(def_id, codemap::DUMMY_SP);
|
||||||
|
for predicate in bounds.predicates {
|
||||||
|
// Change the predicate to refer to the type variable,
|
||||||
|
// which will be the concrete type, instead of the TyAnon.
|
||||||
|
// This also instantiates nested `impl Trait`.
|
||||||
|
let predicate = self.instantiate_anon_types(&predicate);
|
||||||
|
|
||||||
|
// Require that the predicate holds for the concrete type.
|
||||||
|
let cause = traits::ObligationCause::new(span, self.body_id,
|
||||||
|
traits::ReturnType);
|
||||||
|
self.register_predicate(traits::Obligation::new(cause, predicate));
|
||||||
|
}
|
||||||
|
|
||||||
|
ty_var
|
||||||
|
} else {
|
||||||
|
ty
|
||||||
|
}
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
|
||||||
fn normalize_associated_types_in<T>(&self, span: Span, value: &T) -> T
|
fn normalize_associated_types_in<T>(&self, span: Span, value: &T) -> T
|
||||||
where T : TypeFoldable<'tcx>
|
where T : TypeFoldable<'tcx>
|
||||||
|
|
|
@ -18,7 +18,9 @@ use hir::def_id::DefId;
|
||||||
use rustc::ty::{self, Ty, TyCtxt, MethodCall, MethodCallee};
|
use rustc::ty::{self, Ty, TyCtxt, MethodCall, MethodCallee};
|
||||||
use rustc::ty::adjustment;
|
use rustc::ty::adjustment;
|
||||||
use rustc::ty::fold::{TypeFolder,TypeFoldable};
|
use rustc::ty::fold::{TypeFolder,TypeFoldable};
|
||||||
|
use rustc::ty::subst::ParamSpace;
|
||||||
use rustc::infer::{InferCtxt, FixupError};
|
use rustc::infer::{InferCtxt, FixupError};
|
||||||
|
use rustc::util::nodemap::DefIdMap;
|
||||||
use write_substs_to_tcx;
|
use write_substs_to_tcx;
|
||||||
use write_ty_to_tcx;
|
use write_ty_to_tcx;
|
||||||
|
|
||||||
|
@ -62,6 +64,7 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||||
wbcx.visit_closures();
|
wbcx.visit_closures();
|
||||||
wbcx.visit_liberated_fn_sigs();
|
wbcx.visit_liberated_fn_sigs();
|
||||||
wbcx.visit_fru_field_types();
|
wbcx.visit_fru_field_types();
|
||||||
|
wbcx.visit_anon_types();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,11 +78,48 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
|
||||||
|
|
||||||
struct WritebackCx<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> {
|
struct WritebackCx<'cx, 'gcx: 'cx+'tcx, 'tcx: 'cx> {
|
||||||
fcx: &'cx FnCtxt<'cx, 'gcx, 'tcx>,
|
fcx: &'cx FnCtxt<'cx, 'gcx, 'tcx>,
|
||||||
|
|
||||||
|
// Mapping from free regions of the function to the
|
||||||
|
// early-bound versions of them, visible from the
|
||||||
|
// outside of the function. This is needed by, and
|
||||||
|
// only populated if there are any `impl Trait`.
|
||||||
|
free_to_bound_regions: DefIdMap<ty::Region>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
|
impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
|
||||||
fn new(fcx: &'cx FnCtxt<'cx, 'gcx, 'tcx>) -> WritebackCx<'cx, 'gcx, 'tcx> {
|
fn new(fcx: &'cx FnCtxt<'cx, 'gcx, 'tcx>) -> WritebackCx<'cx, 'gcx, 'tcx> {
|
||||||
WritebackCx { fcx: fcx }
|
let mut wbcx = WritebackCx {
|
||||||
|
fcx: fcx,
|
||||||
|
free_to_bound_regions: DefIdMap()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only build the reverse mapping if `impl Trait` is used.
|
||||||
|
if fcx.anon_types.borrow().is_empty() {
|
||||||
|
return wbcx;
|
||||||
|
}
|
||||||
|
|
||||||
|
let free_substs = fcx.parameter_environment.free_substs;
|
||||||
|
for &space in &ParamSpace::all() {
|
||||||
|
for (i, r) in free_substs.regions.get_slice(space).iter().enumerate() {
|
||||||
|
match *r {
|
||||||
|
ty::ReFree(ty::FreeRegion {
|
||||||
|
bound_region: ty::BoundRegion::BrNamed(def_id, name, _), ..
|
||||||
|
}) => {
|
||||||
|
let bound_region = ty::ReEarlyBound(ty::EarlyBoundRegion {
|
||||||
|
space: space,
|
||||||
|
index: i as u32,
|
||||||
|
name: name,
|
||||||
|
});
|
||||||
|
wbcx.free_to_bound_regions.insert(def_id, bound_region);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
bug!("{:?} is not a free region for an early-bound lifetime", r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wbcx
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tcx(&self) -> TyCtxt<'cx, 'gcx, 'tcx> {
|
fn tcx(&self) -> TyCtxt<'cx, 'gcx, 'tcx> {
|
||||||
|
@ -255,6 +295,58 @@ impl<'cx, 'gcx, 'tcx> WritebackCx<'cx, 'gcx, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn visit_anon_types(&self) {
|
||||||
|
if self.fcx.writeback_errors.get() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let gcx = self.tcx().global_tcx();
|
||||||
|
for (&def_id, &concrete_ty) in self.fcx.anon_types.borrow().iter() {
|
||||||
|
let reason = ResolvingAnonTy(def_id);
|
||||||
|
let inside_ty = self.resolve(&concrete_ty, reason);
|
||||||
|
|
||||||
|
// Convert the type from the function into a type valid outside
|
||||||
|
// the function, by replacing free regions with early-bound ones.
|
||||||
|
let outside_ty = gcx.fold_regions(&inside_ty, &mut false, |r, _| {
|
||||||
|
match r {
|
||||||
|
// 'static is valid everywhere.
|
||||||
|
ty::ReStatic => ty::ReStatic,
|
||||||
|
|
||||||
|
// Free regions that come from early-bound regions are valid.
|
||||||
|
ty::ReFree(ty::FreeRegion {
|
||||||
|
bound_region: ty::BoundRegion::BrNamed(def_id, _, _), ..
|
||||||
|
}) if self.free_to_bound_regions.contains_key(&def_id) => {
|
||||||
|
self.free_to_bound_regions[&def_id]
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::ReFree(_) |
|
||||||
|
ty::ReEarlyBound(_) |
|
||||||
|
ty::ReLateBound(..) |
|
||||||
|
ty::ReScope(_) |
|
||||||
|
ty::ReSkolemized(..) => {
|
||||||
|
let span = reason.span(self.tcx());
|
||||||
|
span_err!(self.tcx().sess, span, E0564,
|
||||||
|
"only named lifetimes are allowed in `impl Trait`, \
|
||||||
|
but `{}` was found in the type `{}`", r, inside_ty);
|
||||||
|
ty::ReStatic
|
||||||
|
}
|
||||||
|
|
||||||
|
ty::ReVar(_) |
|
||||||
|
ty::ReEmpty |
|
||||||
|
ty::ReErased => {
|
||||||
|
let span = reason.span(self.tcx());
|
||||||
|
span_bug!(span, "invalid region in impl Trait: {:?}", r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
gcx.tcache.borrow_mut().insert(def_id, ty::TypeScheme {
|
||||||
|
ty: outside_ty,
|
||||||
|
generics: ty::Generics::empty()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn visit_node_id(&self, reason: ResolveReason, id: ast::NodeId) {
|
fn visit_node_id(&self, reason: ResolveReason, id: ast::NodeId) {
|
||||||
// Resolve any borrowings for the node with id `id`
|
// Resolve any borrowings for the node with id `id`
|
||||||
self.visit_adjustments(reason, id);
|
self.visit_adjustments(reason, id);
|
||||||
|
@ -377,7 +469,8 @@ enum ResolveReason {
|
||||||
ResolvingUpvar(ty::UpvarId),
|
ResolvingUpvar(ty::UpvarId),
|
||||||
ResolvingClosure(DefId),
|
ResolvingClosure(DefId),
|
||||||
ResolvingFnSig(ast::NodeId),
|
ResolvingFnSig(ast::NodeId),
|
||||||
ResolvingFieldTypes(ast::NodeId)
|
ResolvingFieldTypes(ast::NodeId),
|
||||||
|
ResolvingAnonTy(DefId),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'gcx, 'tcx> ResolveReason {
|
impl<'a, 'gcx, 'tcx> ResolveReason {
|
||||||
|
@ -395,12 +488,9 @@ impl<'a, 'gcx, 'tcx> ResolveReason {
|
||||||
ResolvingFieldTypes(id) => {
|
ResolvingFieldTypes(id) => {
|
||||||
tcx.map.span(id)
|
tcx.map.span(id)
|
||||||
}
|
}
|
||||||
ResolvingClosure(did) => {
|
ResolvingClosure(did) |
|
||||||
if let Some(node_id) = tcx.map.as_local_node_id(did) {
|
ResolvingAnonTy(did) => {
|
||||||
tcx.expr_span(node_id)
|
tcx.map.def_id_span(did, DUMMY_SP)
|
||||||
} else {
|
|
||||||
DUMMY_SP
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -483,6 +573,12 @@ impl<'cx, 'gcx, 'tcx> Resolver<'cx, 'gcx, 'tcx> {
|
||||||
span,
|
span,
|
||||||
&format!("cannot resolve some aspect of data for {:?}", id));
|
&format!("cannot resolve some aspect of data for {:?}", id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ResolvingAnonTy(_) => {
|
||||||
|
let span = self.reason.span(self.tcx);
|
||||||
|
span_err!(self.tcx.sess, span, E0563,
|
||||||
|
"cannot determine a type for this `impl Trait`: {}", e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1197,6 +1197,7 @@ fn ensure_super_predicates_step(ccx: &CrateCtxt,
|
||||||
self_param_ty,
|
self_param_ty,
|
||||||
bounds,
|
bounds,
|
||||||
SizedByDefault::No,
|
SizedByDefault::No,
|
||||||
|
None,
|
||||||
item.span);
|
item.span);
|
||||||
|
|
||||||
let superbounds1 = superbounds1.predicates(tcx, self_param_ty);
|
let superbounds1 = superbounds1.predicates(tcx, self_param_ty);
|
||||||
|
@ -1407,6 +1408,7 @@ fn convert_trait_predicates<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>, it: &hir::Item)
|
||||||
assoc_ty,
|
assoc_ty,
|
||||||
bounds,
|
bounds,
|
||||||
SizedByDefault::Yes,
|
SizedByDefault::Yes,
|
||||||
|
None,
|
||||||
trait_item.span);
|
trait_item.span);
|
||||||
|
|
||||||
bounds.predicates(ccx.tcx, assoc_ty).into_iter()
|
bounds.predicates(ccx.tcx, assoc_ty).into_iter()
|
||||||
|
@ -1780,6 +1782,7 @@ fn ty_generic_predicates<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
|
||||||
param_ty,
|
param_ty,
|
||||||
¶m.bounds,
|
¶m.bounds,
|
||||||
SizedByDefault::Yes,
|
SizedByDefault::Yes,
|
||||||
|
None,
|
||||||
param.span);
|
param.span);
|
||||||
let predicates = bounds.predicates(ccx.tcx, param_ty);
|
let predicates = bounds.predicates(ccx.tcx, param_ty);
|
||||||
result.predicates.extend(space, predicates.into_iter());
|
result.predicates.extend(space, predicates.into_iter());
|
||||||
|
@ -2052,25 +2055,43 @@ pub fn compute_bounds<'gcx: 'tcx, 'tcx>(astconv: &AstConv<'gcx, 'tcx>,
|
||||||
param_ty: ty::Ty<'tcx>,
|
param_ty: ty::Ty<'tcx>,
|
||||||
ast_bounds: &[hir::TyParamBound],
|
ast_bounds: &[hir::TyParamBound],
|
||||||
sized_by_default: SizedByDefault,
|
sized_by_default: SizedByDefault,
|
||||||
|
anon_scope: Option<AnonTypeScope>,
|
||||||
span: Span)
|
span: Span)
|
||||||
-> Bounds<'tcx>
|
-> Bounds<'tcx>
|
||||||
{
|
{
|
||||||
let mut bounds =
|
let tcx = astconv.tcx();
|
||||||
conv_param_bounds(astconv,
|
let PartitionedBounds {
|
||||||
span,
|
mut builtin_bounds,
|
||||||
param_ty,
|
trait_bounds,
|
||||||
ast_bounds);
|
region_bounds
|
||||||
|
} = partition_bounds(tcx, span, &ast_bounds);
|
||||||
|
|
||||||
if let SizedByDefault::Yes = sized_by_default {
|
if let SizedByDefault::Yes = sized_by_default {
|
||||||
add_unsized_bound(astconv,
|
add_unsized_bound(astconv, &mut builtin_bounds, ast_bounds, span);
|
||||||
&mut bounds.builtin_bounds,
|
|
||||||
ast_bounds,
|
|
||||||
span);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bounds.trait_bounds.sort_by(|a,b| a.def_id().cmp(&b.def_id()));
|
let mut projection_bounds = vec![];
|
||||||
|
|
||||||
bounds
|
let rscope = MaybeWithAnonTypes::new(ExplicitRscope, anon_scope);
|
||||||
|
let mut trait_bounds: Vec<_> = trait_bounds.iter().map(|&bound| {
|
||||||
|
astconv.instantiate_poly_trait_ref(&rscope,
|
||||||
|
bound,
|
||||||
|
Some(param_ty),
|
||||||
|
&mut projection_bounds)
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
let region_bounds = region_bounds.into_iter().map(|r| {
|
||||||
|
ast_region_to_region(tcx, r)
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
trait_bounds.sort_by(|a,b| a.def_id().cmp(&b.def_id()));
|
||||||
|
|
||||||
|
Bounds {
|
||||||
|
region_bounds: region_bounds,
|
||||||
|
builtin_bounds: builtin_bounds,
|
||||||
|
trait_bounds: trait_bounds,
|
||||||
|
projection_bounds: projection_bounds,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a specific TyParamBound from the AST into a set of
|
/// Converts a specific TyParamBound from the AST into a set of
|
||||||
|
@ -2116,42 +2137,6 @@ fn conv_poly_trait_ref<'gcx: 'tcx, 'tcx>(astconv: &AstConv<'gcx, 'tcx>,
|
||||||
projections)
|
projections)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn conv_param_bounds<'gcx: 'tcx, 'tcx>(astconv: &AstConv<'gcx, 'tcx>,
|
|
||||||
span: Span,
|
|
||||||
param_ty: ty::Ty<'tcx>,
|
|
||||||
ast_bounds: &[hir::TyParamBound])
|
|
||||||
-> Bounds<'tcx>
|
|
||||||
{
|
|
||||||
let tcx = astconv.tcx();
|
|
||||||
let PartitionedBounds {
|
|
||||||
builtin_bounds,
|
|
||||||
trait_bounds,
|
|
||||||
region_bounds
|
|
||||||
} = partition_bounds(tcx, span, &ast_bounds);
|
|
||||||
|
|
||||||
let mut projection_bounds = Vec::new();
|
|
||||||
|
|
||||||
let trait_bounds: Vec<ty::PolyTraitRef> =
|
|
||||||
trait_bounds.iter()
|
|
||||||
.map(|bound| conv_poly_trait_ref(astconv,
|
|
||||||
param_ty,
|
|
||||||
*bound,
|
|
||||||
&mut projection_bounds))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let region_bounds: Vec<ty::Region> =
|
|
||||||
region_bounds.into_iter()
|
|
||||||
.map(|r| ast_region_to_region(tcx, r))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Bounds {
|
|
||||||
region_bounds: region_bounds,
|
|
||||||
builtin_bounds: builtin_bounds,
|
|
||||||
trait_bounds: trait_bounds,
|
|
||||||
projection_bounds: projection_bounds,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compute_type_scheme_of_foreign_fn_decl<'a, 'tcx>(
|
fn compute_type_scheme_of_foreign_fn_decl<'a, 'tcx>(
|
||||||
ccx: &CrateCtxt<'a, 'tcx>,
|
ccx: &CrateCtxt<'a, 'tcx>,
|
||||||
id: DefId,
|
id: DefId,
|
||||||
|
|
|
@ -4086,4 +4086,7 @@ register_diagnostics! {
|
||||||
E0533, // `{}` does not name a unit variant, unit struct or a constant
|
E0533, // `{}` does not name a unit variant, unit struct or a constant
|
||||||
E0562, // `impl Trait` not allowed outside of function
|
E0562, // `impl Trait` not allowed outside of function
|
||||||
// and inherent method return types
|
// and inherent method return types
|
||||||
|
E0563, // cannot determine a type for this `impl Trait`: {}
|
||||||
|
E0564, // only named lifetimes are allowed in `impl Trait`,
|
||||||
|
// but `{}` was found in the type `{}`
|
||||||
}
|
}
|
||||||
|
|
929
src/test/run-pass/impl-trait/example-calendar.rs
Normal file
929
src/test/run-pass/impl-trait/example-calendar.rs
Normal file
|
@ -0,0 +1,929 @@
|
||||||
|
// Copyright 2016 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 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
#![feature(conservative_impl_trait, fn_traits, step_trait, unboxed_closures)]
|
||||||
|
|
||||||
|
//! Derived from: <https://raw.githubusercontent.com/quickfur/dcal/master/dcal.d>.
|
||||||
|
//!
|
||||||
|
//! Originally converted to Rust by [Daniel Keep](https://github.com/DanielKeep).
|
||||||
|
|
||||||
|
use std::fmt::Write;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
/// 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<'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<Self> {
|
||||||
|
Some(self + by)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn steps_between(_: &Self, _: &Self, _: &Self) -> Option<usize> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn steps_between_by_one(_: &Self, _: &Self) -> Option<usize> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_negative(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn replace_one(&mut self) -> Self {
|
||||||
|
mem::replace(self, NaiveDate(0, 0, 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn replace_zero(&mut self) -> Self {
|
||||||
|
mem::replace(self, NaiveDate(0, 0, 0))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_one(&self) -> Self {
|
||||||
|
self.succ()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sub_one(&self) -> Self {
|
||||||
|
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.
|
||||||
|
// HACK(eddyb) Only needed because closures can't implement Copy.
|
||||||
|
struct Fn0<F>(std::marker::PhantomData<F>);
|
||||||
|
|
||||||
|
impl<F> Copy for Fn0<F> {}
|
||||||
|
impl<F> Clone for Fn0<F> {
|
||||||
|
fn clone(&self) -> Self { *self }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FnOnce<A>, A> FnOnce<A> for Fn0<F> {
|
||||||
|
type Output = F::Output;
|
||||||
|
|
||||||
|
extern "rust-call" fn call_once(self, args: A) -> Self::Output {
|
||||||
|
let f = unsafe { std::mem::uninitialized::<F>() };
|
||||||
|
f.call_once(args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FnMut<A>, A> FnMut<A> for Fn0<F> {
|
||||||
|
extern "rust-call" fn call_mut(&mut self, args: A) -> Self::Output {
|
||||||
|
let mut f = unsafe { std::mem::uninitialized::<F>() };
|
||||||
|
f.call_mut(args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait AsFn0<A>: Sized {
|
||||||
|
fn copyable(self) -> Fn0<Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<F: FnMut<A>, A> AsFn0<A> for F {
|
||||||
|
fn copyable(self) -> Fn0<Self> {
|
||||||
|
assert_eq!(std::mem::size_of::<F>(), 0);
|
||||||
|
Fn0(std::marker::PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// GroupBy implementation.
|
||||||
|
struct GroupBy<It: Iterator, F> {
|
||||||
|
it: std::iter::Peekable<It>,
|
||||||
|
f: F,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<It, F> Clone for GroupBy<It, F>
|
||||||
|
where It: Iterator + Clone, It::Item: Clone, F: Clone {
|
||||||
|
fn clone(&self) -> GroupBy<It, F> {
|
||||||
|
GroupBy {
|
||||||
|
it: self.it.clone(),
|
||||||
|
f: self.f.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, G, It: 'a, F: 'a> Iterator for GroupBy<It, F>
|
||||||
|
where It: Iterator + Clone,
|
||||||
|
It::Item: Clone,
|
||||||
|
F: Clone + FnMut(&It::Item) -> G,
|
||||||
|
G: Eq + Clone
|
||||||
|
{
|
||||||
|
type Item = (G, InGroup<std::iter::Peekable<It>, F, G>);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
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, F, G> {
|
||||||
|
it: It,
|
||||||
|
f: F,
|
||||||
|
g: G
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<It: Iterator, F: FnMut(&It::Item) -> G, G: Eq> Iterator for InGroup<It, F, G> {
|
||||||
|
type Item = It::Item;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<It::Item> {
|
||||||
|
self.it.next().and_then(|x| {
|
||||||
|
if (self.f)(&x) == self.g { Some(x) } else { None }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait IteratorExt: Iterator + Sized {
|
||||||
|
fn group_by<G, F>(self, f: F) -> GroupBy<Self, Fn0<F>>
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// HACK(eddyb) Only needed because `impl Trait` can't be
|
||||||
|
// used with trait methods: `.foo()` becomes `.__(foo)`.
|
||||||
|
fn __<F, R>(self, f: F) -> R
|
||||||
|
where F: FnOnce(Self) -> R {
|
||||||
|
f(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<It> IteratorExt for It where It: Iterator {}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Generates an iterator that yields exactly n spaces.
|
||||||
|
///
|
||||||
|
fn spaces(n: usize) -> std::iter::Take<std::iter::Repeat<char>> {
|
||||||
|
std::iter::repeat(' ').take(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_spaces() {
|
||||||
|
assert_eq!(spaces(0).collect::<String>(), "");
|
||||||
|
assert_eq!(spaces(10).collect::<String>(), " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Returns an iterator of dates in a given year.
|
||||||
|
///
|
||||||
|
fn dates_in_year(year: i32) -> impl Iterator<Item=NaiveDate>+Clone {
|
||||||
|
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: Iterator<Item=NaiveDate> + Clone {}
|
||||||
|
impl<It> DateIterator for It where It: Iterator<Item=NaiveDate> + Clone {}
|
||||||
|
|
||||||
|
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::<Vec<_>>()[..], 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::<Vec<_>>()[..], b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Groups an iterator of dates by month.
|
||||||
|
///
|
||||||
|
fn by_month<It>(it: It)
|
||||||
|
-> impl Iterator<Item=(u32, impl Iterator<Item=NaiveDate> + Clone)> + Clone
|
||||||
|
where It: Iterator<Item=NaiveDate> + Clone {
|
||||||
|
it.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.
|
||||||
|
///
|
||||||
|
fn by_week<It>(it: It)
|
||||||
|
-> impl Iterator<Item=(u32, impl DateIterator)> + Clone
|
||||||
|
where It: DateIterator {
|
||||||
|
// We go forward one day because `isoweekdate` considers the week to start on a Monday.
|
||||||
|
it.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::<Vec<_>>(),
|
||||||
|
&[
|
||||||
|
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::<Vec<_>>(),
|
||||||
|
&[
|
||||||
|
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.
|
||||||
|
///
|
||||||
|
fn format_weeks<It>(it: It) -> impl Iterator<Item=String>
|
||||||
|
where It: Iterator, It::Item: DateIterator {
|
||||||
|
it.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.
|
||||||
|
///
|
||||||
|
fn format_month<It: DateIterator>(it: It) -> impl Iterator<Item=String> {
|
||||||
|
let mut month_days = it.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.
|
||||||
|
///
|
||||||
|
fn format_months<It>(it: It) -> impl Iterator<Item=impl Iterator<Item=String>>
|
||||||
|
where It: Iterator, It::Item: DateIterator {
|
||||||
|
it.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<Item=String> {
|
||||||
|
fn paste_blocks(self, sep_width: usize) -> PasteBlocksIter<Self::Item> {
|
||||||
|
PasteBlocksIter {
|
||||||
|
iters: self.collect(),
|
||||||
|
cache: vec![],
|
||||||
|
col_widths: None,
|
||||||
|
sep_width: sep_width,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<It> PasteBlocks for It where It: Iterator, It::Item: Iterator<Item=String> {}
|
||||||
|
|
||||||
|
struct PasteBlocksIter<StrIt>
|
||||||
|
where StrIt: Iterator<Item=String> {
|
||||||
|
iters: Vec<StrIt>,
|
||||||
|
cache: Vec<Option<String>>,
|
||||||
|
col_widths: Option<Vec<usize>>,
|
||||||
|
sep_width: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<StrIt> Iterator for PasteBlocksIter<StrIt>
|
||||||
|
where StrIt: Iterator<Item=String> {
|
||||||
|
type Item = String;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<String> {
|
||||||
|
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<Self> {
|
||||||
|
assert!(n > 0);
|
||||||
|
ChunksIter {
|
||||||
|
it: self,
|
||||||
|
n: n,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<It> Chunks for It where It: Iterator {}
|
||||||
|
|
||||||
|
struct ChunksIter<It>
|
||||||
|
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<It> Iterator for ChunksIter<It>
|
||||||
|
where It: Iterator {
|
||||||
|
type Item = Vec<It::Item>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Vec<It::Item>> {
|
||||||
|
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::<Vec<_>>();
|
||||||
|
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();
|
||||||
|
}
|
40
src/test/run-pass/impl-trait/example-st.rs
Normal file
40
src/test/run-pass/impl-trait/example-st.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
// Copyright 2016 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 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
#![feature(conservative_impl_trait, question_mark)]
|
||||||
|
|
||||||
|
struct State;
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
trait Bind<F> {
|
||||||
|
type Output;
|
||||||
|
fn bind(self, f: F) -> Self::Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind<T, U, A, B, F>(mut a: A, mut f: F)
|
||||||
|
-> impl FnMut(&mut State) -> Result<U, Error>
|
||||||
|
where F: FnMut(T) -> B,
|
||||||
|
A: FnMut(&mut State) -> Result<T, Error>,
|
||||||
|
B: FnMut(&mut State) -> Result<U, Error>
|
||||||
|
{
|
||||||
|
move |state | {
|
||||||
|
let r = a(state)?;
|
||||||
|
f(r)(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn atom<T>(x: T) -> impl FnMut(&mut State) -> Result<T, Error> {
|
||||||
|
let mut x = Some(x);
|
||||||
|
move |_| x.take().map_or(Err(()), Ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
assert_eq!(bind(atom(5), |x| atom(x > 4))(&mut State), Ok(true));
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue