<commit_before>import driver::session::session;
import middle::ty;
import syntax::{ast, visit};
import syntax::attr;
import syntax::codemap::span;
import std::map::{map,hashmap,int_hash,hash_from_strs};
import std::smallintmap::{map,smallintmap};
import io::writer_util;
import syntax::print::pprust::expr_to_str;

export lint, ctypes, unused_imports;
export level, ignore, warn, error;
export lookup_lint, lint_dict, get_lint_dict, check_crate;

#[doc="

A 'lint' check is a kind of miscellaneous constraint that a user _might_ want
to enforce, but might reasonably want to permit as well, on a module-by-module
basis. They contrast with static constraints enforced by other phases of the
compiler, which are generally required to hold in order to compile the program
at all.

"]

enum lint {
    ctypes,
    unused_imports,
    while_true,
    path_statement,
    old_vecs,
}

// This is pretty unfortunate. We really want some sort of "deriving Enum"
// type of thing.
fn int_to_lint(i: int) -> lint {
    alt check i {
      0 { ctypes }
      1 { unused_imports }
      2 { while_true }
      3 { path_statement }
      4 { old_vecs }
    }
}

enum level {
    ignore, warn, error
}

type lint_spec = @{lint: lint,
                   desc: str,
                   default: level};

type lint_dict = hashmap<str,lint_spec>;

/*
  Pass names should not contain a '-', as the compiler normalizes
  '-' to '_' in command-line flags
 */
fn get_lint_dict() -> lint_dict {
    let v = [
        ("ctypes",
         @{lint: ctypes,
           desc: "proper use of core::libc types in native modules",
           default: warn}),

        ("unused_imports",
         @{lint: unused_imports,
           desc: "imports that are never used",
           default: ignore}),

        ("while_true",
         @{lint: while_true,
           desc: "suggest using loop { } instead of while(true) { }",
           default: warn}),

        ("path_statement",
         @{lint: path_statement,
           desc: "path statements with no effect",
           default: warn}),

        ("old_vecs",
         @{lint: old_vecs,
           desc: "old (deprecated) vectors and strings",
           default: ignore})

    ];
    hash_from_strs(v)
}

type ctxt = @{dict: lint_dict,
              curr: smallintmap<level>,
              tcx: ty::ctxt};

impl methods for ctxt {
    fn get_level(lint: lint) -> level {
        alt self.curr.find(lint as uint) {
          some(c) { c }
          none { ignore }
        }
    }

    fn set_level(lint: lint, level: level) {
        if level == ignore {
            self.curr.remove(lint as uint);
        } else {
            self.curr.insert(lint as uint, level);
        }
    }

    fn span_lint(level: level, span: span, msg: str) {
        alt level {
          ignore { }
          warn { self.tcx.sess.span_warn(span, msg); }
          error { self.tcx.sess.span_err(span, msg); }
        }
    }

    #[doc="
          Merge the warnings specified by any `warn(...)` attributes into the
          current lint context, call the provided function, then reset the
          warnings in effect to their previous state.
    "]
    fn with_warn_attrs(attrs: [ast::attribute], f: fn(ctxt)) {

        let mut undo = [];

        let metas = attr::attr_metas(attr::find_attrs_by_name(attrs, "warn"));
        for metas.each {|meta|
            alt meta.node {
              ast::meta_list(_, metas) {
                for metas.each {|meta|
                    alt meta.node {
                      ast::meta_word(lintname) {
                        alt lookup_lint(self.dict, lintname) {
                          none {
                            self.tcx.sess.span_err(
                                meta.span,
                                #fmt("unknown warning: '%s'", lintname));
                          }
                          some((lint, new_level)) {
                            let old_level = self.get_level(lint);
                            self.set_level(lint, new_level);
                            undo += [(lint, old_level)]
                          }
                        }
                      }
                      _ {
                        self.tcx.sess.span_err(
                            meta.span,
                            "malformed warning attribute");
                      }
                    }
                }
              }
              _ {
                self.tcx.sess.span_err(meta.span,
                                       "malformed warning attribute");
              }
            }
        }

        f(self);

        for undo.each {|pair|
            let (lint,old_level) = pair;
            self.set_level(lint, old_level);
        }
    }
}

fn lookup_lint(dict: lint_dict, s: str)
    -> option<(lint, level)> {
    let s = str::replace(s, "-", "_");
    let (name, level) = if s.starts_with("no_") {
        (s.substr(3u, s.len() - 3u), ignore)
    } else if s.starts_with("err_") {
        (s.substr(4u, s.len() - 4u), error)
    } else {
        (s, warn)
    };
    alt dict.find(name) {
      none { none }
      some(spec) { some((spec.lint, level)) }
    }
}

// FIXME: Copied from driver.rs, to work around a bug(#1566)
fn time(do_it: bool, what: str, thunk: fn()) {
    if !do_it{ ret thunk(); }
    let start = std::time::precise_time_s();
    thunk();
    let end = std::time::precise_time_s();
    io::stdout().write_str(#fmt("time: %3.3f s\t%s\n",
                                end - start, what));
}

fn check_item(cx: ctxt, i: @ast::item) {
    cx.with_warn_attrs(i.attrs) {|cx|
        for cx.curr.each {|lint, level|
            alt int_to_lint(lint as int) {
              ctypes { check_item_ctypes(cx, level, i); }
              unused_imports { check_item_unused_imports(cx, level, i); }
              while_true { check_item_while_true(cx, level, i); }
              path_statement { check_item_path_statement(cx, level, i); }
              old_vecs { check_item_old_vecs(cx, level, i); }
            }
        }
    }
}

fn check_item_while_true(cx: ctxt, level: level, it: @ast::item) {
    let visit = visit::mk_simple_visitor(@{
        visit_expr: fn@(e: @ast::expr) {
           alt e.node {
             ast::expr_while(cond, _) {
                alt cond.node {
                    ast::expr_lit(@{node: ast::lit_bool(true),_}) {
                            cx.span_lint(
                              level, e.span,
                              "denote infinite loops with loop { ... }");
                    }
                    _ {}
                }
             }
             _ {}
          }
        }
        with *visit::default_simple_visitor()
    });
    visit::visit_item(it, (), visit);
}

fn check_item_unused_imports(_cx: ctxt, _level: level, _it: @ast::item) {
    // FIXME: Don't know how to check this in lint yet, it's currently being
    // done over in resolve. When resolve is rewritten, do it here instead.
}

fn check_item_ctypes(cx: ctxt, level: level, it: @ast::item) {

    fn check_native_fn(cx: ctxt, level: level, decl: ast::fn_decl) {
        let tys = vec::map(decl.inputs) {|a| a.ty };
        for vec::each(tys + [decl.output]) {|ty|
            alt ty.node {
              ast::ty_path(_, id) {
                alt cx.tcx.def_map.get(id) {
                  ast::def_prim_ty(ast::ty_int(ast::ty_i)) {
                    cx.span_lint(
                        level, ty.span,
                        "found rust type `int` in native module, while \
                         libc::c_int or libc::c_long should be used");
                  }
                  ast::def_prim_ty(ast::ty_uint(ast::ty_u)) {
                    cx.span_lint(
                        level, ty.span,
                        "found rust type `uint` in native module, while \
                         libc::c_uint or libc::c_ulong should be used");
                  }
                  _ { }
                }
              }
              _ { }
            }
        }
    }

    alt it.node {
      ast::item_native_mod(nmod) if attr::native_abi(it.attrs) !=
      either::right(ast::native_abi_rust_intrinsic) {
        for nmod.items.each {|ni|
            alt ni.node {
              ast::native_item_fn(decl, tps) {
                check_native_fn(cx, level, decl);
              }
            }
        }
      }
      _ {/* nothing to do */ }
    }
}

fn check_item_path_statement(cx: ctxt, level: level, it: @ast::item) {
    let visit = visit::mk_simple_visitor(@{
        visit_stmt: fn@(s: @ast::stmt) {
            alt s.node {
              ast::stmt_semi(@{id: _,
                               node: ast::expr_path(@path),
                               span: _}, _) {
                cx.span_lint(
                    level, s.span,
                    "path statement with no effect");
              }
              _ {}
            }
        }
        with *visit::default_simple_visitor()
    });
    visit::visit_item(it, (), visit);
}

fn check_item_old_vecs(cx: ctxt, level: level, it: @ast::item) {
    let uses_vstore = int_hash();

    let visit = visit::mk_simple_visitor(@{

        visit_expr:fn@(e: @ast::expr) {
            alt e.node {
              ast::expr_vec(_, _) |
              ast::expr_lit(@{node: ast::lit_str(_), span:_})
              if ! uses_vstore.contains_key(e.id) {
                cx.span_lint(level, e.span, "deprecated vec/str expr");
              }
              ast::expr_vstore(@inner, _) {
                uses_vstore.insert(inner.id, true);
              }
              _ { }
            }
        },

        visit_ty: fn@(t: @ast::ty) {
            alt t.node {
              ast::ty_vec(_)
              if ! uses_vstore.contains_key(t.id) {
                cx.span_lint(level, t.span, "deprecated vec type");
              }

              ast::ty_path(@{span: _, global: _, idents: ids,
                             rp: none, types: _}, _)
              if ids == ["str"] && (! uses_vstore.contains_key(t.id)) {
                cx.span_lint(level, t.span, "deprecated str type");
              }

              ast::ty_vstore(inner, _) {
                uses_vstore.insert(inner.id, true);
              }
              _ { }
            }
        }

        with *visit::default_simple_visitor()
    });
    visit::visit_item(it, (), visit);
}

