<commit_before>/* Simple getopt alternative. Construct a vector of options, either by using
 * reqopt, optopt, and optflag or by building them from components yourself,
 * and pass them to getopts, along with a vector of actual arguments (not
 * including argv[0]). You'll either get a failure code back, or a match.
 * You'll have to verify whether the amount of 'free' arguments in the match
 * is what you expect. Use opt_* accessors (bottom of the file) to get
 * argument values out of the match object.
 */

import option::some;
import option::none;

tag name { long(str); short(char); }
tag hasarg { yes; no; maybe; }
tag occur { req; optional; multi; }

type opt = rec(name name, hasarg hasarg, occur occur);

fn mkname(str nm) -> name {
    if (str::char_len(nm) == 1u) { ret short(str::char_at(nm, 0u)); }
    else { ret long(nm); }
}
fn reqopt(str name) -> opt {
    ret rec(name=mkname(name), hasarg=yes, occur=req);
}
fn optopt(str name) -> opt {
    ret rec(name=mkname(name), hasarg=yes, occur=optional);
}
fn optflag(str name) -> opt {
    ret rec(name=mkname(name), hasarg=no, occur=optional);
}
fn optmulti(str name) -> opt {
    ret rec(name=mkname(name), hasarg=yes, occur=multi);
}

tag optval {
    val(str);
    given;
}

type match = rec(vec[opt] opts, vec[mutable vec[optval]] vals, vec[str] free);

fn is_arg(str arg) -> bool {
    ret str::byte_len(arg) > 1u && arg.(0) == '-' as u8;
}
fn name_str(name nm) -> str {
    alt (nm) {
        case (short(?ch)) {ret str::from_char(ch);}
        case (long(?s)) {ret s;}
    }
}

// FIXME rustboot workaround
fn name_eq(name a, name b) -> bool {
    alt (a) {
        case (long(?a)) {
            alt (b) {
                case (long(?b)) { ret str::eq(a, b); }
                case (_) { ret false; }
            }
        }
        case (_) { if (a == b) { ret true; } else {ret false; } }
    }
}
fn find_opt(vec[opt] opts, name nm) -> option::t[uint] {
    auto i = 0u;
    auto l = vec::len[opt](opts);
    while (i < l) {
        if (name_eq(opts.(i).name, nm)) { ret some[uint](i); }
        i += 1u;
    }
    ret none[uint];
}

tag fail_ {
    argument_missing(str);
    unrecognized_option(str);
    option_missing(str);
    option_duplicated(str);
}

fn fail_str(fail_ f) -> str {
    alt (f) {
        case (argument_missing(?nm)) {
            ret "Argument to option '" + nm + "' missing.";
        }
        case (unrecognized_option(?nm)) {
            ret "Unrecognized option: '" + nm + "'.";
        }
        case (option_missing(?nm)) {
            ret "Required option '" + nm + "' missing.";
        }
        case (option_duplicated(?nm)) {
            ret "option '" + nm + "' given more than once.";
        }
    }
}

tag result {
    success(match);
    failure(fail_);
}

fn getopts(vec[str] args, vec[opt] opts) -> result {
    auto n_opts = vec::len[opt](opts);
    fn empty_(uint x) -> vec[optval]{ret vec::empty[optval]();}
    auto f = empty_;
    auto vals = vec::init_fn_mut[vec[optval]](f, n_opts);
    let vec[str] free = [];

    auto l = vec::len[str](args);
    auto i = 0u;
    while (i < l) {
        auto cur = args.(i);
        auto curlen = str::byte_len(cur);
        if (!is_arg(cur)) {
            vec::push[str](free, cur);
        } else if (str::eq(cur, "--")) {
            free += vec::slice[str](args, i + 1u, l);
            break;
        } else {
            auto names;
            auto i_arg = option::none[str];
            if (cur.(1) == '-' as u8) {
                auto tail = str::slice(cur, 2u, curlen);
                auto eq = str::index(tail, '=' as u8);
                if (eq == -1) {
                    names = [long(tail)];
                } else {
                    names = [long(str::slice(tail, 0u, eq as uint))];
                    i_arg = option::some[str]
                        (str::slice(tail, (eq as uint) + 1u, curlen - 2u));
                }
            } else {
                auto j = 1u;
                names = [];
                while (j < curlen) {
                    auto range = str::char_range_at(cur, j);
                    vec::push[name](names, short(range._0));
                    j = range._1;
                }
            }
            auto name_pos = 0u;
            for (name nm in names) {
                name_pos += 1u;
                auto optid;
                alt (find_opt(opts, nm)) {
                    case (some[uint](?id)) {optid = id;}
                    case (none[uint]) {
                        ret failure(unrecognized_option(name_str(nm)));
                    }
                }
                alt (opts.(optid).hasarg) {
                    case (no) {
                        vec::push[optval](vals.(optid), given);
                    }
                    case (maybe) {
                        if (!option::is_none[str](i_arg)) {
                            vec::push[optval](vals.(optid),
                                              val(option::get[str](i_arg)));
                        } else if (name_pos < vec::len[name](names) ||
                                   i + 1u == l || is_arg(args.(i + 1u))) {
                            vec::push[optval](vals.(optid), given);
                        } else {
                            i += 1u;
                            vec::push[optval](vals.(optid), val(args.(i)));
                        }
                    }
                    case (yes) {
                        if (!option::is_none[str](i_arg)) {
                            vec::push[optval](vals.(optid),
                                              val(option::get[str](i_arg)));
                        } else if (i + 1u == l) {
                            ret failure(argument_missing(name_str(nm)));
                        } else {
                            i += 1u;
                            vec::push[optval](vals.(optid), val(args.(i)));
                        }
                    }
                }
            }
        }
        i += 1u;
    }

    i = 0u;
    while (i < n_opts) {
        auto n = vec::len[optval](vals.(i));
        auto occ = opts.(i).occur;
        if (occ == req) {if (n == 0u) {
            ret failure(option_missing(name_str(opts.(i).name)));
        }}
        if (occ != multi) {if (n > 1u) {
            ret failure(option_duplicated(name_str(opts.(i).name)));
        }}
        i += 1u;
    }

    ret success(rec(opts=opts, vals=vals, free=free));
}

