<commit_before>use rustc::mir::*;
use rustc::ty::{self, adjustment::PointerCast, Predicate, Ty, TyCtxt};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
use std::borrow::Cow;
use syntax::attr;

type McfResult = Result<(), (Span, Cow<'static, str>)>;

pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) -> McfResult {
    let mut current = def_id;
    loop {
        let predicates = tcx.predicates_of(current);
        for (predicate, _) in predicates.predicates {
            match predicate {
                Predicate::RegionOutlives(_)
                | Predicate::TypeOutlives(_)
                | Predicate::WellFormed(_)
                | Predicate::Projection(_)
                | Predicate::ConstEvaluatable(..) => continue,
                Predicate::ObjectSafe(_) => {
                    bug!("object safe predicate on function: {:#?}", predicate)
                }
                Predicate::ClosureKind(..) => {
                    bug!("closure kind predicate on function: {:#?}", predicate)
                }
                Predicate::Subtype(_) => bug!("subtype predicate on function: {:#?}", predicate),
                Predicate::Trait(pred) => {
                    if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
                        continue;
                    }
                    match pred.skip_binder().self_ty().kind {
                        ty::Param(ref p) => {
                            let generics = tcx.generics_of(current);
                            let def = generics.type_param(p, tcx);
                            let span = tcx.def_span(def.def_id);
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ));
                        }
                        // other kinds of bounds are either tautologies
                        // or cause errors in other passes
                        _ => continue,
                    }
                }
            }
        }
        match predicates.parent {
            Some(parent) => current = parent,
            None => break,
        }
    }

    for local in &body.local_decls {
        check_ty(tcx, local.ty, local.source_info.span, def_id)?;
    }
    // impl trait is gone in MIR, so check the return type manually
    check_ty(
        tcx,
        tcx.fn_sig(def_id).output().skip_binder(),
        body.local_decls.iter().next().unwrap().source_info.span,
        def_id,
    )?;

    for bb in body.basic_blocks() {
        check_terminator(tcx, body, def_id, bb.terminator())?;
        for stmt in &bb.statements {
            check_statement(tcx, body, def_id, stmt)?;
        }
    }
    Ok(())
}

fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, fn_def_id: DefId) -> McfResult {
    for ty in ty.walk() {
        match ty.kind {
            ty::Ref(_, _, hir::Mutability::Mut) => {
                if !feature_allowed(tcx, fn_def_id, sym::const_mut_refs) {
                    return Err((span, "mutable references in const fn are unstable".into()));
                }
            }
            ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
            ty::FnPtr(..) => {
                if !tcx.const_fn_is_allowed_fn_ptr(fn_def_id) {
                    return Err((span, "function pointers in const fn are unstable".into()));
                }
            }
            ty::Dynamic(preds, _) => {
                for pred in preds.iter() {
                    match pred.skip_binder() {
                        ty::ExistentialPredicate::AutoTrait(_)
                        | ty::ExistentialPredicate::Projection(_) => {
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ));
                        }
                        ty::ExistentialPredicate::Trait(trait_ref) => {
                            if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
                                return Err((
                                    span,
                                    "trait bounds other than `Sized` \
                                     on const fn parameters are unstable"
                                        .into(),
                                ));
                            }
                        }
                    }
                }
            }
            _ => {}
        }
    }
    Ok(())
}

fn check_rvalue(
    tcx: TyCtxt<'tcx>,
    body: &Body<'tcx>,
    def_id: DefId,
    rvalue: &Rvalue<'tcx>,
    span: Span,
) -> McfResult {
    match rvalue {
        Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => {
            check_operand(tcx, operand, span, def_id, body)
        }
        Rvalue::Len(place)
        | Rvalue::Discriminant(place)
        | Rvalue::Ref(_, _, place)
        | Rvalue::AddressOf(_, place) => check_place(tcx, place, span, def_id, body),
        Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
            use rustc::ty::cast::CastTy;
            let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast");
            let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
            match (cast_in, cast_out) {
                (CastTy::Ptr(_), CastTy::Int(_)) | (CastTy::FnPtr, CastTy::Int(_)) => {
                    Err((span, "casting pointers to ints is unstable in const fn".into()))
                }
                _ => check_operand(tcx, operand, span, def_id, body),
            }
        }
        Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer), operand, _)
        | Rvalue::Cast(CastKind::Pointer(PointerCast::ArrayToPointer), operand, _) => {
            check_operand(tcx, operand, span, def_id, body)
        }
        Rvalue::Cast(CastKind::Pointer(PointerCast::UnsafeFnPointer), _, _)
        | Rvalue::Cast(CastKind::Pointer(PointerCast::ClosureFnPointer(_)), _, _)
        | Rvalue::Cast(CastKind::Pointer(PointerCast::ReifyFnPointer), _, _) => {
            Err((span, "function pointer casts are not allowed in const fn".into()))
        }
        Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), _, _) => {
            Err((span, "unsizing casts are not allowed in const fn".into()))
        }
        // binops are fine on integers
        Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
            check_operand(tcx, lhs, span, def_id, body)?;
            check_operand(tcx, rhs, span, def_id, body)?;
            let ty = lhs.ty(body, tcx);
            if ty.is_integral() || ty.is_bool() || ty.is_char() {
                Ok(())
            } else {
                Err((span, "only int, `bool` and `char` operations are stable in const fn".into()))
            }
        }
        Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()),
        Rvalue::NullaryOp(NullOp::Box, _) => {
            Err((span, "heap allocations are not allowed in const fn".into()))
        }
        Rvalue::UnaryOp(_, operand) => {
            let ty = operand.ty(body, tcx);
            if ty.is_integral() || ty.is_bool() {
                check_operand(tcx, operand, span, def_id, body)
            } else {
                Err((span, "only int and `bool` operations are stable in const fn".into()))
            }
        }
        Rvalue::Aggregate(_, operands) => {
            for operand in operands {
                check_operand(tcx, operand, span, def_id, body)?;
            }
            Ok(())
        }
    }
}

fn check_statement(
    tcx: TyCtxt<'tcx>,
    body: &Body<'tcx>,
    def_id: DefId,
    statement: &Statement<'tcx>,
) -> McfResult {
    let span = statement.source_info.span;
    match &statement.kind {
        StatementKind::Assign(box (place, rval)) => {
            check_place(tcx, place, span, def_id, body)?;
            check_rvalue(tcx, body, def_id, rval, span)
        }

        StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _)
            if !feature_allowed(tcx, def_id, sym::const_if_match) =>
        {
            Err((span, "loops and conditional expressions are not stable in const fn".into()))
        }

        StatementKind::FakeRead(_, place) => check_place(tcx, place, span, def_id, body),

        // just an assignment
        StatementKind::SetDiscriminant { place, .. } => check_place(tcx, place, span, def_id, body),

        StatementKind::InlineAsm { .. } => {
            Err((span, "cannot use inline assembly in const fn".into()))
        }

        // These are all NOPs
        StatementKind::StorageLive(_)
        | StatementKind::StorageDead(_)
        | StatementKind::Retag { .. }
        | StatementKind::AscribeUserType(..)
        | StatementKind::Nop => Ok(()),
    }
}

fn check_operand(
    tcx: TyCtxt<'tcx>,
    operand: &Operand<'tcx>,
    span: Span,
    def_id: DefId,
    body: &Body<'tcx>,
) -> McfResult {
    match operand {
        Operand::Move(place) | Operand::Copy(place) => check_place(tcx, place, span, def_id, body),
        Operand::Constant(c) => match c.check_static_ptr(tcx) {
            Some(_) => Err((span, "cannot access `static` items in const fn".into())),
            None => Ok(()),
        },
    }
}

fn check_place(
    tcx: TyCtxt<'tcx>,
    place: &Place<'tcx>,
    span: Span,
    def_id: DefId,
    body: &Body<'tcx>,
) -> McfResult {
    let mut cursor = place.projection.as_ref();
    while let &[ref proj_base @ .., elem] = cursor {
        cursor = proj_base;
        match elem {
            ProjectionElem::Downcast(..) if !feature_allowed(tcx, def_id, sym::const_if_match) => {
                return Err((span, "`match` or `if let` in `const fn` is unstable".into()));
            }
            ProjectionElem::Downcast(_symbol, _variant_index) => {}

            ProjectionElem::Field(..) => {
                let base_ty = Place::ty_from(&place.local, &proj_base, body, tcx).ty;
                if let Some(def) = base_ty.ty_adt_def() {
                    // No union field accesses in `const fn`
                    if def.is_union() {
                        if !feature_allowed(tcx, def_id, sym::const_fn_union) {
                            return Err((span, "accessing union fields is unstable".into()));
                        }
                    }
                }
            }
            ProjectionElem::ConstantIndex { .. }
            | ProjectionElem::Subslice { .. }
            | ProjectionElem::Deref
            | ProjectionElem::Index(_) => {}
        }
    }

    Ok(())
}

/// Returns `true` if the given feature gate is allowed within the function with the given `DefId`.
fn feature_allowed(tcx: TyCtxt<'tcx>, def_id: DefId, feature_gate: Symbol) -> bool {
    // All features require that the corresponding gate be enabled,
    // even if the function has `#[allow_internal_unstable(the_gate)]`.
    if !tcx.features().enabled(feature_gate) {
        return false;
    }

    // If this crate is not using stability attributes, or this function is not claiming to be a
    // stable `const fn`, that is all that is required.
    if !tcx.features().staged_api || tcx.has_attr(def_id, sym::rustc_const_unstable) {
        return true;
    }

    // However, we cannot allow stable `const fn`s to use unstable features without an explicit
    // opt-in via `allow_internal_unstable`.
    attr::allow_internal_unstable(&tcx.get_attrs(def_id), &tcx.sess.diagnostic())
        .map_or(false, |mut features| features.any(|name| name == feature_gate))
}