fn check_crate(tcx: ty::ctxt, crate: @ast::crate,
               lint_opts: [(lint, level)], time_pass: bool) {

    fn hash_lint(&&lint: lint) -> uint { lint as uint }
    fn eq_lint(&&a: lint, &&b: lint) -> bool { a == b }

    let cx = @{dict: get_lint_dict(),
               curr: std::smallintmap::mk(),
               tcx: tcx};

    // Install defaults.
    for cx.dict.each {|_k, spec| cx.set_level(spec.lint, spec.default); }

    // Install command-line options, overriding defaults.
    for lint_opts.each {|pair|
        let (lint,level) = pair;
        cx.set_level(lint, level);
    }

    time(time_pass, "lint checking") {||
        cx.with_warn_attrs(crate.node.attrs) {|cx|
            let visit = visit::mk_simple_visitor(@{
                visit_item: fn@(i: @ast::item) { check_item(cx, i); }
                with *visit::default_simple_visitor()
            });
            visit::visit_crate(*crate, (), visit);
        }
    }

    tcx.sess.abort_if_errors();
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//
<commit_msg>Rework lint to copy lint mode maps when changing them.<commit_after>import driver::session::session;
import middle::ty;
import syntax::{ast, visit};
import syntax::attr;
import syntax::codemap::span;
import std::map::{map,hashmap,int_hash,hash_from_strs};
import std::smallintmap::{map,smallintmap};
import io::writer_util;
import syntax::print::pprust::expr_to_str;

export lint, ctypes, unused_imports;
export level, ignore, warn, error;
export lookup_lint, lint_dict, get_lint_dict, check_crate;

#[doc="

A 'lint' check is a kind of miscellaneous constraint that a user _might_ want
to enforce, but might reasonably want to permit as well, on a module-by-module
basis. They contrast with static constraints enforced by other phases of the
compiler, which are generally required to hold in order to compile the program
at all.

"]

enum lint {
    ctypes,
    unused_imports,
    while_true,
    path_statement,
    old_vecs,
}

// This is pretty unfortunate. We really want some sort of "deriving Enum"
// type of thing.
fn int_to_lint(i: int) -> lint {
    alt check i {
      0 { ctypes }
      1 { unused_imports }
      2 { while_true }
      3 { path_statement }
      4 { old_vecs }
    }
}

enum level {
    ignore, warn, error
}

type lint_spec = @{lint: lint,
                   desc: str,
                   default: level};

type lint_dict = hashmap<str,lint_spec>;

/*
  Pass names should not contain a '-', as the compiler normalizes
  '-' to '_' in command-line flags
 */
fn get_lint_dict() -> lint_dict {
    let v = [
        ("ctypes",
         @{lint: ctypes,
           desc: "proper use of core::libc types in native modules",
           default: warn}),

        ("unused_imports",
         @{lint: unused_imports,
           desc: "imports that are never used",
           default: ignore}),

        ("while_true",
         @{lint: while_true,
           desc: "suggest using loop { } instead of while(true) { }",
           default: warn}),

        ("path_statement",
         @{lint: path_statement,
           desc: "path statements with no effect",
           default: warn}),

        ("old_vecs",
         @{lint: old_vecs,
           desc: "old (deprecated) vectors and strings",
           default: ignore})

    ];
    hash_from_strs(v)
}

// This is a highly not-optimal set of data structure decisions.
type lint_modes = smallintmap<level>;
type lint_mode_map = hashmap<ast::node_id, lint_modes>;

type warning_settings = {
    default_settings: lint_modes,
    settings_map: lint_mode_map
};

// This is kind of unfortunate. It should be somewhere else, or we should use
// a persistent data structure...
fn clone_lint_modes(modes: lint_modes) -> lint_modes {
    @{v: copy modes.v}
}

type ctxt = {dict: lint_dict,
             curr: lint_modes,
             tcx: ty::ctxt};

impl methods for ctxt {
    fn get_level(lint: lint) -> level {
        alt self.curr.find(lint as uint) {
          some(c) { c }
          none { ignore }
        }
    }

    fn set_level(lint: lint, level: level) {
        if level == ignore {
            self.curr.remove(lint as uint);
        } else {
            self.curr.insert(lint as uint, level);
        }
    }

    fn span_lint(level: level, span: span, msg: str) {
        alt level {
          ignore { }
          warn { self.tcx.sess.span_warn(span, msg); }
          error { self.tcx.sess.span_err(span, msg); }
        }
    }

    #[doc="
          Merge the warnings specified by any `warn(...)` attributes into the
          current lint context, call the provided function, then reset the
          warnings in effect to their previous state.
    "]
    fn with_warn_attrs(attrs: [ast::attribute], f: fn(ctxt)) {

        let mut new_ctxt = self;

        let metas = attr::attr_metas(attr::find_attrs_by_name(attrs, "warn"));
        for metas.each {|meta|
            alt meta.node {
              ast::meta_list(_, metas) {
                for metas.each {|meta|
                    alt meta.node {
                      ast::meta_word(lintname) {
                        alt lookup_lint(self.dict, lintname) {
                          none {
                            self.tcx.sess.span_err(
                                meta.span,
                                #fmt("unknown warning: '%s'", lintname));
                          }
                          some((lint, new_level)) {
                            // we do multiple unneeded copies of the map
                            // if many attributes are set, but this shouldn't
                            // actually be a problem...
                            new_ctxt = {curr: clone_lint_modes(self.curr)
                                        with new_ctxt};
                            new_ctxt.set_level(lint, new_level);
                          }
                        }
                      }
                      _ {
                        self.tcx.sess.span_err(
                            meta.span,
                            "malformed warning attribute");
                      }
                    }
                }
              }
              _ {
                self.tcx.sess.span_err(meta.span,
                                       "malformed warning attribute");
              }
            }
        }

        f(new_ctxt);
    }
}

fn lookup_lint(dict: lint_dict, s: str)
    -> option<(lint, level)> {
    let s = str::replace(s, "-", "_");
    let (name, level) = if s.starts_with("no_") {
        (s.substr(3u, s.len() - 3u), ignore)
    } else if s.starts_with("err_") {
        (s.substr(4u, s.len() - 4u), error)
    } else {
        (s, warn)
    };
    alt dict.find(name) {
      none { none }
      some(spec) { some((spec.lint, level)) }
    }
}

// FIXME: Copied from driver.rs, to work around a bug(#1566)
fn time(do_it: bool, what: str, thunk: fn()) {
    if !do_it{ ret thunk(); }
    let start = std::time::precise_time_s();
    thunk();
    let end = std::time::precise_time_s();
    io::stdout().write_str(#fmt("time: %3.3f s\t%s\n",
                                end - start, what));
}

fn check_item(cx: ctxt, i: @ast::item) {
    cx.with_warn_attrs(i.attrs) {|cx|
        for cx.curr.each {|lint, level|
            alt int_to_lint(lint as int) {
              ctypes { check_item_ctypes(cx, level, i); }
              unused_imports { check_item_unused_imports(cx, level, i); }
              while_true { check_item_while_true(cx, level, i); }
              path_statement { check_item_path_statement(cx, level, i); }
              old_vecs { check_item_old_vecs(cx, level, i); }
            }
        }
    }
}

fn check_item_while_true(cx: ctxt, level: level, it: @ast::item) {
    let visit = visit::mk_simple_visitor(@{
        visit_expr: fn@(e: @ast::expr) {
           alt e.node {
             ast::expr_while(cond, _) {
                alt cond.node {
                    ast::expr_lit(@{node: ast::lit_bool(true),_}) {
                            cx.span_lint(
                              level, e.span,
                              "denote infinite loops with loop { ... }");
                    }
                    _ {}
                }
             }
             _ {}
          }
        }
        with *visit::default_simple_visitor()
    });
    visit::visit_item(it, (), visit);
}

fn check_item_unused_imports(_cx: ctxt, _level: level, _it: @ast::item) {
    // FIXME: Don't know how to check this in lint yet, it's currently being
    // done over in resolve. When resolve is rewritten, do it here instead.
}

fn check_item_ctypes(cx: ctxt, level: level, it: @ast::item) {

    fn check_native_fn(cx: ctxt, level: level, decl: ast::fn_decl) {
        let tys = vec::map(decl.inputs) {|a| a.ty };
        for vec::each(tys + [decl.output]) {|ty|
            alt ty.node {
              ast::ty_path(_, id) {
                alt cx.tcx.def_map.get(id) {
                  ast::def_prim_ty(ast::ty_int(ast::ty_i)) {
                    cx.span_lint(
                        level, ty.span,
                        "found rust type `int` in native module, while \
                         libc::c_int or libc::c_long should be used");
                  }
                  ast::def_prim_ty(ast::ty_uint(ast::ty_u)) {
                    cx.span_lint(
                        level, ty.span,
                        "found rust type `uint` in native module, while \
                         libc::c_uint or libc::c_ulong should be used");
                  }
                  _ { }
                }
              }
              _ { }
            }
        }
    }

    alt it.node {
      ast::item_native_mod(nmod) if attr::native_abi(it.attrs) !=
      either::right(ast::native_abi_rust_intrinsic) {
        for nmod.items.each {|ni|
            alt ni.node {
              ast::native_item_fn(decl, tps) {
                check_native_fn(cx, level, decl);
              }
            }
        }
      }
      _ {/* nothing to do */ }
    }
}

fn check_item_path_statement(cx: ctxt, level: level, it: @ast::item) {
    let visit = visit::mk_simple_visitor(@{
        visit_stmt: fn@(s: @ast::stmt) {
            alt s.node {
              ast::stmt_semi(@{id: _,
                               node: ast::expr_path(@path),
                               span: _}, _) {
                cx.span_lint(
                    level, s.span,
                    "path statement with no effect");
              }
              _ {}
            }
        }
        with *visit::default_simple_visitor()
    });
    visit::visit_item(it, (), visit);
}

fn check_item_old_vecs(cx: ctxt, level: level, it: @ast::item) {
    let uses_vstore = int_hash();

    let visit = visit::mk_simple_visitor(@{

        visit_expr:fn@(e: @ast::expr) {
            alt e.node {
              ast::expr_vec(_, _) |
              ast::expr_lit(@{node: ast::lit_str(_), span:_})
              if ! uses_vstore.contains_key(e.id) {
                cx.span_lint(level, e.span, "deprecated vec/str expr");
              }
              ast::expr_vstore(@inner, _) {
                uses_vstore.insert(inner.id, true);
              }
              _ { }
            }
        },

        visit_ty: fn@(t: @ast::ty) {
            alt t.node {
              ast::ty_vec(_)
              if ! uses_vstore.contains_key(t.id) {
                cx.span_lint(level, t.span, "deprecated vec type");
              }

              ast::ty_path(@{span: _, global: _, idents: ids,
                             rp: none, types: _}, _)
              if ids == ["str"] && (! uses_vstore.contains_key(t.id)) {
                cx.span_lint(level, t.span, "deprecated str type");
              }

              ast::ty_vstore(inner, _) {
                uses_vstore.insert(inner.id, true);
              }
              _ { }
            }
        }

        with *visit::default_simple_visitor()
    });
    visit::visit_item(it, (), visit);
}

fn check_crate(tcx: ty::ctxt, crate: @ast::crate,
               lint_opts: [(lint, level)], time_pass: bool) {

    fn hash_lint(&&lint: lint) -> uint { lint as uint }
    fn eq_lint(&&a: lint, &&b: lint) -> bool { a == b }

    let cx = {dict: get_lint_dict(),
              curr: std::smallintmap::mk(),
              tcx: tcx};

    // Install defaults.
    for cx.dict.each {|_k, spec| cx.set_level(spec.lint, spec.default); }

    // Install command-line options, overriding defaults.
    for lint_opts.each {|pair|
        let (lint,level) = pair;
        cx.set_level(lint, level);
    }

    time(time_pass, "lint checking") {||
        cx.with_warn_attrs(crate.node.attrs) {|cx|
            let visit = visit::mk_simple_visitor(@{
                visit_item: fn@(i: @ast::item) { check_item(cx, i); }
                with *visit::default_simple_visitor()
            });
            visit::visit_crate(*crate, (), visit);
        }
    }

    tcx.sess.abort_if_errors();
}

//
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//
<|endoftext|>


<commit_before>// xfail-test
use std;

import std::task;
import std::task::task;
import std::comm;
import std::comm::chan;
import std::comm::port;
import std::comm::send;
import std::comm::recv;

fn main() {
    test00();
    // test01();
    test02();
    test03();
    test04();
    test05();
    test06();
}

fn test00_start(ch: chan<int>, message: int, count: int) {
    log "Starting test00_start";
    let i: int = 0;
    while i < count {
        log "Sending Message";
        send(ch, message + 0);
        i = i + 1;
    }
    log "Ending test00_start";
}

fn test00() {
    let number_of_tasks: int = 1;
    let number_of_messages: int = 4;
    log "Creating tasks";

    let po = port();
    let ch = chan(po);

    let i: int = 0;

    let tasks = [];
    while i < number_of_tasks {
        i = i + 1;
        let thunk = bind test00_start(ch, i, number_of_messages);
        tasks += [task::spawn_joinable(thunk)];
    }
    let sum: int = 0;
    for t in tasks {
        i = 0;
        while i < number_of_messages { sum += recv(po); i = i + 1; }
    }

    for t in tasks { task::join(t); }

    log "Completed: Final number is: ";
    assert (sum ==
                number_of_messages *
                    (number_of_tasks * number_of_tasks + number_of_tasks) /
                    2);
}

fn test01() {
    let p = port();
    log "Reading from a port that is never written to.";
    let value: int = recv(p);
    log value;
}

fn test02() {
    let p = port();
    let c = chan(p);
    log "Writing to a local task channel.";
    send(c, 42);
    log "Reading from a local task port.";
    let value: int = recv(p);
    log value;
}

obj vector(mutable x: int, y: int) {
    fn length() -> int { x = x + 2; ret x + y; }
}

fn test03() {
    log "Creating object ...";
    let v: vector = vector(1, 2);
    log "created object ...";
    let t: vector = v;
    log v.length();
}

fn test04_start() {
    log "Started task";
    let i: int = 1024 * 1024;
    while i > 0 { i = i - 1; }
    log "Finished task";
}

fn test04() {
    log "Spawning lots of tasks.";
    let i: int = 4;
    while i > 0 { i = i - 1; let f = test04_start; task::spawn(f); }
    log "Finishing up.";
}

fn test05_start(ch: chan<int>) {
    send(ch, 10);
    send(ch, 20);
    send(ch, 30);
    send(ch, 30);
    send(ch, 30);
}

fn test05() {
    let po = comm::port();
    let ch = chan(po);
    task::spawn(bind test05_start(ch));
    let value: int;
    value = recv(po);
    value = recv(po);
    value = recv(po);
    log value;
}

fn test06_start(task_number: int) {
    log "Started task.";
    let i: int = 0;
    while i < 1000000 { i = i + 1; }
    log "Finished task.";
}

fn test06() {
    let number_of_tasks: int = 4;
    log "Creating tasks";

    let i: int = 0;

    let tasks = [];
    while i < number_of_tasks {
        i = i + 1;
        tasks += [task::spawn_joinable(bind test06_start(i))];
    }

    for t in tasks { task::join(t); }
}

<commit_msg>Rehabilitate run-pass/task-comm.rs<commit_after>use std;

import std::task;
import std::task::task;
import std::comm;
import std::comm::chan;
import std::comm::port;
import std::comm::send;
import std::comm::recv;

fn main() {
    test00();
    // test01();
    test02();
    test03();
    test04();
    test05();
    test06();
}

fn test00_start(&&args: (chan<int>, int, int)) {
    let (ch, message, count) = args;
    log "Starting test00_start";
    let i: int = 0;
    while i < count {
        log "Sending Message";
        send(ch, message + 0);
        i = i + 1;
    }
    log "Ending test00_start";
}

fn test00() {
    let number_of_tasks: int = 1;
    let number_of_messages: int = 4;
    log "Creating tasks";

    let po = port();
    let ch = chan(po);

    let i: int = 0;

    let tasks = [];
    while i < number_of_tasks {
        i = i + 1;
        tasks += [task::spawn_joinable(
            (ch, i, number_of_messages), test00_start)];
    }
    let sum: int = 0;
    for t in tasks {
        i = 0;
        while i < number_of_messages { sum += recv(po); i = i + 1; }
    }

    for t in tasks { task::join(t); }

    log "Completed: Final number is: ";
    assert (sum ==
                number_of_messages *
                    (number_of_tasks * number_of_tasks + number_of_tasks) /
                    2);
}

fn test01() {
    let p = port();
    log "Reading from a port that is never written to.";
    let value: int = recv(p);
    log value;
}

fn test02() {
    let p = port();
    let c = chan(p);
    log "Writing to a local task channel.";
    send(c, 42);
    log "Reading from a local task port.";
    let value: int = recv(p);
    log value;
}

obj vector(mutable x: int, y: int) {
    fn length() -> int { x = x + 2; ret x + y; }
}

fn test03() {
    log "Creating object ...";
    let v: vector = vector(1, 2);
    log "created object ...";
    let t: vector = v;
    log v.length();
}

fn test04_start(&&_args: ()) {
    log "Started task";
    let i: int = 1024 * 1024;
    while i > 0 { i = i - 1; }
    log "Finished task";
}

fn test04() {
    log "Spawning lots of tasks.";
    let i: int = 4;
    while i > 0 { i = i - 1; task::spawn((), test04_start); }
    log "Finishing up.";
}

fn test05_start(ch: chan<int>) {
    send(ch, 10);
    send(ch, 20);
    send(ch, 30);
    send(ch, 30);
    send(ch, 30);
}

fn test05() {
    let po = comm::port();
    let ch = chan(po);
    task::spawn(ch, test05_start);
    let value: int;
    value = recv(po);
    value = recv(po);
    value = recv(po);
    log value;
}

fn test06_start(&&task_number: int) {
    log "Started task.";
    let i: int = 0;
    while i < 1000000 { i = i + 1; }
    log "Finished task.";
}

fn test06() {
    let number_of_tasks: int = 4;
    log "Creating tasks";

    let i: int = 0;

    let tasks = [];
    while i < number_of_tasks {
        i = i + 1;
        tasks += [task::spawn_joinable(copy i, test06_start)];
    }

    for t in tasks { task::join(t); }
}

<|endoftext|>


<commit_before>use std::process;

pub fn compare(args: Vec<&str>) {
    let apple = process::Command::new("/Library/Developer/CommandLineTools/usr/bin/dyldinfo")
        .args(&args)
        .output()
        .expect("run Apple dyldinfo");

    let goblin = process::Command::new("cargo")
        .arg("run")
        .arg("--quiet")
        .arg("--example")
        .arg("dyldinfo")
        .arg("--")
        .args(&args)
        .output()
        .expect("run cargo dyldinfo");

    if apple.stdout.as_slice() != goblin.stdout.as_slice() {
        println!("dyldinfo calls disagree!");
        println!(
            "Apple dyldinfo {:?} output:\n{}",
            &args,
            String::from_utf8_lossy(&apple.stdout)
        );
        println!("---");
        println!(
            "cargo dyldinfo {:?} output:\n{}",
            &args,
            String::from_utf8_lossy(&goblin.stdout)
        );
        panic!(
            "Apple dyldinfo and cargo dyldinfo differed (args: {:?})",
            args
        );
    }
}

#[cfg(target_os = "macos")]
#[test]
fn compare_binds() {
    compare(vec![
        "-bind",
        "/Library/Developer/CommandLineTools/usr/bin/dyldinfo",
    ]);
    compare(vec![
        "-bind",
        "/Library/Developer/CommandLineTools/usr/bin/clang",
    ]);
    compare(vec!["-bind", "/usr/bin/tmutil"]);
}

#[cfg(target_os = "macos")]
#[test]
fn compare_lazy_binds() {
    compare(vec![
        "-lazy_bind",
        "/Library/Developer/CommandLineTools/usr/bin/dyldinfo",
    ]);
    compare(vec![
        "-lazy_bind",
        "/Library/Developer/CommandLineTools/usr/bin/clang",
    ]);
    compare(vec!["-lazy_bind", "/usr/bin/tmutil"]);
}

#[cfg(target_os = "macos")]
#[test]
fn compare_combined_options() {
    compare(vec![
        "-lazy_bind",
        "-bind",
        "/Library/Developer/CommandLineTools/usr/bin/dyldinfo",
    ]);
}

#[cfg(not(target_os = "macos"))]
#[test]
fn skipped_on_this_platform() {
    // this test does nothing on other platforms
}
<commit_msg>test: remove hardcoded CommandLineTools path in macos test<commit_after>use std::process;

fn get_realpath(cmd: &str) -> String {
    let output = process::Command::new("/usr/bin/xcrun")
        .arg("-f")
        .arg(cmd)
        .output()
        .expect("can get realpath");
    String::from_utf8(output.stdout).expect("output is valid utf8")
}

pub fn compare(args: Vec<&str>) {
    let apple = process::Command::new("/usr/bin/xcrun")
        .arg("dyldinfo")
        .args(&args)
        .output()
        .expect("run Apple dyldinfo");

    let goblin = process::Command::new("cargo")
        .arg("run")
        .arg("--quiet")
        .arg("--example")
        .arg("dyldinfo")
        .arg("--")
        .args(&args)
        .output()
        .expect("run cargo dyldinfo");

    if apple.stdout.as_slice() != goblin.stdout.as_slice() {
        println!("dyldinfo calls disagree!");
        println!(
            "Apple dyldinfo {:?} output:\n{}",
            &args,
            String::from_utf8_lossy(&apple.stdout)
        );
        println!("---");
        println!(
            "cargo dyldinfo {:?} output:\n{}",
            &args,
            String::from_utf8_lossy(&goblin.stdout)
        );
        panic!(
            "Apple dyldinfo and cargo dyldinfo differed (args: {:?})",
            args
        );
    }
}

#[cfg(target_os = "macos")]
#[test]
fn compare_binds() {
    let dyldinfo = get_realpath("dyldinfo");
    let clang = get_realpath("clang");
    compare(vec!["-bind", &dyldinfo]);
    compare(vec!["-bind", &clang]);
    compare(vec!["-bind", "/usr/bin/tmutil"]);
}

#[cfg(target_os = "macos")]
#[test]
fn compare_lazy_binds() {
    let dyldinfo = get_realpath("dyldinfo");
    let clang = get_realpath("clang");
    compare(vec!["-lazy_bind", &dyldinfo]);
    compare(vec!["-lazy_bind", &clang]);
    compare(vec!["-lazy_bind", "/usr/bin/tmutil"]);
}

#[cfg(target_os = "macos")]
#[test]
fn compare_combined_options() {
    let dyldinfo = get_realpath("dyldinfo");
    compare(vec!["-lazy_bind", "-bind", &dyldinfo]);
}

#[cfg(not(target_os = "macos"))]
#[test]
fn skipped_on_this_platform() {
    // this test does nothing on other platforms
}
<|endoftext|>


<commit_before>use std::borrow::Borrow;
use std::env;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::process::{self, Command};

fn run_command_or_fail<P, S>(dir: &str, cmd: P, args: &[S])
where
    P: AsRef<Path>,
    S: Borrow<str> + AsRef<OsStr>,
{
    let cmd = cmd.as_ref();
    let cmd = if cmd.components().count() > 1 && cmd.is_relative() {
        // If `cmd` is a relative path (and not a bare command that should be
        // looked up in PATH), absolutize it relative to `dir`, as otherwise the
        // behavior of std::process::Command is undefined.
        // https://github.com/rust-lang/rust/issues/37868
        PathBuf::from(dir)
            .join(cmd)
            .canonicalize()
            .expect("canonicalization failed")
    } else {
        PathBuf::from(cmd)
    };
    eprintln!(
        "Running command: \"{} {}\" in dir: {}",
        cmd.display(),
        args.join(" "),
        dir
    );
    let ret = Command::new(cmd).current_dir(dir).args(args).status();
    match ret.map(|status| (status.success(), status.code())) {
        Ok((true, _)) => (),
        Ok((false, Some(c))) => panic!("Command failed with error code {}", c),
        Ok((false, None)) => panic!("Command got killed"),
        Err(e) => panic!("Command failed with error: {}", e),
    }
}

fn main() {
    if env::var("CARGO_FEATURE_DYNAMIC_LINKING").is_ok() {
        eprintln!("librdkafka will be linked dynamically");

        let librdkafka_version = match env!("CARGO_PKG_VERSION")
            .split('+')
            .collect::<Vec<_>>()
            .as_slice()
        {
            [_rdsys_version, librdkafka_version] => *librdkafka_version,
            _ => panic!("Version format is not valid"),
        };

        let pkg_probe = pkg_config::Config::new()
            .cargo_metadata(true)
            .atleast_version(librdkafka_version)
            .probe("rdkafka");

        match pkg_probe {
            Ok(library) => {
                eprintln!("librdkafka found on the system:");
                eprintln!("  Name: {:?}", library.libs);
                eprintln!("  Path: {:?}", library.link_paths);
                eprintln!("  Version: {}", library.version);
            }
            Err(err) => {
                eprintln!(
                    "librdkafka {} cannot be found on the system: {}",
                    librdkafka_version,
                    err
                );
                eprintln!("Dynamic linking failed. Exiting.");
                process::exit(1);
            }
        }
    } else {
        if !Path::new("librdkafka/LICENSE").exists() {
            eprintln!("Setting up submodules");
            run_command_or_fail("../", "git", &["submodule", "update", "--init"]);
        }
        eprintln!("Building and linking librdkafka statically");
        build_librdkafka();
    }
}

#[cfg(not(feature = "cmake-build"))]
fn build_librdkafka() {
    let mut configure_flags: Vec<String> = Vec::new();

    let mut cflags = Vec::new();
    if let Ok(var) = env::var("CFLAGS") {
        cflags.push(var);
    }

    let mut ldflags = Vec::new();
    if let Ok(var) = env::var("LDFLAGS") {
        ldflags.push(var);
    }

    if env::var("CARGO_FEATURE_SSL").is_ok() {
        configure_flags.push("--enable-ssl".into());
        if let Ok(openssl_root) = env::var("DEP_OPENSSL_ROOT") {
            cflags.push(format!("-I{}/include", openssl_root));
            ldflags.push(format!("-L{}/lib", openssl_root));
        }
    } else {
        configure_flags.push("--disable-ssl".into());
    }

    if env::var("CARGO_FEATURE_GSSAPI").is_ok() {
        configure_flags.push("--enable-gssapi".into());
        if let Ok(sasl2_root) = env::var("DEP_SASL2_ROOT") {
            cflags.push(format!("-I{}/include", sasl2_root));
            ldflags.push(format!("-L{}/build", sasl2_root));
        }
    } else {
        configure_flags.push("--disable-gssapi".into());
    }

    if env::var("CARGO_FEATURE_LIBZ").is_ok() {
        // There is no --enable-zlib option, but it is enabled by default.
        if let Ok(z_root) = env::var("DEP_Z_ROOT") {
            cflags.push(format!("-I{}/include", z_root));
            ldflags.push(format!("-L{}/build", z_root));
        }
    } else {
        configure_flags.push("--disable-zlib".into());
    }

    if env::var("CARGO_FEATURE_ZSTD").is_ok() {
        configure_flags.push("--enable-zstd".into());
        if let Ok(zstd_root) = env::var("DEP_ZSTD_ROOT") {
            cflags.push(format!("-I{}/include", zstd_root));
            ldflags.push(format!("-L{}", zstd_root));
        }
    } else {
        configure_flags.push("--disable-zstd".into());
    }

    if env::var("CARGO_FEATURE_EXTERNAL_LZ4").is_ok() {
        configure_flags.push("--enable-lz4-ext".into());
        if let Ok(lz4_root) = env::var("DEP_LZ4_ROOT") {
            cflags.push(format!("-I{}/include", lz4_root));
            ldflags.push(format!("-L{}", lz4_root));
        }
    } else {
        configure_flags.push("--disable-lz4-ext".into());
    }

    env::set_var("CFLAGS", cflags.join(" "));
    env::set_var("LDFLAGS", ldflags.join(" "));

    let out_dir = env::var("OUT_DIR").expect("OUT_DIR missing");

    if !Path::new(&out_dir).join("LICENSE").exists() {
        // We're not allowed to build in-tree directly, as ~/.cargo/registry is
        // globally shared. mklove doesn't support out-of-tree builds [0], so we
        // work around the issue by creating a clone of librdkafka inside of
        // OUT_DIR, and build inside of *that* tree.
        //
        // https://github.com/edenhill/mklove/issues/17
        println!("Cloning librdkafka");
        run_command_or_fail(".", "cp", &["-a", "librdkafka/.", &out_dir]);
    }

    println!("Configuring librdkafka");
    run_command_or_fail(&out_dir, "./configure", configure_flags.as_slice());

    println!("Compiling librdkafka");
    env::set_var(
        "MAKEFLAGS",
        env::var_os("CARGO_MAKEFLAGS").expect("CARGO_MAKEFLAGS env var missing"),
    );
    run_command_or_fail(
        &out_dir,
        if cfg!(target_os = "freebsd") {
            "gmake"
        } else {
            "make"
        },
        &["libs"],
    );

    println!("cargo:rustc-link-search=native={}/src", out_dir);
    println!("cargo:rustc-link-lib=static=rdkafka");
    println!("cargo:root={}", out_dir);
}

#[cfg(feature = "cmake-build")]
fn build_librdkafka() {
    let mut config = cmake::Config::new("librdkafka");

    config
        .define("RDKAFKA_BUILD_STATIC", "1")
        .define("RDKAFKA_BUILD_TESTS", "0")
        .define("RDKAFKA_BUILD_EXAMPLES", "0")
        // CMAKE_INSTALL_LIBDIR is inferred as "lib64" on some platforms, but we
        // want a stable location that we can add to the linker search path.
        // Since we're not actually installing to /usr or /usr/local, there's no
        // harm to always using "lib" here.
        .define("CMAKE_INSTALL_LIBDIR", "lib");

    if env::var("CARGO_FEATURE_LIBZ").is_ok() {
        config.define("WITH_ZLIB", "1");
        config.register_dep("z");
        if let Ok(z_root) = env::var("DEP_Z_ROOT") {
            env::set_var("CMAKE_LIBRARY_PATH", format!("{}/build", z_root));
        }
    } else {
        config.define("WITH_ZLIB", "0");
    }

    if env::var("CARGO_FEATURE_SSL").is_ok() {
        config.define("WITH_SSL", "1");
        config.register_dep("openssl");
    } else {
        config.define("WITH_SSL", "0");
    }

    if env::var("CARGO_FEATURE_GSSAPI").is_ok() {
        config.define("WITH_SASL", "1");
        config.register_dep("sasl2");
        if let Ok(sasl2_root) = env::var("DEP_SASL2_ROOT") {
            config.cflag(format!("-I{}/include", sasl2_root));
            config.cxxflag(format!("-I{}/include", sasl2_root));
        }
    } else {
        config.define("WITH_SASL", "0");
    }

    if env::var("CARGO_FEATURE_ZSTD").is_ok() {
        config.define("WITH_ZSTD", "1");
        config.register_dep("zstd");
    } else {
        config.define("WITH_ZSTD", "0");
    }

    if env::var("CARGO_FEATURE_EXTERNAL_LZ4").is_ok() {
        config.define("ENABLE_LZ4_EXT", "1");
        config.register_dep("lz4");
    } else {
        config.define("ENABLE_LZ4_EXT", "0");
    }

    if let Ok(system_name) = env::var("CMAKE_SYSTEM_NAME") {
        config.define("CMAKE_SYSTEM_NAME", system_name);
    }

    println!("Configuring and compiling librdkafka");
    let dst = config.build();

    println!("cargo:rustc-link-search=native={}/lib", dst.display());
    println!("cargo:rustc-link-lib=static=rdkafka");
}
<commit_msg>Fix lint error<commit_after>use std::borrow::Borrow;
use std::env;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::process::{self, Command};

fn run_command_or_fail<P, S>(dir: &str, cmd: P, args: &[S])
where
    P: AsRef<Path>,
    S: Borrow<str> + AsRef<OsStr>,
{
    let cmd = cmd.as_ref();
    let cmd = if cmd.components().count() > 1 && cmd.is_relative() {
        // If `cmd` is a relative path (and not a bare command that should be
        // looked up in PATH), absolutize it relative to `dir`, as otherwise the
        // behavior of std::process::Command is undefined.
        // https://github.com/rust-lang/rust/issues/37868
        PathBuf::from(dir)
            .join(cmd)
            .canonicalize()
            .expect("canonicalization failed")
    } else {
        PathBuf::from(cmd)
    };
    eprintln!(
        "Running command: \"{} {}\" in dir: {}",
        cmd.display(),
        args.join(" "),
        dir
    );
    let ret = Command::new(cmd).current_dir(dir).args(args).status();
    match ret.map(|status| (status.success(), status.code())) {
        Ok((true, _)) => (),
        Ok((false, Some(c))) => panic!("Command failed with error code {}", c),
        Ok((false, None)) => panic!("Command got killed"),
        Err(e) => panic!("Command failed with error: {}", e),
    }
}

fn main() {
    if env::var("CARGO_FEATURE_DYNAMIC_LINKING").is_ok() {
        eprintln!("librdkafka will be linked dynamically");

        let librdkafka_version = match env!("CARGO_PKG_VERSION")
            .split('+')
            .collect::<Vec<_>>()
            .as_slice()
        {
            [_rdsys_version, librdkafka_version] => *librdkafka_version,
            _ => panic!("Version format is not valid"),
        };

        let pkg_probe = pkg_config::Config::new()
            .cargo_metadata(true)
            .atleast_version(librdkafka_version)
            .probe("rdkafka");

        match pkg_probe {
            Ok(library) => {
                eprintln!("librdkafka found on the system:");
                eprintln!("  Name: {:?}", library.libs);
                eprintln!("  Path: {:?}", library.link_paths);
                eprintln!("  Version: {}", library.version);
            }
            Err(err) => {
                eprintln!(
                    "librdkafka {} cannot be found on the system: {}",
                    librdkafka_version, err
                );
                eprintln!("Dynamic linking failed. Exiting.");
                process::exit(1);
            }
        }
    } else {
        if !Path::new("librdkafka/LICENSE").exists() {
            eprintln!("Setting up submodules");
            run_command_or_fail("../", "git", &["submodule", "update", "--init"]);
        }
        eprintln!("Building and linking librdkafka statically");
        build_librdkafka();
    }
}

#[cfg(not(feature = "cmake-build"))]
fn build_librdkafka() {
    let mut configure_flags: Vec<String> = Vec::new();

    let mut cflags = Vec::new();
    if let Ok(var) = env::var("CFLAGS") {
        cflags.push(var);
    }

    let mut ldflags = Vec::new();
    if let Ok(var) = env::var("LDFLAGS") {
        ldflags.push(var);
    }

    if env::var("CARGO_FEATURE_SSL").is_ok() {
        configure_flags.push("--enable-ssl".into());
        if let Ok(openssl_root) = env::var("DEP_OPENSSL_ROOT") {
            cflags.push(format!("-I{}/include", openssl_root));
            ldflags.push(format!("-L{}/lib", openssl_root));
        }
    } else {
        configure_flags.push("--disable-ssl".into());
    }

    if env::var("CARGO_FEATURE_GSSAPI").is_ok() {
        configure_flags.push("--enable-gssapi".into());
        if let Ok(sasl2_root) = env::var("DEP_SASL2_ROOT") {
            cflags.push(format!("-I{}/include", sasl2_root));
            ldflags.push(format!("-L{}/build", sasl2_root));
        }
    } else {
        configure_flags.push("--disable-gssapi".into());
    }

    if env::var("CARGO_FEATURE_LIBZ").is_ok() {
        // There is no --enable-zlib option, but it is enabled by default.
        if let Ok(z_root) = env::var("DEP_Z_ROOT") {
            cflags.push(format!("-I{}/include", z_root));
            ldflags.push(format!("-L{}/build", z_root));
        }
    } else {
        configure_flags.push("--disable-zlib".into());
    }

    if env::var("CARGO_FEATURE_ZSTD").is_ok() {
        configure_flags.push("--enable-zstd".into());
        if let Ok(zstd_root) = env::var("DEP_ZSTD_ROOT") {
            cflags.push(format!("-I{}/include", zstd_root));
            ldflags.push(format!("-L{}", zstd_root));
        }
    } else {
        configure_flags.push("--disable-zstd".into());
    }

    if env::var("CARGO_FEATURE_EXTERNAL_LZ4").is_ok() {
        configure_flags.push("--enable-lz4-ext".into());
        if let Ok(lz4_root) = env::var("DEP_LZ4_ROOT") {
            cflags.push(format!("-I{}/include", lz4_root));
            ldflags.push(format!("-L{}", lz4_root));
        }
    } else {
        configure_flags.push("--disable-lz4-ext".into());
    }

    env::set_var("CFLAGS", cflags.join(" "));
    env::set_var("LDFLAGS", ldflags.join(" "));

    let out_dir = env::var("OUT_DIR").expect("OUT_DIR missing");

    if !Path::new(&out_dir).join("LICENSE").exists() {
        // We're not allowed to build in-tree directly, as ~/.cargo/registry is
        // globally shared. mklove doesn't support out-of-tree builds [0], so we
        // work around the issue by creating a clone of librdkafka inside of
        // OUT_DIR, and build inside of *that* tree.
        //
        // https://github.com/edenhill/mklove/issues/17
        println!("Cloning librdkafka");
        run_command_or_fail(".", "cp", &["-a", "librdkafka/.", &out_dir]);
    }

    println!("Configuring librdkafka");
    run_command_or_fail(&out_dir, "./configure", configure_flags.as_slice());

    println!("Compiling librdkafka");
    env::set_var(
        "MAKEFLAGS",
        env::var_os("CARGO_MAKEFLAGS").expect("CARGO_MAKEFLAGS env var missing"),
    );
    run_command_or_fail(
        &out_dir,
        if cfg!(target_os = "freebsd") {
            "gmake"
        } else {
            "make"
        },
        &["libs"],
    );

    println!("cargo:rustc-link-search=native={}/src", out_dir);
    println!("cargo:rustc-link-lib=static=rdkafka");
    println!("cargo:root={}", out_dir);
}

#[cfg(feature = "cmake-build")]
fn build_librdkafka() {
    let mut config = cmake::Config::new("librdkafka");

    config
        .define("RDKAFKA_BUILD_STATIC", "1")
        .define("RDKAFKA_BUILD_TESTS", "0")
        .define("RDKAFKA_BUILD_EXAMPLES", "0")
        // CMAKE_INSTALL_LIBDIR is inferred as "lib64" on some platforms, but we
        // want a stable location that we can add to the linker search path.
        // Since we're not actually installing to /usr or /usr/local, there's no
        // harm to always using "lib" here.
        .define("CMAKE_INSTALL_LIBDIR", "lib");

    if env::var("CARGO_FEATURE_LIBZ").is_ok() {
        config.define("WITH_ZLIB", "1");
        config.register_dep("z");
        if let Ok(z_root) = env::var("DEP_Z_ROOT") {
            env::set_var("CMAKE_LIBRARY_PATH", format!("{}/build", z_root));
        }
    } else {
        config.define("WITH_ZLIB", "0");
    }

    if env::var("CARGO_FEATURE_SSL").is_ok() {
        config.define("WITH_SSL", "1");
        config.register_dep("openssl");
    } else {
        config.define("WITH_SSL", "0");
    }

    if env::var("CARGO_FEATURE_GSSAPI").is_ok() {
        config.define("WITH_SASL", "1");
        config.register_dep("sasl2");
        if let Ok(sasl2_root) = env::var("DEP_SASL2_ROOT") {
            config.cflag(format!("-I{}/include", sasl2_root));
            config.cxxflag(format!("-I{}/include", sasl2_root));
        }
    } else {
        config.define("WITH_SASL", "0");
    }

    if env::var("CARGO_FEATURE_ZSTD").is_ok() {
        config.define("WITH_ZSTD", "1");
        config.register_dep("zstd");
    } else {
        config.define("WITH_ZSTD", "0");
    }

    if env::var("CARGO_FEATURE_EXTERNAL_LZ4").is_ok() {
        config.define("ENABLE_LZ4_EXT", "1");
        config.register_dep("lz4");
    } else {
        config.define("ENABLE_LZ4_EXT", "0");
    }

    if let Ok(system_name) = env::var("CMAKE_SYSTEM_NAME") {
        config.define("CMAKE_SYSTEM_NAME", system_name);
    }

    println!("Configuring and compiling librdkafka");
    let dst = config.build();

    println!("cargo:rustc-link-search=native={}/lib", dst.display());
    println!("cargo:rustc-link-lib=static=rdkafka");
}
<|endoftext|>


<commit_before>#![stable(feature = "core_hint", since = "1.27.0")]

//! Hints to compiler that affects how code should be emitted or optimized.

use crate::intrinsics;

/// Informs the compiler that this point in the code is not reachable, enabling
/// further optimizations.
///
/// # Safety
///
/// Reaching this function is completely *undefined behavior* (UB). In
/// particular, the compiler assumes that all UB must never happen, and
/// therefore will eliminate all branches that reach to a call to
/// `unreachable_unchecked()`.
///
/// Like all instances of UB, if this assumption turns out to be wrong, i.e., the
/// `unreachable_unchecked()` call is actually reachable among all possible
/// control flow, the compiler will apply the wrong optimization strategy, and
/// may sometimes even corrupt seemingly unrelated code, causing
/// difficult-to-debug problems.
///
/// Use this function only when you can prove that the code will never call it.
/// Otherwise, consider using the [`unreachable!`] macro, which does not allow
/// optimizations but will panic when executed.
///
///
/// # Example
///
/// ```
/// fn div_1(a: u32, b: u32) -> u32 {
///     use std::hint::unreachable_unchecked;
///
///     // `b.saturating_add(1)` is always positive (not zero),
///     // hence `checked_div` will never return `None`.
///     // Therefore, the else branch is unreachable.
///     a.checked_div(b.saturating_add(1))
///         .unwrap_or_else(|| unsafe { unreachable_unchecked() })
/// }
///
/// assert_eq!(div_1(7, 0), 7);
/// assert_eq!(div_1(9, 1), 4);
/// assert_eq!(div_1(11, u32::MAX), 0);
/// ```
#[inline]
#[stable(feature = "unreachable", since = "1.27.0")]
#[rustc_const_unstable(feature = "const_unreachable_unchecked", issue = "53188")]
pub const unsafe fn unreachable_unchecked() -> ! {
    // SAFETY: the safety contract for `intrinsics::unreachable` must
    // be upheld by the caller.
    unsafe { intrinsics::unreachable() }
}

/// Emits a machine instruction hinting to the processor that it is running in busy-wait
/// spin-loop ("spin lock").
///
/// For a discussion of different locking strategies and their trade-offs, see
/// [`core::sync::atomic::spin_loop_hint`].
///
/// **Note**: On platforms that do not support receiving spin-loop hints this function does not
/// do anything at all.
///
/// [`core::sync::atomic::spin_loop_hint`]: crate::sync::atomic::spin_loop_hint
#[inline]
#[unstable(feature = "renamed_spin_loop", issue = "55002")]
pub fn spin_loop() {
    #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "sse2"))]
    {
        #[cfg(target_arch = "x86")]
        {
            // SAFETY: the `cfg` attr ensures that we only execute this on x86 targets.
            unsafe { crate::arch::x86::_mm_pause() };
        }

        #[cfg(target_arch = "x86_64")]
        {
            // SAFETY: the `cfg` attr ensures that we only execute this on x86_64 targets.
            unsafe { crate::arch::x86_64::_mm_pause() };
        }
    }

    #[cfg(any(target_arch = "aarch64", all(target_arch = "arm", target_feature = "v6")))]
    {
        #[cfg(target_arch = "aarch64")]
        {
            // SAFETY: the `cfg` attr ensures that we only execute this on aarch64 targets.
            unsafe { crate::arch::aarch64::__yield() };
        }
        #[cfg(target_arch = "arm")]
        {
            // SAFETY: the `cfg` attr ensures that we only execute this on arm targets
            // with support for the v6 feature.
            unsafe { crate::arch::arm::__yield() };
        }
    }
}

/// An identity function that *__hints__* to the compiler to be maximally pessimistic about what
/// `black_box` could do.
///
/// [`std::convert::identity`]: https://doc.rust-lang.org/core/convert/fn.identity.html
///
/// Unlike [`std::convert::identity`], a Rust compiler is encouraged to assume that `black_box` can
/// use `dummy` in any possible valid way that Rust code is allowed to without introducing undefined
/// behavior in the calling code. This property makes `black_box` useful for writing code in which
/// certain optimizations are not desired, such as benchmarks.
///
/// Note however, that `black_box` is only (and can only be) provided on a "best-effort" basis. The
/// extent to which it can block optimisations may vary depending upon the platform and code-gen
/// backend used. Programs cannot rely on `black_box` for *correctness* in any way.
#[inline]
#[unstable(feature = "test", issue = "50297")]
#[allow(unreachable_code)] // this makes #[cfg] a bit easier below.
pub fn black_box<T>(dummy: T) -> T {
    // We need to "use" the argument in some way LLVM can't introspect, and on
    // targets that support it we can typically leverage inline assembly to do
    // this. LLVM's interpretation of inline assembly is that it's, well, a black
    // box. This isn't the greatest implementation since it probably deoptimizes
    // more than we want, but it's so far good enough.

    #[cfg(not(miri))] // This is just a hint, so it is fine to skip in Miri.
    // SAFETY: the inline assembly is a no-op.
    unsafe {
        llvm_asm!("" : : "r"(&dummy));
    }

    dummy
}
<commit_msg>do not inline black_box when building for Miri<commit_after>#![stable(feature = "core_hint", since = "1.27.0")]

//! Hints to compiler that affects how code should be emitted or optimized.

use crate::intrinsics;

/// Informs the compiler that this point in the code is not reachable, enabling
/// further optimizations.
///
/// # Safety
///
/// Reaching this function is completely *undefined behavior* (UB). In
/// particular, the compiler assumes that all UB must never happen, and
/// therefore will eliminate all branches that reach to a call to
/// `unreachable_unchecked()`.
///
/// Like all instances of UB, if this assumption turns out to be wrong, i.e., the
/// `unreachable_unchecked()` call is actually reachable among all possible
/// control flow, the compiler will apply the wrong optimization strategy, and
/// may sometimes even corrupt seemingly unrelated code, causing
/// difficult-to-debug problems.
///
/// Use this function only when you can prove that the code will never call it.
/// Otherwise, consider using the [`unreachable!`] macro, which does not allow
/// optimizations but will panic when executed.
///
///
/// # Example
///
/// ```
/// fn div_1(a: u32, b: u32) -> u32 {
///     use std::hint::unreachable_unchecked;
///
///     // `b.saturating_add(1)` is always positive (not zero),
///     // hence `checked_div` will never return `None`.
///     // Therefore, the else branch is unreachable.
///     a.checked_div(b.saturating_add(1))
///         .unwrap_or_else(|| unsafe { unreachable_unchecked() })
/// }
///
/// assert_eq!(div_1(7, 0), 7);
/// assert_eq!(div_1(9, 1), 4);
/// assert_eq!(div_1(11, u32::MAX), 0);
/// ```
#[inline]
#[stable(feature = "unreachable", since = "1.27.0")]
#[rustc_const_unstable(feature = "const_unreachable_unchecked", issue = "53188")]
pub const unsafe fn unreachable_unchecked() -> ! {
    // SAFETY: the safety contract for `intrinsics::unreachable` must
    // be upheld by the caller.
    unsafe { intrinsics::unreachable() }
}

/// Emits a machine instruction hinting to the processor that it is running in busy-wait
/// spin-loop ("spin lock").
///
/// For a discussion of different locking strategies and their trade-offs, see
/// [`core::sync::atomic::spin_loop_hint`].
///
/// **Note**: On platforms that do not support receiving spin-loop hints this function does not
/// do anything at all.
///
/// [`core::sync::atomic::spin_loop_hint`]: crate::sync::atomic::spin_loop_hint
#[inline]
#[unstable(feature = "renamed_spin_loop", issue = "55002")]
pub fn spin_loop() {
    #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), target_feature = "sse2"))]
    {
        #[cfg(target_arch = "x86")]
        {
            // SAFETY: the `cfg` attr ensures that we only execute this on x86 targets.
            unsafe { crate::arch::x86::_mm_pause() };
        }

        #[cfg(target_arch = "x86_64")]
        {
            // SAFETY: the `cfg` attr ensures that we only execute this on x86_64 targets.
            unsafe { crate::arch::x86_64::_mm_pause() };
        }
    }

    #[cfg(any(target_arch = "aarch64", all(target_arch = "arm", target_feature = "v6")))]
    {
        #[cfg(target_arch = "aarch64")]
        {
            // SAFETY: the `cfg` attr ensures that we only execute this on aarch64 targets.
            unsafe { crate::arch::aarch64::__yield() };
        }
        #[cfg(target_arch = "arm")]
        {
            // SAFETY: the `cfg` attr ensures that we only execute this on arm targets
            // with support for the v6 feature.
            unsafe { crate::arch::arm::__yield() };
        }
    }
}

/// An identity function that *__hints__* to the compiler to be maximally pessimistic about what
/// `black_box` could do.
///
/// [`std::convert::identity`]: https://doc.rust-lang.org/core/convert/fn.identity.html
///
/// Unlike [`std::convert::identity`], a Rust compiler is encouraged to assume that `black_box` can
/// use `dummy` in any possible valid way that Rust code is allowed to without introducing undefined
/// behavior in the calling code. This property makes `black_box` useful for writing code in which
/// certain optimizations are not desired, such as benchmarks.
///
/// Note however, that `black_box` is only (and can only be) provided on a "best-effort" basis. The
/// extent to which it can block optimisations may vary depending upon the platform and code-gen
/// backend used. Programs cannot rely on `black_box` for *correctness* in any way.
#[cfg_attr(not(miri), inline)]
#[cfg_attr(miri, inline(never))]
#[unstable(feature = "test", issue = "50297")]
#[allow(unreachable_code)] // this makes #[cfg] a bit easier below.
pub fn black_box<T>(dummy: T) -> T {
    // We need to "use" the argument in some way LLVM can't introspect, and on
    // targets that support it we can typically leverage inline assembly to do
    // this. LLVM's interpretation of inline assembly is that it's, well, a black
    // box. This isn't the greatest implementation since it probably deoptimizes
    // more than we want, but it's so far good enough.

    #[cfg(not(miri))] // This is just a hint, so it is fine to skip in Miri.
    // SAFETY: the inline assembly is a no-op.
    unsafe {
        llvm_asm!("" : : "r"(&dummy));
    }

    dummy
}
<|endoftext|>


<commit_before>// ignore-test FIXME ignored to let https://github.com/rust-lang/rust/pull/59119 land
#![feature(
    async_await,
    await_macro,
    futures_api,
)]

