<commit_before>use anyhow::Error;

use crate::util::errors::{CargoResult, HttpNot200};
use crate::util::Config;

pub struct Retry<'a> {
    config: &'a Config,
    remaining: u32,
}

impl<'a> Retry<'a> {
    pub fn new(config: &'a Config) -> CargoResult<Retry<'a>> {
        Ok(Retry {
            config,
            remaining: config.net_config()?.retry.unwrap_or(2),
        })
    }

    pub fn r#try<T>(&mut self, f: impl FnOnce() -> CargoResult<T>) -> CargoResult<Option<T>> {
        match f() {
            Err(ref e) if maybe_spurious(e) && self.remaining > 0 => {
                let msg = format!(
                    "spurious network error ({} tries \
                     remaining): {}",
                    self.remaining, e
                );
                self.config.shell().warn(msg)?;
                self.remaining -= 1;
                Ok(None)
            }
            other => other.map(Some),
        }
    }
}

fn maybe_spurious(err: &Error) -> bool {
    for e in err.chain() {
        if let Some(git_err) = e.downcast_ref::<git2::Error>() {
            match git_err.class() {
                git2::ErrorClass::Net | git2::ErrorClass::Os => return true,
                _ => (),
            }
        }
        if let Some(curl_err) = e.downcast_ref::<curl::Error>() {
            if curl_err.is_couldnt_connect()
                || curl_err.is_couldnt_resolve_proxy()
                || curl_err.is_couldnt_resolve_host()
                || curl_err.is_operation_timedout()
                || curl_err.is_recv_error()
                || curl_err.is_http2_error()
                || curl_err.is_http2_stream_error()
                || curl_err.is_ssl_connect_error()
                || curl_err.is_partial_file()
            {
                return true;
            }
        }
        if let Some(not_200) = e.downcast_ref::<HttpNot200>() {
            if 500 <= not_200.code && not_200.code < 600 {
                return true;
            }
        }
    }
    false
}

/// Wrapper method for network call retry logic.
///
/// Retry counts provided by Config object `net.retry`. Config shell outputs
/// a warning on per retry.
///
/// Closure must return a `CargoResult`.
///
/// # Examples
///
/// ```
/// # use crate::cargo::util::{CargoResult, Config};
/// # let download_something = || return Ok(());
/// # let config = Config::default().unwrap();
/// use cargo::util::network;
/// let cargo_result = network::with_retry(&config, || download_something());
/// ```
pub fn with_retry<T, F>(config: &Config, mut callback: F) -> CargoResult<T>
where
    F: FnMut() -> CargoResult<T>,
{
    let mut retry = Retry::new(config)?;
    loop {
        if let Some(ret) = retry.r#try(&mut callback)? {
            return Ok(ret);
        }
    }
}

#[test]
fn with_retry_repeats_the_call_then_works() {
    use crate::core::Shell;

    //Error HTTP codes (5xx) are considered maybe_spurious and will prompt retry
    let error1 = HttpNot200 {
        code: 501,
        url: "Uri".to_string(),
    }
    .into();
    let error2 = HttpNot200 {
        code: 502,
        url: "Uri".to_string(),
    }
    .into();
    let mut results: Vec<CargoResult<()>> = vec![Ok(()), Err(error1), Err(error2)];
    let config = Config::default().unwrap();
    *config.shell() = Shell::from_write(Box::new(Vec::new()));
    let result = with_retry(&config, || results.pop().unwrap());
    assert!(result.is_ok())
}

#[test]
fn with_retry_finds_nested_spurious_errors() {
    use crate::core::Shell;

    //Error HTTP codes (5xx) are considered maybe_spurious and will prompt retry
    //String error messages are not considered spurious
    let error1 = anyhow::Error::from(HttpNot200 {
        code: 501,
        url: "Uri".to_string(),
    });
    let error1 = anyhow::Error::from(error1.context("A non-spurious wrapping err"));
    let error2 = anyhow::Error::from(HttpNot200 {
        code: 502,
        url: "Uri".to_string(),
    });
    let error2 = anyhow::Error::from(error2.context("A second chained error"));
    let mut results: Vec<CargoResult<()>> = vec![Ok(()), Err(error1), Err(error2)];
    let config = Config::default().unwrap();
    *config.shell() = Shell::from_write(Box::new(Vec::new()));
    let result = with_retry(&config, || results.pop().unwrap());
    assert!(result.is_ok())
}

#[test]
fn curle_http2_stream_is_spurious() {
    let code = curl_sys::CURLE_HTTP2_STREAM;
    let err = curl::Error::new(code);
    assert!(maybe_spurious(&err.into()));
}
<commit_msg>Remove unnecessary loop in `maybe_spurious`<commit_after>use anyhow::Error;

use crate::util::errors::{CargoResult, HttpNot200};
use crate::util::Config;

pub struct Retry<'a> {
    config: &'a Config,
    remaining: u32,
}

impl<'a> Retry<'a> {
    pub fn new(config: &'a Config) -> CargoResult<Retry<'a>> {
        Ok(Retry {
            config,
            remaining: config.net_config()?.retry.unwrap_or(2),
        })
    }

    pub fn r#try<T>(&mut self, f: impl FnOnce() -> CargoResult<T>) -> CargoResult<Option<T>> {
        match f() {
            Err(ref e) if maybe_spurious(e) && self.remaining > 0 => {
                let msg = format!(
                    "spurious network error ({} tries \
                     remaining): {}",
                    self.remaining, e
                );
                self.config.shell().warn(msg)?;
                self.remaining -= 1;
                Ok(None)
            }
            other => other.map(Some),
        }
    }
}

fn maybe_spurious(err: &Error) -> bool {
    if let Some(git_err) = err.downcast_ref::<git2::Error>() {
        match git_err.class() {
            git2::ErrorClass::Net | git2::ErrorClass::Os => return true,
            _ => (),
        }
    }
    if let Some(curl_err) = err.downcast_ref::<curl::Error>() {
        if curl_err.is_couldnt_connect()
            || curl_err.is_couldnt_resolve_proxy()
            || curl_err.is_couldnt_resolve_host()
            || curl_err.is_operation_timedout()
            || curl_err.is_recv_error()
            || curl_err.is_http2_error()
            || curl_err.is_http2_stream_error()
            || curl_err.is_ssl_connect_error()
            || curl_err.is_partial_file()
        {
            return true;
        }
    }
    if let Some(not_200) = err.downcast_ref::<HttpNot200>() {
        if 500 <= not_200.code && not_200.code < 600 {
            return true;
        }
    }
    false
}

/// Wrapper method for network call retry logic.
///
/// Retry counts provided by Config object `net.retry`. Config shell outputs
/// a warning on per retry.
///
/// Closure must return a `CargoResult`.
///
/// # Examples
///
/// ```
/// # use crate::cargo::util::{CargoResult, Config};
/// # let download_something = || return Ok(());
/// # let config = Config::default().unwrap();
/// use cargo::util::network;
/// let cargo_result = network::with_retry(&config, || download_something());
/// ```
pub fn with_retry<T, F>(config: &Config, mut callback: F) -> CargoResult<T>
where
    F: FnMut() -> CargoResult<T>,
{
    let mut retry = Retry::new(config)?;
    loop {
        if let Some(ret) = retry.r#try(&mut callback)? {
            return Ok(ret);
        }
    }
}

#[test]
fn with_retry_repeats_the_call_then_works() {
    use crate::core::Shell;

    //Error HTTP codes (5xx) are considered maybe_spurious and will prompt retry
    let error1 = HttpNot200 {
        code: 501,
        url: "Uri".to_string(),
    }
    .into();
    let error2 = HttpNot200 {
        code: 502,
        url: "Uri".to_string(),
    }
    .into();
    let mut results: Vec<CargoResult<()>> = vec![Ok(()), Err(error1), Err(error2)];
    let config = Config::default().unwrap();
    *config.shell() = Shell::from_write(Box::new(Vec::new()));
    let result = with_retry(&config, || results.pop().unwrap());
    assert!(result.is_ok())
}

#[test]
fn with_retry_finds_nested_spurious_errors() {
    use crate::core::Shell;

    //Error HTTP codes (5xx) are considered maybe_spurious and will prompt retry
    //String error messages are not considered spurious
    let error1 = anyhow::Error::from(HttpNot200 {
        code: 501,
        url: "Uri".to_string(),
    });
    let error1 = anyhow::Error::from(error1.context("A non-spurious wrapping err"));
    let error2 = anyhow::Error::from(HttpNot200 {
        code: 502,
        url: "Uri".to_string(),
    });
    let error2 = anyhow::Error::from(error2.context("A second chained error"));
    let mut results: Vec<CargoResult<()>> = vec![Ok(()), Err(error1), Err(error2)];
    let config = Config::default().unwrap();
    *config.shell() = Shell::from_write(Box::new(Vec::new()));
    let result = with_retry(&config, || results.pop().unwrap());
    assert!(result.is_ok())
}

#[test]
fn curle_http2_stream_is_spurious() {
    let code = curl_sys::CURLE_HTTP2_STREAM;
    let err = curl::Error::new(code);
    assert!(maybe_spurious(&err.into()));
}
<|endoftext|>


<commit_before>use crate::analysis::symbols;
use once_cell::sync::Lazy;
use regex::{Captures, Regex};