fn check_terminator(
    tcx: TyCtxt<'tcx>,
    body: &'a Body<'tcx>,
    def_id: DefId,
    terminator: &Terminator<'tcx>,
) -> McfResult {
    let span = terminator.source_info.span;
    match &terminator.kind {
        TerminatorKind::Goto { .. } | TerminatorKind::Return | TerminatorKind::Resume => Ok(()),

        TerminatorKind::Drop { location, .. } => check_place(tcx, location, span, def_id, body),
        TerminatorKind::DropAndReplace { location, value, .. } => {
            check_place(tcx, location, span, def_id, body)?;
            check_operand(tcx, value, span, def_id, body)
        }

        TerminatorKind::FalseEdges { .. } | TerminatorKind::SwitchInt { .. }
            if !feature_allowed(tcx, def_id, sym::const_if_match) =>
        {
            Err((span, "loops and conditional expressions are not stable in const fn".into()))
        }

        TerminatorKind::FalseEdges { .. } => Ok(()),
        TerminatorKind::SwitchInt { discr, switch_ty: _, values: _, targets: _ } => {
            check_operand(tcx, discr, span, def_id, body)
        }

        // FIXME(ecstaticmorse): We probably want to allow `Unreachable` unconditionally.
        TerminatorKind::Unreachable if feature_allowed(tcx, def_id, sym::const_if_match) => Ok(()),

        TerminatorKind::Abort | TerminatorKind::Unreachable => {
            Err((span, "const fn with unreachable code is not stable".into()))
        }
        TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
            Err((span, "const fn generators are unstable".into()))
        }

        TerminatorKind::Call { func, args, from_hir_call: _, destination: _, cleanup: _ } => {
            let fn_ty = func.ty(body, tcx);
            if let ty::FnDef(def_id, _) = fn_ty.kind {
                if !crate::const_eval::is_min_const_fn(tcx, def_id) {
                    return Err((
                        span,
                        format!(
                            "can only call other `const fn` within a `const fn`, \
                             but `{:?}` is not stable as `const fn`",
                            func,
                        )
                        .into(),
                    ));
                }

                check_operand(tcx, func, span, def_id, body)?;

                for arg in args {
                    check_operand(tcx, arg, span, def_id, body)?;
                }
                Ok(())
            } else {
                Err((span, "can only call other const fns within const fn".into()))
            }
        }

        TerminatorKind::Assert { cond, expected: _, msg: _, target: _, cleanup: _ } => {
            check_operand(tcx, cond, span, def_id, body)
        }

        TerminatorKind::FalseUnwind { .. } if feature_allowed(tcx, def_id, sym::const_loop) => {
            Ok(())
        }

        TerminatorKind::FalseUnwind { .. } => {
            Err((span, "loops are not allowed in const fn".into()))
        }
    }
}
<commit_msg>Stop treating `FalseEdges` and `FalseUnwind` as having semantic value for const eval<commit_after>use rustc::mir::*;
use rustc::ty::{self, adjustment::PointerCast, Predicate, Ty, TyCtxt};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
use std::borrow::Cow;
use syntax::attr;

type McfResult = Result<(), (Span, Cow<'static, str>)>;

pub fn is_min_const_fn(tcx: TyCtxt<'tcx>, def_id: DefId, body: &'a Body<'tcx>) -> McfResult {
    let mut current = def_id;
    loop {
        let predicates = tcx.predicates_of(current);
        for (predicate, _) in predicates.predicates {
            match predicate {
                Predicate::RegionOutlives(_)
                | Predicate::TypeOutlives(_)
                | Predicate::WellFormed(_)
                | Predicate::Projection(_)
                | Predicate::ConstEvaluatable(..) => continue,
                Predicate::ObjectSafe(_) => {
                    bug!("object safe predicate on function: {:#?}", predicate)
                }
                Predicate::ClosureKind(..) => {
                    bug!("closure kind predicate on function: {:#?}", predicate)
                }
                Predicate::Subtype(_) => bug!("subtype predicate on function: {:#?}", predicate),
                Predicate::Trait(pred) => {
                    if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
                        continue;
                    }
                    match pred.skip_binder().self_ty().kind {
                        ty::Param(ref p) => {
                            let generics = tcx.generics_of(current);
                            let def = generics.type_param(p, tcx);
                            let span = tcx.def_span(def.def_id);
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ));
                        }
                        // other kinds of bounds are either tautologies
                        // or cause errors in other passes
                        _ => continue,
                    }
                }
            }
        }
        match predicates.parent {
            Some(parent) => current = parent,
            None => break,
        }
    }

    for local in &body.local_decls {
        check_ty(tcx, local.ty, local.source_info.span, def_id)?;
    }
    // impl trait is gone in MIR, so check the return type manually
    check_ty(
        tcx,
        tcx.fn_sig(def_id).output().skip_binder(),
        body.local_decls.iter().next().unwrap().source_info.span,
        def_id,
    )?;

    for bb in body.basic_blocks() {
        check_terminator(tcx, body, def_id, bb.terminator())?;
        for stmt in &bb.statements {
            check_statement(tcx, body, def_id, stmt)?;
        }
    }
    Ok(())
}

fn check_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span, fn_def_id: DefId) -> McfResult {
    for ty in ty.walk() {
        match ty.kind {
            ty::Ref(_, _, hir::Mutability::Mut) => {
                if !feature_allowed(tcx, fn_def_id, sym::const_mut_refs) {
                    return Err((span, "mutable references in const fn are unstable".into()));
                }
            }
            ty::Opaque(..) => return Err((span, "`impl Trait` in const fn is unstable".into())),
            ty::FnPtr(..) => {
                if !tcx.const_fn_is_allowed_fn_ptr(fn_def_id) {
                    return Err((span, "function pointers in const fn are unstable".into()));
                }
            }
            ty::Dynamic(preds, _) => {
                for pred in preds.iter() {
                    match pred.skip_binder() {
                        ty::ExistentialPredicate::AutoTrait(_)
                        | ty::ExistentialPredicate::Projection(_) => {
                            return Err((
                                span,
                                "trait bounds other than `Sized` \
                                 on const fn parameters are unstable"
                                    .into(),
                            ));
                        }
                        ty::ExistentialPredicate::Trait(trait_ref) => {
                            if Some(trait_ref.def_id) != tcx.lang_items().sized_trait() {
                                return Err((
                                    span,
                                    "trait bounds other than `Sized` \
                                     on const fn parameters are unstable"
                                        .into(),
                                ));
                            }
                        }
                    }
                }
            }
            _ => {}
        }
    }
    Ok(())
}

fn check_rvalue(
    tcx: TyCtxt<'tcx>,
    body: &Body<'tcx>,
    def_id: DefId,
    rvalue: &Rvalue<'tcx>,
    span: Span,
) -> McfResult {
    match rvalue {
        Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => {
            check_operand(tcx, operand, span, def_id, body)
        }
        Rvalue::Len(place)
        | Rvalue::Discriminant(place)
        | Rvalue::Ref(_, _, place)
        | Rvalue::AddressOf(_, place) => check_place(tcx, place, span, def_id, body),
        Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
            use rustc::ty::cast::CastTy;
            let cast_in = CastTy::from_ty(operand.ty(body, tcx)).expect("bad input type for cast");
            let cast_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
            match (cast_in, cast_out) {
                (CastTy::Ptr(_), CastTy::Int(_)) | (CastTy::FnPtr, CastTy::Int(_)) => {
                    Err((span, "casting pointers to ints is unstable in const fn".into()))
                }
                _ => check_operand(tcx, operand, span, def_id, body),
            }
        }
        Rvalue::Cast(CastKind::Pointer(PointerCast::MutToConstPointer), operand, _)
        | Rvalue::Cast(CastKind::Pointer(PointerCast::ArrayToPointer), operand, _) => {
            check_operand(tcx, operand, span, def_id, body)
        }
        Rvalue::Cast(CastKind::Pointer(PointerCast::UnsafeFnPointer), _, _)
        | Rvalue::Cast(CastKind::Pointer(PointerCast::ClosureFnPointer(_)), _, _)
        | Rvalue::Cast(CastKind::Pointer(PointerCast::ReifyFnPointer), _, _) => {
            Err((span, "function pointer casts are not allowed in const fn".into()))
        }
        Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), _, _) => {
            Err((span, "unsizing casts are not allowed in const fn".into()))
        }
        // binops are fine on integers
        Rvalue::BinaryOp(_, lhs, rhs) | Rvalue::CheckedBinaryOp(_, lhs, rhs) => {
            check_operand(tcx, lhs, span, def_id, body)?;
            check_operand(tcx, rhs, span, def_id, body)?;
            let ty = lhs.ty(body, tcx);
            if ty.is_integral() || ty.is_bool() || ty.is_char() {
                Ok(())
            } else {
                Err((span, "only int, `bool` and `char` operations are stable in const fn".into()))
            }
        }
        Rvalue::NullaryOp(NullOp::SizeOf, _) => Ok(()),
        Rvalue::NullaryOp(NullOp::Box, _) => {
            Err((span, "heap allocations are not allowed in const fn".into()))
        }
        Rvalue::UnaryOp(_, operand) => {
            let ty = operand.ty(body, tcx);
            if ty.is_integral() || ty.is_bool() {
                check_operand(tcx, operand, span, def_id, body)
            } else {
                Err((span, "only int and `bool` operations are stable in const fn".into()))
            }
        }
        Rvalue::Aggregate(_, operands) => {
            for operand in operands {
                check_operand(tcx, operand, span, def_id, body)?;
            }
            Ok(())
        }
    }
}

fn check_statement(
    tcx: TyCtxt<'tcx>,
    body: &Body<'tcx>,
    def_id: DefId,
    statement: &Statement<'tcx>,
) -> McfResult {
    let span = statement.source_info.span;
    match &statement.kind {
        StatementKind::Assign(box (place, rval)) => {
            check_place(tcx, place, span, def_id, body)?;
            check_rvalue(tcx, body, def_id, rval, span)
        }

        StatementKind::FakeRead(FakeReadCause::ForMatchedPlace, _)
            if !feature_allowed(tcx, def_id, sym::const_if_match) =>
        {
            Err((span, "loops and conditional expressions are not stable in const fn".into()))
        }

        StatementKind::FakeRead(_, place) => check_place(tcx, place, span, def_id, body),

        // just an assignment
        StatementKind::SetDiscriminant { place, .. } => check_place(tcx, place, span, def_id, body),

        StatementKind::InlineAsm { .. } => {
            Err((span, "cannot use inline assembly in const fn".into()))
        }

        // These are all NOPs
        StatementKind::StorageLive(_)
        | StatementKind::StorageDead(_)
        | StatementKind::Retag { .. }
        | StatementKind::AscribeUserType(..)
        | StatementKind::Nop => Ok(()),
    }
}

fn check_operand(
    tcx: TyCtxt<'tcx>,
    operand: &Operand<'tcx>,
    span: Span,
    def_id: DefId,
    body: &Body<'tcx>,
) -> McfResult {
    match operand {
        Operand::Move(place) | Operand::Copy(place) => check_place(tcx, place, span, def_id, body),
        Operand::Constant(c) => match c.check_static_ptr(tcx) {
            Some(_) => Err((span, "cannot access `static` items in const fn".into())),
            None => Ok(()),
        },
    }
}

fn check_place(
    tcx: TyCtxt<'tcx>,
    place: &Place<'tcx>,
    span: Span,
    def_id: DefId,
    body: &Body<'tcx>,
) -> McfResult {
    let mut cursor = place.projection.as_ref();
    while let &[ref proj_base @ .., elem] = cursor {
        cursor = proj_base;
        match elem {
            ProjectionElem::Downcast(..) if !feature_allowed(tcx, def_id, sym::const_if_match) => {
                return Err((span, "`match` or `if let` in `const fn` is unstable".into()));
            }
            ProjectionElem::Downcast(_symbol, _variant_index) => {}

            ProjectionElem::Field(..) => {
                let base_ty = Place::ty_from(&place.local, &proj_base, body, tcx).ty;
                if let Some(def) = base_ty.ty_adt_def() {
                    // No union field accesses in `const fn`
                    if def.is_union() {
                        if !feature_allowed(tcx, def_id, sym::const_fn_union) {
                            return Err((span, "accessing union fields is unstable".into()));
                        }
                    }
                }
            }
            ProjectionElem::ConstantIndex { .. }
            | ProjectionElem::Subslice { .. }
            | ProjectionElem::Deref
            | ProjectionElem::Index(_) => {}
        }
    }

    Ok(())
}