use std::{future::Future, pin::Pin, task::Poll, ptr};
use std::task::{Waker, RawWaker, RawWakerVTable};

// See if we can run a basic `async fn`
pub async fn foo(x: &u32, y: u32) -> u32 {
    let y = &y;
    let z = 9;
    let z = &z;
    let y = await!(async { *y + *z });
    let a = 10;
    let a = &a;
    *x + y + *a
}

fn raw_waker_clone(_this: *const ()) -> RawWaker {
    panic!("unimplemented");
}
fn raw_waker_wake(_this: *const ()) {
    panic!("unimplemented");
}
fn raw_waker_drop(_this: *const ()) {}

static RAW_WAKER: RawWakerVTable = RawWakerVTable {
    clone: raw_waker_clone,
    wake: raw_waker_wake,
    drop: raw_waker_drop,
};

fn main() {
    let x = 5;
    let mut fut = foo(&x, 7);
    let waker = unsafe { Waker::new_unchecked(RawWaker::new(ptr::null(), &RAW_WAKER)) };
    assert_eq!(unsafe { Pin::new_unchecked(&mut fut) }.poll(&waker), Poll::Ready(31));
}
<commit_msg>fix async-fn test<commit_after>#![feature(
    async_await,
    await_macro,
    futures_api,
)]

