From c2e8f3b481fd41f801c3e79d43861e8e86cd1a44 Mon Sep 17 00:00:00 2001 From: Jakub Wieczorek Date: Sun, 12 Oct 2014 22:01:38 +0200 Subject: [PATCH 1/2] Add a memoize! macro and use it throughout rustc --- src/librustc/lib.rs | 1 + src/librustc/middle/macros.rs | 71 +++++++++++++++ src/librustc/middle/ty.rs | 166 +++++++++++----------------------- 3 files changed, 125 insertions(+), 113 deletions(-) create mode 100644 src/librustc/middle/macros.rs diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index bd440892515..e3b6e5e0176 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -97,6 +97,7 @@ pub mod middle { pub mod intrinsicck; pub mod lang_items; pub mod liveness; + pub mod macros; pub mod mem_categorization; pub mod pat_util; pub mod privacy; diff --git a/src/librustc/middle/macros.rs b/src/librustc/middle/macros.rs new file mode 100644 index 00000000000..3d38e0fd5ed --- /dev/null +++ b/src/librustc/middle/macros.rs @@ -0,0 +1,71 @@ +// Copyright 2014 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. + +#![macro_escape] + +macro_rules! memoize_expand_block( + ($cache_map:expr, $cache_key:expr, $($param_name:ident: $param_ty:ty),*) => { { + match ($cache_map).borrow().find(&$cache_key) { + Some(ref result) => return (*result).clone(), + None => {} + } + let result = inner($($param_name), *); + ($cache_map).borrow_mut().insert($cache_key, result.clone()); + result + } } +) + +/// Memoizes a function using a cache that is available by evaluating the +/// `$cache_map` exression in the context of the function's arguments. +/// `$cache_key` is the expression that will be used to compute the cache key +/// for each function invocation. +/// +/// The macro assumes the cache to be a RefCell containing a HashMap, +/// which is in practice how most caching in rustc is currently carried out. +/// +/// # Example +/// +/// ``` +/// struct Context { +/// fibonacci_cache: RefCell> +/// } +/// +/// memoize!(context.fibonacci_cache, n, +/// fn fibonacci(context: &Context, n: uint) -> uint { +/// match n { +/// 0 | 1 => n, +/// _ => fibonacci(n - 2) + fibonacci(n - 1) +/// } +/// } +/// ) +/// ``` +macro_rules! memoize( + ($cache_map:expr, $cache_key:expr, + fn $name:ident( + $($param_name:ident: $param_ty:ty),* + ) -> $output_ty:ty $block:block + ) => { + fn $name($($param_name: $param_ty), *) -> $output_ty { + fn inner($($param_name: $param_ty), *) -> $output_ty $block + memoize_expand_block!($cache_map, $cache_key, $($param_name: $param_ty), *) + } + }; + + ($cache_map:expr, $cache_key:expr, + pub fn $name:ident( + $($param_name:ident: $param_ty:ty),* + ) -> $output_ty:ty $block:block + ) => { + pub fn $name($($param_name: $param_ty), *) -> $output_ty { + fn inner($($param_name: $param_ty), *) -> $output_ty $block + memoize_expand_block!($cache_map, $cache_key, $($param_name: $param_ty), *) + } + } +) diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 1185c644978..21d65c1e8ee 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -2117,22 +2117,8 @@ pub fn type_needs_drop(cx: &ctxt, ty: t) -> bool { // task can free them all at once later. Currently only things // that only contain scalars and shared boxes can avoid unwind // cleanups. -pub fn type_needs_unwind_cleanup(cx: &ctxt, ty: t) -> bool { - match cx.needs_unwind_cleanup_cache.borrow().find(&ty) { - Some(&result) => return result, - None => () - } - - let mut tycache = HashSet::new(); - let needs_unwind_cleanup = - type_needs_unwind_cleanup_(cx, ty, &mut tycache); - cx.needs_unwind_cleanup_cache.borrow_mut().insert(ty, needs_unwind_cleanup); - needs_unwind_cleanup -} - -fn type_needs_unwind_cleanup_(cx: &ctxt, ty: t, - tycache: &mut HashSet) -> bool { - +memoize!(cx.needs_unwind_cleanup_cache, ty, +fn type_needs_unwind_cleanup_(cx: &ctxt, ty: t, tycache: &mut HashSet) -> bool { // Prevent infinite recursion if !tycache.insert(ty) { return false; @@ -2140,32 +2126,29 @@ fn type_needs_unwind_cleanup_(cx: &ctxt, ty: t, let mut needs_unwind_cleanup = false; maybe_walk_ty(ty, |ty| { - let result = match get(ty).sty { - ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) | - ty_tup(_) | ty_ptr(_) => { - true - } - ty_enum(did, ref substs) => { - for v in (*enum_variants(cx, did)).iter() { - for aty in v.args.iter() { - let t = aty.subst(cx, substs); - needs_unwind_cleanup |= - type_needs_unwind_cleanup_(cx, t, tycache); - } - } - !needs_unwind_cleanup - } - _ => { - needs_unwind_cleanup = true; - false - } + needs_unwind_cleanup |= match get(ty).sty { + ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | + ty_float(_) | ty_tup(_) | ty_ptr(_) => false, + + ty_enum(did, ref substs) => + enum_variants(cx, did).iter().any(|v| + v.args.iter().any(|aty| { + let t = aty.subst(cx, substs); + type_needs_unwind_cleanup_(cx, t, tycache) + }) + ), + + _ => true }; - - result + !needs_unwind_cleanup }); - needs_unwind_cleanup } +) + +pub fn type_needs_unwind_cleanup(cx: &ctxt, ty: t) -> bool { + type_needs_unwind_cleanup_(cx, ty, &mut HashSet::new()) +} /** * Type contents is how the type checker reasons about kinds. @@ -2179,6 +2162,7 @@ fn type_needs_unwind_cleanup_(cx: &ctxt, ty: t, * easier for me (nmatsakis) to think about what is contained within * a type than to think about what is *not* contained within a type. */ +#[deriving(Clone)] pub struct TypeContents { pub bits: u64 } @@ -2358,19 +2342,9 @@ pub fn type_interior_is_unsafe(cx: &ctxt, t: ty::t) -> bool { type_contents(cx, t).interior_unsafe() } +memoize!(cx.tc_cache, type_id(ty), pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents { - let ty_id = type_id(ty); - - match cx.tc_cache.borrow().find(&ty_id) { - Some(tc) => { return *tc; } - None => {} - } - - let mut cache = HashMap::new(); - let result = tc_ty(cx, ty, &mut cache); - - cx.tc_cache.borrow_mut().insert(ty_id, result); - return result; + return tc_ty(cx, ty, &mut HashMap::new()); fn tc_ty(cx: &ctxt, ty: t, @@ -2685,6 +2659,7 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents { } } } +) pub fn type_moves_by_default(cx: &ctxt, ty: t) -> bool { type_contents(cx, ty).moves_by_default(cx) @@ -4033,28 +4008,23 @@ pub fn impl_or_trait_item(cx: &ctxt, id: ast::DefId) -> ImplOrTraitItem { /// Returns true if the given ID refers to an associated type and false if it /// refers to anything else. +memoize!(cx.associated_types, id, pub fn is_associated_type(cx: &ctxt, id: ast::DefId) -> bool { - let result = match cx.associated_types.borrow_mut().find(&id) { - Some(result) => return *result, - None if id.krate == ast::LOCAL_CRATE => { - match cx.impl_or_trait_items.borrow().find(&id) { - Some(ref item) => { - match **item { - TypeTraitItem(_) => true, - MethodTraitItem(_) => false, - } + if id.krate == ast::LOCAL_CRATE { + match cx.impl_or_trait_items.borrow().find(&id) { + Some(ref item) => { + match **item { + TypeTraitItem(_) => true, + MethodTraitItem(_) => false, } - None => false, } + None => false, } - None => { - csearch::is_associated_type(&cx.sess.cstore, id) - } - }; - - cx.associated_types.borrow_mut().insert(id, result); - result + } else { + csearch::is_associated_type(&cx.sess.cstore, id) + } } +) /// Returns the parameter index that the given associated type corresponds to. pub fn associated_type_parameter_index(cx: &ctxt, @@ -4110,13 +4080,9 @@ pub fn trait_item_def_ids(cx: &ctxt, id: ast::DefId) }) } +memoize!(cx.impl_trait_cache, id, pub fn impl_trait_ref(cx: &ctxt, id: ast::DefId) -> Option> { - match cx.impl_trait_cache.borrow().find(&id) { - Some(ret) => { return ret.clone(); } - None => {} - } - - let ret = if id.krate == ast::LOCAL_CRATE { + if id.krate == ast::LOCAL_CRATE { debug!("(impl_trait_ref) searching for trait impl {:?}", id); match cx.map.find(id.node) { Some(ast_map::NodeItem(item)) => { @@ -4136,11 +4102,9 @@ pub fn impl_trait_ref(cx: &ctxt, id: ast::DefId) -> Option> { } } else { csearch::get_impl_trait(cx, id) - }; - - cx.impl_trait_cache.borrow_mut().insert(id, ret.clone()); - ret + } } +) pub fn trait_ref_to_def_id(tcx: &ctxt, tr: &ast::TraitRef) -> ast::DefId { let def = *tcx.def_map.borrow() @@ -4324,13 +4288,9 @@ pub fn type_is_empty(cx: &ctxt, t: t) -> bool { } } +memoize!(cx.enum_var_cache, id, pub fn enum_variants(cx: &ctxt, id: ast::DefId) -> Rc>> { - match cx.enum_var_cache.borrow().find(&id) { - Some(variants) => return variants.clone(), - _ => { /* fallthrough */ } - } - - let result = if ast::LOCAL_CRATE != id.krate { + if ast::LOCAL_CRATE != id.krate { Rc::new(csearch::get_enum_variants(cx, id)) } else { /* @@ -4385,12 +4345,9 @@ pub fn enum_variants(cx: &ctxt, id: ast::DefId) -> Rc>> { } _ => cx.sess.bug("enum_variants: id not bound to an enum") } - }; - - cx.enum_var_cache.borrow_mut().insert(id, result.clone()); - result + } } - +) // Returns information about the enum variant with the given ID: pub fn enum_variant_with_id(cx: &ctxt, @@ -4415,22 +4372,12 @@ pub fn lookup_item_type(cx: &ctxt, } /// Given the did of a trait, returns its canonical trait ref. +memoize!(cx.trait_defs, did, pub fn lookup_trait_def(cx: &ctxt, did: ast::DefId) -> Rc { - let mut trait_defs = cx.trait_defs.borrow_mut(); - match trait_defs.find_copy(&did) { - Some(trait_def) => { - // The item is in this crate. The caller should have added it to the - // type cache already - trait_def - } - None => { - assert!(did.krate != ast::LOCAL_CRATE); - let trait_def = Rc::new(csearch::get_trait_def(cx, did)); - trait_defs.insert(did, trait_def.clone()); - trait_def - } - } + assert!(did.krate != ast::LOCAL_CRATE); + Rc::new(csearch::get_trait_def(cx, did)) } +) /// Given a reference to a trait, returns the bounds declared on the /// trait, with appropriate substitutions applied. @@ -4489,13 +4436,9 @@ pub fn lookup_simd(tcx: &ctxt, did: DefId) -> bool { } /// Obtain the representation annotation for a struct definition. +memoize!(tcx.repr_hint_cache, did, pub fn lookup_repr_hints(tcx: &ctxt, did: DefId) -> Rc> { - match tcx.repr_hint_cache.borrow().find(&did) { - None => {} - Some(ref hints) => return (*hints).clone(), - } - - let acc = if did.krate == LOCAL_CRATE { + Rc::new(if did.krate == LOCAL_CRATE { let mut acc = Vec::new(); ty::each_attr(tcx, did, |meta| { acc.extend(attr::find_repr_attrs(tcx.sess.diagnostic(), @@ -4505,12 +4448,9 @@ pub fn lookup_repr_hints(tcx: &ctxt, did: DefId) -> Rc> { acc } else { csearch::get_repr_attrs(&tcx.sess.cstore, did) - }; - - let acc = Rc::new(acc); - tcx.repr_hint_cache.borrow_mut().insert(did, acc.clone()); - acc + }) } +) // Look up a field ID, whether or not it's local // Takes a list of type substs in case the struct is generic From dbc4a4b53b91e687429db622626a6eb221252b04 Mon Sep 17 00:00:00 2001 From: Jakub Wieczorek Date: Tue, 14 Oct 2014 20:41:50 +0200 Subject: [PATCH 2/2] Make memoize!() a function instead --- src/librustc/lib.rs | 8 +- src/librustc/middle/macros.rs | 71 --------- src/librustc/middle/ty.rs | 263 +++++++++++++++++----------------- src/librustc/util/common.rs | 58 +++++++- 4 files changed, 190 insertions(+), 210 deletions(-) delete mode 100644 src/librustc/middle/macros.rs diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index e3b6e5e0176..6d5cd6061cf 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -29,12 +29,9 @@ This API is completely unstable and subject to change. html_root_url = "http://doc.rust-lang.org/nightly/")] #![allow(deprecated)] -#![allow(unknown_features)] -#![feature(macro_rules, globs, struct_variant, quote)] -#![feature(default_type_params, phase, unsafe_destructor, slicing_syntax)] - +#![feature(default_type_params, globs, if_let, import_shadowing, macro_rules, phase, quote)] +#![feature(slicing_syntax, struct_variant, unsafe_destructor)] #![feature(rustc_diagnostic_macros)] -#![feature(import_shadowing)] extern crate arena; extern crate debug; @@ -97,7 +94,6 @@ pub mod middle { pub mod intrinsicck; pub mod lang_items; pub mod liveness; - pub mod macros; pub mod mem_categorization; pub mod pat_util; pub mod privacy; diff --git a/src/librustc/middle/macros.rs b/src/librustc/middle/macros.rs deleted file mode 100644 index 3d38e0fd5ed..00000000000 --- a/src/librustc/middle/macros.rs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2014 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. - -#![macro_escape] - -macro_rules! memoize_expand_block( - ($cache_map:expr, $cache_key:expr, $($param_name:ident: $param_ty:ty),*) => { { - match ($cache_map).borrow().find(&$cache_key) { - Some(ref result) => return (*result).clone(), - None => {} - } - let result = inner($($param_name), *); - ($cache_map).borrow_mut().insert($cache_key, result.clone()); - result - } } -) - -/// Memoizes a function using a cache that is available by evaluating the -/// `$cache_map` exression in the context of the function's arguments. -/// `$cache_key` is the expression that will be used to compute the cache key -/// for each function invocation. -/// -/// The macro assumes the cache to be a RefCell containing a HashMap, -/// which is in practice how most caching in rustc is currently carried out. -/// -/// # Example -/// -/// ``` -/// struct Context { -/// fibonacci_cache: RefCell> -/// } -/// -/// memoize!(context.fibonacci_cache, n, -/// fn fibonacci(context: &Context, n: uint) -> uint { -/// match n { -/// 0 | 1 => n, -/// _ => fibonacci(n - 2) + fibonacci(n - 1) -/// } -/// } -/// ) -/// ``` -macro_rules! memoize( - ($cache_map:expr, $cache_key:expr, - fn $name:ident( - $($param_name:ident: $param_ty:ty),* - ) -> $output_ty:ty $block:block - ) => { - fn $name($($param_name: $param_ty), *) -> $output_ty { - fn inner($($param_name: $param_ty), *) -> $output_ty $block - memoize_expand_block!($cache_map, $cache_key, $($param_name: $param_ty), *) - } - }; - - ($cache_map:expr, $cache_key:expr, - pub fn $name:ident( - $($param_name:ident: $param_ty:ty),* - ) -> $output_ty:ty $block:block - ) => { - pub fn $name($($param_name: $param_ty), *) -> $output_ty { - fn inner($($param_name: $param_ty), *) -> $output_ty $block - memoize_expand_block!($cache_map, $cache_key, $($param_name: $param_ty), *) - } - } -) diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 21d65c1e8ee..06d36230068 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -33,7 +33,7 @@ use middle; use util::ppaux::{note_and_explain_region, bound_region_ptr_to_string}; use util::ppaux::{trait_store_to_string, ty_to_string}; use util::ppaux::{Repr, UserString}; -use util::common::{indenter}; +use util::common::{indenter, memoized, memoized_with_key}; use util::nodemap::{NodeMap, NodeSet, DefIdMap, DefIdSet, FnvHashMap}; use std::cell::{Cell, RefCell}; @@ -2117,37 +2117,37 @@ pub fn type_needs_drop(cx: &ctxt, ty: t) -> bool { // task can free them all at once later. Currently only things // that only contain scalars and shared boxes can avoid unwind // cleanups. -memoize!(cx.needs_unwind_cleanup_cache, ty, -fn type_needs_unwind_cleanup_(cx: &ctxt, ty: t, tycache: &mut HashSet) -> bool { - // Prevent infinite recursion - if !tycache.insert(ty) { - return false; - } - - let mut needs_unwind_cleanup = false; - maybe_walk_ty(ty, |ty| { - needs_unwind_cleanup |= match get(ty).sty { - ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | - ty_float(_) | ty_tup(_) | ty_ptr(_) => false, - - ty_enum(did, ref substs) => - enum_variants(cx, did).iter().any(|v| - v.args.iter().any(|aty| { - let t = aty.subst(cx, substs); - type_needs_unwind_cleanup_(cx, t, tycache) - }) - ), - - _ => true - }; - !needs_unwind_cleanup - }); - needs_unwind_cleanup -} -) - pub fn type_needs_unwind_cleanup(cx: &ctxt, ty: t) -> bool { - type_needs_unwind_cleanup_(cx, ty, &mut HashSet::new()) + return memoized(&cx.needs_unwind_cleanup_cache, ty, |ty| { + type_needs_unwind_cleanup_(cx, ty, &mut HashSet::new()) + }); + + fn type_needs_unwind_cleanup_(cx: &ctxt, ty: t, tycache: &mut HashSet) -> bool { + // Prevent infinite recursion + if !tycache.insert(ty) { + return false; + } + + let mut needs_unwind_cleanup = false; + maybe_walk_ty(ty, |ty| { + needs_unwind_cleanup |= match get(ty).sty { + ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | + ty_float(_) | ty_tup(_) | ty_ptr(_) => false, + + ty_enum(did, ref substs) => + enum_variants(cx, did).iter().any(|v| + v.args.iter().any(|aty| { + let t = aty.subst(cx, substs); + type_needs_unwind_cleanup_(cx, t, tycache) + }) + ), + + _ => true + }; + !needs_unwind_cleanup + }); + needs_unwind_cleanup + } } /** @@ -2342,9 +2342,10 @@ pub fn type_interior_is_unsafe(cx: &ctxt, t: ty::t) -> bool { type_contents(cx, t).interior_unsafe() } -memoize!(cx.tc_cache, type_id(ty), pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents { - return tc_ty(cx, ty, &mut HashMap::new()); + return memoized_with_key(&cx.tc_cache, ty, |ty| { + tc_ty(cx, ty, &mut HashMap::new()) + }, |&ty| type_id(ty)); fn tc_ty(cx: &ctxt, ty: t, @@ -2659,7 +2660,6 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents { } } } -) pub fn type_moves_by_default(cx: &ctxt, ty: t) -> bool { type_contents(cx, ty).moves_by_default(cx) @@ -4008,23 +4008,23 @@ pub fn impl_or_trait_item(cx: &ctxt, id: ast::DefId) -> ImplOrTraitItem { /// Returns true if the given ID refers to an associated type and false if it /// refers to anything else. -memoize!(cx.associated_types, id, pub fn is_associated_type(cx: &ctxt, id: ast::DefId) -> bool { - if id.krate == ast::LOCAL_CRATE { - match cx.impl_or_trait_items.borrow().find(&id) { - Some(ref item) => { - match **item { - TypeTraitItem(_) => true, - MethodTraitItem(_) => false, + memoized(&cx.associated_types, id, |id: ast::DefId| { + if id.krate == ast::LOCAL_CRATE { + match cx.impl_or_trait_items.borrow().find(&id) { + Some(ref item) => { + match **item { + TypeTraitItem(_) => true, + MethodTraitItem(_) => false, + } } + None => false, } - None => false, + } else { + csearch::is_associated_type(&cx.sess.cstore, id) } - } else { - csearch::is_associated_type(&cx.sess.cstore, id) - } + }) } -) /// Returns the parameter index that the given associated type corresponds to. pub fn associated_type_parameter_index(cx: &ctxt, @@ -4080,31 +4080,31 @@ pub fn trait_item_def_ids(cx: &ctxt, id: ast::DefId) }) } -memoize!(cx.impl_trait_cache, id, pub fn impl_trait_ref(cx: &ctxt, id: ast::DefId) -> Option> { - if id.krate == ast::LOCAL_CRATE { - debug!("(impl_trait_ref) searching for trait impl {:?}", id); - match cx.map.find(id.node) { - Some(ast_map::NodeItem(item)) => { - match item.node { - ast::ItemImpl(_, ref opt_trait, _, _) => { - match opt_trait { - &Some(ref t) => { - Some(ty::node_id_to_trait_ref(cx, t.ref_id)) + memoized(&cx.impl_trait_cache, id, |id: ast::DefId| { + if id.krate == ast::LOCAL_CRATE { + debug!("(impl_trait_ref) searching for trait impl {:?}", id); + match cx.map.find(id.node) { + Some(ast_map::NodeItem(item)) => { + match item.node { + ast::ItemImpl(_, ref opt_trait, _, _) => { + match opt_trait { + &Some(ref t) => { + Some(ty::node_id_to_trait_ref(cx, t.ref_id)) + } + &None => None } - &None => None } + _ => None } - _ => None } + _ => None } - _ => None + } else { + csearch::get_impl_trait(cx, id) } - } else { - csearch::get_impl_trait(cx, id) - } + }) } -) pub fn trait_ref_to_def_id(tcx: &ctxt, tr: &ast::TraitRef) -> ast::DefId { let def = *tcx.def_map.borrow() @@ -4288,66 +4288,67 @@ pub fn type_is_empty(cx: &ctxt, t: t) -> bool { } } -memoize!(cx.enum_var_cache, id, pub fn enum_variants(cx: &ctxt, id: ast::DefId) -> Rc>> { - if ast::LOCAL_CRATE != id.krate { - Rc::new(csearch::get_enum_variants(cx, id)) - } else { - /* - Although both this code and check_enum_variants in typeck/check - call eval_const_expr, it should never get called twice for the same - expr, since check_enum_variants also updates the enum_var_cache - */ - match cx.map.get(id.node) { - ast_map::NodeItem(ref item) => { - match item.node { - ast::ItemEnum(ref enum_definition, _) => { - let mut last_discriminant: Option = None; - Rc::new(enum_definition.variants.iter().map(|variant| { + memoized(&cx.enum_var_cache, id, |id: ast::DefId| { + if ast::LOCAL_CRATE != id.krate { + Rc::new(csearch::get_enum_variants(cx, id)) + } else { + /* + Although both this code and check_enum_variants in typeck/check + call eval_const_expr, it should never get called twice for the same + expr, since check_enum_variants also updates the enum_var_cache + */ + match cx.map.get(id.node) { + ast_map::NodeItem(ref item) => { + match item.node { + ast::ItemEnum(ref enum_definition, _) => { + let mut last_discriminant: Option = None; + Rc::new(enum_definition.variants.iter().map(|variant| { - let mut discriminant = match last_discriminant { - Some(val) => val + 1, - None => INITIAL_DISCRIMINANT_VALUE - }; + let mut discriminant = match last_discriminant { + Some(val) => val + 1, + None => INITIAL_DISCRIMINANT_VALUE + }; - match variant.node.disr_expr { - Some(ref e) => match const_eval::eval_const_expr_partial(cx, &**e) { - Ok(const_eval::const_int(val)) => { - discriminant = val as Disr - } - Ok(const_eval::const_uint(val)) => { - discriminant = val as Disr - } - Ok(_) => { - cx.sess - .span_err(e.span, - "expected signed integer constant"); - } - Err(ref err) => { - cx.sess - .span_err(e.span, - format!("expected constant: {}", - *err).as_slice()); - } - }, - None => {} - }; + match variant.node.disr_expr { + Some(ref e) => + match const_eval::eval_const_expr_partial(cx, &**e) { + Ok(const_eval::const_int(val)) => { + discriminant = val as Disr + } + Ok(const_eval::const_uint(val)) => { + discriminant = val as Disr + } + Ok(_) => { + cx.sess + .span_err(e.span, + "expected signed integer constant"); + } + Err(ref err) => { + cx.sess + .span_err(e.span, + format!("expected constant: {}", + *err).as_slice()); + } + }, + None => {} + }; - last_discriminant = Some(discriminant); - Rc::new(VariantInfo::from_ast_variant(cx, &**variant, - discriminant)) - }).collect()) - } - _ => { - cx.sess.bug("enum_variants: id not bound to an enum") + last_discriminant = Some(discriminant); + Rc::new(VariantInfo::from_ast_variant(cx, &**variant, + discriminant)) + }).collect()) + } + _ => { + cx.sess.bug("enum_variants: id not bound to an enum") + } } } + _ => cx.sess.bug("enum_variants: id not bound to an enum") } - _ => cx.sess.bug("enum_variants: id not bound to an enum") } - } + }) } -) // Returns information about the enum variant with the given ID: pub fn enum_variant_with_id(cx: &ctxt, @@ -4372,12 +4373,12 @@ pub fn lookup_item_type(cx: &ctxt, } /// Given the did of a trait, returns its canonical trait ref. -memoize!(cx.trait_defs, did, -pub fn lookup_trait_def(cx: &ctxt, did: ast::DefId) -> Rc { - assert!(did.krate != ast::LOCAL_CRATE); - Rc::new(csearch::get_trait_def(cx, did)) +pub fn lookup_trait_def(cx: &ctxt, did: DefId) -> Rc { + memoized(&cx.trait_defs, did, |did: DefId| { + assert!(did.krate != ast::LOCAL_CRATE); + Rc::new(csearch::get_trait_def(cx, did)) + }) } -) /// Given a reference to a trait, returns the bounds declared on the /// trait, with appropriate substitutions applied. @@ -4436,21 +4437,21 @@ pub fn lookup_simd(tcx: &ctxt, did: DefId) -> bool { } /// Obtain the representation annotation for a struct definition. -memoize!(tcx.repr_hint_cache, did, pub fn lookup_repr_hints(tcx: &ctxt, did: DefId) -> Rc> { - Rc::new(if did.krate == LOCAL_CRATE { - let mut acc = Vec::new(); - ty::each_attr(tcx, did, |meta| { - acc.extend(attr::find_repr_attrs(tcx.sess.diagnostic(), - meta).into_iter()); - true - }); - acc - } else { - csearch::get_repr_attrs(&tcx.sess.cstore, did) + memoized(&tcx.repr_hint_cache, did, |did: DefId| { + Rc::new(if did.krate == LOCAL_CRATE { + let mut acc = Vec::new(); + ty::each_attr(tcx, did, |meta| { + acc.extend(attr::find_repr_attrs(tcx.sess.diagnostic(), + meta).into_iter()); + true + }); + acc + } else { + csearch::get_repr_attrs(&tcx.sess.cstore, did) + }) }) } -) // Look up a field ID, whether or not it's local // Takes a list of type substs in case the struct is generic diff --git a/src/librustc/util/common.rs b/src/librustc/util/common.rs index c39f011189a..8ea9015c05d 100644 --- a/src/librustc/util/common.rs +++ b/src/librustc/util/common.rs @@ -1,4 +1,4 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT +// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // @@ -10,8 +10,9 @@ #![allow(non_camel_case_types)] -use std::hash::{Hash, Hasher}; +use std::cell::RefCell; use std::collections::HashMap; +use std::hash::{Hash, Hasher}; use syntax::ast; use syntax::visit; use syntax::visit::Visitor; @@ -155,3 +156,56 @@ pub fn can_reach,T:Eq+Clone+Hash>( } return false; } + +/// Memoizes a one-argument closure using the given RefCell containing +/// a type implementing MutableMap to serve as a cache. +/// +/// In the future the signature of this function is expected to be: +/// ``` +/// pub fn memoized>( +/// cache: &RefCell, +/// f: &|&: T| -> U +/// ) -> impl |&: T| -> U { +/// ``` +/// but currently it is not possible. +/// +/// # Example +/// ``` +/// struct Context { +/// cache: RefCell> +/// } +/// +/// fn factorial(ctxt: &Context, n: uint) -> uint { +/// memoized(&ctxt.cache, n, |n| match n { +/// 0 | 1 => n, +/// _ => factorial(ctxt, n - 2) + factorial(ctxt, n - 1) +/// }) +/// } +/// ``` +#[inline(always)] +pub fn memoized>( + cache: &RefCell, + arg: T, + f: |T| -> U +) -> U { + memoized_with_key(cache, arg, f, |arg| arg.clone()) +} + +#[inline(always)] +pub fn memoized_with_key>( + cache: &RefCell, + arg: T, + f: |T| -> U, + k: |&T| -> K +) -> U { + let key = k(&arg); + let result = cache.borrow().find(&key).map(|result| result.clone()); + match result { + Some(result) => result, + None => { + let result = f(arg); + cache.borrow_mut().insert(key, result.clone()); + result + } + } +}