/// Returns `true` if the given feature gate is allowed within the function with the given `DefId`.
fn feature_allowed(tcx: TyCtxt<'tcx>, def_id: DefId, feature_gate: Symbol) -> bool {
    // All features require that the corresponding gate be enabled,
    // even if the function has `#[allow_internal_unstable(the_gate)]`.
    if !tcx.features().enabled(feature_gate) {
        return false;
    }

    // If this crate is not using stability attributes, or this function is not claiming to be a
    // stable `const fn`, that is all that is required.
    if !tcx.features().staged_api || tcx.has_attr(def_id, sym::rustc_const_unstable) {
        return true;
    }

    // However, we cannot allow stable `const fn`s to use unstable features without an explicit
    // opt-in via `allow_internal_unstable`.
    attr::allow_internal_unstable(&tcx.get_attrs(def_id), &tcx.sess.diagnostic())
        .map_or(false, |mut features| features.any(|name| name == feature_gate))
}

fn check_terminator(
    tcx: TyCtxt<'tcx>,
    body: &'a Body<'tcx>,
    def_id: DefId,
    terminator: &Terminator<'tcx>,
) -> McfResult {
    let span = terminator.source_info.span;
    match &terminator.kind {
        TerminatorKind::FalseEdges { .. }
        | TerminatorKind::FalseUnwind { .. }
        | TerminatorKind::Goto { .. }
        | TerminatorKind::Return
        | TerminatorKind::Resume => Ok(()),

        TerminatorKind::Drop { location, .. } => check_place(tcx, location, span, def_id, body),
        TerminatorKind::DropAndReplace { location, value, .. } => {
            check_place(tcx, location, span, def_id, body)?;
            check_operand(tcx, value, span, def_id, body)
        }

        TerminatorKind::SwitchInt { .. } if !feature_allowed(tcx, def_id, sym::const_if_match) => {
            Err((span, "loops and conditional expressions are not stable in const fn".into()))
        }

        TerminatorKind::SwitchInt { discr, switch_ty: _, values: _, targets: _ } => {
            check_operand(tcx, discr, span, def_id, body)
        }

        // FIXME(ecstaticmorse): We probably want to allow `Unreachable` unconditionally.
        TerminatorKind::Unreachable if feature_allowed(tcx, def_id, sym::const_if_match) => Ok(()),

        TerminatorKind::Abort | TerminatorKind::Unreachable => {
            Err((span, "const fn with unreachable code is not stable".into()))
        }
        TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
            Err((span, "const fn generators are unstable".into()))
        }

        TerminatorKind::Call { func, args, from_hir_call: _, destination: _, cleanup: _ } => {
            let fn_ty = func.ty(body, tcx);
            if let ty::FnDef(def_id, _) = fn_ty.kind {
                if !crate::const_eval::is_min_const_fn(tcx, def_id) {
                    return Err((
                        span,
                        format!(
                            "can only call other `const fn` within a `const fn`, \
                             but `{:?}` is not stable as `const fn`",
                            func,
                        )
                        .into(),
                    ));
                }

                check_operand(tcx, func, span, def_id, body)?;

                for arg in args {
                    check_operand(tcx, arg, span, def_id, body)?;
                }
                Ok(())
            } else {
                Err((span, "can only call other const fns within const fn".into()))
            }
        }

        TerminatorKind::Assert { cond, expected: _, msg: _, target: _, cleanup: _ } => {
            check_operand(tcx, cond, span, def_id, body)
        }
    }
}
<|endoftext|>


<commit_before>pub mod anilist;
pub mod offline;

use crate::err::Result;
use serde_derive::{Deserialize, Serialize};
use std::borrow::Cow;
use std::fmt;

pub trait RemoteService: ScoreParser {
    fn search_info_by_name(&self, name: &str) -> Result<Vec<SeriesInfo>>;
    fn search_info_by_id(&self, id: u32) -> Result<SeriesInfo>;

    fn get_list_entry(&self, id: u32) -> Result<Option<SeriesEntry>>;
    fn update_list_entry(&self, entry: &SeriesEntry) -> Result<()>;

    fn is_offline(&self) -> bool {
        false
    }
}

pub trait ScoreParser {
    fn parse_score(&self, score: &str) -> Option<u8> {
        score
            .parse()
            .ok()
            .and_then(|score| if score <= 100 { Some(score) } else { None })
    }

    fn score_to_str(&self, score: u8) -> Cow<str> {
        Cow::Owned(score.to_string())
    }
}

pub type Minutes = u32;

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct SeriesInfo {
    pub id: u32,
    pub title: SeriesTitle,
    pub episodes: u32,
    pub episode_length: Minutes,
    pub sequel: Option<u32>,
}

impl<'a> Into<Cow<'a, SeriesInfo>> for SeriesInfo {
    fn into(self) -> Cow<'a, SeriesInfo> {
        Cow::Owned(self)
    }
}

impl<'a> Into<Cow<'a, SeriesInfo>> for &'a SeriesInfo {
    fn into(self) -> Cow<'a, SeriesInfo> {
        Cow::Borrowed(self)
    }
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct SeriesTitle {
    pub romaji: String,
    pub preferred: String,
}

#[derive(Debug, Deserialize, Serialize)]
pub struct SeriesEntry {
    pub id: u32,
    pub watched_eps: u32,
    pub score: Option<u8>,
    pub status: Status,
    pub times_rewatched: u32,
    pub start_date: Option<chrono::NaiveDate>,
    pub end_date: Option<chrono::NaiveDate>,
}

impl SeriesEntry {
    pub fn new(id: u32) -> SeriesEntry {
        SeriesEntry {
            id,
            watched_eps: 0,
            score: None,
            status: Status::default(),
            times_rewatched: 0,
            start_date: None,
            end_date: None,
        }
    }
}

#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub enum Status {
    Watching,
    Completed,
    OnHold,
    Dropped,
    PlanToWatch,
    Rewatching,
}

impl Default for Status {
    fn default() -> Status {
        Status::PlanToWatch
    }
}

impl fmt::Display for Status {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let value = match self {
            Status::Watching => "Watching",
            Status::Completed => "Completed",
            Status::OnHold => "On Hold",
            Status::Dropped => "Dropped",
            Status::PlanToWatch => "Plan To Watch",
            Status::Rewatching => "Rewatching",
        };

        write!(f, "{}", value)
    }
}
<commit_msg>anime: add documentation to remote module<commit_after>pub mod anilist;
pub mod offline;

use crate::err::Result;
use serde_derive::{Deserialize, Serialize};
use std::borrow::Cow;
use std::fmt;

/// Core functionality to interact with an anime tracking service.
pub trait RemoteService: ScoreParser {
    /// Search for an anime's information by title and return all of the matches.
    fn search_info_by_name(&self, name: &str) -> Result<Vec<SeriesInfo>>;

    /// Get an anime's information by its ID.
    /// Note that the ID will differ from service to service.
    fn search_info_by_id(&self, id: u32) -> Result<SeriesInfo>;

    /// Retrieve the anime list entry from the currently authenticated user.
    ///
    /// `id` is the ID of the anime, which differs from service to service.
    fn get_list_entry(&self, id: u32) -> Result<Option<SeriesEntry>>;

    /// Upload `entry` to the currently authenticated user's anime list.
    ///
    /// Please ensure that the `SeriesEntry` you are using comes from the current service
    /// in use, or you may overwrite a completely different list entry.
    fn update_list_entry(&self, entry: &SeriesEntry) -> Result<()>;

    /// Indicates whether or not this service is meant to be used without an internet connection.
    ///
    /// Returns false by default.
    fn is_offline(&self) -> bool {
        false
    }
}

/// Functionality to deal with scores from an anime tracking service.
pub trait ScoreParser {
    /// Parse the given `score` string to a u8 between 0 - 100.
    ///
    /// By default, it will simply map `score` to its equivalent u8 value.
    fn parse_score(&self, score: &str) -> Option<u8> {
        score
            .parse()
            .ok()
            .and_then(|score| if score <= 100 { Some(score) } else { None })
    }

    /// Map the given `score` to its string equivalent.
    ///
    /// By default, it will simply return `score` as a string.
    fn score_to_str(&self, score: u8) -> Cow<str> {
        Cow::Owned(score.to_string())
    }
}

pub type Minutes = u32;

/// General information for an anime series.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct SeriesInfo {
    /// The ID of the series.
    pub id: u32,
    /// The titles of the series.
    pub title: SeriesTitle,
    /// The number of episodes.
    pub episodes: u32,
    /// The length of a single episode in minutes.
    pub episode_length: Minutes,
    /// An ID pointing to the sequel of this series.
    pub sequel: Option<u32>,
}

impl<'a> Into<Cow<'a, SeriesInfo>> for SeriesInfo {
    fn into(self) -> Cow<'a, SeriesInfo> {
        Cow::Owned(self)
    }
}

impl<'a> Into<Cow<'a, SeriesInfo>> for &'a SeriesInfo {
    fn into(self) -> Cow<'a, SeriesInfo> {
        Cow::Borrowed(self)
    }
}

/// Various title formats for an anime series.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct SeriesTitle {
    /// The title in romaji.
    pub romaji: String,
    /// The title in the user's preferred format.
    pub preferred: String,
}

/// A list entry for an anime series.
#[derive(Debug, Deserialize, Serialize)]
pub struct SeriesEntry {
    /// The ID of the anime.
    pub id: u32,
    /// The number of episodes that have been watched.
    pub watched_eps: u32,
    /// The score given by the user.
    pub score: Option<u8>,
    /// The user's current watch status of the series.
    pub status: Status,
    /// The number of times the user has rewatched the series.
    pub times_rewatched: u32,
    /// The date the user started watching the series.
    pub start_date: Option<chrono::NaiveDate>,
    /// The date the user finished watching the series.
    pub end_date: Option<chrono::NaiveDate>,
}

impl SeriesEntry {
    /// Create a new `SeriesEntry` associated to the anime with the specified `id`.
    #[inline]
    pub fn new(id: u32) -> SeriesEntry {
        SeriesEntry {
            id,
            watched_eps: 0,
            score: None,
            status: Status::default(),
            times_rewatched: 0,
            start_date: None,
            end_date: None,
        }
    }
}

/// The watch status of an anime series.
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
pub enum Status {
    Watching,
    Completed,
    OnHold,
    Dropped,
    PlanToWatch,
    Rewatching,
}

impl Default for Status {
    fn default() -> Status {
        Status::PlanToWatch
    }
}

impl fmt::Display for Status {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let value = match self {
            Status::Watching => "Watching",
            Status::Completed => "Completed",
            Status::OnHold => "On Hold",
            Status::Dropped => "Dropped",
            Status::PlanToWatch => "Plan To Watch",
            Status::Rewatching => "Rewatching",
        };

        write!(f, "{}", value)
    }
}
<|endoftext|>


<commit_before>//! Layout handling

use rustwlc::handle::{WlcView, WlcOutput};

enum ContainerTypes {
    Root,        /* Root container, only one exists */
    Output,      /* Output, like a monitor or head */
    Workspace,   /* A workspace */
    View         /* A view (aka a window) */
}

enum Layout {
    None,
    Horizontal,
    Vertical,
    Stacked,
    Tabbed,
    Floating
}