use std::{future::Future, pin::Pin, task::Poll, ptr};
use std::task::{Waker, RawWaker, RawWakerVTable, Context};

// See if we can run a basic `async fn`
pub async fn foo(x: &u32, y: u32) -> u32 {
    let y = &y;
    let z = 9;
    let z = &z;
    let y = await!(async { *y + *z });
    let a = 10;
    let a = &a;
    *x + y + *a
}

fn raw_waker_clone(_this: *const ()) -> RawWaker {
    panic!("unimplemented");
}
fn raw_waker_wake(_this: *const ()) {
    panic!("unimplemented");
}
fn raw_waker_drop(_this: *const ()) {}

static RAW_WAKER: RawWakerVTable = RawWakerVTable::new(
    raw_waker_clone,
    raw_waker_wake,
    raw_waker_drop,
);

fn main() {
    let x = 5;
    let mut fut = foo(&x, 7);
    let waker = unsafe { Waker::new_unchecked(RawWaker::new(ptr::null(), &RAW_WAKER)) };
    let mut context = Context::from_waker(&waker);
    assert_eq!(unsafe { Pin::new_unchecked(&mut fut) }.poll(&mut context), Poll::Ready(31));
}
<|endoftext|>


<commit_before>extern crate limonite;

use limonite::codegen::llvm::LLVMGenerator;
use limonite::syntax::expr::{Expr, ExprWrapper};
use limonite::syntax::op::InfixOp;
use limonite::syntax::literals::Literals;