const LANGUAGE_SEP_BEGIN: &str = "<!-- language=\"";
const LANGUAGE_SEP_END: &str = "\" -->";
const LANGUAGE_BLOCK_BEGIN: &str = "|[";
const LANGUAGE_BLOCK_END: &str = "\n]|";

pub fn reformat_doc(input: &str, symbols: &symbols::Info, in_type: &str) -> String {
    code_blocks_transformation(input, symbols, in_type)
}

fn try_split<'a>(src: &'a str, needle: &str) -> (&'a str, Option<&'a str>) {
    match src.find(needle) {
        Some(pos) => (&src[..pos], Some(&src[pos + needle.len()..])),
        None => (src, None),
    }
}

fn code_blocks_transformation(mut input: &str, symbols: &symbols::Info, in_type: &str) -> String {
    let mut out = String::with_capacity(input.len());

    loop {
        input = match try_split(input, LANGUAGE_BLOCK_BEGIN) {
            (before, Some(after)) => {
                out.push_str(&format(before, symbols, in_type));
                if let (before, Some(after)) =
                    try_split(get_language(after, &mut out), LANGUAGE_BLOCK_END)
                {
                    out.push_str(before);
                    out.push_str("\n```");
                    after
                } else {
                    after
                }
            }
            (before, None) => {
                out.push_str(&format(before, symbols, in_type));
                return out;
            }
        };
    }
}

fn get_language<'a>(entry: &'a str, out: &mut String) -> &'a str {
    if let (_, Some(after)) = try_split(entry, LANGUAGE_SEP_BEGIN) {
        if let (before, Some(after)) = try_split(after, LANGUAGE_SEP_END) {
            out.push_str(&format!("\n```{}", before));
            return after;
        }
    }
    out.push_str("\n```text");
    entry
}

fn format(mut input: &str, symbols: &symbols::Info, in_type: &str) -> String {
    let mut ret = String::with_capacity(input.len());
    loop {
        let (before, after) = try_split(input, "`");
        ret.push_str(&replace_c_types(before, symbols, in_type));
        if let Some(after) = after {
            ret.push('`');
            let (before, after) = try_split(after, "`");
            // don't touch anything enclosed in backticks
            ret.push_str(before);
            if let Some(after) = after {
                ret.push('`');
                input = after;
            } else {
                return ret;
            }
        } else {
            return ret;
        }
    }
}

static SYMBOL: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(^|[^\\])([@#%])(\w+\b)([:.]+[\w-]+\b)?").unwrap());
static FUNCTION: Lazy<Regex> = Lazy::new(|| Regex::new(r"(\b[a-z0-9_]+)\(\)").unwrap());
static GDK_GTK: Lazy<Regex> = Lazy::new(|| Regex::new(r"G[dt]k[A-Z]\w+\b").unwrap());
static TAGS: Lazy<Regex> = Lazy::new(|| Regex::new(r"<[\w/-]+>").unwrap());
static SPACES: Lazy<Regex> = Lazy::new(|| Regex::new(r"[ ]{2,}").unwrap());

fn replace_c_types(entry: &str, symbols: &symbols::Info, in_type: &str) -> String {
    let lookup = |s: &str| -> String {
        symbols
            .by_c_name(s)
            .map(symbols::Symbol::full_rust_name)
            .unwrap_or_else(|| s.into())
    };

    let out = SYMBOL.replace_all(entry, |caps: &Captures<'_>| {
        let member = caps.get(4).map(|m| m.as_str()).unwrap_or("");
        let sym = symbols.by_c_name(&caps[3]);
        match &caps[2] {
            /* References to members, enum variants or methods within the same type. */
            "@" => {
                if let Some(sym) = sym {
                    // Catch invalid @ references that have a C symbol available but do not belong
                    // to the current type (and can hence not use `Self::`). For now generate XXX
                    // but with a valid global link so that the can be easily spotted in the code.
                    // assert_eq!(sym.owner_name(), Some(in_type));
                    if sym.owner_name() != Some(in_type) {
                        format!(
                            "{}[`crate::{}{}`] (XXX: @-reference does not belong to {}!)",
                            &caps[1],
                            sym.full_rust_name(),
                            member,
                            in_type,
                        )
                    } else {
                        format!(
                            "{}[`{n}{m}`](Self::{n}{m})",
                            &caps[1],
                            n = sym.name(),
                            m = member
                        )
                    }
                } else {
                    format!("{}`{}{}`", &caps[1], &caps[3], member)
                }
            }
            "#" | "%" => {
                format!(
                    "{}`{}{}`",
                    &caps[1],
                    sym.map(symbols::Symbol::full_rust_name)
                        .unwrap_or_else(|| caps[3].into()),
                    member
                )
            }
            u => panic!("Unknown reference type {}", u),
        }
    });
    let out = GDK_GTK.replace_all(&out, |caps: &Captures<'_>| {
        format!("`{}`", lookup(&caps[0]))
    });
    let out = FUNCTION.replace_all(&out, |caps: &Captures<'_>| {
        if let Some(sym) = symbols.by_c_name(&caps[1]) {
            if sym.owner_name() == Some(in_type) {
                format!("[`{f}()`](Self::{f}())", f = sym.name())
            } else {
                format!("[`{f}()`](crate::{f}())", f = sym.full_rust_name())
            }
        } else {
            format!("`{}()`", &caps[1])
        }
    });
    let out = TAGS.replace_all(&out, "`$0`");
    SPACES.replace_all(&out, " ").into_owned()
}
<commit_msg>codegen/doc: Wrap virtual functions properly and use intra-doc-links<commit_after>use crate::analysis::symbols;
use once_cell::sync::Lazy;
use regex::{Captures, Match, Regex};

const LANGUAGE_SEP_BEGIN: &str = "<!-- language=\"";
const LANGUAGE_SEP_END: &str = "\" -->";
const LANGUAGE_BLOCK_BEGIN: &str = "|[";
const LANGUAGE_BLOCK_END: &str = "\n]|";

pub fn reformat_doc(input: &str, symbols: &symbols::Info, in_type: &str) -> String {
    code_blocks_transformation(input, symbols, in_type)
}

fn try_split<'a>(src: &'a str, needle: &str) -> (&'a str, Option<&'a str>) {
    match src.find(needle) {
        Some(pos) => (&src[..pos], Some(&src[pos + needle.len()..])),
        None => (src, None),
    }
}

fn code_blocks_transformation(mut input: &str, symbols: &symbols::Info, in_type: &str) -> String {
    let mut out = String::with_capacity(input.len());

    loop {
        input = match try_split(input, LANGUAGE_BLOCK_BEGIN) {
            (before, Some(after)) => {
                out.push_str(&format(before, symbols, in_type));
                if let (before, Some(after)) =
                    try_split(get_language(after, &mut out), LANGUAGE_BLOCK_END)
                {
                    out.push_str(before);
                    out.push_str("\n```");
                    after
                } else {
                    after
                }
            }
            (before, None) => {
                out.push_str(&format(before, symbols, in_type));
                return out;
            }
        };
    }
}

fn get_language<'a>(entry: &'a str, out: &mut String) -> &'a str {
    if let (_, Some(after)) = try_split(entry, LANGUAGE_SEP_BEGIN) {
        if let (before, Some(after)) = try_split(after, LANGUAGE_SEP_END) {
            out.push_str(&format!("\n```{}", before));
            return after;
        }
    }
    out.push_str("\n```text");
    entry
}

fn format(mut input: &str, symbols: &symbols::Info, in_type: &str) -> String {
    let mut ret = String::with_capacity(input.len());
    loop {
        let (before, after) = try_split(input, "`");
        ret.push_str(&replace_c_types(before, symbols, in_type));
        if let Some(after) = after {
            ret.push('`');
            let (before, after) = try_split(after, "`");
            // don't touch anything enclosed in backticks
            ret.push_str(before);
            if let Some(after) = after {
                ret.push('`');
                input = after;
            } else {
                return ret;
            }
        } else {
            return ret;
        }
    }
}

static SYMBOL: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"(^|[^\\])([@#%])(\w+\b)([:.]+[\w-]+\b)?").unwrap());
static FUNCTION: Lazy<Regex> =
    Lazy::new(|| Regex::new(r"([@#%])?(\w+\b[:.]+)?(\b[a-z0-9_]+)\(\)").unwrap());
static GDK_GTK: Lazy<Regex> = Lazy::new(|| Regex::new(r"G[dt]k[A-Z]\w+\b").unwrap());
static TAGS: Lazy<Regex> = Lazy::new(|| Regex::new(r"<[\w/-]+>").unwrap());
static SPACES: Lazy<Regex> = Lazy::new(|| Regex::new(r"[ ]{2,}").unwrap());