/// Like i3, everything (workspaces, containers, views) are containable.
pub trait Containable {
    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent<T: Containable>(&self) -> Option<T>;

    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children<T: Containable>(&self) -> Vec<T>;

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool;

    /// Removes this container and all of its children
    fn remove_container(self);

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(visibilty: bool);

    /// Gets the X and Y dimensions of the container
    fn get_dimensions(&self) -> (u64, u64);

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64);

    /// Finds a parent container with the given type
    fn get_parent_by_type<T: Containable>(&self, type_: T) -> T;
}

/// View specific functions
pub trait Viewable {
    /// Determines if the view is full screen
    fn is_fullscreen(&self) -> bool;

    /// Figures out if the view is focused
    fn is_active(&self) -> bool;

    /// Returns true if this view is a parent is an ancestor of the child
    fn is_parent_of<T: Containable>(&self, child: T) -> bool;

    /// Returns true if this view is a child is an decedent of the parent
    fn is_child_of<T: Containable>(&self, parent: T) -> bool;

    /// Gets the active workspace of the view
    fn active_workspace<T: Containable>(&self) -> T;
}

struct Container {
    // Can't be a view, that's the View struct's job
    handle: Option<WlcOutput>,
    type_: ContainerTypes,
    layout: Layout,

    width: u64,
    height: u64,

    x: i64,
    y: i64,

    visible: bool,
    is_focused: bool,
    is_floating: bool,
}

struct View {
    handle: Option<WlcView>,

    width: u64,
    height: u64,

    x: i64,
    y: i64,

    visible: bool,
    is_focused: bool,
    is_floating: bool,
}
<commit_msg>Implemented containable functions for container<commit_after>//! Layout handling

use rustwlc::handle::{WlcView, WlcOutput};

#[derive(PartialEq, Clone, Copy)]
pub enum ContainerType {
    Root,        /* Root container, only one exists */
    Output,      /* Output, like a monitor or head */
    Workspace,   /* A workspace */
    View         /* A view (aka a window) */
}

enum Layout {
    None,
    Horizontal,
    Vertical,
    Stacked,
    Tabbed,
    Floating
}

/// Like i3, everything (workspaces, containers, views) are containable.
pub trait Containable {
    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent(&self) -> Option<&Containable>;

    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children(&self) -> Vec<Box<Containable>>;

    /// Gets the type of the container
    fn get_type(&self) -> ContainerType;

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool;

    /// Removes this container and all of its children
    fn remove_container(self);

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(&mut self, visibilty: bool);

    /// Gets the X and Y dimensions of the container
    fn get_dimensions(&self) -> (u64, u64);

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64);

    /// Finds a parent container with the given type
    fn get_parent_by_type(&self, type_: ContainerType) -> Option<&Containable>;
}

/// View specific functions
pub trait Viewable {
    /// Determines if the view is full screen
    fn is_fullscreen(&self) -> bool;

    /// Figures out if the view is focused
    fn is_active(&self) -> bool;

    /// Returns true if this view is a parent is an ancestor of the child
    fn is_parent_of<T: Containable>(&self, child: T) -> bool;

    /// Returns true if this view is a child is an decedent of the parent
    fn is_child_of<T: Containable>(&self, parent: T) -> bool;

    /// Gets the active workspace of the view
    fn active_workspace<T: Containable>(&self) -> T;
}

struct Container<T: Containable> {
    handle: Option<WlcOutput>,

    parent: Box<T>,
    children: Vec<Box<T>>,
    type_: ContainerType,
    layout: Layout,

    width: u64,
    height: u64,

    x: i64,
    y: i64,

    visible: bool,
    is_focused: bool,
    is_floating: bool,
}

impl<C: Containable> Containable for Container<C> {

    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent(&self) -> Option<&Containable> {
        match self.type_ {
            ContainerType::Root => None,
            _ => Some(&*self.parent as &Containable),
        }
    }
    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children(&self) -> Vec<Box<Containable>> {
        unimplemented!();
    }

    fn get_type(&self) -> ContainerType {
        self.type_
    }

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool {
        self.is_focused
    }

    /// Removes this container and all of its children
    fn remove_container(self) {
        for child in self.children {
            child.remove_container();
        }
    }

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(&mut self, visibilty: bool) {
        self.visible = visibilty
    }

    /// Gets the X (width) and Y (height) dimensions of the container
    fn get_dimensions(&self) -> (u64, u64) {
        (self.width, self.height)
    }

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64) {
        (self.x, self.y)
    }

    /// Finds a parent container with the given type, if there is any
    fn get_parent_by_type(&self, type_: ContainerType) -> Option<&Containable> {
        let mut container = self.get_parent();
        while container.is_some() && container.unwrap().get_type() != type_ {
            container = container.unwrap().get_parent();
        }
        container
    }
}

struct View {
    handle: Option<WlcView>,

    width: u64,
    height: u64,

    x: i64,
    y: i64,

    visible: bool,
    is_focused: bool,
    is_floating: bool,
}
<|endoftext|>


<commit_before>extern crate rs6502;
extern crate sdl2;
extern crate vm;

mod ship;

use std::io::{self, BufRead, Write};
use std::path::Path;
use std::sync::mpsc::channel;
use std::thread;

use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::image::LoadTexture;
use sdl2::pixels::Color;
use sdl2::rect::Rect;
use sdl2::render::TextureQuery;

use rs6502::{Assembler, Cpu};
use vm::VirtualMachine;

const FPS_STEP: u32 = 1000 / 60;

fn main() {

    let bytecode = assemble("level.asm");
    let cpu = init_cpu(&bytecode);
    let mut vm = VirtualMachine::new(cpu, 0xC000, 150);

    let sdl_context = sdl2::init().unwrap();
    let ttf_context = sdl2::ttf::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();

    let window = video_subsystem.window("Hakka", 1280, 400)
        .position_centered()
        .build()
        .unwrap();

    let mut renderer = window.renderer()
        .accelerated()
        .build()
        .unwrap();

    let ship_texture = renderer.load_texture(Path::new("ship.png")).unwrap();

    let mut font = ttf_context.load_font(Path::new("FantasqueSansMono-Bold.ttf"), 128).unwrap();
    font.set_style(sdl2::ttf::STYLE_BOLD);

    let surface = font.render("You win!")
        .blended(Color::RGBA(0, 153, 192, 255))
        .unwrap();
    let mut font_texture = renderer.create_texture_from_surface(&surface).unwrap();
    let mut events = sdl_context.event_pump().unwrap();

    let mut ship = ship::Ship::new(ship_texture);
    let mut last_fps = 0;
    let mut monitor_last = 0;

    'running: loop {

        for event in events.poll_iter() {
            match event {
                Event::Quit { .. } => break 'running,
                Event::KeyDown { keycode: Option::Some(Keycode::Right), .. } => {
                    vm.cpu.memory[0x04] = 39
                }
                Event::KeyDown { keycode: Option::Some(Keycode::Left), .. } => {
                    vm.cpu.memory[0x04] = 37
                }
                Event::KeyUp { keycode: Option::Some(Keycode::Right), .. } => {
                    vm.cpu.memory[0x04] = 0
                }
                Event::KeyUp { keycode: Option::Some(Keycode::Left), .. } => {
                    vm.cpu.memory[0x04] = 0
                }
                Event::KeyDown { keycode: Option::Some(Keycode::Escape), .. } => break 'running,
                _ => (),
            }
        }

        if ship.x > 1280 - 0xFF {
            let TextureQuery { width, height, .. } = font_texture.query();
            renderer.clear();
            renderer.copy(&font_texture,
                          None,
                          Some(Rect::new(350, 128, width, height)));
            renderer.present();
        } else {
            vm.try_execute_command();
            ship.process(&vm.cpu.memory[..]);

            if ship.x >= 0x190 && ship.x <= 0x1AF && vm.cpu.memory[0x04] != 0 {
                vm.cpu.memory[0x00] = 0x90;
                vm.cpu.memory[0x01] = 0x01;
            }

            let now = sdl_context.timer().unwrap().ticks();
            let delta = now - last_fps;
            if delta < FPS_STEP {
                sdl_context.timer().unwrap().delay(FPS_STEP - delta);
            } else {
                vm.cycle();
                if !vm.cpu.flags.interrupt_disabled {
                    renderer.clear();
                    ship.render(&mut renderer);
                    renderer.present();
                    last_fps = now;
                }
            }

            let delta = now - monitor_last;
            if delta > 1000 && vm.monitor.enabled {
                vm.dump_memory();
                monitor_last = now;
            }
        }
    }
}

fn assemble<P>(path: P) -> Vec<u8>
    where P: AsRef<Path>
{
    let mut assembler = Assembler::new();
    assembler.assemble_file(path).unwrap()
}

fn init_cpu(bytecode: &[u8]) -> Cpu {
    let mut cpu = Cpu::new();
    cpu.load(&bytecode[..], None).unwrap();
    cpu.flags.interrupt_disabled = false;

    cpu.memory[0x02] = 0x80;
    cpu.memory[0x03] = 0x00;
    cpu.memory[0x05] = 0x05;
    cpu.memory[0x06] = 0x00;

    cpu
}<commit_msg>Render a finish line<commit_after>extern crate rs6502;
extern crate sdl2;
extern crate vm;

mod ship;

use std::io::{self, BufRead, Write};
use std::path::Path;
use std::sync::mpsc::channel;

use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::image::LoadTexture;
use sdl2::pixels::Color;
use sdl2::rect::Rect;
use sdl2::render::TextureQuery;

use rs6502::{Assembler, Cpu};
use vm::VirtualMachine;

const FPS_STEP: u32 = 1000 / 60;