use std::mem::transmute;

#[test]
fn test_sum_function() {
    // Creates a limonite function that looks like:
    // fn add_two_ints(left: u64, right: u64) -> u64,
    //     return left + right;

    let left = ExprWrapper::default(Expr::Var("left".into()));
    let right = ExprWrapper::default(Expr::Var("right".into()));

    let sum = ExprWrapper::default(Expr::InfixOp(InfixOp::Add, left, right));
    let ast_body = ExprWrapper::default(Expr::Return(Some(sum)));
    let fn_args = vec![("left".into(), "u64".into()), ("right".into(), "u64".into())];
    let ast = ExprWrapper::default(Expr::FnDecl("add_two_ints".into(), fn_args, Some("u64".into()), ast_body));

    let mut llvm_generator = LLVMGenerator::new();

    llvm_generator.add_module(ast, false, false);
    llvm_generator.initialize(true);

    let address = llvm_generator.get_function_address("add_two_ints").expect("Could not find function address");

    let add_two_ints: extern "C" fn(u64, u64) -> u64 = unsafe { transmute(address) };

    assert_eq!(add_two_ints(456, 987), 1443);
}

#[test]
fn test_while_lt_increment_u8() {
    // Creates a limonite function that looks like:
    // fn inc_until() -> u8,
    //     var i = 0u8;
    //
    //     while i < 10,
    //         i += 1
    //
    //     return i

    let var_decl = ExprWrapper::default(Expr::VarDecl(false, "i".into(), Some("u8".into()), ExprWrapper::default(Expr::Literal(Literals::U8Num(0)))));
    let loop_cond = ExprWrapper::default(Expr::InfixOp(InfixOp::Lt,
                                                       ExprWrapper::default(Expr::Var("i".into())),
                                                       ExprWrapper::default(Expr::Literal(Literals::U8Num(10)))));
    let loop_body = ExprWrapper::default(Expr::Block(vec![
        ExprWrapper::default(Expr::Assign(ExprWrapper::default(Expr::Var("i".into())),
                                          ExprWrapper::default(Expr::InfixOp(InfixOp::Add,
                                                                             ExprWrapper::default(Expr::Var("i".into())),
                                                                             ExprWrapper::default(Expr::Literal(Literals::U8Num(1)))))))
    ]));
    let while_loop = ExprWrapper::default(Expr::WhileLoop(loop_cond, loop_body));
    let ret = ExprWrapper::default(Expr::Return(Some(ExprWrapper::default(Expr::Var("i".into())))));
    let body = ExprWrapper::default(Expr::Block(vec![
        var_decl,
        while_loop,
        ret
    ]));
    let ast = ExprWrapper::default(Expr::FnDecl("inc_until".into(), Vec::new(), Some("u8".into()), body));

    let mut llvm_generator = LLVMGenerator::new();

    llvm_generator.add_module(ast, false, false);
    llvm_generator.initialize(true);

    let address = llvm_generator.get_function_address("inc_until").expect("Could not find function address");

    let inc_until: extern "C" fn() -> u8 = unsafe { transmute(address) };

    assert_eq!(inc_until(), 10);
}