fn opt_vals(match m, str nm) -> vec[optval] {
    alt (find_opt(m.opts, mkname(nm))) {
        case (some[uint](?id)) { ret m.vals.(id); }
        case (none[uint]) {
            log_err "No option '" + nm + "' defined.";
            fail;
        }
    }
}
fn opt_val(match m, str nm) -> optval {
    ret opt_vals(m, nm).(0);
}
fn opt_present(match m, str nm) -> bool {
    ret vec::len[optval](opt_vals(m, nm)) > 0u;
}
fn opt_str(match m, str nm) -> str {
    alt (opt_val(m, nm)) {
        case (val(?s)) { ret s; }
        case (_) { fail; }
    }
}
fn opt_strs(match m, str nm) -> vec[str] {
    let vec[str] acc = [];
    for (optval v in opt_vals(m, nm)) {
        alt (v) {
            case (val(?s)) { vec::push[str](acc, s); }
            case (_) {}
        }
    }
    ret acc;
}
fn opt_maybe_str(match m, str nm) -> option::t[str] {
    auto vals = opt_vals(m, nm);
    if (vec::len[optval](vals) == 0u) { ret none[str]; }
    alt (vals.(0)) {
        case (val(?s)) { ret some[str](s); }
        case (_) { ret none[str]; }
    }
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>stdlib: Only export what's necessary from std::getopts<commit_after>/* Simple getopt alternative. Construct a vector of options, either by using
 * reqopt, optopt, and optflag or by building them from components yourself,
 * and pass them to getopts, along with a vector of actual arguments (not
 * including argv[0]). You'll either get a failure code back, or a match.
 * You'll have to verify whether the amount of 'free' arguments in the match
 * is what you expect. Use opt_* accessors (bottom of the file) to get
 * argument values out of the match object.
 */

import option::some;
import option::none;

export opt;

export reqopt;
export optopt;
export optflag;
export optmulti;

export getopts;

export result;
export success;
export failure;

export fail_;
export fail_str;

export opt_present;
export opt_str;
export opt_strs;
export opt_maybe_str;

tag name { long(str); short(char); }
tag hasarg { yes; no; maybe; }
tag occur { req; optional; multi; }

type opt = rec(name name, hasarg hasarg, occur occur);

fn mkname(str nm) -> name {
    if (str::char_len(nm) == 1u) { ret short(str::char_at(nm, 0u)); }
    else { ret long(nm); }
}
fn reqopt(str name) -> opt {
    ret rec(name=mkname(name), hasarg=yes, occur=req);
}
fn optopt(str name) -> opt {
    ret rec(name=mkname(name), hasarg=yes, occur=optional);
}
fn optflag(str name) -> opt {
    ret rec(name=mkname(name), hasarg=no, occur=optional);
}
fn optmulti(str name) -> opt {
    ret rec(name=mkname(name), hasarg=yes, occur=multi);
}

tag optval {
    val(str);
    given;
}

type match = rec(vec[opt] opts, vec[mutable vec[optval]] vals, vec[str] free);

fn is_arg(str arg) -> bool {
    ret str::byte_len(arg) > 1u && arg.(0) == '-' as u8;
}
fn name_str(name nm) -> str {
    alt (nm) {
        case (short(?ch)) {ret str::from_char(ch);}
        case (long(?s)) {ret s;}
    }
}

// FIXME rustboot workaround
fn name_eq(name a, name b) -> bool {
    alt (a) {
        case (long(?a)) {
            alt (b) {
                case (long(?b)) { ret str::eq(a, b); }
                case (_) { ret false; }
            }
        }
        case (_) { if (a == b) { ret true; } else {ret false; } }
    }
}
fn find_opt(vec[opt] opts, name nm) -> option::t[uint] {
    auto i = 0u;
    auto l = vec::len[opt](opts);
    while (i < l) {
        if (name_eq(opts.(i).name, nm)) { ret some[uint](i); }
        i += 1u;
    }
    ret none[uint];
}

tag fail_ {
    argument_missing(str);
    unrecognized_option(str);
    option_missing(str);
    option_duplicated(str);
}

fn fail_str(fail_ f) -> str {
    alt (f) {
        case (argument_missing(?nm)) {
            ret "Argument to option '" + nm + "' missing.";
        }
        case (unrecognized_option(?nm)) {
            ret "Unrecognized option: '" + nm + "'.";
        }
        case (option_missing(?nm)) {
            ret "Required option '" + nm + "' missing.";
        }
        case (option_duplicated(?nm)) {
            ret "option '" + nm + "' given more than once.";
        }
    }
}

tag result {
    success(match);
    failure(fail_);
}

fn getopts(vec[str] args, vec[opt] opts) -> result {
    auto n_opts = vec::len[opt](opts);
    fn empty_(uint x) -> vec[optval]{ret vec::empty[optval]();}
    auto f = empty_;
    auto vals = vec::init_fn_mut[vec[optval]](f, n_opts);
    let vec[str] free = [];

    auto l = vec::len[str](args);
    auto i = 0u;
    while (i < l) {
        auto cur = args.(i);
        auto curlen = str::byte_len(cur);
        if (!is_arg(cur)) {
            vec::push[str](free, cur);
        } else if (str::eq(cur, "--")) {
            free += vec::slice[str](args, i + 1u, l);
            break;
        } else {
            auto names;
            auto i_arg = option::none[str];
            if (cur.(1) == '-' as u8) {
                auto tail = str::slice(cur, 2u, curlen);
                auto eq = str::index(tail, '=' as u8);
                if (eq == -1) {
                    names = [long(tail)];
                } else {
                    names = [long(str::slice(tail, 0u, eq as uint))];
                    i_arg = option::some[str]
                        (str::slice(tail, (eq as uint) + 1u, curlen - 2u));
                }
            } else {
                auto j = 1u;
                names = [];
                while (j < curlen) {
                    auto range = str::char_range_at(cur, j);
                    vec::push[name](names, short(range._0));
                    j = range._1;
                }
            }
            auto name_pos = 0u;
            for (name nm in names) {
                name_pos += 1u;
                auto optid;
                alt (find_opt(opts, nm)) {
                    case (some[uint](?id)) {optid = id;}
                    case (none[uint]) {
                        ret failure(unrecognized_option(name_str(nm)));
                    }
                }
                alt (opts.(optid).hasarg) {
                    case (no) {
                        vec::push[optval](vals.(optid), given);
                    }
                    case (maybe) {
                        if (!option::is_none[str](i_arg)) {
                            vec::push[optval](vals.(optid),
                                              val(option::get[str](i_arg)));
                        } else if (name_pos < vec::len[name](names) ||
                                   i + 1u == l || is_arg(args.(i + 1u))) {
                            vec::push[optval](vals.(optid), given);
                        } else {
                            i += 1u;
                            vec::push[optval](vals.(optid), val(args.(i)));
                        }
                    }
                    case (yes) {
                        if (!option::is_none[str](i_arg)) {
                            vec::push[optval](vals.(optid),
                                              val(option::get[str](i_arg)));
                        } else if (i + 1u == l) {
                            ret failure(argument_missing(name_str(nm)));
                        } else {
                            i += 1u;
                            vec::push[optval](vals.(optid), val(args.(i)));
                        }
                    }
                }
            }
        }
        i += 1u;
    }

    i = 0u;
    while (i < n_opts) {
        auto n = vec::len[optval](vals.(i));
        auto occ = opts.(i).occur;
        if (occ == req) {if (n == 0u) {
            ret failure(option_missing(name_str(opts.(i).name)));
        }}
        if (occ != multi) {if (n > 1u) {
            ret failure(option_duplicated(name_str(opts.(i).name)));
        }}
        i += 1u;
    }

    ret success(rec(opts=opts, vals=vals, free=free));
}

fn opt_vals(match m, str nm) -> vec[optval] {
    alt (find_opt(m.opts, mkname(nm))) {
        case (some[uint](?id)) { ret m.vals.(id); }
        case (none[uint]) {
            log_err "No option '" + nm + "' defined.";
            fail;
        }
    }
}
fn opt_val(match m, str nm) -> optval {
    ret opt_vals(m, nm).(0);
}
fn opt_present(match m, str nm) -> bool {
    ret vec::len[optval](opt_vals(m, nm)) > 0u;
}
fn opt_str(match m, str nm) -> str {
    alt (opt_val(m, nm)) {
        case (val(?s)) { ret s; }
        case (_) { fail; }
    }
}
fn opt_strs(match m, str nm) -> vec[str] {
    let vec[str] acc = [];
    for (optval v in opt_vals(m, nm)) {
        alt (v) {
            case (val(?s)) { vec::push[str](acc, s); }
            case (_) {}
        }
    }
    ret acc;
}
fn opt_maybe_str(match m, str nm) -> option::t[str] {
    auto vals = opt_vals(m, nm);
    if (vec::len[optval](vals) == 0u) { ret none[str]; }
    alt (vals.(0)) {
        case (val(?s)) { ret some[str](s); }
        case (_) { ret none[str]; }
    }
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>


<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>Rollup merge of #68314 - oli-obk:true_unwind, r=eddyb<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>use core::prelude::*;

use aspace::AddressSpace;
use con;
use con::write;
use dlist::DList;
use dlist::DListNode;
use dlist::DListItem;
use dict::*;
use mem::heap_copy;

pub enum FlagBit {
// The process is currently queued on the run queue.
    Queued = 0,

// Can return to user-mode with sysret, only some registers will be restored:
// rsp, rip: restored to previous values
// rcx, r11: rip and rflags, respectively
// rax: syscall return value
// Remaining registers will be 0 (?)
    FastRet = 1,
// IN_RECV: Similar to FASTRET, when waiting for a message-send rendezvous
// When set together with IN_SEND, it's a sendrcv and the SEND needs to finish
// first.
// At any time when IN_RECV is set, the proc's saved rdi contains a pointer to
// the handle being received from.
// When a process starts a receive, until it becomes properly blocked on some
// process or finishes the receive immediately, it will be both RUNNING and
// IN_RECV.
    InRecv = 2,
// Process is trying to do a synchronous send or sendrcv, blocking on the
// waiting_for process to reach a PROC_IN_RECV state. Both IN_SEND and IN_RECV
// can be set at the same time.
// At any time when IN_SEND is set, the proc's saved rdi contains a pointer to
// the handle being sent to.
// When a process starts a send, until it becomes properly blocked on some
// process or finishes the operation, it will be both RUNNING and IN_SEND.
    InSend = 3,
// Is the currently running process
    Running = 4,
// Process has had a page fault that requires a response from a backer, or has
// requested a page paged in.
// proc.fault_addr is the address that faulted/was requested.
    PFault = 5
}

impl FlagBit {
    #[inline]
    fn mask(self) -> uint {
        return 1 << (self as uint);
    }
}

pub struct FXSaveRegs {
    space : [u8, ..512]
}

pub struct Handle {
    node : DictNode<uint, Handle>,
    pub process : *mut Process,
    // pointer to other handle if any. Its 'key' field is the other-name that
    // we need when e.g. sending it a message. If null this is not associated
    // in other-proc yet.
    other : Option<*mut Handle>,
    events : uint,
}

impl DictItem<uint> for Handle {
    fn node<'a>(&'a mut self) -> &'a mut DictNode<uint, Handle> {
        &mut self.node
    }
}