fn replace_c_types(entry: &str, symbols: &symbols::Info, in_type: &str) -> String {
    let lookup = |s: &str| -> String {
        symbols
            .by_c_name(s)
            .map(symbols::Symbol::full_rust_name)
            .unwrap_or_else(|| s.into())
    };

    let out = FUNCTION.replace_all(entry, |caps: &Captures<'_>| {
        let name = &caps[3];
        let sym = symbols.by_c_name(name);

        if let Some(sym) = sym {
            if sym.owner_name() == Some(in_type) {
                // `#` or `%` symbols should probably have been `@` to denote
                // that it is a reference within the current type.
                format!("[`{f}()`](Self::{f}())", f = sym.name())
            } else {
                match caps.get(1).as_ref().map(Match::as_str) {
                    // Catch invalid @ references that have a C symbol available but do not belong
                    // to the current type (and can hence not use `Self::`). For now generate XXX
                    // but with a valid global link so that the can be easily spotted in the code.
                    // assert_eq!(sym.owner_name(), Some(in_type));
                    Some("@") => format!(
                        "[`crate::{}()`] (XXX: @-reference does not belong to {}!)",
                        sym.full_rust_name(),
                        in_type,
                    ),
                    Some("#") | None => {
                        format!("[`{f}()`](crate::{f}())", f = sym.full_rust_name())
                    }
                    Some("%") => panic!("% not allowed for {:?}", caps),
                    Some(c) => panic!("Unknown symbol reference {}", c),
                }
            }
        } else if let Some(typ) = caps.get(2) {
            let typ = typ.as_str();
            if typ == in_type {
                format!("[`{f}()`](Self::{f}())", f = name)
            } else {
                format!("[`{t}{f}()`](crate::{t}{f}())", t = typ, f = name)
            }
        } else {
            format!("`{}()`", name)
        }
    });

    let out = SYMBOL.replace_all(&out, |caps: &Captures<'_>| {
        let member = caps.get(4).map(|m| m.as_str()).unwrap_or("");
        let sym = symbols.by_c_name(&caps[3]);
        match &caps[2] {
            /* References to members, enum variants or methods within the same type. */
            "@" => {
                if let Some(sym) = sym {
                    // Catch invalid @ references that have a C symbol available but do not belong
                    // to the current type (and can hence not use `Self::`). For now generate XXX
                    // but with a valid global link so that the can be easily spotted in the code.
                    // assert_eq!(sym.owner_name(), Some(in_type));
                    if sym.owner_name() != Some(in_type) {
                        format!(
                            "{}[`crate::{}{}`] (XXX: @-reference does not belong to {}!)",
                            &caps[1],
                            sym.full_rust_name(),
                            member,
                            in_type,
                        )
                    } else {
                        format!(
                            "{}[`{n}{m}`](Self::{n}{m})",
                            &caps[1],
                            n = sym.name(),
                            m = member
                        )
                    }
                } else {
                    format!("{}`{}{}`", &caps[1], &caps[3], member)
                }
            }
            "#" | "%" => {
                format!(
                    "{}`{}{}`",
                    &caps[1],
                    sym.map(symbols::Symbol::full_rust_name)
                        .unwrap_or_else(|| caps[3].into()),
                    member
                )
            }
            u => panic!("Unknown reference type {}", u),
        }
    });
    let out = GDK_GTK.replace_all(&out, |caps: &Captures<'_>| {
        format!("`{}`", lookup(&caps[0]))
    });
    let out = TAGS.replace_all(&out, "`$0`");
    SPACES.replace_all(&out, " ").into_owned()
}
<|endoftext|>


<commit_before>//! Lowers intrinsic calls

use crate::transform::MirPass;
use rustc_middle::mir::*;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::symbol::{sym, Symbol};
use rustc_target::spec::abi::Abi;

pub struct LowerIntrinsics;

impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
        let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
        for block in basic_blocks {
            let terminator = block.terminator.as_mut().unwrap();
            if let TerminatorKind::Call { func, args, destination, .. } = &mut terminator.kind {
                let func_ty = func.ty(local_decls, tcx);
                let (intrinsic_name, substs) = match resolve_rust_intrinsic(tcx, func_ty) {
                    None => continue,
                    Some(it) => it,
                };
                match intrinsic_name {
                    sym::unreachable => {
                        terminator.kind = TerminatorKind::Unreachable;
                    }
                    sym::forget => {
                        if let Some((destination, target)) = *destination {
                            block.statements.push(Statement {
                                source_info: terminator.source_info,
                                kind: StatementKind::Assign(box (
                                    destination,
                                    Rvalue::Use(Operand::Constant(box Constant {
                                        span: terminator.source_info.span,
                                        user_ty: None,
                                        literal: ty::Const::zero_sized(tcx, tcx.types.unit).into(),
                                    })),
                                )),
                            });
                            terminator.kind = TerminatorKind::Goto { target };
                        }
                    }
                    sym::copy_nonoverlapping => {
                        let target = destination.unwrap().1;
                        let mut args = args.drain(..);
                        block.statements.push(Statement {
                            source_info: terminator.source_info,
                            kind: StatementKind::CopyNonOverlapping(
                                box rustc_middle::mir::CopyNonOverlapping {
                                    src: args.next().unwrap(),
                                    dst: args.next().unwrap(),
                                    count: args.next().unwrap(),
                                },
                            ),
                        });
                        assert_eq!(
                            args.next(),
                            None,
                            "Extra argument for copy_non_overlapping intrinsic"
                        );
                        drop(args);
                        terminator.kind = TerminatorKind::Goto { target };
                    }
                    sym::wrapping_add | sym::wrapping_sub | sym::wrapping_mul => {
                        if let Some((destination, target)) = *destination {
                            let lhs;
                            let rhs;
                            {
                                let mut args = args.drain(..);
                                lhs = args.next().unwrap();
                                rhs = args.next().unwrap();
                            }
                            let bin_op = match intrinsic_name {
                                sym::wrapping_add => BinOp::Add,
                                sym::wrapping_sub => BinOp::Sub,
                                sym::wrapping_mul => BinOp::Mul,
                                _ => bug!("unexpected intrinsic"),
                            };
                            block.statements.push(Statement {
                                source_info: terminator.source_info,
                                kind: StatementKind::Assign(box (
                                    destination,
                                    Rvalue::BinaryOp(bin_op, box (lhs, rhs)),
                                )),
                            });
                            terminator.kind = TerminatorKind::Goto { target };
                        }
                    }
                    sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => {
                        // The checked binary operations are not suitable target for lowering here,
                        // since their semantics depend on the value of overflow-checks flag used
                        // during codegen. Issue #35310.
                    }
                    sym::size_of => {
                        if let Some((destination, target)) = *destination {
                            let tp_ty = substs.type_at(0);
                            block.statements.push(Statement {
                                source_info: terminator.source_info,
                                kind: StatementKind::Assign(box (
                                    destination,
                                    Rvalue::NullaryOp(NullOp::SizeOf, tp_ty),
                                )),
                            });
                            terminator.kind = TerminatorKind::Goto { target };
                        }
                    }
                    sym::discriminant_value => {
                        if let (Some((destination, target)), Some(arg)) =
                            (*destination, args[0].place())
                        {
                            let arg = tcx.mk_place_deref(arg);
                            block.statements.push(Statement {
                                source_info: terminator.source_info,
                                kind: StatementKind::Assign(box (
                                    destination,
                                    Rvalue::Discriminant(arg),
                                )),
                            });
                            terminator.kind = TerminatorKind::Goto { target };
                        }
                    }
                    _ => {}
                }
            }
        }
    }
}

fn resolve_rust_intrinsic(
    tcx: TyCtxt<'tcx>,
    func_ty: Ty<'tcx>,
) -> Option<(Symbol, SubstsRef<'tcx>)> {
    if let ty::FnDef(def_id, substs) = *func_ty.kind() {
        let fn_sig = func_ty.fn_sig(tcx);
        if fn_sig.abi() == Abi::RustIntrinsic {
            return Some((tcx.item_name(def_id), substs));
        }
    }
    None
}
<commit_msg>add check that simd_shuffle arguments are constants<commit_after>//! Lowers intrinsic calls

use crate::transform::MirPass;
use rustc_middle::mir::*;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
use rustc_target::spec::abi::Abi;

pub struct LowerIntrinsics;