#[test]
fn test_while_gt_decrement_u8() {
    // Creates a limonite function that looks like:
    // fn dec_until() -> u8,
    //     var i = 10u8;
    //
    //     while i > 0,
    //         i -= 1
    //
    //     return i

    let var_decl = ExprWrapper::default(Expr::VarDecl(false, "i".into(), Some("u8".into()), ExprWrapper::default(Expr::Literal(Literals::U8Num(10)))));
    let loop_cond = ExprWrapper::default(Expr::InfixOp(InfixOp::Gt,
                                                       ExprWrapper::default(Expr::Var("i".into())),
                                                       ExprWrapper::default(Expr::Literal(Literals::U8Num(0)))));
    let loop_body = ExprWrapper::default(Expr::Block(vec![
        ExprWrapper::default(Expr::Assign(ExprWrapper::default(Expr::Var("i".into())),
                                          ExprWrapper::default(Expr::InfixOp(InfixOp::Sub,
                                                                             ExprWrapper::default(Expr::Var("i".into())),
                                                                             ExprWrapper::default(Expr::Literal(Literals::U8Num(1)))))))
    ]));
    let while_loop = ExprWrapper::default(Expr::WhileLoop(loop_cond, loop_body));
    let ret = ExprWrapper::default(Expr::Return(Some(ExprWrapper::default(Expr::Var("i".into())))));
    let body = ExprWrapper::default(Expr::Block(vec![
        var_decl,
        while_loop,
        ret
    ]));
    let ast = ExprWrapper::default(Expr::FnDecl("dec_until".into(), Vec::new(), Some("u8".into()), body));

    let mut llvm_generator = LLVMGenerator::new();

    llvm_generator.add_module(ast, false, false);
    llvm_generator.initialize(true);

    let address = llvm_generator.get_function_address("dec_until").expect("Could not find function address");

    let dec_until: extern "C" fn() -> u8 = unsafe { transmute(address) };

    assert_eq!(dec_until(), 0);
}

