<commit_before>// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

pub mod setup {
    use std::io::{self, Write};
    use std::path::Path;
    use std::process;

    use ansi_term::Colour::{Cyan, Green, White};
    use hcore::crypto::SigKeyPair;
    use hcore::env;

    use analytics;
    use command;
    use config::{self, Config};
    use error::Result;

    pub fn start(cache_path: &Path, analytics_path: &Path) -> Result<()> {
        let mut generated_origin = false;

        println!("");
        title("Habitat CLI Setup");
        para("Welcome to hab setup. Let's get started.");

        heading("Set up a default origin");
        para("Every package in Habitat belongs to an origin, which indicates the person or \
              organization responsible for maintaining that package. Each origin also has \
              a key used to cryptographically sign packages in that origin.");
        para("Selecting a default origin tells package building operations such as 'hab pkg \
              build' what key should be used to sign the packages produced. If you do not \
              set a default origin now, you will have to tell package building commands each \
              time what origin to use.");
        para("For more information on origins and how they are used in building packages, \
              please consult the docs at https://www.habitat.sh/docs/build-packages-overview/");
        if try!(ask_default_origin()) {
            println!("");
            para("Enter the name of your origin. If you plan to publish your packages publicly, \
                  we recommend that you select one that is not already in use on the Habitat \
                  build service found at https://app.habitat.sh/.");
            let origin = try!(prompt_origin());
            try!(write_cli_config_origin(&origin));
            println!("");
            if is_origin_in_cache(&origin, cache_path) {
                para(&format!("You already have an origin key for {} created and installed. \
                              Great work!",
                              &origin));
            } else {
                heading("Create origin key pair");
                para(&format!("It doesn't look like you have a signing key for the origin `{}'. \
                               Without it, you won't be able to build new packages successfully.",
                              &origin));
                para("You can either create a new signing key now, or, if you are building \
                      packages for an origin that already exists, ask the owner to give you the \
                      signing key.");
                para("For more information on the use of origin keys, please consult the \
                      documentation at https://www.habitat.sh/docs/concepts-keys/#origin-keys");
                if try!(ask_create_origin(&origin)) {
                    try!(create_origin(&origin, cache_path));
                    generated_origin = true;
                } else {
                    para(&format!("You might want to create an origin key later with: `hab \
                                  origin key generate {}'",
                                  &origin));
                }
            }
        } else {
            para("Okay, maybe another time");
        }
        heading("GitHub Access Token");
        para("While you can build and run Habitat packages without sharing them on the public \
              depot, doing so allows you to collaborate with the Habitat community. In addition, \
              it is how you can perform continuous deployment with Habitat.");
        para("The depot uses GitHub authentication with an access token \
              (https://help.github.com/articles/creating-an-access-token-for-command-line-use/).");
        para("If you would like to share your packages on the depot, please enter your GitHub \
              access token. Otherwise, just enter No.");
        para("For more information on sharing packages on the depot, please read the \
              documentation at https://www.habitat.sh/docs/share-packages-overview/");
        if try!(ask_default_auth_token()) {
            println!("");
            para("Enter your GitHub access token.");
            let auth_token = try!(prompt_auth_token());
            try!(write_cli_config_auth_token(&auth_token));
        } else {
            para("Okay, maybe another time");
        }
        println!("");
        heading("Analytics");
        para("The `hab` command-line tool will optionally send anonymous usage data to Habitat's \
             Google Analytics account. This is a strictly opt-in activity and no tracking will \
             occur unless you respond affirmatively to the question below.");
        para("We collect this data to help improve Habitat's user experience. For example, we \
              would like to know the category of tasks users are performing, and which ones they \
              are having trouble with (e.g. mistyping command line arguments).");
        para("To see what kinds of data are sent and how they are anonymized, please read more \
             about our analytics here: https://www.habitat.sh/docs/about-analytics/");
        if try!(ask_enable_analytics(analytics_path)) {
            try!(opt_in_analytics(analytics_path, generated_origin));
        } else {
            try!(opt_out_analytics(analytics_path));
        }
        heading("CLI Setup Complete");
        para("That's all for now. Thanks for using Habitat!");
        Ok(())
    }

    fn ask_default_origin() -> Result<bool> {
        prompt_yes_no("Set up a default origin?", Some(true))
    }

    fn ask_create_origin(origin: &str) -> Result<bool> {
        prompt_yes_no(&format!("Create an origin key for `{}'?", origin),
                      Some(true))
    }

    fn write_cli_config_origin(origin: &str) -> Result<()> {
        let mut config = try!(config::load());
        config.origin = Some(origin.to_string());
        config::save(&config)
    }

    fn write_cli_config_auth_token(auth_token: &str) -> Result<()> {
        let mut config = try!(config::load());
        config.auth_token = Some(auth_token.to_string());
        config::save(&config)
    }

    fn is_origin_in_cache(origin: &str, cache_path: &Path) -> bool {
        match SigKeyPair::get_latest_pair_for(origin, cache_path) {
            Ok(pair) => {
                match pair.secret() {
                    Ok(_) => true,
                    _ => false,
                }
            }
            _ => false,
        }
    }

    fn create_origin(origin: &str, cache_path: &Path) -> Result<()> {
        let result = command::origin::key::generate::start(&origin, cache_path);
        println!("");
        result
    }

    fn prompt_origin() -> Result<String> {
        let config = try!(config::load());
        let default = match config.origin {
            Some(o) => {
                para(&format!("You already have a default origin set up as `{}', but feel free \
                               to change it if you wish.",
                              &o));
                Some(o)
            }
            None => env::var("USER").ok(),
        };
        prompt_ask("Default origin name", default.as_ref().map(|x| &**x))
    }

    fn ask_default_auth_token() -> Result<bool> {
        prompt_yes_no("Set up a default GitHub access token?", Some(true))
    }

    fn prompt_auth_token() -> Result<String> {
        let config = try!(config::load());
        let default = match config.auth_token {
            Some(o) => {
                para("You already have a default auth token set up, but feel free to change it \
                      if you wish.");
                Some(o)
            }
            None => None,
        };
        prompt_ask("GitHub access token", default.as_ref().map(|x| &**x))
    }

    fn ask_enable_analytics(analytics_path: &Path) -> Result<bool> {
        let default = match analytics::is_opted_in(analytics_path) {
            Some(val) => Some(val),
            None => Some(true),
        };
        prompt_yes_no("Enable analytics?", default)
    }

    fn opt_in_analytics(analytics_path: &Path, generated_origin: bool) -> Result<()> {
        let result = analytics::opt_in(analytics_path, generated_origin);
        println!("");
        result
    }

    fn opt_out_analytics(analytics_path: &Path) -> Result<()> {
        let result = analytics::opt_out(analytics_path);
        println!("");
        result
    }

    fn title(text: &str) {
        println!("{}", Green.bold().paint(text));
        println!("{}\n",
                 Green.bold().paint(format!("{:=<width$}", "", width = text.chars().count())));
    }

    fn heading(text: &str) {
        println!("{}\n", Green.bold().paint(text));
    }

    fn para(text: &str) {
        print_wrapped(text, 75, 2)
    }

    fn print_wrapped(text: &str, wrap_width: usize, left_indent: usize) {
        for line in text.split("\n\n") {
            let mut buffer = String::new();
            let mut width = 0;
            for word in line.split_whitespace() {
                let wl = word.chars().count();
                if (width + wl + 1) > (wrap_width - left_indent) {
                    println!("{:<width$}{}", " ", buffer, width = left_indent);
                    buffer.clear();
                    width = 0;
                }
                width = width + wl + 1;
                buffer.push_str(word);
                buffer.push(' ');
            }
            if !buffer.is_empty() {
                println!("{:<width$}{}", " ", buffer, width = left_indent);
            }
            println!("");
        }
    }

    fn prompt_yes_no(question: &str, default: Option<bool>) -> Result<bool> {
        let choice = match default {
            Some(yes) => {
                if yes {
                    format!("{}{}{}",
                            White.paint("["),
                            White.bold().paint("Yes"),
                            White.paint("/no/quit]"))
                } else {
                    format!("{}{}{}",
                            White.paint("[yes/"),
                            White.bold().paint("No"),
                            White.paint("/quit]"))
                }
            }
            None => format!("{}", White.paint("[yes/no/quit]")),
        };
        loop {
            try!(io::stdout().flush());
            print!("{} {} ", Cyan.paint(question), choice);
            try!(io::stdout().flush());
            let mut response = String::new();
            try!(io::stdin().read_line(&mut response));
            match response.trim().chars().next().unwrap_or('\n') {
                'y' | 'Y' => return Ok(true),
                'n' | 'N' => return Ok(false),
                'q' | 'Q' => process::exit(0),
                '\n' => {
                    match default {
                        Some(default) => return Ok(default),
                        None => continue,
                    }
                }
                _ => continue,
            }
        }
    }