impl<'tcx> MirPass<'tcx> for LowerIntrinsics {
    fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
        let (basic_blocks, local_decls) = body.basic_blocks_and_local_decls_mut();
        for block in basic_blocks {
            let terminator = block.terminator.as_mut().unwrap();
            if let TerminatorKind::Call { func, args, destination, .. } = &mut terminator.kind {
                let func_ty = func.ty(local_decls, tcx);
                let (intrinsic_name, substs) = match resolve_rust_intrinsic(tcx, func_ty) {
                    None => continue,
                    Some(it) => it,
                };
                match intrinsic_name {
                    sym::unreachable => {
                        terminator.kind = TerminatorKind::Unreachable;
                    }
                    sym::forget => {
                        if let Some((destination, target)) = *destination {
                            block.statements.push(Statement {
                                source_info: terminator.source_info,
                                kind: StatementKind::Assign(box (
                                    destination,
                                    Rvalue::Use(Operand::Constant(box Constant {
                                        span: terminator.source_info.span,
                                        user_ty: None,
                                        literal: ty::Const::zero_sized(tcx, tcx.types.unit).into(),
                                    })),
                                )),
                            });
                            terminator.kind = TerminatorKind::Goto { target };
                        }
                    }
                    sym::copy_nonoverlapping => {
                        let target = destination.unwrap().1;
                        let mut args = args.drain(..);
                        block.statements.push(Statement {
                            source_info: terminator.source_info,
                            kind: StatementKind::CopyNonOverlapping(
                                box rustc_middle::mir::CopyNonOverlapping {
                                    src: args.next().unwrap(),
                                    dst: args.next().unwrap(),
                                    count: args.next().unwrap(),
                                },
                            ),
                        });
                        assert_eq!(
                            args.next(),
                            None,
                            "Extra argument for copy_non_overlapping intrinsic"
                        );
                        drop(args);
                        terminator.kind = TerminatorKind::Goto { target };
                    }
                    sym::wrapping_add | sym::wrapping_sub | sym::wrapping_mul => {
                        if let Some((destination, target)) = *destination {
                            let lhs;
                            let rhs;
                            {
                                let mut args = args.drain(..);
                                lhs = args.next().unwrap();
                                rhs = args.next().unwrap();
                            }
                            let bin_op = match intrinsic_name {
                                sym::wrapping_add => BinOp::Add,
                                sym::wrapping_sub => BinOp::Sub,
                                sym::wrapping_mul => BinOp::Mul,
                                _ => bug!("unexpected intrinsic"),
                            };
                            block.statements.push(Statement {
                                source_info: terminator.source_info,
                                kind: StatementKind::Assign(box (
                                    destination,
                                    Rvalue::BinaryOp(bin_op, box (lhs, rhs)),
                                )),
                            });
                            terminator.kind = TerminatorKind::Goto { target };
                        }
                    }
                    sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => {
                        // The checked binary operations are not suitable target for lowering here,
                        // since their semantics depend on the value of overflow-checks flag used
                        // during codegen. Issue #35310.
                    }
                    sym::size_of => {
                        if let Some((destination, target)) = *destination {
                            let tp_ty = substs.type_at(0);
                            block.statements.push(Statement {
                                source_info: terminator.source_info,
                                kind: StatementKind::Assign(box (
                                    destination,
                                    Rvalue::NullaryOp(NullOp::SizeOf, tp_ty),
                                )),
                            });
                            terminator.kind = TerminatorKind::Goto { target };
                        }
                    }
                    sym::discriminant_value => {
                        if let (Some((destination, target)), Some(arg)) =
                            (*destination, args[0].place())
                        {
                            let arg = tcx.mk_place_deref(arg);
                            block.statements.push(Statement {
                                source_info: terminator.source_info,
                                kind: StatementKind::Assign(box (
                                    destination,
                                    Rvalue::Discriminant(arg),
                                )),
                            });
                            terminator.kind = TerminatorKind::Goto { target };
                        }
                    }
                    _ if intrinsic_name.as_str().starts_with("simd_shuffle") => {
                        validate_simd_shuffle(tcx, args, terminator.source_info.span);
                    }
                    _ => {}
                }
            }
        }
    }
}

fn resolve_rust_intrinsic(
    tcx: TyCtxt<'tcx>,
    func_ty: Ty<'tcx>,
) -> Option<(Symbol, SubstsRef<'tcx>)> {
    if let ty::FnDef(def_id, substs) = *func_ty.kind() {
        let fn_sig = func_ty.fn_sig(tcx);
        if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = fn_sig.abi() {
            return Some((tcx.item_name(def_id), substs));
        }
    }
    None
}

fn validate_simd_shuffle(tcx: TyCtxt<'tcx>, args: &[Operand<'tcx>], span: Span) {
    match &args[2] {
        Operand::Constant(_) => {} // all good
        _ => {
            let msg = format!("last argument of `simd_shuffle` is required to be a `const` item");
            tcx.sess.span_err(span, &msg);
        }
    }
}
<|endoftext|>


<commit_before>//
// Copyright 2020 The Project Oak Authors
//
// 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 crate::{
    io::Receiver,
    node::{
        grpc::{codec::VecCodec, from_abi_status, from_tonic_status, invocation::Invocation},
        ConfigurationError, Node,
    },
    runtime::RuntimeProxy,
};
use log::{debug, error, info};
use oak_abi::{
    proto::oak::{
        application::GrpcClientConfiguration,
        encap::{GrpcRequest, GrpcResponse},
    },
    Handle, OakStatus,
};
use tokio::sync::oneshot;
use tonic::transport::{Certificate, Channel, ClientTlsConfig, Uri};

/// Struct that represents a gRPC client pseudo-Node.
pub struct GrpcClientNode {
    /// Pseudo-Node name.
    node_name: String,
    /// The URI component of a gRPC server endpoint. Must contain the "Host" element.
    /// https://docs.rs/tonic/0.2.1/tonic/transport/struct.Uri.html
    uri: Uri,
    /// Loaded PEM encoded X.509 TLS root certificate file used to authenticate an external gRPC
    /// service.
    root_tls_certificate: Certificate,
    /// A gRPC client dispatcher that wraps a gRPC connection and encodes/decodes messages via a
    /// provided codec. The value is assigned using [`GrpcClientNode::connect`] upon receiving
    /// the first gRPC request.
    dispatcher: Option<tonic::client::Grpc<tonic::transport::channel::Channel>>,
}

/// Checks if URI contains the "Host" element.
fn check_uri(uri: &Uri) -> Result<(), ConfigurationError> {
    uri.authority()
        .filter(|authority| !authority.host().is_empty())
        .map(|_| ())
        .ok_or(ConfigurationError::NoHostElement)
}

impl GrpcClientNode {
    /// Creates a new [`GrpcClientNode`] instance, but does not start it.
    pub fn new(
        node_name: &str,
        config: GrpcClientConfiguration,
        root_tls_certificate: Certificate,
    ) -> Result<Self, ConfigurationError> {
        let uri = config.uri.parse().map_err(|error| {
            error!("Error parsing URI {}: {:?}", config.uri, error);
            ConfigurationError::IncorrectURI
        })?;
        check_uri(&uri)?;
        Ok(Self {
            node_name: node_name.to_string(),
            uri,
            root_tls_certificate,
            dispatcher: None,
        })
    }

    /// Main loop that handles gRPC invocations from the `handle`, sends gRPC requests to an
    /// external gRPC service and writes gRPC responses back to the invocation channel.
    async fn handle_loop(
        &mut self,
        runtime: RuntimeProxy,
        handle: Handle,
    ) -> Result<(), OakStatus> {
        // Create a [`Receiver`] used for reading gRPC invocations.
        let receiver = Receiver::<Invocation>::new(handle);
        loop {
            debug!("Waiting for gRPC invocations");
            // Read a gRPC invocation from the [`Receiver`].
            let invocation = receiver.receive(&runtime).map_err(|error| {
                error!("Couldn't receive the invocation: {:?}", error);
                error
            })?;

            // Receive a request from the invocation channel.
            let request = invocation.receive_request(&runtime).map_err(|error| {
                error!(
                    "Couldn't receive gRPC request from the invocation: {:?}",
                    error
                );
                error
            })?;
            debug!("Incoming gRPC request: {:?}", request);

            // Send an unary request to an external gRPC service and wait for the response.
            let response = self.unary_request(request).await.unwrap_or_else(|error| {
                error!("Couldn't send gRPC request: {:?}", error);
                GrpcResponse {
                    rsp_msg: vec![],
                    status: Some(from_abi_status(error)),
                    last: true,
                }
            });

            // Send a response back to the invocation channel.
            debug!("Sending gRPC response: {:?}", response);
            invocation
                .send_response(response, &runtime)
                .map_err(|error| {
                    error!("Couldn't send gRPC response to the invocation: {:?}", error);
                    error
                })?;
        }
    }

    /// Creates a TLS connection to an external gRPC service.
    async fn connect(&mut self) -> Result<(), OakStatus> {
        debug!("Connecting to {}", self.uri);

        // Create a TLS configuration.
        let tls_config = ClientTlsConfig::new().ca_certificate(self.root_tls_certificate.clone());

        // Connect to a remote gRPC service.
        let connection = Channel::builder(self.uri.clone())
            .tls_config(tls_config)
            .connect()
            .await
            .map_err(|error| {
                error!("Couldn't connect to {}: {:?}", self.uri, error);
                OakStatus::ErrInternal
            })?;

        self.dispatcher = Some(tonic::client::Grpc::new(connection));
        debug!("Connected to {}", self.uri);
        Ok(())
    }

    /// Checks whether the [`GrpcRequestHandler::dispatcher`] is able to accept new requests.
    async fn ready(&mut self) -> Result<(), OakStatus> {
        self.dispatcher
            .as_mut()
            .expect("Handler is not connected to a gRPC service")
            .ready()
            .await
            .map_err(|error| {
                error!("Service was not ready: {}", error);
                OakStatus::ErrInternal
            })?;
        Ok(())
    }

    /// Sends an unary gRPC request wrapped in [`GrpcResponse`] to an external gRPC service.
    async fn unary_request(&mut self, request: GrpcRequest) -> Result<GrpcResponse, OakStatus> {
        // Connect to an external gRPC service.
        self.connect().await?;
        self.ready().await?;

        let codec = VecCodec::default();
        let path = request.method_name.parse().map_err(|error| {
            error!("Invalid URI {}: {}", request.method_name, error);
            OakStatus::ErrInternal
        })?;

        // Send gRPC request and wait for the response.
        self.dispatcher
            .as_mut()
            .expect("Handler is not connected to a gRPC service")
            .unary(tonic::Request::new(request.req_msg), path, codec)
            .await
            .map(|response| GrpcResponse {
                rsp_msg: response.into_inner(),
                status: Some(from_tonic_status(tonic::Status::ok(""))),
                last: true,
            })
            .or_else(|status| {
                Ok(GrpcResponse {
                    rsp_msg: vec![],
                    status: Some(from_tonic_status(status)),
                    last: true,
                })
            })
    }
}