impl Handle {
    pub fn new(id : uint, process : *mut Process) -> Handle {
        Handle {
            node : DictNode::new(id),
            process : process,
            other : None,
            events : 0
        }
    }

    pub fn id(&self) -> uint { self.node.key }
    pub fn process(&self) -> &mut Process { unsafe { &mut *self.process } }
}

pub struct PendingPulse {
    node : DictNode<uint, PendingPulse>,
    handle : *mut Handle,
}

impl DictItem<uint> for PendingPulse {
    fn node<'a>(&'a mut self) -> &'a mut DictNode<uint, PendingPulse> {
        return &mut self.node;
    }
}

impl FXSaveRegs {
    fn new() -> FXSaveRegs {
        FXSaveRegs { space : [0, ..512] }
    }
}

enum RegIndex {
    RAX = 0,
    RCX = 1,
    RDX = 2,
    RBX = 3,
    RSP = 4,
    RBP = 5,
    RSI = 6,
    RDI = 7
}

pub struct Regs {
	pub gps : [u64, ..16],
	pub rip : u64,
	pub rflags : u64,
}

impl Regs {
    fn new() -> Regs {
        use x86::rflags;
        Regs { gps : [0, ..16], rip : 0, rflags : rflags::IF as u64 }
    }

    pub fn set_rsp(&mut self, rsp : uint) {
        self.gps[RSP as uint] = rsp as u64;
    }
    pub fn set_rip(&mut self, rip : uint) {
        self.rip = rip as u64;
    }
    pub fn set_rax(&mut self, rax: uint) {
        self.gps[RAX as uint] = rax as u64;
    }
}

type Flags = uint;

pub struct Process {
    pub regs : Regs,
    // Physical address of PML4 to put in CR3
    pub cr3 : uint,

    // Bitwise OR of flags values
    flags : Flags,
    count : uint,

    // Pointer to the process we're waiting for (if any). See flags.
    waiting_for : *mut Process, // Option

    // List of processes waiting on this process.
    waiters : DList<Process>,
    node : DListNode<Process>,

    aspace : *mut AddressSpace,

    // TODO: move this into address space so handles can be shared between
    // threads.
    handles : Dict<Handle>,
    pending : Dict<PendingPulse>,

    // When PROC_PFAULT is set, the virtual address that faulted.
    // Note that we lose a lot of data about the mapping that we looked up
    // in PFAULT, and have to look up again in GRANT. This is intentional,
    // since we have to verify and match the GRANT to the correct page, we
    // simply don't save anything that might be wrong.
    // The lower bits are access flags for the fault/request.
    fault_addr: uint,

    fxsave : FXSaveRegs,
}

impl DListItem for Process {
    fn node<'a>(&'a mut self) -> &'a mut DListNode<Process> {
        return &mut self.node;
    }
}

impl Process {
    pub fn new(aspace : *mut AddressSpace) -> Process {
        let init_flags = FastRet.mask();
        Process {
            regs : Regs::new(),
            flags : init_flags, count : 0,
            waiting_for : RawPtr::null(),
            waiters : DList::empty(),
            node : DListNode::new(),
            cr3 : unsafe { (*aspace).cr3() },
            aspace : aspace,
            handles : Dict::empty(),
            pending : Dict::empty(),
            fault_addr : 0,
            fxsave : FXSaveRegs::new()
        }
    }

    #[inline]
    pub fn is(&self, f : FlagBit) -> bool {
        (self.flags & f.mask()) != 0
    }

    pub fn set(&mut self, f : FlagBit) {
        self.flags |= f.mask();
    }

    pub fn unset(&mut self, f : FlagBit) {
        self.flags &= !f.mask();
    }

    #[inline]
    pub fn is_queued(&self) -> bool { self.is(Queued) }

    pub fn aspace<'a>(&'a mut self) -> &'a mut AddressSpace {
        unsafe {
            return &mut *self.aspace;
        }
    }

    pub fn find_handle<'a>(&mut self, id : uint) -> Option<&'a mut Handle> {
        let res = self.handles.find(id);
        match res {
            Some(ref h) if h.id() != id => None,
            _ => res
        }
    }

    pub fn assoc_handles(&mut self, id: uint, other : &mut Process, other_id: uint) {
        let x = self.new_handle(id, other);
        let y = other.new_handle(other_id, self);
        x.other = Some(y as *mut Handle);
        y.other = Some(x as *mut Handle);
    }

    pub fn new_handle<'a>(&mut self, id : uint, other : *mut Process) -> &'a mut Handle {
        match self.handles.find(id) {
            Some(ref h) if h.id() != id => (),
            Some(h) => self.delete_handle(h),
            None => ()
        }
        self.handles.insert(heap_copy(Handle::new(id, other)))
    }

    pub fn delete_handle(&mut self, handle : &mut Handle) {
    }

    pub fn rename_handle(&mut self, handle : &mut Handle, new_id: uint) {
        handle.node.key = new_id;
        // TODO self.handles.unlink/relink/key_changed
    }

    pub fn dump(&self) {
        write("proc ");
        con::writePtr(self);
        write(":\n");

        for (id,h) in self.handles.iter() {
            write("  handle ");
            con::writeUInt(id);
            write(" -> proc ");
            con::writeMutPtr(h.process());
            con::newline();
        }
    }
}
<commit_msg>Mark delete_handle unimplemented<commit_after>use core::prelude::*;