#[test]
fn test_hello_world() {
    // Creates a limonite function that looks like:
    // fn hello_world(),
    //     var s = "Hello, World!";
    //     print(s)
    //     return

    let var_decl = ExprWrapper::default(Expr::VarDecl(false, "s".into(), Some("std.string.String".into()), ExprWrapper::default(Expr::Literal(Literals::UTF8String("Hello, World!".into())))));
    let print_call = ExprWrapper::default(Expr::FnCall("print".into(), vec![ExprWrapper::default(Expr::Var("s".into()))]));
    let ret = ExprWrapper::default(Expr::Return(None));

    let body = ExprWrapper::default(Expr::Block(vec![
        var_decl,
        print_call,
        ret,
    ]));
    let ast = ExprWrapper::default(Expr::FnDecl("hello_world".into(), Vec::new(), None, body));

    let mut llvm_generator = LLVMGenerator::new();

    llvm_generator.add_module(ast, false, true);
    llvm_generator.initialize(true);

    let address = llvm_generator.get_function_address("hello_world").expect("Could not find function address");

    let hello_world: extern "C" fn() = unsafe { transmute(address) };

    // REVIEW: Is there a way to capture stdout to ensure the right text is being printed?

    hello_world();
}
<commit_msg>Added macros for reducing test boilerplate<commit_after>extern crate limonite;