/// Oak Node implementation for the gRPC client pseudo-Node.
impl Node for GrpcClientNode {
    fn run(
        mut self: Box<Self>,
        runtime: RuntimeProxy,
        handle: Handle,
        notify_receiver: oneshot::Receiver<()>,
    ) {
        // Create an Async runtime for executing futures.
        // https://docs.rs/tokio/
        let mut async_runtime = tokio::runtime::Builder::new()
            // Use simple scheduler that runs all tasks on the current-thread.
            // https://docs.rs/tokio/0.2.16/tokio/runtime/index.html#basic-scheduler
            .basic_scheduler()
            // Enables the I/O driver.
            // Necessary for using net, process, signal, and I/O types on the Tokio runtime.
            .enable_io()
            // Enables the time driver.
            // Necessary for creating a Tokio Runtime.
            .enable_time()
            .build()
            .expect("Couldn't create an Async runtime");

        // Listen to incoming gRPC invocations.
        info!(
            "{}: Starting gRPC client pseudo-Node thread",
            self.node_name
        );
        async_runtime.block_on(futures::future::select(
            Box::pin(self.handle_loop(runtime, handle)),
            notify_receiver,
        ));
        info!("{}: Exiting gRPC client pseudo-Node thread", self.node_name);
    }
}
<commit_msg>runtime: make gRPC client object ephemeral<commit_after>//
// Copyright 2020 The Project Oak Authors
//
// 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 crate::{
    io::Receiver,
    node::{
        grpc::{codec::VecCodec, from_abi_status, from_tonic_status, invocation::Invocation},
        ConfigurationError, Node,
    },
    runtime::RuntimeProxy,
};
use log::{debug, error, info};
use oak_abi::{
    proto::oak::{
        application::GrpcClientConfiguration,
        encap::{GrpcRequest, GrpcResponse},
    },
    Handle, OakStatus,
};
use tokio::sync::oneshot;
use tonic::transport::{Certificate, Channel, ClientTlsConfig, Uri};

/// Struct that represents a gRPC client pseudo-Node.
pub struct GrpcClientNode {
    /// Pseudo-Node name.
    node_name: String,
    /// The URI component of a gRPC server endpoint. Must contain the "Host" element.
    /// https://docs.rs/tonic/0.2.1/tonic/transport/struct.Uri.html
    uri: Uri,
    /// Loaded PEM encoded X.509 TLS root certificate file used to authenticate an external gRPC
    /// service.
    root_tls_certificate: Certificate,
}

/// Checks if URI contains the "Host" element.
fn check_uri(uri: &Uri) -> Result<(), ConfigurationError> {
    uri.authority()
        .filter(|authority| !authority.host().is_empty())
        .map(|_| ())
        .ok_or(ConfigurationError::NoHostElement)
}

impl GrpcClientNode {
    /// Creates a new [`GrpcClientNode`] instance, but does not start it.
    pub fn new(
        node_name: &str,
        config: GrpcClientConfiguration,
        root_tls_certificate: Certificate,
    ) -> Result<Self, ConfigurationError> {
        let uri = config.uri.parse().map_err(|error| {
            error!("Error parsing URI {}: {:?}", config.uri, error);
            ConfigurationError::IncorrectURI
        })?;
        check_uri(&uri)?;
        Ok(Self {
            node_name: node_name.to_string(),
            uri,
            root_tls_certificate,
        })
    }

    /// Main loop that handles gRPC invocations from the `handle`, sends gRPC requests to an
    /// external gRPC service and writes gRPC responses back to the invocation channel.
    async fn handle_loop(&self, runtime: RuntimeProxy, handle: Handle) -> Result<(), OakStatus> {
        // Create a [`Receiver`] used for reading gRPC invocations.
        let receiver = Receiver::<Invocation>::new(handle);
        loop {
            debug!("Waiting for gRPC invocations");
            // Read a gRPC invocation from the [`Receiver`].
            let invocation = receiver.receive(&runtime).map_err(|error| {
                error!("Couldn't receive the invocation: {:?}", error);
                error
            })?;

            // Receive a request from the invocation channel.
            let request = invocation.receive_request(&runtime).map_err(|error| {
                error!(
                    "Couldn't receive gRPC request from the invocation: {:?}",
                    error
                );
                error
            })?;
            debug!("Incoming gRPC request: {:?}", request);

            // Send an unary request to an external gRPC service and wait for the response.
            let response = self.unary_request(request).await.unwrap_or_else(|error| {
                error!("Couldn't send gRPC request: {:?}", error);
                GrpcResponse {
                    rsp_msg: vec![],
                    status: Some(from_abi_status(error)),
                    last: true,
                }
            });

            // Send a response back to the invocation channel.
            debug!("Sending gRPC response: {:?}", response);
            invocation
                .send_response(response, &runtime)
                .map_err(|error| {
                    error!("Couldn't send gRPC response to the invocation: {:?}", error);
                    error
                })?;
        }
    }

    /// Sends an unary gRPC request wrapped in [`GrpcResponse`] to an external gRPC service.
    async fn unary_request(&self, request: GrpcRequest) -> Result<GrpcResponse, OakStatus> {
        // Connect to an external gRPC service.
        let mut dispatcher = self.connect().await?;
        dispatcher.ready().await.map_err(|error| {
            error!("Service was not ready: {}", error);
            OakStatus::ErrInternal
        })?;

        let codec = VecCodec::default();
        let path = request.method_name.parse().map_err(|error| {
            error!("Invalid URI {}: {}", request.method_name, error);
            OakStatus::ErrInternal
        })?;

        // Send gRPC request and wait for the response.
        dispatcher
            .unary(tonic::Request::new(request.req_msg), path, codec)
            .await
            .map(|response| GrpcResponse {
                rsp_msg: response.into_inner(),
                status: Some(from_tonic_status(tonic::Status::ok(""))),
                last: true,
            })
            .or_else(|status| {
                Ok(GrpcResponse {
                    rsp_msg: vec![],
                    status: Some(from_tonic_status(status)),
                    last: true,
                })
            })
    }

    /// Creates a TLS connection to an external gRPC service.
    async fn connect(
        &self,
    ) -> Result<tonic::client::Grpc<tonic::transport::channel::Channel>, OakStatus> {
        debug!("Connecting to {}", self.uri);

        // Create a TLS configuration.
        let tls_config = ClientTlsConfig::new().ca_certificate(self.root_tls_certificate.clone());

        // Connect to a remote gRPC service.
        let connection = Channel::builder(self.uri.clone())
            .tls_config(tls_config)
            .connect()
            .await
            .map_err(|error| {
                error!("Couldn't connect to {}: {:?}", self.uri, error);
                OakStatus::ErrInternal
            })?;

        debug!("Connected to {}", self.uri);
        Ok(tonic::client::Grpc::new(connection))
    }
}