use aspace::AddressSpace;
use con;
use con::write;
use dlist::DList;
use dlist::DListNode;
use dlist::DListItem;
use dict::*;
use mem::heap_copy;
use util::abort;

pub enum FlagBit {
// The process is currently queued on the run queue.
    Queued = 0,

// Can return to user-mode with sysret, only some registers will be restored:
// rsp, rip: restored to previous values
// rcx, r11: rip and rflags, respectively
// rax: syscall return value
// Remaining registers will be 0 (?)
    FastRet = 1,
// IN_RECV: Similar to FASTRET, when waiting for a message-send rendezvous
// When set together with IN_SEND, it's a sendrcv and the SEND needs to finish
// first.
// At any time when IN_RECV is set, the proc's saved rdi contains a pointer to
// the handle being received from.
// When a process starts a receive, until it becomes properly blocked on some
// process or finishes the receive immediately, it will be both RUNNING and
// IN_RECV.
    InRecv = 2,
// Process is trying to do a synchronous send or sendrcv, blocking on the
// waiting_for process to reach a PROC_IN_RECV state. Both IN_SEND and IN_RECV
// can be set at the same time.
// At any time when IN_SEND is set, the proc's saved rdi contains a pointer to
// the handle being sent to.
// When a process starts a send, until it becomes properly blocked on some
// process or finishes the operation, it will be both RUNNING and IN_SEND.
    InSend = 3,
// Is the currently running process
    Running = 4,
// Process has had a page fault that requires a response from a backer, or has
// requested a page paged in.
// proc.fault_addr is the address that faulted/was requested.
    PFault = 5
}

impl FlagBit {
    #[inline]
    fn mask(self) -> uint {
        return 1 << (self as uint);
    }
}

pub struct FXSaveRegs {
    space : [u8, ..512]
}

pub struct Handle {
    node : DictNode<uint, Handle>,
    pub process : *mut Process,
    // pointer to other handle if any. Its 'key' field is the other-name that
    // we need when e.g. sending it a message. If null this is not associated
    // in other-proc yet.
    other : Option<*mut Handle>,
    events : uint,
}

impl DictItem<uint> for Handle {
    fn node<'a>(&'a mut self) -> &'a mut DictNode<uint, Handle> {
        &mut self.node
    }
}

impl Handle {
    pub fn new(id : uint, process : *mut Process) -> Handle {
        Handle {
            node : DictNode::new(id),
            process : process,
            other : None,
            events : 0
        }
    }

    pub fn id(&self) -> uint { self.node.key }
    pub fn process(&self) -> &mut Process { unsafe { &mut *self.process } }
}

pub struct PendingPulse {
    node : DictNode<uint, PendingPulse>,
    handle : *mut Handle,
}

impl DictItem<uint> for PendingPulse {
    fn node<'a>(&'a mut self) -> &'a mut DictNode<uint, PendingPulse> {
        return &mut self.node;
    }
}

impl FXSaveRegs {
    fn new() -> FXSaveRegs {
        FXSaveRegs { space : [0, ..512] }
    }
}

enum RegIndex {
    RAX = 0,
    RCX = 1,
    RDX = 2,
    RBX = 3,
    RSP = 4,
    RBP = 5,
    RSI = 6,
    RDI = 7
}

pub struct Regs {
	pub gps : [u64, ..16],
	pub rip : u64,
	pub rflags : u64,
}

impl Regs {
    fn new() -> Regs {
        use x86::rflags;
        Regs { gps : [0, ..16], rip : 0, rflags : rflags::IF as u64 }
    }

    pub fn set_rsp(&mut self, rsp : uint) {
        self.gps[RSP as uint] = rsp as u64;
    }
    pub fn set_rip(&mut self, rip : uint) {
        self.rip = rip as u64;
    }
    pub fn set_rax(&mut self, rax: uint) {
        self.gps[RAX as uint] = rax as u64;
    }
}

type Flags = uint;

pub struct Process {
    pub regs : Regs,
    // Physical address of PML4 to put in CR3
    pub cr3 : uint,

    // Bitwise OR of flags values
    flags : Flags,
    count : uint,

    // Pointer to the process we're waiting for (if any). See flags.
    waiting_for : *mut Process, // Option

    // List of processes waiting on this process.
    waiters : DList<Process>,
    node : DListNode<Process>,

    aspace : *mut AddressSpace,

    // TODO: move this into address space so handles can be shared between
    // threads.
    handles : Dict<Handle>,
    pending : Dict<PendingPulse>,

    // When PROC_PFAULT is set, the virtual address that faulted.
    // Note that we lose a lot of data about the mapping that we looked up
    // in PFAULT, and have to look up again in GRANT. This is intentional,
    // since we have to verify and match the GRANT to the correct page, we
    // simply don't save anything that might be wrong.
    // The lower bits are access flags for the fault/request.
    fault_addr: uint,

    fxsave : FXSaveRegs,
}

impl DListItem for Process {
    fn node<'a>(&'a mut self) -> &'a mut DListNode<Process> {
        return &mut self.node;
    }
}

impl Process {
    pub fn new(aspace : *mut AddressSpace) -> Process {
        let init_flags = FastRet.mask();
        Process {
            regs : Regs::new(),
            flags : init_flags, count : 0,
            waiting_for : RawPtr::null(),
            waiters : DList::empty(),
            node : DListNode::new(),
            cr3 : unsafe { (*aspace).cr3() },
            aspace : aspace,
            handles : Dict::empty(),
            pending : Dict::empty(),
            fault_addr : 0,
            fxsave : FXSaveRegs::new()
        }
    }

    #[inline]
    pub fn is(&self, f : FlagBit) -> bool {
        (self.flags & f.mask()) != 0
    }

    pub fn set(&mut self, f : FlagBit) {
        self.flags |= f.mask();
    }

    pub fn unset(&mut self, f : FlagBit) {
        self.flags &= !f.mask();
    }

    #[inline]
    pub fn is_queued(&self) -> bool { self.is(Queued) }

    pub fn aspace<'a>(&'a mut self) -> &'a mut AddressSpace {
        unsafe {
            return &mut *self.aspace;
        }
    }

    pub fn find_handle<'a>(&mut self, id : uint) -> Option<&'a mut Handle> {
        let res = self.handles.find(id);
        match res {
            Some(ref h) if h.id() != id => None,
            _ => res
        }
    }

    pub fn assoc_handles(&mut self, id: uint, other : &mut Process, other_id: uint) {
        let x = self.new_handle(id, other);
        let y = other.new_handle(other_id, self);
        x.other = Some(y as *mut Handle);
        y.other = Some(x as *mut Handle);
    }

    pub fn new_handle<'a>(&mut self, id : uint, other : *mut Process) -> &'a mut Handle {
        match self.handles.find(id) {
            Some(ref h) if h.id() != id => (),
            Some(h) => self.delete_handle(h),
            None => ()
        }
        self.handles.insert(heap_copy(Handle::new(id, other)))
    }

    pub fn delete_handle(&mut self, handle : &mut Handle) {
        abort("not impl.");
    }

    pub fn rename_handle(&mut self, handle : &mut Handle, new_id: uint) {
        handle.node.key = new_id;
        // TODO self.handles.unlink/relink/key_changed
    }

    pub fn dump(&self) {
        write("proc ");
        con::writePtr(self);
        write(":\n");

        for (id,h) in self.handles.iter() {
            write("  handle ");
            con::writeUInt(id);
            write(" -> proc ");
            con::writeMutPtr(h.process());
            con::newline();
        }
    }
}
<|endoftext|>