    fn prompt_ask(question: &str, default: Option<&str>) -> Result<String> {
        let choice = match default {
            Some(d) => {
                format!(" {}{}{}",
                        White.paint("[default: "),
                        White.bold().paint(d),
                        White.paint("]"))
            }
            None => "".to_string(),
        };
        loop {
            try!(io::stdout().flush());
            print!("{}{} ", Cyan.paint(format!("{}:", question)), choice);
            try!(io::stdout().flush());
            let mut response = String::new();
            try!(io::stdin().read_line(&mut response));
            if response.trim().is_empty() {
                match default {
                    Some(d) => return Ok(d.to_string()),
                    None => continue,
                }
            }
            return Ok(response.trim().to_string());
        }
    }
}
<commit_msg>Fix minor formatting errors in setup.<commit_after>// Copyright (c) 2016 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

pub mod setup {
    use std::io::{self, Write};
    use std::path::Path;
    use std::process;

    use ansi_term::Colour::{Cyan, Green, White};
    use hcore::crypto::SigKeyPair;
    use hcore::env;

    use analytics;
    use command;
    use config::{self, Config};
    use error::Result;

    pub fn start(cache_path: &Path, analytics_path: &Path) -> Result<()> {
        let mut generated_origin = false;

        println!("");
        title("Habitat CLI Setup");
        para("Welcome to hab setup. Let's get started.");

        heading("Set up a default origin");
        para("Every package in Habitat belongs to an origin, which indicates the person or \
              organization responsible for maintaining that package. Each origin also has \
              a key used to cryptographically sign packages in that origin.");
        para("Selecting a default origin tells package building operations such as 'hab pkg \
              build' what key should be used to sign the packages produced. If you do not \
              set a default origin now, you will have to tell package building commands each \
              time what origin to use.");
        para("For more information on origins and how they are used in building packages, \
              please consult the docs at https://www.habitat.sh/docs/build-packages-overview/");
        if try!(ask_default_origin()) {
            println!("");
            para("Enter the name of your origin. If you plan to publish your packages publicly, \
                  we recommend that you select one that is not already in use on the Habitat \
                  build service found at https://app.habitat.sh/.");
            let origin = try!(prompt_origin());
            try!(write_cli_config_origin(&origin));
            println!("");
            if is_origin_in_cache(&origin, cache_path) {
                para(&format!("You already have an origin key for {} created and installed. \
                              Great work!",
                              &origin));
            } else {
                heading("Create origin key pair");
                para(&format!("It doesn't look like you have a signing key for the origin `{}'. \
                               Without it, you won't be able to build new packages successfully.",
                              &origin));
                para("You can either create a new signing key now, or, if you are building \
                      packages for an origin that already exists, ask the owner to give you the \
                      signing key.");
                para("For more information on the use of origin keys, please consult the \
                      documentation at https://www.habitat.sh/docs/concepts-keys/#origin-keys");
                if try!(ask_create_origin(&origin)) {
                    try!(create_origin(&origin, cache_path));
                    generated_origin = true;
                } else {
                    para(&format!("You might want to create an origin key later with: `hab \
                                  origin key generate {}'",
                                  &origin));
                }
            }
        } else {
            para("Okay, maybe another time.");
        }
        heading("GitHub Access Token");
        para("While you can build and run Habitat packages without sharing them on the public \
              depot, doing so allows you to collaborate with the Habitat community. In addition, \
              it is how you can perform continuous deployment with Habitat.");
        para("The depot uses GitHub authentication with an access token \
              (https://help.github.com/articles/creating-an-access-token-for-command-line-use/).");
        para("If you would like to share your packages on the depot, please enter your GitHub \
              access token. Otherwise, just enter No.");
        para("For more information on sharing packages on the depot, please read the \
              documentation at https://www.habitat.sh/docs/share-packages-overview/");
        if try!(ask_default_auth_token()) {
            println!("");
            para("Enter your GitHub access token.");
            let auth_token = try!(prompt_auth_token());
            try!(write_cli_config_auth_token(&auth_token));
        } else {
            para("Okay, maybe another time.");
        }
        heading("Analytics");
        para("The `hab` command-line tool will optionally send anonymous usage data to Habitat's \
             Google Analytics account. This is a strictly opt-in activity and no tracking will \
             occur unless you respond affirmatively to the question below.");
        para("We collect this data to help improve Habitat's user experience. For example, we \
              would like to know the category of tasks users are performing, and which ones they \
              are having trouble with (e.g. mistyping command line arguments).");
        para("To see what kinds of data are sent and how they are anonymized, please read more \
             about our analytics here: https://www.habitat.sh/docs/about-analytics/");
        if try!(ask_enable_analytics(analytics_path)) {
            try!(opt_in_analytics(analytics_path, generated_origin));
        } else {
            try!(opt_out_analytics(analytics_path));
        }
        heading("CLI Setup Complete");
        para("That's all for now. Thanks for using Habitat!");
        Ok(())
    }

    fn ask_default_origin() -> Result<bool> {
        prompt_yes_no("Set up a default origin?", Some(true))
    }

    fn ask_create_origin(origin: &str) -> Result<bool> {
        prompt_yes_no(&format!("Create an origin key for `{}'?", origin),
                      Some(true))
    }

    fn write_cli_config_origin(origin: &str) -> Result<()> {
        let mut config = try!(config::load());
        config.origin = Some(origin.to_string());
        config::save(&config)
    }

    fn write_cli_config_auth_token(auth_token: &str) -> Result<()> {
        let mut config = try!(config::load());
        config.auth_token = Some(auth_token.to_string());
        config::save(&config)
    }

    fn is_origin_in_cache(origin: &str, cache_path: &Path) -> bool {
        match SigKeyPair::get_latest_pair_for(origin, cache_path) {
            Ok(pair) => {
                match pair.secret() {
                    Ok(_) => true,
                    _ => false,
                }
            }
            _ => false,
        }
    }

    fn create_origin(origin: &str, cache_path: &Path) -> Result<()> {
        let result = command::origin::key::generate::start(&origin, cache_path);
        println!("");
        result
    }

    fn prompt_origin() -> Result<String> {
        let config = try!(config::load());
        let default = match config.origin {
            Some(o) => {
                para(&format!("You already have a default origin set up as `{}', but feel free \
                               to change it if you wish.",
                              &o));
                Some(o)
            }
            None => env::var("USER").ok(),
        };
        prompt_ask("Default origin name", default.as_ref().map(|x| &**x))
    }

    fn ask_default_auth_token() -> Result<bool> {
        prompt_yes_no("Set up a default GitHub access token?", Some(true))
    }

    fn prompt_auth_token() -> Result<String> {
        let config = try!(config::load());
        let default = match config.auth_token {
            Some(o) => {
                para("You already have a default auth token set up, but feel free to change it \
                      if you wish.");
                Some(o)
            }
            None => None,
        };
        prompt_ask("GitHub access token", default.as_ref().map(|x| &**x))
    }

    fn ask_enable_analytics(analytics_path: &Path) -> Result<bool> {
        let default = match analytics::is_opted_in(analytics_path) {
            Some(val) => Some(val),
            None => Some(true),
        };
        prompt_yes_no("Enable analytics?", default)
    }

    fn opt_in_analytics(analytics_path: &Path, generated_origin: bool) -> Result<()> {
        let result = analytics::opt_in(analytics_path, generated_origin);
        println!("");
        result
    }

    fn opt_out_analytics(analytics_path: &Path) -> Result<()> {
        let result = analytics::opt_out(analytics_path);
        println!("");
        result
    }

    fn title(text: &str) {
        println!("{}", Green.bold().paint(text));
        println!("{}\n",
                 Green.bold().paint(format!("{:=<width$}", "", width = text.chars().count())));
    }

    fn heading(text: &str) {
        println!("{}\n", Green.bold().paint(text));
    }

    fn para(text: &str) {
        print_wrapped(text, 75, 2)
    }