/// Oak Node implementation for the gRPC client pseudo-Node.
impl Node for GrpcClientNode {
    fn run(
        self: Box<Self>,
        runtime: RuntimeProxy,
        handle: Handle,
        notify_receiver: oneshot::Receiver<()>,
    ) {
        // Create an Async runtime for executing futures.
        // https://docs.rs/tokio/
        let mut async_runtime = tokio::runtime::Builder::new()
            // Use simple scheduler that runs all tasks on the current-thread.
            // https://docs.rs/tokio/0.2.16/tokio/runtime/index.html#basic-scheduler
            .basic_scheduler()
            // Enables the I/O driver.
            // Necessary for using net, process, signal, and I/O types on the Tokio runtime.
            .enable_io()
            // Enables the time driver.
            // Necessary for creating a Tokio Runtime.
            .enable_time()
            .build()
            .expect("Couldn't create an Async runtime");

        // Listen to incoming gRPC invocations.
        info!(
            "{}: Starting gRPC client pseudo-Node thread",
            self.node_name
        );
        async_runtime.block_on(futures::future::select(
            Box::pin(self.handle_loop(runtime, handle)),
            notify_receiver,
        ));
        info!("{}: Exiting gRPC client pseudo-Node thread", self.node_name);
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 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 std::env;
use std::process::Command;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::io::Write;

struct Test {
    repo: &'static str,
    name: &'static str,
    sha: &'static str,
    lock: Option<&'static str>,
}

const TEST_REPOS: &'static [Test] = &[
    Test {
        name: "cargo",
        repo: "https://github.com/rust-lang/cargo",
        sha: "38f52ea846db202f322071f00b083ae8defdda3a",
        lock: None,
    },
    Test {
        name: "iron",
        repo: "https://github.com/iron/iron",
        sha: "16c858ec2901e2992fe5e529780f59fa8ed12903",
        lock: Some(include_str!("lockfiles/iron-Cargo.lock")),
    },
    Test {
        name: "ripgrep",
        repo: "https://github.com/BurntSushi/ripgrep",
        sha: "b65bb37b14655e1a89c7cd19c8b011ef3e312791",
        lock: None,
    },
    Test {
        name: "tokei",
        repo: "https://github.com/Aaronepower/tokei",
        sha: "5e11c4852fe4aa086b0e4fe5885822fbe57ba928",
        lock: None,
    },
    Test {
        name: "treeify",
        repo: "https://github.com/dzamlo/treeify",
        sha: "999001b223152441198f117a68fb81f57bc086dd",
        lock: None,
    },
    Test {
        name: "xsv",
        repo: "https://github.com/BurntSushi/xsv",
        sha: "a9a7163f2a2953cea426fee1216bec914fe2f56a",
        lock: None,
    },
];

fn main() {
    // One of the projects being tested here is Cargo, and when being tested
    // Cargo will at some point call `nmake.exe` on Windows MSVC. Unfortunately
    // `nmake` will read these two environment variables below and try to
    // intepret them. We're likely being run, however, from MSYS `make` which
    // uses the same variables.
    //
    // As a result, to prevent confusion and errors, we remove these variables
    // from our environment to prevent passing MSYS make flags to nmake, causing
    // it to blow up.
    if cfg!(target_env = "msvc") {
        env::remove_var("MAKE");
        env::remove_var("MAKEFLAGS");
    }

    let args = env::args().collect::<Vec<_>>();
    let ref cargo = args[1];
    let out_dir = Path::new(&args[2]);
    let ref cargo = Path::new(cargo);

    for test in TEST_REPOS.iter().rev() {
        test_repo(cargo, out_dir, test);
    }
}

fn test_repo(cargo: &Path, out_dir: &Path, test: &Test) {
    println!("testing {}", test.repo);
    let dir = clone_repo(test, out_dir);
    if let Some(lockfile) = test.lock {
        File::create(&dir.join("Cargo.lock"))
            .expect("")
            .write_all(lockfile.as_bytes())
            .expect("");
    }
    if !run_cargo_test(cargo, &dir) {
        panic!("tests failed for {}", test.repo);
    }
}

fn clone_repo(test: &Test, out_dir: &Path) -> PathBuf {
    let out_dir = out_dir.join(test.name);

    if !out_dir.join(".git").is_dir() {
        let status = Command::new("git")
                         .arg("init")
                         .arg(&out_dir)
                         .status()
                         .expect("");
        assert!(status.success());
    }

    // Try progressively deeper fetch depths to find the commit
    let mut found = false;
    for depth in &[0, 1, 10, 100, 1000, 100000] {
        if *depth > 0 {
            let status = Command::new("git")
                             .arg("fetch")
                             .arg(test.repo)
                             .arg("master")
                             .arg(&format!("--depth={}", depth))
                             .current_dir(&out_dir)
                             .status()
                             .expect("");
            assert!(status.success());
        }

        let status = Command::new("git")
                         .arg("reset")
                         .arg(test.sha)
                         .arg("--hard")
                         .current_dir(&out_dir)
                         .status()
                         .expect("");

        if status.success() {
            found = true;
            break;
        }
    }

    if !found {
        panic!("unable to find commit {}", test.sha)
    }
    let status = Command::new("git")
                     .arg("clean")
                     .arg("-fdx")
                     .current_dir(&out_dir)
                     .status()
                     .unwrap();
    assert!(status.success());

    out_dir
}

fn run_cargo_test(cargo_path: &Path, crate_path: &Path) -> bool {
    let status = Command::new(cargo_path)
        .arg("test")
        // Disable rust-lang/cargo's cross-compile tests
        .env("CFG_DISABLE_CROSS_TESTS", "1")
        .current_dir(crate_path)
        .status()
        .expect("");

    status.success()
}
<commit_msg>Update to last cargo version<commit_after>// Copyright 2016 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 std::env;
use std::process::Command;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::io::Write;

struct Test {
    repo: &'static str,
    name: &'static str,
    sha: &'static str,
    lock: Option<&'static str>,
}

const TEST_REPOS: &'static [Test] = &[
    Test {
        name: "cargo",
        repo: "https://github.com/rust-lang/cargo",
        sha: "0e1e34be7540bdaed4918457654fbf028cf69e56",
        lock: None,
    },
    Test {
        name: "iron",
        repo: "https://github.com/iron/iron",
        sha: "16c858ec2901e2992fe5e529780f59fa8ed12903",
        lock: Some(include_str!("lockfiles/iron-Cargo.lock")),
    },
    Test {
        name: "ripgrep",
        repo: "https://github.com/BurntSushi/ripgrep",
        sha: "b65bb37b14655e1a89c7cd19c8b011ef3e312791",
        lock: None,
    },
    Test {
        name: "tokei",
        repo: "https://github.com/Aaronepower/tokei",
        sha: "5e11c4852fe4aa086b0e4fe5885822fbe57ba928",
        lock: None,
    },
    Test {
        name: "treeify",
        repo: "https://github.com/dzamlo/treeify",
        sha: "999001b223152441198f117a68fb81f57bc086dd",
        lock: None,
    },
    Test {
        name: "xsv",
        repo: "https://github.com/BurntSushi/xsv",
        sha: "a9a7163f2a2953cea426fee1216bec914fe2f56a",
        lock: None,
    },
];

fn main() {
    // One of the projects being tested here is Cargo, and when being tested
    // Cargo will at some point call `nmake.exe` on Windows MSVC. Unfortunately
    // `nmake` will read these two environment variables below and try to
    // intepret them. We're likely being run, however, from MSYS `make` which
    // uses the same variables.
    //
    // As a result, to prevent confusion and errors, we remove these variables
    // from our environment to prevent passing MSYS make flags to nmake, causing
    // it to blow up.
    if cfg!(target_env = "msvc") {
        env::remove_var("MAKE");
        env::remove_var("MAKEFLAGS");
    }

    let args = env::args().collect::<Vec<_>>();
    let ref cargo = args[1];
    let out_dir = Path::new(&args[2]);
    let ref cargo = Path::new(cargo);

    for test in TEST_REPOS.iter().rev() {
        test_repo(cargo, out_dir, test);
    }
}

fn test_repo(cargo: &Path, out_dir: &Path, test: &Test) {
    println!("testing {}", test.repo);
    let dir = clone_repo(test, out_dir);
    if let Some(lockfile) = test.lock {
        File::create(&dir.join("Cargo.lock"))
            .expect("")
            .write_all(lockfile.as_bytes())
            .expect("");
    }
    if !run_cargo_test(cargo, &dir) {
        panic!("tests failed for {}", test.repo);
    }
}

fn clone_repo(test: &Test, out_dir: &Path) -> PathBuf {
    let out_dir = out_dir.join(test.name);

    if !out_dir.join(".git").is_dir() {
        let status = Command::new("git")
                         .arg("init")
                         .arg(&out_dir)
                         .status()
                         .expect("");
        assert!(status.success());
    }

    // Try progressively deeper fetch depths to find the commit
    let mut found = false;
    for depth in &[0, 1, 10, 100, 1000, 100000] {
        if *depth > 0 {
            let status = Command::new("git")
                             .arg("fetch")
                             .arg(test.repo)
                             .arg("master")
                             .arg(&format!("--depth={}", depth))
                             .current_dir(&out_dir)
                             .status()
                             .expect("");
            assert!(status.success());
        }

        let status = Command::new("git")
                         .arg("reset")
                         .arg(test.sha)
                         .arg("--hard")
                         .current_dir(&out_dir)
                         .status()
                         .expect("");

        if status.success() {
            found = true;
            break;
        }
    }

    if !found {
        panic!("unable to find commit {}", test.sha)
    }
    let status = Command::new("git")
                     .arg("clean")
                     .arg("-fdx")
                     .current_dir(&out_dir)
                     .status()
                     .unwrap();
    assert!(status.success());

    out_dir
}

fn run_cargo_test(cargo_path: &Path, crate_path: &Path) -> bool {
    let status = Command::new(cargo_path)
        .arg("test")
        // Disable rust-lang/cargo's cross-compile tests
        .env("CFG_DISABLE_CROSS_TESTS", "1")
        .current_dir(crate_path)
        .status()
        .expect("");

    status.success()
}
<|endoftext|>


<commit_before>use fall_tree::{Language, NodeType, TextUnit};

use ::lex_engine::Token;

pub type BlackIdx = usize;

pub struct BlackTokens<'a> {
    text: &'a str,
    non_ws_indexes: Vec<BlackIdx>,
    original_tokens: &'a [Token],
}

impl<'a> BlackTokens<'a> {
    pub fn new(lang: &Language, text: &'a str, tokens: &'a [Token]) -> BlackTokens<'a> {
        let is_ws = |t: Token| lang.node_type_info(t.ty).whitespace_like;

        let non_ws_indexes = tokens.iter().enumerate()
            .filter_map(|(i, &t)| if is_ws(t) { None } else { Some(i) })
            .collect();

        let ws_len = tokens.iter()
            .take_while(|&&t| is_ws(t))
            .map(|t| t.len.as_u32() as usize)
            .sum();

        BlackTokens {
            text: &text[ws_len..],
            non_ws_indexes: non_ws_indexes,
            original_tokens: tokens,
        }
    }

    pub fn seq(&'a self) -> TokenSeq<'a> {
        TokenSeq {
            text: self.text,
            non_ws_indexes: &self.non_ws_indexes,
            original_tokens: self.original_tokens,
        }
    }
}

/// An iterator over slice of tokens,
/// capable of skipping over insignificant trivia
/// like whitespace or comments.
#[derive(Clone, Copy, Debug)]
pub struct TokenSeq<'a> {
    pub text: &'a str,
    pub non_ws_indexes: &'a [BlackIdx],
    pub original_tokens: &'a [Token],
}

impl<'a> TokenSeq<'a> {
    pub fn current(&self) -> Option<Token> {
        self.non_ws_indexes.first().map(|&idx| {
            self.original_tokens[idx]
        })
    }