<commit_before>extern crate open_gl_bindings;
extern crate sdl2;

use open_gl_bindings::gl;

use sdl2::event::Event;

use std::ffi::CStr;
use std::str;

fn find_sdl_gl_driver() -> Option<u32> {
    for (index, item) in sdl2::render::drivers().enumerate() {
        if item.name == "opengl" {
            return Some(index as u32);
        }
    }
    None
}

fn main() {
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();

    let gl_attr = video_subsystem.gl_attr();
    gl_attr.set_stencil_size(1);
    gl_attr.set_context_major_version(2);
    gl_attr.set_context_minor_version(1);

    let canvas = video_subsystem
        .window("Window", 800, 600)
        .opengl()
        .build()
        .unwrap()
        .into_canvas()
        .index(find_sdl_gl_driver().unwrap())
        .build()
        .unwrap();

    let ctx = gl::Gl::load_with(|name| video_subsystem.gl_get_proc_address(name) as *const _);
    canvas.window().gl_set_context_to_current().unwrap();

    let char_ptr = unsafe { ctx.GetString(gl::VERSION) };
    let c_str: &CStr = unsafe { CStr::from_ptr(std::mem::transmute(char_ptr)) };
    let buf: &[u8] = c_str.to_bytes();
    let str_slice: &str = str::from_utf8(buf).unwrap();
    let version: String = str_slice.to_owned();

    println!("OpenGL version: {}", version);

    let window = canvas.window();

    let (width, height) = window.drawable_size();

    unsafe {
        ctx.Viewport(0, 0, width as _, height as _);

        ctx.MatrixMode(gl::PROJECTION);
        ctx.LoadIdentity();

        ctx.MatrixMode(gl::MODELVIEW);
        ctx.LoadIdentity();
    }

    unsafe {
        ctx.ClearColor(0.0, 0.0, 0.0, 1.0);
        ctx.Clear(
            gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT,
        );
        ctx.Enable(gl::BLEND);
        ctx.BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);

        ctx.Clear(gl::STENCIL_BUFFER_BIT);

        let verts: Vec<f32> = get_verts();

        ctx.EnableClientState(gl::VERTEX_ARRAY);
        ctx.VertexPointer(2, gl::FLOAT, 0, std::mem::transmute(verts.as_ptr()));

        let cnt = (verts.len() / 2) as _;

        ctx.Enable(gl::STENCIL_TEST);

        ctx.ColorMask(gl::FALSE, gl::FALSE, gl::FALSE, gl::FALSE);
        ctx.StencilOp(gl::INVERT, gl::INVERT, gl::INVERT);
        ctx.StencilFunc(gl::ALWAYS, 0x1, 0x1);
        ctx.Color4f(1.0, 1.0, 1.0, 1.0);
        ctx.DrawArrays(gl::TRIANGLE_FAN, 0, cnt);

        ctx.ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE);
        ctx.Color4f(32.0 / 255.0, 32.0 / 255.0, 63.0 / 255.0, 1.0);

        ctx.StencilOp(gl::ZERO, gl::ZERO, gl::ZERO);
        ctx.StencilFunc(gl::EQUAL, 1, 1);
        ctx.DrawArrays(gl::TRIANGLE_FAN, 0, cnt);
        ctx.Disable(gl::STENCIL_TEST);

        //outline
        ctx.Color4f(128.0 / 255.0, 128.0 / 255.0, 32.0 / 255.0, 1.0);
        ctx.DrawArrays(gl::LINE_STRIP, 0, cnt);

    }

    let mut event_pump = sdl_context.event_pump().unwrap();

    'running: loop {
        for event in event_pump.poll_iter() {
            match event {
                Event::Quit { .. } |
                Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Escape), .. } |
                Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::F10), .. } => {
                    break 'running;
                }
                _ => {}
            }
        }

        window.gl_swap_window();

        std::thread::sleep(std::time::Duration::from_millis(8));
    }
}

#[cfg_attr(rustfmt, rustfmt_skip)]
fn get_verts() -> Vec<f32> {
    vec![
        -0.012640, 0.255336,
        0.152259, 0.386185,
        0.223982, 0.275978,
        0.191749, 0.169082,
        0.396864, 0.121742,
        0.355419, -0.003047,
        0.251747, -0.044495,
        0.342622, -0.234376,
        0.219218, -0.279777,
        0.122174, -0.224565,
        0.030379, -0.414003,
        -0.082058, -0.345830,
        -0.099398, -0.235534,
        -0.304740, -0.281878,
        -0.321543, -0.151465,
        -0.246122, -0.069141,
        -0.410383, 0.062507,
        -0.318899, 0.156955,
        -0.207511, 0.149317,
        -0.207000, 0.359823,
        -0.076118, 0.347186,
        -0.012640, 0.255336,
    ]
}
<commit_msg>fixed amount translation<commit_after>extern crate open_gl_bindings;
extern crate sdl2;

use open_gl_bindings::gl;

use sdl2::event::Event;

use std::ffi::CStr;
use std::str;

fn find_sdl_gl_driver() -> Option<u32> {
    for (index, item) in sdl2::render::drivers().enumerate() {
        if item.name == "opengl" {
            return Some(index as u32);
        }
    }
    None
}

fn main() {
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();

    let gl_attr = video_subsystem.gl_attr();
    gl_attr.set_stencil_size(1);
    gl_attr.set_context_major_version(2);
    gl_attr.set_context_minor_version(1);

    let canvas = video_subsystem
        .window("Window", 800, 600)
        .opengl()
        .build()
        .unwrap()
        .into_canvas()
        .index(find_sdl_gl_driver().unwrap())
        .build()
        .unwrap();

    let ctx = gl::Gl::load_with(|name| video_subsystem.gl_get_proc_address(name) as *const _);
    canvas.window().gl_set_context_to_current().unwrap();

    let char_ptr = unsafe { ctx.GetString(gl::VERSION) };
    let c_str: &CStr = unsafe { CStr::from_ptr(std::mem::transmute(char_ptr)) };
    let buf: &[u8] = c_str.to_bytes();
    let str_slice: &str = str::from_utf8(buf).unwrap();
    let version: String = str_slice.to_owned();

    println!("OpenGL version: {}", version);

    let window = canvas.window();

    let (width, height) = window.drawable_size();

    unsafe {
        ctx.Viewport(0, 0, width as _, height as _);

        ctx.MatrixMode(gl::PROJECTION);
        ctx.LoadIdentity();

        ctx.MatrixMode(gl::MODELVIEW);
        ctx.LoadIdentity();

        ctx.ClearColor(0.0, 0.0, 0.0, 1.0);
        ctx.Enable(gl::BLEND);
        ctx.BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
    }

    let mut world_matrix: [f32; 16] = [
        1.0,
        0.0,
        0.0,
        0.0,
        0.0,
        1.0,
        0.0,
        0.0,
        0.0,
        0.0,
        1.0,
        0.0,
        0.0,
        0.0,
        0.0,
        1.0,
    ];

    let verts: Vec<f32> = get_verts();

    let mut event_pump = sdl_context.event_pump().unwrap();

    'running: loop {
        for event in event_pump.poll_iter() {
            match event {
                Event::Quit { .. } |
                Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Escape), .. } |
                Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::F10), .. } => {
                    break 'running;
                }
                Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Left), .. } => {
                    world_matrix[12] -= 0.1;
                }
                Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Right), .. } => {
                    world_matrix[12] += 0.1;
                }
                Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Down), .. } => {
                    world_matrix[13] -= 0.1;
                }
                Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Up), .. } => {
                    world_matrix[13] += 0.1;
                }
                _ => {}
            }
        }

        draw_frame(&ctx, &world_matrix, &verts);

        window.gl_swap_window();

        std::thread::sleep(std::time::Duration::from_millis(8));
    }
}