    fn print_wrapped(text: &str, wrap_width: usize, left_indent: usize) {
        for line in text.split("\n\n") {
            let mut buffer = String::new();
            let mut width = 0;
            for word in line.split_whitespace() {
                let wl = word.chars().count();
                if (width + wl + 1) > (wrap_width - left_indent) {
                    println!("{:<width$}{}", " ", buffer, width = left_indent);
                    buffer.clear();
                    width = 0;
                }
                width = width + wl + 1;
                buffer.push_str(word);
                buffer.push(' ');
            }
            if !buffer.is_empty() {
                println!("{:<width$}{}", " ", buffer, width = left_indent);
            }
            println!("");
        }
    }

    fn prompt_yes_no(question: &str, default: Option<bool>) -> Result<bool> {
        let choice = match default {
            Some(yes) => {
                if yes {
                    format!("{}{}{}",
                            White.paint("["),
                            White.bold().paint("Yes"),
                            White.paint("/no/quit]"))
                } else {
                    format!("{}{}{}",
                            White.paint("[yes/"),
                            White.bold().paint("No"),
                            White.paint("/quit]"))
                }
            }
            None => format!("{}", White.paint("[yes/no/quit]")),
        };
        loop {
            try!(io::stdout().flush());
            print!("{} {} ", Cyan.paint(question), choice);
            try!(io::stdout().flush());
            let mut response = String::new();
            try!(io::stdin().read_line(&mut response));
            match response.trim().chars().next().unwrap_or('\n') {
                'y' | 'Y' => return Ok(true),
                'n' | 'N' => return Ok(false),
                'q' | 'Q' => process::exit(0),
                '\n' => {
                    match default {
                        Some(default) => return Ok(default),
                        None => continue,
                    }
                }
                _ => continue,
            }
        }
    }