    pub fn cut_suffix(&self, suffix: TokenSeq<'a>) -> TokenSeq<'a> {
        let end = self.non_ws_indexes.len() - suffix.non_ws_indexes.len();
        TokenSeq {
            text: self.text,
            non_ws_indexes: &self.non_ws_indexes[..end],
            original_tokens: self.original_tokens
        }
    }

    pub fn bump(&self) -> ((NodeType, BlackIdx), TokenSeq<'a>) {
        let token = self.current().expect("Can't bump an empty token sequence");
        let (ty, idx) = (token.ty, self.non_ws_indexes[0]);

        // CLEANUP
        let mut text_len = 0;
        let next_idx = self.non_ws_indexes.get(1).map(|&i| i).unwrap_or(self.original_tokens.len());
        for i in self.non_ws_indexes[0]..next_idx {
            text_len += self.original_tokens[i].len.as_u32() as usize;
        }
        let rest = TokenSeq {
            text: &self.text[text_len..],
            non_ws_indexes: &self.non_ws_indexes[1..],
            original_tokens: self.original_tokens,
        };
        ((ty, idx), rest)
    }

    pub fn bump_by_text(&self, text: &str) -> Option<(&'a [BlackIdx], TokenSeq<'a>)> {
        if !self.text.starts_with(text) {
            return None;
        }

        let mut leftover = TextUnit::from_usize(text.len());
        let mut rest = *self;
        let mut n_tokens = 0;

        while leftover > TextUnit::zero() {
            let cur_len = rest.current().unwrap().len;
            if leftover < cur_len {
                panic!("Textual match split token in half")
            }
            leftover -= cur_len;
            let (_, rest_) = rest.bump();
            rest = rest_;
            n_tokens += 1;
        }

        Some((&self.non_ws_indexes[..n_tokens], rest))
    }
}
<commit_msg>Use iterators<commit_after>use fall_tree::{Language, NodeType, TextUnit};

use ::lex_engine::Token;

pub type BlackIdx = usize;

pub struct BlackTokens<'a> {
    text: &'a str,
    non_ws_indexes: Vec<BlackIdx>,
    original_tokens: &'a [Token],
}

impl<'a> BlackTokens<'a> {
    pub fn new(lang: &Language, text: &'a str, tokens: &'a [Token]) -> BlackTokens<'a> {
        let is_ws = |t: Token| lang.node_type_info(t.ty).whitespace_like;

        let non_ws_indexes = tokens.iter().enumerate()
            .filter_map(|(i, &t)| if is_ws(t) { None } else { Some(i) })
            .collect();

        let ws_len = tokens.iter()
            .take_while(|&&t| is_ws(t))
            .map(|t| t.len.as_u32() as usize)
            .sum();

        BlackTokens {
            text: &text[ws_len..],
            non_ws_indexes: non_ws_indexes,
            original_tokens: tokens,
        }
    }

    pub fn seq(&'a self) -> TokenSeq<'a> {
        TokenSeq {
            text: self.text,
            non_ws_indexes: &self.non_ws_indexes,
            original_tokens: self.original_tokens,
        }
    }
}

/// An iterator over slice of tokens,
/// capable of skipping over insignificant trivia
/// like whitespace or comments.
#[derive(Clone, Copy, Debug)]
pub struct TokenSeq<'a> {
    pub text: &'a str,
    pub non_ws_indexes: &'a [BlackIdx],
    pub original_tokens: &'a [Token],
}

impl<'a> TokenSeq<'a> {
    pub fn current(&self) -> Option<Token> {
        self.non_ws_indexes.first().map(|&idx| {
            self.original_tokens[idx]
        })
    }

    pub fn cut_suffix(&self, suffix: TokenSeq<'a>) -> TokenSeq<'a> {
        let end = self.non_ws_indexes.len() - suffix.non_ws_indexes.len();
        TokenSeq {
            text: self.text,
            non_ws_indexes: &self.non_ws_indexes[..end],
            original_tokens: self.original_tokens
        }
    }

    pub fn bump(&self) -> ((NodeType, BlackIdx), TokenSeq<'a>) {
        let token = self.current().expect("Can't bump an empty token sequence");
        let (ty, idx) = (token.ty, self.non_ws_indexes[0]);

        let text_len = {
            let next_idx = self.non_ws_indexes.get(1).map(|&i| i).unwrap_or(self.original_tokens.len());
            self.original_tokens[self.non_ws_indexes[0] .. next_idx]
                .iter()
                .map(|t| t.len)
                .sum::<TextUnit>()
                .as_u32() as usize
        };

        let rest = TokenSeq {
            text: &self.text[text_len..],
            non_ws_indexes: &self.non_ws_indexes[1..],
            original_tokens: self.original_tokens,
        };
        ((ty, idx), rest)
    }

    pub fn bump_by_text(&self, text: &str) -> Option<(&'a [BlackIdx], TokenSeq<'a>)> {
        if !self.text.starts_with(text) {
            return None;
        }

        let mut leftover = TextUnit::from_usize(text.len());
        let mut rest = *self;
        let mut n_tokens = 0;

        while leftover > TextUnit::zero() {
            let cur_len = rest.current().unwrap().len;
            if leftover < cur_len {
                panic!("Textual match split token in half")
            }
            leftover -= cur_len;
            let (_, rest_) = rest.bump();
            rest = rest_;
            n_tokens += 1;
        }

        Some((&self.non_ws_indexes[..n_tokens], rest))
    }
}
<|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 fx::FxHashMap;
use std::hash::Hash;
use std::ops;
use std::mem;

#[cfg(test)]
mod test;

pub struct SnapshotMap<K, V>
    where K: Hash + Clone + Eq
{
    map: FxHashMap<K, V>,
    undo_log: Vec<UndoLog<K, V>>,
}

// HACK(eddyb) manual impl avoids `Default` bounds on `K` and `V`.
impl<K, V> Default for SnapshotMap<K, V>
    where K: Hash + Clone + Eq
{
    fn default() -> Self {
        SnapshotMap {
            map: Default::default(),
            undo_log: Default::default(),
        }
    }
}

pub struct Snapshot {
    len: usize,
}

enum UndoLog<K, V> {
    OpenSnapshot,
    CommittedSnapshot,
    Inserted(K),
    Overwrite(K, V),
    Purged,
}

impl<K, V> SnapshotMap<K, V>
    where K: Hash + Clone + Eq
{
    pub fn clear(&mut self) {
        self.map.clear();
        self.undo_log.clear();
    }

    pub fn insert(&mut self, key: K, value: V) -> bool {
        match self.map.insert(key.clone(), value) {
            None => {
                if !self.undo_log.is_empty() {
                    self.undo_log.push(UndoLog::Inserted(key));
                }
                true
            }
            Some(old_value) => {
                if !self.undo_log.is_empty() {
                    self.undo_log.push(UndoLog::Overwrite(key, old_value));
                }
                false
            }
        }
    }

    pub fn remove(&mut self, key: K) -> bool {
        match self.map.remove(&key) {
            Some(old_value) => {
                if !self.undo_log.is_empty() {
                    self.undo_log.push(UndoLog::Overwrite(key, old_value));
                }
                true
            }
            None => false,
        }
    }

    pub fn get(&self, key: &K) -> Option<&V> {
        self.map.get(key)
    }

    pub fn snapshot(&mut self) -> Snapshot {
        self.undo_log.push(UndoLog::OpenSnapshot);
        let len = self.undo_log.len() - 1;
        Snapshot { len }
    }

    fn assert_open_snapshot(&self, snapshot: &Snapshot) {
        assert!(snapshot.len < self.undo_log.len());
        assert!(match self.undo_log[snapshot.len] {
            UndoLog::OpenSnapshot => true,
            _ => false,
        });
    }

    pub fn commit(&mut self, snapshot: &Snapshot) {
        self.assert_open_snapshot(snapshot);
        if snapshot.len == 0 {
            // The root snapshot.
            self.undo_log.truncate(0);
        } else {
            self.undo_log[snapshot.len] = UndoLog::CommittedSnapshot;
        }
    }

    pub fn partial_rollback<F>(&mut self,
                               snapshot: &Snapshot,
                               should_revert_key: &F)
        where F: Fn(&K) -> bool
    {
        self.assert_open_snapshot(snapshot);
        for i in (snapshot.len + 1..self.undo_log.len()).rev() {
            let reverse = match self.undo_log[i] {
                UndoLog::OpenSnapshot => false,
                UndoLog::CommittedSnapshot => false,
                UndoLog::Purged => false,
                UndoLog::Inserted(ref k) => should_revert_key(k),
                UndoLog::Overwrite(ref k, _) => should_revert_key(k),
            };

            if reverse {
                let entry = mem::replace(&mut self.undo_log[i], UndoLog::Purged);
                self.reverse(entry);
            }
        }
    }

    pub fn rollback_to(&mut self, snapshot: &Snapshot) {
        self.assert_open_snapshot(snapshot);
        while self.undo_log.len() > snapshot.len + 1 {
            let entry = self.undo_log.pop().unwrap();
            self.reverse(entry);
        }

        let v = self.undo_log.pop().unwrap();
        assert!(match v {
            UndoLog::OpenSnapshot => true,
            _ => false,
        });
        assert!(self.undo_log.len() == snapshot.len);
    }

    fn reverse(&mut self, entry: UndoLog<K, V>) {
        match entry {
            UndoLog::OpenSnapshot => {
                panic!("cannot rollback an uncommitted snapshot");
            }

            UndoLog::CommittedSnapshot => {}

            UndoLog::Inserted(key) => {
                self.map.remove(&key);
            }

            UndoLog::Overwrite(key, old_value) => {
                self.map.insert(key, old_value);
            }

            UndoLog::Purged => {}
        }
    }
}

impl<'k, K, V> ops::Index<&'k K> for SnapshotMap<K, V>
    where K: Hash + Clone + Eq
{
    type Output = V;
    fn index(&self, key: &'k K) -> &V {
        &self.map[key]
    }
}
<commit_msg>Replace a `.truncate(0)` call with `.clear()`.<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 fx::FxHashMap;
use std::hash::Hash;
use std::ops;
use std::mem;

#[cfg(test)]
mod test;

pub struct SnapshotMap<K, V>
    where K: Hash + Clone + Eq
{
    map: FxHashMap<K, V>,
    undo_log: Vec<UndoLog<K, V>>,
}

// HACK(eddyb) manual impl avoids `Default` bounds on `K` and `V`.
impl<K, V> Default for SnapshotMap<K, V>
    where K: Hash + Clone + Eq
{
    fn default() -> Self {
        SnapshotMap {
            map: Default::default(),
            undo_log: Default::default(),
        }
    }
}

pub struct Snapshot {
    len: usize,
}

enum UndoLog<K, V> {
    OpenSnapshot,
    CommittedSnapshot,
    Inserted(K),
    Overwrite(K, V),
    Purged,
}

impl<K, V> SnapshotMap<K, V>
    where K: Hash + Clone + Eq
{
    pub fn clear(&mut self) {
        self.map.clear();
        self.undo_log.clear();
    }

    pub fn insert(&mut self, key: K, value: V) -> bool {
        match self.map.insert(key.clone(), value) {
            None => {
                if !self.undo_log.is_empty() {
                    self.undo_log.push(UndoLog::Inserted(key));
                }
                true
            }
            Some(old_value) => {
                if !self.undo_log.is_empty() {
                    self.undo_log.push(UndoLog::Overwrite(key, old_value));
                }
                false
            }
        }
    }

    pub fn remove(&mut self, key: K) -> bool {
        match self.map.remove(&key) {
            Some(old_value) => {
                if !self.undo_log.is_empty() {
                    self.undo_log.push(UndoLog::Overwrite(key, old_value));
                }
                true
            }
            None => false,
        }
    }

    pub fn get(&self, key: &K) -> Option<&V> {
        self.map.get(key)
    }

    pub fn snapshot(&mut self) -> Snapshot {
        self.undo_log.push(UndoLog::OpenSnapshot);
        let len = self.undo_log.len() - 1;
        Snapshot { len }
    }

    fn assert_open_snapshot(&self, snapshot: &Snapshot) {
        assert!(snapshot.len < self.undo_log.len());
        assert!(match self.undo_log[snapshot.len] {
            UndoLog::OpenSnapshot => true,
            _ => false,
        });
    }

    pub fn commit(&mut self, snapshot: &Snapshot) {
        self.assert_open_snapshot(snapshot);
        if snapshot.len == 0 {
            // The root snapshot.
            self.undo_log.clear();
        } else {
            self.undo_log[snapshot.len] = UndoLog::CommittedSnapshot;
        }
    }

    pub fn partial_rollback<F>(&mut self,
                               snapshot: &Snapshot,
                               should_revert_key: &F)
        where F: Fn(&K) -> bool
    {
        self.assert_open_snapshot(snapshot);
        for i in (snapshot.len + 1..self.undo_log.len()).rev() {
            let reverse = match self.undo_log[i] {
                UndoLog::OpenSnapshot => false,
                UndoLog::CommittedSnapshot => false,
                UndoLog::Purged => false,
                UndoLog::Inserted(ref k) => should_revert_key(k),
                UndoLog::Overwrite(ref k, _) => should_revert_key(k),
            };

            if reverse {
                let entry = mem::replace(&mut self.undo_log[i], UndoLog::Purged);
                self.reverse(entry);
            }
        }
    }

    pub fn rollback_to(&mut self, snapshot: &Snapshot) {
        self.assert_open_snapshot(snapshot);
        while self.undo_log.len() > snapshot.len + 1 {
            let entry = self.undo_log.pop().unwrap();
            self.reverse(entry);
        }

        let v = self.undo_log.pop().unwrap();
        assert!(match v {
            UndoLog::OpenSnapshot => true,
            _ => false,
        });
        assert!(self.undo_log.len() == snapshot.len);
    }

    fn reverse(&mut self, entry: UndoLog<K, V>) {
        match entry {
            UndoLog::OpenSnapshot => {
                panic!("cannot rollback an uncommitted snapshot");
            }

            UndoLog::CommittedSnapshot => {}

            UndoLog::Inserted(key) => {
                self.map.remove(&key);
            }

            UndoLog::Overwrite(key, old_value) => {
                self.map.insert(key, old_value);
            }

            UndoLog::Purged => {}
        }
    }
}

impl<'k, K, V> ops::Index<&'k K> for SnapshotMap<K, V>
    where K: Hash + Clone + Eq
{
    type Output = V;
    fn index(&self, key: &'k K) -> &V {
        &self.map[key]
    }
}
<|endoftext|>


<commit_before>use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions};