fn main() {

    let bytecode = assemble("level.asm");
    let cpu = init_cpu(&bytecode);
    let mut vm = VirtualMachine::new(cpu, 0xC000, 150);

    let sdl_context = sdl2::init().unwrap();
    let ttf_context = sdl2::ttf::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();

    let window = video_subsystem.window("Hakka", 1280, 400)
        .position_centered()
        .build()
        .unwrap();

    let mut renderer = window.renderer()
        .accelerated()
        .build()
        .unwrap();

    let ship_texture = renderer.load_texture(Path::new("ship.png")).unwrap();

    let mut win_font = ttf_context.load_font(Path::new("FantasqueSansMono-Bold.ttf"), 128).unwrap();
    win_font.set_style(sdl2::ttf::STYLE_BOLD);

    let mut finish_font = ttf_context.load_font(Path::new("FantasqueSansMono-Bold.ttf"), 64)
        .unwrap();
    finish_font.set_style(sdl2::ttf::STYLE_BOLD);

    let win_message_surface = win_font.render("You win!")
        .blended(Color::RGBA(0, 153, 192, 255))
        .unwrap();
    let finish_banner_surface = finish_font.render("F I N I S H")
        .blended_wrapped(Color::RGBA(0, 0, 0, 255), 56)
        .unwrap();
    let mut win_message_texture = renderer.create_texture_from_surface(&win_message_surface)
        .unwrap();
    let mut finish_banner_texture = renderer.create_texture_from_surface(&finish_banner_surface)
        .unwrap();
    let mut events = sdl_context.event_pump().unwrap();

    let mut ship = ship::Ship::new(ship_texture);
    let mut last_fps = 0;
    let mut monitor_last = 0;

    'running: loop {

        for event in events.poll_iter() {
            match event {
                Event::Quit { .. } => break 'running,
                Event::KeyDown { keycode: Option::Some(Keycode::Right), .. } => {
                    vm.cpu.memory[0x04] = 39
                }
                Event::KeyDown { keycode: Option::Some(Keycode::Left), .. } => {
                    vm.cpu.memory[0x04] = 37
                }
                Event::KeyUp { keycode: Option::Some(Keycode::Right), .. } => {
                    vm.cpu.memory[0x04] = 0
                }
                Event::KeyUp { keycode: Option::Some(Keycode::Left), .. } => {
                    vm.cpu.memory[0x04] = 0
                }
                Event::KeyDown { keycode: Option::Some(Keycode::Escape), .. } => break 'running,
                _ => (),
            }
        }

        if ship.x > 1280 - 0xFF {
            let TextureQuery { width, height, .. } = win_message_texture.query();
            renderer.clear();
            renderer.copy(&win_message_texture,
                          None,
                          Some(Rect::new(350, 128, width, height)));
            renderer.present();
        } else {
            vm.try_execute_command();
            ship.process(&vm.cpu.memory[..]);

            if ship.x >= 0x190 && ship.x <= 0x1AF && vm.cpu.memory[0x04] != 0 {
                vm.cpu.memory[0x00] = 0x90;
                vm.cpu.memory[0x01] = 0x01;
            }

            let now = sdl_context.timer().unwrap().ticks();
            let delta = now - last_fps;
            if delta < FPS_STEP {
                sdl_context.timer().unwrap().delay(FPS_STEP - delta);
            } else {
                vm.cycle();
                if !vm.cpu.flags.interrupt_disabled {
                    let TextureQuery { width, height, .. } = finish_banner_texture.query();
                    renderer.clear();
                    renderer.set_draw_color(Color::RGB(0, 144, 192));
                    renderer.fill_rect(Rect::new(1160, 0, 120, 400)).unwrap();
                    renderer.set_draw_color(Color::RGB(0, 0, 0));
                    renderer.copy(&finish_banner_texture,
                                  None,
                                  Some(Rect::new(1200, 0, width, height)));
                    ship.render(&mut renderer);
                    renderer.present();
                    last_fps = now;
                }
            }

            let delta = now - monitor_last;
            if delta > 1000 && vm.monitor.enabled {
                vm.dump_memory();
                monitor_last = now;
            }
        }
    }
}

fn assemble<P>(path: P) -> Vec<u8>
    where P: AsRef<Path>
{
    let mut assembler = Assembler::new();
    assembler.assemble_file(path).unwrap()
}

fn init_cpu(bytecode: &[u8]) -> Cpu {
    let mut cpu = Cpu::new();
    cpu.load(&bytecode[..], None).unwrap();
    cpu.flags.interrupt_disabled = false;

    cpu.memory[0x02] = 0x80;
    cpu.memory[0x03] = 0x00;
    cpu.memory[0x05] = 0x05;
    cpu.memory[0x06] = 0x00;

    cpu
}<|endoftext|>


<commit_before>//! Memory allocation APIs

#![stable(feature = "alloc_module", since = "1.28.0")]

mod global;
mod layout;

#[stable(feature = "global_alloc", since = "1.28.0")]
pub use self::global::GlobalAlloc;
#[stable(feature = "alloc_layout", since = "1.28.0")]
pub use self::layout::{Layout, LayoutErr};

use crate::fmt;
use crate::mem;
use crate::ptr::{self, NonNull, Unique};

/// The `AllocErr` error indicates an allocation failure
/// that may be due to resource exhaustion or to
/// something wrong when combining the given input arguments with this
/// allocator.
#[unstable(feature = "allocator_api", issue = "32838")]
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct AllocErr;

// (we need this for downstream impl of trait Error)
#[unstable(feature = "allocator_api", issue = "32838")]
impl fmt::Display for AllocErr {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("memory allocation failed")
    }
}

/// A desired initial state for allocated memory.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[unstable(feature = "allocator_api", issue = "32838")]
pub enum AllocInit {
    /// The contents of the new memory are undefined.
    ///
    /// Reading uninitialized memory is Undefined Behavior; it must be initialized before use.
    Uninitialized,
    /// The new memory is guaranteed to be zeroed.
    Zeroed,
}

/// Represents a block of allocated memory returned by an allocator.
#[derive(Debug)]
#[unstable(feature = "allocator_api", issue = "32838")]
#[must_use = "`MemoryBlock` should be passed to `AllocRef::dealloc`"]
pub struct MemoryBlock {
    ptr: Unique<u8>,
    layout: Layout,
}

impl MemoryBlock {
    /// Creates a new `MemoryBlock`.
    ///
    /// # Safety
    ///
    /// * The block must be allocated with the same alignment as [`layout.align()`], and
    /// * The provided [`layout.size()`] must fall in the range `min ..= max`, where:
    ///   - `min` is the size requested size when allocating the block, and
    ///   - `max` is the size of the memory block.
    #[inline]
    #[unstable(feature = "allocator_api", issue = "32838")]
    pub const unsafe fn new(ptr: NonNull<u8>, layout: Layout) -> Self {
        Self { ptr: Unique::new_unchecked(ptr.as_ptr()), layout }
    }

    /// Acquires the underlying `NonNull<u8>` pointer.
    #[inline]
    #[unstable(feature = "allocator_api", issue = "32838")]
    pub const fn ptr(&self) -> NonNull<u8> {
        // SAFETY: Unique<T> is always non-null
        unsafe { NonNull::new_unchecked(self.ptr.as_ptr()) }
    }

    /// Returns the layout describing the memory block.
    #[inline]
    #[unstable(feature = "allocator_api", issue = "32838")]
    pub const fn layout(&self) -> Layout {
        self.layout
    }

    /// Returns the size of the memory block.
    #[inline]
    #[unstable(feature = "allocator_api", issue = "32838")]
    pub const fn size(&self) -> usize {
        self.layout().size()
    }

    /// Returns the minimum alignment of the memory block.
    #[inline]
    #[unstable(feature = "allocator_api", issue = "32838")]
    pub const fn align(&self) -> usize {
        self.layout().align()
    }

    /// Initialize the memory block like specified by `init`.
    ///
    /// This behaves like calling [`MemoryBlock::initialize_offset(ptr, layout, 0)`][off].
    ///
    /// [off]: MemoryBlock::init_offset
    ///
    /// [*fit*]: trait.AllocRef.html#memory-fitting
    #[inline]
    #[unstable(feature = "allocator_api", issue = "32838")]
    pub fn init(&mut self, init: AllocInit) {
        // SAFETY: 0 is always smaller or equal to the size
        unsafe { self.init_offset(init, 0) }
    }

    /// Initialize the memory block like specified by `init` at the specified `offset`.
    ///
    /// This is a no-op for [`AllocInit::Uninitialized`] and writes zeroes for [`AllocInit::Zeroed`]
    /// at `ptr + offset` until `ptr + layout.size()`.
    ///
    /// # Safety
    ///
    /// * `offset` must be smaller than or equal to `size()`
    ///
    /// [*fit*]: trait.AllocRef.html#memory-fitting
    #[inline]
    #[unstable(feature = "allocator_api", issue = "32838")]
    pub unsafe fn init_offset(&mut self, init: AllocInit, offset: usize) {
        debug_assert!(offset <= self.size(), "`offset` must be smaller than or equal to `size()`");
        match init {
            AllocInit::Uninitialized => (),
            AllocInit::Zeroed => {
                self.ptr().as_ptr().add(offset).write_bytes(0, self.size() - offset)
            }
        }
    }
}

/// A placement constraint when growing or shrinking an existing allocation.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[unstable(feature = "allocator_api", issue = "32838")]
pub enum ReallocPlacement {
    /// The allocator is allowed to move the allocation to a different memory address.
    // FIXME(wg-allocators#46): Add a section to the module documentation "What is a legal
    //                          allocator" and link it at "valid location".
    ///
    /// If the allocation _does_ move, it's the responsibility of the allocator
    /// to also move the data from the previous location to the new location.
    MayMove,
    /// The address of the new memory must not change.
    ///
    /// If the allocation would have to be moved to a new location to fit, the
    /// reallocation request will fail.
    InPlace,
}

/// An implementation of `AllocRef` can allocate, grow, shrink, and deallocate arbitrary blocks of
/// data described via [`Layout`][].
///
/// `AllocRef` is designed to be implemented on ZSTs, references, or smart pointers because having
/// an allocator like `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the
/// allocated memory.
///
/// Unlike [`GlobalAlloc`][], zero-sized allocations are allowed in `AllocRef`. If an underlying
/// allocator does not support this (like jemalloc) or return a null pointer (such as
/// `libc::malloc`), this case must be caught.
///
/// # Safety
///
/// * Memory blocks returned from an allocator must point to valid memory and retain their validity
///   until the instance and all of its clones are dropped, and
///
/// * cloning or moving the allocator must not invalidate memory blocks returned from this
///   allocator. A cloned allocator must behave like the same allocator.
///
/// [*currently allocated*]: #currently-allocated-memory
#[unstable(feature = "allocator_api", issue = "32838")]
pub unsafe trait AllocRef {
    /// On success, returns a memory block meeting the size and alignment guarantees of `layout`.
    ///
    /// The returned block may have a larger size than specified by `layout.size()` and is
    /// initialized as specified by [`init`], all the way up to the returned size of the block.
    ///
    /// [`init`]: AllocInit
    ///
    /// # Errors
    ///
    /// Returning `Err` indicates that either memory is exhausted or `layout` does not meet
    /// allocator's size or alignment constraints.
    ///
    /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
    /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
    /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
    ///
    /// Clients wishing to abort computation in response to an allocation error are encouraged to
    /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
    ///
    /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
    fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result<MemoryBlock, AllocErr>;

    /// Deallocates the memory denoted by `memory`.
    ///
    /// # Safety
    ///
    /// `memory` must be a memory block returned by this allocator.
    unsafe fn dealloc(&mut self, memory: MemoryBlock);