fn draw_frame(ctx: &gl::Gl, world_matrix: &[f32; 16], verts: &Vec<f32>) {
    unsafe {
        ctx.Clear(
            gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT,
        );

        ctx.Clear(gl::STENCIL_BUFFER_BIT);

        ctx.EnableClientState(gl::VERTEX_ARRAY);
        ctx.VertexPointer(2, gl::FLOAT, 0, std::mem::transmute(verts.as_ptr()));

        ctx.MatrixMode(gl::MODELVIEW);
        ctx.PushMatrix();

        ctx.MultMatrixf(world_matrix.as_ptr());

        let cnt = (verts.len() / 2) as _;

        ctx.Enable(gl::STENCIL_TEST);

        ctx.ColorMask(gl::FALSE, gl::FALSE, gl::FALSE, gl::FALSE);
        ctx.StencilOp(gl::INVERT, gl::INVERT, gl::INVERT);
        ctx.StencilFunc(gl::ALWAYS, 0x1, 0x1);
        ctx.Color4f(1.0, 1.0, 1.0, 1.0);
        ctx.DrawArrays(gl::TRIANGLE_FAN, 0, cnt);

        ctx.ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE);
        ctx.Color4f(32.0 / 255.0, 32.0 / 255.0, 63.0 / 255.0, 1.0);

        ctx.StencilOp(gl::ZERO, gl::ZERO, gl::ZERO);
        ctx.StencilFunc(gl::EQUAL, 1, 1);
        ctx.DrawArrays(gl::TRIANGLE_FAN, 0, cnt);
        ctx.Disable(gl::STENCIL_TEST);

        //outline
        ctx.Color4f(128.0 / 255.0, 128.0 / 255.0, 32.0 / 255.0, 1.0);
        ctx.DrawArrays(gl::LINE_STRIP, 0, cnt);

        ctx.PopMatrix();
    }
}

#[cfg_attr(rustfmt, rustfmt_skip)]
fn get_verts() -> Vec<f32> {
    vec![
        -0.012640, 0.255336,
        0.152259, 0.386185,
        0.223982, 0.275978,
        0.191749, 0.169082,
        0.396864, 0.121742,
        0.355419, -0.003047,
        0.251747, -0.044495,
        0.342622, -0.234376,
        0.219218, -0.279777,
        0.122174, -0.224565,
        0.030379, -0.414003,
        -0.082058, -0.345830,
        -0.099398, -0.235534,
        -0.304740, -0.281878,
        -0.321543, -0.151465,
        -0.246122, -0.069141,
        -0.410383, 0.062507,
        -0.318899, 0.156955,
        -0.207511, 0.149317,
        -0.207000, 0.359823,
        -0.076118, 0.347186,
        -0.012640, 0.255336,
    ]
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate git2;

use git2::{Repository, Remote, Error};
use clap::{Arg, App};
use std::path::Path;
use std::collections::HashMap;

const E_NO_GIT_REPO : i32 = 1;

fn main() {
    let options = App::new("git-submerge")
                          .version("0.1")
                          .author("Alexander Batischev <eual.jp@gmail.com>")
                          .about("Merges git submodule into the repo as if it was that way from the start")
                          .arg(Arg::with_name("SUBMODULE_DIR")
                               .help("The submodule to merge")
                               .required(true)
                               .index(1))
                          .get_matches();
    // We can safely use unwrap() here because if the option is empty, Clap would've already shown
    // the error message and aborted.
    let submodule_dir = options.value_of("SUBMODULE_DIR").unwrap();
    println!("Merging {}...", submodule_dir);

    let repo = match Repository::open(".") {
        Ok(repo) => repo,
        Err(e) => {
            eprintln!("Couldn't find Git repo in the current directory: {}", e.message());
            std::process::exit(E_NO_GIT_REPO);
        },
    };

    // 1. Add submodule as a remote
    let mut remote = add_remote(&repo, submodule_dir).expect("Couldn't add a remote");
    // 2. Fetch submodule's history
    remote.fetch(&[], None, None).expect("Couldn't fetch submodule's history");
    // 3. Find out submodule's HEAD commit id
    let submodules = repo.submodules().expect("Couldn't obtain a list of submodules");
    let submodule_path = Path::new(submodule_dir);
    let submodule = submodules.iter().find(|s| s.path() == submodule_path)
        .expect("Couldn't find the submodule with expected path");
    let submodule_head = submodule.head_id()
        .expect("Couldn't obtain submodule's HEAD");
    // 4. Rewrite submodule branch's history, moving everything under a single directory named
    //    after the submodule
    let mut revwalk = repo.revwalk().expect("Couldn't obtain RevWalk object for the repo");
    revwalk.set_sorting(git2::SORT_REVERSE | git2::SORT_TOPOLOGICAL);
    revwalk.push(submodule_head).expect("Couldn't add submodule's HEAD to RevWalk list");

    let mut old_id_to_new = HashMap::new();

    for maybe_oid in revwalk {
        match maybe_oid {
            Ok(oid) => {
                // 4.1. Extract the tree
                // 4.2. Obtain the new tree, where everything from the old one is moved under
                //   a directory named after the submodule
                // 4.3. Create new commit with the new tree
                // 4.4. Update the map with the new commit's ID
                println!("{:?}", oid);
                old_id_to_new.insert(oid, oid);
            },
            Err(e) =>
                eprintln!("Error walking the submodule's history: {:?}", e),
        }
    };
    // 5. Run through main branch's history and note down commit IDs where submodule was touched,
    //    along with submodule's commit ID
    // 6. Run through old and new submodule's history (in lockstep) and note down new commit IDs of
    //    the commits that were referenced in the main repo
    // 7. Remove submodule's remote
    // 8. Run through master's history, doing two things:
    //      8.1 updating the tree to contain the relevant tree from submodule
    //      8.2 in commits that used to update the submodule, add a parent pointing to appropriate
    //          commit in new submodule history
}

fn add_remote<'a>(repo : &'a Repository, submodule_name : &str) -> Result<Remote<'a>, Error> {
    // TODO: randomize remote's name or at least check that it doesn't exist already
    let url = String::from("./") + submodule_name;
    repo.remote(submodule_name, &url)
}
<commit_msg>Move commit's files into a directory<commit_after>extern crate clap;
extern crate git2;

use git2::{Repository, Remote, Error, Index};
use clap::{Arg, App};
use std::path::Path;
use std::collections::HashMap;

const E_NO_GIT_REPO : i32 = 1;