    fn prompt_ask(question: &str, default: Option<&str>) -> Result<String> {
        let choice = match default {
            Some(d) => {
                format!(" {}{}{}",
                        White.paint("[default: "),
                        White.bold().paint(d),
                        White.paint("]"))
            }
            None => "".to_string(),
        };
        loop {
            try!(io::stdout().flush());
            print!("{}{} ", Cyan.paint(format!("{}:", question)), choice);
            try!(io::stdout().flush());
            let mut response = String::new();
            try!(io::stdin().read_line(&mut response));
            if response.trim().is_empty() {
                match default {
                    Some(d) => return Ok(d.to_string()),
                    None => continue,
                }
            }
            return Ok(response.trim().to_string());
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::str;

use butterfly::rumor::service::SysInfo;
use hcore;
#[cfg(any(target_os="linux", target_os="macos"))]
use libc;

use VERSION;
use config::GossipListenAddr;
use error::{Error, Result};
use http_gateway;

static LOGKEY: &'static str = "SY";

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct Sys {
    pub version: String,
    pub member_id: String,
    pub ip: IpAddr,
    pub hostname: String,
    pub gossip_ip: IpAddr,
    pub gossip_port: u16,
    pub http_gateway_ip: IpAddr,
    pub http_gateway_port: u16,
    pub permanent: bool,
}

impl Sys {
    pub fn new(permanent: bool, gossip: GossipListenAddr, http: http_gateway::ListenAddr) -> Sys {
        let ip = match lookup_ip() {
            Ok(ip) => ip,
            Err(e) => {
                let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
                outputln!("IP Address lookup failed; using fallback of {} ({})", ip, e);
                ip
            }
        };
        let host = match lookup_hostname() {
            Ok(host) => host,
            Err(e) => {
                let host = String::from("localhost");
                outputln!("Hostname lookup failed; using fallback of {} ({})", host, e);
                host
            }
        };
        Sys {
            version: VERSION.to_string(),
            member_id: "unloaded".to_string(),
            ip: ip,
            hostname: host,
            gossip_ip: gossip.ip(),
            gossip_port: gossip.port(),
            http_gateway_ip: http.ip(),
            http_gateway_port: http.port(),
            permanent: permanent,
        }
    }

    pub fn as_sys_info(&self) -> SysInfo {
        let mut sys_info = SysInfo::new();
        sys_info.set_ip(self.ip.to_string());
        sys_info.set_hostname(self.hostname.clone());
        sys_info.set_gossip_ip(self.gossip_ip.to_string());
        sys_info.set_gossip_port(self.gossip_port as u32);
        sys_info.set_http_gateway_ip(self.http_gateway_ip.to_string());
        sys_info.set_http_gateway_port(self.http_gateway_port as u32);
        sys_info
    }

    pub fn gossip_listen(&self) -> SocketAddr {
        SocketAddr::new(self.gossip_ip, self.gossip_port)
    }

    pub fn http_listen(&self) -> http_gateway::ListenAddr {
        http_gateway::ListenAddr::new(self.http_gateway_ip, self.http_gateway_port)
    }
}

pub fn lookup_ip() -> Result<IpAddr> {
    match hcore::util::sys::ip() {
        Ok(s) => Ok(s),
        Err(e) => Err(sup_error!(Error::HabitatCore(e))),
    }
}

#[cfg(any(target_os="linux", target_os="macos"))]
pub fn lookup_hostname() -> Result<String> {
    use std::ffi::CStr;

    let len = 255;
    let mut buf = Vec::<u8>::with_capacity(len);
    let ptr = buf.as_mut_slice().as_mut_ptr();
    let err = unsafe { gethostname(ptr as *mut libc::c_char, len as libc::size_t) };
    match err {
        0 => {
            let slice = unsafe { CStr::from_ptr(ptr as *const i8) };
            let s = try!(slice.to_str());
            Ok(s.to_string())
        }
        n => {
            error!("gethostname failure: {}", n);
            Err(sup_error!(Error::IPFailed))
        }
    }
}

#[cfg(target_os = "windows")]
pub fn lookup_hostname() -> Result<String> {
    use std::env;

    match env::var("COMPUTERNAME") {
        Ok(computername) => Ok(computername),
        Err(_) => Err(sup_error!(Error::IPFailed)),
    }
}

#[cfg(any(target_os="linux", target_os="macos"))]
extern "C" {
    pub fn gethostname(name: *mut libc::c_char, size: libc::size_t) -> libc::c_int;
}
<commit_msg>ARM returns u8, x86 i8, changed from hard-coded to whatever libc recommends for the platform Signed-off-by: Lamont Lucas <lamont@fastrobot.com><commit_after>// Copyright (c) 2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::str;

use butterfly::rumor::service::SysInfo;
use hcore;
#[cfg(any(target_os="linux", target_os="macos"))]
use libc;

use VERSION;
use config::GossipListenAddr;
use error::{Error, Result};
use http_gateway;

static LOGKEY: &'static str = "SY";

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct Sys {
    pub version: String,
    pub member_id: String,
    pub ip: IpAddr,
    pub hostname: String,
    pub gossip_ip: IpAddr,
    pub gossip_port: u16,
    pub http_gateway_ip: IpAddr,
    pub http_gateway_port: u16,
    pub permanent: bool,
}

impl Sys {
    pub fn new(permanent: bool, gossip: GossipListenAddr, http: http_gateway::ListenAddr) -> Sys {
        let ip = match lookup_ip() {
            Ok(ip) => ip,
            Err(e) => {
                let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
                outputln!("IP Address lookup failed; using fallback of {} ({})", ip, e);
                ip
            }
        };
        let host = match lookup_hostname() {
            Ok(host) => host,
            Err(e) => {
                let host = String::from("localhost");
                outputln!("Hostname lookup failed; using fallback of {} ({})", host, e);
                host
            }
        };
        Sys {
            version: VERSION.to_string(),
            member_id: "unloaded".to_string(),
            ip: ip,
            hostname: host,
            gossip_ip: gossip.ip(),
            gossip_port: gossip.port(),
            http_gateway_ip: http.ip(),
            http_gateway_port: http.port(),
            permanent: permanent,
        }
    }

    pub fn as_sys_info(&self) -> SysInfo {
        let mut sys_info = SysInfo::new();
        sys_info.set_ip(self.ip.to_string());
        sys_info.set_hostname(self.hostname.clone());
        sys_info.set_gossip_ip(self.gossip_ip.to_string());
        sys_info.set_gossip_port(self.gossip_port as u32);
        sys_info.set_http_gateway_ip(self.http_gateway_ip.to_string());
        sys_info.set_http_gateway_port(self.http_gateway_port as u32);
        sys_info
    }

    pub fn gossip_listen(&self) -> SocketAddr {
        SocketAddr::new(self.gossip_ip, self.gossip_port)
    }

    pub fn http_listen(&self) -> http_gateway::ListenAddr {
        http_gateway::ListenAddr::new(self.http_gateway_ip, self.http_gateway_port)
    }
}

pub fn lookup_ip() -> Result<IpAddr> {
    match hcore::util::sys::ip() {
        Ok(s) => Ok(s),
        Err(e) => Err(sup_error!(Error::HabitatCore(e))),
    }
}

#[cfg(any(target_os="linux", target_os="macos"))]
pub fn lookup_hostname() -> Result<String> {
    use std::ffi::CStr;

    let len = 255;
    let mut buf = Vec::<u8>::with_capacity(len);
    let ptr = buf.as_mut_slice().as_mut_ptr();
    let err = unsafe { gethostname(ptr as *mut libc::c_char, len as libc::size_t) };
    match err {
        0 => {
            let slice = unsafe { CStr::from_ptr(ptr as *const libc::c_char) };
            let s = try!(slice.to_str());
            Ok(s.to_string())
        }
        n => {
            error!("gethostname failure: {}", n);
            Err(sup_error!(Error::IPFailed))
        }
    }
}

#[cfg(target_os = "windows")]
pub fn lookup_hostname() -> Result<String> {
    use std::env;

    match env::var("COMPUTERNAME") {
        Ok(computername) => Ok(computername),
        Err(_) => Err(sup_error!(Error::IPFailed)),
    }
}

#[cfg(any(target_os="linux", target_os="macos"))]
extern "C" {
    pub fn gethostname(name: *mut libc::c_char, size: libc::size_t) -> libc::c_int;
}
<|endoftext|>


<commit_before>extern crate llvm_sys as llvm;
use std::collections::hash_map::Entry;
use std::ffi::CString;
use std::ptr;
use self::llvm::prelude::*;

use node::*;
use env::Env;

#[derive(Clone)]
pub enum Value {
    Int(LLVMValueRef),
    Function(LLVMValueRef), // to fix
    Lambda(Env<Value>, Node, Node), // env, args, body
}

impl Value {
    pub fn to_ref(&self) -> LLVMValueRef {
        match self {
            &Value::Int(v) => v,
            &Value::Function(v) => v,
            _ => panic!("TBC"),
        }
    }
}

pub struct VM {
    context: LLVMContextRef,
    builder: LLVMBuilderRef,
    module: LLVMModuleRef,
    int_value_type: LLVMTypeRef,
    prims: Env<String>,
}

macro_rules! cptr {
    ($x: expr) => (CString::new($x).unwrap().as_ptr())
}

impl VM {
    pub fn new() -> VM {
        let context = VM::create_context();
        let e = &mut Env::new();
        // alias
        e.register("+", "prim_add".to_string());
        e.register("-", "prim_sub".to_string());
        e.register("*", "prim_mul".to_string());
        e.register("/", "prim_div".to_string());

        VM {
            context: context,
            builder: VM::create_builder_in_context(context),
            module: VM::create_module_with_name("rlisp"),
            int_value_type: VM::int_type(context),
            prims: e.clone(),
        }
    }

    pub fn run(&self, node: &Node) {
        let env = &mut Env::new();
        self.init(env);
        self.pre_gen(node, env);
        self.llvm_ret(self.codegen(node, env));
        self.finalize();
    }

    fn init(&self, env: &mut Env<Value>) {
        self.register_symbols(env);

        // create main
        self.create_fun_and_set_bb("main", self.int_value_type, &mut []);
    }

    fn pre_gen(&self, ast: &Node, env: &mut Env<Value>) {
        if let &Node::Cell(ref car, ref cdr) = ast {
            if rnil() == **cdr {
                self.pre_gen(car, env);
                return;
            }

            match sym_to_str(car) {
                Ok(x) => {
                    match x.as_ref() {
                        "define" => {
                            if let Node::Cell(ref ncar, ref ncdr) = **cdr {
                                let sym_name = sym_to_str(ncar).unwrap();
                                let val = self.codegen(&rcar(ncdr).unwrap().clone(), env);
                                let ptr = match env.entry(sym_name.as_ref()) {
                                    Entry::Occupied(o) => o.get().clone(),
                                    Entry::Vacant(v) => {
                                        let p =
                                            self.allocate_mem(sym_name.as_ref(),
                                                              self.int_value_type);
                                        let p = Value::Int(p);
                                        v.insert(p.clone());
                                        p
                                    }
                                };
                                self.llmv_store(val, ptr.to_ref());

                            }
                        }
                        _ => self.pre_gen(cdr, env),
                    }
                }
                Err(_) => {
                    self.pre_gen(car, env);
                    self.pre_gen(cdr, env);
                }
            }
        }
    }

    fn register_symbols(&self, env: &mut Env<Value>) {
        env.register("+", Value::Function(self.prim_arith("prim_add")));
        env.register("-", Value::Function(self.prim_arith("prim_sub")));
        env.register("*", Value::Function(self.prim_arith("prim_mul")));
        env.register("/", Value::Function(self.prim_arith("prim_div")));
    }

    fn finalize(&self) {
        unsafe {
            llvm::core::LLVMDumpModule(self.module);
            llvm::core::LLVMPrintModuleToFile(self.module, cptr!("out.ll"), ptr::null_mut());

            llvm::core::LLVMDisposeBuilder(self.builder);
            llvm::core::LLVMDisposeModule(self.module);
            llvm::core::LLVMContextDispose(self.context);
        }
    }

    fn prim_arith(&self, name: &str) -> LLVMValueRef {
        let ty = self.int_value_type;
        let arg_types = &mut [ty, ty];
        let fun = self.create_fun_and_set_bb(name, ty, arg_types);
        let lh = self.get_param_fun(&fun, 0);
        let rh = self.get_param_fun(&fun, 1);
        let v = match name {
            "prim_add" => self.llvm_add(lh, rh),
            "prim_sub" => self.llvm_sub(lh, rh),
            "prim_mul" => self.llvm_mul(lh, rh),
            "prim_div" => self.llvm_div(lh, rh),
            _ => {
                println!("{:?}", name);
                panic!("not support arith")
            }
        };
        self.llvm_ret(v);
        fun
    }

    fn llvm_ret(&self, ret: LLVMValueRef) -> LLVMValueRef {
        unsafe { llvm::core::LLVMBuildRet(self.builder, ret) }
    }

    fn llvm_add(&self, lh: LLVMValueRef, rh: LLVMValueRef) -> LLVMValueRef {
        unsafe { llvm::core::LLVMBuildAdd(self.builder, lh, rh, cptr!("v")) }
    }

    fn llvm_sub(&self, lh: LLVMValueRef, rh: LLVMValueRef) -> LLVMValueRef {
        unsafe { llvm::core::LLVMBuildSub(self.builder, lh, rh, cptr!("v")) }
    }

    fn llvm_mul(&self, lh: LLVMValueRef, rh: LLVMValueRef) -> LLVMValueRef {
        unsafe { llvm::core::LLVMBuildMul(self.builder, lh, rh, cptr!("v")) }
    }

    fn llvm_div(&self, lh: LLVMValueRef, rh: LLVMValueRef) -> LLVMValueRef {
        unsafe { llvm::core::LLVMBuildUDiv(self.builder, lh, rh, cptr!("v")) }
    }

    fn get_param_fun(&self, func: &LLVMValueRef, i: u32) -> LLVMValueRef {
        unsafe { llvm::core::LLVMGetParam(*func, i) }
    }

    fn count_params_fun(&self, func: &LLVMValueRef) -> u32 {
        unsafe { llvm::core::LLVMCountParams(*func) }
    }

    fn get_params_fun(&self, fun: &LLVMValueRef) -> Vec<LLVMValueRef> {
        let count = self.count_params_fun(fun) as usize;
        let p = {
            let buf = &mut Vec::with_capacity(count);
            buf.as_mut_ptr()
        };
        unsafe {
            // std::mem::forget(buf);
            llvm::core::LLVMGetParams(*fun, p);
            Vec::from_raw_parts(p, count, count)
        }
    }

    fn create_fun_and_set_bb(&self,
                             name: &str,
                             ret_ty: LLVMTypeRef,
                             arg_types: &mut [LLVMTypeRef])
                             -> LLVMValueRef {
        let fun_type = self.get_function_type(ret_ty, arg_types);
        let fun = self.add_function(name, fun_type);
        let bb = self.append_basic_block("entry", fun);
        self.set_builder_position_at_end(bb);
        fun
    }

    fn codegen(&self, ast: &Node, env: &mut Env<Value>) -> LLVMValueRef {
        match *ast {
            Node::Int(val) => self.int_value(val as u64),
            Node::Cell(ref car, ref cdr) => {
                match **car {
                    Node::Sym(ref n) => self.apply_fun(env, n, cdr),
                    _ => panic!("not suport"),
                }
            }
            Node::Sym(ref name) => {
                let ptr = env.find(name).unwrap();
                self.build_load(ptr.to_ref(), name)
            }
            ref a => {
                println!("{:?}", a);
                panic!("not support in codegen")
            }
        }
    }

    fn apply_fun(&self, env: &mut Env<Value>, name: &str, rest: &Node) -> LLVMValueRef {
        match name {
            "+" | "-" | "*" | "/" => self.codegen_arith(name, rest, env),
            "define" => {
                let c = rcar(rest).and_then(|v| sym_to_str(&v.clone())).unwrap();
                self.codegen(&Node::Sym(c), env)
            }
            "progn" => {
                env.push_local_scope();

                let mut vec = node_to_list(&mut rest.clone());
                let vec2 = vec.split_off(1);
                let mut ret = self.codegen(&vec[0], env);
                for v in vec2 {
                    ret = self.codegen(&v.clone(), env);
                }
                env.pop_local_scope();
                ret
            }
            _ => {
                // env.find(name)
                println!("at apply fun {:?}", name);
                panic!("unknow");
            }
        }
    }

    fn codegen_arith(&self, fname: &str, rest: &Node, env: &mut Env<Value>) -> LLVMValueRef {
        match rest {
            &Node::Cell(ref car, ref cdr) => {
                let lh = self.codegen(car, env); // to fix
                if **cdr == rnil() {
                    return lh;
                }
                let rh = self.codegen_arith(fname, cdr, env);
                let v = &fname.into();
                let name = self.prims.find(fname).unwrap_or(v);
                let fun = self.find_function(name).unwrap();
                let args = &mut Vec::new();
                args.push(lh);
                args.push(rh);
                unsafe {
                    llvm::core::LLVMBuildCall(self.builder, fun, args.as_mut_ptr(), 2, cptr!("v"))
                }

            }
            _ => panic!("siran"),
        }
    }

    fn codegen_list(&self, env: &mut Env<Value>, n: &Node) -> Vec<LLVMValueRef> {
        let args = &mut Vec::new();
        let mut node = n;
        while *node != rnil() {
            match node {
                &Node::Cell(ref car, ref cdr) => {
                    args.push(self.codegen(car, env));
                    node = cdr
                }
                _ => panic!("hgoe"),
            }
        }
        args.clone()
    }

    fn apply_codegen(&self, env: &mut Env<Value>, fun: LLVMValueRef, rest: &Node) -> LLVMValueRef {
        let args = self.codegen_list(env, rest);
        let mut_args = args.clone().as_mut_ptr();
        unsafe {
            llvm::core::LLVMBuildCall(self.builder, fun, mut_args, args.len() as u32, cptr!("v"))
        }
    }

    fn find_function(&self, name: &str) -> Option<LLVMValueRef> {
        let v = unsafe { llvm::core::LLVMGetNamedFunction(self.module, cptr!(name)) };
        let is_null = unsafe { llvm::core::LLVMIsNull(v) > 0 };
        if is_null { None } else { Some(v) }
    }

    fn int_value(&self, val: u64) -> LLVMValueRef {
        unsafe { llvm::core::LLVMConstInt(self.int_value_type, val, 0) }
    }

    fn allocate_mem(&self, name: &str, typ: LLVMTypeRef) -> LLVMValueRef {
        unsafe { llvm::core::LLVMBuildAlloca(self.builder, typ, cptr!(name)) }
    }

    fn build_load(&self, ptr: LLVMValueRef, name: &str) -> LLVMValueRef {
        unsafe { llvm::core::LLVMBuildLoad(self.builder, ptr, cptr!(name)) }
    }

    fn llmv_store(&self, val: LLVMValueRef, target: LLVMValueRef) -> LLVMValueRef {
        unsafe { llvm::core::LLVMBuildStore(self.builder, val, target) }
    }

    fn set_builder_position_at_end(&self, bb: LLVMBasicBlockRef) {
        unsafe {
            llvm::core::LLVMPositionBuilderAtEnd(self.builder, bb);
        }
    }

    fn append_basic_block(&self, name: &str, fun: LLVMValueRef) -> LLVMBasicBlockRef {
        unsafe { llvm::core::LLVMAppendBasicBlockInContext(self.context, fun, cptr!(name)) }
    }

    fn get_function_type(&self,
                         ret_type: LLVMTypeRef,
                         args_type: &mut [LLVMTypeRef])
                         -> LLVMTypeRef {
        let l = args_type.len() as u32;
        if l == 0 {
            unsafe { llvm::core::LLVMFunctionType(ret_type, ptr::null_mut(), 0, 0) }
        } else {
            unsafe { llvm::core::LLVMFunctionType(ret_type, args_type.as_mut_ptr(), l, 0) }
        }
    }

    fn add_function(&self, name: &str, fun_type: LLVMTypeRef) -> LLVMValueRef {
        unsafe { llvm::core::LLVMAddFunction(self.module, cptr!(name), fun_type) }
    }

    fn create_module_with_name(name: &str) -> LLVMModuleRef {
        unsafe { llvm::core::LLVMModuleCreateWithName(cptr!(name)) }
    }

    fn create_builder_in_context(context: LLVMContextRef) -> LLVMBuilderRef {
        unsafe { llvm::core::LLVMCreateBuilderInContext(context) }
    }

    fn create_context() -> LLVMContextRef {
        unsafe { llvm::core::LLVMContextCreate() }
    }

    fn int_type(context: LLVMContextRef) -> LLVMTypeRef {
        unsafe { llvm::core::LLVMInt32TypeInContext(context) }
    }
}
<commit_msg>rearrange the code, create prim define function<commit_after>extern crate llvm_sys as llvm;
use std::collections::hash_map::Entry;
use std::ffi::CString;
use std::ptr;
use self::llvm::prelude::*;

use node::*;
use env::Env;

#[derive(Clone)]
pub enum Value {
    Int(LLVMValueRef),
    Function(LLVMValueRef), // to fix
    Lambda(Env<Value>, Node, Node), // env, args, body
}

impl Value {
    pub fn to_ref(&self) -> LLVMValueRef {
        match self {
            &Value::Int(v) => v,
            &Value::Function(v) => v,
            _ => panic!("TBC"),
        }
    }
}

pub struct VM {
    context: LLVMContextRef,
    builder: LLVMBuilderRef,
    module: LLVMModuleRef,
    int_value_type: LLVMTypeRef,
    prims: Env<String>,
}

macro_rules! cptr {
    ($x: expr) => (CString::new($x).unwrap().as_ptr())
}

impl VM {
    pub fn new() -> VM {
        let context = VM::create_context();
        let e = &mut Env::new();
        // alias
        e.register("+", "prim_add".to_string());
        e.register("-", "prim_sub".to_string());
        e.register("*", "prim_mul".to_string());
        e.register("/", "prim_div".to_string());

        VM {
            context: context,
            builder: VM::create_builder_in_context(context),
            module: VM::create_module_with_name("rlisp"),
            int_value_type: VM::int_type(context),
            prims: e.clone(),
        }
    }

    pub fn run(&self, node: &Node) {
        let env = &mut Env::new();
        self.init(env);
        self.pre_gen(node, env);
        self.llvm_ret(self.codegen(node, env));
        self.finalize();
    }

    fn init(&self, env: &mut Env<Value>) {
        self.register_symbols(env);

        // create main
        self.create_fun_and_set_bb("main", self.int_value_type, &mut []);
    }

    fn pre_gen(&self, ast: &Node, env: &mut Env<Value>) {
        if let &Node::Cell(ref car, ref cdr) = ast {
            if rnil() == **cdr {
                self.pre_gen(car, env);
                return;
            }

            match sym_to_str(car) {
                Ok(x) => {
                    match x.as_ref() {
                        "define" => self.prim_define(&(**cdr), env),
                        _ => self.pre_gen(cdr, env),
                    }
                }
                Err(_) => {
                    self.pre_gen(car, env);
                    self.pre_gen(cdr, env);
                }
            }
        }
    }

    fn prim_define(&self, body: &Node, env: &mut Env<Value>) {
        if let Node::Cell(ref ncar, ref ncdr) = *body {
            let sym_name = sym_to_str(ncar).unwrap();
            let val = self.codegen(&rcar(ncdr).unwrap().clone(), env);
            let ptr = match env.entry(sym_name.as_ref()) {
                Entry::Occupied(o) => o.get().clone(),
                Entry::Vacant(v) => {
                    let p = self.allocate_mem(sym_name.as_ref(), self.int_value_type);
                    let p = Value::Int(p);
                    v.insert(p.clone());
                    p
                }
            };
            self.llmv_store(val, ptr.to_ref());
        }
    }

    fn register_symbols(&self, env: &mut Env<Value>) {
        env.register("+", Value::Function(self.prim_arith("prim_add")));
        env.register("-", Value::Function(self.prim_arith("prim_sub")));
        env.register("*", Value::Function(self.prim_arith("prim_mul")));
        env.register("/", Value::Function(self.prim_arith("prim_div")));
    }

    fn finalize(&self) {
        unsafe {
            llvm::core::LLVMDumpModule(self.module);
            llvm::core::LLVMPrintModuleToFile(self.module, cptr!("out.ll"), ptr::null_mut());

            llvm::core::LLVMDisposeBuilder(self.builder);
            llvm::core::LLVMDisposeModule(self.module);
            llvm::core::LLVMContextDispose(self.context);
        }
    }

    fn prim_arith(&self, name: &str) -> LLVMValueRef {
        let ty = self.int_value_type;
        let arg_types = &mut [ty, ty];
        let fun = self.create_fun_and_set_bb(name, ty, arg_types);
        let lh = self.get_param_fun(&fun, 0);
        let rh = self.get_param_fun(&fun, 1);
        let v = match name {
            "prim_add" => self.llvm_add(lh, rh),
            "prim_sub" => self.llvm_sub(lh, rh),
            "prim_mul" => self.llvm_mul(lh, rh),
            "prim_div" => self.llvm_div(lh, rh),
            _ => {
                println!("{:?}", name);
                panic!("not support arith")
            }
        };
        self.llvm_ret(v);
        fun
    }

    fn llvm_ret(&self, ret: LLVMValueRef) -> LLVMValueRef {
        unsafe { llvm::core::LLVMBuildRet(self.builder, ret) }
    }

    fn llvm_add(&self, lh: LLVMValueRef, rh: LLVMValueRef) -> LLVMValueRef {
        unsafe { llvm::core::LLVMBuildAdd(self.builder, lh, rh, cptr!("v")) }
    }

    fn llvm_sub(&self, lh: LLVMValueRef, rh: LLVMValueRef) -> LLVMValueRef {
        unsafe { llvm::core::LLVMBuildSub(self.builder, lh, rh, cptr!("v")) }
    }

    fn llvm_mul(&self, lh: LLVMValueRef, rh: LLVMValueRef) -> LLVMValueRef {
        unsafe { llvm::core::LLVMBuildMul(self.builder, lh, rh, cptr!("v")) }
    }

    fn llvm_div(&self, lh: LLVMValueRef, rh: LLVMValueRef) -> LLVMValueRef {
        unsafe { llvm::core::LLVMBuildUDiv(self.builder, lh, rh, cptr!("v")) }
    }

    fn get_param_fun(&self, func: &LLVMValueRef, i: u32) -> LLVMValueRef {
        unsafe { llvm::core::LLVMGetParam(*func, i) }
    }

    fn count_params_fun(&self, func: &LLVMValueRef) -> u32 {
        unsafe { llvm::core::LLVMCountParams(*func) }
    }

    fn get_params_fun(&self, fun: &LLVMValueRef) -> Vec<LLVMValueRef> {
        let count = self.count_params_fun(fun) as usize;
        let p = {
            let buf = &mut Vec::with_capacity(count);
            buf.as_mut_ptr()
        };
        unsafe {
            // std::mem::forget(buf);
            llvm::core::LLVMGetParams(*fun, p);
            Vec::from_raw_parts(p, count, count)
        }
    }

    fn create_fun_and_set_bb(&self,
                             name: &str,
                             ret_ty: LLVMTypeRef,
                             arg_types: &mut [LLVMTypeRef])
                             -> LLVMValueRef {
        let fun_type = self.get_function_type(ret_ty, arg_types);
        let fun = self.add_function(name, fun_type);
        let bb = self.append_basic_block("entry", fun);
        self.set_builder_position_at_end(bb);
        fun
    }

    fn codegen(&self, ast: &Node, env: &mut Env<Value>) -> LLVMValueRef {
        match *ast {
            Node::Int(val) => self.int_value(val as u64),
            Node::Cell(ref car, ref cdr) => {
                match **car {
                    Node::Sym(ref n) => self.apply_fun(env, n, cdr),
                    _ => panic!("not suport"),
                }
            }
            Node::Sym(ref name) => {
                let ptr = env.find(name).unwrap();
                self.build_load(ptr.to_ref(), name)
            }
            ref a => {
                println!("{:?}", a);
                panic!("not support in codegen")
            }
        }
    }

    fn apply_fun(&self, env: &mut Env<Value>, name: &str, rest: &Node) -> LLVMValueRef {
        match name {
            "+" | "-" | "*" | "/" => self.codegen_arith(name, rest, env),
            "define" => {
                let c = rcar(rest).and_then(|v| sym_to_str(&v.clone())).unwrap();
                self.codegen(&Node::Sym(c), env)
            }
            "progn" => {
                env.push_local_scope();

                let mut vec = node_to_list(&mut rest.clone());
                let vec2 = vec.split_off(1);
                let mut ret = self.codegen(&vec[0], env);
                for v in vec2 {
                    ret = self.codegen(&v.clone(), env);
                }
                env.pop_local_scope();
                ret
            }
            _ => {
                // env.find(name)
                println!("at apply fun {:?}", name);
                panic!("unknow");
            }
        }
    }

    fn codegen_arith(&self, fname: &str, rest: &Node, env: &mut Env<Value>) -> LLVMValueRef {
        match rest {
            &Node::Cell(ref car, ref cdr) => {
                let lh = self.codegen(car, env); // to fix
                if **cdr == rnil() {
                    return lh;
                }
                let rh = self.codegen_arith(fname, cdr, env);
                let v = &fname.into();
                let name = self.prims.find(fname).unwrap_or(v);
                let fun = self.find_function(name).unwrap();
                let args = &mut Vec::new();
                args.push(lh);
                args.push(rh);
                unsafe {
                    llvm::core::LLVMBuildCall(self.builder, fun, args.as_mut_ptr(), 2, cptr!("v"))
                }

            }
            _ => panic!("siran"),
        }
    }

    fn codegen_list(&self, env: &mut Env<Value>, n: &Node) -> Vec<LLVMValueRef> {
        let args = &mut Vec::new();
        let mut node = n;
        while *node != rnil() {
            match node {
                &Node::Cell(ref car, ref cdr) => {
                    args.push(self.codegen(car, env));
                    node = cdr
                }
                _ => panic!("hgoe"),
            }
        }
        args.clone()
    }

    fn apply_codegen(&self, env: &mut Env<Value>, fun: LLVMValueRef, rest: &Node) -> LLVMValueRef {
        let args = self.codegen_list(env, rest);
        let mut_args = args.clone().as_mut_ptr();
        unsafe {
            llvm::core::LLVMBuildCall(self.builder, fun, mut_args, args.len() as u32, cptr!("v"))
        }
    }

    fn find_function(&self, name: &str) -> Option<LLVMValueRef> {
        let v = unsafe { llvm::core::LLVMGetNamedFunction(self.module, cptr!(name)) };
        let is_null = unsafe { llvm::core::LLVMIsNull(v) > 0 };
        if is_null { None } else { Some(v) }
    }

    fn int_value(&self, val: u64) -> LLVMValueRef {
        unsafe { llvm::core::LLVMConstInt(self.int_value_type, val, 0) }
    }

    fn allocate_mem(&self, name: &str, typ: LLVMTypeRef) -> LLVMValueRef {
        unsafe { llvm::core::LLVMBuildAlloca(self.builder, typ, cptr!(name)) }
    }

    fn build_load(&self, ptr: LLVMValueRef, name: &str) -> LLVMValueRef {
        unsafe { llvm::core::LLVMBuildLoad(self.builder, ptr, cptr!(name)) }
    }

    fn llmv_store(&self, val: LLVMValueRef, target: LLVMValueRef) -> LLVMValueRef {
        unsafe { llvm::core::LLVMBuildStore(self.builder, val, target) }
    }

    fn set_builder_position_at_end(&self, bb: LLVMBasicBlockRef) {
        unsafe {
            llvm::core::LLVMPositionBuilderAtEnd(self.builder, bb);
        }
    }

    fn append_basic_block(&self, name: &str, fun: LLVMValueRef) -> LLVMBasicBlockRef {
        unsafe { llvm::core::LLVMAppendBasicBlockInContext(self.context, fun, cptr!(name)) }
    }

    fn get_function_type(&self,
                         ret_type: LLVMTypeRef,
                         args_type: &mut [LLVMTypeRef])
                         -> LLVMTypeRef {
        let l = args_type.len() as u32;
        if l == 0 {
            unsafe { llvm::core::LLVMFunctionType(ret_type, ptr::null_mut(), 0, 0) }
        } else {
            unsafe { llvm::core::LLVMFunctionType(ret_type, args_type.as_mut_ptr(), l, 0) }
        }
    }

    fn add_function(&self, name: &str, fun_type: LLVMTypeRef) -> LLVMValueRef {
        unsafe { llvm::core::LLVMAddFunction(self.module, cptr!(name), fun_type) }
    }

    fn create_module_with_name(name: &str) -> LLVMModuleRef {
        unsafe { llvm::core::LLVMModuleCreateWithName(cptr!(name)) }
    }

    fn create_builder_in_context(context: LLVMContextRef) -> LLVMBuilderRef {
        unsafe { llvm::core::LLVMCreateBuilderInContext(context) }
    }

    fn create_context() -> LLVMContextRef {
        unsafe { llvm::core::LLVMContextCreate() }
    }

    fn int_type(context: LLVMContextRef) -> LLVMTypeRef {
        unsafe { llvm::core::LLVMInt32TypeInContext(context) }
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use target::Target;

pub fn target() -> Target {
    Target {
        data_layout: "e-p:32:32-f64:32:64-i64:32:64-f80:32:32-n8:16:32".to_string(),
        llvm_target: "i686-unknown-dragonfly".to_string(),
        target_endian: "little".to_string(),
        target_word_size: "32".to_string(),
        arch: "x86".to_string(),
        target_os: "dragonfly".to_string(),
        options: super::dragonfly_base::opts()
    }
}
<commit_msg>Same fix for dragonfly<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use target::Target;

pub fn target() -> Target {
    let mut base = super::draginfly_base::opts();
    base.pre_link_args.push("-m32".to_string());

    Target {
        data_layout: "e-p:32:32-f64:32:64-i64:32:64-f80:32:32-n8:16:32".to_string(),
        llvm_target: "i686-unknown-dragonfly".to_string(),
        target_endian: "little".to_string(),
        target_word_size: "32".to_string(),
        arch: "x86".to_string(),
        target_os: "dragonfly".to_string(),
        options: base,
    }
}
<|endoftext|>


<commit_before>use rustc::hir::def_id::DefId;
use rustc::hir;
use rustc::mir::*;
use rustc::ty::{self, Predicate, TyCtxt};
use rustc_target::spec::abi;
use std::borrow::Cow;
use syntax_pos::Span;

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

pub fn is_min_const_fn(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    def_id: DefId,
    mir: &'a Mir<'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::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::Projection(_) => {
                    let span = tcx.def_span(current);
                    // we'll hit a `Predicate::Trait` later which will report an error
                    tcx.sess
                        .delay_span_bug(span, "projection without trait bound");
                    continue;
                }
                Predicate::Trait(pred) => {
                    if Some(pred.def_id()) == tcx.lang_items().sized_trait() {
                        continue;
                    }
                    match pred.skip_binder().self_ty().sty {
                        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 &mir.local_decls {
        check_ty(tcx, local.ty, local.source_info.span)?;
    }
    // impl trait is gone in MIR, so check the return type manually
    check_ty(
        tcx,
        tcx.fn_sig(def_id).output().skip_binder(),
        mir.local_decls.iter().next().unwrap().source_info.span,
    )?;

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

fn check_ty(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    ty: ty::Ty<'tcx>,
    span: Span,
) -> McfResult {
    for ty in ty.walk() {
        match ty.sty {
            ty::Ref(_, _, hir::Mutability::MutMutable) => 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(..) => {
                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<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    rvalue: &Rvalue<'tcx>,
    span: Span,
) -> McfResult {
    match rvalue {
        Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => {
            check_operand(tcx, mir, operand, span)
        }
        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) => {
            check_place(tcx, mir, place, span)
        }
        Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
            use rustc::ty::cast::CastTy;
            let cast_in = CastTy::from_ty(operand.ty(mir, 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(),
                )),
                (CastTy::RPtr(_), CastTy::Float) => bug!(),
                (CastTy::RPtr(_), CastTy::Int(_)) => bug!(),
                (CastTy::Ptr(_), CastTy::RPtr(_)) => bug!(),
                _ => check_operand(tcx, mir, operand, span),
            }
        }
        Rvalue::Cast(CastKind::UnsafeFnPointer, _, _) |
        Rvalue::Cast(CastKind::ClosureFnPointer, _, _) |
        Rvalue::Cast(CastKind::ReifyFnPointer, _, _) => Err((
            span,
            "function pointer casts are not allowed in const fn".into(),
        )),
        Rvalue::Cast(CastKind::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, mir, lhs, span)?;
            check_operand(tcx, mir, rhs, span)?;
            let ty = lhs.ty(mir, 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(mir, tcx);
            if ty.is_integral() || ty.is_bool() {
                check_operand(tcx, mir, operand, span)
            } else {
                Err((
                    span,
                    "only int and `bool` operations are stable in const fn".into(),
                ))
            }
        }
        Rvalue::Aggregate(_, operands) => {
            for operand in operands {
                check_operand(tcx, mir, operand, span)?;
            }
            Ok(())
        }
    }
}

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

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

        // just an assignment
        StatementKind::SetDiscriminant { .. } => Ok(()),

        | 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<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    operand: &Operand<'tcx>,
    span: Span,
) -> McfResult {
    match operand {
        Operand::Move(place) | Operand::Copy(place) => {
            check_place(tcx, mir, place, span)
        }
        Operand::Constant(_) => Ok(()),
    }
}

fn check_place(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    place: &Place<'tcx>,
    span: Span,
) -> McfResult {
    match place {
        Place::Local(_) => Ok(()),
        // promoteds are always fine, they are essentially constants
        Place::Promoted(_) => Ok(()),
        Place::Static(_) => Err((span, "cannot access `static` items in const fn".into())),
        Place::Projection(proj) => {
            match proj.elem {
                | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. }
                | ProjectionElem::Deref | ProjectionElem::Field(..) | ProjectionElem::Index(_) => {
                    check_place(tcx, mir, &proj.base, span)
                }
                | ProjectionElem::Downcast(..) => {
                    Err((span, "`match` or `if let` in `const fn` is unstable".into()))
                }
            }
        }
    }
}

fn check_terminator(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    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, mir, location, span)
        }
        TerminatorKind::DropAndReplace { location, value, .. } => {
            check_place(tcx, mir, location, span)?;
            check_operand(tcx, mir, value, span)
        },

        TerminatorKind::FalseEdges { .. } | TerminatorKind::SwitchInt { .. } => Err((
            span,
            "`if`, `match`, `&&` and `||` are not stable in const fn".into(),
        )),
        | 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(mir, tcx);
            if let ty::FnDef(def_id, _) = fn_ty.sty {

                // some intrinsics are waved through if called inside the
                // standard library. Users never need to call them directly
                match tcx.fn_sig(def_id).abi() {
                    abi::Abi::RustIntrinsic => if !is_intrinsic_whitelisted(tcx, def_id) {
                        return Err((
                            span,
                            "can only call a curated list of intrinsics in `min_const_fn`".into(),
                        ))
                    },
                    abi::Abi::Rust if tcx.is_min_const_fn(def_id) => {},
                    abi::Abi::Rust => return Err((
                        span,
                        "can only call other `min_const_fn` within a `min_const_fn`".into(),
                    )),
                    abi => return Err((
                        span,
                        format!(
                            "cannot call functions with `{}` abi in `min_const_fn`",
                            abi,
                        ).into(),
                    )),
                }

                check_operand(tcx, mir, func, span)?;

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

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

        TerminatorKind::FalseUnwind { .. } => {
            Err((span, "loops are not allowed in const fn".into()))
        },
    }
}

/// Returns true if the `def_id` refers to an intrisic which we've whitelisted
/// for being called from stable `const fn`s (`min_const_fn`).
///
/// Adding more intrinsics requires sign-off from @rust-lang/lang.
fn is_intrinsic_whitelisted(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
    match &tcx.item_name(def_id).as_str()[..] {
        | "size_of"
        | "min_align_of"
        | "needs_drop"
        // Arithmetic:
        | "add_with_overflow" // ~> .overflowing_add
        | "sub_with_overflow" // ~> .overflowing_sub
        | "mul_with_overflow" // ~> .overflowing_mul
        | "overflowing_add" // ~> .wrapping_add
        | "overflowing_sub" // ~> .wrapping_sub
        | "overflowing_mul" // ~> .wrapping_mul
        | "unchecked_shl" // ~> .wrapping_shl
        | "unchecked_shr" // ~> .wrapping_shr
        | "rotate_left" // ~> .rotate_left
        | "rotate_right" // ~> .rotate_right
        | "ctpop" // ~> .count_ones
        | "ctlz" // ~> .leading_zeros
        | "cttz" // ~> .trailing_zeros
        | "bswap" // ~> .swap_bytes
        | "bitreverse" // ~> .reverse_bits
        => true,
        _ => false,
    }
}
<commit_msg>Remove delay_span_bug from qualify_min_const_fn<commit_after>use rustc::hir::def_id::DefId;
use rustc::hir;
use rustc::mir::*;
use rustc::ty::{self, Predicate, TyCtxt};
use rustc_target::spec::abi;
use std::borrow::Cow;
use syntax_pos::Span;

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

pub fn is_min_const_fn(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    def_id: DefId,
    mir: &'a Mir<'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().sty {
                        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 &mir.local_decls {
        check_ty(tcx, local.ty, local.source_info.span)?;
    }
    // impl trait is gone in MIR, so check the return type manually
    check_ty(
        tcx,
        tcx.fn_sig(def_id).output().skip_binder(),
        mir.local_decls.iter().next().unwrap().source_info.span,
    )?;

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

fn check_ty(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    ty: ty::Ty<'tcx>,
    span: Span,
) -> McfResult {
    for ty in ty.walk() {
        match ty.sty {
            ty::Ref(_, _, hir::Mutability::MutMutable) => 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(..) => {
                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<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    rvalue: &Rvalue<'tcx>,
    span: Span,
) -> McfResult {
    match rvalue {
        Rvalue::Repeat(operand, _) | Rvalue::Use(operand) => {
            check_operand(tcx, mir, operand, span)
        }
        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) => {
            check_place(tcx, mir, place, span)
        }
        Rvalue::Cast(CastKind::Misc, operand, cast_ty) => {
            use rustc::ty::cast::CastTy;
            let cast_in = CastTy::from_ty(operand.ty(mir, 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(),
                )),
                (CastTy::RPtr(_), CastTy::Float) => bug!(),
                (CastTy::RPtr(_), CastTy::Int(_)) => bug!(),
                (CastTy::Ptr(_), CastTy::RPtr(_)) => bug!(),
                _ => check_operand(tcx, mir, operand, span),
            }
        }
        Rvalue::Cast(CastKind::UnsafeFnPointer, _, _) |
        Rvalue::Cast(CastKind::ClosureFnPointer, _, _) |
        Rvalue::Cast(CastKind::ReifyFnPointer, _, _) => Err((
            span,
            "function pointer casts are not allowed in const fn".into(),
        )),
        Rvalue::Cast(CastKind::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, mir, lhs, span)?;
            check_operand(tcx, mir, rhs, span)?;
            let ty = lhs.ty(mir, 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(mir, tcx);
            if ty.is_integral() || ty.is_bool() {
                check_operand(tcx, mir, operand, span)
            } else {
                Err((
                    span,
                    "only int and `bool` operations are stable in const fn".into(),
                ))
            }
        }
        Rvalue::Aggregate(_, operands) => {
            for operand in operands {
                check_operand(tcx, mir, operand, span)?;
            }
            Ok(())
        }
    }
}

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

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

        // just an assignment
        StatementKind::SetDiscriminant { .. } => Ok(()),

        | 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<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    operand: &Operand<'tcx>,
    span: Span,
) -> McfResult {
    match operand {
        Operand::Move(place) | Operand::Copy(place) => {
            check_place(tcx, mir, place, span)
        }
        Operand::Constant(_) => Ok(()),
    }
}

fn check_place(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    place: &Place<'tcx>,
    span: Span,
) -> McfResult {
    match place {
        Place::Local(_) => Ok(()),
        // promoteds are always fine, they are essentially constants
        Place::Promoted(_) => Ok(()),
        Place::Static(_) => Err((span, "cannot access `static` items in const fn".into())),
        Place::Projection(proj) => {
            match proj.elem {
                | ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. }
                | ProjectionElem::Deref | ProjectionElem::Field(..) | ProjectionElem::Index(_) => {
                    check_place(tcx, mir, &proj.base, span)
                }
                | ProjectionElem::Downcast(..) => {
                    Err((span, "`match` or `if let` in `const fn` is unstable".into()))
                }
            }
        }
    }
}

fn check_terminator(
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    mir: &'a Mir<'tcx>,
    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, mir, location, span)
        }
        TerminatorKind::DropAndReplace { location, value, .. } => {
            check_place(tcx, mir, location, span)?;
            check_operand(tcx, mir, value, span)
        },

        TerminatorKind::FalseEdges { .. } | TerminatorKind::SwitchInt { .. } => Err((
            span,
            "`if`, `match`, `&&` and `||` are not stable in const fn".into(),
        )),
        | 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(mir, tcx);
            if let ty::FnDef(def_id, _) = fn_ty.sty {

                // some intrinsics are waved through if called inside the
                // standard library. Users never need to call them directly
                match tcx.fn_sig(def_id).abi() {
                    abi::Abi::RustIntrinsic => if !is_intrinsic_whitelisted(tcx, def_id) {
                        return Err((
                            span,
                            "can only call a curated list of intrinsics in `min_const_fn`".into(),
                        ))
                    },
                    abi::Abi::Rust if tcx.is_min_const_fn(def_id) => {},
                    abi::Abi::Rust => return Err((
                        span,
                        "can only call other `min_const_fn` within a `min_const_fn`".into(),
                    )),
                    abi => return Err((
                        span,
                        format!(
                            "cannot call functions with `{}` abi in `min_const_fn`",
                            abi,
                        ).into(),
                    )),
                }

                check_operand(tcx, mir, func, span)?;

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

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

        TerminatorKind::FalseUnwind { .. } => {
            Err((span, "loops are not allowed in const fn".into()))
        },
    }
}

/// Returns true if the `def_id` refers to an intrisic which we've whitelisted
/// for being called from stable `const fn`s (`min_const_fn`).
///
/// Adding more intrinsics requires sign-off from @rust-lang/lang.
fn is_intrinsic_whitelisted(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool {
    match &tcx.item_name(def_id).as_str()[..] {
        | "size_of"
        | "min_align_of"
        | "needs_drop"
        // Arithmetic:
        | "add_with_overflow" // ~> .overflowing_add
        | "sub_with_overflow" // ~> .overflowing_sub
        | "mul_with_overflow" // ~> .overflowing_mul
        | "overflowing_add" // ~> .wrapping_add
        | "overflowing_sub" // ~> .wrapping_sub
        | "overflowing_mul" // ~> .wrapping_mul
        | "unchecked_shl" // ~> .wrapping_shl
        | "unchecked_shr" // ~> .wrapping_shr
        | "rotate_left" // ~> .rotate_left
        | "rotate_right" // ~> .rotate_right
        | "ctpop" // ~> .count_ones
        | "ctlz" // ~> .leading_zeros
        | "cttz" // ~> .trailing_zeros
        | "bswap" // ~> .swap_bytes
        | "bitreverse" // ~> .reverse_bits
        => true,
        _ => false,
    }
}
<|endoftext|>