    /// Attempts to extend the memory block.
    ///
    /// The behavior of how the allocator tries to grow the memory is specified by [`placement`].
    /// The first `memory.size()` bytes are preserved or copied as appropriate from `ptr`, and the
    /// remaining bytes up to the new `memory.size()` are initialized according to [`init`].
    ///
    /// [`placement`]: ReallocPlacement
    /// [`init`]: AllocInit
    ///
    /// # Safety
    ///
    /// * `memory` must be a memory block returned by this allocator.
    // We can't require that `new_size` is strictly greater than `memory.size()` because of ZSTs.
    // An alternative would be
    // * `new_size must be strictly greater than `memory.size()` or both are zero
    /// * `new_size` must be greater than or equal to `memory.size()`
    /// * `new_size`, when rounded up to the nearest multiple of `memory.align()`, must not overflow
    ///   (i.e., the rounded value must be less than `usize::MAX`).
    ///
    /// [*currently allocated*]: #currently-allocated-memory
    /// [*fit*]: #memory-fitting
    ///
    /// # Errors
    ///
    /// Returns `Err` if the new layout does not meet the allocator's size and alignment
    /// constraints of the allocator, or if growing otherwise fails.
    ///
    /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
    /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
    /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
    ///
    /// Clients wishing to abort computation in response to an allocation error are encouraged to
    /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
    ///
    /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
    unsafe fn grow(
        &mut self,
        memory: &mut MemoryBlock,
        new_size: usize,
        placement: ReallocPlacement,
        init: AllocInit,
    ) -> Result<(), AllocErr> {
        match placement {
            ReallocPlacement::InPlace => Err(AllocErr),
            ReallocPlacement::MayMove => {
                let old_size = memory.size();
                debug_assert!(
                    new_size >= old_size,
                    "`new_size` must be greater than or equal to `memory.size()`"
                );

                if new_size == old_size {
                    return Ok(());
                }

                let new_layout = Layout::from_size_align_unchecked(new_size, memory.align());
                let new_memory = self.alloc(new_layout, init)?;
                ptr::copy_nonoverlapping(
                    memory.ptr().as_ptr(),
                    new_memory.ptr().as_ptr(),
                    old_size,
                );
                self.dealloc(mem::replace(memory, new_memory));
                Ok(())
            }
        }
    }

    /// Attempts to shrink the memory block.
    ///
    /// The behavior of how the allocator tries to shrink the memory is specified by [`placement`].
    ///
    /// [`placement`]: ReallocPlacement
    ///
    /// # Safety
    ///
    /// * `memory` must be a memory block returned by this allocator.
    // We can't require that `new_size` is strictly smaller than `memory.size()` because of ZSTs.
    // An alternative would be
    // * `new_size must be strictly smaller than `memory.size()` or both are zero
    /// * `new_size` must be smaller than or equal to `memory.size()`
    ///
    /// [*currently allocated*]: #currently-allocated-memory
    /// [*fit*]: #memory-fitting
    ///
    /// # Errors
    ///
    /// Returns `Err` if the new layout does not meet the allocator's size and alignment
    /// constraints of the allocator, or if growing otherwise fails.
    ///
    /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
    /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
    /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
    ///
    /// Clients wishing to abort computation in response to an allocation error are encouraged to
    /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
    ///
    /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
    unsafe fn shrink(
        &mut self,
        memory: &mut MemoryBlock,
        new_size: usize,
        placement: ReallocPlacement,
    ) -> Result<(), AllocErr> {
        match placement {
            ReallocPlacement::InPlace => Err(AllocErr),
            ReallocPlacement::MayMove => {
                let old_size = memory.size();
                debug_assert!(
                    new_size <= old_size,
                    "`new_size` must be smaller than or equal to `layout.size()`"
                );

                if new_size == old_size {
                    return Ok(());
                }

                let new_layout = Layout::from_size_align_unchecked(new_size, memory.align());
                let new_memory = self.alloc(new_layout, AllocInit::Uninitialized)?;
                ptr::copy_nonoverlapping(
                    memory.ptr().as_ptr(),
                    new_memory.ptr().as_ptr(),
                    new_size,
                );
                self.dealloc(mem::replace(memory, new_memory));
                Ok(())
            }
        }
    }
}
<commit_msg>Use `NonNull` instead of `Unique` in `MemoryBlock`<commit_after>//! Memory allocation APIs

#![stable(feature = "alloc_module", since = "1.28.0")]

mod global;
mod layout;

#[stable(feature = "global_alloc", since = "1.28.0")]
pub use self::global::GlobalAlloc;
#[stable(feature = "alloc_layout", since = "1.28.0")]
pub use self::layout::{Layout, LayoutErr};

use crate::fmt;
use crate::mem;
use crate::ptr::{self, NonNull, Unique};

/// The `AllocErr` error indicates an allocation failure
/// that may be due to resource exhaustion or to
/// something wrong when combining the given input arguments with this
/// allocator.
#[unstable(feature = "allocator_api", issue = "32838")]
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct AllocErr;

// (we need this for downstream impl of trait Error)
#[unstable(feature = "allocator_api", issue = "32838")]
impl fmt::Display for AllocErr {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("memory allocation failed")
    }
}

/// A desired initial state for allocated memory.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[unstable(feature = "allocator_api", issue = "32838")]
pub enum AllocInit {
    /// The contents of the new memory are undefined.
    ///
    /// Reading uninitialized memory is Undefined Behavior; it must be initialized before use.
    Uninitialized,
    /// The new memory is guaranteed to be zeroed.
    Zeroed,
}

/// Represents a block of allocated memory returned by an allocator.
#[derive(Debug)]
#[unstable(feature = "allocator_api", issue = "32838")]
#[must_use = "`MemoryBlock` should be passed to `AllocRef::dealloc`"]
pub struct MemoryBlock {
    ptr: NonNull<u8>,
    layout: Layout,
}

impl MemoryBlock {
    /// Creates a new `MemoryBlock`.
    ///
    /// # Safety
    ///
    /// * The block must be allocated with the same alignment as [`layout.align()`], and
    /// * The provided [`layout.size()`] must fall in the range `min ..= max`, where:
    ///   - `min` is the size requested size when allocating the block, and
    ///   - `max` is the size of the memory block.
    #[inline]
    #[unstable(feature = "allocator_api", issue = "32838")]
    pub const unsafe fn new(ptr: NonNull<u8>, layout: Layout) -> Self {
        Self { ptr, layout }
    }

    /// Acquires the underlying `NonNull<u8>` pointer.
    #[inline]
    #[unstable(feature = "allocator_api", issue = "32838")]
    pub const fn ptr(&self) -> NonNull<u8> {
        self.ptr
    }

    /// Returns the layout describing the memory block.
    #[inline]
    #[unstable(feature = "allocator_api", issue = "32838")]
    pub const fn layout(&self) -> Layout {
        self.layout
    }

    /// Returns the size of the memory block.
    #[inline]
    #[unstable(feature = "allocator_api", issue = "32838")]
    pub const fn size(&self) -> usize {
        self.layout().size()
    }

    /// Returns the minimum alignment of the memory block.
    #[inline]
    #[unstable(feature = "allocator_api", issue = "32838")]
    pub const fn align(&self) -> usize {
        self.layout().align()
    }

    /// Initialize the memory block like specified by `init`.
    ///
    /// This behaves like calling [`MemoryBlock::initialize_offset(ptr, layout, 0)`][off].
    ///
    /// [off]: MemoryBlock::init_offset
    ///
    /// [*fit*]: trait.AllocRef.html#memory-fitting
    #[inline]
    #[unstable(feature = "allocator_api", issue = "32838")]
    pub fn init(&mut self, init: AllocInit) {
        // SAFETY: 0 is always smaller or equal to the size
        unsafe { self.init_offset(init, 0) }
    }

    /// Initialize the memory block like specified by `init` at the specified `offset`.
    ///
    /// This is a no-op for [`AllocInit::Uninitialized`] and writes zeroes for [`AllocInit::Zeroed`]
    /// at `ptr + offset` until `ptr + layout.size()`.
    ///
    /// # Safety
    ///
    /// * `offset` must be smaller than or equal to `size()`
    ///
    /// [*fit*]: trait.AllocRef.html#memory-fitting
    #[inline]
    #[unstable(feature = "allocator_api", issue = "32838")]
    pub unsafe fn init_offset(&mut self, init: AllocInit, offset: usize) {
        debug_assert!(offset <= self.size(), "`offset` must be smaller than or equal to `size()`");
        match init {
            AllocInit::Uninitialized => (),
            AllocInit::Zeroed => {
                self.ptr().as_ptr().add(offset).write_bytes(0, self.size() - offset)
            }
        }
    }
}

/// A placement constraint when growing or shrinking an existing allocation.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[unstable(feature = "allocator_api", issue = "32838")]
pub enum ReallocPlacement {
    /// The allocator is allowed to move the allocation to a different memory address.
    // FIXME(wg-allocators#46): Add a section to the module documentation "What is a legal
    //                          allocator" and link it at "valid location".
    ///
    /// If the allocation _does_ move, it's the responsibility of the allocator
    /// to also move the data from the previous location to the new location.
    MayMove,
    /// The address of the new memory must not change.
    ///
    /// If the allocation would have to be moved to a new location to fit, the
    /// reallocation request will fail.
    InPlace,
}

/// An implementation of `AllocRef` can allocate, grow, shrink, and deallocate arbitrary blocks of
/// data described via [`Layout`][].
///
/// `AllocRef` is designed to be implemented on ZSTs, references, or smart pointers because having
/// an allocator like `MyAlloc([u8; N])` cannot be moved, without updating the pointers to the
/// allocated memory.
///
/// Unlike [`GlobalAlloc`][], zero-sized allocations are allowed in `AllocRef`. If an underlying
/// allocator does not support this (like jemalloc) or return a null pointer (such as
/// `libc::malloc`), this case must be caught.
///
/// # Safety
///
/// * Memory blocks returned from an allocator must point to valid memory and retain their validity
///   until the instance and all of its clones are dropped, and
///
/// * cloning or moving the allocator must not invalidate memory blocks returned from this
///   allocator. A cloned allocator must behave like the same allocator.
///
/// [*currently allocated*]: #currently-allocated-memory
#[unstable(feature = "allocator_api", issue = "32838")]
pub unsafe trait AllocRef {
    /// On success, returns a memory block meeting the size and alignment guarantees of `layout`.
    ///
    /// The returned block may have a larger size than specified by `layout.size()` and is
    /// initialized as specified by [`init`], all the way up to the returned size of the block.
    ///
    /// [`init`]: AllocInit
    ///
    /// # Errors
    ///
    /// Returning `Err` indicates that either memory is exhausted or `layout` does not meet
    /// allocator's size or alignment constraints.
    ///
    /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
    /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
    /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
    ///
    /// Clients wishing to abort computation in response to an allocation error are encouraged to
    /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
    ///
    /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
    fn alloc(&mut self, layout: Layout, init: AllocInit) -> Result<MemoryBlock, AllocErr>;

    /// Deallocates the memory denoted by `memory`.
    ///
    /// # Safety
    ///
    /// `memory` must be a memory block returned by this allocator.
    unsafe fn dealloc(&mut self, memory: MemoryBlock);