use limonite::codegen::llvm::LLVMGenerator;
use limonite::syntax::expr::{Expr, ExprWrapper};
use limonite::syntax::op::InfixOp;
use limonite::syntax::literals::Literals;

use std::mem::transmute;

macro_rules! block {
    ($($args:tt)*) => {
        ExprWrapper::default(Expr::Block(vec![$($args)*]))
    }
}

macro_rules! var {
    ($arg:expr) => {
        ExprWrapper::default(Expr::Var($arg.into()))
    }
}

macro_rules! ret {
    ($arg:expr) => {
        ExprWrapper::default(Expr::Return($arg))
    }
}

macro_rules! u8 {
    ($arg:tt) => {
        ExprWrapper::default(Expr::Literal(Literals::U8Num($arg)))
    }
}

macro_rules! string {
    ($arg:tt) => {
        ExprWrapper::default(Expr::Literal(Literals::UTF8String($arg.into())))
    }
}

macro_rules! op {
    ($left_arg:expr, + $right_arg:expr) => {
        ExprWrapper::default(Expr::InfixOp(InfixOp::Add, $left_arg, $right_arg))
    };
    ($left_arg:expr, = $right_arg:expr) => {
        ExprWrapper::default(Expr::Assign($left_arg, $right_arg))
    };
    ($left_arg:expr, < $right_arg:expr) => {
        ExprWrapper::default(Expr::InfixOp(InfixOp::Lt, $left_arg, $right_arg))
    };
    ($left_arg:expr, > $right_arg:expr) => {
        ExprWrapper::default(Expr::InfixOp(InfixOp::Gt, $left_arg, $right_arg))
    };
}

macro_rules! assign {
    ($left_arg:expr, = $right_arg:expr) => {
        ExprWrapper::default(Expr::Assign($left_arg, $right_arg))
    };
    ($left_arg:expr, += $right_arg:expr) => {
        ExprWrapper::default(Expr::Assign($left_arg, ExprWrapper::default(Expr::InfixOp(InfixOp::Add, $left_arg, $right_arg))))
    };
    ($left_arg:expr, -= $right_arg:expr) => {
        ExprWrapper::default(Expr::Assign($left_arg, ExprWrapper::default(Expr::InfixOp(InfixOp::Sub, $left_arg, $right_arg))))
    };
}

#[test]
fn test_sum_function() {
    // Creates a limonite function that looks like:
    // fn add_two_ints(left: u64, right: u64) -> u64,
    //     return left + right;

    let sum = op!(var!("left"), + var!("right"));
    let ast_body = ret!(Some(sum));
    let fn_args = vec![("left".into(), "u64".into()), ("right".into(), "u64".into())];
    let ast = ExprWrapper::default(Expr::FnDecl("add_two_ints".into(), fn_args, Some("u64".into()), ast_body));

    let mut llvm_generator = LLVMGenerator::new();

    llvm_generator.add_module(ast, false, false);
    llvm_generator.initialize(true);

    let address = llvm_generator.get_function_address("add_two_ints").expect("Could not find function address");

    let add_two_ints: extern "C" fn(u64, u64) -> u64 = unsafe { transmute(address) };

    assert_eq!(add_two_ints(456, 987), 1443);
}

#[test]
fn test_while_lt_increment_u8() {
    // Creates a limonite function that looks like:
    // fn inc_until() -> u8,
    //     var i = 0u8;
    //
    //     while i < 10,
    //         i += 1
    //
    //     return i

    let var_decl = ExprWrapper::default(Expr::VarDecl(false, "i".into(), Some("u8".into()), u8!(0)));
    let loop_cond = op!(var!("i"), < u8!(10));
    let loop_body = assign!(var!("i"), += u8!(1));
    let while_loop = ExprWrapper::default(Expr::WhileLoop(loop_cond, loop_body));
    let ret = ret!(Some(var!("i")));
    let body = block![
        var_decl,
        while_loop,
        ret
    ];
    let ast = ExprWrapper::default(Expr::FnDecl("inc_until".into(), Vec::new(), Some("u8".into()), body));

    let mut llvm_generator = LLVMGenerator::new();

    llvm_generator.add_module(ast, false, false);
    llvm_generator.initialize(true);

    let address = llvm_generator.get_function_address("inc_until").expect("Could not find function address");

    let inc_until: extern "C" fn() -> u8 = unsafe { transmute(address) };

    assert_eq!(inc_until(), 10);
}

#[test]
fn test_while_gt_decrement_u8() {
    // Creates a limonite function that looks like:
    // fn dec_until() -> u8,
    //     var i = 10u8;
    //
    //     while i > 0,
    //         i -= 1
    //
    //     return i

    let var_decl = ExprWrapper::default(Expr::VarDecl(false, "i".into(), Some("u8".into()), u8!(10)));
    let loop_cond = op!(var!("i"), > u8!(0));
    let loop_body = assign!(var!("i"), -= u8!(1));
    let while_loop = ExprWrapper::default(Expr::WhileLoop(loop_cond, loop_body));
    let ret = ret!(Some(var!("i")));
    let body = block![
        var_decl,
        while_loop,
        ret
    ];
    let ast = ExprWrapper::default(Expr::FnDecl("dec_until".into(), Vec::new(), Some("u8".into()), body));

    let mut llvm_generator = LLVMGenerator::new();

    llvm_generator.add_module(ast, false, false);
    llvm_generator.initialize(true);

    let address = llvm_generator.get_function_address("dec_until").expect("Could not find function address");

    let dec_until: extern "C" fn() -> u8 = unsafe { transmute(address) };

    assert_eq!(dec_until(), 0);
}

#[test]
fn test_hello_world() {
    // Creates a limonite function that looks like:
    // fn hello_world(),
    //     var s = "Hello, World!";
    //     print(s)
    //     return

    let var_decl = ExprWrapper::default(Expr::VarDecl(false, "s".into(), Some("std.string.String".into()), string!("Hello, World!")));
    let print_call = ExprWrapper::default(Expr::FnCall("print".into(), vec![var!("s")]));
    let ret = ret!(None);

    let body = block![
        var_decl,
        print_call,
        ret,
    ];
    let ast = ExprWrapper::default(Expr::FnDecl("hello_world".into(), Vec::new(), None, body));

    let mut llvm_generator = LLVMGenerator::new();

    llvm_generator.add_module(ast, false, true);
    llvm_generator.initialize(true);

    let address = llvm_generator.get_function_address("hello_world").expect("Could not find function address");

    let hello_world: extern "C" fn() = unsafe { transmute(address) };

    // REVIEW: Is there a way to capture stdout to ensure the right text is being printed?

    hello_world();
}
<|endoftext|>