pub fn opts() -> TargetOptions {
    let mut pre_link_args = LinkArgs::new();
    pre_link_args.insert(
        LinkerFlavor::Gcc,
        vec![
            // Tell GCC to avoid linker plugins, because we are not bundling
            // them with Windows installer, and Rust does its own LTO anyways.
            "-fno-use-linker-plugin".to_string(),
            // Always enable DEP (NX bit) when it is available
            "-Wl,--nxcompat".to_string(),
        ],
    );

    let mut late_link_args = LinkArgs::new();
    late_link_args.insert(
        LinkerFlavor::Gcc,
        vec![
            //"-lwinstorecompat".to_string(),
            //"-lmingwex".to_string(),
            //"-lwinstorecompat".to_string(),
            "-lwinstorecompat".to_string(),
            "-lruntimeobject".to_string(),
            "-lsynchronization".to_string(),
            "-lvcruntime140_app".to_string(),
            "-lucrt".to_string(),
            "-lwindowsapp".to_string(),
            "-lmingwex".to_string(),
            "-lmingw32".to_string(),
        ],
    );

    TargetOptions {
        // FIXME(#13846) this should be enabled for windows
        function_sections: false,
        linker: Some("gcc".to_string()),
        dynamic_linking: true,
        executables: false,
        dll_prefix: String::new(),
        dll_suffix: ".dll".to_string(),
        exe_suffix: ".exe".to_string(),
        staticlib_prefix: "lib".to_string(),
        staticlib_suffix: ".a".to_string(),
        target_family: Some("windows".to_string()),
        is_like_windows: true,
        allows_weak_linkage: false,
        pre_link_args,
        pre_link_objects_exe: vec![
            "rsbegin.o".to_string(), // Rust compiler runtime initialization, see rsbegin.rs
        ],
        pre_link_objects_dll: vec!["rsbegin.o".to_string()],
        late_link_args,
        post_link_objects: vec!["rsend.o".to_string()],
        abi_return_struct_as_int: true,
        emit_debug_gdb_scripts: false,
        requires_uwtable: true,
        limit_rdylib_exports: false,

        ..Default::default()
    }
}
<commit_msg>rustc_target: Inherit `windows_uwp_gnu_base` from `windows_gnu_base`<commit_after>use crate::spec::{LinkArgs, LinkerFlavor, TargetOptions};

pub fn opts() -> TargetOptions {
    let base = super::windows_gnu_base::opts();

    // FIXME: Consider adding `-nostdlib` and inheriting from `windows_gnu_base`.
    let mut pre_link_args = LinkArgs::new();
    pre_link_args.insert(
        LinkerFlavor::Gcc,
        vec![
            // Tell GCC to avoid linker plugins, because we are not bundling
            // them with Windows installer, and Rust does its own LTO anyways.
            "-fno-use-linker-plugin".to_string(),
            // Always enable DEP (NX bit) when it is available
            "-Wl,--nxcompat".to_string(),
        ],
    );

    // FIXME: Should UWP target be updated for the exception machinery changes from #67502?
    let mut late_link_args = LinkArgs::new();
    let late_link_args_dynamic = LinkArgs::new();
    let late_link_args_static = LinkArgs::new();
    late_link_args.insert(
        LinkerFlavor::Gcc,
        vec![
            //"-lwinstorecompat".to_string(),
            //"-lmingwex".to_string(),
            //"-lwinstorecompat".to_string(),
            "-lwinstorecompat".to_string(),
            "-lruntimeobject".to_string(),
            "-lsynchronization".to_string(),
            "-lvcruntime140_app".to_string(),
            "-lucrt".to_string(),
            "-lwindowsapp".to_string(),
            "-lmingwex".to_string(),
            "-lmingw32".to_string(),
        ],
    );

    TargetOptions {
        executables: false,
        limit_rdylib_exports: false,
        pre_link_args,
        // FIXME: Consider adding `-nostdlib` and inheriting from `windows_gnu_base`.
        pre_link_objects_exe: vec!["rsbegin.o".to_string()],
        // FIXME: Consider adding `-nostdlib` and inheriting from `windows_gnu_base`.
        pre_link_objects_dll: vec!["rsbegin.o".to_string()],
        late_link_args,
        late_link_args_dynamic,
        late_link_args_static,

        ..base
    }
}
<|endoftext|>