fn main() {
    let options = App::new("git-submerge")
                          .version("0.1")
                          .author("Alexander Batischev <eual.jp@gmail.com>")
                          .about("Merges git submodule into the repo as if it was that way from the start")
                          .arg(Arg::with_name("SUBMODULE_DIR")
                               .help("The submodule to merge")
                               .required(true)
                               .index(1))
                          .get_matches();
    // We can safely use unwrap() here because if the option is empty, Clap would've already shown
    // the error message and aborted.
    let submodule_dir = options.value_of("SUBMODULE_DIR").unwrap();
    println!("Merging {}...", submodule_dir);

    let repo = match Repository::open(".") {
        Ok(repo) => repo,
        Err(e) => {
            eprintln!("Couldn't find Git repo in the current directory: {}", e.message());
            std::process::exit(E_NO_GIT_REPO);
        },
    };

    // 1. Add submodule as a remote
    let mut remote = add_remote(&repo, submodule_dir).expect("Couldn't add a remote");
    // 2. Fetch submodule's history
    remote.fetch(&[], None, None).expect("Couldn't fetch submodule's history");
    // 3. Find out submodule's HEAD commit id
    let submodules = repo.submodules().expect("Couldn't obtain a list of submodules");
    let submodule_path = Path::new(submodule_dir);
    let submodule = submodules.iter().find(|s| s.path() == submodule_path)
        .expect("Couldn't find the submodule with expected path");
    let submodule_head = submodule.head_id()
        .expect("Couldn't obtain submodule's HEAD");
    // 4. Rewrite submodule branch's history, moving everything under a single directory named
    //    after the submodule
    let mut revwalk = repo.revwalk().expect("Couldn't obtain RevWalk object for the repo");
    revwalk.set_sorting(git2::SORT_REVERSE | git2::SORT_TOPOLOGICAL);
    revwalk.push(submodule_head).expect("Couldn't add submodule's HEAD to RevWalk list");

    let mut old_id_to_new = HashMap::new();

    for maybe_oid in revwalk {
        match maybe_oid {
            Ok(oid) => {
                println!("{:?}", oid);
                // 4.1. Extract the tree
                let commit = repo.find_commit(oid).expect(&format!("Couldn't get a commit with ID {}", oid));
                let tree = commit.tree().expect(&format!("Couldn't obtain the tree of a commit with ID {}", oid));
                let mut old_index = Index::new().expect("Couldn't create an in-memory index for commit");
                let mut new_index = Index::new().expect("Couldn't create an in-memory index");
                old_index.read_tree(&tree).expect(&format!("Couldn't read the commit {} into index", oid));
                // 4.2. Obtain the new tree, where everything from the old one is moved under
                //   a directory named after the submodule
                for entry in old_index.iter() {
                    let mut new_entry = entry;

                    let mut new_path = String::from(submodule_dir);
                    new_path += "/";
                    new_path += &String::from_utf8(new_entry.path).expect("Failed to convert a path to str");

                    new_entry.path = new_path.into_bytes();
                    new_index.add(&new_entry);
                }
                // 4.3. TODO: Create new commit with the new tree
                // 4.4. TODO: Update the map with the new commit's ID
                old_id_to_new.insert(oid, oid);
            },
            Err(e) =>
                eprintln!("Error walking the submodule's history: {:?}", e),
        }
    };
    // 5. Run through main branch's history and note down commit IDs where submodule was touched,
    //    along with submodule's commit ID
    // 6. Run through old and new submodule's history (in lockstep) and note down new commit IDs of
    //    the commits that were referenced in the main repo
    // 7. Remove submodule's remote
    // 8. Run through master's history, doing two things:
    //      8.1 updating the tree to contain the relevant tree from submodule
    //      8.2 in commits that used to update the submodule, add a parent pointing to appropriate
    //          commit in new submodule history
}

fn add_remote<'a>(repo : &'a Repository, submodule_name : &str) -> Result<Remote<'a>, Error> {
    // TODO: randomize remote's name or at least check that it doesn't exist already
    let url = String::from("./") + submodule_name;
    repo.remote(submodule_name, &url)
}
<|endoftext|>


<commit_before>#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
#[macro_use]
extern crate glium;
extern crate cgmath;

use std::path::{Path, PathBuf};
use std::sync::Arc;

use glium::Surface;
use glium::backend::Facade;
use glium::glutin::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode};

use cgmath::Vector3;

mod aabb;
mod camera;
mod input;
mod renderer;
mod scene;

use camera::Camera;
use input::InputState;
use scene::{Scene, GPUScene};

/// Get the root directory of the project
fn get_project_root() -> PathBuf {
    let exe_dir = std::env::current_exe().unwrap();
    let mut parent_dir = exe_dir.parent().unwrap();
    // This fails if the executable is not in the project tree
    // or the directory is renamed
    while !(parent_dir.ends_with("rusty") || parent_dir.ends_with("Rusty")) {
        parent_dir = parent_dir.parent()
            .expect(&format!("Failed to find project root from {:?}!", exe_dir));
    }
    parent_dir.to_path_buf()
}

fn new_scene<F: Facade>(path: &Path, facade: &F) -> (Arc<Scene>, GPUScene, Camera) {
    let scene = Scene::new(path);
    let gpu_scene = scene.upload_data(facade);
    let mut camera = Camera::new(scene.center() + scene.size() * Vector3::new(0.0, 0.0, 1.0f32),
                                 Vector3::new(0.0, 0.0, -1.0f32));
    camera.set_scale(scene.size());
    (Arc::new(scene), gpu_scene, camera)
}