    /// Attempts to extend the memory block.
    ///
    /// The behavior of how the allocator tries to grow the memory is specified by [`placement`].
    /// The first `memory.size()` bytes are preserved or copied as appropriate from `ptr`, and the
    /// remaining bytes up to the new `memory.size()` are initialized according to [`init`].
    ///
    /// [`placement`]: ReallocPlacement
    /// [`init`]: AllocInit
    ///
    /// # Safety
    ///
    /// * `memory` must be a memory block returned by this allocator.
    // We can't require that `new_size` is strictly greater than `memory.size()` because of ZSTs.
    // An alternative would be
    // * `new_size must be strictly greater than `memory.size()` or both are zero
    /// * `new_size` must be greater than or equal to `memory.size()`
    /// * `new_size`, when rounded up to the nearest multiple of `memory.align()`, must not overflow
    ///   (i.e., the rounded value must be less than `usize::MAX`).
    ///
    /// [*currently allocated*]: #currently-allocated-memory
    /// [*fit*]: #memory-fitting
    ///
    /// # Errors
    ///
    /// Returns `Err` if the new layout does not meet the allocator's size and alignment
    /// constraints of the allocator, or if growing otherwise fails.
    ///
    /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
    /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
    /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
    ///
    /// Clients wishing to abort computation in response to an allocation error are encouraged to
    /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
    ///
    /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
    unsafe fn grow(
        &mut self,
        memory: &mut MemoryBlock,
        new_size: usize,
        placement: ReallocPlacement,
        init: AllocInit,
    ) -> Result<(), AllocErr> {
        match placement {
            ReallocPlacement::InPlace => Err(AllocErr),
            ReallocPlacement::MayMove => {
                let old_size = memory.size();
                debug_assert!(
                    new_size >= old_size,
                    "`new_size` must be greater than or equal to `memory.size()`"
                );

                if new_size == old_size {
                    return Ok(());
                }

                let new_layout = Layout::from_size_align_unchecked(new_size, memory.align());
                let new_memory = self.alloc(new_layout, init)?;
                ptr::copy_nonoverlapping(
                    memory.ptr().as_ptr(),
                    new_memory.ptr().as_ptr(),
                    old_size,
                );
                self.dealloc(mem::replace(memory, new_memory));
                Ok(())
            }
        }
    }

    /// Attempts to shrink the memory block.
    ///
    /// The behavior of how the allocator tries to shrink the memory is specified by [`placement`].
    ///
    /// [`placement`]: ReallocPlacement
    ///
    /// # Safety
    ///
    /// * `memory` must be a memory block returned by this allocator.
    // We can't require that `new_size` is strictly smaller than `memory.size()` because of ZSTs.
    // An alternative would be
    // * `new_size must be strictly smaller than `memory.size()` or both are zero
    /// * `new_size` must be smaller than or equal to `memory.size()`
    ///
    /// [*currently allocated*]: #currently-allocated-memory
    /// [*fit*]: #memory-fitting
    ///
    /// # Errors
    ///
    /// Returns `Err` if the new layout does not meet the allocator's size and alignment
    /// constraints of the allocator, or if growing otherwise fails.
    ///
    /// Implementations are encouraged to return `Err` on memory exhaustion rather than panicking or
    /// aborting, but this is not a strict requirement. (Specifically: it is *legal* to implement
    /// this trait atop an underlying native allocation library that aborts on memory exhaustion.)
    ///
    /// Clients wishing to abort computation in response to an allocation error are encouraged to
    /// call the [`handle_alloc_error`] function, rather than directly invoking `panic!` or similar.
    ///
    /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html
    unsafe fn shrink(
        &mut self,
        memory: &mut MemoryBlock,
        new_size: usize,
        placement: ReallocPlacement,
    ) -> Result<(), AllocErr> {
        match placement {
            ReallocPlacement::InPlace => Err(AllocErr),
            ReallocPlacement::MayMove => {
                let old_size = memory.size();
                debug_assert!(
                    new_size <= old_size,
                    "`new_size` must be smaller than or equal to `layout.size()`"
                );

                if new_size == old_size {
                    return Ok(());
                }

                let new_layout = Layout::from_size_align_unchecked(new_size, memory.align());
                let new_memory = self.alloc(new_layout, AllocInit::Uninitialized)?;
                ptr::copy_nonoverlapping(
                    memory.ptr().as_ptr(),
                    new_memory.ptr().as_ptr(),
                    new_size,
                );
                self.dealloc(mem::replace(memory, new_memory));
                Ok(())
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 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.

use to_str::{ToStr,ToStrConsume};
use str;
use cast;

/// Datatype to hold one ascii character. It is 8 bit long.
#[deriving(Clone, Eq)]
pub struct Ascii { priv chr: u8 }

pub impl Ascii {
    /// Converts a ascii character into a `u8`.
    #[inline(always)]
    fn to_byte(self) -> u8 {
        self.chr
    }

    /// Converts a ascii character into a `char`.
    #[inline(always)]
    fn to_char(self) -> char {
        self.chr as char
    }

    /// Convert to lowercase.
    #[inline(always)]
    fn to_lower(self) -> Ascii {
        if self.chr >= 65 && self.chr <= 90 {
            Ascii{chr: self.chr | 0x20 }
        } else {
            self
        }
    }

    /// Convert to uppercase.
    #[inline(always)]
    fn to_upper(self) -> Ascii {
        if self.chr >= 97 && self.chr <= 122 {
            Ascii{chr: self.chr & !0x20 }
        } else {
            self
        }
    }

    // Compares two ascii characters of equality, ignoring case.
    #[inline(always)]
    fn eq_ignore_case(self, other: Ascii) -> bool {
        self.to_lower().chr == other.to_lower().chr
    }
}

impl ToStr for Ascii {
    #[inline(always)]
    fn to_str(&self) -> ~str { str::from_bytes(['\'' as u8, self.chr, '\'' as u8]) }
}

/// Trait for converting into an ascii type.
pub trait AsciiCast<T> {
    /// Convert to an ascii type
    fn to_ascii(&self) -> T;

    /// Check if convertible to ascii
    fn is_ascii(&self) -> bool;
}

impl<'self> AsciiCast<&'self[Ascii]> for &'self [u8] {
    #[inline(always)]
    fn to_ascii(&self) -> &'self[Ascii] {
        assert!(self.is_ascii());
        unsafe{ cast::transmute(*self) }
    }

    #[inline(always)]
    fn is_ascii(&self) -> bool {
        for self.each |b| {
            if !b.is_ascii() { return false; }
        }
        true
    }
}

impl<'self> AsciiCast<&'self[Ascii]> for &'self str {
    #[inline(always)]
    fn to_ascii(&self) -> &'self[Ascii] {
        assert!(self.is_ascii());
        let (p,len): (*u8, uint) = unsafe{ cast::transmute(*self) };
        unsafe{ cast::transmute((p, len - 1))}
    }

    #[inline(always)]
    fn is_ascii(&self) -> bool {
        for self.each |b| {
            if !b.is_ascii() { return false; }
        }
        true
    }
}

impl AsciiCast<Ascii> for u8 {
    #[inline(always)]
    fn to_ascii(&self) -> Ascii {
        assert!(self.is_ascii());
        Ascii{ chr: *self }
    }

    #[inline(always)]
    fn is_ascii(&self) -> bool {
        *self & 128 == 0u8
    }
}

impl AsciiCast<Ascii> for char {
    #[inline(always)]
    fn to_ascii(&self) -> Ascii {
        assert!(self.is_ascii());
        Ascii{ chr: *self as u8 }
    }

    #[inline(always)]
    fn is_ascii(&self) -> bool {
        *self - ('\x7F' & *self) == '\x00'
    }
}

/// Trait for copyless casting to an ascii vector.
pub trait OwnedAsciiCast {
    /// Take ownership and cast to an ascii vector without trailing zero element.
    fn to_ascii_consume(self) -> ~[Ascii];
}

impl OwnedAsciiCast for ~[u8] {
    #[inline(always)]
    fn to_ascii_consume(self) -> ~[Ascii] {
        assert!(self.is_ascii());
        unsafe {cast::transmute(self)}
    }
}

impl OwnedAsciiCast for ~str {
    #[inline(always)]
    fn to_ascii_consume(self) -> ~[Ascii] {
        assert!(self.is_ascii());
        let mut s = self;
        unsafe {
            str::raw::pop_byte(&mut s);
            cast::transmute(s)
        }
    }
}

/// Trait for converting an ascii type to a string. Needed to convert `&[Ascii]` to `~str`
pub trait AsciiStr {
    /// Convert to a string.
    fn to_str_ascii(&self) -> ~str;

    /// Convert to vector representing a lower cased ascii string.
    fn to_lower(&self) -> ~[Ascii];

    /// Convert to vector representing a upper cased ascii string.
    fn to_upper(&self) -> ~[Ascii];

}

impl<'self> AsciiStr for &'self [Ascii] {
    #[inline(always)]
    fn to_str_ascii(&self) -> ~str {
        let mut cpy = self.to_owned();
        cpy.push(0u8.to_ascii());
        unsafe {cast::transmute(cpy)}
    }

    #[inline(always)]
    fn to_lower(&self) -> ~[Ascii] {
        self.map(|a| a.to_lower())
    }

    #[inline(always)]
    fn to_upper(&self) -> ~[Ascii] {
        self.map(|a| a.to_upper())
    }
}

impl ToStrConsume for ~[Ascii] {
    #[inline(always)]
    fn to_str_consume(self) -> ~str {
        let mut cpy = self;
        cpy.push(0u8.to_ascii());
        unsafe {cast::transmute(cpy)}
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use str;

    macro_rules! v2ascii (
        ( [$($e:expr),*]) => ( [$(Ascii{chr:$e}),*]);
        (~[$($e:expr),*]) => (~[$(Ascii{chr:$e}),*]);
    )

    #[test]
    fn test_ascii() {
        assert_eq!(65u8.to_ascii().to_byte(), 65u8);
        assert_eq!(65u8.to_ascii().to_char(), 'A');
        assert_eq!('A'.to_ascii().to_char(), 'A');
        assert_eq!('A'.to_ascii().to_byte(), 65u8);

        assert_eq!('A'.to_ascii().to_lower().to_char(), 'a');
        assert_eq!('Z'.to_ascii().to_lower().to_char(), 'z');
        assert_eq!('a'.to_ascii().to_upper().to_char(), 'A');
        assert_eq!('z'.to_ascii().to_upper().to_char(), 'Z');

        assert_eq!('@'.to_ascii().to_lower().to_char(), '@');
        assert_eq!('['.to_ascii().to_lower().to_char(), '[');
        assert_eq!('`'.to_ascii().to_upper().to_char(), '`');
        assert_eq!('{'.to_ascii().to_upper().to_char(), '{');

        assert!(str::all(~"banana", |c| c.is_ascii()));
        assert!(! str::all(~"ประเทศไทย中华Việt Nam", |c| c.is_ascii()));
    }

    #[test]
    fn test_ascii_vec() {
        assert_eq!((&[40u8, 32u8, 59u8]).to_ascii(), v2ascii!([40, 32, 59]));
        assert_eq!("( ;".to_ascii(),                 v2ascii!([40, 32, 59]));
        // FIXME: #5475 borrowchk error, owned vectors do not live long enough
        // if chained-from directly
        let v = ~[40u8, 32u8, 59u8]; assert_eq!(v.to_ascii(), v2ascii!([40, 32, 59]));
        let v = ~"( ;";              assert_eq!(v.to_ascii(), v2ascii!([40, 32, 59]));

        assert_eq!("abCDef&?#".to_ascii().to_lower().to_str_ascii(), ~"abcdef&?#");
        assert_eq!("abCDef&?#".to_ascii().to_upper().to_str_ascii(), ~"ABCDEF&?#");

        assert_eq!("".to_ascii().to_lower().to_str_ascii(), ~"");
        assert_eq!("YMCA".to_ascii().to_lower().to_str_ascii(), ~"ymca");
        assert_eq!("abcDEFxyz:.;".to_ascii().to_upper().to_str_ascii(), ~"ABCDEFXYZ:.;");

        assert!("".is_ascii());
        assert!("a".is_ascii());
        assert!(!"\u2009".is_ascii());

    }

    #[test]
    fn test_owned_ascii_vec() {
        // FIXME: #4318 Compiler crashes on moving self
        //assert_eq!(~"( ;".to_ascii_consume(), v2ascii!(~[40, 32, 59]));
        //assert_eq!(~[40u8, 32u8, 59u8].to_ascii_consume(), v2ascii!(~[40, 32, 59]));
        //assert_eq!(~"( ;".to_ascii_consume_with_null(), v2ascii!(~[40, 32, 59, 0]));
        //assert_eq!(~[40u8, 32u8, 59u8].to_ascii_consume_with_null(),
        //           v2ascii!(~[40, 32, 59, 0]));
    }

    #[test]
    fn test_ascii_to_str() { assert_eq!(v2ascii!([40, 32, 59]).to_str_ascii(), ~"( ;"); }

    #[test]
    fn test_ascii_to_str_consume() {
        // FIXME: #4318 Compiler crashes on moving self
        //assert_eq!(v2ascii!(~[40, 32, 59]).to_str_consume(), ~"( ;");
    }

    #[test] #[should_fail]
    fn test_ascii_vec_fail_u8_slice()  { (&[127u8, 128u8, 255u8]).to_ascii(); }

    #[test] #[should_fail]
    fn test_ascii_vec_fail_str_slice() { "zoä华".to_ascii(); }

    #[test] #[should_fail]
    fn test_ascii_fail_u8_slice() { 255u8.to_ascii(); }

    #[test] #[should_fail]
    fn test_ascii_fail_char_slice() { 'λ'.to_ascii(); }
}
<commit_msg>Add a brief description to show up in http://static.rust-lang.org/doc/core/index.html<commit_after>// Copyright 2013 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.

//! Operations on ASCII strings and characters.

use to_str::{ToStr,ToStrConsume};
use str;
use cast;

/// Datatype to hold one ascii character. It is 8 bit long.
#[deriving(Clone, Eq)]
pub struct Ascii { priv chr: u8 }

pub impl Ascii {
    /// Converts a ascii character into a `u8`.
    #[inline(always)]
    fn to_byte(self) -> u8 {
        self.chr
    }

