This is his own mirror, so it shouldn't go down, unlike the previous one.
Fixes #26661
-% The (old) Rust Pointer Guide
+% The Rust Pointer Guide
-This content has moved into
-[the Rust Programming Language book](book/pointers.html).
+This content has been removed, with no direct replacement. Rust only
+has two built-in pointer types now,
+[references](book/references-and-borrowing.html) and [raw
+pointers](book/raw-pointers.html). Older Rusts had many more pointer
+types, they’re gone now.
the borrow ‘doesn’t live long enough’ because it’s not valid for the right
amount of time.
-The same problem occurs when the reference is declared _before_ the variable it refers to:
+The same problem occurs when the reference is declared _before_ the variable it
+refers to. This is because resources within the same scope are freed in the
+opposite order they were declared:
```rust,ignore
let y: &i32;
println!("{}", y);
}
```
+
+In the above example, `y` is declared before `x`, meaning that `y` lives longer
+than `x`, which is not allowed.
unsafe { atomic_swap(self.v.get(), val, order) > 0 }
}
- /// Stores a value into the bool if the current value is the same as the expected value.
+ /// Stores a value into the `bool` if the current value is the same as the `current` value.
///
- /// The return value is always the previous value. If it is equal to `old`, then the value was
- /// updated.
+ /// The return value is always the previous value. If it is equal to `current`, then the value
+ /// was updated.
///
- /// `swap` also takes an `Ordering` argument which describes the memory ordering of this
- /// operation.
+ /// `compare_and_swap` also takes an `Ordering` argument which describes the memory ordering of
+ /// this operation.
///
/// # Examples
///
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
- pub fn compare_and_swap(&self, old: bool, new: bool, order: Ordering) -> bool {
- let old = if old { UINT_TRUE } else { 0 };
+ pub fn compare_and_swap(&self, current: bool, new: bool, order: Ordering) -> bool {
+ let current = if current { UINT_TRUE } else { 0 };
let new = if new { UINT_TRUE } else { 0 };
- unsafe { atomic_compare_and_swap(self.v.get(), old, new, order) > 0 }
+ unsafe { atomic_compare_and_swap(self.v.get(), current, new, order) > 0 }
}
/// Logical "and" with a boolean value.
unsafe { atomic_swap(self.v.get(), val, order) }
}
- /// Stores a value into the isize if the current value is the same as the expected value.
+ /// Stores a value into the `isize` if the current value is the same as the `current` value.
///
- /// The return value is always the previous value. If it is equal to `old`, then the value was
- /// updated.
+ /// The return value is always the previous value. If it is equal to `current`, then the value
+ /// was updated.
///
/// `compare_and_swap` also takes an `Ordering` argument which describes the memory ordering of
/// this operation.
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
- pub fn compare_and_swap(&self, old: isize, new: isize, order: Ordering) -> isize {
- unsafe { atomic_compare_and_swap(self.v.get(), old, new, order) }
+ pub fn compare_and_swap(&self, current: isize, new: isize, order: Ordering) -> isize {
+ unsafe { atomic_compare_and_swap(self.v.get(), current, new, order) }
}
/// Add an isize to the current value, returning the previous value.
unsafe { atomic_swap(self.v.get(), val, order) }
}
- /// Stores a value into the usize if the current value is the same as the expected value.
+ /// Stores a value into the `usize` if the current value is the same as the `current` value.
///
- /// The return value is always the previous value. If it is equal to `old`, then the value was
- /// updated.
+ /// The return value is always the previous value. If it is equal to `current`, then the value
+ /// was updated.
///
/// `compare_and_swap` also takes an `Ordering` argument which describes the memory ordering of
/// this operation.
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
- pub fn compare_and_swap(&self, old: usize, new: usize, order: Ordering) -> usize {
- unsafe { atomic_compare_and_swap(self.v.get(), old, new, order) }
+ pub fn compare_and_swap(&self, current: usize, new: usize, order: Ordering) -> usize {
+ unsafe { atomic_compare_and_swap(self.v.get(), current, new, order) }
}
/// Add to the current usize, returning the previous value.
unsafe { atomic_swap(self.p.get() as *mut usize, ptr as usize, order) as *mut T }
}
- /// Stores a value into the pointer if the current value is the same as the expected value.
+ /// Stores a value into the pointer if the current value is the same as the `current` value.
///
- /// The return value is always the previous value. If it is equal to `old`, then the value was
- /// updated.
+ /// The return value is always the previous value. If it is equal to `current`, then the value
+ /// was updated.
///
/// `compare_and_swap` also takes an `Ordering` argument which describes the memory ordering of
/// this operation.
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
- pub fn compare_and_swap(&self, old: *mut T, new: *mut T, order: Ordering) -> *mut T {
+ pub fn compare_and_swap(&self, current: *mut T, new: *mut T, order: Ordering) -> *mut T {
unsafe {
- atomic_compare_and_swap(self.p.get() as *mut usize, old as usize,
+ atomic_compare_and_swap(self.p.get() as *mut usize, current as usize,
new as usize, order) as *mut T
}
}
mod rand_impls;
/// A type that can be randomly generated using an `Rng`.
+#[doc(hidden)]
pub trait Rand : Sized {
/// Generates a random instance of this type using the specified source of
/// randomness.
where T: TypeFoldable<'tcx>,
F: FnMut(ty::Region, ty::DebruijnIndex) -> ty::Region,
{
- unbound_value.fold_with(&mut ty_fold::RegionFolder::new(tcx, &mut |region, current_depth| {
+ ty_fold::fold_regions(tcx, unbound_value, &mut false, |region, current_depth| {
// we should only be encountering "escaping" late-bound regions here,
// because the ones at the current level should have been replaced
// with fresh variables
});
fldr(region, ty::DebruijnIndex::new(current_depth))
- }))
+ })
}
impl<'a,'tcx> InferCtxtExt for InferCtxt<'a,'tcx> {
let escaping_types =
self.type_variables.borrow().types_escaping_snapshot(&snapshot.type_snapshot);
- let escaping_region_vars: FnvHashSet<_> =
- escaping_types
- .iter()
- .flat_map(|&t| ty_fold::collect_regions(self.tcx, &t))
- .collect();
+ let mut escaping_region_vars = FnvHashSet();
+ for ty in &escaping_types {
+ ty_fold::collect_regions(self.tcx, ty, &mut escaping_region_vars);
+ }
region_vars.retain(|®ion_vid| {
let r = ty::ReInfer(ty::ReVar(region_vid));
// binder is that we encountered in `value`. The caller is
// responsible for ensuring that (a) `value` contains at least one
// binder and (b) that binder is the one we want to use.
- let result = ty_fold::fold_regions(infcx.tcx, &value, |r, current_depth| {
+ let result = ty_fold::fold_regions(infcx.tcx, &value, &mut false, |r, current_depth| {
match inv_skol_map.get(&r) {
None => r,
Some(br) => {
// regions. If there are, we will call this obligation an
// error. Eventually we should be able to support some
// cases here, I imagine (e.g., `for<'a> int : 'a`).
- if selcx.tcx().count_late_bound_regions(binder) != 0 {
- errors.push(
- FulfillmentError::new(
- obligation.clone(),
- CodeSelectionError(Unimplemented)));
- } else {
- let ty::OutlivesPredicate(t_a, r_b) = binder.0;
- register_region_obligation(t_a, r_b,
- obligation.cause.clone(),
- region_obligations);
+ match selcx.tcx().no_late_bound_regions(binder) {
+ None => {
+ errors.push(
+ FulfillmentError::new(
+ obligation.clone(),
+ CodeSelectionError(Unimplemented)))
+ }
+ Some(ty::OutlivesPredicate(t_a, r_b)) => {
+ register_region_obligation(t_a, r_b,
+ obligation.cause.clone(),
+ region_obligations);
+ }
}
true
}
!self.set.insert(p.clone())
}
}
-
-
_ => false,
}
}
+
+ /// Returns the depth of `self` from the (1-based) binding level `depth`
+ pub fn from_depth(&self, depth: u32) -> Region {
+ match *self {
+ ty::ReLateBound(debruijn, r) => ty::ReLateBound(DebruijnIndex {
+ depth: debruijn.depth - (depth - 1)
+ }, r),
+ r => r
+ }
+ }
}
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash,
ByBoxExplicitSelfCategory,
}
-impl<'tcx> TyS<'tcx> {
- /// Pushes all the lifetimes in the given type onto the given list. A
- /// "lifetime in a type" is a lifetime specified by a reference or a lifetime
- /// in a list of type substitutions. This does *not* traverse into nominal
- /// types, nor does it resolve fictitious types.
- pub fn accumulate_lifetimes_in_type(&self, accumulator: &mut Vec<ty::Region>) {
- for ty in self.walk() {
- match ty.sty {
- TyRef(region, _) => {
- accumulator.push(*region)
- }
- TyTrait(ref t) => {
- accumulator.push_all(t.principal.0.substs.regions().as_slice());
- }
- TyEnum(_, substs) |
- TyStruct(_, substs) => {
- accum_substs(accumulator, substs);
- }
- TyClosure(_, substs) => {
- accum_substs(accumulator, substs);
- }
- TyBool |
- TyChar |
- TyInt(_) |
- TyUint(_) |
- TyFloat(_) |
- TyBox(_) |
- TyStr |
- TyArray(_, _) |
- TySlice(_) |
- TyRawPtr(_) |
- TyBareFn(..) |
- TyTuple(_) |
- TyProjection(_) |
- TyParam(_) |
- TyInfer(_) |
- TyError => {
- }
- }
- }
-
- fn accum_substs(accumulator: &mut Vec<Region>, substs: &Substs) {
- match substs.regions {
- subst::ErasedRegions => {}
- subst::NonerasedRegions(ref regions) => {
- for region in regions {
- accumulator.push(*region)
- }
- }
- }
- }
- }
-}
-
/// A free variable referred to in a function.
#[derive(Copy, Clone, RustcEncodable, RustcDecodable)]
pub struct Freevar {
|br| ty::ReFree(ty::FreeRegion{scope: all_outlive_scope, bound_region: br})).0
}
- pub fn count_late_bound_regions<T>(&self, value: &Binder<T>) -> usize
- where T : TypeFoldable<'tcx>
- {
- let (_, skol_map) = ty_fold::replace_late_bound_regions(self, value, |_| ty::ReStatic);
- skol_map.len()
- }
-
- pub fn binds_late_bound_regions<T>(&self, value: &Binder<T>) -> bool
- where T : TypeFoldable<'tcx>
- {
- self.count_late_bound_regions(value) > 0
- }
-
/// Flattens two binding levels into one. So `for<'a> for<'b> Foo`
/// becomes `for<'a,'b> Foo`.
pub fn flatten_late_bound_regions<T>(&self, bound2_value: &Binder<Binder<T>>)
where T: TypeFoldable<'tcx>
{
let bound0_value = bound2_value.skip_binder().skip_binder();
- let value = ty_fold::fold_regions(self, bound0_value, |region, current_depth| {
+ let value = ty_fold::fold_regions(self, bound0_value, &mut false,
+ |region, current_depth| {
match region {
ty::ReLateBound(debruijn, br) if debruijn.depth >= current_depth => {
// should be true if no escaping regions from bound2_value
}
pub fn no_late_bound_regions<T>(&self, value: &Binder<T>) -> Option<T>
- where T : TypeFoldable<'tcx>
+ where T : TypeFoldable<'tcx> + RegionEscape
{
- if self.binds_late_bound_regions(value) {
+ if value.0.has_escaping_regions() {
None
} else {
Some(value.0.clone())
}
}
+impl<T:RegionEscape> RegionEscape for Vec<T> {
+ fn has_regions_escaping_depth(&self, depth: u32) -> bool {
+ self.iter().any(|t| t.has_regions_escaping_depth(depth))
+ }
+}
+
+impl<'tcx> RegionEscape for FnSig<'tcx> {
+ fn has_regions_escaping_depth(&self, depth: u32) -> bool {
+ self.inputs.has_regions_escaping_depth(depth) ||
+ self.output.has_regions_escaping_depth(depth)
+ }
+}
+
impl<'tcx,T:RegionEscape> RegionEscape for VecPerParamSpace<T> {
fn has_regions_escaping_depth(&self, depth: u32) -> bool {
self.iter_enumerated().any(|(space, _, t)| {
}
}
+impl<'tcx> RegionEscape for FnOutput<'tcx> {
+ fn has_regions_escaping_depth(&self, depth: u32) -> bool {
+ match *self {
+ FnConverging(t) => t.has_regions_escaping_depth(depth),
+ FnDiverging => false
+ }
+ }
+}
+
impl<'tcx> RegionEscape for EquatePredicate<'tcx> {
fn has_regions_escaping_depth(&self, depth: u32) -> bool {
self.0.has_regions_escaping_depth(depth) || self.1.has_regions_escaping_depth(depth)
use syntax::abi;
use syntax::ast;
use syntax::owned_slice::OwnedSlice;
-use util::nodemap::FnvHashMap;
+use util::nodemap::{FnvHashMap, FnvHashSet};
///////////////////////////////////////////////////////////////////////////
// Two generic traits
pub struct RegionFolder<'a, 'tcx: 'a> {
tcx: &'a ty::ctxt<'tcx>,
+ skipped_regions: &'a mut bool,
current_depth: u32,
fld_r: &'a mut (FnMut(ty::Region, u32) -> ty::Region + 'a),
}
impl<'a, 'tcx> RegionFolder<'a, 'tcx> {
- pub fn new<F>(tcx: &'a ty::ctxt<'tcx>, fld_r: &'a mut F) -> RegionFolder<'a, 'tcx>
+ pub fn new<F>(tcx: &'a ty::ctxt<'tcx>,
+ skipped_regions: &'a mut bool,
+ fld_r: &'a mut F) -> RegionFolder<'a, 'tcx>
where F : FnMut(ty::Region, u32) -> ty::Region
{
RegionFolder {
tcx: tcx,
+ skipped_regions: skipped_regions,
current_depth: 1,
fld_r: fld_r,
}
}
}
-pub fn collect_regions<'tcx,T>(tcx: &ty::ctxt<'tcx>, value: &T) -> Vec<ty::Region>
+/// Collects the free and escaping regions in `value` into `region_set`. Returns
+/// whether any late-bound regions were skipped
+pub fn collect_regions<'tcx,T>(tcx: &ty::ctxt<'tcx>,
+ value: &T,
+ region_set: &mut FnvHashSet<ty::Region>) -> bool
where T : TypeFoldable<'tcx>
{
- let mut vec = Vec::new();
- fold_regions(tcx, value, |r, _| { vec.push(r); r });
- vec
+ let mut have_bound_regions = false;
+ fold_regions(tcx, value, &mut have_bound_regions,
+ |r, d| { region_set.insert(r.from_depth(d)); r });
+ have_bound_regions
}
+/// Folds the escaping and free regions in `value` using `f`, and
+/// sets `skipped_regions` to true if any late-bound region was found
+/// and skipped.
pub fn fold_regions<'tcx,T,F>(tcx: &ty::ctxt<'tcx>,
value: &T,
+ skipped_regions: &mut bool,
mut f: F)
-> T
where F : FnMut(ty::Region, u32) -> ty::Region,
T : TypeFoldable<'tcx>,
{
- value.fold_with(&mut RegionFolder::new(tcx, &mut f))
+ value.fold_with(&mut RegionFolder::new(tcx, skipped_regions, &mut f))
}
impl<'a, 'tcx> TypeFolder<'tcx> for RegionFolder<'a, 'tcx>
ty::ReLateBound(debruijn, _) if debruijn.depth < self.current_depth => {
debug!("RegionFolder.fold_region({:?}) skipped bound region (current depth={})",
r, self.current_depth);
+ *self.skipped_regions = true;
r
}
_ => {
debug!("shift_regions(value={:?}, amount={})",
value, amount);
- value.fold_with(&mut RegionFolder::new(tcx, &mut |region, _current_depth| {
+ value.fold_with(&mut RegionFolder::new(tcx, &mut false, &mut |region, _current_depth| {
shift_region(region, amount)
}))
}
// except according to those terms.
//! An iterator over the type substructure.
+//! WARNING: this does not keep track of the region depth.
use middle::ty::{self, Ty};
use std::iter::Iterator;
fn_id: ast::NodeId,
_: ast::Ident,
id: ast::NodeId) -> bool {
- tcx.def_map.borrow().get(&id)
- .map_or(false, |def| def.def_id() == local_def(fn_id))
+ match tcx.map.get(id) {
+ ast_map::NodeExpr(&ast::Expr { node: ast::ExprCall(ref callee, _), .. }) => {
+ tcx.def_map.borrow().get(&callee.id)
+ .map_or(false, |def| def.def_id() == local_def(fn_id))
+ }
+ _ => false
+ }
}
// check if the method call `id` refers to method `method_id`
// method instead.
ty::MethodTypeParam(
ty::MethodParam { ref trait_ref, method_num, impl_def_id: None, }) => {
+
+ let on_self = m.substs.self_ty().map_or(false, |t| t.is_self());
+ if !on_self {
+ // we can only be recurring in a default
+ // method if we're being called literally
+ // on the `Self` type.
+ return false
+ }
+
tcx.trait_item(trait_ref.def_id, method_num).def_id()
}
pub trait Cleanup<'tcx> {
fn must_unwind(&self) -> bool;
- fn clean_on_unwind(&self) -> bool;
fn is_lifetime_end(&self) -> bool;
fn trans<'blk>(&self,
bcx: Block<'blk, 'tcx>,
if !self.type_needs_drop(ty) { return; }
let drop = box DropValue {
is_immediate: false,
- must_unwind: common::type_needs_unwind_cleanup(self.ccx, ty),
val: val,
ty: ty,
fill_on_drop: false,
let drop = box DropValue {
is_immediate: false,
- must_unwind: common::type_needs_unwind_cleanup(self.ccx, ty),
val: val,
ty: ty,
fill_on_drop: true,
let drop = box DropValue {
is_immediate: false,
- must_unwind: common::type_needs_unwind_cleanup(self.ccx, ty),
val: val,
ty: ty,
fill_on_drop: false,
if !self.type_needs_drop(ty) { return; }
let drop = box DropValue {
is_immediate: true,
- must_unwind: common::type_needs_unwind_cleanup(self.ccx, ty),
val: val,
ty: ty,
fill_on_drop: false,
//
// At this point, `popped_scopes` is empty, and so the final block
// that we return to the user is `Cleanup(AST 24)`.
- while !popped_scopes.is_empty() {
- let mut scope = popped_scopes.pop().unwrap();
-
- if scope.cleanups.iter().any(|c| cleanup_is_suitable_for(&**c, label))
- {
- let name = scope.block_name("clean");
- debug!("generating cleanups for {}", name);
- let bcx_in = self.new_block(label.is_unwind(),
- &name[..],
- None);
- let mut bcx_out = bcx_in;
- for cleanup in scope.cleanups.iter().rev() {
- if cleanup_is_suitable_for(&**cleanup, label) {
- bcx_out = cleanup.trans(bcx_out,
- scope.debug_loc);
- }
- }
- build::Br(bcx_out, prev_llbb, DebugLoc::None);
- prev_llbb = bcx_in.llbb;
- } else {
- debug!("no suitable cleanups in {}",
- scope.block_name("clean"));
+ while let Some(mut scope) = popped_scopes.pop() {
+ let name = scope.block_name("clean");
+ debug!("generating cleanups for {}", name);
+ let bcx_in = self.new_block(label.is_unwind(),
+ &name[..],
+ None);
+ let mut bcx_out = bcx_in;
+ for cleanup in scope.cleanups.iter().rev() {
+ bcx_out = cleanup.trans(bcx_out,
+ scope.debug_loc);
}
+ build::Br(bcx_out, prev_llbb, DebugLoc::None);
+ prev_llbb = bcx_in.llbb;
scope.add_cached_early_exit(label, prev_llbb);
self.push_scope(scope);
#[derive(Copy, Clone)]
pub struct DropValue<'tcx> {
is_immediate: bool,
- must_unwind: bool,
val: ValueRef,
ty: Ty<'tcx>,
fill_on_drop: bool,
impl<'tcx> Cleanup<'tcx> for DropValue<'tcx> {
fn must_unwind(&self) -> bool {
- self.must_unwind
- }
-
- fn clean_on_unwind(&self) -> bool {
- self.must_unwind
+ true
}
fn is_lifetime_end(&self) -> bool {
true
}
- fn clean_on_unwind(&self) -> bool {
- true
- }
-
fn is_lifetime_end(&self) -> bool {
false
}
false
}
- fn clean_on_unwind(&self) -> bool {
- true
- }
-
fn is_lifetime_end(&self) -> bool {
true
}
r
}
-fn cleanup_is_suitable_for(c: &Cleanup,
- label: EarlyExitLabel) -> bool {
- !label.is_unwind() || c.clean_on_unwind()
-}
-
///////////////////////////////////////////////////////////////////////////
// These traits just exist to put the methods into this file.
use middle::mem_categorization as mc;
use middle::mem_categorization::Typer;
use middle::region;
-use middle::subst::{self, Subst, Substs};
+use middle::subst::{self, Substs};
use trans::base;
use trans::build;
use trans::cleanup;
use syntax::codemap::{DUMMY_SP, Span};
use syntax::parse::token::InternedString;
use syntax::parse::token;
-use util::common::memoized;
-use util::nodemap::FnvHashSet;
pub use trans::context::CrateContext;
}
}
-// Some things don't need cleanups during unwinding because the
-// thread 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<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ty: Ty<'tcx>) -> bool {
- return memoized(ccx.needs_unwind_cleanup_cache(), ty, |ty| {
- type_needs_unwind_cleanup_(ccx.tcx(), ty, &mut FnvHashSet())
- });
-
- fn type_needs_unwind_cleanup_<'tcx>(tcx: &ty::ctxt<'tcx>,
- ty: Ty<'tcx>,
- tycache: &mut FnvHashSet<Ty<'tcx>>)
- -> bool
- {
- // Prevent infinite recursion
- if !tycache.insert(ty) {
- return false;
- }
-
- let mut needs_unwind_cleanup = false;
- ty.maybe_walk(|ty| {
- needs_unwind_cleanup |= match ty.sty {
- ty::TyBool | ty::TyInt(_) | ty::TyUint(_) |
- ty::TyFloat(_) | ty::TyTuple(_) | ty::TyRawPtr(_) => false,
-
- ty::TyEnum(did, substs) =>
- tcx.enum_variants(did).iter().any(|v|
- v.args.iter().any(|&aty| {
- let t = aty.subst(tcx, substs);
- type_needs_unwind_cleanup_(tcx, t, tycache)
- })
- ),
-
- _ => true
- };
- !needs_unwind_cleanup
- });
- needs_unwind_cleanup
- }
-}
-
/// If `type_needs_drop` returns true, then `ty` is definitely
/// non-copy and *might* have a destructor attached; if it returns
/// false, then `ty` definitely has no destructor (i.e. no drop glue).
use middle::ty::{self, RegionEscape, Ty, ToPredicate, HasTypeFlags};
use middle::ty_fold;
use rscope::{self, UnelidableRscope, RegionScope, ElidableRscope, ExplicitRscope,
- ObjectLifetimeDefaultRscope, ShiftedRscope, BindingRscope};
+ ObjectLifetimeDefaultRscope, ShiftedRscope, BindingRscope,
+ ElisionFailureInfo, ElidedLifetime};
use util::common::{ErrorReported, FN_OUTPUT_NAME};
use util::nodemap::FnvHashSet;
item_name: ast::Name)
-> Ty<'tcx>
{
- if self.tcx().binds_late_bound_regions(&poly_trait_ref) {
+ if let Some(trait_ref) = self.tcx().no_late_bound_regions(&poly_trait_ref) {
+ self.projected_ty(span, trait_ref, item_name)
+ } else {
+ // no late-bound regions, we can just ignore the binder
span_err!(self.tcx().sess, span, E0212,
"cannot extract an associated type from a higher-ranked trait bound \
in this context");
self.tcx().types.err
- } else {
- // no late-bound regions, we can just ignore the binder
- self.projected_ty(span, poly_trait_ref.0.clone(), item_name)
}
}
r
}
+fn report_elision_failure(
+ tcx: &ty::ctxt,
+ default_span: Span,
+ params: Vec<ElisionFailureInfo>)
+{
+ let mut m = String::new();
+ let len = params.len();
+ for (i, info) in params.into_iter().enumerate() {
+ let ElisionFailureInfo {
+ name, lifetime_count: n, have_bound_regions
+ } = info;
+
+ let help_name = if name.is_empty() {
+ format!("argument {}", i + 1)
+ } else {
+ format!("`{}`", name)
+ };
+
+ m.push_str(&(if n == 1 {
+ help_name
+ } else {
+ format!("one of {}'s {} elided {}lifetimes", help_name, n,
+ if have_bound_regions { "free " } else { "" } )
+ })[..]);
+
+ if len == 2 && i == 0 {
+ m.push_str(" or ");
+ } else if i + 2 == len {
+ m.push_str(", or ");
+ } else if i + 1 != len {
+ m.push_str(", ");
+ }
+ }
+ if len == 1 {
+ fileline_help!(tcx.sess, default_span,
+ "this function's return type contains a borrowed value, but \
+ the signature does not say which {} it is borrowed from",
+ m);
+ } else if len == 0 {
+ fileline_help!(tcx.sess, default_span,
+ "this function's return type contains a borrowed value, but \
+ there is no value for it to be borrowed from");
+ fileline_help!(tcx.sess, default_span,
+ "consider giving it a 'static lifetime");
+ } else {
+ fileline_help!(tcx.sess, default_span,
+ "this function's return type contains a borrowed value, but \
+ the signature does not say whether it is borrowed from {}",
+ m);
+ }
+}
+
pub fn opt_ast_region_to_region<'tcx>(
this: &AstConv<'tcx>,
rscope: &RegionScope,
ast_region_to_region(this.tcx(), lifetime)
}
- None => {
- match rscope.anon_regions(default_span, 1) {
- Err(v) => {
- debug!("optional region in illegal location");
- span_err!(this.tcx().sess, default_span, E0106,
- "missing lifetime specifier");
- match v {
- Some(v) => {
- let mut m = String::new();
- let len = v.len();
- for (i, (name, n)) in v.into_iter().enumerate() {
- let help_name = if name.is_empty() {
- format!("argument {}", i + 1)
- } else {
- format!("`{}`", name)
- };
-
- m.push_str(&(if n == 1 {
- help_name
- } else {
- format!("one of {}'s {} elided lifetimes", help_name, n)
- })[..]);
-
- if len == 2 && i == 0 {
- m.push_str(" or ");
- } else if i + 2 == len {
- m.push_str(", or ");
- } else if i + 1 != len {
- m.push_str(", ");
- }
- }
- if len == 1 {
- fileline_help!(this.tcx().sess, default_span,
- "this function's return type contains a borrowed value, but \
- the signature does not say which {} it is borrowed from",
- m);
- } else if len == 0 {
- fileline_help!(this.tcx().sess, default_span,
- "this function's return type contains a borrowed value, but \
- there is no value for it to be borrowed from");
- fileline_help!(this.tcx().sess, default_span,
- "consider giving it a 'static lifetime");
- } else {
- fileline_help!(this.tcx().sess, default_span,
- "this function's return type contains a borrowed value, but \
- the signature does not say whether it is borrowed from {}",
- m);
- }
- }
- None => {},
- }
- ty::ReStatic
+ None => match rscope.anon_regions(default_span, 1) {
+ Ok(rs) => rs[0],
+ Err(params) => {
+ span_err!(this.tcx().sess, default_span, E0106,
+ "missing lifetime specifier");
+ if let Some(params) = params {
+ report_elision_failure(this.tcx(), default_span, params);
}
-
- Ok(rs) => rs[0],
+ ty::ReStatic
}
}
};
/// Returns the appropriate lifetime to use for any output lifetimes
/// (if one exists) and a vector of the (pattern, number of lifetimes)
/// corresponding to each input type/pattern.
-fn find_implied_output_region(input_tys: &[Ty], input_pats: Vec<String>)
- -> (Option<ty::Region>, Vec<(String, usize)>)
+fn find_implied_output_region<'tcx>(tcx: &ty::ctxt<'tcx>,
+ input_tys: &[Ty<'tcx>],
+ input_pats: Vec<String>) -> ElidedLifetime
{
- let mut lifetimes_for_params: Vec<(String, usize)> = Vec::new();
+ let mut lifetimes_for_params = Vec::new();
let mut possible_implied_output_region = None;
for (input_type, input_pat) in input_tys.iter().zip(input_pats) {
- let mut accumulator = Vec::new();
- input_type.accumulate_lifetimes_in_type(&mut accumulator);
+ let mut regions = FnvHashSet();
+ let have_bound_regions = ty_fold::collect_regions(tcx,
+ input_type,
+ &mut regions);
+
+ debug!("find_implied_output_regions: collected {:?} from {:?} \
+ have_bound_regions={:?}", ®ions, input_type, have_bound_regions);
- if accumulator.len() == 1 {
+ if regions.len() == 1 {
// there's a chance that the unique lifetime of this
// iteration will be the appropriate lifetime for output
// parameters, so lets store it.
- possible_implied_output_region = Some(accumulator[0])
+ possible_implied_output_region = regions.iter().cloned().next();
}
- lifetimes_for_params.push((input_pat, accumulator.len()));
+ lifetimes_for_params.push(ElisionFailureInfo {
+ name: input_pat,
+ lifetime_count: regions.len(),
+ have_bound_regions: have_bound_regions
+ });
}
- let implied_output_region =
- if lifetimes_for_params.iter().map(|&(_, n)| n).sum::<usize>() == 1 {
- assert!(possible_implied_output_region.is_some());
- possible_implied_output_region
- } else {
- None
- };
- (implied_output_region, lifetimes_for_params)
+ if lifetimes_for_params.iter().map(|e| e.lifetime_count).sum::<usize>() == 1 {
+ Ok(possible_implied_output_region.unwrap())
+ } else {
+ Err(Some(lifetimes_for_params))
+ }
}
fn convert_ty_with_lifetime_elision<'tcx>(this: &AstConv<'tcx>,
- implied_output_region: Option<ty::Region>,
- param_lifetimes: Vec<(String, usize)>,
+ elided_lifetime: ElidedLifetime,
ty: &ast::Ty)
-> Ty<'tcx>
{
- match implied_output_region {
- Some(implied_output_region) => {
+ match elided_lifetime {
+ Ok(implied_output_region) => {
let rb = ElidableRscope::new(implied_output_region);
ast_ty_to_ty(this, &rb, ty)
}
- None => {
+ Err(param_lifetimes) => {
// All regions must be explicitly specified in the output
// if the lifetime elision rules do not apply. This saves
// the user from potentially-confusing errors.
.collect::<Vec<Ty<'tcx>>>();
let input_params: Vec<_> = repeat(String::new()).take(inputs.len()).collect();
- let (implied_output_region,
- params_lifetimes) = find_implied_output_region(&*inputs, input_params);
+ let implied_output_region = find_implied_output_region(this.tcx(), &inputs, input_params);
let input_ty = this.tcx().mk_tup(inputs);
Some(ref output_ty) => {
(convert_ty_with_lifetime_elision(this,
implied_output_region,
- params_lifetimes,
- &**output_ty),
+ &output_ty),
output_ty.span)
}
None => {
// here), if self is by-reference, then the implied output region is the
// region of the self parameter.
let mut explicit_self_category_result = None;
- let (self_ty, mut implied_output_region) = match opt_self_info {
+ let (self_ty, implied_output_region) = match opt_self_info {
None => (None, None),
Some(self_info) => {
// This type comes from an impl or trait; no late-bound
// Second, if there was exactly one lifetime (either a substitution or a
// reference) in the arguments, then any anonymous regions in the output
// have that lifetime.
- let lifetimes_for_params = if implied_output_region.is_none() {
- let input_tys = if self_ty.is_some() {
- // Skip the first argument if `self` is present.
- &self_and_input_tys[1..]
- } else {
- &self_and_input_tys[..]
- };
+ let implied_output_region = match implied_output_region {
+ Some(r) => Ok(r),
+ None => {
+ let input_tys = if self_ty.is_some() {
+ // Skip the first argument if `self` is present.
+ &self_and_input_tys[1..]
+ } else {
+ &self_and_input_tys[..]
+ };
- let (ior, lfp) = find_implied_output_region(input_tys, input_pats);
- implied_output_region = ior;
- lfp
- } else {
- vec![]
+ find_implied_output_region(this.tcx(), input_tys, input_pats)
+ }
};
let output_ty = match decl.output {
ast::Return(ref output) =>
ty::FnConverging(convert_ty_with_lifetime_elision(this,
implied_output_region,
- lifetimes_for_params,
- &**output)),
+ &output)),
ast::DefaultReturn(..) => ty::FnConverging(this.tcx().mk_nil()),
ast::NoReturn(..) => ty::FnDiverging
};
use middle::ty::{self, HasTypeFlags, RegionEscape, ToPolyTraitRef, Ty};
use middle::ty::{MethodCall, MethodCallee};
use middle::ty_fold::{TypeFolder, TypeFoldable};
-use rscope::RegionScope;
+use rscope::{ElisionFailureInfo, RegionScope};
use session::Session;
use {CrateCtxt, lookup_full_def, require_same_types};
use TypeAndSubsts;
}
fn anon_regions(&self, span: Span, count: usize)
- -> Result<Vec<ty::Region>, Option<Vec<(String, usize)>>> {
+ -> Result<Vec<ty::Region>, Option<Vec<ElisionFailureInfo>>> {
Ok((0..count).map(|_| {
self.infcx().next_region_var(infer::MiscVariable(span))
}).collect())
result
}
+fn convert_default_type_parameter<'a, 'tcx>(ccx: &CrateCtxt<'a, 'tcx>,
+ path: &P<ast::Ty>,
+ space: ParamSpace,
+ index: u32)
+ -> Ty<'tcx>
+{
+ let ty = ast_ty_to_ty(&ccx.icx(&()), &ExplicitRscope, &path);
+
+ for leaf_ty in ty.walk() {
+ if let ty::TyParam(p) = leaf_ty.sty {
+ if p.space == space && p.idx >= index {
+ span_err!(ccx.tcx.sess, path.span, E0128,
+ "type parameters with a default cannot use \
+ forward declared identifiers");
+
+ return ccx.tcx.types.err
+ }
+ }
+ }
+
+ ty
+}
+
fn get_or_create_type_parameter_def<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
ast_generics: &ast::Generics,
space: ParamSpace,
None => { }
}
- let default = match param.default {
- None => None,
- Some(ref path) => {
- let ty = ast_ty_to_ty(&ccx.icx(&()), &ExplicitRscope, &**path);
- let cur_idx = index;
-
- for leaf_ty in ty.walk() {
- if let ty::TyParam(p) = leaf_ty.sty {
- if p.idx > cur_idx {
- span_err!(tcx.sess, path.span, E0128,
- "type parameters with a default cannot use \
- forward declared identifiers");
- }
- }
- }
-
- Some(ty)
- }
- };
+ let default = param.default.as_ref().map(
+ |def| convert_default_type_parameter(ccx, def, space, index)
+ );
let object_lifetime_default =
compute_object_lifetime_default(ccx, param.id,
* before we really have a `ParameterEnvironment` to check.
*/
- ty_fold::fold_regions(tcx, value, |region, _| {
+ ty_fold::fold_regions(tcx, value, &mut false, |region, _| {
match region {
ty::ReEarlyBound(data) => {
let def_id = local_def(data.param_id);
use std::iter::repeat;
use syntax::codemap::Span;
+#[derive(Clone)]
+pub struct ElisionFailureInfo {
+ pub name: String,
+ pub lifetime_count: usize,
+ pub have_bound_regions: bool
+}
+
+pub type ElidedLifetime = Result<ty::Region, Option<Vec<ElisionFailureInfo>>>;
+
/// Defines strategies for handling regions that are omitted. For
/// example, if one writes the type `&Foo`, then the lifetime of
/// this reference has been omitted. When converting this
fn anon_regions(&self,
span: Span,
count: usize)
- -> Result<Vec<ty::Region>, Option<Vec<(String, usize)>>>;
+ -> Result<Vec<ty::Region>, Option<Vec<ElisionFailureInfo>>>;
/// If an object omits any explicit lifetime bound, and none can
/// be derived from the object traits, what should we use? If
fn anon_regions(&self,
_span: Span,
_count: usize)
- -> Result<Vec<ty::Region>, Option<Vec<(String, usize)>>> {
+ -> Result<Vec<ty::Region>, Option<Vec<ElisionFailureInfo>>> {
Err(None)
}
}
// Same as `ExplicitRscope`, but provides some extra information for diagnostics
-pub struct UnelidableRscope(Vec<(String, usize)>);
+pub struct UnelidableRscope(Option<Vec<ElisionFailureInfo>>);
impl UnelidableRscope {
- pub fn new(v: Vec<(String, usize)>) -> UnelidableRscope {
+ pub fn new(v: Option<Vec<ElisionFailureInfo>>) -> UnelidableRscope {
UnelidableRscope(v)
}
}
fn anon_regions(&self,
_span: Span,
_count: usize)
- -> Result<Vec<ty::Region>, Option<Vec<(String, usize)>>> {
+ -> Result<Vec<ty::Region>, Option<Vec<ElisionFailureInfo>>> {
let UnelidableRscope(ref v) = *self;
- Err(Some(v.clone()))
+ Err(v.clone())
}
}
fn anon_regions(&self,
_span: Span,
count: usize)
- -> Result<Vec<ty::Region>, Option<Vec<(String, usize)>>>
+ -> Result<Vec<ty::Region>, Option<Vec<ElisionFailureInfo>>>
{
Ok(repeat(self.default).take(count).collect())
}
fn anon_regions(&self,
_: Span,
count: usize)
- -> Result<Vec<ty::Region>, Option<Vec<(String, usize)>>>
+ -> Result<Vec<ty::Region>, Option<Vec<ElisionFailureInfo>>>
{
Ok((0..count).map(|_| self.next_region()).collect())
}
fn anon_regions(&self,
span: Span,
count: usize)
- -> Result<Vec<ty::Region>, Option<Vec<(String, usize)>>>
+ -> Result<Vec<ty::Region>, Option<Vec<ElisionFailureInfo>>>
{
self.base_scope.anon_regions(span, count)
}
fn anon_regions(&self,
span: Span,
count: usize)
- -> Result<Vec<ty::Region>, Option<Vec<(String, usize)>>>
+ -> Result<Vec<ty::Region>, Option<Vec<ElisionFailureInfo>>>
{
match self.base_scope.anon_regions(span, count) {
Ok(mut v) => {
--- /dev/null
+// 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 <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.
+
+pub struct Foo<Bar=Bar>; //~ ERROR E0128
+pub struct Baz(Foo);
+fn main() {}
--- /dev/null
+// 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 <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.
+
+fn parse_type(iter: Box<Iterator<Item=&str>+'static>) -> &str { iter.next() }
+//~^ ERROR missing lifetime specifier [E0106]
+//~^^ HELP 2 elided lifetimes
+
+fn parse_type_2(iter: fn(&u8)->&u8) -> &str { iter() }
+//~^ ERROR missing lifetime specifier [E0106]
+//~^^ HELP 0 elided free lifetimes
+
+fn main() {}
}
}
+fn all_fine() {
+ let _f = all_fine;
+}
+
+// issue 26333
+trait Bar {
+ fn method<T: Bar>(&self, x: &T) {
+ x.method(x)
+ }
+}
+
fn main() {}
--- /dev/null
+// 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 <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.
+
+const JSVAL_TAG_CLEAR: u32 = 0xFFFFFF80;
+const JSVAL_TYPE_INT32: u8 = 0x01;
+const JSVAL_TYPE_UNDEFINED: u8 = 0x02;
+#[repr(u32)]
+enum ValueTag {
+ JSVAL_TAG_INT32 = JSVAL_TAG_CLEAR | (JSVAL_TYPE_INT32 as u32),
+ JSVAL_TAG_UNDEFINED = JSVAL_TAG_CLEAR | (JSVAL_TYPE_UNDEFINED as u32),
+}
+
+fn main() {
+ let _ = ValueTag::JSVAL_TAG_INT32;
+}
--- /dev/null
+// 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 <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.
+
+const count : usize = 2 as usize;
+fn main() {
+ let larger : [usize; count*2];
+}
--- /dev/null
+// 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 <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(const_fn)]
+
+// Check that the destructors of simple enums are run on unwinding
+
+use std::sync::atomic::{Ordering, AtomicUsize};
+use std::thread;
+
+static LOG: AtomicUsize = AtomicUsize::new(0);
+
+enum WithDtor { Val }
+impl Drop for WithDtor {
+ fn drop(&mut self) {
+ LOG.store(LOG.load(Ordering::SeqCst)+1,Ordering::SeqCst);
+ }
+}
+
+pub fn main() {
+ thread::spawn(move|| {
+ let _e: WithDtor = WithDtor::Val;
+ panic!("fail");
+ }).join().unwrap_err();
+
+ assert_eq!(LOG.load(Ordering::SeqCst), 1);
+}