fn main() {
    let mut events_loop = glium::glutin::EventsLoop::new();
    let window = glium::glutin::WindowBuilder::new();
    let context = glium::glutin::ContextBuilder::new().with_depth_buffer(24);
    let display = glium::Display::new(window, context, &events_loop).expect("Failed to create display");

    let root_path = get_project_root();
    // TODO: Enable use of arbitrary scene
    let scenes = vec!(root_path.join("scenes/plane.obj"),
                      root_path.join("scenes/cornell/cornell.obj"),
                      root_path.join("scenes/cornell/cornell_chesterfield.obj"),
                      root_path.join("scenes/cornell-box/CornellBox-Original.obj"),
                      root_path.join("scenes/cornell-box/CornellBox-Glossy.obj"),
                      root_path.join("scenes/cornell-box/CornellBox-Water.obj"),
                      root_path.join("scenes/nanosuit/nanosuit.obj"),
                      root_path.join("scenes/sibenik/sibenik.obj"));

    let (mut scene, mut gpu_scene, mut camera) = new_scene(&scenes[0], &display);
    let gl_renderer = renderer::GLRenderer::new(&display);
    let mut pt_renderer = renderer::PTRenderer::new(&display);

    let mut input = InputState::new();
    let mut trace = false;
    let mut quit = false;

    loop {
        let mut target = display.draw();

        let (width, height) = target.get_dimensions();
        // Don't draw if the window is minimized
        if width != 0 && height != 0 {
            let camera_to_clip = camera.get_camera_to_clip(width, height);
            let world_to_camera = camera.get_world_to_camera();

            target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);
            if trace {
                pt_renderer.render(&mut target);
            } else {
                gl_renderer.render(&gpu_scene, &mut target, camera_to_clip * world_to_camera);
            }
        }
        target.finish().unwrap();

        events_loop.poll_events(|event| {
            input.update(&event);
            match event {
                Event::WindowEvent{event: WindowEvent::KeyboardInput{input, ..}, ..} => {
                    match input {
                        KeyboardInput{state: ElementState::Pressed,
                                      virtual_keycode: Some(VirtualKeyCode::Space), ..} => {
                            trace = !trace;
                            if trace {
                                pt_renderer.start_render(&display, &scene, &camera, width, height);
                            } else {
                                pt_renderer.wait_for_close();
                            }
                        },
                        // TODO: make this a macro
                        KeyboardInput{state: ElementState::Pressed,
                                      virtual_keycode: Some(VirtualKeyCode::Key1), ..} => {
                            let res = new_scene(&scenes[0], &display);
                            scene = res.0;
                            gpu_scene = res.1;
                            camera = res.2;
                        },
                        KeyboardInput{state: ElementState::Pressed,
                                      virtual_keycode: Some(VirtualKeyCode::Key2), ..} => {
                            let res = new_scene(&scenes[1], &display);
                            scene = res.0;
                            gpu_scene = res.1;
                            camera = res.2;
                        },
                        KeyboardInput{state: ElementState::Pressed,
                                      virtual_keycode: Some(VirtualKeyCode::Key3), ..} => {
                            let res = new_scene(&scenes[2], &display);
                            scene = res.0;
                            gpu_scene = res.1;
                            camera = res.2;
                        },
                        KeyboardInput{state: ElementState::Pressed,
                                      virtual_keycode: Some(VirtualKeyCode::Key4), ..} => {
                            let res = new_scene(&scenes[3], &display);
                            scene = res.0;
                            gpu_scene = res.1;
                            camera = res.2;
                        },
                        KeyboardInput{state: ElementState::Pressed,
                                      virtual_keycode: Some(VirtualKeyCode::Key5), ..} => {
                            let res = new_scene(&scenes[4], &display);
                            scene = res.0;
                            gpu_scene = res.1;
                            camera = res.2;
                        },
                        KeyboardInput{state: ElementState::Pressed,
                                      virtual_keycode: Some(VirtualKeyCode::Key6), ..} => {
                            let res = new_scene(&scenes[5], &display);
                            scene = res.0;
                            gpu_scene = res.1;
                            camera = res.2;
                        },
                        KeyboardInput{state: ElementState::Pressed,
                                      virtual_keycode: Some(VirtualKeyCode::Key7), ..} => {
                            let res = new_scene(&scenes[6], &display);
                            scene = res.0;
                            gpu_scene = res.1;
                            camera = res.2;
                        },
                        KeyboardInput{state: ElementState::Pressed,
                                      virtual_keycode: Some(VirtualKeyCode::Key8), ..} => {
                            let res = new_scene(&scenes[7], &display);
                            scene = res.0;
                            gpu_scene = res.1;
                            camera = res.2;
                        },
                        _ => ()
                    }
                }
                Event::WindowEvent{event: WindowEvent::Closed, ..} => quit = true,
                _ => ()
            }
        });
        camera.process_input(&input);
        input.reset_deltas();
        if quit {
            println!("Stopping threads");
            pt_renderer.wait_for_close();
            println!("Stopped");
            return;
        }
    }
}
<commit_msg>Refactor scene switching<commit_after>#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
#[macro_use]
extern crate glium;
extern crate cgmath;

use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::Arc;

use glium::Surface;
use glium::backend::Facade;
use glium::glutin::{Event, WindowEvent, KeyboardInput, ElementState, VirtualKeyCode};

use cgmath::Vector3;

mod aabb;
mod camera;
mod input;
mod renderer;
mod scene;

use camera::Camera;
use input::InputState;
use scene::{Scene, GPUScene};

/// Get the root directory of the project
fn get_project_root() -> PathBuf {
    let exe_dir = std::env::current_exe().unwrap();
    let mut parent_dir = exe_dir.parent().unwrap();
    // This fails if the executable is not in the project tree
    // or the directory is renamed
    while !(parent_dir.ends_with("rusty") || parent_dir.ends_with("Rusty")) {
        parent_dir = parent_dir.parent()
            .expect(&format!("Failed to find project root from {:?}!", exe_dir));
    }
    parent_dir.to_path_buf()
}

fn load_scene<F: Facade>(path: &Path, facade: &F) -> (Arc<Scene>, GPUScene, Camera) {
    let scene = Scene::new(path);
    let gpu_scene = scene.upload_data(facade);
    let mut camera = Camera::new(scene.center() + scene.size() * Vector3::new(0.0, 0.0, 1.0f32),
                                 Vector3::new(0.0, 0.0, -1.0f32));
    camera.set_scale(scene.size());
    (Arc::new(scene), gpu_scene, camera)
}

fn main() {
    let mut events_loop = glium::glutin::EventsLoop::new();
    let window = glium::glutin::WindowBuilder::new();
    let context = glium::glutin::ContextBuilder::new().with_depth_buffer(24);
    let display = glium::Display::new(window, context, &events_loop).expect("Failed to create display");

    let root_path = get_project_root();
    // TODO: Enable use of arbitrary scene
    let scenes: HashMap<VirtualKeyCode, PathBuf> =
        [(VirtualKeyCode::Key1, root_path.join("scenes/plane.obj")),
         (VirtualKeyCode::Key2, root_path.join("scenes/cornell/cornell.obj")),
         (VirtualKeyCode::Key3, root_path.join("scenes/cornell/cornell_chesterfield.obj")),
         (VirtualKeyCode::Key4, root_path.join("scenes/cornell-box/CornellBox-Original.obj")),
         (VirtualKeyCode::Key5, root_path.join("scenes/cornell-box/CornellBox-Glossy.obj")),
         (VirtualKeyCode::Key6, root_path.join("scenes/cornell-box/CornellBox-Water.obj")),
         (VirtualKeyCode::Key7, root_path.join("scenes/nanosuit/nanosuit.obj")),
         (VirtualKeyCode::Key8, root_path.join("scenes/sibenik/sibenik.obj"))]
        .iter().cloned().collect();

    let (mut scene, mut gpu_scene, mut camera) =
        load_scene(&scenes[&VirtualKeyCode::Key1], &display);
    let gl_renderer = renderer::GLRenderer::new(&display);
    let mut pt_renderer = renderer::PTRenderer::new(&display);

    let mut input = InputState::new();
    let mut trace = false;
    let mut quit = false;

    loop {
        let mut target = display.draw();

        let (width, height) = target.get_dimensions();
        // Don't draw if the window is minimized
        if width != 0 && height != 0 {
            let camera_to_clip = camera.get_camera_to_clip(width, height);
            let world_to_camera = camera.get_world_to_camera();

            target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);
            if trace {
                pt_renderer.render(&mut target);
            } else {
                gl_renderer.render(&gpu_scene, &mut target, camera_to_clip * world_to_camera);
            }
        }
        target.finish().unwrap();

        events_loop.poll_events(|event| {
            input.update(&event);
            match event {
                Event::WindowEvent{event: WindowEvent::KeyboardInput{input, ..}, ..} => {
                    match input {
                        KeyboardInput{state: ElementState::Pressed,
                                      virtual_keycode: Some(VirtualKeyCode::Space), ..} => {
                            trace = !trace;
                            if trace {
                                pt_renderer.start_render(&display, &scene, &camera, width, height);
                            } else {
                                pt_renderer.wait_for_close();
                            }
                        },
                        KeyboardInput{state: ElementState::Pressed,
                                      virtual_keycode: Some(ref keycode), ..} => {
                            if let Some(scene_to_load) = scenes.get(keycode) {
                                let res = load_scene(scene_to_load, &display);
                                scene = res.0;
                                gpu_scene = res.1;
                                camera = res.2;
                            }
                        },
                        _ => ()
                    }
                }
                Event::WindowEvent{event: WindowEvent::Closed, ..} => quit = true,
                _ => ()
            }
        });
        camera.process_input(&input);
        input.reset_deltas();
        if quit {
            println!("Stopping threads");
            pt_renderer.wait_for_close();
            println!("Stopped");
            return;
        }
    }
}
<|endoftext|>