    /// Converts a ascii character into a `char`.
    #[inline(always)]
    fn to_char(self) -> char {
        self.chr as char
    }

    /// Convert to lowercase.
    #[inline(always)]
    fn to_lower(self) -> Ascii {
        if self.chr >= 65 && self.chr <= 90 {
            Ascii{chr: self.chr | 0x20 }
        } else {
            self
        }
    }

    /// Convert to uppercase.
    #[inline(always)]
    fn to_upper(self) -> Ascii {
        if self.chr >= 97 && self.chr <= 122 {
            Ascii{chr: self.chr & !0x20 }
        } else {
            self
        }
    }

    // Compares two ascii characters of equality, ignoring case.
    #[inline(always)]
    fn eq_ignore_case(self, other: Ascii) -> bool {
        self.to_lower().chr == other.to_lower().chr
    }
}

impl ToStr for Ascii {
    #[inline(always)]
    fn to_str(&self) -> ~str { str::from_bytes(['\'' as u8, self.chr, '\'' as u8]) }
}

/// Trait for converting into an ascii type.
pub trait AsciiCast<T> {
    /// Convert to an ascii type
    fn to_ascii(&self) -> T;

    /// Check if convertible to ascii
    fn is_ascii(&self) -> bool;
}

impl<'self> AsciiCast<&'self[Ascii]> for &'self [u8] {
    #[inline(always)]
    fn to_ascii(&self) -> &'self[Ascii] {
        assert!(self.is_ascii());
        unsafe{ cast::transmute(*self) }
    }

    #[inline(always)]
    fn is_ascii(&self) -> bool {
        for self.each |b| {
            if !b.is_ascii() { return false; }
        }
        true
    }
}

impl<'self> AsciiCast<&'self[Ascii]> for &'self str {
    #[inline(always)]
    fn to_ascii(&self) -> &'self[Ascii] {
        assert!(self.is_ascii());
        let (p,len): (*u8, uint) = unsafe{ cast::transmute(*self) };
        unsafe{ cast::transmute((p, len - 1))}
    }

    #[inline(always)]
    fn is_ascii(&self) -> bool {
        for self.each |b| {
            if !b.is_ascii() { return false; }
        }
        true
    }
}

impl AsciiCast<Ascii> for u8 {
    #[inline(always)]
    fn to_ascii(&self) -> Ascii {
        assert!(self.is_ascii());
        Ascii{ chr: *self }
    }

    #[inline(always)]
    fn is_ascii(&self) -> bool {
        *self & 128 == 0u8
    }
}

impl AsciiCast<Ascii> for char {
    #[inline(always)]
    fn to_ascii(&self) -> Ascii {
        assert!(self.is_ascii());
        Ascii{ chr: *self as u8 }
    }

    #[inline(always)]
    fn is_ascii(&self) -> bool {
        *self - ('\x7F' & *self) == '\x00'
    }
}

/// Trait for copyless casting to an ascii vector.
pub trait OwnedAsciiCast {
    /// Take ownership and cast to an ascii vector without trailing zero element.
    fn to_ascii_consume(self) -> ~[Ascii];
}

impl OwnedAsciiCast for ~[u8] {
    #[inline(always)]
    fn to_ascii_consume(self) -> ~[Ascii] {
        assert!(self.is_ascii());
        unsafe {cast::transmute(self)}
    }
}

impl OwnedAsciiCast for ~str {
    #[inline(always)]
    fn to_ascii_consume(self) -> ~[Ascii] {
        assert!(self.is_ascii());
        let mut s = self;
        unsafe {
            str::raw::pop_byte(&mut s);
            cast::transmute(s)
        }
    }
}

/// Trait for converting an ascii type to a string. Needed to convert `&[Ascii]` to `~str`
pub trait AsciiStr {
    /// Convert to a string.
    fn to_str_ascii(&self) -> ~str;

    /// Convert to vector representing a lower cased ascii string.
    fn to_lower(&self) -> ~[Ascii];

    /// Convert to vector representing a upper cased ascii string.
    fn to_upper(&self) -> ~[Ascii];

}

impl<'self> AsciiStr for &'self [Ascii] {
    #[inline(always)]
    fn to_str_ascii(&self) -> ~str {
        let mut cpy = self.to_owned();
        cpy.push(0u8.to_ascii());
        unsafe {cast::transmute(cpy)}
    }

    #[inline(always)]
    fn to_lower(&self) -> ~[Ascii] {
        self.map(|a| a.to_lower())
    }

    #[inline(always)]
    fn to_upper(&self) -> ~[Ascii] {
        self.map(|a| a.to_upper())
    }
}

impl ToStrConsume for ~[Ascii] {
    #[inline(always)]
    fn to_str_consume(self) -> ~str {
        let mut cpy = self;
        cpy.push(0u8.to_ascii());
        unsafe {cast::transmute(cpy)}
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use str;

    macro_rules! v2ascii (
        ( [$($e:expr),*]) => ( [$(Ascii{chr:$e}),*]);
        (~[$($e:expr),*]) => (~[$(Ascii{chr:$e}),*]);
    )

    #[test]
    fn test_ascii() {
        assert_eq!(65u8.to_ascii().to_byte(), 65u8);
        assert_eq!(65u8.to_ascii().to_char(), 'A');
        assert_eq!('A'.to_ascii().to_char(), 'A');
        assert_eq!('A'.to_ascii().to_byte(), 65u8);

        assert_eq!('A'.to_ascii().to_lower().to_char(), 'a');
        assert_eq!('Z'.to_ascii().to_lower().to_char(), 'z');
        assert_eq!('a'.to_ascii().to_upper().to_char(), 'A');
        assert_eq!('z'.to_ascii().to_upper().to_char(), 'Z');

        assert_eq!('@'.to_ascii().to_lower().to_char(), '@');
        assert_eq!('['.to_ascii().to_lower().to_char(), '[');
        assert_eq!('`'.to_ascii().to_upper().to_char(), '`');
        assert_eq!('{'.to_ascii().to_upper().to_char(), '{');

        assert!(str::all(~"banana", |c| c.is_ascii()));
        assert!(! str::all(~"ประเทศไทย中华Việt Nam", |c| c.is_ascii()));
    }

    #[test]
    fn test_ascii_vec() {
        assert_eq!((&[40u8, 32u8, 59u8]).to_ascii(), v2ascii!([40, 32, 59]));
        assert_eq!("( ;".to_ascii(),                 v2ascii!([40, 32, 59]));
        // FIXME: #5475 borrowchk error, owned vectors do not live long enough
        // if chained-from directly
        let v = ~[40u8, 32u8, 59u8]; assert_eq!(v.to_ascii(), v2ascii!([40, 32, 59]));
        let v = ~"( ;";              assert_eq!(v.to_ascii(), v2ascii!([40, 32, 59]));

        assert_eq!("abCDef&?#".to_ascii().to_lower().to_str_ascii(), ~"abcdef&?#");
        assert_eq!("abCDef&?#".to_ascii().to_upper().to_str_ascii(), ~"ABCDEF&?#");

        assert_eq!("".to_ascii().to_lower().to_str_ascii(), ~"");
        assert_eq!("YMCA".to_ascii().to_lower().to_str_ascii(), ~"ymca");
        assert_eq!("abcDEFxyz:.;".to_ascii().to_upper().to_str_ascii(), ~"ABCDEFXYZ:.;");

        assert!("".is_ascii());
        assert!("a".is_ascii());
        assert!(!"\u2009".is_ascii());

    }

    #[test]
    fn test_owned_ascii_vec() {
        // FIXME: #4318 Compiler crashes on moving self
        //assert_eq!(~"( ;".to_ascii_consume(), v2ascii!(~[40, 32, 59]));
        //assert_eq!(~[40u8, 32u8, 59u8].to_ascii_consume(), v2ascii!(~[40, 32, 59]));
        //assert_eq!(~"( ;".to_ascii_consume_with_null(), v2ascii!(~[40, 32, 59, 0]));
        //assert_eq!(~[40u8, 32u8, 59u8].to_ascii_consume_with_null(),
        //           v2ascii!(~[40, 32, 59, 0]));
    }

    #[test]
    fn test_ascii_to_str() { assert_eq!(v2ascii!([40, 32, 59]).to_str_ascii(), ~"( ;"); }

    #[test]
    fn test_ascii_to_str_consume() {
        // FIXME: #4318 Compiler crashes on moving self
        //assert_eq!(v2ascii!(~[40, 32, 59]).to_str_consume(), ~"( ;");
    }

    #[test] #[should_fail]
    fn test_ascii_vec_fail_u8_slice()  { (&[127u8, 128u8, 255u8]).to_ascii(); }

    #[test] #[should_fail]
    fn test_ascii_vec_fail_str_slice() { "zoä华".to_ascii(); }

    #[test] #[should_fail]
    fn test_ascii_fail_u8_slice() { 255u8.to_ascii(); }

    #[test] #[should_fail]
    fn test_ascii_fail_char_slice() { 'λ'.to_ascii(); }
}
<|endoftext|>